UAI Credential Architecture
How Verifiable Credentials are issued, hosted, referenced via vc_refs, and verified across UAI.
Scope. How Verifiable Credentials (VCs) are issued, hosted, referenced, and verified across UAI. Complements the Registry, Trust Server, and Audit Ledger LLDs. Implements the frozen schemas and must not contradict them.
Status. First draft (proposed).
1. Summary
A VC is a signed digital certificate about a provider (its authorization, insurance, compliance, or data residency). Trust comes from the issuer’s signature, not from where the file sits: change one character and verification fails. So the design splits three jobs that are usually conflated. The issuer signs (only the issuer can do this, and it is a one-time act per credential). UAI publishes the issuer’s public key in a governed trusted-issuer list, so the issuer hosts nothing. The signed file is hosted at a stable HTTPS URL, by default in a UAI credential store. Verifiers fetch the file and check it offline against the pinned key. No runtime call to any issuer, ever.
2. Components
2.1 The vc_ref convention
A vc_ref is an HTTPS URL pointing at one signed VC JSON document. The frozen RegistryAgentRecord schema already types vc_refs items as strings, so this is a convention, not a schema change. It supersedes the DID-fragment example in the Technical Specifications (did:web:icar.gov.in#vc-crop-advisory-2026), which mixed identity resolution with file hosting.
Three hosts are valid, verified identically: the UAI credential store (default), issuer-hosted, and provider-hosted.
2.2 The UAI credential store
Purpose: give issuers who will never run infrastructure (regulators, insurers, auditors) somewhere to put signed files. This is the DigiLocker pattern: the issuer deposits, the store holds, verifiers fetch.
- Phase 1 shape: a static object store behind
https://credentials.uai.gov.in/vc/{credential_id}. No service, no database, no schema. It is a file host, not a second Registry. - Write once. A replacement credential is a new object at a new URL. The old one is revoked via status list (Phase 2), never edited in place.
- Write path is authenticated and governed (ops runbook). Read path is public, cacheable, CDN-fronted.
- Status lists (Phase 2) are themselves signed VCs and are hosted the same way, at
https://credentials.uai.gov.in/status/{issuer}/{list_id}, so issuers stay hosting-free even for revocation.
2.3 The trusted-issuer list
A small governed file in pkg/issuers/, satisfying IssuerTrust.Allowed(issuerDID, vcType). Each entry pins:
- issuer DID (acts as a label), Ed25519 public key(s) with key ids
- allowed VC types and sectors
- status (
activeorsuspended) and a validity window
Genesis follows the ceremony already set in the Registry LLD: keys generated offline, m-of-n approval, fingerprints published, chained to India’s CCA PKI where the issuer holds one. Changes go by PR with review, a single named owner, and deprecate-not-delete, the same governance shape as the capability taxonomy. The same pattern is reused later for the trusted Consent Manager list.
Because keys are pinned, issuer DID resolution is never on a hot path, and key rotation is a reviewed PR. At a handful of issuers, that review step is a feature, not a bottleneck.
2.4 Verification points
Registry, at registration (fail-secure, 422 on any miss):
- the URL resolves over TLS 1.3;
- the signature verifies against the pinned key of the claimed issuer;
- the credential is unexpired;
- the issuer is allowed to vouch for this VC type and sector.
Revocation is deliberately deferred to Phase 2 at the Trust Server, which fetches the issuer’s status list at token time (cached for minutes). Tier mapping stays as specified: compliance VC for all registered providers, data-residency for Tier 2+, authorization and insurance for Tier 3+.
Storage rule. The Registry stores the vc_ref plus cached audit fields (issuer, credential id, expiry, verified-at). The URL stays authoritative; a stored copy is never re-trusted without re-fetching. This keeps D5/D6 intact: the Trust Server fetches credentials itself and never reads the Registry’s database.
3. NAFPO example
Actors: ICAR (sector regulator, issuer), BharatGen Krishi (crop-advisory provider), the credential store, the Registry.
- Genesis (once). ICAR’s public key enters the trusted-issuer list, allowed for
UAIProviderAuthorizationCredential, sector agriculture. - Approval (outside UAI). BharatGen applies to ICAR the normal way. ICAR signs a VC: subject
did:webvh:{scid}:bharatgen.ai, scoperead:crop_diagnosis, expiry set, status-list entry assigned. - Deposit. The signed file lands at
https://credentials.uai.gov.in/vc/icar-bharatgen-crop-2026. ICAR runs no website. - Registration. BharatGen puts that URL in
vc_refs. The Registry fetches, runs the four checks, and registers on pass. - Reality check. Crop advisory is a Tier 1 scope in the live NAFPO corridor, so this VC is optional today. The same flow becomes mandatory when a Tier 3 scope goes live (for example the insurance-claim write in the Execution Blueprint). Either way it is the same three moves: sign, host, verify.
sequenceDiagram
participant I as ICAR (issuer)
participant S as Credential store
participant P as BharatGen (provider)
participant R as Registry
I->>I: Sign VC (Ed25519)
I->>S: Deposit signed file (write once)
S-->>P: Stable HTTPS URL
P->>R: Register with vc_ref = URL
R->>S: Fetch over TLS 1.3 (SSRF-guarded)
R->>R: Verify against pinned ICAR key
R-->>P: 201 registered, else 422 fail-secure(The SVG above is the canonical visual artifact; the Mermaid block below is for wiki and markdown rendering only.)
4. Security
- Untrusted-URL fetching (SSRF). A
vc_refis registrant-supplied input. The fetcher enforces: HTTPS only; DNS resolution followed by a block on private, loopback, link-local, and cloud-metadata ranges; at most 3 redirects, each re-checked; a 256 KB body cap; a 5 s timeout; a JSON content type. - Offline verification. EdDSA (Ed25519) against pinned keys, RFC 8785 canonicalization where detached proofs apply. No callback to issuers, matching the token-verification posture.
- Store compromise, honest bounds. An attacker controlling the store still cannot mint a valid VC (no issuer keys). Worst cases are deletion (availability; mitigated by backups and the provider-hosted fallback) and serving a stale file (mitigated in Phase 2 by fresh status-list checks at token time).
- Immutability and audit. Write-once objects, access-logged writes, verification outcomes in the Registry audit trail, receipts binding transactions to credentials in the Audit Ledger. CERT-In 180-day log retention applies.
- Emergency issuer suspension. Flip the list entry to
suspendedvia an expedited two-approver path, then run a re-verification sweep over provider records citing that issuer. Sweep mechanics are an open item below.
5. Scalability
- Reads are static files. Immutable objects, long cache lifetimes, CDN in front: the same static-hosting posture already planned for
/.well-known/did.json. - Verification cost sits at the edges. Registration is rare. In Phase 2, revocation adds one small status-list fetch per issuer at token time: a Bitstring Status List packs about 131,000 credentials into roughly 16 KB gzipped and is cached for minutes. One tiny cached GET, not a per-call dependency.
- Out of every hot path. A store outage blocks only new registrations and re-verification sweeps. Tokens for already-registered Tier 1 and 2 providers, and all P2P data flows, continue.
- Growth is O(files). No migrations, no shared database, no coupling to Registry or Trust Server scale.
6. Precedents
DigiLocker (issuer deposits into a central store; MeitY’s own pattern). eIDAS trusted lists (keys pinned in a governed list). Account Aggregator (signed artifacts from regulated third parties). W3C VC Data Model with Data Integrity proofs. W3C Bitstring Status List for revocation. Switzerland’s swiyu trust registry (sovereign precedent already cited in D15).
7. Phasing
Phase 1. Adopt the vc_ref URL convention, stand up the static bucket, land the pinned trusted-issuer list, the SSRF-hardened fetcher, and the four Registry checks.
Phase 2. Issuer signing CLI (issuers only ever sign). Status lists hosted on behalf of issuers. Status-list revocation at the Trust Server. Trusted Consent Manager list on the same governance pattern.
Later. OpenID Federation entity statements replace the static list when issuer count justifies it, as already flagged in both LLDs.
8. Decisions and open items
Proposed decisions (numbers assigned on ratification, ADR per house rule):
- A
vc_refis an HTTPS URL. The Registry stores the reference plus audit fields, never authoritative bytes. - Default hosting is the UAI credential store; issuer-hosted and provider-hosted URLs remain valid and are verified identically.
- Issuer trust is pinned keys in the governed trusted-issuer list; issuer DID resolution is off the hot path.
Open items:
- Re-verification sweep after issuer suspension: batch size, ordering, provider notification.
- Store write-path ownership and the approval flow for deposits (ops).
- Supersede the DID-fragment
vc_refexample in the Technical Specifications and the Registry LLD.