Add opt-in Receipt Required gate for create_watch (RR-1)#77
Open
FutureEnterprises wants to merge 2 commits into
Open
Add opt-in Receipt Required gate for create_watch (RR-1)#77FutureEnterprises wants to merge 2 commits into
FutureEnterprises wants to merge 2 commits into
Conversation
create_watch is the one tool in the MCP server that mutates the world and spends a paid credit: it registers a 90-day webhook watch that POSTs to a caller-supplied callback_url. Today nothing requires a human to have authorized that exact callback_url + type. This adds an OPT-IN authorization-receipt gate in front of it, built on @emilia-protocol/require-receipt (Apache-2.0, fully offline, no API key or account). It is OFF by default and byte-identical to current behavior unless the operator sets TENSORFEED_RECEIPT_REQUIRED. When enabled, create_watch refuses to register the watch (and never spends the credit) unless the call carries an authorization_receipt bound to that exact type + callback_url; a valid receipt is consumed one-time only after a successful registration. - src/receipt-gate.ts: opt-in switch + gate wiring (binds receipt to type + callback_url; trusted-keys via env, inline-key fallback for demo) - src/index.ts: optional authorization_receipt arg; gate dispatch at the create_watch seam (original POST + formatting unchanged) - src/types/require-receipt.d.ts: minimal ambient types for the JS package - README.md: opt-in usage note Builds clean (tsc --noEmit), web test suite passes (83/83). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…keys; honest replay/manifest docs Acts on external review (CodesWhat/portkey-admin-mcp#15). create_watch no longer falls back to accepting self-signed (inline-key) receipts: with TENSORFEED_RECEIPT_REQUIRED=1 and no TENSORFEED_RECEIPT_TRUSTED_KEYS, the gate fails closed (receipt_enforcement_misconfigured) and never spends the credit; TENSORFEED_RECEIPT_ALLOW_INLINE_KEY=1 is an explicit non-prod opt-in. Manifest only advertised when served (TENSORFEED_RECEIPT_MANIFEST_URL). Replay store documented honestly (process-local default). tsc build clean.
Author
|
Hardened based on external review feedback (cf. CodesWhat/portkey-admin-mcp#15): the gate now fails closed without a pinned trusted issuer key (no self-signed acceptance by default — set |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
create_watchis the one tool in this MCP server that mutates the world and spends a paid credit: it registers a 90-day webhook watch that POSTs to a caller-suppliedcallback_url. Right now nothing requires that a human ever authorized that exactcallback_url+typebefore an agent registers it and burns the credit.This PR adds an opt-in authorization-receipt gate in front of
create_watchso the maintainer can require a verifiable human authorization receipt for it. Merging earns RR-1.What it does
create_watchbehaves byte-identically to today. The gate only engages when the operator setsTENSORFEED_RECEIPT_REQUIRED=1.create_watchrefuses to register the watch (and never spends the credit) unless the call carries anauthorization_receiptbound to that exacttype+callback_url: portable proof that a named human authorized this exact action.428 Receipt Required), no credit spentHow it's built
One dependency:
@emilia-protocol/require-receipt(Apache-2.0). Fully offline — no API key, no account, no EMILIA server trusted. The easy-to-get-wrong parts (target binding, consume-after-success so a transient failure never burns a valid approval, sanitized refusals) live in that reviewed gate; this PR just wires it to thecreate_watchseam. The original POST and output formatting are unchanged.src/receipt-gate.ts— opt-in switch + gate wiring (binds the receipt totype+callback_url; trusted issuer keys viaTENSORFEED_RECEIPT_TRUSTED_KEYS, with an inline-key fallback for setup-free demos)src/index.ts— optionalauthorization_receiptarg + gate dispatch at the seamsrc/types/require-receipt.d.ts— minimal ambient types (the package ships JS + JSDoc)mcp-server/README.md— short opt-in usage noteBuilds clean (
tsc --noEmit) and the web test suite passes (83/83).What this is / isn't
This is not auth ("who are you") or permissions ("are you allowed"). It's portable accountability evidence the service can keep for its own liability: proof a named human authorized an irreversible, paid action — a necessary, not sufficient condition. It does not prove the decision was wise or lawful. This is a reference implementation, not a vulnerability report — nothing here is exploitable today; it's an optional control you can switch on.
Spec: IETF Internet-Draft
draft-schrock-ep-authorization-receipts(an I-D, not an RFC).Happy to adjust the env var name, the assurance class, or which fields the receipt binds to before merge.