Integrations + registry-token consolidation — S0 target design (SPRINT_20260722_024 INT-1)
Status: APPROVED (owner, 2026-08-03, ledger round 88 ruling C), then RE-SCOPED by ADR-041 OD-2 (2026-08-04). Legwork completed 2026-08-03 — the zot auth-flow map, the exact-type pin inventory, and the TokenService key/pin check, each verified against
src//devops/. Verification base: the sprint’s recorded digs (round 17 + the 2026-07-23 DC-30 decision), the live migration baselines (re-enumerated), and the 2026-08-03 build-boundary register.⚠ OD-2 RE-SCOPE — read before using §1/§2/§4a/§4b. The registry TokenService does not fold into Integrations. It joins the Registry family (
SPRINT_20260803_004), which gets its own databasestellaops_registry(ADR-041 D2). Everything in this document about the token service — the role container, theregistry_tokenschema, the zot auth flow, §4a and §4b — is retained as the analysis that produced that ruling, not as this program’s build scope. What Integrations still owns: theintegrationsschema, the DC-15 Scanner-edge extraction, and the X12 seams. The consolidation-design sections below are annotated where the re-scope bites.INT-3/INT-3b implementation notes — two corrections this document owed, both found while building the baseline and now closed in source: (a) the table inventory omitted the shared credential-store
cryptoschema that integrations-web also converges; (b) §4 catalogued only the OUTBOUND cross-schema reads — the INBOUND Scanner discovery poll is now an authenticated retained owner feed.INT-6/INT-7 closeout (2026-08-24). The own-database window executed on 2026-08-23.
StellaOps.Integrations.Persistenceis now the single canonical source/migration authority; the temporary.Persistence.Consolidatedproject anddocker-compose.integrations-consolidated.ymlstaging overlay are retired. Canonical compose carries the own DSN, Scanner projection/auth settings, and the explicit separate Policy DSN. The old SQL is archived and non-embedded; no legacy schema/database was dropped.
1. Current → target shape
| Current | Target | |
|---|---|---|
| Deployables | integrations-web, registry token service (src/Registry/StellaOps.Registry.TokenService) | one Integrations family with the token issuer as an availability-isolated role container (owner guardrail: its own healthcheck + restart policy — a webhook storm in integrations-web must never affect token issuance) |
| Persistence | integrations schema (2 tables) + TokenService tables (plan_audit, plan_rules — live home confirmed: own registry_token schema, §2) | one fresh 001_* baseline, own database + fail-closed env per the recipe |
2. Table inventory + dispositions (P13)
Tiny data footprint — this program’s work is topology + seams, not data volume.
| Table | Purpose | P13 class | Disposition |
|---|---|---|---|
integrations.integrations | integration definitions | operational | carry |
integrations.registry_image_discoveries | registry image discovery feeding Scanner auto-scan | operational | carry; every new row appends integrations.registry-image.discovered atomically and Scanner consumes the retained owner feed |
integrations.registry_image_scan_projections | local current state from Scanner’s scanner.scan.completed stream | rebuildable | live after INT-6; carried by canonical compose after INT-7; delete + full replay on producer epoch change; raw payload retained for additive fields |
crypto.* (KEK ledger, re-seal progress, secret provider/store) | shared credential-store schema that seals this service’s inline credentials | operational | follows the service into its own database — see below |
eventing.* (inbox, checkpoints, leases) | shared P6 reliability state for the Scanner projection | shared lineage: windowed / source-of-truth / ephemeral-operational by table | converges in this service’s own database; no parallel Integrations-specific inbox or lease lineage |
plan_audit, plan_rules (TokenService) | zot token plan rules + audit | operational/audit | LEFT this program (OD-2). Home is the Registry family’s own database stellaops_registry (SPRINT_20260803_004 SR-2/SR-5). The 2026-08-03 finding that its live home is its own clean registry_token schema still stands and transfers with it. |
The crypto schema was missing from this inventory (found at INT-3, 2026-08-04). integrations-web wires three startup migration hosts: its integrations baseline, the shared credential-store baseline through AddCredentialStorePersistence, and the P6 eventing baseline. The crypto schema is shared-library-owned, not family-owned — src/__Libraries/StellaOps.Cryptography.CredentialStore.Persistence owns the four-table baseline; ReSeal owns no migration. It is therefore NOT re-created by the Integrations baseline, but it must converge into the same database: integrations.integrations.credential_kek_version references crypto.kek_versions.version, so a credential sealed under a KEK version the database cannot resolve is unopenable. Platform and ReleaseOrchestrator wire the same shared schema into their own databases; each gets its own copy, which is correct — the KEK ledger is per-database custody state, not a shared registry.
The S9 window (INT-6) moved two existing schemas’ worth of state for this family — the measured 216 kB integrations schema and this service’s classified crypto slice — and converged the fresh shared eventing schema beside them for the retained-feed consumer.
Baseline shape as built (INT-3, extended by INT-3b): the integrations schema is the two carried operational tables plus one rebuildable Scanner projection and the migration ledger. The baseline reproduces the FINAL shape (pre-1.0 collapsed baseline + migrations 002–006) with no legacy history and no backfill steps; in particular the tightened credential-backend invariant is the one that ships, and the superseded form that tolerated none alongside a non-null auth_ref_uri never exists. The extra projection is new target structure, not a copied Scanner table and not a source of truth.
3. Seams + decisions already made
- Zot auth flow — MAPPED (2026-08-03): the token dance is the standard registry bearer flow: zot 401s with
WWW-Authenticate: Bearer realm=…/v2/token→ the client GETs the TokenServiceGET /token?service=…(Program.cs:192) → the service authenticates the caller via Authority (audienceregistry, scoperegistry.token.issue), resolves the caller’s PLAN (defaultPlan: community; per-plan repository patterns + actions;plan_rules/plan_auditin theregistry_tokenschema; admin CRUD viaMapPlanAdminEndpoints) → issues a 5-minute registry JWT signed withsigning.keyPath→ the client retries the pull. Config home:devops/etc/registry-token.yaml; publish keyregistry-tokenEXISTS in the publish helper. Two live findings: (a) the deployeddevops/compose/zot-config.jsonhad NOhttp.authsection — the flagship lab registry was ANONYMOUS (measured 2026-08-03 against a copy of that config: anonymous blob upload returned202, i.e. unauthenticated PUSH was open, not merely open reads); the S7 forcing function must therefore ENABLE bearer auth on zot, not just preserve it. Correction to the original note:registry-tokenis not legacy-only — it is defined in the flagshipdocker-compose.stella-services.yml(slot 35) and runs in the live stack; what was missing was the registry’s half of the contract. Bearer auth landed tree-side on 2026-08-03 (B1); enablement is gated on seeding theregistry.token.issuescope. (b)signing.keyPathused to default to the COMMITTEDetc/registry-signing-sample.pem; RESOLVED 2026-08-03 by SPRINT_20260803_001 KEY-1 — the key is deleted, the default is the git-ignoredetc/secrets/registry-token-signing.pem, and the service fails closed with generation guidance when the operator key is absent. The merge inherits that posture. - DC-30 / severity correction (INT-3b): the stale Scanner+
vuln.*join and itsVulnMatch.Coresource edge are deleted. Scanner’s v1 event truthfully emitsseverityCounts: null; the local projection preserves that as unknown rather than inventing zero or reopening the Vulnerabilities database. A future owner artifact/API may populate it. - X12 (
scanner.*scan-state reads): closed in source and live. Scanner exposes retainedscanner.scans; Integrations pulls it withscanner:read, applies the fenced local projection, and canonical compose enables registry browse from that projection. No Scanner DSN remains.
4. Build-boundary dispositions (2026-08-03 register)
integrations-web carries NINE pins — for a three-table service this is the estate’s clearest “host compiles half the world” case. Exact-type inventory DONE (2026-08-03), and it collapses the problem: SEVEN of the nine pins are transitive baggage of ONE edge.
What Integrations source code consumed before INT-3b (kept here as the disposition ledger):
| Consumed | Kind | From |
|---|---|---|
ScanRuntimeStateRow via IScanRuntimeStateRepository, ArtifactBomRow via IArtifactBomRepository | typed repository reads against Scanner’s database | Scanner.Storage |
PublicReachabilityVerdictObject/PublicReachabilityVerdict via IPublicReachabilityVerdictStore (+ the one registration call AddReachabilityEvidence(scannerStorageConnectionString)) | typed store read | Scanner.Reachability |
EcosystemVersionFilter | pure matcher helper (DC-30 path) | Vulnerabilities-owned closed VulnMatch.Core artifact-consumer SDK |
scanner.scan_runtime_state / scanner.*; vuln.advisory_affected ⋈ vuln.advisories | retired raw-SQL reads | deleted by INT-3b; Scanner facts arrive by retained owner feed and missing severity remains unknown |
policy.gate_decisions; shared.tenants | transitional raw-SQL read contract | policy/shared schemas; still owned by the 007 seam |
Zero usings of Attestor, EvidenceLocker, Signer, BinaryIndex, Policy, Replay, or Signals types exist anywhere under src/Integrations/ — those seven pins (attestor ×5 projects, binaryindex ×6, evidence-locker ×2, signer ×1, policy ×3, replay ×1, signals ×1) all arrive through the Scanner.Reachability → Scanner.Core/Attestor.GraphRoot/… closure. Dispositions:
- The lever: replace the three typed Scanner reads + the raw scanner-SQL with the DC-15
scan.completedevent/API contract (017) and drop theScanner.Reachability+Scanner.Storagereferences — seven pins collapse in one extraction, without waiting on the 011/014/019 producer programs. (Register bookkeeping stays with those owners; the EDGE is ours.)DONE — the source-edge half, 2026-08-05 (INT-4, commit
300012e388). The two halves of this bullet separate cleanly and only one needed 017. The three typed reads are now Integrations-owned read seams (RegistryImageScanPlaneReaders.cs) over the same Scanner DSN — the posture the two sibling providers in this host already hold — soScanner.ReachabilityandScanner.Storageare gone and the seven pins with them. Measured: integrations-web’s closure 109 → 47 projects, register 173 → 166 pins, scanner pin 22 → 1, concelier 11 → 1. The final concelier pair became 1 → 0 on 2026-08-22 when the unchanged matcher moved to the Vulnerabilities-owned closed DC-30 SDK seam. Completed in source at INT-3b: Scanner now serves retainedscanner.scans; Integrations pulls it through an authenticated client and applies the rebuildableregistry_image_scan_projectionsunder THE shared P6 inbox/checkpoint/lease primitives. Fencing, admission, projection write and checkpoint commit atomically; gaps/retention loss do not advance, and epoch changes clear then replay. INT-7 folded the executed activation into canonical compose and retired the window overlay. One deliberate residual:StellaOps.Scanner.Contractsis kept — a leaf with zero ProjectReferences, i.e. the closed-contract shape P19 permits — because it owns the reachability verdict vocabulary, which must not be forked into a string comparison here (D-INT4-2 asks 017 to classify it as across-service-client-sdk, asScanner.Reachability.Contractsalready is). - DC-30 stale implementation removed by INT-3b:
VulnMatch.Core, its calculator, and thevuln.*raw reader are gone from integrations-web. The Scanner event carriesnullwhen severity is unmeasured, and the local projection preserves unknown. policy.gate_decisionsraw read → the 007 gate-projection contract (already a documented projection; no source edge exists today).⚠ The SAME DSN trap as D-INT4-4, and worse (found 2026-08-06, D-INT7-1). This bullet used to read as though only the eventual contract was owed.
ResolvePolicyDecisionConnectionStringfalls backPolicyStorage:Postgres:ConnectionString→Policy:Postgres:ConnectionString→ConnectionStrings:PolicyDb, and thenProgram.cs:419appends?? connectionString— so unlike the Scanner plane there is no honest “not configured” branch and no null provider:PostgresRegistryImagePolicyDecisionProvideris ALWAYS constructed, and no compose key has ever set any of those three. It catches every exception, logs atLogWarningand returns an empty decision map, so after the INT-6 repoint every registry image loses its gate verdict and the output is byte-identical to an estate that has taken no decisions. Sharper still:ResolveTenantUuidAsyncreadsshared.tenantsover the same connection FIRST, so the failure starts before any policy table is touched — an operator debugging “verdicts are empty” would be looking in the wrong schema. Staged:PolicyStorage__Postgres__ConnectionStringis pinned explicitly and separately indevops/compose/docker-compose.stella-services.ymland asserted byIntegrationsConsolidationConformanceTests; measured at runtime bydoctor.integrations.policy-plane.dsn-attribution— retired 2026-09-14 bySPRINT_20260913_001ORP-3 together with the Policy DSN it attributed; its successor isdoctor.integrations.policy-decisions.owner-api. Still owed by INT-6: the forcing function must assert a populated gate verdict, not a 200.- Cross-database Scanner reads were §2.7 violations and are removed by INT-3b. With the projection disabled, Scanner fields are honestly unavailable; with canonical compose enabled, the authenticated retained feed writes only local
integrations.*/eventing.*state. The doctor forcing function is nowdoctor.integrations.scanner-projection.local.
4c. The INBOUND cross-schema read this design missed — resolved by INT-3b
Everything above catalogues what Integrations reads FROM other owners. There is also a read in the other direction, and it is the one that actually breaks at the S9 window:
Integrations now appends integrations.registry-image.discovered v1 to retained logical stream integrations.registry-images in the same transaction as each new discovery row. The owner route publishes epoch, head, retention horizon and ordered envelopes. Scanner.Worker pulls it through the existing authenticated Integrations client, holds a P6 lease, and advances a Scanner-owned durable checkpoint only after the deterministic trigger succeeds. Retention loss fails closed and names the replay procedure. There is no FROM integrations.registry_image_discoveries in Scanner.
The registry TokenService side — RESOLVED: publish key registry-token exists (build-service-publish.sh:247); the register carries NO pins for it under any grepped name (clean graph); its schema home registry_token was already confirmed (§2).
4a. What the registry token service IS (owner-requested explainer, review item A8)
What it does. It implements the standard OCI registry token dance for the zot registry: a client’s pull gets 401 + WWW-Authenticate: realm=…/token; the client calls GET /token; the service authenticates the caller against Authority (client credentials, scope registry.token.issue), resolves the caller’s PLAN (named repository patterns + actions — community, enterprise, per-release grants), and issues a ~5-minute registry JWT carrying exactly those grants. The admin API manages plans and writes plan_audit.
Use cases. (1) The product’s registry-access story: client estates pull RELEASE images through the masking registry with per-release, plan-tuned access (ADR-033/034/035 — deploy by digest, access revocable per license/plan). (2) Plan-tiered distribution of licensed content (e.g. an enterprise vuln-db namespace). It is NOT involved in anonymous public reads (§4b).
Why the availability-isolated role. If token issuance is down, every authenticated pull in a client estate fails — while integrations-web absorbs webhook storms from SCMs/registries by design. The role container gives the token path its own healthcheck/restart so webhook churn can never take down pulls, while remaining ONE family (one DB, one deploy unit).
4b. Registry auth model — analysis for the owner (review item B1; NO decision taken)
Owner frame (verbatim intent): the masking registry exists for “very grain tuned even per release access to the registries it is masking”; a Stella-shipped registry serves “the cli to be used from clients env” and “the cli itself is fine to be anonymous”; other uses should be denied unless argued.
Enumerated uses and dispositions proposed:
| Use | Disposition |
|---|---|
| Per-release pulls of masked upstream/release content (client estates, agents, orchestrators) | Bearer-token ONLY, plan-scoped — this is the product; anonymous here would void the per-release grant model |
| CLI pulling Stella public artifacts (tool images, community vuln-db) from client envs | Anonymous READ allowed, restricted to stella-ops/public/**— integrity rides digest + signature, not secrecy |
| Licensed content distribution (enterprise vuln-db etc.) | Token-gated via plans (the existing enterprise plan shape models exactly this) |
| Tenant/customer private artifact hosting | DENY — not a product goal; would drag tenant isolation into the registry |
| Lab/developer scratch pushes | DENY on the product registry — scratch uses local Docker/dev registries |
| ANY anonymous write | DENY — pushes always authenticated |
OWNER RULING (2026-08-03, B1 approved: “ok. i agree”): two surfaces — anonymous READ only on stella-ops/public/**; every other repository requires the bearer token (plans); all pushes authenticated. The A8 role-container topology is also approved as recommended.
ARCHITECT REFINEMENT (2026-08-03, after the implementation dig): the two surfaces are already TWO DEPLOYMENTS — keep them that way.
| Surface | Where it actually lives | Auth |
|---|---|---|
| Public distribution (CLI image, vuln-db mirror) — “the cli used from clients env” | VENDOR infrastructure: registry.stella-ops.org (its own host ov-d06667: nginx → registry:3.0.0, htpasswd for push, anonymous pull ALREADY working) + mirrors.stella-ops.org | anonymous pull, authenticated push |
The lab/dev OCI registry (devops/compose zot, registry.stella-ops.local) | INFRASTRUCTURE, not a product component (INSTALL_GUIDE lists Zot under -InfraOnly with PostgreSQL/Valkey/RustFS/Rekor); serves plugin-bundle publishing, Scanner scan fixtures, integration fixtures | token-only — NO anonymous path |
Currency (2026-08-11): the second row’s framing was superseded the same day it was written. ADR-041 rules that zot is demo/development-lab material, not infrastructure and not part of a release installation; the
-InfraOnlylisting it cites is exactly the framing ADR-041 calls out as having disguised a stand-in. The lab zot is the interim Stella-artifact host (plugin bundles) until the Stella Registry cutover, after which the estate’s only zot is the demo-integration simulator atcustomer-registry.stella-ops.local. The auth conclusion (“token-only, no anonymous path”) stands and is now live — bearer has been enforced onregistry.stella-ops.localsince 2026-08-10. Current roles: registry topology.
Owner correction (2026-08-03) — this zot is NOT “Stella’s masking registry”. Stella’s masking is ADR-033’s stella+oci:// REFERENCE model (digest is identity; masking is routing + presentation; MaskedImageRefResolver resolves to real refs server-side), and its pull-through masker is StellaOps.Agent.Registry— an in-tree C# OCI registry (~2,170 lines: /v2/ manifests + blobs over a digest-verified content store, PullThrough/BundleOnly, loopback-only, default OFF). The stella+oci://registry selector — the internal PRIMARY image registry, the layer System of Record — is reserved and not provisioned (the resolver throws by design). Consequence for this program: the token service’s only current subject is the lab zot (AllowedServices__0 = registry.stella-ops.local); its product-side subject does not exist yet, so B1 is lab hygiene + a proving ground for the plan/token model. See ledger round 93.
Consequences: (1) the customer’s masking registry needs no anonymous surface at all — the zot accessControl limitation (§token-service.md) is therefore MOOT rather than blocking; (2) the token service’s anonymous-plan capability ships disabled (fail-closed default, pinned by a test asserting the shipped config resolves no anonymous plan) — it exists as the mechanism IF the vendor registry ever consolidates onto zot + this service, which would also retire that deployment’s nginx anon-map hack (a known push-breaking trap); (3) enabling bearer auth on the customer’s zot still requires the registry.token.issue scope to exist in the catalog + seed — the real rollout blocker.
Rejected alternatives (recorded so they are not re-litigated): fronting the registry with the gateway — puts layer-blob throughput through the front door for no authorization gain, since the grants already live in the token; a minimal in-house C# registry — would make us own OCI distribution-spec conformance (content negotiation, ranges, referrers, digest validation) to re-implement what zot does, for a surface that is already solved by two existing deployments.
RULING (2026-08-03, ADR-040, closes ledger round 93’s open question): zot is KEPT as third-party infrastructure with a recorded boundary; stella+oci://registry stays reserved (not backed by zot); the anonymous path is the token-service anonymousPlan over stella-ops/public/** (config flip, shipped disabled), and this program’s B1/B1c work is kept unchanged as zot’s auth plane. Two pre-flip gates for S7 ride SPRINT_20260803_002: the plugin namespace moves under a plan (stella-ops/plugins/* — closed 2026-08-03 by RZB-1, which added the plugin-publisher / plugin-consumer plans; before that, stellaops/plugins was covered by NO plan and would 401), and every inventory consumer gets a named credential path (RZB-4 matrix).
4d. Gateway surface + Console centralisation (INT-4, 2026-08-05)
The S5/S6 stage landed as a prepared swap — manifest, script, guard and runbook — with nothing installed on any gateway. DC-19 puts the routing change in this family’s single window (INT-6).
The consolidated group: ^/api/integrations/v1(.*) → http://integrations.stella-ops.local/api/integrations/v1$1 (Microservice, regex). Definition: tools/scripts/int4-integrations-route-swap.json; applied by tools/scripts/int4-swap-integrations-routes.ps1; guarded by IntegrationsConsolidatedRouteConfigTests, which reads the same manifest. Window runbook: docs/runbooks/integrations/integrations-cutover-int4-route-swap.md.
Currency (2026-09-04) — this swap is RETIRED; the script refuses. The 2026-08-28 owner ruling (“all microservices needs to autopublish no router configuration for microservices endpoints”) was executed by
SPRINT_20260809_001RAR-5/RAR-7; both gateway route tables now hold 22 routes and the one row it replaced (^/api/v1/secret-authority(.*)) is gone. Sinceced8a2d286tools/scripts/int4-swap-integrations-routes.ps1reports the swap as RETIRED and exits 2 on both-Checkand a bare run. Integrations is served by the auto-published layer. Read the runbook banner before acting on anything in this section; remediation is tracked asSPRINT_20260904_001RRS-1.
§4’s build-boundary picture said nothing about the gateway, and the gateway picture is the opposite shape: this family is almost entirely UNROUTED. It owns exactly ONE explicit route, ^/api/v1/secret-authority(.*). The primary external plane — the integration hub CRUD (IntegrationEndpoints.cs:23) and the registry browser (RegistryBrowseEndpoints.cs:14) — reaches the host only through the derived-hostname wildcard ^/api/v1/([^/]+)(.*), which builds the target service name from the first path segment and therefore works only because the deployable is registered as integrations(Program.cs:526). The group ADOPTS those two surfaces; there is nothing to remove on their behalf, which is exactly why they are easy to leave behind.
The namespace is shared with release-orchestrator, by sub-path. Two RO routes live INSIDE /api/v1/integrations/** and sit ABOVE the wildcard — .../{guid}/request-delete (index 9, a sub-verb carve-out already pinned by TopologyOwnershipRouteConfigTests) and .../registry-search (index 140) — and /api/v2/integrations (index 106) is platform-web’s. The group takes a NEW prefix so it cannot capture any of them, and the Console’s component-registry.client.ts deliberately keeps its registry-search literal.
Identity: unchanged by this swap, for a reason no sibling manifest states. The Bearer decision is taken from the request PATH, not from the matched route — IdentityHeaderPolicyMiddleware.InvokeAsync calls StripReservedHeaders(context, ShouldPreserveAuthHeaders(context.Request.Path)), before any route is matched. The bare /api catch-all is non-regex with an ABSENT PreserveAuthHeaders that defaults to true (StellaOpsRoute.cs:37), and /api is in ApprovedAuthPassthroughPrefixes in both configs, so every /api/** request forwards Authorization. Old prefix, wildcard prefix and consolidated prefix are all under /api, so no host identity work is owed here. See D-INT5-4 in SPRINT_20260722_024 for the correction this implies for the OfflineKit sibling.
Console. Five keys now live in src/Web/StellaOps.Web/src/app/core/api/api-path-prefixes.ts: integrations, integrationsRegistries, integrationsSecretAuthority, integrationsAudit, integrationsInstanceAudit. Before INT-4 the registry carried zero Integrations keys while the Console had seven call sites, five of them built as `${environment.apiBaseUrl}/v1/integrations` — invisible to any registry-centric search. tools/scripts/validate/check-console-integrations-prefix-literals.sh keeps it that way.
Two keys have no successor and must not be flipped at the window. integrationsAudit and integrationsInstanceAudit are 404 farms: integrations-web maps no audit endpoint at all, and both are deprecated per-service fallbacks behind the unified Timeline endpoint. They are centralised so the literals are not scattered, not because they work.
4e. Doctor adoption + compose staging (INT-5, 2026-08-06)
doctor-check/v1 is live on integrations-web, default-off, additive. The three standard checks plus seven family checks; GET /doctor/integrations/checks is scope-gated on ops.health (adoption guide §1 — one ops-read identity covers the aggregator’s fan-out instead of the aggregator collecting each service’s domain scopes); Platform registration is opt-in and inert until Doctor:Registration:Enabled.
Why each check exists. Every one measures a failure of this window that is SILENT — the reads fail soft, the bridge retries forever, the severity provider logs at Debug — so a healthy container is not evidence of anything here:
| Check | Measures |
|---|---|
…database.own-database-cutover | the query plane’s real database vs the canonical variable’s intent, AND the compiled-in-localhost state |
…scanner-projection.local | INT-3b — projection transport is either explicitly disabled or its local projection/checkpoint tables exist |
…policy-plane.dsn-attribution…policy-decisions.owner-api | D-INT7-1 — superseded 2026-09-14 (ORP-3): the Policy DSN is gone; the successor check pins the owner-API seam (base address, client id, tenant control, no retired DSN key set) |
…credential-custody.kek-resolvable | D-INT3-5, matched on (kek_id, version) — see D-INT7-2 |
…registry-auth.credential-readiness | the consumer-side precondition for the S7 bearer-auth flip |
The Policy answer is not green pre-window and that is correct. Scanner projection reports an explicit disabled state before INT-6 and validates only local tables after enablement; no Scanner or Vulnerabilities DSN is a supported cure.
Three wiring divergences from the reference adopters, each measured — this host is a LIVE CONTINUATION host, the AAI-7/AUTH-7 class:
- The
ServiceDoctorContextis registered EXPLICITLY. This host already holds a bareNpgsqlDataSource(the KEK control plane,AddSingletonnot TryAdd, namedIntegrations.Kek), andAddServiceDoctorChecksbindsGetService<NpgsqlDataSource>()through a TryAdd context — so left implicit the standard checks would have run on the credential-unsealing pool. That pool points at the right database today, which makes the accident correct and therefore unpinnable: INT-6 adds a second data source and last-one-wins would repoint all three checks with no log line. The doctor’s pools are NAMED and deliberately kept OUT of DI so they cannot shadow the KEK pool in the other direction. - The standard checks bind what the host is CONNECTED to (the GRA-7/AUTH-7 form), not the canonical variable — because this host does not read it (D-INT3-1). The cutover check compares the two instead, which is the only way the half-done repoint is visible.
- The registrar may configure its OWN auth client, unlike both live siblings: measured over
src/Integrations/**, nothing here registersAddStellaOpsAuthClientor anIStellaOpsTokenCache, so there is no sharedStellaOpsAuthClientOptionsto hijack. The blank-credential guard is still the correctedIsNullOrWhiteSpaceform (adoption guide §3), never §2’s?? throw— which accepts an empty string, and compose hands one over as${VAR:-}.
Compose closeout. INT-6 used the staged devops/compose/docker-compose.integrations-consolidated.yml overlay to execute the controlled repoint. INT-7 retired that overlay and folded its durable settings into the existing integrations-web key in docker-compose.stella-services.yml: the canonical own-database DSN, Scanner retained-feed/auth settings, Doctor registration settings, and the explicit separate Policy DSN. The host now consumes only the canonical Integrations DSN; neither generic ConnectionStrings key remains an authority. Scanner is an authenticated feed/projection rather than a foreign database read.
docker-compose.integrations.yml remains the separate third-party integration-services lane (D-INT7-4); INT-7 neither renamed nor repurposed it. Conformance pins both that lane and its customer-registry fixture while asserting the retired cutover overlay is absent.
Deploy procedure, preconditions and the forcing-function table: docs/runbooks/integrations/integrations-deploy-int5-staged-repoint.md.
4f. Registry credential package and neutral OCI transport (INT-3c, 2026-08-24)
The registry credential resolution boundary is HTTP at runtime and an exact package at build time. Integrations owns StellaOps.Integrations.RegistryCredentials.Contracts, a BCL-only project that contains only the resolve request/response DTOs, sealed-envelope DTO and canonical sealing protocol (domain labels, AAD construction and payload serialization). It has no host, persistence, Core, background-work or cryptography implementation dependency. The historical broad Contracts assembly references and type-forwards this surface so existing callers remain source/binary compatible.
The generic OCI Distribution client is not Integrations domain implementation. Its 12 source pairs were moved byte-for-byte into the explicitly classified neutral foundation StellaOps.Oci.RegistryClient; the historical StellaOps.Integrations.DockerV2 assembly is now a compatibility forwarder. New service consumers may use the neutral foundation directly, but may not compile the historical producer implementation graph.
The committed offline package is version 0.1.0-p19.5, package SHA256 5c0111b7e5fef42be2becb2f5bb497e5c591bc33a9554965068f46a44beae72d, produced from clean commit 48cfee3c6b679504cad4209f34d7db9a1de40cdf. SbomService’s owner-local adapter combines that wire protocol with the shared CredentialAead primitive; Integrations does not export cryptography implementation through the package. The acceptance gate is an isolated SbomService Release publish with src/Integrations absent. That proof emits the closed contract and neutral OCI assemblies and no Integrations Contracts/Core/DockerV2 assembly, reducing the measured foreign graph from 3 to 0.
5. What the S0 review must settle
- Role-container topology sign-off (the owner guardrail made concrete: compose shape, health, restart).
The zot auth-flow mapdone (§3) — review question it raises: the flagship zot is ANONYMOUS today; does the merge’s S7 forcing function turn bearer auth ON in the flagship (recommended), and who re-keys the committed sample signing key?TokenService schema placement + deployable-key inventorydone (§2, §4).- The pin repoint order — SIMPLIFIED by the §4 inventory: one Scanner-edge extraction (DC-15) collapsed the Scanner pins; only the Policy raw-SQL contract (007 seam) remains in this lane.
