Files
git.stella-ops.org/docs/modules/registry/architecture.md
master fdf95e0f46 docs: module dossier + install/quickstart sync for truthful cutover sprints
- API_CLI_REFERENCE.md, INSTALL_GUIDE.md, quickstart.md, architecture/integrations.md, dev/DEV_ENVIRONMENT_SETUP.md, integrations/LOCAL_SERVICES.md: reflect real-service wiring.
- docs/modules/**: module dossier updates across the modules touched by SPRINT_20260415_001..007 + SPRINT_20260416_003..017 + SPRINT_20260417_018..024 + SPRINT_20260418_025 + SPRINT_20260419_026.
- docs/features/checked/web/**: update feature notes where UI changed.
- docs/qa/feature-checks/runs/web/evidence-presentation-ux/: QA evidence artifacts.
- docs/setup/**, docs/technical/**: align with setup wizard contracts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:45:09 +03:00

4.6 KiB

Registry Token Service architecture

Overview

Registry Token Service issues short-lived Docker registry bearer tokens for private or mirrored registries. It is designed for offline/self-hosted operation and enforces plan/licence constraints before minting any registry token.

The service is intentionally small:

  • One HTTP endpoint: GET /token
  • Authorization decisions based on (a) Authority-issued identity token claims and (b) either persisted or static plan rules

Primary responsibilities

  • Validate caller identity using Authority-issued tokens (deployment profile may use bearer-only, DPoP, and/or mTLS).
  • Authorize requested registry scopes against a configured plan catalogue.
  • Deny issuance for revoked licences.
  • Mint a Docker-registry-compatible JWT with an access claim covering the permitted repository actions.
  • Emit deterministic observability signals (metrics + structured logs) for audits and ops.

Runtime components

Minimal API host

  • Project: src/Registry/StellaOps.Registry.TokenService
  • Endpoints:
    • GET /token (authorized)
    • GET /healthz (unauthenticated liveness)

Auth integration

  • Resource server validation is configured from RegistryTokenService:Authority.
  • Authorization policy name: registry.token.issue
  • Required scopes default to registry.token.issue (configurable via RegistryTokenService:Authority:RequiredScopes).

Plan registry (authorization rules)

  • The caller's plan is read from stellaops:plan claim (fallback: configured DefaultPlan).
  • Licence revocation uses stellaops:license claim and configured RevokedLicenses.
  • Plan rules match repositories by wildcard pattern (*) and validate requested actions (pull, push, etc.) as a subset of allowed actions.

Plan administration storage

  • The admin IPlanRuleStore is backed by PostgreSQL when RegistryTokenService:Postgres:ConnectionString is configured.
  • Startup migrations run automatically on host startup for the registry-token schema.
  • The in-memory store is restricted to Testing hosts only; live runtime composition requires the durable backend.
  • The persistence schema stores plan rules plus audit history so plan CRUD, audit endpoints, and /token authorization survive process restarts.

Token issuer

  • Tokens are signed with an RSA private key loaded from RegistryTokenService:Signing:KeyPath (PEM or PFX).
  • aud defaults to the requested registry service value unless Signing:Audience is configured.
  • Token lifetime is configured and bounded (1s..1h, default 5m).

Observability

  • OpenTelemetry metrics:
    • registry_token_issued_total{plan=...}
    • registry_token_rejected_total{reason=...}
  • Structured logs via Serilog request logging.

Request flow

  1. Docker/OCI client receives a 401 from the registry with a WWW-Authenticate: Bearer realm=...,service=...,scope=repository:... challenge.
  2. Client obtains an Authority token with the registry.token.issue scope (and any required sender constraints for the deployment).
  3. Client calls GET /token?service=<service>&scope=repository:<repo>:<actions> on Registry Token Service.
  4. Service validates:
    • service is present (and is allow-listed if AllowedServices is configured)
    • requested scopes parse correctly
    • caller plan/licence claims authorize all requested repository actions
  5. Service returns a JSON response containing the signed registry token.

Denial paths:

  • 400 for malformed requests (service missing, invalid scope query).
  • 403 for authorization failures (plan/licence/policy denies).

Token shape (Docker registry compatible)

The issued JWT includes:

  • sub: subject derived from nameidentifier/client_id/sub claims
  • service: the requested registry service
  • access: array of { type, name, actions[] } entries (type is repository)
  • Optional: stellaops:license passthrough claim (for downstream correlation)

Configuration

Configuration is loaded from:

  • etc/registry-token.yaml (optional)
  • environment variables prefixed with REGISTRY_TOKEN_

Key sections are defined by RegistryTokenServiceOptions:

  • Authority (issuer/metadata, audiences, required scopes)
  • Signing (issuer, key, lifetime, optional audience/kid)
  • Registry (realm, allow-listed service values)
  • Plans, DefaultPlan, RevokedLicenses

Durable plan-rule persistence is configured separately under RegistryTokenService:Postgres. When Postgres persistence is configured, the host may start without any statically configured Plans; persisted plan rules become the canonical source for admin CRUD and token issuance.

References

  • Operations/runbook: docs/modules/registry/operations/token-service.md