Skip to content

The shared secret is transport, not authorisation - #20

Open
sumerjohal wants to merge 3 commits into
mainfrom
fix/internal-secret-no-bypass
Open

sumerjohal wants to merge 3 commits into
mainfrom
fix/internal-secret-no-bypass

Conversation

@sumerjohal

Copy link
Copy Markdown
Contributor

What

AR2_INTERNAL_SHARED_SECRET opened GET /list-artifact/{list_id} and GET /list-artifact/{list_id}/traceback with no credential and no MEAL entry (review ledger AG-076). Pancake, which holds the same secret, treats the header as transport only: its holders route still demands an authority credential and still logs. The two services disagreed about what the secret means, and AR2 had the weaker reading.

Change

Kept, and audited: the secret still opens one read of a lot's members. Pancake makes that read to mint a grant for the lot's owner (grants.py, fieldlists.py), before any grant exists, so it cannot present one. The read is now written to the ledger as list.read.internal with the hub subject Pancake forwards, before the members are returned. If the ledger write fails, the members are not disclosed (503).

Removed: the secret no longer opens trace-back. No service called it that way (checked Pancake: audit.py, fieldlists.py, grants.py); the walk takes a grant or an authority credential only.

MCP trace_back wrapper no longer passes the internal header. docs/MCP_SURFACE.md regenerated (no textual change).

Tests

app/tests/test_internal_secret.py, eight tests:

  • test_the_secret_does_not_open_trace_back fails against main (returned the walk, no ledger entry); its sibling returns the same lot's fields with a credential, so the control refuses a real answer.
  • test_the_secret_reads_members_and_the_read_is_in_the_ledger fails against main (no entry); test_the_ledger_write_happens_before_the_members_are_returned shows fail-closed.
  • Wrong secret, no secret, credential path unchanged, secret plus credential.

harness/bin/stomata run: 12/12. Suite 180 passed, 1 xfailed (was 172). Lint 36, unchanged.

Operator note

After this merges, every Pancake grant issuance writes one list.read.internal event to Pancake's /audit/events. That is the point. If Pancake's audit route is down, grant issuance fails rather than proceeding unrecorded.

Independent of #19; the two touch different routes in traceforward.py and merge in either order. Review finding A2, 2026-10-08.

sumerjohal and others added 2 commits October 11, 2026 09:13
AR2_INTERNAL_SHARED_SECRET opened GET /list-artifact/{list_id} and
GET /list-artifact/{list_id}/traceback with no credential and no MEAL entry
(review ledger AG-076). Pancake, which holds the same secret, treats the
header as transport only: its holders route still demands an authority
credential and still logs. The two services disagreed about what the secret
means, and AR2 had the weaker reading.

What the secret must open: one read of a lot's members, which Pancake makes
to mint a grant for the lot's owner before any grant exists. That read is
kept, and is now written to the ledger as list.read.internal, with the hub
subject Pancake forwarded, before the members are returned. If the ledger
cannot be written the members are not disclosed.

What it need not open: the trace-back walk. No service called it with the
secret. The bypass is removed and the route takes a credential only.

Tests: app/tests/test_internal_secret.py, eight tests. The trace-back
control and the ledger control fail against the previous code; each has a
sibling showing the same check fires on a real answer. One existing test
that read members internally now captures the ledger write.

stomata run: 12/12. Suite: 180 passed, 1 xfailed (was 172). Lint: 36, unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
…popping it

Other modules install theirs at import time; popping left later modules
running against the real dependency. Surfaced by the same pattern in #21.

Co-authored-by: Cursor <cursoragent@cursor.com>
…tRequest

credential_id is a required string there; the packet carried None and the
e2e workflow's Pancake answered 422, which AR2 correctly turned into a 503
and a refused read. The string now says there was no credential rather
than inventing one. The unit test asserts the four required fields are
strings, naming the Pancake schema it mirrors.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sumerjohal

Copy link
Copy Markdown
Contributor Author

CI's e2e job caught a contract mismatch on the first push: Pancake's AuditEventRequest requires credential_id as a string, the new list.read.internal packet carried None, Pancake answered 422 and AR2 correctly refused the read with 503. Fixed in 55f869b: the field now reads none:internal-shared-secret, and the unit test asserts the four required fields are strings, naming the Pancake schema. The fail-closed behaviour worked exactly as intended; the packet was wrong.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant