Release Seal Aggregate API

POST /api/v1/release-orchestrator/releases/seal creates or re-reads a sealed release in one deterministic call for operator and fixture harnesses that should not manage the create, ready, approval-truth, and deploy-dispatch calls separately.

Authorization

Requires ReleaseOrchestratorPolicies.ReleaseApprove (release:publish). The aggregate uses the strictest scope from the composed release lifecycle operations and does not accept release:read or release:write alone.

The route is tenant-scoped through the standard Release Orchestrator tenant filter. Tenant resolution follows the shared auth integration tenant resolver and accepts the normal Stella Ops tenant headers/claims.

Request

{
  "releaseRequest": {
    "name": "tier4-golden-001",
    "version": "v0.0.1-golden",
    "targetEnvironment": "tenant-tier4-w1-run-a",
    "deploymentStrategy": "rolling",
    "components": [
      {
        "name": "checkout-api",
        "imageRef": "registry.local/stellaops/checkout-api",
        "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "tag": "v0.0.1-golden",
        "version": "v0.0.1-golden",
        "type": "image"
      }
    ]
  },
  "evidenceBundleId": "bundle-golden-001",
  "fixtureId": "tier4-golden"
}

releaseRequest is create-release compatible, with components added for the aggregate because the canonical ready transition rejects componentless releases.

The idempotency key is the SHA-256 hash of normalized (name, version, tenant). Reposting the same release name/version for the same tenant returns the previously sealed release without dispatching a second deployment.

Response

First seal returns 202 Accepted; an idempotent re-seal returns 200 OK.

{
  "status": "release-seal-accepted",
  "releaseId": "rel-...",
  "sealedAt": "2026-05-18T10:30:00Z",
  "deploymentId": "dep-...",
  "evidenceBundleId": "bundle-golden-001",
  "dsseDigest": "sha256:...",
  "tenant": "tenant-release-seal",
  "fixtureId": "tier4-golden",
  "ledgerStatus": "deployed"
}

status is a stable contract string. dsseDigest is the SHA-256 digest of the canonical release-seal evidence packet persisted as recorded, integrity-checked release evidence and bound to evidenceBundleId. The aggregate does not sign the packet, so it is not verified evidence.

Errors

StatusCondition
400Missing request body, releaseRequest, releaseRequest.name, releaseRequest.version, releaseRequest.targetEnvironment, evidenceBundleId, or required component fields.
403Caller lacks release:publish.
409Existing release truth, approval truth, gate truth, or evidence truth blocks deployment dispatch.

State Effects

On first seal the endpoint:

  1. creates or resumes the release row,
  2. upserts deterministic component rows,
  3. drives the existing Draft -> Ready validator,
  4. writes recorded release-seal evidence (canonical SHA-256 integrity, no signature and therefore not verified),
  5. writes approved approval and passing gate-decision truth for the publish-authorized aggregate,
  6. dispatches deployment through the same deploy-engine/fallback boundary as release deploy,
  7. records the release as deployed and appends a sealed release event.

The existing rollback endpoint remains unchanged: POST /api/v1/release-orchestrator/releases/{id}/rollback.