feat: compose fresh owner facts with explicit binding and evidence
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e747-8f27-7242-8df8-8bc44f88c929
This commit is contained in:
parent
f11b948e8c
commit
2a0586b4c9
5 changed files with 358 additions and 0 deletions
|
|
@ -32,6 +32,11 @@ 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.
|
||||
|
||||
The follow-up [owner facts contract](owner-facts-contract.md) pins the re-reviewed
|
||||
sources and owner decisions needed for actual HTTP readers. `PlatformFacts` now
|
||||
provides the typed, bounded Hub-side join with live revocation and evidence
|
||||
binding; it intentionally has no fabricated network endpoints or default grants.
|
||||
|
||||
## Audit Core adapter and sender admission
|
||||
|
||||
`AuditCoreSink` implements the actual `docs/event-envelope.md` contract:
|
||||
|
|
|
|||
85
docs/owner-facts-contract.md
Normal file
85
docs/owner-facts-contract.md
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# Platform authority facts: owner review contract
|
||||
|
||||
HUB-WP-0012 T01/T02, 2026-09-28. Proposed owner contract and implemented Hub-side
|
||||
composition; **no admitted HTTP adapter or production authority source**.
|
||||
|
||||
## Reviewed source
|
||||
|
||||
- User Engine `b4cef6446bc10555b598ea74de1743bc9b03e7ba`:
|
||||
`service.py::me` creates user/account/tenant-account/identity records for unknown
|
||||
`(issuer, subject)`. It cannot be an authorization lookup. `identity_context`
|
||||
reads linked identity/account and membership evidence without this provisioning,
|
||||
but accepts an actor or target user ID, not an admitted Hub workload's lookup of
|
||||
an arbitrary immutable identity. It does not establish Hub's root entitlement.
|
||||
`PLATFORM_TENANT` is `platform:root`; token `platform-operator` roles are not
|
||||
evidence of a live root grant.
|
||||
- Tenant Engine `4ce89969c0cc0ab4ba111f1b4009aa137c94a68b`:
|
||||
`GET /tenants/{tenant_id}` exposes lifecycle/version and accepts an `actor`
|
||||
query for authorization. `/roles/live` returns tenant roles, not a person's
|
||||
root entitlement. These route handlers do not authenticate a Hub workload.
|
||||
An admitted authenticating gateway could supply that boundary, but no such
|
||||
composition is established by these sources.
|
||||
|
||||
These are source findings, not probes of deployed configurations. No owner
|
||||
repository, database, grant or credential was modified.
|
||||
|
||||
## Contract for owner disposition
|
||||
|
||||
| Requirement | Owning decision |
|
||||
| --- | --- |
|
||||
| Side-effect-free lookup by exact issuer and subject | User Engine: existing identity only; unknown identity denies without provisioning |
|
||||
| Current account state and explicit root grant | User Engine + NetKingdom: identity link, global and scoped account state, grant source/revocation/version, independent of JWT roles and Hub root-subject configuration |
|
||||
| Platform naming | Both owners: explicit reviewed mapping of User Engine scope to canonical `tenant:platform`; no string substitution inferred by Hub |
|
||||
| Current tenant lifecycle | Tenant Engine: canonical identifier bound to immutable record and version |
|
||||
| Caller authentication | Each owner: dedicated Hub workload audience/identity and narrowly scoped read authorization; no end-user bearer forwarding |
|
||||
| Freshness and evidence | Each owner: source observation timestamp and durable evidence/version reference; no stale cache on failure |
|
||||
| Producer aliases | Account/identity owner: explicit current bindings, or an empty set; no aliases derived from display names |
|
||||
|
||||
Owners must specify the actual route, request/response schema, authentication
|
||||
mechanism and scoped grants before HTTP reader implementations can be written.
|
||||
The Python types below are an internal normalization seam, **not a proposed
|
||||
HTTP endpoint that already exists**. Missing/unknown/ambiguous results must raise
|
||||
and deny; they must never synthesize an active account.
|
||||
|
||||
## Implemented composition
|
||||
|
||||
`hub_core.security.facts.PlatformFacts` implements `FactSource` with injected
|
||||
`AccountReader` and `TenantReader`. The account reader returns a typed
|
||||
`AccountObservation`; the tenant reader returns a `TenantObservation`. Readers
|
||||
own authentication and wire verification. `account_active` must include the
|
||||
current identity link, global account and required scoped-account validity; a
|
||||
globally active account alone is insufficient. They receive identity references only,
|
||||
not the end-user token. No concrete network reader or automatic environment
|
||||
activation is provided.
|
||||
|
||||
The host supplies the reviewed account-tenant identifier and mapping evidence
|
||||
reference explicitly. That mapping is not an entitlement grant. This candidate
|
||||
admits only human actors in `tenant:platform`; workload and cross-tenant lookups
|
||||
remain separately scoped work. The shared controller still verifies the exact
|
||||
root issuer/subject before consulting this source.
|
||||
|
||||
Every request rereads both owners within one three-second budget. Results must
|
||||
match exact identity/tenant bindings, contain strict booleans and nonempty evidence,
|
||||
and be no older than five seconds or in the future. The result retains the oldest
|
||||
source timestamp, so slow joining/policy/audit cannot refresh authority. Inactive
|
||||
or withdrawn state remains a valid observation and is denied by the controller.
|
||||
Failures expose only a sanitized 503 and never fall back to an earlier allow.
|
||||
Cancellation propagates to the pending lookup.
|
||||
|
||||
The evidence join is SHA-256 over canonical JSON containing the account, tenant
|
||||
and mapping references. Readers/owners must retain the referenced evidence for
|
||||
reconstruction; this hash is a join identifier, not a signature or independent
|
||||
proof of owner authenticity. No account payload or credential enters the join.
|
||||
|
||||
## Acceptance cases and remaining work
|
||||
|
||||
Local tests cover exact bindings, malformed state, stale/future observations,
|
||||
oldest-timestamp preservation, bounded outages, repeated lookup, entitlement and
|
||||
account suspension, tenant inactivity, and refusal before policy after withdrawal.
|
||||
Fixtures implement the internal readers; they do not prove owner API acceptance.
|
||||
|
||||
T01/T02 remain open for owner disposition of the table above, implementation of
|
||||
real authenticated readers, and isolated owner-source integration tests proving
|
||||
unknown lookups create nothing. Private acceptance must then demonstrate root
|
||||
allow, ordinary/forged identity denial, next-request revocation and owner outages
|
||||
with real admitted callers. No public exposure follows from this contract.
|
||||
Loading…
Add table
Add a link
Reference in a new issue