Documentation Index
Fetch the complete documentation index at: https://docs.getbindu.com/llms.txt
Use this file to discover all available pages before exploring further.
mTLS, On By Default
This release flips Bindu’s transport-layer security from opt-in to default. The operator’s personal agent (and any fleet agent that opts in with the same env block) bootstraps a real X.509 cert from production step-ca over a Hydra OIDC token, serves uvicorn under mTLS, and renews itself in-process every ~16 hours. The inbox spawns the personal agent withBINDU_PERSONAL_MTLS=1 and starts the gateway with the same cert/key/CA bundle pointers, so outbound A2A from a /plan fan-out, inbox compose, or peer discovery all present a verified identity at the TLS layer before a single byte of JSON-RPC moves.
This release also includes all the supporting fixes that surfaced once “mTLS on by default” actually started running on a developer laptop. There were six of them. Each one quietly broke mTLS in a different way.
The UI gets a small but meaningful affordance: bindu DIDs now render everywhere as Gmail-shape name+author@domain addresses. Hover to see the raw DID. The “Gmail-for-A2A” framing now matches what the sidebar shows.
The Six Things That Were Breaking mTLS
Each fix is in this release.1. The .env Loaded After bindu Did
The personal agent shipped withMTLS__ENABLED=true in the spawn env, but the generated agent.py loaded the .env file after importing bindu. bindu’s app_settings = Settings() is built at module-import time, so the mTLS env vars landed in os.environ but never reached the singleton. mtls.enabled silently defaulted to False. The agent served plain HTTP. The spawner (which polls HTTPS) hung until the timeout. The wizard showed “starting…” forever.
Fix. The generated agent.py template now loads .env before importing bindu. Same fix applied to every agent in examples/gateway_test_fleet/.
2. Hydra Audience Drift Wasn’t Reconciled
After fixing the load order, mTLS bootstrap reached step-ca via Hydra and got back a 400: “Requested audience ‘step-ca’ has not been whitelisted.” The registration path included that audience for brand-new clients, but if your agent had ever registered with mTLS off, the existing-client branch returned early and never reconciled. The agent silently degraded to plain HTTP. Fix. Hydra registration now reconciles audience drift on every boot. If your agent registered before mTLS was enabled and step-ca is later configured to issue against your DID, the agent patches its own Hydra client to include the step-ca audience instead of failing.3. Hydra Rotated the Client Secret on Reconciliation
The second attempt at mTLS bootstrap returned a different 401: “passwords do not match.” The reconciliationPUT was building its body from Hydra’s GET response, which never returns the client_secret. Hydra interpreted the missing secret as a rotation and overwrote the agent’s password — the next client_credentials call then 401’d.
Fix. Reconciliation re-sends the local client_secret in the PUT body so Hydra doesn’t rotate the password as a side effect of the missing field.
4. The Agent Card Advertised http://localhost
Once mTLS finally worked, the card published at /.well-known/agent.json advertised url: "http://localhost" — no scheme matching reality, no port. Peers fetching the card couldn’t reach back. The BinduApplication constructor defaulted url to "http://localhost" and bindufy() never passed agent_url through.
Fix. The agent-card endpoint reads app.url (not manifest.url) when building the payload. The penguin layer now passes the resolved deployment URL into BinduApplication(url=...) so the card advertises what bindu actually serves, port included.
5. fetch failed — bundled undici 6 vs pinned undici 8
Once peers could fetch the card, the inbox’s live-agent inspector andfetchWellKnown both used Node’s global fetch with our pinned undici-8 dispatcher. Node 22 bundles undici 6. The dispatcher protocol drifted between v6 and v8, so the call threw TypeError: fetch failed with no further detail. The same bug existed in the gateway’s outbound JSON-RPC client and agent-card fetcher — every multi-agent /plan fan-out targeting an mTLS peer came back with transport: fetch failed.
Fix. Both outbound fetch sites in the gateway (gateway/src/bindu/client/fetch.ts, agent-card.ts) and the inbox’s live-agent inspector and well-known fetcher now route through undici.fetch when a dispatcher is present.
6. The SAN Matcher Rejected URL-Fragment-Style DIDs
The DID-pinned loopback dispatcher’s SAN matcher rejected every valid cert. step-ca’s Hydra OIDC provisioner emits the URI SAN ashttps://hydra.getbindu.com#did:bindu:... — the DID is a URL fragment, not a bare URI. The matcher checked for literal-DID-in-SANs only.
Fix. The matcher now accepts both bare-DID SANs and URL-fragment-style SANs.
Plus: The Stale Inspector Row
Independent of TLS: each personal-agent respawn picked a fresh free port and updated the in-memoryAGENT_URLS map, but the ecosystem agents table row stayed pinned to the prior port. The inspector reads from that table and was always one spawn behind, surfacing “fetch failed” across all four panels.
Fix. Spawning the personal agent now writes the new url/did into the ecosystem agents row in addition to the in-memory AGENT_URLS map.
Personal Agent
Two new behaviors and one new env var:BINDU_PERSONAL_MTLS=1on the comms server now gates mTLS for the spawned personal agent. With it, the agent acquires a real cert and serves HTTPS; without it, it defaults to plain HTTP so local development without a configured Hydra still works. The baseUrl the spawner polls and the deployment URL written intoagent.pyboth follow the same toggle, so the schemes always match what bindu actually serves.- The generated
agent.pytemplate loads .env before importing bindu soMTLS__/HYDRA__/AUTH__vars survive into theapp_settingssingleton (see fix #1). - Hydra registration reconciles audience drift on every boot (see fix #2).
DIDs as Email Addresses
didToEmail() (inbox/src/lib/format.ts) reverses bindu’s deterministic DID slugifier and renders identities in a Gmail-shape form:
+subaddress slot. Applied across:
- The sidebar (ecosystem rows and the personal-agent block)
- The compose-to dropdown
- The sending-from footer
- Event rows
- The agent-info modal
title attribute for power users.
Dependency Hygiene
trufflesecurity/trufflehog3.95.2 → 3.95.3 (CI action patch bump, no runtime impact)cryptographyupper bound widened from<47to<49(constraint expansion only — no forced upgrade)richupper bound widened from<15to<16(constraint expansion only)brace-expansionbumped to 5.0.6 in the gateway lockfile, clearing GHSA-jxxr-4gwj-5jf2 / CVE-2026-45149 (transitive DoS via minimatch)
Operational Changes
- The inbox passes
BINDU_PERSONAL_MTLS=1through to the personal agent when set on the comms server. Local-dev users who want plain HTTP can omit the env var. - The inbox spawns the gateway with
BINDU_GATEWAY_TLS_CERT,BINDU_GATEWAY_TLS_KEY, andBINDU_GATEWAY_TLS_CApointing at the personal agent’s cert files. Existing HTTP gateways spawned before the personal agent had a cert are recycled on next/api/me/spawnso the new gateway boot picks up TLS. - The
gateway_test_fleetREADME still works against the new HTTPS defaults; every fleet agent’s deployment URL is nowhttps://127.0.0.1:{PORT}so the agent card advertises a reachable address.
Upgrade Notes
Turn it on
Start the inbox with
BINDU_PERSONAL_MTLS=1 in its env (e.g. BINDU_PERSONAL_MTLS=1 npm run dev in inbox/).For fleet agents — set the same env block
AUTH__ENABLED=true, AUTH__PROVIDER=hydra, MTLS__ENABLED=true, MTLS__MODE=hybrid, MTLS__REQUIRE_CLIENT_CERT=false, plus HYDRA__ADMIN_URL / HYDRA__PUBLIC_URL / MTLS__CA_URL / MTLS__CA_ROOT_URL.If your Hydra client is stuck on an empty audience
If you previously ran the personal agent with mTLS attempts and saw silent HTTP fallback, your Hydra client may be stuck with an empty audience array. This release reconciles that automatically on the next agent boot — no manual
curl to Hydra admin needed.If a prior reconciliation rotated your secret
If your Hydra client got its secret rotated by an earlier reconciliation attempt (the “passwords do not match” symptom), delete
examples/<your-agent>/.bindu/oauth_credentials.json to force a fresh registration on next start. The delete-and-recreate branch in register_agent_in_hydra handles the rest.Files of Note
Known Issues
/.well-known/did.jsonstill returns 404 on bindu agents; the DID is discoverable via/.well-known/agent.json’scapabilities.extensions[*].urifield. Cosmetic, not blocking.- Two Dependabot bumps were deliberately held in this release: PR #556 (opentelemetry stack 1.35→1.41 + instrumentation 0.56b0→0.62b1) and PR #559 (starlette
==0.49.1→==1.0.0, a major-version pin bump on the load-bearing HTTP framework). Both need manual migration testing before they land.