BharatGrid Identity Ceremony (Launch Build)
How the 11 BharatGrid identities are minted, published, verified, and handed off — the one-time step every other UAI flow depends on.
Scope. The launch build only. This is the operational how of minting the
did:webvhidentities for the BharatGrid deployment: what an identity is, how the ceremony runs, where the keys live, and what the outputs feed. The system shape lives in the UAI x BharatGrid Integration HLD; the resolver and proof-of-control mechanics live in the Registry LLD. Key rotation, KMS, and external-provider onboarding are explicitly not in this build (section 10).
Index
- Why this exists
- What one identity is
- Who gets one
- The ceremony
- Step by step: running the tool
- Re-runs, re-mints, and hygiene
- Where the private keys live
- How the identities get used
- Rules
- Not in this build
- Honest claim
1. Why this exists
Before launch, every actor in the system gets a digital identity. Nothing in UAI works without them: registering an agent, minting a token, and verifying a receipt all start with the same question — who is this, and can they prove it? The ceremony is the one-time step that makes the answer possible.
2. What one identity is
Each identity has three parts.
| Part | What it is | Who sees it |
|---|---|---|
| Private key | The secret. Signs challenges and receipts. | Us only, on the ceremony machine, then the secrets store |
Public log (did.jsonl) | A small file on a domain we control that says “this identity uses this public key”. Anyone can download it and check signatures against it. | Public, over HTTPS |
| Name (the DID) | Contains a fingerprint (the SCID) of the log’s first version. | Public, embedded everywhere |
The name is what locks the history. If anyone swaps the log file later, the fingerprint stops matching and every check fails — the resolver rejects the identity rather than silently trusting a rewritten past.
3. Who gets one
11 identities: the 7 BharatGrid agents, the Orchestrator, the Registry, the Trust Server, and the Audit Ledger.
All 11 logs are hosted on a domain we control. At launch each log has exactly one entry (genesis only). Key rotation comes later.
4. The ceremony
The ceremony is a small CLI in the repo (tools/ceremony). Run it on a trusted machine, not a shared server.
- Fill in the config file: the 11 names and the host domain.
- Run the tool in dry run. It prints what it will create. Review it. Nothing is written.
- Run it with
--yes. For each identity it creates a key, the genesis log, and the DID. - Move the private keys into the secrets store. Fill in the custody sheet: identity, DID, key location, holder.
- Copy the publish folder onto the website over HTTPS.
- Run the verify command. It fetches all 11 logs through our real resolver code. All 11 must pass.
- Hand off the outputs.
Handoffs (step 7)
| Output | Goes to | Tracking |
|---|---|---|
manifest.json | Registry seed — the 7 agent DIDs | #331 |
| Orchestrator DID + key path | Orchestrator config (ORCH_DID, ORCH_KEY_PATH) | Orchestrator LLD §14 |
| Trust Server key + log | Trust Server deploy | #342 |
5. Step by step: running the tool
Run from the repo root on a trusted machine. Everything writes to out/, which is gitignored.
1. Dry run. Writes nothing:
go run ./tools/ceremony mint --config tools/ceremony/identities.yaml --out out/ceremony
Expect a plan of 11 identities, all MINT, and no DIDs anywhere in the output. The SCID does not exist until the real mint.
2. Apply:
go run ./tools/ceremony mint --out out/ceremony --yes
Expect 11 minted, 0 re-minted, 0 skipped, with real DIDs.
3. Check permissions:
ls -l out/ceremony/orchestrator
Every key and output file must be -rw------- and the directories drwx------. Skim custody.md and manifest.json once. In a genesis entry, the real SCID must appear in the id fields with no {SCID} placeholder left.
4. Verify offline. This is the real check:
go run ./tools/ceremony verify --manifest out/ceremony/manifest.json
Expect 11 pass, 0 fail, 0 skip and exit 0. This resolves every DID through the real pkg/did resolver against the local publish bundle, so SCID, hash chain, and proof are all checked.
5. Publish, then verify online. Copy out/ceremony/publish/ onto the host, then run the same verify with --online.
This only works against the real HTTPS host. Locally it fails with target address not allowed, because the resolver blocks loopback and private addresses and allows HTTPS on port 443 only. That failure is the SSRF guard working, not a bug (see Registry LLD §9.2).
6. Finish. Keys into secrets, holder column filled in custody.md, manifest.json handed off.
Two optional confidence checks
- Flip one character of a
proofValuein a publisheddid.jsonl. Verify must fail that identity with a non-zero exit. - Grep the mint output for
PRIVATE KEYor the key body. Expect zero hits.
6. Re-runs, re-mints, and hygiene
- Re-running mint is safe. Expect
0 minted, 0 re-minted, 11 skipped, key files byte-identical, custody holders preserved. --force <name>re-mints one identity with a new SCID. The tool warns and markscustody.mdout of date for that name. Everything that referenced the old DID must be re-seeded — a re-mint is a brand new identity, not an update.- Use
go run, nevergo build ./tools/.... The build drops a 10 MB binary in your working directory. git statusmust never showout/.
7. Where the private keys live
A private key is a file at launch. It is created on the ceremony machine, permissions locked to the owner, never committed to git.
| Key set | At launch | Later maturity step |
|---|---|---|
| Service keys (Orchestrator, Registry, Trust Server, Ledger) | Mounted as secret files on the deploy VM; service config points at the file path | KMS / HSM |
| Agent keys (7) | Stay with us. Used once, for proof-of-control at registration. Agents never sign receipts and never present tokens, so the keys sit offline after that. | Custody transfer to the owner |
The custody sheet (custody.md) is the single record of every key location and holder.
8. How the identities get used
flowchart TB
C["Ceremony<br/>11 keys + 11 did.jsonl logs"] -->|seed by DID| RG["Registry"]
C -->|DID + key path| OR["Orchestrator"]
C -->|key + log| TS["Trust Server"]
OR -.->|1. sign challenge, per TTL| TS
TS -.->|2. fetch log, verify, mint token| OR
OR -->|3. gate checks token offline| G["Query path"]
OR -.->|4. sign receipt| AL["Audit Ledger"]
AL -.->|5. fetch log, verify, store| ALRegistry. Seeds the 7 agents by DID, then a one-time proof-of-control sweep confirms each one: challenge, sign, resolve the log, verify.
Orchestrator. The daily user of its identity:
| # | Step | Cadence |
|---|---|---|
| 1–2 | Signs a challenge to prove itself to the Trust Server, which fetches the Orchestrator’s log and checks the signature before minting a token | Once per token TTL |
| 3 | The gate checks the token against the Trust Server’s published key | Every query |
| 4–5 | Signs the receipt with the same key; the Ledger fetches the log and verifies before storing | After every query |
9. Rules
- Dry run is the default.
--yesapplies. - Private key material is never printed, logged, or committed. Only the key file itself holds it.
- Re-running the tool skips identities that already exist. It never silently regenerates a key.
- A re-mint is a brand new identity with a new fingerprint. Everything that referenced the old DID must be re-seeded.
- Nothing hardcodes a DID. Every service and seed reads DIDs from the ceremony manifest.
10. Not in this build
KMS or HSM for signing keys. Key rotation and publish-ahead. A one-command setup for external providers (Provider and Seeker Kits, parked). Witnesses and watchers on the resolver.
Each of these has a named later home. Do not add them here.
11. Honest claim
At launch we mint every identity and operate every side. This is the full machinery, live and exercised end to end. It is not yet independent trust. That claim starts only when an outside party holds its own key — for example, when an owner takes custody of the agent keys.