Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e747-8f27-7242-8df8-8bc44f88c929
6 KiB
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::mecreates user/account/tenant-account/identity records for unknown(issuer, subject). It cannot be an authorization lookup.identity_contextreads 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_TENANTisplatform:root; tokenplatform-operatorroles are not evidence of a live root grant. - Tenant Engine
4ce89969c0cc0ab4ba111f1b4009aa137c94a68b:GET /tenants/{tenant_id}exposes lifecycle/version and accepts anactorquery for authorization./roles/livereturns 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. The follow-up
User Engine commit 686da7c adds UserEngineService.lookup_account_authority:
an authorized exact issuer/subject/tenant lookup that never provisions a missing
identity, returns current global and scoped account status (missing scoped state
is explicit), and separates caller from target. The new policy action is
account.authority.read; existing generic read grants are not assumed sufficient.
Its result includes source observation time, identity/account references and the
policy decision reference, but no root entitlement or profile data.
This closes the read-only service primitive, not the wire contract or admitted Hub workload. USER-WP-0037 owns root-grant/mapping disposition and authenticated HTTP exposure/private acceptance. No database, live grant or credential changed.
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.