Skip to content

Commit de3d782

Browse files
committed
Initial scaffold: bindu-scopeblind 0.1.0a1
Standalone Bindu extension emitting Ed25519-signed authorization receipts in the Veritas Acta format (draft-farley-acta-signed- receipts-02). Responds to three concerns raised on GetBindu/Bindu#459 review: 1. Embedded-key rejection. sign_receipt()/verify_receipt() refuse payloads containing verification_key / issuer_key / signer_public_key. require_conformance_check=True runs a negative- conformance vector at init. 2. Policy content anchoring. Cedar policies live in a file directory; policy_digest is sha256 of the concatenated source. Inline policy strings are not supported (avoids silent fallback to literal-string on path typos). 3. VOPRF scope clarification. This extension emits Ed25519 receipts (tier T1) only. VOPRF issuer-blind tokens (tier T4) are a separate ScopeBlind product and are not in scope for this extension. Plus the maintainer-requested package boundary: ships as a separate installable, not inside bindu/extensions/ core. Design posture: - Shadow mode is the DEFAULT. Enforcement requires explicit opt-in. - Enforce mode requires a non-empty cedar_policy_dir; empty is a configuration error. - Verification key source is external only (PinnedTrustAnchor, JwksKeySource, DidDocumentKeySource, AgentCardKeySource). - Agent card extension block published by ScopeBlindExtension so verifiers can resolve the issuer pubkey without ever seeing it in the receipt body. Tests: 19 passing, covering default posture (4), signing and chain linkage (4), embedded-key rejection (4), tamper detection (1), key sources (4), agent card extension (1), enforce mode (1). Package files: - pyproject.toml, README.md, DESIGN.md, CALL-AGENDA.md - bindu_scopeblind/{__init__, extension, middleware, receipts, key_sources, cedar_bridge, conformance}.py - tests/test_extension.py CALL-AGENDA.md is the pre-call artifact for the design review with @raahulrahl scheduled for week of 2026-04-22.
0 parents  commit de3d782

15 files changed

Lines changed: 1684 additions & 0 deletions

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
__pycache__/
2+
*.py[cod]
3+
*$py.class
4+
*.so
5+
.Python
6+
build/
7+
dist/
8+
*.egg-info/
9+
.pytest_cache/
10+
.tox/
11+
.coverage
12+
htmlcov/
13+
.venv/
14+
venv/
15+
.env
16+
*.bak

CALL-AGENDA.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Proposed agenda: bindu-scopeblind design review
2+
3+
30 minutes. Proposed by @raahulrahl on [GetBindu/Bindu#459](https://github.com/GetBindu/Bindu/pull/459). Slot TBD from the three offered (Mon 22 Apr 10:00 UTC / Wed 24 Apr 14:00 UTC / Thu 25 Apr 08:00 UTC).
4+
5+
## Attendees (proposed)
6+
7+
- @raahulrahl (Bindu, chair)
8+
- @tomjwxf (ScopeBlind / Veritas Acta)
9+
- @vipul674 (author of #459, optional)
10+
- @desiorac (ArkForge, optional)
11+
12+
## Reading to bring to the call
13+
14+
- This package's [`README.md`](./README.md) and [`DESIGN.md`](./DESIGN.md).
15+
- The relevant sections of [draft-farley-acta-signed-receipts-02](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/) (§9 Key Distribution; appendix Implementation Status).
16+
- [`ScopeBlind/agent-governance-testvectors`](https://github.com/ScopeBlind/agent-governance-testvectors) negative vectors directory.
17+
- [`VeritasActa/agt-integration-profile`](https://github.com/VeritasActa/agt-integration-profile) for the AGT-to-receipt normative mapping (mirrors what this extension will emit).
18+
19+
## Agenda
20+
21+
### Block 1: Shape of the shadow-mode-first plan (10 min)
22+
23+
- Confirm the default posture (shadow, no policy-dir required, receipts observable only).
24+
- Confirm the enforce-mode contract (explicit opt-in, policy-dir required, optional `require_conformance_check=True`).
25+
- Confirm what passes the bar for flipping the default to enforce at the Bindu core level (vs. at the operator level). Candidate criteria documented in [`DESIGN.md`](./DESIGN.md) §Open Questions.
26+
27+
### Block 2: Key anchoring model (10 min)
28+
29+
- Walk through the four `KeySource` subclasses (`PinnedTrustAnchor`, `JwksKeySource`, `DidDocumentKeySource`, `AgentCardKeySource`).
30+
- Decide the preferred default for Bindu agents. The DID-document path reuses existing Bindu infrastructure; the agent-card-extension path is new but A2A-native. Either or both.
31+
- Decide where the policy manifest URL lives (if anywhere): agent card extension field, DID document service endpoint, or deferred.
32+
33+
### Block 3: Test-vector hookup and enforcement readiness (10 min)
34+
35+
- Negative conformance vectors from [`agent-governance-testvectors`](https://github.com/ScopeBlind/agent-governance-testvectors) wired into this package's CI.
36+
- Agreement on the specific vectors that must fail-closed before enforce-mode default is acceptable.
37+
- Agreement on what "ready to enable enforcement by default" looks like: which vectors, which implementation count in the draft-02 appendix, which third-party reviews.
38+
39+
## Outputs expected from the call
40+
41+
- A merged plan (written up in this repo or in a Bindu-side doc) with decisions on:
42+
- Package repo location (assumed: `ScopeBlind/bindu-scopeblind`).
43+
- Default KeySource for Bindu agents.
44+
- Policy manifest publication convention (or explicit deferral).
45+
- CI conformance gate specification.
46+
- Criteria for enforce-mode default flip.
47+
- Owner + rough date for a 0.1.0 tag.
48+
- Disposition of [#459](https://github.com/GetBindu/Bindu/pull/459): close with thanks to @vipul674, or merge a stripped-down version as a shadow-only receipt-attachment hook inside Bindu core while this package owns the full extension surface.
49+
50+
## What this call is NOT
51+
52+
- Not a live demo. Demos move faster over async review of the code.
53+
- Not a VOPRF deep-dive. VOPRF is orthogonal to this extension (see [`DESIGN.md`](./DESIGN.md) §3); separate conversation if useful.
54+
- Not a commitment to specific Bindu core changes. The extension is designed to work against existing Bindu primitives (agent card, DID document, middleware pipeline) without core modifications.

DESIGN.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Design notes: bindu-scopeblind
2+
3+
Pre-call design document for the Bindu maintainer review (week of 2026-04-22). Captures the three concerns raised on [GetBindu/Bindu#459](https://github.com/GetBindu/Bindu/pull/459) and how this package addresses them.
4+
5+
## 1. Key anchoring (embedded-key rejection)
6+
7+
**Concern raised on #459.** `verify_receipt()` in that PR falls back to `receipt.get("verification_key")` when no external key is passed. That collapses the issuer-blind property: a malicious issuer signs any payload with a fresh key pair and ships the matching pubkey in the receipt body.
8+
9+
**Resolution in this package.**
10+
11+
- No embedded-key fallback exists. `sign_receipt()` REJECTS payloads containing `verification_key`, `issuer_key`, or `signer_public_key`. `verify_receipt()` rejects envelopes whose payload contains any of those fields even when an external key is also provided.
12+
- A negative-conformance check can be wired at `ScopeBlindExtension` construction via `require_conformance_check=True`. The check exercises a synthetic embedded-key receipt and raises if the library accepts it.
13+
- Key anchoring is pluggable via four reference `KeySource` subclasses: `PinnedTrustAnchor`, `JwksKeySource`, `DidDocumentKeySource`, `AgentCardKeySource`. Operators pick the transport that fits their Bindu deployment.
14+
- The extension exposes `ScopeBlindExtension.agent_card_extension()` so Bindu can publish the issuer pubkey via the A2A agent card mechanism without the receipt body ever carrying it.
15+
16+
**Spec anchor.** [draft-farley-acta-signed-receipts-02 §9 (Key Distribution and Trust Anchors)](https://datatracker.ietf.org/doc/draft-farley-acta-signed-receipts/) establishes this requirement normatively. draft-02 is being submitted to datatracker this week.
17+
18+
## 2. Policy content anchoring
19+
20+
**Concern raised on #459.** Receipts commit to a `policy_hash` but a verifier has no way to recover the policy text, so the hash proves which policy ran but not what it says.
21+
22+
**Resolution in this package.**
23+
24+
- Cedar policies MUST live in a file directory (`cedar_policy_dir`). The directory is scanned for `.cedar` files at extension startup; their concatenated source is hashed into `payload.policy_digest` as `sha256:<hex>`.
25+
- Operators publishing the policy text at a discoverable URL (alongside the JWKS) close the loop. The current release does not ship a publication mechanism; this is called out as an open design question for the call.
26+
- Inline policy strings are NOT supported. This avoids the silent-fallback-to-literal-string path where a typo like `policies/authz.cedar` survives startup and fails only during authorization.
27+
28+
**Open for discussion on the call.** Does Bindu want a normative `policyManifestUrl` field in the agent card extension? Or should the policy text live in the DID document service endpoint? Both are viable; we defer the choice.
29+
30+
## 3. VOPRF scope clarification
31+
32+
**Concern raised on #459.** "VOPRF (RFC 9497) solves a different problem than 'verify a signature without trusting the signer.'"
33+
34+
**Correct. The original issue text conflated two layers.**
35+
36+
This extension emits **Ed25519 signed receipts** (conformance tier T1), not VOPRF tokens. The VOPRF tier is structurally separate:
37+
38+
| Primitive | What this extension ships | Where VOPRF lives in the wider stack |
39+
|---|---|---|
40+
| Ed25519 receipts (tier T1) | Yes, all decision receipts | - |
41+
| JCS canonicalization (RFC 8785) | Yes | - |
42+
| External key anchoring | Yes, 4 reference sources | - |
43+
| Selective disclosure (AIP-0002) | Yes, pass-through | - |
44+
| VOPRF issuer-blind tokens (tier T4) | No | Verification currently PARTIAL in `@veritasacta/verify@0.5.0` (structural checks only; full dual DLEQ proof verification is being extracted from `/veritasacta-verify/` into the unified binary). Issuance is served by a proprietary ScopeBlind Cloudflare Worker at `api.scopeblind.com`. |
45+
46+
The "issuer-blind" property in the Ed25519 tier comes from external key resolution (verifier does not have to trust the server emitting the receipt; the verifier resolves the key via a channel independent of the receipt body). This is a weaker property than VOPRF-style anonymous credentials but is the right property for authorization receipts, where the signer identity is expected to be known and carried in the agent card / DID document.
47+
48+
For this extension, the VOPRF state does not matter. Bindu agents using this extension produce Ed25519 receipts that verify against `@veritasacta/verify`'s Ed25519 engine (production-ready). The partial VOPRF engine in the same binary is orthogonal: it processes a different token type this extension never emits.
49+
50+
## 4. Shadow-mode-first adoption pattern
51+
52+
**Guidance from the #459 review.** "Shadow mode only in the first iteration. No enforcement path. Receipts are produced, logged, attached to OpenTelemetry spans, but nothing in the task flow gates on them."
53+
54+
**Resolution in this package.**
55+
56+
- `ReceiptMode.SHADOW` is the default. No enforcement logic runs unless the operator passes `mode=ReceiptMode.ENFORCE` explicitly.
57+
- Enforce mode requires a non-empty Cedar policy directory. Constructor raises `ValueError` if misconfigured.
58+
- Enforce mode plus `require_conformance_check=True` is the full-lockdown configuration. A CI pipeline wiring the negative-conformance vector set from [`ScopeBlind/agent-governance-testvectors`](https://github.com/ScopeBlind/agent-governance-testvectors) is the recommended precondition for flipping default to enforce.
59+
60+
## 5. Package boundary
61+
62+
**Guidance from the #459 review.** "We'd prefer this live as a separate installable extension (e.g. `bindu-scopeblind`) rather than inside `bindu/extensions/` in the core repo."
63+
64+
**Resolution.** This package is that separate installable. Planned publication at [ScopeBlind/bindu-scopeblind](https://github.com/ScopeBlind/bindu-scopeblind) (repo not yet public at the time of drafting). Release cadence is independent of Bindu core. The only Bindu-core touchpoint required is that the agent card publishing path supports extension blocks, which it already does per existing patterns (DID extension, X402 extension).
65+
66+
## 6. Threat model
67+
68+
The extension protects against:
69+
70+
- **Tampering with signed decisions in transit or at rest.** Ed25519 + JCS canonicalization + `previousReceiptHash` chain linkage make any modification detectable by a verifier who already holds the issuer public key.
71+
- **Receipt forgery by third parties.** Verifier external key resolution ensures only the operator can produce receipts that verify against the agent card / DID document / JWKS.
72+
73+
The extension does NOT protect against:
74+
75+
- **A compromised operator.** If the operator's signing key is exfiltrated, the attacker can produce valid-looking receipts. Key rotation and revocation are operator responsibilities.
76+
- **A compromised verifier key-source endpoint.** If the JWKS URL is hijacked, verifiers can be pointed at attacker-controlled keys. Operators pinning trust anchors (`PinnedTrustAnchor`) avoid this class of attack.
77+
- **Policy content confusion.** The extension commits to `policy_digest` but does not enforce publication of the policy text. An operator running a permissive policy hashed as `sha256:...` can claim any policy without publishing; verifiers who do not fetch the policy text have no defense. This is the same gap the #459 review identified; resolving it requires a publication convention (see §2).
78+
79+
## Open questions for the call
80+
81+
1. **Policy manifest publication.** Agent card extension field or DID document service endpoint?
82+
2. **Revocation signal.** Does a revoked issuer pubkey get published back into the agent card extension, or is that an agent-card-level change?
83+
3. **OpenTelemetry integration.** Is the `@scopeblind/otel-exporter` Python port on Bindu's roadmap, or does Bindu want to consume the OpenTelemetry span attributes via the existing observability bus?
84+
4. **Enforce-mode default flip.** What passes your internal bar? Passing a named vector set? A specific implementation count? A Microsoft AGT conformance listing? Signet conformance listing?

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Tom Farley (ScopeBlind)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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

Comments
 (0)