feat: integrate durable authorization audit and runtime composition
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / pytest-smoke (push) Failing after 1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e747-8f27-7242-8df8-8bc44f88c929
This commit is contained in:
tegwick 2026-09-28 12:01:42 +02:00
parent 3e386147fd
commit c9b6916dac
14 changed files with 767 additions and 16 deletions

View file

@ -47,8 +47,9 @@ A host composes `create_app(access_controller=AccessController(...))` with:
set. The token is reread on every call and is separate from the end-user token.
Remote `/v1/keys` responses never establish their own trust. Current/previous keys
can coexist in the mounted trust file; removing a key takes effect next call.
- `Audit.append`: an **owner implementation still owed** that returns only after
durable acceptance. Every allow must reach this sink before handler execution;
- `AuditCoreSink`: implemented against the owner
[eight-field ingestion contract](../../audit-core/docs/event-envelope.md), returning
only after an operational-custody probe and explicit durable acceptance. Every allow must reach this sink before handler execution;
a failed sink blocks reads as well as writes. Authorization receipts say
`authorized`, not “operation completed.” Domain commit/outcome audit remains a
separate requirement; this source seam does not claim transactional audit.
@ -58,7 +59,10 @@ A host composes `create_app(access_controller=AccessController(...))` with:
Do not implement these missing adapters as a constant allow, in-memory audit sink,
or an assertion copied from token claims. Tests use synthetic owners explicitly.
The current CLI deliberately provides no fixture adapter or production bypass.
A deployment composition factory and dependency health probes remain T02–T04 work.
An explicit `SecuritySettings`/owner-facts composition now owns the HTTP client
and closes it with the runtime. Audit custody is probed per append. Real owner-facts
admission and additional owner dependency probes remain T02–T04 work; see the
[owner integration review](owner-access-integration.md).
For this candidate all Hub resources are explicitly **platform-owned**. Other
target tenants are refused. Root requires AAL2/3, active account and tenants,
@ -124,8 +128,8 @@ browser PKCE sessions/logout and real MCP root login remain open.
2. T02: immutable root binding, registered audience/redirects, PKCE/MFA/recovery,
admitted live facts adapters, attended login/logout and revocation receipts.
3. T03: dedicated Hub policy and fact provenance review; authenticated deployment,
credential/key custody, durable audit implementation and native rotation probes.
4. T04–T05: composed deployable runtime, dependency health probes, all client/extension
credential/key custody, real Audit Core sender admission and native rotation probes.
4. T04–T05: deploy the admitted composition, complete dependency health probes and all client/extension
migrations, domain outcome audit, legacy lane rollback and full root journeys.
5. T06–T08: every platform/Railiance receipt, separate public-enable approval and
later role/delegation/tenant isolation. No milestone is closed by local fixtures.

View file

@ -0,0 +1,46 @@
# HUB-WP-0012 owner integration continuation — 2026-09-28
Initial foundation committed and pushed: `3e38614`.
Implemented in the follow-up:
- Audit Core sender conforming to its real eight-field ingestion contract,
authenticated with a distinct rotating credential, and requiring operational
durable custody plus an explicit accepted/duplicate receipt before execution.
- Exact verified signed policy artifacts retained in the archive; secret-shaped
decision fields fail closed before serialization. Refusals retain verified
identity, action and request/resource digests where available.
- Explicit `SecuritySettings` + owner `FactSource` runtime composition, bounded
HTTP calls, no proxy-environment inheritance, and runtime-owned client cleanup.
Configuration alone cannot activate a missing authority implementation.
Validation:
- Full suite with `HUB_CORE_AUDIT_CORE_SOURCE=/home/worsch/audit-core`:
**304 passed**, one existing TestClient deprecation warning, 52.56 seconds.
- Final focused owner/denial checks after enriching refusal attribution: **7 passed**.
- Source inventory unchanged and validated: 161 Hub surfaces / 48 platform rows /
250 observed cluster objects.
- `uv build` succeeds; `git diff --check` passes.
Five opt-in owner-source cases use the actual Audit Core receiver:
1. Hub envelope accepted and exact evidence retained across storage reopen.
2. Unmodified development custody rejected before posting.
3. A lost receipt blocks execution even though the receiver holds the attempt.
4. Invalid sender credential creates no record.
5. Composed JWT verification, current facts, signed decision, real receiver
ingestion and native Hub write; archived decision independently verifies;
root entitlement withdrawal denies the next attempt.
These are local tests with synthetic identities, keys and sender registration.
SQLite is durable test storage, not operational production custody. A clearly
named test-only readiness fixture simulates that custody classification; no
live receiver admission, real root login or platform access is claimed.
The [owner review](../owner-access-integration.md) records concrete next gates:
User Engine's `platform:root` mapping, a non-provisioning immutable identity and
root-entitlement lookup (its `/api/v1/me` can create an account), authenticated
Tenant Engine caller admission, and actual Hub Audit Core sender custody.
`warden route show audit-core-senders --json` routes registry ownership to
ops-mason/OpenBao and is unresolved. No secrets were retrieved or grants changed.

View file

@ -0,0 +1,101 @@
# HUB-WP-0012 owner integration review — 2026-09-28
The Hub foundation is committed as `3e38614`. This follow-up delivers an Audit
Core sender and runtime composition, and identifies the remaining source
contracts. These are owner review inputs, not evidence of deployed grants.
## Account, root entitlement and tenant facts
| Owner surface reviewed | Observed behavior | Required integration |
| --- | --- | --- |
| User Engine `web.py`, `service.py::me` | `/api/v1/me` resolves `(iss, sub)` but creates User/Account/ExternalIdentity records if absent | A side-effect-free authority lookup; Hub must not bootstrap identities to check access |
| User Engine `service.py` | `PLATFORM_TENANT = "platform:root"`; `platform-operator` is an actor role | Explicit mapping to IAM `tenant:platform` and the one immutable root principal; a role or string substitution is insufficient |
| User Engine tenant administration APIs | Human/edge-oriented routes; no reviewed Hub workload lookup for current root entitlement | Independently authenticated Hub workload, exact lookup scope and current entitlement provenance |
| Tenant Engine `/tenants/{tenant_id}` | Current lifecycle and record version, authorized `tenant.read` | Resolve immutable tenant ID versus canonical identifier; preserve owner version and lifecycle |
| Tenant Engine `/tenants/{tenant_id}/roles/live` | Live role state with `tenant.role.read.live`, caller supplies an `actor` query | Admit/authenticate the actual Hub workload and its actor assertion; do not mistake query text or a network path for caller authentication |
The accepted integration must return identity references, account status,
explicit current root entitlement, actor/target tenant status, observation times
and owner evidence/version references. Unknown identity is denied without
creating anything. Revocation must be visible on the next privileged read as
well as write; no cached token role supplies current entitlement.
`LiveFacts` is the Hub-side normalized result, not a wire endpoint invented for
an owner. Each source lookup must complete inside its timeout. The observation
age is measured from the actual source observation and cannot be reset after
slow downstream calls. All joined facts must still be at most five seconds old
when policy/audit finish. Missing, ambiguous, inactive or stale facts deny.
Producer aliases must come from an explicit owner binding to that identity.
T01/T02 require owner review of the lookup and root/tenant mapping before a
production `FactSource` is configured. The runtime will not load fixture facts,
query owner databases directly, forward Hub bearer tokens to other audiences,
or use `/me` as an account-provisioning side effect.
## Audit Core adapter and sender admission
`AuditCoreSink` implements the actual `docs/event-envelope.md` contract:
`POST /v1/events` with eight fields, a distinct rotating sender bearer, and an
`Idempotency-Key` matching the event ID. Source is exactly `hub-core`, tenant
exactly `tenant:platform`. Only a matching `202 accepted` or `200 duplicate`
with a nonempty archive reference counts as custody. Before each append,
`/readyz` must report `status=ok`, `durable=true`, and custody class
`operational` or its rollout alias `archive`. The entire append is bounded
at three seconds, uses TLS, and never follows redirects.
Allow is blocked until the archive accepts the authorization record. A lost
receipt blocks the business operation even if the attempt reached storage. This
is a pre-execution authorization journal, not proof that an operation committed.
There is no local success buffer or silent redaction. Domain transaction/outcome
atomicity and failure detection remain separate T03/T04 acceptance gates.
The exact verified signed decision is retained under `data.signed_decision` as
serialized JSON so another serialization of the archive cannot reorder its Go
struct fields. The verifier refuses secret-shaped field names before retention.
No end-user bearer, message body or command body is emitted. Independent tests
reverify the signed artifact after retrieval from the owner's receiver.
Proposed receiver registration (no credential values):
- Name/source: `hub-core`; allowed tenants: only `tenant:platform`.
- Write enabled, read disabled; distinct sender tokens with rotation overlap.
- `secret_policy=reject`; authorization record classes `hub.access.authorized`,
`hub.access.denied`, `hub.access.refused`.
- Load-bearing authorization evidence: execution requires receipt. Owner review
must settle exact emission-cadence/failure detection; no claim that this journal
provides complete domain mutation evidence or archive tamper evidence.
Credential routing: `warden route show audit-core-senders --json` identifies
`ops-mason`, subsystem OpenBao + audit-core, `warden_executes=false`, and currently
`resolvable=false`. This route points to registry custody; it does not prove an
admitted Hub sender or mint a credential. No secret was requested or retrieved.
## Runtime assembly
`SecuritySettings` uses these `HUB_CORE_SECURITY_` environment suffixes:
| Suffix | Value |
| --- | --- |
| `ISSUER`, `AUDIENCE`, `ROOT_SUBJECT` | Admitted HTTPS issuer, Hub audience, immutable root subject |
| `POLICY_URL`, `POLICY_CALLER` | Dedicated HTTPS PDP and exact admitted ServiceAccount principal |
| `POLICY_TOKEN_FILE`, `POLICY_KEYS_FILE` | Absolute projected caller-token and trusted public-key paths |
| `AUDIT_URL`, `AUDIT_TOKEN_FILE` | HTTPS receiver and separate absolute sender-token path |
A host calls `create_app(access_facts=reviewed_owner_adapter)` in enforcement
mode. It can alternatively supply an explicit `SecuritySettings` instance.
The runtime owns and closes the shared HTTP client; proxy environment variables
are not inherited. Partial/invalid composition is rejected. Without an explicit
facts adapter, the standalone app stays closed even if security variables exist.
No file-backed allowlist or dynamic arbitrary-module loader supplies authority.
## Evidence boundary
The opt-in `tests/test_audit_core_owner_contract.py` exercises the actual Audit
Core WSGI receiver and durable SQLite storage, including reopen, wrong-credential
and lost-receipt cases. A **test-only** readiness fixture reports operational
custody to exercise the production client's receipt checks; this is not native
operational custody. Unmodified development custody is independently refused.
A composed fixture journey verifies a real RSA JWT, fresh facts, an Ed25519
policy decision, receiver custody and native Hub write, followed by entitlement
withdrawal denial. Real root enrollment/login, service deployment, live key and
sender custody, and operational acceptance remain open.