Retain verified tenant provenance and reconcile production blockers
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a1046e-fdbf-7b93-a267-5f72b9b24526
This commit is contained in:
parent
931e0b3249
commit
c9ad5c2dd7
12 changed files with 274 additions and 25 deletions
13
SCOPE.md
13
SCOPE.md
|
|
@ -102,12 +102,13 @@ systems. Consumption follows the assented `GH-DEC-2026-003` contract.
|
|||
|
||||
## Current State
|
||||
|
||||
- Status: **production-packaged spine, not yet live**. Authenticated HTTP
|
||||
mutations, schema-v2 SQLite, outbox drain to audit-core, digest-pin-ready
|
||||
image and single-writer StatefulSet, and a fail-closed PEP harness exist in
|
||||
this repository. Live rollout, KeyCape audience/client proof, audit-core
|
||||
sender registration, and a served secrets-engine consume binding remain
|
||||
external gates on `APPROVAL-WP-0002`.
|
||||
- Status: **deployed schema-v5 service; schema-v6 source ready**. Authenticated
|
||||
HTTP mutations, durable SQLite, transactional outbox and restart/restore
|
||||
evidence are implemented. Source now retains verified tenant provenance;
|
||||
promotion needs a new release and approved migration/rollout. Native issuer
|
||||
profile acceptance, lifecycle audit reconciliation and a named served PEP
|
||||
remain blocked in `APPROVAL-WP-0002`; current owner requirements are in
|
||||
`docs/production-dependencies.md`.
|
||||
- Layer declaration: INTENT frontmatter + `layer.yaml`. Cadence declared in
|
||||
`cadence.yaml`. No Tooling contacts.
|
||||
- Consumption is public at `POST /v1/approvals/{id}/consume` under
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ class App:
|
|||
assurance=json.dumps(identity.assurance, sort_keys=True),
|
||||
evidence_ref=identity.evidence_ref,
|
||||
principal_type=identity.principal_type,
|
||||
tenant_source=identity.tenant_source,
|
||||
)
|
||||
return 200, obj.as_dict()
|
||||
if rest == ["revoke"] and method == "POST":
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class Identity:
|
|||
scopes: frozenset[str]
|
||||
assurance: dict[str, Any]
|
||||
evidence_ref: str
|
||||
tenant_source: str | None = None
|
||||
|
||||
def require(self, scope: str) -> "Identity":
|
||||
if scope not in self.scopes:
|
||||
|
|
@ -76,6 +77,12 @@ def identity_from_claims(claims: Mapping[str, Any], token: str) -> Identity:
|
|||
raise Unauthenticated("token identity claims are invalid")
|
||||
if not tenant or not isinstance(assurance, dict):
|
||||
raise Unauthenticated("token tenant or assurance claim is invalid")
|
||||
tenant_source = claims.get("tenant_source")
|
||||
if "tenant_source" in claims and (
|
||||
not isinstance(tenant_source, str)
|
||||
or tenant_source not in {"directory", "registration", "default"}
|
||||
):
|
||||
raise Unauthenticated("token tenant_source claim is invalid")
|
||||
audiences = _texts(claims.get("aud"), "aud")
|
||||
roles = frozenset(_texts(claims.get("roles"), "roles", allow_empty=True))
|
||||
scopes = frozenset(_texts(claims.get("scope", claims.get("scp")), "scope"))
|
||||
|
|
@ -90,6 +97,7 @@ def identity_from_claims(claims: Mapping[str, Any], token: str) -> Identity:
|
|||
scopes=scopes,
|
||||
assurance=dict(assurance),
|
||||
evidence_ref=f"jwt-sha256:{fingerprint}",
|
||||
tenant_source=tenant_source,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ EVENT_CLASSES = ("issuance", "use", "supersession", "revocation", "heartbeat")
|
|||
# class and says nothing about the quiet one — here that is `revocation`, which
|
||||
# is the class whose silence actually matters.
|
||||
HEARTBEAT_CLASSES = ("issuance", "use", "supersession", "revocation")
|
||||
LATEST_SCHEMA_VERSION = 5
|
||||
LATEST_SCHEMA_VERSION = 6
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS approvals (
|
||||
|
|
@ -75,6 +75,7 @@ CREATE TABLE IF NOT EXISTS entries (
|
|||
assurance TEXT,
|
||||
evidence_ref TEXT,
|
||||
principal_type TEXT,
|
||||
tenant_source TEXT,
|
||||
PRIMARY KEY (approval_id, subject_id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS outbox (
|
||||
|
|
@ -258,6 +259,9 @@ class Engine:
|
|||
# of principal bound it, and defaulting it to 'human' would
|
||||
# manufacture approver evidence that was never presented.
|
||||
conn.execute("ALTER TABLE entries ADD COLUMN principal_type TEXT")
|
||||
if "tenant_source" not in entry_columns:
|
||||
# v6: historical entries have no verified tenant provenance.
|
||||
conn.execute("ALTER TABLE entries ADD COLUMN tenant_source TEXT")
|
||||
outbox_columns = {
|
||||
row["name"] for row in conn.execute("PRAGMA table_info(outbox)").fetchall()
|
||||
}
|
||||
|
|
@ -456,10 +460,11 @@ class Engine:
|
|||
"assurance": e["assurance"],
|
||||
"evidence_ref": e["evidence_ref"],
|
||||
"principal_type": e["principal_type"],
|
||||
"tenant_source": e["tenant_source"],
|
||||
}
|
||||
for e in self._conn()
|
||||
.execute(
|
||||
"SELECT subject_id, approved_at, assurance, evidence_ref, principal_type "
|
||||
"SELECT subject_id, approved_at, assurance, evidence_ref, principal_type, tenant_source "
|
||||
"FROM entries WHERE approval_id=? ORDER BY approved_at",
|
||||
(row["id"],),
|
||||
)
|
||||
|
|
@ -520,9 +525,15 @@ class Engine:
|
|||
assurance: str | None = None,
|
||||
evidence_ref: str | None = None,
|
||||
principal_type: str | None = None,
|
||||
tenant_source: str | None = None,
|
||||
) -> Approval:
|
||||
if not subject_id:
|
||||
raise Unprocessable("subject_id is required")
|
||||
if tenant_source is not None and (
|
||||
not isinstance(tenant_source, str)
|
||||
or tenant_source not in {"directory", "registration", "default"}
|
||||
):
|
||||
raise Unprocessable("tenant_source is invalid")
|
||||
conn = self._conn()
|
||||
now = iso(self.now())
|
||||
try:
|
||||
|
|
@ -541,9 +552,9 @@ class Engine:
|
|||
try:
|
||||
conn.execute(
|
||||
"INSERT INTO entries "
|
||||
"(approval_id, subject_id, approved_at, assurance, evidence_ref, principal_type) "
|
||||
"VALUES (?,?,?,?,?,?)",
|
||||
(approval_id, subject_id, now, assurance, evidence_ref, principal_type),
|
||||
"(approval_id, subject_id, approved_at, assurance, evidence_ref, principal_type, tenant_source) "
|
||||
"VALUES (?,?,?,?,?,?,?)",
|
||||
(approval_id, subject_id, now, assurance, evidence_ref, principal_type, tenant_source),
|
||||
)
|
||||
except sqlite3.IntegrityError as exc:
|
||||
conn.rollback()
|
||||
|
|
@ -852,7 +863,7 @@ class Engine:
|
|||
a future schema that relaxes it.
|
||||
"""
|
||||
rows = conn.execute(
|
||||
"SELECT subject_id, approved_at, assurance, evidence_ref, principal_type "
|
||||
"SELECT subject_id, approved_at, assurance, evidence_ref, principal_type, tenant_source "
|
||||
"FROM entries WHERE approval_id=? ORDER BY approved_at, subject_id",
|
||||
(approval_id,),
|
||||
).fetchall()
|
||||
|
|
@ -866,6 +877,7 @@ class Engine:
|
|||
**({"assurance": r["assurance"]} if r["assurance"] else {}),
|
||||
**({"evidence_ref": r["evidence_ref"]} if r["evidence_ref"] else {}),
|
||||
**({"principal_type": r["principal_type"]} if r["principal_type"] else {}),
|
||||
"tenant_source": r["tenant_source"],
|
||||
}
|
||||
for r in seen.values()
|
||||
]
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ database.
|
|||
(registry tag `0.1.0-hfact-be1a388`, source `be1a388`). Both references MUST stay
|
||||
identical immutable digests: migration and server share one database.
|
||||
|
||||
The new artifact carries `LATEST_SCHEMA_VERSION = 5`, matching this source:
|
||||
The deployed artifact carries schema v5 and predates schema v6 in this source.
|
||||
Schema v6 records verified `entries.tenant_source`; it requires a new scanned,
|
||||
published image, identical migration/server pins, backup and an approved rollout.
|
||||
The existing deployed image records:
|
||||
`entries.principal_type` and explicit `approvals.human_control`. Its release scan
|
||||
found zero HIGH/CRITICAL vulnerabilities. Disposable on-disk v4-to-v5 migration,
|
||||
negative service/agent binding, human binding/consumption and restart persistence
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
# Requirements on the approver surface (`informed-decision`)
|
||||
|
||||
Status: issued by approval-engine 2026-09-09, against `APPROVAL-WP-0002-T01`.
|
||||
Status: issued 2026-09-09; current acceptance reviewed 2026-10-04 against
|
||||
`APPROVAL-WP-0002-T01`. Sections 3 and 4 retain the original questions:
|
||||
KeyCape has since published approval:read and the assurance shape, registered
|
||||
`informed-decision-approver`, and Informed Decision has recorded native human
|
||||
acceptance. Current remaining receipts are in [production-dependencies.md](production-dependencies.md).
|
||||
Schema-v6 source additionally preserves verified tenant_source; the deployed
|
||||
schema-v5 image awaits separately approved promotion.
|
||||
Addressed to `informed-decision` (`INFD-WP-0001`), copied to `key-cape`
|
||||
(`KEY-WP-0013-T02`).
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,12 @@ Consequences this engine accepts deliberately:
|
|||
platform zone" is a fact about the person, and this claim cannot carry it.
|
||||
No such doctrine exists today; if `gate-house` issues one, it needs a
|
||||
directory-sourced claim and this check does not become that claim by matching.
|
||||
- When key-cape emits provenance alongside the tenant, this engine records it on
|
||||
the approver entry the way schema v4 records `principal_type` — an evidence
|
||||
reader should not have to infer provenance from a string that cannot carry it.
|
||||
- Schema v6 records verified `tenant_source` on approver entries and issuance/use
|
||||
threshold evidence. KeyCape publishes `directory`, `registration`, or `default`.
|
||||
An absent claim remains null; an explicit malformed or unknown value is refused.
|
||||
Legacy entries remain null, never inferred from their subject or tenant.
|
||||
Provenance does not change store isolation or establish zone membership.
|
||||
The deployed schema-v5 image does not yet record this field.
|
||||
|
||||
| Route | Required scope |
|
||||
|---|---|
|
||||
|
|
@ -44,8 +47,8 @@ Consequences this engine accepts deliberately:
|
|||
| explicit heartbeat | `approval:emit` |
|
||||
|
||||
Create additionally requires `binding.actor == sub`. Approval-entry subject,
|
||||
assurance, evidence reference, and **principal type** are derived from the
|
||||
verified JWT, never the request body.
|
||||
assurance, evidence reference, **principal type**, and **tenant source** derive
|
||||
from the verified JWT, never the request body.
|
||||
|
||||
`principal_type` is recorded from verified identity (since schema v4). Schema v5
|
||||
implements GH-DEC-2026-016: when an approval declares `human_control: true`,
|
||||
|
|
|
|||
62
docs/production-dependencies.md
Normal file
62
docs/production-dependencies.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# Production dependency requirements
|
||||
|
||||
Reviewed 2026-10-04 for APPROVAL-WP-0002. Task status `wait` means blocked;
|
||||
the workplan remains `blocked` after completing reachable source work.
|
||||
|
||||
| Task | Owner return required | Evidence that clears the wait |
|
||||
| --- | --- | --- |
|
||||
| T01 — authenticated approver evidence | key-cape and informed-decision; founder for release rollout | Exact deployed issuer revision and client/profile receipt for requester and human paths; human sub/type, tenant/source, resource audience, scopes and assurance captured as non-secret facts and correlated with a stored human-control entry. Promote scanned schema-v6 image under per-change approval, back up/migrate once, then verify native entry and issuance/use provenance. No tokens or secrets in receipts. |
|
||||
| T04 — delivery and reconciliation | audit-core, with railiance-platform for deployed sender/custody acceptance | Accept current cadence.yaml inventory for source approval-engine and tenant tenant:platform, explicit redact policy and four transition heartbeat classes at the requested 172800s bound. Return actual lifecycle event ids/drain acknowledgment and independent accepted counts for the same bounded time window, with pending rows accounted separately. Prove startup beats, missing-class and lag findings (depth 100 / age 3600s), and scoped sender bearer revocation. |
|
||||
| T05 — live PEP consumption | secrets-engine, with informed-decision for the act-specific human review and access-engine for decision binding | Name the served PEP route/workload, immutable component revision, engine image/endpoint, authenticated caller identities and exact binding. Prove claim-before-decision and CAS consume after ALLOW before protected side effect; same-digest retry, different-digest conflict, spent-on-failure, revoked/expired/superseded/wrong-binding/unavailable denial with no protected callback. Preserve the native approval and decision digests, declared human_control where required, and consumer-owned side-effect/refusal evidence. |
|
||||
|
||||
For live whitehat T-06 only, T05's named surface must additionally be covered by
|
||||
a complete engagement and plane admission naming identities, bounded window
|
||||
and technique. Neither an in-process client nor this dependency document
|
||||
admits a test or authorizes a packet. WHITEHAT-WP-0008 remains blocked.
|
||||
|
||||
## Delivered prerequisites
|
||||
|
||||
- Approval Engine deployment, schema-v5 persistence, restart and restore:
|
||||
[deployment receipt](evidence/2026-09-14-approval-engine-deployment.json).
|
||||
Image sha256:251941a5cb2724b57cc32cff6b693b1ab0be695bee4f56f02d51961189c0fa49.
|
||||
- KeyCape: KEY-WP-0013 publishes `informed-decision-approver`, callback
|
||||
https://decisions.coulomb.social/auth/callback, scopes openid/approval:read/
|
||||
approval:approve, S256 PKCE, MFA and tenant:platform. KEY-WP-0031 is finished
|
||||
and publishes directory/registration/default provenance. A source fragment
|
||||
is insufficient to pin the exact deployed issuer/profile used by an entry.
|
||||
- Informed Decision: archived INFD-WP-0001-T08 reports real human acceptance.
|
||||
Its docs/evidence/2026-09-27-tool-budget-approval-create.json records requester
|
||||
actor informed-decision and human_control=true; the corresponding
|
||||
2026-09-27-tool-budget-acceptance.json confirms approval
|
||||
e9521804-8fce-4fbc-bfdf-8a1aa959fd61, subject
|
||||
uid=platform-root,ou=people,dc=netkingdom,dc=local at 18:05:35Z.
|
||||
These discharge the historical absence of a requester/human flow. They do
|
||||
not prove the new v6 field or admit a different protected action. Standing
|
||||
requester runtime composition remains INFD-WP-0005-T05's platform dependency.
|
||||
- Secrets Engine: SECRETS-WP-0010's native reader acceptance records existing
|
||||
CCR-2026-0019 delivery and separate native read/consume scopes, an engine 404
|
||||
for an unknown approval and 403 for consume-only read. Credential delivery is
|
||||
complete; that test explicitly created, bound and consumed no approval.
|
||||
- Audit Core: AUDIT-WP-0009 is finished. Native producer/readback receipts from
|
||||
2026-09-11 and engine startup heartbeat drain are delivered. Current class
|
||||
admission, actual lifecycle reconciliation and observed findings still need
|
||||
their own receipt; implementation completion is insufficient evidence.
|
||||
|
||||
## Approval Engine source completion and release boundary
|
||||
|
||||
Schema v6 retains verified tenant provenance without treating registration or
|
||||
default provenance as directory membership. Entries and issuance/use evidence
|
||||
carry null for absent provenance; historic rows/events are preserved. Signed
|
||||
HTTP tests cover all three sources, absent/malformed claims and body spoofing;
|
||||
on-disk tests cover migration, restart, binding preservation and rollback.
|
||||
|
||||
The production pin remains schema v5. Release preparation uses the existing
|
||||
image-build/image-scan targets and immutable matching migration/server pins.
|
||||
Production migration/rollout requires founder approval per change, a verified
|
||||
backup and native readback; this review performs none of those live operations.
|
||||
The [deployment runbook](../deploy/README.md) and
|
||||
[storage operations](storage-operations.md) specify that boundary and recovery.
|
||||
|
||||
APPROVAL-IN-0001 also remains open for the unassigned shared Taxonomy schema
|
||||
owner. It does not block this workplan's local claim contract and is not a
|
||||
substitute for the three owner returns above.
|
||||
|
|
@ -78,3 +78,19 @@ and verify the existing backup before migration. The old v3 image must not serve
|
|||
a v5 database; rollback requires the matching pre-migration backup and the
|
||||
existing single-writer recovery procedure. No live database is migrated by the
|
||||
source test run.
|
||||
|
||||
|
||||
## Schema v6 — verified tenant provenance
|
||||
|
||||
`entries.tenant_source` is nullable TEXT, recording only the verified issuer claim
|
||||
(`directory`, `registration`, or `default`). Missing claims and pre-v6 entries
|
||||
stay NULL; migration never infers membership or changes bindings, quorum,
|
||||
human-control declarations, or existing outbox evidence. New issuance/use
|
||||
threshold evidence retains this field, including null for unknown provenance.
|
||||
|
||||
Migration is additive and must precede serving the v6 release. The deployed
|
||||
schema-v5 image remains pinned; source changes do not migrate production.
|
||||
Publish/scan and pin the same v6 artifact for both containers, take and verify
|
||||
the pre-migration backup, and obtain per-change rollout approval. An old v5
|
||||
server refuses a v6 store; rollback uses the verified pre-migration backup with
|
||||
the matching release and the stopped single-writer restore procedure above.
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ def test_legacy_v4_rows_are_not_reclassified_from_human_entries(tmp_path):
|
|||
assert survivor.entries[0]["principal_type"] == "human"
|
||||
assert survivor.human_control is False
|
||||
assert upgraded.claim(obj.id)["binding"]["human_control"] is False
|
||||
assert upgraded.storage_status()["schema_version"] == LATEST_SCHEMA_VERSION == 5
|
||||
assert upgraded.storage_status()["schema_version"] == LATEST_SCHEMA_VERSION == 6
|
||||
upgraded.close()
|
||||
|
||||
|
||||
|
|
|
|||
97
tests/test_tenant_provenance.py
Normal file
97
tests/test_tenant_provenance.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
"""Verified tenant origin remains evidence, including absence and legacy rows."""
|
||||
import json
|
||||
import sqlite3
|
||||
|
||||
import pytest
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
|
||||
from approval_engine.api import App, call
|
||||
from approval_engine.auth import JWTAuthenticator
|
||||
from approval_engine.errors import StoreUnavailable
|
||||
from approval_engine.store import Engine, LATEST_SCHEMA_VERSION
|
||||
from tests.conftest import FROZEN, binding, validity
|
||||
from tests.test_auth import _JWKS, _jwt
|
||||
|
||||
|
||||
def authenticator():
|
||||
private = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
||||
auth = JWTAuthenticator(
|
||||
issuer="https://keycape.example", audience="approval-engine",
|
||||
jwks_url="https://keycape.example/jwks", jwks_client=_JWKS(private.public_key()),
|
||||
)
|
||||
return private, auth
|
||||
|
||||
|
||||
@pytest.mark.parametrize("source", ["directory", "registration", "default", None])
|
||||
def test_verified_provenance_survives_http_entry_restart_and_use(tmp_path, source):
|
||||
private, auth = authenticator()
|
||||
claims = dict(sub="human", tenant="tenant:platform", principal_type="human",
|
||||
scope="approval:approve")
|
||||
if source is not None:
|
||||
claims["tenant_source"] = source
|
||||
token = _jwt(private, **claims)
|
||||
path = tmp_path / "approval.sqlite"
|
||||
engine = Engine(path, clock=lambda: FROZEN)
|
||||
obj = engine.create(binding(), validity(), human_control=True)
|
||||
digest = obj.binding_digest
|
||||
status, approved = call(App(engine, auth), "POST", f"/v1/approvals/{obj.id}/entries",
|
||||
{"tenant_source": "spoofed"}, authorization="Bearer " + token)
|
||||
assert status == 200
|
||||
assert approved["entries"][0]["tenant_source"] == source
|
||||
engine.close()
|
||||
engine = Engine(path, clock=lambda: FROZEN, auto_migrate=False)
|
||||
assert engine.get(obj.id).entries[0]["tenant_source"] == source
|
||||
assert engine.get(obj.id).binding_digest == digest
|
||||
assert "tenant_source" not in json.dumps(engine.claim(obj.id))
|
||||
engine.consume(obj.id, digest)
|
||||
rows = engine._conn().execute("SELECT class, payload_json FROM outbox").fetchall()
|
||||
for row in rows:
|
||||
payload = json.loads(row["payload_json"])
|
||||
assert payload["details"]["threshold"]["approvers"][0]["tenant_source"] == source
|
||||
assert {row["class"] for row in rows} == {"issuance", "use"}
|
||||
engine.close()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("source", [None, "unknown", "", {}, [], 1])
|
||||
def test_explicit_invalid_provenance_refuses_http_bind_without_mutation(engine, source):
|
||||
private, auth = authenticator()
|
||||
token = _jwt(private, sub="human", tenant="tenant:platform", principal_type="human",
|
||||
scope="approval:approve", tenant_source=source)
|
||||
obj = engine.create(binding(), validity(), human_control=True)
|
||||
status, _ = call(App(engine, auth), "POST", f"/v1/approvals/{obj.id}/entries", {},
|
||||
authorization="Bearer " + token)
|
||||
assert status == 401
|
||||
assert engine.get(obj.id).entries == []
|
||||
assert engine._conn().execute("SELECT COUNT(*) FROM outbox").fetchone()[0] == 0
|
||||
|
||||
|
||||
def test_v5_migration_preserves_unknown_provenance_and_existing_evidence(tmp_path):
|
||||
path = tmp_path / "approval.sqlite"
|
||||
engine = Engine(path, clock=lambda: FROZEN)
|
||||
obj = engine.create(binding(), validity(), human_control=True)
|
||||
engine.add_entry(obj.id, "human", principal_type="human")
|
||||
digest = obj.binding_digest
|
||||
evidence = engine._conn().execute("SELECT payload_json FROM outbox").fetchall()[0][0]
|
||||
engine.close()
|
||||
with sqlite3.connect(path) as conn:
|
||||
conn.execute("ALTER TABLE entries DROP COLUMN tenant_source")
|
||||
conn.execute("PRAGMA user_version=5")
|
||||
with pytest.raises(StoreUnavailable, match="migrate"):
|
||||
Engine(path, auto_migrate=False)
|
||||
engine = Engine(path, clock=lambda: FROZEN)
|
||||
assert engine.storage_status()["schema_version"] == LATEST_SCHEMA_VERSION == 6
|
||||
assert engine.get(obj.id).entries[0]["tenant_source"] is None
|
||||
assert engine.get(obj.id).binding_digest == digest
|
||||
assert engine.get(obj.id).human_control is True
|
||||
assert engine._conn().execute("SELECT payload_json FROM outbox").fetchone()[0] == evidence
|
||||
engine.close()
|
||||
|
||||
|
||||
def test_provenance_rolls_back_with_failed_outbox(engine):
|
||||
obj = engine.create(binding(), validity(), human_control=True)
|
||||
engine.fail_outbox = True
|
||||
with pytest.raises(StoreUnavailable):
|
||||
engine.add_entry(obj.id, "human", principal_type="human", tenant_source="directory")
|
||||
engine.fail_outbox = False
|
||||
assert engine.get(obj.id).entries == []
|
||||
assert engine.get(obj.id).status == "requested"
|
||||
|
|
@ -9,7 +9,7 @@ flavor: residual
|
|||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-09-01"
|
||||
updated: "2026-10-03"
|
||||
updated: "2026-10-04"
|
||||
reviewed_at: "2026-09-01"
|
||||
reviewed_against_commit: "ebce5abb276c01ab29ce2526f3b8abb332dc9e90"
|
||||
reviewed_note: >-
|
||||
|
|
@ -30,8 +30,9 @@ Move the completed first-cut engine spine into an authenticated, durable,
|
|||
observable production service and prove one PEP integration end to end. This is
|
||||
the residual production scope deliberately excluded from APPROVAL-WP-0001.
|
||||
|
||||
The workplan is proposed pending review against the deployment estate and the
|
||||
current key-cape, access-engine, audit-core, and secrets-engine contracts.
|
||||
Current status: blocked after the 2026-10-04 dependency review. T02, T03 and
|
||||
T06 are complete. T01, T04 and T05 use `wait` for blocked task status; their
|
||||
remaining owner receipts are specified in `docs/production-dependencies.md`.
|
||||
|
||||
Review completed 2026-09-01. The plan is active. Production mode will verify
|
||||
KeyCape JWT signatures and exact issuer/audience/scopes; local caller-supplied
|
||||
|
|
@ -50,7 +51,10 @@ id: APPROVAL-WP-0002-T01
|
|||
status: wait
|
||||
flavor: residual
|
||||
priority: high
|
||||
blocking_reason: "Await key-cape's exact requester/human client registrations and native issuer proof with a real human flow; repository implementation is complete."
|
||||
blocked_on: message-from:key-cape
|
||||
needs_human: true
|
||||
blocking_reason: "Source records verified tenant provenance in schema v6. Existing native requester/human review evidence is delivered; await revision-pinned issuer/profile receipt from key-cape/informed-decision and founder-approved v6 release/migration with native entry/outbox readback."
|
||||
intervention_note: "Founder per-change approval is required before promoting the schema-v6 image and migrating production; no live change is authorized by this review."
|
||||
state_hub_task_id: "dc4523f5-e0af-5734-a0ef-07aa7f2b2a27"
|
||||
```
|
||||
|
||||
|
|
@ -546,7 +550,8 @@ id: APPROVAL-WP-0002-T04
|
|||
status: wait
|
||||
flavor: residual
|
||||
priority: high
|
||||
blocking_reason: "Source declaration migrated and emitter mapping verified; sender/native custody delivered 2026-09-11; await canonical receiver declaration and heartbeat class admission, service admission and actual lifecycle outbox/cadence proof plus bearer revocation (synthetic accepted/duplicate/read-refusal/readback already verified 2026-09-11), exact-window reconciliation and observed per-class heartbeat/lag findings. Existing AUDIT-WP-0009 observer implementation is reused."
|
||||
blocked_on: message-from:audit-core
|
||||
blocking_reason: "Sender custody, native synthetic delivery/readback and service deployment are delivered. Await audit-core acceptance of current cadence/class inventory, actual lifecycle drain and same-window reconciliation, class-silence/lag findings and scoped bearer-revocation receipt. Existing AUDIT-WP-0009 implementation is complete and reused."
|
||||
state_hub_task_id: "777a5cf5-c1db-5e07-8de6-ab109db5ccdc"
|
||||
```
|
||||
|
||||
|
|
@ -610,6 +615,8 @@ id: APPROVAL-WP-0002-T05
|
|||
status: wait
|
||||
flavor: residual
|
||||
priority: high
|
||||
blocked_on: message-from:secrets-engine
|
||||
blocking_reason: "Engine deployment and consumer read/consume credential delivery are delivered. Await secrets-engine named served PEP revision/route/identities and native exact-binding consume-before-side-effect/failure proof; requester/reviewer admission must match that act. Live T-06 additionally requires a complete engagement and plane admission with window/technique."
|
||||
state_hub_task_id: "3196fb77-3df0-5358-ae12-2d07bac12041"
|
||||
```
|
||||
|
||||
|
|
@ -805,3 +812,36 @@ approval.heartbeat alias while preserving the 86400s emission and requested
|
|||
explicitly pending. See docs/emission-cadence.md for the bounded owner acceptance
|
||||
requirements; T04 stays wait, and this workplan stays blocked. Source validation
|
||||
does not admit a workload or close its real delivery proof.
|
||||
|
||||
|
||||
## Dependency review and tenant provenance — 2026-10-04
|
||||
|
||||
Reviewed all tasks in all three workplans; only T01/T04/T05 in this workplan
|
||||
remain open. The 2026-09-27 assertion that no repository work was reachable is
|
||||
superseded: KEY-WP-0031 publishes `tenant_source`, and this engine had promised
|
||||
to retain it but discarded it. Implemented schema v6: signature-verified
|
||||
provenance survives entries and issuance/use threshold evidence; absent and
|
||||
legacy provenance remains null, malformed explicit claims fail before mutation,
|
||||
and HTTP body spoofing cannot supply it. Migration preserves bindings,
|
||||
human-control declarations and historical outbox bytes.
|
||||
|
||||
Existing receipts remove old dependencies: T03 is done with deployed schema v5;
|
||||
secrets-engine SECRETS-WP-0010 documents native consumer credential delivery;
|
||||
informed-decision INFD-WP-0001-T08 is done and its 2026-09-27 requester/create
|
||||
and human acceptance receipts are real. KEY-WP-0013 publishes the exact
|
||||
`informed-decision-approver` registration and KEY-WP-0031 implements tenant
|
||||
provenance. AUDIT-WP-0009 is finished; its implementation is reused, rather
|
||||
than described as an unfinished predecessor. None of these supplies a named
|
||||
served T-06 PEP or an engagement/admission. Whitehat reply
|
||||
`4ebf1784-36a5-43c0-8685-1a8cccf49006` retains that blocking gap.
|
||||
|
||||
Current requirements, owner records and the evidence needed to clear each wait
|
||||
are in `docs/production-dependencies.md`. No other repo was edited and no
|
||||
production change or packet was performed. Deployed v5 remains pinned and the
|
||||
runbook acknowledges that it predates v6. T03's historical deployment acceptance
|
||||
is retained; promotion of the new evidence field is part of T01 acceptance.
|
||||
|
||||
Validation: 181 tests pass, including 12 new signed-HTTP, malformed-claim,
|
||||
persistent migration/restart and transactional rollback cases. Repository
|
||||
implementation is complete for these open tasks; native owner acceptance and
|
||||
the per-change release/rollout gate remain. Workplan stays blocked.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue