Skip to content

Define explicit authorization for releasing confidential data and derived outputs #660

Open

Description

@imran-siddique

Parent tracker: agentrust-io/.github#41

Problem and scope

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.

Activity

  1. altrudev commented on Sep 19, 2026

    @altrudev
    Contributor

    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:

    1. approval verifies;
    2. request ID is durably consumed;
    3. recipient adapter performs the irreversible external side effect;
    4. 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.

    That gives the sequence:

    verify -> consume request ID -> recheck validity -> persist minimized attempt(unknown) -> deliver exact bytes -> best-effort durable acknowledge

    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.

  2. imran-siddique commented on Sep 20, 2026

    @imran-siddique
    MemberAuthor

    @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.

  3. imran-siddique commented on Sep 29, 2026

    @imran-siddique
    MemberAuthor

    @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.

  4. altrudev commented on Oct 3, 2026

    @altrudev
    Contributor

    @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:

    • focused disclosure/sink tests: 82 passed
    • full repository suite: 2410 passed, 37 skipped
    • git diff --check: clean

    PR: #721

  5. imran-siddique commented on Oct 6, 2026

    @imran-siddique
    MemberAuthor

    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.

  6. dinakarjs commented on Oct 19, 2026

    @dinakarjs
    Contributor

    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 Unchanged release / inherited confidential restriction requires approval
    Ordinary text / instruction to relabel the same confidential derivation public Same accumulated restrictions; text grants no authority
    Valid exact-summary approval / altered summary with old approval Authorized release / binding refusal before callback
    Complete trusted source context / unresolved required source context Eligible request / integration reports unavailable and performs no release
    Explicit release signer / catalog reviewer or Cedar advice without release grant Scoped approval / no disclosure authority
    Permitted recipient/scope/policy / substituted context Exact authorized delivery / named refusal
    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.

  7. dinakarjs commented on Oct 20, 2026

    @dinakarjs
    Contributor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions