Stella Ops Signer Architecture (Relocated)
This module dossier is a stable redirect for older links, plus a source-grounded summary of the Signer runtime for readers who land here. The canonical architecture material lives in the Attestor dossier.
Signer source now lives under src/Attestor/StellaOps.Signer/ (the WebService, Core, Infrastructure, and Tests projects), with key-rotation and trust-anchor logic in src/Attestor/__Libraries/StellaOps.Signer.KeyManagement/. The canonical architecture material is maintained in the Attestor dossier:
- Signer relocation README
- Attestor architecture: Trust Domain Model (Sprint 204)
- Attestor architecture: PostgreSQL schema ownership (Signer
signerschema + KeyManagement boundary) - Attestor architecture: Security boundary — no-merge ADR
Do not add new standalone Signer architecture content here. Update the Attestor architecture dossier (and the relocation README) and keep this redirect current. The summary below is a source-grounded pointer for the many docs that still link here — it is not the authoritative dossier.
Runtime at a glance (source-grounded)
Verified against src/Attestor/StellaOps.Signer/ on this checkout.
- Docker image / base path:
stellaops/signer; HTTP surface is rooted at/api/v1/signer/(SignerEndpoints.MapSignerEndpoints). - Authentication: Authority resource-server auth in live hosts (
AddStellaOpsResourceServerAuthentication); a test bearer/scope handler is injected only in theTestingenvironment by the test harness (Program.cs). - Database: PostgreSQL schema
signer, auto-migrated on startup viaAddStartupMigrations(schemaName: "signer", moduleName: "Signer.KeyManagement", …)from theStellaOps.Signer.KeyManagementmigrations (Migrations/001_v1_signer_keymanagement_baseline.sql— the collapsed pre-1.0 baseline; the historical001_initial_schema.sql…004_key_audit_log_shape_fix.sqlare archived underMigrations/_archived/pre_1.0/and are not embedded — plusMigrations/002_operator_decision_envelopes.sql). Live tables:signer.key_history,signer.key_audit_log,signer.trust_anchors,signer.ceremonies,signer.ceremony_approvals,signer.ceremony_audit_log,signer.operator_decision_envelopes. A liveConnectionStrings:KeyManagementis required outside the test host harness; EF InMemory key-management storage is rejected. - Ceremony-quorum trust evidence is durable (SPRINT_20260712_003 EVI-2): on quorum,
CeremonyQuorumAttestorassembles the multi-signedoperator-decision@v1DSSE envelope and persists it throughPostgresOperatorDecisionEnvelopeStoreintosigner.operator_decision_envelopes(idempotent on(tenant_id, envelope_digest)).InMemoryOperatorDecisionEnvelopeStoreis now registered only when noConnectionStrings:KeyManagementexists (the test host) andSignerRuntimeGuardsrejects it outside Development/Testing. See operator-decision predicate — envelope persistence.
HTTP endpoints
Routes and policies as registered in Program.cs / the *Endpoints.cs files.
| Method & path | Policy (scope) | Source |
|---|---|---|
GET / | none (readiness banner) | Program.cs |
GET /healthz, GET /readyz | anonymous | Program.cs (SIGNER_HEALTH-01) |
GET /internal/plugins/status | anonymous | Program.cs:269 — crypto-provider pack readiness (provider names, pack status) |
POST /internal/plugins/probe | anonymous | Program.cs:297 — crypto-provider pack probe |
build-info endpoint (MapBuildInfoEndpoint) | anonymous | Program.cs:329 — version/build banner |
GET /openapi/* (MapOpenApi) | anonymous | Program.cs:255 — OpenAPI document |
POST /api/v1/signer/sign/dsse | Signer.Sign → signer:sign | SignerEndpoints |
POST /api/v1/signer/verify/dsse | Signer.Verify → signer:read | SignerEndpoints |
GET /api/v1/signer/verify/referrers?digest= | Signer.Verify → signer:read | SignerEndpoints |
POST /api/v1/anchors/{anchorId}/keys | KeyManagement → signer:rotate | KeyRotationEndpoints |
POST /api/v1/anchors/{anchorId}/keys/{keyId}/revoke | KeyManagement → signer:rotate | KeyRotationEndpoints |
GET /api/v1/anchors/{anchorId}/keys/{keyId}/validity | KeyManagement → signer:rotate | KeyRotationEndpoints |
GET /api/v1/anchors/{anchorId}/keys/history | KeyManagement → signer:rotate | KeyRotationEndpoints |
GET /api/v1/anchors/{anchorId}/keys/warnings | KeyManagement → signer:rotate | KeyRotationEndpoints |
POST /api/v1/ceremonies | ceremony:create → signer:sign | CeremonyEndpoints |
GET /api/v1/ceremonies | ceremony:read → signer:read | CeremonyEndpoints |
GET /api/v1/ceremonies/{ceremonyId} | ceremony:read → signer:read | CeremonyEndpoints |
POST /api/v1/ceremonies/{ceremonyId}/approve | ceremony:approve → signer:sign | CeremonyEndpoints |
POST /api/v1/ceremonies/{ceremonyId}/execute | ceremony:execute → signer:admin | CeremonyEndpoints |
DELETE /api/v1/ceremonies/{ceremonyId} | ceremony:cancel → signer:admin | CeremonyEndpoints |
Notes:
- The ceremony endpoints declare string policy names (
ceremony:read,ceremony:create, …) and the anchor endpoints declareKeyManagement. These are registered inProgram.csas legacy-alias policies alongside the canonicalSigner.*policy names, and both map to the scopes shown above. POST /verify/dsseverifies a complete Signer-produced bundle over DSSE PAE. The bundle carries the signer-selected provider and algorithm;IDsseVerifierresolves that registered provider and key id throughICryptoProviderRegistry. Raw-envelope input is retained only for the explicit Development/Testing HMAC signer, and that path is PAE-bound as well. This is Signer-key verification, not arbitrary Sigstore/certificate-chain/transparency-policy evaluation; the broader trust decision remains Attestor-owned.- In-toto statement version is selected from the normalized predicate type on every default signing backend. SLSA provenance v1 and
stella.ops/*predicates usehttps://in-toto.io/Statement/v1; legacy SLSA v0.2 and the currently mapped CycloneDX/SPDX/OpenVEX families retain Statement v0.1. KMS, keyless, and the Development/Testing HMAC harness must not produce different statement contracts for the same predicate solely because the signer backend changed. GET /verify/referrersresolves release-integrity trust for a scanner image digest viaIReleaseIntegrityVerifierand returns{ trusted, releaseSigner }.
Scopes
The Signer scope constants live in StellaOps.Auth.Abstractions/StellaOpsScopes.cs:
signer:read(SignerRead) — read Signer configuration/key metadata and perform DSSE verify / ceremony read.signer:sign(SignerSign) — create signatures, create/approve ceremonies.signer:rotate(SignerRotate) — key-rotation and trust-anchor management.signer:admin(SignerAdmin) — execute/cancel ceremonies; administrative control.
Configuration keys
Bound in Program.cs:
Authority:ResourceServer(resource-server auth; audiencesigner).ConnectionStrings:KeyManagement(PostgreSQL; required in live hosts).Signer:Entitlements,Signer:ReleaseVerification,Signer:Crypto,Signer:Dsse.Signer:Dsse:CadesBaselineBPfxPath— optional offline-only CAdES-B companion (PKCS#12/PFX). It does not provide qualified eIDAS/QSCD evidence and does not satisfy the T/LT/LTA profiles, which remain fail-closed (cadenced.cades.provider_pack_required).Router(Stella Router microservice registration;serviceName: "signer").
DSSE signing notes
- The DSSE sign endpoint runs
ISignerPipeline.SignAsync, which validates the proof-of-entitlement (PoE), sender binding (DPoP for JWT PoE, mTLS client cert for mTLS PoE), predicate, release integrity, quota, and provider resolution before emitting the DSSE envelope and an audit id. - The in-toto request predicate type must match the exact
PredicateTypesallowlist and the predicate payload must be a JSON object. Unsupported types and null/array/scalar payloads fail aspredicate_type_unsupportedorpredicate_invalidbefore entitlement, release-integrity, quota, provider, crypto, or audit side effects. - Signing mode is
kms(default) orkeyless; primary signatures are algorithm-pinned by the active crypto profile before provider lookup. - The CRA “cadenced” DSSE wrapper is opt-in via
options.cadenced=trueor areturnBundletoken containingcadenced, with CAdES profilesBaselineB/BaselineT/BaselineLT/BaselineLTA. - In local-runtime environments (
Development/Testing) the pipeline is replaced with a deterministicHmacDsseSignerharness; live hosts reject it.
Why the move
Signer, Attestor, and Provenance form the trust domain — the services responsible for cryptographic evidence production, transparency logging, and verification. Consolidating source ownership under src/Attestor/ makes trust-boundary responsibilities explicit while preserving runtime isolation between Signer key/state material and Attestor evidence state. See the no-merge decision ADR.
