Connect policy-gated browser review and audit runtime

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
tegwick 2026-09-11 00:31:03 +02:00
parent 2cc32168ac
commit 83849b75d4
35 changed files with 2381 additions and 83 deletions

View file

@ -27,7 +27,7 @@ from .provenance import Claim, Route, assert_human_control_dischargeable
from .stance import resolve
from .records import disposition_from, dumps, memo_from, presentation_from
SCHEMA_VERSION = 1
SCHEMA_VERSION = 2
_SCHEMA = """
CREATE TABLE documents (digest TEXT PRIMARY KEY, media_type TEXT NOT NULL, content BLOB NOT NULL);
CREATE TABLE memos (id TEXT NOT NULL, version INTEGER NOT NULL, body TEXT NOT NULL, PRIMARY KEY(id,version));
@ -99,8 +99,19 @@ class Store:
triggers += (f"CREATE TRIGGER immutable_{table}_{operation} BEFORE {operation} ON {table} "
"BEGIN SELECT RAISE(ABORT,'immutable evidence'); END;\n")
db.executescript("BEGIN IMMEDIATE;" + _SCHEMA + triggers + "PRAGMA user_version=1; COMMIT;")
elif version != SCHEMA_VERSION:
elif version not in (1, SCHEMA_VERSION):
raise StoreError("unsupported evidence schema version")
if version in (0, 1):
db.executescript("""BEGIN IMMEDIATE;
CREATE TABLE policy_observations (id TEXT PRIMARY KEY, at TEXT NOT NULL,
request TEXT NOT NULL, decision TEXT, outcome TEXT NOT NULL,
usable_until REAL NOT NULL, response_sha256 TEXT,
decision_attributable INTEGER NOT NULL CHECK(decision_attributable=0));
CREATE TRIGGER immutable_policy_observations_UPDATE BEFORE UPDATE ON policy_observations
BEGIN SELECT RAISE(ABORT,'immutable evidence'); END;
CREATE TRIGGER immutable_policy_observations_DELETE BEFORE DELETE ON policy_observations
BEGIN SELECT RAISE(ABORT,'immutable evidence'); END;
PRAGMA user_version=2; COMMIT;""")
@contextmanager
def _connection(self):
@ -168,9 +179,35 @@ class Store:
raise EvidenceUnavailable("informed-decision cannot produce the named memo version")
return memo_from(json.loads(row["body"]))
def memo(self, memo_id):
def memo(self, memo_id, version=None):
with self._connection() as db:
return self._memo(db, memo_id)
return self._memo(db, memo_id, version)
def observe_policy(self, observation):
request = json.loads(observation.request_json)
_text(request["id"], "policy observation id")
with self._transaction() as db:
db.execute("INSERT INTO policy_observations VALUES (?,?,?,?,?,?,?,0)",
(request["id"], _now(), observation.request_json, observation.decision_json,
observation.outcome, observation.usable_until, observation.response_sha256))
return request["id"]
def policy_observations(self):
with self._connection() as db:
return [dict(r) for r in db.execute("SELECT * FROM policy_observations ORDER BY rowid")]
def _guard_policy(self, db, policy_id, memo, subject, action):
if policy_id is None:
return # Existing internal domain API; the browser always supplies one.
row = db.execute("SELECT * FROM policy_observations WHERE id=?", (policy_id,)).fetchone()
if row is None or row["outcome"] != "allow" or row["usable_until"] <= time.time():
raise Conflict("a current recorded policy observation is required")
request = json.loads(row["request"])
if (request["action"] != action or request["subject"]["id"] != subject
or request["resource"]["id"] != "memo:" + memo.id
or request["context"] != {"memo_version": memo.version, "approval_id": memo.approval_id,
"approval_binding_digest": memo.approval_binding_digest}):
raise Conflict("policy observation does not name this act")
def _presentation(self, db, presentation_id):
row = db.execute("SELECT body FROM presentations WHERE id=?", (presentation_id,)).fetchone()
@ -202,7 +239,8 @@ class Store:
db.execute("INSERT INTO outbox(id) VALUES (?)", (commitment.id,))
return commitment.id
def present(self, memo_id, *, principal_sub, tenant, principal_type, awareness=None):
def present(self, memo_id, *, principal_sub, tenant, principal_type, awareness=None,
expected_version=None, policy_id=None, approval_binding=None):
_text(principal_sub, "principal")
if (not isinstance(tenant, Claim) or tenant.value != "tenant:platform"
or tenant.route not in (Route.DIRECTORY, Route.REGISTRATION)
@ -210,17 +248,26 @@ class Store:
raise ValueError("platform tenant and principal provenance are required")
with self._transaction() as db:
memo = self._memo(db, memo_id)
if expected_version is not None and memo.version != expected_version:
raise Conflict("memo changed after the entitlement check")
self._guard_policy(db, policy_id, memo, principal_sub, "read")
if approval_binding is not None and approval_binding.get("digest") != memo.approval_binding_digest:
raise Conflict("observed act binding does not match this memo")
presentation = render(memo, principal_sub=principal_sub, tenant=tenant,
principal_type=principal_type, awareness=awareness)
content = {"memo": json.loads(dumps(memo)), "presentation": json.loads(dumps(presentation)),
"binding_document": memo.binding_document(), "awareness_document": memo.awareness_document(awareness)}
if policy_id is not None:
content["policy_observation_id"] = policy_id
if approval_binding is not None:
content["approval_binding"] = approval_binding # Observed act, never approval validity/state.
db.execute("INSERT INTO presentations VALUES (?,?,?,?)",
(presentation.id, memo.id, memo.version, dumps(presentation)))
commitment = commit_presentation(presentation, custody="informed-decision:presentations:" + presentation.id)
self._event(db, commitment, content)
return presentation
def acknowledge(self, presentation_id, actor: Actor, highlight_ids):
def acknowledge(self, presentation_id, actor: Actor, highlight_ids, *, policy_id=None):
ids = frozenset(highlight_ids)
with self._transaction() as db:
p = self._presentation(db, presentation_id)
@ -229,6 +276,7 @@ class Store:
raise DispositionRefused("G_ACTOR", "actor did not receive this presentation")
if memo.version != p.memo_version:
raise DispositionRefused("G_PRES", "presentation is stale")
self._guard_policy(db, policy_id, memo, actor.sub, "acknowledge")
if ids - {h.id for h in memo.highlights}:
raise DispositionRefused("G_ACK", "unknown highlight")
fresh = ids - p.acked_highlight_ids
@ -239,11 +287,11 @@ class Store:
db.execute("INSERT INTO acknowledgments VALUES (?,?,?)", (p.id, highlight_id, at))
p = replace(p, acked_highlight_ids=p.acked_highlight_ids | fresh)
c = commit_presentation(p, custody="informed-decision:presentations:" + p.id)
self._event(db, c, {"presentation": json.loads(dumps(p))},
self._event(db, c, {"presentation": json.loads(dumps(p)), "policy_observation_id": policy_id},
{"event_kind": "acknowledgment", "acknowledged_at": at})
return p
def record_disposition(self, presentation_id, actor, verb, *, operation_id, reasons=(), note=None):
def record_disposition(self, presentation_id, actor, verb, *, operation_id, reasons=(), note=None, policy_id=None):
_text(operation_id, "operation id")
if note is not None and (not isinstance(note, str) or len(note) > 8192):
raise ValueError("disposition note is too large or invalid")
@ -258,6 +306,7 @@ class Store:
return disposition_from(json.loads(existing["body"]))
p = self._presentation(db, presentation_id)
memo = self._memo(db, p.memo_id)
self._guard_policy(db, policy_id, memo, actor.sub, verb.value)
d = record(memo, p, verb, actor, reasons=tuple(reasons), note=note)
if verb in BINDING_VERBS:
if p.principal_type is None:
@ -272,11 +321,12 @@ class Store:
if verb is Verb.ACCEPT:
db.execute("INSERT INTO submissions(disposition_id,approval_id,subject) VALUES (?,?,?)", (d.id, memo.approval_id, actor.sub))
c = commit_disposition(d, custody="informed-decision:dispositions:" + d.id)
self._event(db, c, {"disposition": json.loads(dumps(d)), "presentation": json.loads(dumps(p))},
self._event(db, c, {"disposition": json.loads(dumps(d)), "presentation": json.loads(dumps(p)),
"policy_observation_id": policy_id},
{"submission_state": "prepared"} if verb is Verb.ACCEPT else None)
return d
def begin_submission(self, disposition_id):
def begin_submission(self, disposition_id, *, policy_id=None):
"""Reserve one external attempt, AFTER the caller's fresh policy check.
No network occurs in this store. A crash after this reservation stays
@ -290,6 +340,7 @@ class Store:
p = self._presentation(db, d.presentation_id)
if self._memo(db, p.memo_id).version != p.memo_version:
raise DispositionRefused("G_PRES", "presentation is stale")
self._guard_policy(db, policy_id, self._memo(db, p.memo_id), d.actor.sub, "accept")
attempt = str(uuid.uuid4())
db.execute("UPDATE submissions SET state='in_flight',attempt=? WHERE disposition_id=?", (attempt, disposition_id))
return attempt
@ -323,6 +374,27 @@ class Store:
row = db.execute("SELECT * FROM submissions WHERE disposition_id=?", (disposition_id,)).fetchone()
return dict(row) if row else None
def intent_for(self, approval_id, subject):
with self._connection() as db:
row = db.execute("SELECT s.*,d.presentation_id,d.operation_id FROM submissions s "
"JOIN dispositions d ON d.id=s.disposition_id WHERE s.approval_id=? AND s.subject=?",
(approval_id, subject)).fetchone()
return dict(row) if row else None
def dispositions_for(self, presentation_id):
with self._connection() as db:
return [disposition_from(json.loads(r[0])) for r in db.execute(
"SELECT body FROM dispositions WHERE presentation_id=? ORDER BY rowid", (presentation_id,))]
def presentation_content(self, presentation_id):
with self._connection() as db:
row = db.execute("SELECT content FROM evidence WHERE class=? AND "
"json_extract(envelope,'$.data.presentation_id')=? ORDER BY rowid LIMIT 1",
(EventClass.PRESENTATION.value, presentation_id)).fetchone()
if row is None:
raise EvidenceUnavailable("informed-decision cannot produce the presentation content")
return json.loads(row[0])
def retrieve_presentation(self, presentation_id):
"""Internal custody retrieval, not a browser export/entitlement route."""
with self._connection() as db: