Give the gateway a DID — every outbound call gets a cryptographic signature agents can verify.
Everything so far has been running on localhost. The agents accept unsigned requests because "auth": { "type": "none" } tells the gateway not to sign them. That’s fine for development — there’s no attacker between you and your own laptop.
In production it isn’t. If your gateway calls an agent over the public internet, anyone who can reach that agent’s URL can pretend to be your gateway. They can feed it garbage, steal its output, or (if the agent does anything side-effectful like sending email or moving money) cause real damage.
The fix is: the gateway gets a cryptographic identity and signs every outbound request. Agents verify the signature before processing. If an attacker tries to forge a request, the signature won’t match the gateway’s registered public key, and the agent rejects the call.
DID stands for Decentralized Identifier. It’s a string that looks like:
did:bindu:alice_at_example_com:gateway:abc123
It uniquely identifies an agent or a gateway. Paired with it is an Ed25519 key pair — a private key (secret, 32 bytes, lives in an env var) and a public key (safe to share, published at a .well-known URL).
You sign outbound requests with the private key. Recipients verify with the public key. Standard public-key cryptography — what puts the green lock in your browser.
BINDU_GATEWAY_DID_SEED=<paste the output>BINDU_GATEWAY_AUTHOR=you@example.comBINDU_GATEWAY_NAME=gateway
That’s enough for the gateway to have an identity. It won’t be useful yet — we also need to tell the gateway where to publish its public key so agents can fetch it. That’s the next piece.
Ory Hydra is an open-source OAuth 2.0 / OIDC server. The Bindu team runs one at hydra-admin.getbindu.com that any Bindu gateway or agent can register with.
How it works: You register once at boot; the registry stores your DID + public key; agents that want to talk to you fetch your public key by DID and verify your signatures with it.
[bindu-gateway] DID identity loaded: did:bindu:you_at_example_com:gateway:<uuid>[bindu-gateway] public key (base58): 6MkjQ2r...[bindu-gateway] registering with Hydra at https://hydra-admin.getbindu.com...[bindu-gateway] Hydra registration confirmed for did:bindu:...[bindu-gateway] publishing DID document at /.well-known/did.json[bindu-gateway] listening on http://0.0.0.0:3774
Three things just happened:
1
The gateway derived a DID and public key from your seed.
Deterministic — same seed always produces the same DID.
2
It POSTed to Hydra's admin API to register.
As an OAuth client, with its DID as the client_id and its public key in the metadata. Idempotent — safe to restart as many times as you like.
3
It exchanged client credentials for an OAuth access token.
That token is now cached in memory and refreshed 30 seconds before expiry.
The gateway also published its own DID document at http://localhost:3774/.well-known/did.json. Curl it:
That’s your gateway’s public key, served over HTTP, signed by no one but vouching for itself. Any agent that receives a signed request claiming to be from your DID can fetch this document, extract the public key, and verify the signature.
No token or envVar — the gateway will use its own Hydra token automatically.
Re-fire. On the wire, three things change:
Body signed
The gateway computes a canonical JSON representation of the body, signs it with its Ed25519 private key, and attaches the signature as X-Bindu-Signature (plus the DID in X-Bindu-DID).
OAuth token attached
Authorization: Bearer <token> — the agent introspects this against Hydra to confirm it’s real and unexpired.
Audit trail recorded
The signing result is written to Supabase on the task row: “at time T, gateway signed body hash H to reach agent DID D.”
On the receiving side, the agent:
1
Fetches the gateway's /.well-known/did.json.
Or uses a cached DID→key mapping from a previous interaction.
2
Verifies the signature matches the body.
Using the gateway’s public key.
3
Introspects the bearer token against Hydra.
Confirms it’s real and unexpired.
4
Only then processes the request.
Otherwise returns HTTP 401.
If any of those three checks fail — signature mismatch, unknown DID, invalid token — the agent returns HTTP 401 and the gateway surfaces that as event: task.finished with state: failed and a useful error message.
Use this unless you have a specific reason not to.
For federated setups where different peers trust different Hydra instances.Config — set only the DID env vars (seed, author, name), not the Hydra URLs. For each peer, pre-register your gateway’s DID with that peer’s Hydra (out of band) and obtain an access token. Store the tokens in env vars per peer.Request side — tell the gateway which env var to read:
Configure the DID identity and flip peers to did_signed. The token and signature are automatic once the env vars are set; you never touch crypto code.
If something in this chapter isn’t working, the most common cause is a missing env var — the gateway logs exactly which one on boot when a partial config is detected.