14 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	component_architecture_attestor.md — Stella Ops Attestor (2025Q4)
Scope. Implementation‑ready architecture for the Attestor: the service that submits DSSE envelopes to Rekor v2, retrieves/validates inclusion proofs, caches results, and exposes verification APIs. It accepts DSSE only from the Signer over mTLS, enforces chain‑of‑trust to Stella Ops roots, and returns
{uuid, index, proof, logURL}to calling services (Scanner.WebService for SBOMs; backend for final reports; Excititor exports when configured).
0) Mission & boundaries
Mission. Turn a signed DSSE envelope from the Signer into a transparency‑logged, verifiable fact with a durable, replayable proof (Merkle inclusion + (optional) checkpoint anchoring). Provide fast verification for downstream consumers and a stable retrieval interface for UI/CLI.
Boundaries.
- Attestor does not sign; it must not accept unsigned or third‑party‑signed bundles.
- Attestor does not decide PASS/FAIL; it logs attestations for SBOMs, reports, and export artifacts.
- Rekor v2 backends may be local (self‑hosted) or remote; Attestor handles both with retries, backoff, and idempotency.
1) Topology & dependencies
Process shape: single stateless service stellaops/attestor behind mTLS.
Dependencies:
- Signer (caller) — authenticated via mTLS and Authority OpToks.
- Rekor v2 — tile‑backed transparency log endpoint(s).
- MinIO (S3) — optional archive store for DSSE envelopes & verification bundles.
- MongoDB — local cache of {uuid, index, proof, artifactSha256, bundleSha256}; job state; audit.
- Redis — dedupe/idempotency keys and short‑lived rate‑limit buckets.
- Licensing Service (optional) — “endorse” call for cross‑log publishing when customer opts‑in.
Trust boundary: Only the Signer is allowed to call submission endpoints; enforced by mTLS peer cert allowlist + aud=attestor OpTok.
2) Data model (Mongo)
Database: attestor
Collections & schemas
- 
entries{ _id: "<rekor-uuid>", artifact: { sha256: "<sha256>", kind: "sbom|report|vex-export", imageDigest?, subjectUri? }, bundleSha256: "<sha256>", // canonicalized DSSE index: <int>, // log index/sequence if provided by backend proof: { // inclusion proof checkpoint: { origin, size, rootHash, timestamp }, inclusion: { leafHash, path[] } // Merkle path (tiles) }, log: { url, logId? }, createdAt, status: "included|pending|failed", signerIdentity: { mode: "keyless|kms", issuer, san?, kid? } }
- 
dedupe{ key: "bundle:<sha256>", rekorUuid, createdAt, ttlAt } // idempotency key
- 
audit{ _id, ts, caller: { cn, mTLSThumbprint, sub, aud }, // from mTLS + OpTok action: "submit|verify|fetch", artifactSha256, bundleSha256, rekorUuid?, index?, result, latencyMs, backend }
Indexes:
- entrieson- artifact.sha256,- bundleSha256,- createdAt, and- {status:1, createdAt:-1}.
- dedupe.keyunique (TTL 24–48h).
- audit.tsfor time‑range queries.
3) Input contract (from Signer)
Attestor accepts only DSSE envelopes that satisfy all of:
- mTLS peer certificate maps to signerservice (CA‑pinned).
- Authority OpTok with aud=attestor,scope=attestor.write, DPoP or mTLS bound.
- DSSE envelope is signed by the Signer’s key (or includes a Fulcio‑issued cert chain) and chains to configured roots (Fulcio/KMS).
- Predicate type is one of Stella Ops types (sbom/report/vex‑export) with valid schema.
- subject[*].digest.sha256is present and canonicalized.
Wire shape (JSON):
{
  "bundle": { "dsse": { "payloadType": "application/vnd.in-toto+json", "payload": "<b64>", "signatures": [ ... ] },
              "certificateChain": [ "-----BEGIN CERTIFICATE-----..." ],
              "mode": "keyless" },
  "meta": {
    "artifact": { "sha256": "<subject sha256>", "kind": "sbom|report|vex-export", "imageDigest": "sha256:..." },
    "bundleSha256": "<sha256 of canonical dsse>",
    "logPreference": "primary",               // "primary" | "mirror" | "both"
    "archive": true                           // whether Attestor should archive bundle to S3
  }
}
4) APIs
4.1 Submission
POST /api/v1/rekor/entries  (mTLS + OpTok required)
- 
Body: as above. 
- 
Behavior: - Verify caller (mTLS + OpTok).
- Validate DSSE bundle (signature, cert chain to Fulcio/KMS; DSSE structure; payloadType allowed).
- Idempotency: compute bundleSha256; checkdedupe. If present, return existingrekorUuid.
- Submit canonicalized bundle to Rekor v2 (primary or mirror according to logPreference).
- Retrieve inclusion proof (blocking until inclusion or up to proofTimeoutMs); if backend returns promise only, returnstatus=pendingand retry asynchronously.
- Persist entriesrecord; archive DSSE to S3 ifarchive=true.
 
- 
Response 200: { "uuid": "…", "index": 123456, "proof": { "checkpoint": { "origin": "rekor@site", "size": 987654, "rootHash": "…", "timestamp": "…" }, "inclusion": { "leafHash": "…", "path": ["…","…"] } }, "logURL": "https://rekor…/api/v2/log/…/entries/…", "status": "included" }
- 
Errors: 401 invalid_token,403 not_signer|chain_untrusted,409 duplicate_bundle(with existinguuid),502 rekor_unavailable,504 proof_timeout.
4.2 Proof retrieval
GET /api/v1/rekor/entries/{uuid}
- Returns entriesrow (refreshes proof from Rekor if stale/missing).
- Accepts ?refresh=trueto force backend query.
4.3 Verification (third‑party or internal)
POST /api/v1/rekor/verify
- 
Body (one of): - { "uuid": "…" }
- { "bundle": { …DSSE… } }
- { "artifactSha256": "…" }(looks up most recent entry)
 
- 
Checks: - Bundle signature → cert chain to Fulcio/KMS roots configured.
- Inclusion proof → recompute leaf hash; verify Merkle path against checkpoint root.
- Optionally verify checkpoint against local trust anchors (if Rekor signs checkpoints).
- Confirm subject.digest matches caller‑provided hash (when given).
 
- 
Response: { "ok": true, "uuid": "…", "index": 123, "logURL": "…", "checkedAt": "…" }
4.4 Batch submission (optional)
POST /api/v1/rekor/batch accepts an array of submission objects; processes with per‑item results.
5) Rekor v2 driver (backend)
- 
Canonicalization: DSSE envelopes are normalized (stable JSON ordering, no insignificant whitespace) before hashing and submission. 
- 
Transport: HTTP/2 with retries (exponential backoff, jitter), budgeted timeouts. 
- 
Idempotency: if backend returns “already exists,” map to existing uuid.
- 
Proof acquisition: - In synchronous mode, poll the log for inclusion up to proofTimeoutMs.
- In asynchronous mode, return pendingand schedule a proof fetcher job (Mongo job doc + backoff).
 
- In synchronous mode, poll the log for inclusion up to 
- 
Mirrors/dual logs: - When logPreference="both", submit to primary and mirror; store both UUIDs (primary canonical).
- Optional cloud endorsement: POST to the Stella Ops cloud /attest/endorsewith{uuid, artifactSha256}; store returned endorsement id.
 
- When 
6) Security model
- 
mTLS required for submission from Signer (CA‑pinned). 
- 
Authority token with aud=attestorand DPoP/mTLS binding must be presented; Attestor verifies both.
- 
Bundle acceptance policy: - DSSE signature must chain to the configured Fulcio (keyless) or KMS/HSM roots.
- SAN (Subject Alternative Name) must match Signer identity policy (e.g., urn:stellaops:signeror pinned OIDC issuer).
- Predicate predicateTypemust be on allowlist (sbom/report/vex-export).
- subject.digest.sha256values must be present and well‑formed (hex).
 
- 
No public submission path. Never accept bundles from untrusted clients. 
- 
Rate limits: per mTLS thumbprint/license (from Signer‑forwarded claims) to avoid flooding the log. 
- 
Redaction: Attestor never logs secret material; DSSE payloads should be public by design (SBOMs/reports). If customers require redaction, enforce policy at Signer (predicate minimization) before Attestor. 
7) Storage & archival
- 
Entries in Mongo provide a local ledger keyed by rekorUuidand artifact sha256 for quick reverse lookups.
- 
S3 archival (if enabled): s3://stellaops/attest/ dsse/<bundleSha256>.json proof/<rekorUuid>.json bundle/<artifactSha256>.zip # optional verification bundle
- 
Verification bundles (zip): - DSSE (*.dsse.json), proof (*.proof.json),chain.pem(certs),README.txtwith verification steps & hashes.
 
- DSSE (
8) Observability & audit
Metrics (Prometheus):
- attestor.submit_total{result,backend}
- attestor.submit_latency_seconds{backend}
- attestor.proof_fetch_total{result}
- attestor.verify_total{result}
- attestor.dedupe_hits_total
- attestor.errors_total{type}
Tracing:
- Spans: validate,rekor.submit,rekor.poll,persist,archive,verify.
Audit:
- Immutable auditrows (ts, caller, action, hashes, uuid, index, backend, result, latency).
9) Configuration (YAML)
attestor:
  listen: "https://0.0.0.0:8444"
  security:
    mtls:
      caBundle: /etc/ssl/signer-ca.pem
      requireClientCert: true
    authority:
      issuer: "https://authority.internal"
      jwksUrl: "https://authority.internal/jwks"
      requireSenderConstraint: "dpop"   # or "mtls"
    signerIdentity:
      mode: ["keyless","kms"]
      fulcioRoots: ["/etc/fulcio/root.pem"]
      allowedSANs: ["urn:stellaops:signer"]
      kmsKeys: ["kms://cluster-kms/stellaops-signer"]
  rekor:
    primary:
      url: "https://rekor-v2.internal"
      proofTimeoutMs: 15000
      pollIntervalMs: 250
      maxAttempts: 60
    mirror:
      enabled: false
      url: "https://rekor-v2.mirror"
  mongo:
    uri: "mongodb://mongo/attestor"
  s3:
    enabled: true
    endpoint: "http://minio:9000"
    bucket: "stellaops"
    prefix: "attest/"
    objectLock: "governance"
  redis:
    url: "redis://redis:6379/2"
  quotas:
    perCaller:
      qps: 50
      burst: 100
10) End‑to‑end sequences
A) Submit & include (happy path)
sequenceDiagram
  autonumber
  participant SW as Scanner.WebService
  participant SG as Signer
  participant AT as Attestor
  participant RK as Rekor v2
  SW->>SG: POST /sign/dsse (OpTok+PoE)
  SG-->>SW: DSSE bundle (+certs)
  SW->>AT: POST /rekor/entries (mTLS + OpTok)
  AT->>AT: Validate DSSE (chain to Fulcio/KMS; signer identity)
  AT->>RK: submit(bundle)
  RK-->>AT: {uuid, index?}
  AT->>RK: poll inclusion until proof or timeout
  RK-->>AT: inclusion proof (checkpoint + path)
  AT-->>SW: {uuid, index, proof, logURL}
B) Verify by artifact digest (CLI)
sequenceDiagram
  autonumber
  participant CLI as stellaops verify
  participant SW as Scanner.WebService
  participant AT as Attestor
  CLI->>SW: GET /catalog/artifacts/{id}
  SW-->>CLI: {artifactSha256, rekor: {uuid}}
  CLI->>AT: POST /rekor/verify { uuid }
  AT-->>CLI: { ok: true, index, logURL }
11) Failure modes & responses
| Condition | Return | Details | ||
|---|---|---|---|---|
| mTLS/OpTok invalid | 401 invalid_token | Include WWW-AuthenticateDPoP challenge when applicable | ||
| Bundle not signed by trusted identity | 403 chain_untrusted | DSSE accepted only from Signer identities | ||
| Duplicate bundle | 409 duplicate_bundle | Return existing uuid(idempotent) | ||
| Rekor unreachable/timeout | 502 rekor_unavailable | Retry with backoff; surface Retry-After | ||
| Inclusion proof timeout | 202 accepted | status=pending, background job continues to fetch proof | ||
| Archive failure | 207 multi-status | Entry recorded; archive will retry asynchronously | ||
| Verification mismatch | 400 verify_failed | Include reason: chain | leafHash | rootMismatch | 
12) Performance & scale
- 
Stateless; scale horizontally. 
- 
Targets: - Submit+proof P95 ≤ 300 ms (warm log; local Rekor).
- Verify P95 ≤ 30 ms from cache; ≤ 120 ms with live proof fetch.
- 1k submissions/minute per replica sustained.
 
- 
Hot caches: dedupe(bundle hash → uuid), recententriesby artifact sha256.
13) Testing matrix
- Happy path: valid DSSE, inclusion within timeout.
- Idempotency: resubmit same bundleSha256→ sameuuid.
- Security: reject non‑Signer mTLS, wrong aud, DPoP replay, untrusted cert chain, forbidden predicateType.
- Rekor variants: promise‑then‑proof, proof delayed, mirror dual‑submit, mirror failure.
- Verification: corrupt leaf path, wrong root, tampered bundle.
- Throughput: soak test with 10k submissions; latency SLOs, zero drops.
14) Implementation notes
- Language: .NET 10 minimal API; HttpClientwith sockets handler tuned for HTTP/2.
- JSON: canonical writer for DSSE payload hashing.
- Crypto: use BouncyCastle/System.Security.Cryptography; PEM parsing for cert chains.
- Rekor client: pluggable driver; treat backend errors as retryable/non‑retryable with granular mapping.
- Safety: size caps on bundles; decompress bombs guarded; strict UTF‑8.
- CLI integration: stellaops verify attestation <uuid|bundle|artifact>calls/rekor/verify.
15) Optional features
- Dual‑log write (primary + mirror) and cross‑log proof packaging.
- Cloud endorsement: send {uuid, artifactSha256}to Stella Ops cloud; store returned endorsement id for marketing/chain‑of‑custody.
- Checkpoint pinning: periodically pin latest Rekor checkpoints to an external audit store for independent monitoring.