You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Define explicit authorization for releasing confidential data and derived outputs #660
A tool or ordinary API outside the approved confidential boundary may require a deliberate disclosure. Current sink ceilings in merged #657 conservatively propagate known labels; they do not decide whether a model-generated summary is safe to declassify.
Define a generic release-authorization contract before implementing one policy adapter. Model judgment is not an authorization source.
Acceptance criteria
Bind authorization to a principal with release authority, the recipient/audience, data or derivation scope, purpose where enforced, validity and policy version.
Distinguish unchanged confidentiality, authorized disclosure and unavailable evidence. Unknown labels or missing authorization cannot silently become a release.
Specify how derived outputs retain restrictions and what independently authorized transformation, if any, may lower them.
Bind approval to the actual released bytes or a precisely defined transformation contract; test output/recipient substitution, replay and policy changes.
Test prompt-injection attempts that ask the model to relabel or summarize secrets for an unapproved sink.
Record the boundary change without leaking the protected contents into audit evidence, and state that disclosure cannot be undone.
Relationships
Builds on #657 and depends on confinement for a deployment-wide claim. Reuse existing principal/delegation semantics from #568 and existing human-approval mechanisms where applicable. This issue does not authorize any disclosure itself.
I ran a boundary/failure-mode review of the merged exact-output milestone (#672). The authorization and replay properties look strong, but I think one acceptance criterion still has a crash-window gap:
Record the boundary change without leaking the protected contents into audit evidence.
Today ReplayStore durably records only request_id. The minimized ReleaseObservation is constructed after recipient.deliver(request.payload) and exists only as the return value.
That leaves this sequence:
approval verifies;
request ID is durably consumed;
recipient adapter performs the irreversible external side effect;
process terminates before release() returns (for example a BaseException, SIGKILL, host loss, etc.).
After restart, replay is correctly blocked, but the durable store contains only the consumed request ID. There is no durable minimized record that an authorized disclosure attempt crossed the boundary. This is not a replay bypass or second disclosure; it is an evidence gap at the irreversible transition.
A minimal invariant that seems to close it without logging protected material:
Before invoking an irreversible disclosure callback, persist a minimized attempt record durably. It can contain only an independent random event ID, disposition, reason and conservative delivery=unknown. On normal callback return, upgrade that event to acknowledged. If the process dies or the post-delivery update fails, the durable record remains unknown.
Important ordering detail: keep the existing post-reservation validity recheck before creating the disclosure-attempt record, so an approval that expires while waiting on the replay lock is consumed but is not falsely recorded as a boundary-crossing attempt.
If the pre-delivery audit write fails, do not call the recipient. If the post-delivery acknowledgement write fails, report/retain unknown; the disclosure cannot be undone.
The durable audit row should not contain payload, payload digest, principal, recipient, purpose, source scope, labels or approval. Those remain in the private authorization/replay context, while the audit-facing record stays minimized as #672 intended.
This seems separable from #659 confinement and from downstream installation/execution proof: it only closes the local durable evidence boundary around the release adapter itself.
@altrudev I checked the code and agree this gap exists. Please send a focused PR that saves the minimal attempt record before delivery, after the validity recheck. If that save fails, do not deliver. If delivery or the later audit update is interrupted, keep the outcome unknown. Include restart and storage-failure tests, and keep protected data out of the audit record.
@altrudev still wanted: the attempt record saved before recipient.deliver(), as above. Can you have a PR up by 9 October? If you would rather hand it back, say so and I will take it, crediting your finding.
It implements the requested ordering: verify -> consume -> recheck validity -> persist minimized attempt(unknown) -> deliver -> best-effort acknowledge.
The durable audit row contains only event ID, disposition, reason and delivery; protected release context stays out of it. Pre-delivery audit failure fails closed. Delivery interruption or post-delivery audit failure leaves the durable outcome unknown.
The attempt record before recipient.deliver() landed in #721: the row is persisted as unknown before delivery, acknowledged after, and corrected to not_attempted when the final validity recheck refuses. Keeping this open for the remaining criteria, derived-output restrictions and the prompt-injection relabelling tests.
Current-source reconciliation and a bounded remaining design slice, against main c2e438f44905ae4e5f089070982e95a54c3f39b5.
tests/unit/test_disclosure.py already has test_prompt_text_and_low_declared_label_do_not_authorize, exact-byte/label substitution, valid lower-ceiling approval and minimized-audit controls. The real gateway also accumulates session/catalog/caller sensitivity through sink ceilings; this is already documented and exercised in test_sink_policy.py. Derived-output inheritance is already written into the contract. These should not be proposed again as new features.
Remaining conceptual boundary: the trusted controller supplies accumulated labels and source/derivation scope. The adapter explicitly does not discover provenance or detect a controller omitting a source. A model-written public label cannot replace the accumulated restrictions. This is a producer/integration contract, not a claimed current adapter bypass.
Proposed first slice: one informative controller-to-release integration example using a declared synthetic source set. Freeze candidate bytes and complete controller provenance before owner review. Retain exact-byte approval; no generic summarization classifier or declassification transform.
Pair
Expected result
Public-only derivation within accepted ceiling / derivation also using a confidential source
Authorized disclosure / later unapproved derivation of the same source
First release only / source restrictions remain
Callback returns / callback may finish then raises
Adapter acknowledgement / unknown delivery with durable attempt
Unknown source coverage cannot be repaired by a model score. The example would establish behavior only for its declared source set, not discovery of arbitrary hidden dependencies or semantic safety of summaries.
Acceptance would exercise actual callbacks and durable audit rows, paired cases and isolated accumulation/approval/byte-binding weakenings. Accumulation belongs in the controller example; the adapter cannot check information it never receives. Keep public output separate from private authorization serialization and exclude protected-content digests.
Decision requested before code: select the owning controller/integration surface and accept the source-coverage matrix. A general authorized transformation instead of exact-byte review needs a separate policy decision and proof obligation. No production code or MCP request field is changed by this proposal; #660 remains open.
Executed current-source check: all 53 tests in test_disclosure.py pass on Python 3.12 with a hash-verified subset of the current development lock. In an isolated trial, replacing the accepted-boundary all-labels check with any-labels makes the three prompt/low-label controls fail assertions while four positive controls pass. The source was restored; the full 53-test selection passes again. This validates those existing controls, not a new controller implementation, full gateway suite or end-to-end confidentiality claim.
Preserving the controller-dependent cases here per Imran's #785 ruling. #785 now contains adapter unit tests rather than a second proposal page.
DO04: complete declared context versus an unresolved required source. The future controller example needs an explicit unavailable/no-delivery outcome before constructing an eligible release request; no new adapter enum is prescribed.
DO08: complete source inventory versus deliberate omission of a confidential source. An independently fixed source inventory must expose the controller premise violation. The adapter cannot infer omitted provenance from a valid signature or supplied low label.
Placement and the controller's representation remain open; neither case is claimed executed or closed. Complete source accumulation (the former DO01 integration distinction) also remains a trusted-controller obligation, not a new discovery capability of these adapter tests. Primary antecedents retained in #785: Denning for inherited restrictions (abstract/metadata access), CaMeL v2 for control/data separation, and disclosure-evidence draft -07 for coverage/reconciliation (individual draft, not IETF consensus). These references do not establish controller correctness. Prepared with Codex.
Activity
altrudev commentedon Sep 19, 2026
I ran a boundary/failure-mode review of the merged exact-output milestone (#672). The authorization and replay properties look strong, but I think one acceptance criterion still has a crash-window gap:
Today
ReplayStoredurably records onlyrequest_id. The minimizedReleaseObservationis constructed afterrecipient.deliver(request.payload)and exists only as the return value.That leaves this sequence:
release()returns (for example aBaseException, SIGKILL, host loss, etc.).After restart, replay is correctly blocked, but the durable store contains only the consumed request ID. There is no durable minimized record that an authorized disclosure attempt crossed the boundary. This is not a replay bypass or second disclosure; it is an evidence gap at the irreversible transition.
A minimal invariant that seems to close it without logging protected material:
Before invoking an irreversible disclosure callback, persist a minimized attempt record durably. It can contain only an independent random event ID, disposition, reason and conservative delivery=
unknown. On normal callback return, upgrade that event toacknowledged. If the process dies or the post-delivery update fails, the durable record remainsunknown.Important ordering detail: keep the existing post-reservation validity recheck before creating the disclosure-attempt record, so an approval that expires while waiting on the replay lock is consumed but is not falsely recorded as a boundary-crossing attempt.
That gives the sequence:
verify -> consume request ID -> recheck validity -> persist minimized attempt(unknown) -> deliver exact bytes -> best-effort durable acknowledgeIf the pre-delivery audit write fails, do not call the recipient. If the post-delivery acknowledgement write fails, report/retain
unknown; the disclosure cannot be undone.The durable audit row should not contain payload, payload digest, principal, recipient, purpose, source scope, labels or approval. Those remain in the private authorization/replay context, while the audit-facing record stays minimized as #672 intended.
This seems separable from #659 confinement and from downstream installation/execution proof: it only closes the local durable evidence boundary around the release adapter itself.
imran-siddique commentedon Sep 20, 2026
@altrudev I checked the code and agree this gap exists. Please send a focused PR that saves the minimal attempt record before delivery, after the validity recheck. If that save fails, do not deliver. If delivery or the later audit update is interrupted, keep the outcome unknown. Include restart and storage-failure tests, and keep protected data out of the audit record.
imran-siddique commentedon Sep 29, 2026
@altrudev still wanted: the attempt record saved before
recipient.deliver(), as above. Can you have a PR up by 9 October? If you would rather hand it back, say so and I will take it, crediting your finding.altrudev commentedon Oct 3, 2026
@imran-siddique PR #721 is up from the exact current upstream baseline.
It implements the requested ordering:
verify -> consume -> recheck validity -> persist minimized attempt(unknown) -> deliver -> best-effort acknowledge.The durable audit row contains only event ID, disposition, reason and delivery; protected release context stays out of it. Pre-delivery audit failure fails closed. Delivery interruption or post-delivery audit failure leaves the durable outcome unknown.
Validation:
PR: #721
imran-siddique commentedon Oct 6, 2026
The attempt record before
recipient.deliver()landed in #721: the row is persisted asunknownbefore delivery, acknowledged after, and corrected tonot_attemptedwhen the final validity recheck refuses. Keeping this open for the remaining criteria, derived-output restrictions and the prompt-injection relabelling tests.dinakarjs commentedon Oct 19, 2026
Current-source reconciliation and a bounded remaining design slice, against main
c2e438f44905ae4e5f089070982e95a54c3f39b5.tests/unit/test_disclosure.pyalready hastest_prompt_text_and_low_declared_label_do_not_authorize, exact-byte/label substitution, valid lower-ceiling approval and minimized-audit controls. The real gateway also accumulates session/catalog/caller sensitivity through sink ceilings; this is already documented and exercised in test_sink_policy.py. Derived-output inheritance is already written into the contract. These should not be proposed again as new features.Remaining conceptual boundary: the trusted controller supplies accumulated labels and source/derivation scope. The adapter explicitly does not discover provenance or detect a controller omitting a source. A model-written public label cannot replace the accumulated restrictions. This is a producer/integration contract, not a claimed current adapter bypass.
Proposed first slice: one informative controller-to-release integration example using a declared synthetic source set. Freeze candidate bytes and complete controller provenance before owner review. Retain exact-byte approval; no generic summarization classifier or declassification transform.
Unknown source coverage cannot be repaired by a model score. The example would establish behavior only for its declared source set, not discovery of arbitrary hidden dependencies or semantic safety of summaries.
Acceptance would exercise actual callbacks and durable audit rows, paired cases and isolated accumulation/approval/byte-binding weakenings. Accumulation belongs in the controller example; the adapter cannot check information it never receives. Keep public output separate from private authorization serialization and exclude protected-content digests.
Decision requested before code: select the owning controller/integration surface and accept the source-coverage matrix. A general authorized transformation instead of exact-byte review needs a separate policy decision and proof obligation. No production code or MCP request field is changed by this proposal; #660 remains open.
Executed current-source check: all 53 tests in test_disclosure.py pass on Python 3.12 with a hash-verified subset of the current development lock. In an isolated trial, replacing the accepted-boundary all-labels check with any-labels makes the three prompt/low-label controls fail assertions while four positive controls pass. The source was restored; the full 53-test selection passes again. This validates those existing controls, not a new controller implementation, full gateway suite or end-to-end confidentiality claim.
test(disclosure): exercise derived-output acceptance at adapter boundary
dinakarjs commentedon Oct 20, 2026
Preserving the controller-dependent cases here per Imran's #785 ruling. #785 now contains adapter unit tests rather than a second proposal page.
Placement and the controller's representation remain open; neither case is claimed executed or closed. Complete source accumulation (the former DO01 integration distinction) also remains a trusted-controller obligation, not a new discovery capability of these adapter tests. Primary antecedents retained in #785: Denning for inherited restrictions (abstract/metadata access), CaMeL v2 for control/data separation, and disclosure-evidence draft -07 for coverage/reconciliation (individual draft, not IETF consensus). These references do not establish controller correctness. Prepared with Codex.