Container Deployment Guide — Aggregation-Only Contract (AOC)
Audience: DevOps Guild and platform operators deploying Stella Ops services. Purpose: Cover the AOC-specific deployment considerations for Concelier and Excititor: the always-on write guard and its startup migrations, the pre-flight duplicate audit, AOC metrics, and verifier identities for the HTTP
/aoc/verifyendpoint. (Earlier drafts described “guard environment flags” — those do not exist; see § 2.)
This guide supplements existing deployment manuals with AOC-specific configuration. It assumes familiarity with the supported Compose and Offline Kit manifests described in docs/operations/devops/architecture.md. Kubernetes and Helm are not supported deployment targets.
Reconciliation note (verified against
src/on 2026-05-30). The AOC guard, its metrics, and the/aoc/verifysurface are all implemented and live; however, several operational details earlier drafts described — a separately-applied CHECK-constraint script, a family ofAOC_*environment toggles, anadvisory→advisory_backup_*view swap, and a dedicatedstella-aoc-verifyOAuth client wired to the CLI — are not present in the codebase. Each such claim is annotated inline below. Authoritative behaviour: the guard ships always-on inside Concelier/Excititor (no enablement flag), AOC field/idempotency enforcement is part of the service’s auto-applied startup migrations (AGENTS.md §2.7), and thestella aoc verifyCLI reads PostgreSQL directly (it does not authenticate through Authority).
1 · Schema constraint enablement
1.1 PostgreSQL constraints
CORRECTED — no separate constraint script exists. There is no
apply-aoc-constraints.sqlanywhere in the repository, and AOC enforcement is not applied as standalone DB CHECK constraints. Per the database auto-migration requirement (AGENTS.md §2.7), each service converges its own schema on startup from embedded SQL. The raw-advisory table and its idempotency guarantee ship in migrationsrc/Concelier/__Libraries/StellaOps.Concelier.Persistence/Migrations/030_add_advisory_raw_store.sql, which createsvuln.advisory_rawwith a unique dedupe indexuq_advisory_raw_content ON vuln.advisory_raw(tenant_id, source_vendor, upstream_id, content_hash). The VEX raw store and its lineage live undersrc/Concelier/__Libraries/StellaOps.Excititor.Persistence/Migrations/(e.g.011_excititor_raw_lineage.sql,013_vex_raw_source_revisions.sql). Operators do not run a manual constraint step — deploying the current Concelier/Excititor images is sufficient.
AOC field validation (required keys, forbidden keys, signature metadata) is enforced in-process by the
StellaOps.Aocwrite guard (AocWriteGuard+AdvisoryRawWriteGuard/VexRawWriteGuard), not at the DB layer. The guard’s default required top-level document fields aretenant,source,upstream,content, andlinkset(seesrc/Aoc/__Libraries/StellaOps.Aoc/AocGuardOptions.cs).CORRECTED — required-field list. Earlier drafts listed only
tenant, source, upstream, linksetand described DB-level enforcement. The guard’sRequiredTopLevelFieldsdefault also includescontent, and enforcement is application-side, not a DB CHECK.Before relying on the dedupe index on an existing (pre-migration) database, run the duplicate audit helper to confirm no conflicting raw advisories remain:
# Path corrected: the script lives under devops/tools/ops-scripts/, not ops/devops/scripts/. # LIMIT is read as a psql variable (default 50 if unset); 200 widens the report. psql -d concelier -v LIMIT=200 -f devops/tools/ops-scripts/check-advisory-raw-duplicates.sqlResolve any reported rows prior to rollout. (Verified:
devops/tools/ops-scripts/check-advisory-raw-duplicates.sqlexists;ops/devops/scripts/does not.)
1.2 Migration order
Run the duplicate audit (§ 1.1) in a maintenance window and resolve any reported groups.
Roll out the current Concelier/Excititor images. The startup migrations create/converge the raw tables and dedupe index, and the AOC write guard is registered unconditionally —
AddConcelierAocGuards()/AddExcititorAocGuards()run on every boot (seesrc/Concelier/StellaOps.Concelier.WebService/Program.cs:556andsrc/Concelier/StellaOps.Excititor.WebService/Program.cs:110). There is noAOC_GUARD_ENABLEDflag to set — the guard cannot be toggled off via configuration.Run a pre-flight check (
stella aoc verify --dry-runvalidates configuration without touching the database) before resuming production ingestion.NOT IMPLEMENTED —
stella sources ingest --dry-run. No such command/fixture was found; use the AOC verify dry-run for a pre-flight check instead.
1.3 Supersedes backfill verification
Duplicate audit: Confirm
psql -d concelier -v LIMIT=200 -f devops/tools/ops-scripts/check-advisory-raw-duplicates.sqlreports no conflicts before restarting Concelier with the new migrations.Lineage spot-check: Inspect a sample set to confirm append-only chaining. The real table is
vuln.advisory_raw; the supersede pointer column issupersedes_idand the identity columns aretenant_id/source_vendor/upstream_id:SELECT id, supersedes_id FROM vuln.advisory_raw WHERE upstream_id IS NOT NULL ORDER BY tenant_id, source_vendor, upstream_id, retrieved_at LIMIT 5;Each revision should reference the previous
idviasupersedes_id(orNULLfor the first revision). Record findings in the change ticket before proceeding to production.REMOVED —
advisory→advisory_backup_20251028view check. Earlier drafts told operators to validate that anadvisoryview points to a table namedadvisory_backup_20251028. Neither the view nor the backup table exists anywhere insrc/; the check was orphaned and has been deleted. Column names were also corrected: the table isvuln.advisory_raw(schema-qualified), the supersede column issupersedes_id(notsupersedes), and the tenant column istenant_id(nottenant).
2 · Container environment flags
NOT IMPLEMENTED — the
AOC_*environment variables below do not exist. A repo-wide search (src/,devops/) finds no reference toAOC_GUARD_ENABLED,AOC_ALLOW_SUPERSEDES_RETROFIT,AOC_METRICS_ENABLED,AOC_TENANT_HEADER, orAOC_VERIFIER_USERoutside this document. Setting them has no effect. The table is retained below for historical reference only, annotated with the actual mechanism.
| Variable (NOT WIRED) | Doc default | Actual mechanism in src/ |
|---|---|---|
AOC_GUARD_ENABLED | true | No toggle. The guard is registered unconditionally via AddConcelierAocGuards() / AddExcititorAocGuards() and always intercepts raw writes. There is no supported way to disable it through configuration. |
AOC_ALLOW_SUPERSEDES_RETROFIT | false | No such flag. Supersede lineage is recorded via the supersedes_id column on vuln.advisory_raw; there is no retrofit toggle. |
AOC_METRICS_ENABLED | true | No toggle. Metrics ingestion_write_total and aoc_violation_total are always emitted (src/__Libraries/StellaOps.Ingestion.Telemetry/IngestionTelemetry.Metrics.cs; Concelier Diagnostics/IngestionMetrics.cs). |
AOC_TENANT_HEADER | X-StellaOps-TenantId | The header value X-StellaOps-TenantId is correct, but it is a hardcoded constant (TenantHeaderName in src/Concelier/StellaOps.Excititor.WebService/Program.Helpers.cs:17), not configurable via env var. (A legacy X-Stella-Tenant constant also exists.) |
AOC_VERIFIER_USER | stella-aoc-verify | Not read anywhere. The HTTP verify endpoint authorizes by scope (advisory:read + aoc:verify), not by a named user env var; the CLI verify path uses no Authority identity at all (see § 3). |
Operator takeaway: no AOC-specific environment variables need to be added to Concelier/Excititor. Deploying the current images enables the always-on guard and metrics. Multi-tenant routing already supplies
X-StellaOps-TenantIdfrom the Gateway.
3 · Verifier identity
PARTIALLY IMPLEMENTED — verify which surface you are securing. The two AOC verification paths differ in their auth model:
- HTTP
/aoc/verify(ConcelierProgram.cs:3173; Excititor has an equivalent) does enforce scopes. Its policyConcelier.Aoc.Verifyrequires bothadvisory:readandaoc:verify(Program.cs:1138). All three scopes referenced below —aoc:verify,advisory:read,vex:read— are real, canonical scopes (src/Authority/StellaOps.Authority/StellaOps.Auth.Abstractions/StellaOpsScopes.cs). A dedicated read-only client for Console / API consumers of this endpoint is therefore a sound design.- CLI
stella aoc verify(src/Cli/__Libraries/StellaOps.Cli.Plugins.Aoc/AocCliCommandModule.cs) does not authenticate through Authority at all. It connects directly to PostgreSQL (the--postgres/-pconnection string is a required option) and readsvuln.advisory_rawitself. So the claim that thestella-aoc-verifyclient is “bound tostella aoc verifyCI jobs” is incorrect — CI jobs running the CLI need a Postgres connection string, not an OAuth client.No client named
stella-aoc-verifyis seeded anywhere insrc/ordevops/today; the block below is a recommended (draft) Authority bootstrap entry for the HTTP endpoint, not a description of existing configuration.
- (Draft / recommended) Create a dedicated read-only client for the HTTP verify endpoint via Authority bootstrap:
clients:
- clientId: stella-aoc-verify
grantTypes: [client_credentials]
# advisory:read + aoc:verify are both required by the Concelier.Aoc.Verify policy;
# add vex:read for the Excititor verify endpoint.
scopes: [aoc:verify, advisory:read, vex:read]
tenants: [default]
Store credentials in the approved host secret store or Compose environment secret path.
Bind credentials to Console / API consumers of
/aoc/verify. For CLI-based CI jobs, provision a scoped, read-only PostgreSQL connection string instead and pass it viastella aoc verify --postgres ....Rotate per the Authority key-rotation runbook.
CORRECTED PATH —
ops/authority-key-rotation.mddoes not exist. Noops/directory exists at the repo root. Use the canonical Authority key-rotation runbook instead:docs/operations/key-rotation-runbook.md(module-level detail:docs/modules/authority/operations/key-rotation.md).
4 · Deployment steps
Pre-checks: Confirm database backups, alerting in maintenance mode, and staging environment validated.
Audit duplicates: Run the duplicate-audit helper (there is no separate “validator” script to apply — schema/idempotency converge via the service’s startup migrations) per § 1.1.
Update manifests: No AOC-specific environment variables are required (see Section 2). Confirm the Gateway forwards
X-StellaOps-TenantIdto Concelier/Excititor.Redeploy services: Restart Concelier/Excititor through Docker Compose or the host service manager. The always-on AOC guard and metrics take effect on boot. Monitor
ingestion_write_totalfor steady throughput.Seed verifier (HTTP endpoint only): If you secure
/aoc/verifyfor Console/API consumers, deploy the read-only client from Section 3 and store credentials. (Not needed for the CLI path.)Run verification: Execute
stella aoc verify --since <ISO-8601 timestamp> --postgres <conn-string>and ensure exit code0.CORRECTED —
--sinceand--postgres.--sincerequires an ISO-8601 timestamp (e.g.2026-05-29T00:00:00Z); relative durations such as24hare rejected by the parser (AocVerifyOptionsParser.TryParseSince). The--postgresconnection string is a required option — the CLI queries PostgreSQL directly and does not call the HTTP endpoint. (The HTTP/aoc/verifyendpoint, by contrast, accepts asincetimestamp in the request body and defaults to a 24-hour window when omitted.)Update dashboards: Point Grafana panels to the metrics (
ingestion_write_total,aoc_violation_total).Record handoff: Capture console screenshots and verification logs for release notes.
5 · Offline Kit updates
NOT IMPLEMENTED —
offline-kit/scripts/apply-aoc-validators.js. No such file exists in the repository, and there is no separate validator-script step for AOC (enforcement is in-process + auto-migrated, per § 1.1). Do not ship or reference this script.
- The Offline Kit may bundle the read-only duplicate-audit helper (
devops/tools/ops-scripts/check-advisory-raw-duplicates.sql) for air-gapped pre-flight checks. - Include pre-generated verification reports for air-gapped deployments (output of
stella aoc verify --output ...). - Document the offline CLI workflow in the bundle README referencing
docs/modules/cli/guides/cli-reference.md. - For the CLI verify path in an air-gapped tenant, supply a scoped, read-only PostgreSQL connection string (rotated during bundle refresh). If you additionally expose the HTTP
/aoc/verifyendpoint offline, scope the § 3 client to the offline tenant.
6 · Rollback plan
CORRECTED — the AOC guard cannot be disabled by configuration. There is no
AOC_GUARD_ENABLEDflag and no--removevalidator script (see § 1.1 and § 2). The guard is always-on for the deployed image; “rollback” means redeploying a prior image build that predates the guard, not flipping an env var. Forward-only migrations apply (ADR-004) — DB-level rollback is recovered by PostgreSQL snapshot restore, not by dropping constraints.
- Roll back the image: Redeploy the previously-pinned (digest-addressed) Concelier/Excititor image if the guard behaviour must be reverted. There is no in-place disable.
- Restore schema if needed: Recover the database from a PostgreSQL snapshot per the migration-recovery runbook; migrations are forward-only (ADR-004).
- Pause verification jobs to prevent noise.
- Investigate and remediate upstream issues before redeploying the guard-enabled image.
7 · References
CORRECTED LINKS — relative paths were broken. This file lives at
docs/operations/deployment/, so each../reachesdocs/operations/. The prior links (../aoc/…,../security/…,../observability/…,../modules/…) resolved to non-existent paths. Targets and depth are fixed below (verified to exist on disk 2026-05-31).
- Aggregation-Only Contract reference
- Authority scopes & tenancy
- Observability guide
- CLI AOC commands
- Concelier architecture
- Excititor architecture
8 · Compliance checklist
- [ ] Duplicate-audit helper (
devops/tools/ops-scripts/check-advisory-raw-duplicates.sql) documented for online/offline deployments. - [ ] Operators understand the AOC guard, metrics, and tenant header are always-on / hardcoded (no
AOC_*env vars to set — § 2). - [ ] Read-only verifier client documented for the HTTP
/aoc/verifyendpoint; CLI Postgres-connection path documented separately (§ 3). - [ ] Offline kit instructions reference real scripts only (no
apply-aoc-validators.js). - [ ] Rollback procedure (image roll-back + snapshot restore; no in-place guard disable) captured.
- [ ] Cross-links to AOC docs, Authority scopes, and observability guides present.
- [ ] DevOps Guild sign-off tracked (owner: @devops-guild).
Last updated: 2026-05-30 — deep doc/code reconciliation against src/ (Concelier/Excititor AOC guard, StellaOps.Aoc, scope catalog, CLI AOC plugin). Original draft: 2025-10-26, Sprint 19).
