PacksRegistry Architecture

Audience: developers, operators, and release engineers working on signed pack import, mirror, lifecycle, and offline seed flows.

PacksRegistry is the signed content registry for Stella Ops packs. It stores and serves task packs, policy/data packs, provenance, attestations, lifecycle state, mirror metadata, compliance summaries, and offline seed exports.

Source Locations

AreaPath
Web APIsrc/JobEngine/StellaOps.PacksRegistry/StellaOps.PacksRegistry.WebService/
Core services/modelssrc/JobEngine/StellaOps.PacksRegistry/StellaOps.PacksRegistry.Core/
Infrastructure repositoriessrc/JobEngine/StellaOps.PacksRegistry/StellaOps.PacksRegistry.Infrastructure/
Workersrc/JobEngine/StellaOps.PacksRegistry/StellaOps.PacksRegistry.Worker/
Testssrc/JobEngine/StellaOps.PacksRegistry/StellaOps.PacksRegistry.Tests/
API referencedocs/modules/packsregistry/api-reference.md
OpenAPIdocs/modules/packsregistry/openapi/v1.json

Boundary

PacksRegistry is data-pack-only in the pluginized compose model. Importing a pack verifies and stores signed content; it must not execute code supplied by the pack. Executable behavior remains host-owned unless a future sprint creates a dedicated signed adapter contract with admission, probe, and sandbox rules. Scheduler ISchedulerJobPlugin bundles are not task packs; they are mounted under devops/plugins/scheduler/<profile>/ and admitted by the Scheduler host, not by PacksRegistry import.

The following remain declarative content:

Runtime Flow

  1. API clients upload or request packs through the web service.
  2. PackService, AttestationService, LifecycleService, MirrorService, ParityService, and ExportService validate input and coordinate storage.
  3. Repositories persist records through the configured driver.
  4. Signature verification is performed by the configured IPackSignatureVerifier; verification decisions affect pack status but do not load executable assemblies.
  5. The worker performs background pack maintenance and mirror work using the same core services and storage contracts.

Authentication & Authorization

PacksRegistry is an Authority resource server. Both halves of the identity are claim-derived — no raw header, query value, or body field is ever a tenancy or authorization source.

Authentication. Two paths converge on one scheme (StellaOpsBearer):

Authority:ResourceServer:Authority is required: outside Development/Testing the host aborts at startup without it (a missing issuer would otherwise surface as a 500 on every request, including anonymous /healthz, because UseAuthentication() initializes handlers before any endpoint runs). Dev/Testing fall back to the local-harness issuer; authorization is not relaxed.

Authorization. Every /api/v1 endpoint is gated by RequireAuthorization with a named policy that binds the canonical scopes from src/Authority/StellaOps.Authority/StellaOps.Auth.Abstractions/StellaOpsScopes.cs:

Policy (PacksRegistryPolicies)Scope requirementEndpoints
packsregistry.readpacks.read or packs.writeall reads (write implies read so a publisher can read back what it wrote)
packsregistry.writepacks.writeevery mutation, including the sensitive ones below

/api/v1/buildinfo is the one deliberate anonymous endpoint (platform-wide image provenance for the operator verify aggregator), alongside /healthz and the static OpenAPI stubs. A test (PacksRegistryAuthorizationTests.EveryApiEndpoint_CarriesAPacksRegistryAuthorizationPolicy) fails the build if any other endpoint ships ungated.

Tenancy. The data tenant comes exclusively from the validated stellaops:tenant claim (UseStellaOpsTenantMiddleware + TryRequireTenant). A body/query tenant that disagrees with the claim is rejected with 403 tenant_claim_mismatch; a request with no tenant claim is rejected with 400 tenant_missing and never defaulted.

Not authorization. The optional shared PacksRegistry:Auth:ApiKey (X-API-Key) carries no identity, tenant, or scope, and is skipped entirely when unset. It is a supplementary pre-shared secret only; it can never grant access on its own. PacksRegistry:Auth:AllowedTenants is likewise a coarse allow-list, not an authorization control.

Bypass networks are deliberately NOT configured for this service. StellaOpsBypassEvaluator grants a full authorization bypass to any request from a listed CIDR that carries no Authorization header — and gateway-proxied requests are precisely that shape. Listing the compose subnet would bypass the scope requirement on every gateway call and silently undo the scope gate. PacksRegistry has no in-cluster S2S HTTP caller that needs one.

API Surface

All routes are under /api/v1 (StellaOps.PacksRegistry.WebService/Program.cs).

MethodRoutePolicy
POST/packswrite
GET/packsread
GET/packs/{packId}read
GET/packs/{packId}/contentread
GET/packs/{packId}/provenanceread
GET/packs/{packId}/manifestread
POST/packs/{packId}/signaturewrite — signature rotation
POST/packs/re-envelopewrite — bulk re-envelope; accepts an operator private key in the body
POST/packs/{packId}/attestationswrite
GET/packs/{packId}/attestationsread
GET/packs/{packId}/attestations/{type}read
GET/packs/{packId}/parityread
POST/packs/{packId}/paritywrite
GET/packs/{packId}/lifecycleread
POST/packs/{packId}/lifecyclewrite
POST/export/offline-seedwrite — full-tenant content export
POST/mirrorswrite — mirror configuration
GET/mirrorsread
POST/mirrors/{id}/syncwrite
GET/compliance/summaryread

Non-/api/v1 surface: /healthz (anonymous), /openapi/packs.json and /openapi/pack-manifest.json (static stubs). There is no approval endpoint — lifecycle state set/get is the nearest surface — and no dedicated version-listing endpoint; GET /packs lists packs. See also api-reference.md and openapi/.

Compose Contract

The base compose seed root is read-only:

/app/etc/plugins/packsregistry/packs

It is mounted from:

devops/etc/plugins/packsregistry

Do not mount PacksRegistry content under /app/plugins. That path is reserved for signed executable plugin bundles owned by services with executable plugin contracts.

Persistence

PacksRegistry supports the same durability posture as the rest of Stella Ops: startup must converge the module schema, and pack content must survive service restart. File-system repository tests exist for deterministic local behavior, and Postgres/object-store drivers are selected through service configuration.

Test Expectations

Focused tests should target src/JobEngine/StellaOps.PacksRegistry/StellaOps.PacksRegistry.Tests/StellaOps.PacksRegistry.Tests.csproj.

Required behavioral coverage: