feat(gateway): verification hardening + docs consolidation (post-#487)#488
feat(gateway): verification hardening + docs consolidation (post-#487)#488raahulrahl merged 6 commits intomainfrom
Conversation
…his session
Living list of concrete, file-path-grounded defects and gaps. 20 entries
across 5 categories (security/correctness, reliability/observability,
recipes, fleet/DX, API surface, tests, docs). Severity-tagged so a
reader can triage:
* 3 high-severity items — vacuous signatures.ok=true, dead AgentCard
fallback code, and the permission model's "ask" being silently
equivalent to "allow".
* 10 medium items — catalog mutation on resumed sessions, no request
size limit, no observability, no hot reload for recipes, etc.
* 7 low items — stale names, missing tests, docs gaps.
Each entry has a file path + line number so "I'll fix one" is a concrete
task, not a discovery project. Adds a short "How to add to this list"
footer so future contributors know the format.
No code changes. Meant to be the gateway's equivalent of the "known
issues" sections most production projects keep but this one was missing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI's detect-secrets hook flagged docs/openapi.yaml:598 as a "Base64 High Entropy String." That line is a base58-encoded Ed25519 signature used as an API example — signatures are public by design (that's the whole point), not a secret. Adding the standard `# pragma: allowlist secret` inline comment tells the scanner to skip this specific line without loosening global rules. Matches the pattern the project CLAUDE.md §Recent Learnings documents for .env.example files. Fixes https://github.com/GetBindu/Bindu/actions/runs/24672201148
…s pass Addresses the first high-severity entry in BUGS_AND_KNOWN_ISSUES.md. Before: the <remote_content> envelope collapsed `signatures.ok` (a boolean) into a three-valued attribute — yes/no/unknown. When zero artifacts were signed, `ok` was vacuously true (nothing to fail) and the envelope read `verified="yes"` — indistinguishable to the planner LLM from a real cryptographic pass. After: four-valued `verified` attribute driven by a new pure helper computeVerifiedLabel(signatures): null → "unknown" !ok → "no" ok && signed === 0 → "unsigned" (NEW) ok && signed > 0 && signed === verified → "yes" The authoritative `signatures.ok` in the Bindu client stays unchanged — "no failures" is still a useful concept for the DB audit row and SSE signatures field. The label is just a richer projection for the planner's benefit. Implementation: - New exported VerifiedLabel type + computeVerifiedLabel() in planner - wrapRemoteContent takes the label directly (not a boolean) - Call site passes the full signatures object through the new helper Test: tests/planner/verified-label.test.ts — 7 cases pinning each branch, including the regression (vacuous-yes becomes "unsigned"). openapi.yaml: SSEEvent_TaskArtifact.content description updated to enumerate the four labels with semantics. BUGS_AND_KNOWN_ISSUES.md: entry marked RESOLVED with reference to the helper and the test. Typecheck clean, 201/201 tests pass, redocly lint 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves the second high-severity entry in BUGS_AND_KNOWN_ISSUES.md —
the `peer.card` fallback at bindu/client/index.ts:196 was dead code
because nothing in the codebase ever populated it. Signature
verification required a pinnedDID; with none set, maybeVerifySignatures
always short-circuited.
New src/bindu/client/agent-card.ts — fetchAgentCard(peerUrl, opts):
- GETs /.well-known/agent.json, Zod-parses against the AgentCard
schema, caches per-process.
- 2-second default timeout (short because it blocks the first call
per peer; callers can override).
- Every failure mode degrades to null: non-2xx, malformed JSON,
schema mismatch, network error, abort. Failures are cached too so
a flaky peer doesn't cost one outbound fetch per /plan.
Wired into runCall at bindu/client/index.ts: fetches only when
`trust.verifyDID: true` AND peer.card isn't already set. No cost on
peers that don't care about verification. Mutation of input.peer.card
is safe because PeerDescriptor is built fresh per catalog entry per
request.
End-to-end effect: `trust.verifyDID: true` WITHOUT `pinnedDID` is now
a real feature. The gateway observes the peer's published DID,
resolves its public key via the DID document, and verifies every
artifact signature against it. Pinned wins over observed when both
are set (pinning is a stronger security claim — the caller vouched
for the identity, while observed just trusts what the peer published).
Coverage: tests/bindu/agent-card-fetch.test.ts — 9 cases:
- success path parses valid card
- 404 returns null
- malformed JSON returns null
- schema mismatch returns null
- network throw returns null
- cache hit skips re-fetch (same reference returned)
- negative cache skips re-fetch on known-bad peer
- per-URL isolation — different peers fetched independently
- timeout honored via internal AbortController
BUGS_AND_KNOWN_ISSUES.md: original AgentCard entry marked RESOLVED
with cross-refs to the helper and test. A new medium entry now
tracks the remaining follow-up — SSE's agent_did field still only
reflects pinned DIDs, not observed ones. That's plan-route.ts layer
(findPinnedDID → findAgentDID with observed fallback), scheduled
next.
Typecheck clean, 210/210 tests pass (+9 agent-card-fetch).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves the "SSE agent_did doesn't surface observed DIDs" follow-up
from the AgentCard fetch work. Before: findPinnedDID read only
trust.pinnedDID, so callers who didn't pin saw agent_did: null in
every task.* frame even after the Bindu client had observed the peer's
DID via AgentCard. Signature verification worked against the observed
DID; the display layer didn't know about it.
Changes:
- New findAgentDID(request, observedByName, agentName) → {did, source}
with precedence pinned > observed > null. Replaces findPinnedDID.
- New `agent_did_source: "pinned" | "observed" | null` field on every
task.started / task.artifact / task.finished SSE frame so consumers
can distinguish caller-vouched pinned DIDs from self-reported
observed DIDs and apply their own trust policies per frame.
- Upfront AgentCard discovery at /plan start via Promise.allSettled
with a 2s TOTAL budget shared across the catalog (not 2s per peer).
Results populate the observedByName map and hit the per-process
fetchAgentCard cache, so the Bindu client's verification path gets
them for free on subsequent calls.
openapi.yaml: new shared AgentDIDSource schema with trust-policy docs
referenced from all three task.* events. Each affected event's
required fields list gains `agent_did_source`.
Coverage: tests/api/find-agent-did.test.ts — 6 cases pinning precedence
(pinned wins even when observed exists), per-agent independence,
graceful null for agent names outside the catalog (load_recipe passes
through this helper too).
BUGS_AND_KNOWN_ISSUES.md: entry marked RESOLVED with cross-refs.
Typecheck clean, 216/216 tests pass (+6 find-agent-did), redocly lint
0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…WAY.md moves to repo-level docs/
Matches the repo's existing doc layout:
- /docs/ has topic-based guides per subsystem (AUTHENTICATION.md,
PAYMENT.md, SCHEDULER.md, etc.)
- /bugs/known-issues.md is the single user-facing known-issues
registry; the file's own rules say entries are REMOVED on fix
and new ones added in-place.
This commit aligns gateway docs with that layout:
/gateway/docs/STORY.md → /docs/GATEWAY.md
/gateway/docs/BUGS_AND_...md → merged into /bugs/known-issues.md
/gateway/docs/ → deleted
docs/GATEWAY.md: the end-to-end walkthrough. 8 internal links rewritten
for the new location (one level up from where it was):
../openapi.yaml → ../gateway/openapi.yaml
../README.md → ../gateway/README.md
../agents/... → ../gateway/agents/...
../recipes/... → ../gateway/recipes/...
../../examples/... → ../examples/...
Link check across the four affected docs (docs/GATEWAY.md,
gateway/README.md, fleet/README.md, bugs/known-issues.md) — all
resolve.
bugs/known-issues.md absorbs the unresolved work the gateway-local
file was tracking. Net:
- 15 new gateway entries (5 medium, 6 low, 5 nit) with file
paths, symptoms, workarounds — all the items surfaced during
the recipes/SSE/DID work that aren't yet fixed.
- 2 existing entries narrowed: `tool-name-collisions-silent`
was partially fixed in this session (collision rejection
landed at plan-open time) and is now
`parse-agent-from-tool-greedy-mismatch` tracking only the
residual regex issue. `signature-verification-ok-when-unsigned`
narrowed to `signature-verification-non-text-parts-unverified`
— the envelope ambiguity was fixed by the four-valued
`verified` label, `data`/`file` parts are still unverified.
- Quick-index table + TOC counts updated accordingly.
- Header's `_Last updated_` line reflects the 2026-04-20 session.
gateway/README.md + examples/gateway_test_fleet/README.md: 6 total
references to `docs/STORY.md` rewritten to point at the new
`docs/GATEWAY.md` location.
No code changes; 216/216 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (13)
📝 WalkthroughWalkthroughThe PR enhances peer DID resolution and verification metadata tracking across the Gateway. It introduces agent card discovery via Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant PlanRoute as plan-route.ts<br/>(handleRequest)
participant AgentCardCache as agent-card.ts<br/>(fetchAgentCard)
participant Peer as Peer Service<br/>/.well-known/agent.json
participant Planner as planner.ts
participant SSE as SSE Stream
Client->>PlanRoute: POST /plan
PlanRoute->>PlanRoute: Extract agent names from request
par Pre-fetch all peers in parallel
PlanRoute->>AgentCardCache: fetchAgentCard(peerUrl, {signal, timeout: 2s})
AgentCardCache->>Peer: fetch /.well-known/agent.json
Peer-->>AgentCardCache: AgentCard (or error/timeout)
AgentCardCache-->>PlanRoute: AgentCard | null (cached)
end
PlanRoute->>PlanRoute: Build observedByName map<br/>from fetched AgentCards
PlanRoute->>SSE: Start streaming
loop For each SSE event (task.started, task.artifact, task.finished)
PlanRoute->>PlanRoute: findAgentDID(request, observedByName, agentName)<br/>→ { did, source }
PlanRoute->>SSE: Emit { agent_did, agent_did_source, ... }
end
SSE-->>Client: SSE stream with provenance
PlanRoute->>Planner: Pass artifact + signatures to planner
Planner->>Planner: computeVerifiedLabel(signatures)<br/>→ "yes"|"no"|"unsigned"|"unknown"
Planner->>Planner: wrapRemoteContent(verified_label)
Planner-->>Client: LLM prompt with 4-state verification
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
6 commits that landed on
feat/gateway-recipesafter #487 merged. They tighten the signature-verification pipeline end-to-end and reorganize the docs to match the repo-wide layout.Two 🔴 high-severity bugs from
bugs/known-issues.mdare now marked RESOLVED (vacuous-yes envelope + AgentCard dead-code fallback). One 🟠 medium bug also resolved (SSE agent_did doesn't surface observed DIDs). 15 new entries added for things the work surfaced.What's in it
334066cdocs: add BUGS_AND_KNOWN_ISSUES.md— 20-entry registry (later consolidated)2a3ff15fix: detect-secrets pragma on sample DID signature— unblocks CIb0c0b8afix: split verified="yes" into yes/unsigned— no more vacuous passd540ef2feat: fetch AgentCard on first contact — activate DID fallback8681d48feat: surface observed DIDs in SSE with provenanceaaeb431docs: consolidate — bugs/known-issues.md absorbs gateway issuesKey behavior changes
<remote_content verified="...">is now four-valued —yes,no,unsigned(new),unknown. The planner LLM can distinguish a real crypto pass from "nothing was signed."trust.verifyDID: truewithoutpinnedDIDis a real feature — gateway fetches/.well-known/agent.jsonat first contact, verifies artifacts against the observed DID's public key.agent_did+ newagent_did_sourcefield —pinned | observed | nullprovenance on everytask.started / task.artifact / task.finishedframe. Consumers can apply their own trust policy.docs/openapi.yaml:598silenced with the standard# pragma: allowlist secret.Docs reorganization (final commit)
gateway/docs/STORY.md→docs/GATEWAY.md(matches repo-widedocs/layout alongsideAUTHENTICATION.md,PAYMENT.md, etc.)gateway/docs/BUGS_AND_KNOWN_ISSUES.md→ merged intobugs/known-issues.md(single user-facing registry)gateway/docs/directory deleted.docs/GATEWAY.md+ 6 cross-refs ingateway/README.mdand fleet README updated.Test plan
cd gateway && npm run typecheck— cleancd gateway && npm test— 216/216 pass (up from 185 at Feat/gateway recipes #487 merge: +7 verified-label, +9 agent-card-fetch, +6 find-agent-did)npx redocly lint gateway/openapi.yaml— 0 errors, same 13 benign warningsdocs/GATEWAY.md,gateway/README.md,examples/gateway_test_fleet/README.md,bugs/known-issues.md— all local links resolvetrust.verifyDID: true+pinnedDIDreturnsverified="yes"with populatedsignaturescounts in the real running stackNotes
M README.md,M examples/medical_agent/medical_agent.py,?? assets/*.png,?? openapi.yaml) are not part of this PR — surgical staging used throughout.bugs/known-issues.mdgateway counts updated: 3 high · 15 medium · 19 low · 9 nit. Two existing entries narrowed to reflect partial resolutions (tool-name-collisions-silent→parse-agent-from-tool-greedy-mismatch;signature-verification-ok-when-unsigned→signature-verification-non-text-parts-unverified).🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Improvements
Documentation