|
| 1 | +# bindu-scopeblind |
| 2 | + |
| 3 | +**Bindu extension for Ed25519-signed authorization receipts in the Veritas Acta format.** Shadow mode is the default; enforcement is an explicit opt-in. No embedded-key fallback, ever. |
| 4 | + |
| 5 | +Ships as a separate installable package (per the design discussion on [GetBindu/Bindu#459](https://github.com/GetBindu/Bindu/pull/459)): keeps the Bindu core review surface narrow and lets this extension iterate on its own cadence. |
| 6 | + |
| 7 | +> **Status.** Public Preview (0.1.0a1). Call-prep artifact for a design review with the Bindu maintainers scheduled for the week of 2026-04-22. API is expected to stabilize before 0.1.0. |
| 8 | +
|
| 9 | +## Install |
| 10 | + |
| 11 | +```bash |
| 12 | +pip install bindu-scopeblind # core: Ed25519 + JCS + agent card extension |
| 13 | +pip install "bindu-scopeblind[cedar]" # + Cedar policy evaluation (enforce mode) |
| 14 | +``` |
| 15 | + |
| 16 | +## Posture |
| 17 | + |
| 18 | +| Property | Choice | Rationale | |
| 19 | +|---|---|---| |
| 20 | +| Default mode | `shadow` | Receipts observable before enforcement lands. Aligns with [GetBindu/Bindu#459](https://github.com/GetBindu/Bindu/pull/459) review guidance. | |
| 21 | +| Verification key source | External only (agent card, DID doc, JWKS, pinned trust anchor) | Embedded keys fail verification because a malicious issuer could sign any payload with a fresh key pair. See [draft-farley-acta-signed-receipts-02 §9](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/). | |
| 22 | +| Cedar policies | File directory only | Avoids silent-fallback-to-inline-string path typos. Enforce mode with no `.cedar` files is a configuration error. | |
| 23 | +| Receipts format | [Veritas Acta (draft-02)](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/) | Byte-compatible with `@veritasacta/verify@0.5` and AGT integrations (protect-mcp, sb-runtime, protect-mcp-adk). | |
| 24 | +| Scope | Ed25519 receipts only (tier T1) | VOPRF issuer-blind tokens (tier T4) are a separate primitive served by a different product (`@scopeblind/verifier`). This extension does NOT emit VOPRF tokens. | |
| 25 | + |
| 26 | +## Quick start (shadow mode) |
| 27 | + |
| 28 | +```python |
| 29 | +from pathlib import Path |
| 30 | +from bindu import BinduConfig |
| 31 | +from bindu_scopeblind import ScopeBlindExtension |
| 32 | + |
| 33 | +config = BinduConfig( |
| 34 | + name="my-agent", |
| 35 | + extensions=[ |
| 36 | + ScopeBlindExtension( |
| 37 | + signer_key_path=Path("/etc/scopeblind/issuer.key"), # PEM Ed25519 private key |
| 38 | + cedar_policy_dir=Path("./policies"), # directory of .cedar files |
| 39 | + # mode=ReceiptMode.SHADOW is the default; omit to get it. |
| 40 | + ) |
| 41 | + ], |
| 42 | +) |
| 43 | +``` |
| 44 | + |
| 45 | +Every governed request produces a signed receipt attached to `request.state.scopeblind_receipt`. Task / artifact metadata includes the receipt. Nothing in the task flow gates on the decision in shadow mode. |
| 46 | + |
| 47 | +## Enforce mode (explicit opt-in) |
| 48 | + |
| 49 | +```python |
| 50 | +from bindu_scopeblind import ScopeBlindExtension, ReceiptMode |
| 51 | + |
| 52 | +ScopeBlindExtension( |
| 53 | + signer_key_path=Path("/etc/scopeblind/issuer.key"), |
| 54 | + cedar_policy_dir=Path("./policies"), # required in enforce mode |
| 55 | + mode=ReceiptMode.ENFORCE, |
| 56 | + require_conformance_check=True, # CI gate |
| 57 | +) |
| 58 | +``` |
| 59 | + |
| 60 | +`require_conformance_check=True` runs a minimal negative-conformance vector at construction: confirms the build rejects receipts carrying their own verification key. Construction fails if the check fails closed. CI operators SHOULD wire the full negative-vector set from [`ScopeBlind/agent-governance-testvectors`](https://github.com/ScopeBlind/agent-governance-testvectors) into their build. |
| 61 | + |
| 62 | +## Verification |
| 63 | + |
| 64 | +Receipts verify with `@veritasacta/verify` with no runtime dependency on Bindu or this extension: |
| 65 | + |
| 66 | +```bash |
| 67 | +# Operator publishes the issuer public key out-of-band (agent card / DID doc / JWKS) |
| 68 | +npx @veritasacta/verify receipt.json --jwks https://agent.example/.well-known/jwks.json |
| 69 | +``` |
| 70 | + |
| 71 | +Or, from Python, using one of the provided `KeySource` subclasses: |
| 72 | + |
| 73 | +```python |
| 74 | +from bindu_scopeblind import ( |
| 75 | + AgentCardKeySource, |
| 76 | + JwksKeySource, |
| 77 | + DidDocumentKeySource, |
| 78 | + PinnedTrustAnchor, |
| 79 | + verify_receipt, |
| 80 | +) |
| 81 | + |
| 82 | +source = AgentCardKeySource(agent_card=agent_card_json) |
| 83 | +pub = source.resolve(receipt["signature"]["kid"]) |
| 84 | +assert verify_receipt(receipt, pub) |
| 85 | +``` |
| 86 | + |
| 87 | +## Key anchoring |
| 88 | + |
| 89 | +Four reference sources ship in `bindu_scopeblind.key_sources`: |
| 90 | + |
| 91 | +- `PinnedTrustAnchor`: single pinned Ed25519 public key. Simplest case. |
| 92 | +- `JwksKeySource`: resolves from a parsed JWKS document. |
| 93 | +- `DidDocumentKeySource`: resolves from a DID document's `verificationMethod` list. Fits Bindu's existing DID-first agent identity model. |
| 94 | +- `AgentCardKeySource`: resolves from an A2A agent card's `extensions["veritasacta:issuer"]` block. Returned by `ScopeBlindExtension.agent_card_extension()` so Bindu can publish it. |
| 95 | + |
| 96 | +The extension does not bake in HTTP-fetching behavior; operators choose the transport. This keeps the core module offline-by-default and testable without network. |
| 97 | + |
| 98 | +## Receipt payload |
| 99 | + |
| 100 | +```json |
| 101 | +{ |
| 102 | + "payload": { |
| 103 | + "type": "scopeblind:decision", |
| 104 | + "issuer_id": "scopeblind:<12-char-thumbprint>", |
| 105 | + "agent_id": "did:bindu:acme:searchbot", |
| 106 | + "action": "message/send:search", |
| 107 | + "decision": "allow", |
| 108 | + "mode": "shadow", |
| 109 | + "policy_id": "scopeblind-policy:policies", |
| 110 | + "policy_digest": "sha256:...", |
| 111 | + "jsonrpc_method": "message/send", |
| 112 | + "path": "/a2a", |
| 113 | + "request_id": "req-1234", |
| 114 | + "issued_at": "2026-04-19T...Z", |
| 115 | + "previousReceiptHash": "..." |
| 116 | + }, |
| 117 | + "signature": { |
| 118 | + "alg": "EdDSA", |
| 119 | + "kid": "<operator JWK thumbprint>", |
| 120 | + "sig": "<base64url>" |
| 121 | + } |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +All fields covered by the Ed25519 signature. Tampering with any field fails verification. |
| 126 | + |
| 127 | +## Design artifacts |
| 128 | + |
| 129 | +- [`DESIGN.md`](./DESIGN.md): key-anchoring decision, shadow-mode semantics, threat model. |
| 130 | +- [`CALL-AGENDA.md`](./CALL-AGENDA.md): proposed agenda for the Bindu maintainer design review call. |
| 131 | + |
| 132 | +## License |
| 133 | + |
| 134 | +MIT. See `LICENSE` (to be added on repo publication). |
| 135 | + |
| 136 | +## Related |
| 137 | + |
| 138 | +- [GetBindu/Bindu#439](https://github.com/GetBindu/Bindu/issues/439): originating feature request. |
| 139 | +- [GetBindu/Bindu#459](https://github.com/GetBindu/Bindu/pull/459): alternative implementation in the Bindu core repo (superseded by this standalone package per maintainer guidance). |
| 140 | +- [draft-farley-acta-signed-receipts-02](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/): receipt format specification. |
| 141 | +- [VeritasActa/agt-integration-profile](https://github.com/VeritasActa/agt-integration-profile): normative AGT-to-receipt field mapping. |
0 commit comments