feat(gateway): self-publish DID document at /.well-known/did.json#476
feat(gateway): self-publish DID document at /.well-known/did.json#476raahulrahl merged 1 commit intomainfrom
Conversation
A2A peers now have a Hydra-independent path to resolve the gateway's DID to its Ed25519 public key. Matches the W3C DID Core + Bindu extension shape already published by Python agents, so any peer that can verify an agent's DID document can also verify the gateway's. The route is registered only when an identity is loaded. A gateway without DID signing has nothing to publish here, and a 404 accurately says so. Shape omits ``created``. The gateway's identity is env-driven and stateless — there's no persisted "first published" moment. Emitting process-start or current-now would mislead clients into thinking the key was rotated at that moment. W3C DID Core v1 has ``created`` as optional; absence is valid. Test asserts this explicitly so the decision isn't re-introduced accidentally. Content-Type is ``application/did+json`` per W3C spec — plain ``application/json`` is tolerated by most resolvers but stricter ones reject it. Caches serialized JSON once at handler build time. The document doesn't change for the life of the process, so repeated requests return byte-identical bodies (safe for clients that hash the response). 5-minute public Cache-Control TTL — defensive against misbehaving caches holding forever without making restarts require a cache purge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR adds support for publishing a gateway's Decentralized Identifier (DID) document at Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@gateway/tests/api/did-route.test.ts`:
- Around line 22-35: The tests currently call afterEach(() =>
setSeedEnv(undefined)) which always deletes TEST_ENV and can clobber a
pre-existing environment value; change the setup to capture the original value
(e.g., const originalSeed = process.env[TEST_ENV]) in a beforeEach and restore
it in afterEach by calling setSeedEnv(originalSeed) (or passing undefined if
originalSeed is undefined), and update all similar occurrences (uses of
setSeedEnv/afterEach) so tests restore the prior global TEST_ENV instead of
unconditionally deleting it; references: TEST_ENV, setSeedEnv, beforeEach,
afterEach in the did-route.test.ts file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 550a8ead-c0dd-4d5f-bb20-b3bc9f3a6569
📒 Files selected for processing (4)
gateway/src/api/did-route.tsgateway/src/index.tsgateway/src/server/index.tsgateway/tests/api/did-route.test.ts
| import { describe, it, expect, beforeEach, afterEach } from "vitest" | ||
| import { Hono } from "hono" | ||
| import { | ||
| buildDidDocument, | ||
| buildDidHandler, | ||
| } from "../../src/api/did-route" | ||
| import { loadLocalIdentity } from "../../src/bindu/identity/local" | ||
|
|
||
| const TEST_ENV = "TEST_DID_ROUTE_SEED" | ||
|
|
||
| function setSeedEnv(b64: string | undefined) { | ||
| if (b64 === undefined) delete process.env[TEST_ENV] | ||
| else process.env[TEST_ENV] = b64 | ||
| } |
There was a problem hiding this comment.
Restore the prior test env value instead of always deleting it.
afterEach(() => setSeedEnv(undefined)) can clobber a pre-existing TEST_DID_ROUTE_SEED for later tests in the same worker. Capture the original value and restore it globally.
🧪 Proposed test isolation fix
-import { describe, it, expect, beforeEach, afterEach } from "vitest"
+import { describe, it, expect, afterEach } from "vitest"
@@
const TEST_ENV = "TEST_DID_ROUTE_SEED"
+const ORIGINAL_TEST_ENV = process.env[TEST_ENV]
@@
function setSeedEnv(b64: string | undefined) {
if (b64 === undefined) delete process.env[TEST_ENV]
else process.env[TEST_ENV] = b64
}
+
+afterEach(() => setSeedEnv(ORIGINAL_TEST_ENV))
@@
describe("buildDidDocument", () => {
- afterEach(() => setSeedEnv(undefined))
-
@@
describe("buildDidHandler — HTTP response shape", () => {
- afterEach(() => setSeedEnv(undefined))
-
@@
describe("peer-resolution contract", () => {
- afterEach(() => setSeedEnv(undefined))
-Also applies to: 53-54, 110-111, 159-160
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@gateway/tests/api/did-route.test.ts` around lines 22 - 35, The tests
currently call afterEach(() => setSeedEnv(undefined)) which always deletes
TEST_ENV and can clobber a pre-existing environment value; change the setup to
capture the original value (e.g., const originalSeed = process.env[TEST_ENV]) in
a beforeEach and restore it in afterEach by calling setSeedEnv(originalSeed) (or
passing undefined if originalSeed is undefined), and update all similar
occurrences (uses of setSeedEnv/afterEach) so tests restore the prior global
TEST_ENV instead of unconditionally deleting it; references: TEST_ENV,
setSeedEnv, beforeEach, afterEach in the did-route.test.ts file.
Summary
Gateway now serves its own DID document at
GET /.well-known/did.json, giving A2A peers a Hydra-independent path to resolvedid:bindu:<gateway>to its Ed25519 public key.Before this PR the only way to verify a signature from the gateway was to query Hydra admin for the client's
metadata.public_key. That tightly couples every DID-signed peer to our Hydra deployment. With this endpoint, a peer can resolve via the standard well-known pattern — the same way it would resolve any agent's DID document.What changed
gateway/src/api/did-route.ts— purebuildDidDocument(identity)+ HonobuildDidHandler(identity). Serialization cached once at handler build — the document doesn't change for the life of the process, so repeat requests get byte-identical bodies (safe for clients that hash).gateway/src/index.ts— wires the route after/plan, only when an identity is loaded. A gateway without DID signing has nothing to publish; a 404 accurately says so.gateway/src/server/index.ts— docstring updated to list the new route.gateway/tests/api/did-route.test.ts— 14 tests covering shape, Content-Type, cache headers, determinism, and the peer-resolution contract (pubkey round-trip).Shape decisions worth calling out
@context:[w3c-did-v1, getbindu-ns-v1]— matches the Python agent's DID document so resolvers work across both.authentication[0].type:Ed25519VerificationKey2020— W3C standard value; Bindu's signing is Ed25519 throughout.controller: self-referential (the DID controls its own key). Future multi-sig or delegation can extend this without breaking the current contract.publicKeyBase58: byte-identical toidentity.publicKeyBase58. If these drift, peer verification fails withcrypto_mismatch. Test asserts exact equality.createddeliberately omitted. The gateway's identity is env-driven and stateless — there's no persisted "first published" moment to report. EmittingDate.now()or process start time would mislead clients into thinking the key was rotated at that moment. W3C DID Core v1 hascreatedas optional; absence is valid. A test guards this so the field isn't accidentally added later.HTTP response properties
application/did+jsonper W3C DID Core (stricter resolvers rejectapplication/json)public, max-age=300— short TTL as defense against misbehaving caches. Restarts with a new seed are uncommon enough that 5 min is fine; critical peers can still bypass cache.Test plan
npx vitest run tests/api/did-route.test.ts→ 14/14 passnpx tsc --noEmit→ no errors in modified filescurl http://localhost:<port>/.well-known/did.json | jq(operator test, out of CI scope)Relation to other Phase 1 work
LocalIdentity🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
/.well-known/did.jsonto publish gateway identity documents with HTTP caching enabled (5-minute max-age) for secure peer identity verification and cryptographic key discovery.Tests