Skip to content

Index

SEDI Identity Credential (sedi-id)

Superseded for new issuance. This Bakobo schema remains resolvable for existing credentials. The active Summit schema family is Sam Smith's IAR, Core, and Residence. The text below describes the historical sedi-id design, including its optional faceTemplate; that field is not in Sam's Core. The current rules.json is a standalone Bakobo governance artifact with d and l fields that fit the expanded r section in Sam's schemas. Its former named clauses are retained verbatim within l. The earlier rules remain available for credentials that carry their original SAID. See this.i @epnvr5oq, @2vqmhxai, and @jmr6znql.

Purpose

sedi-id is the root credential of Utah's State-Endorsed Digital Identity (SEDI, Utah Code Title 63A, Chapter 20, enacted by SB 275, effective 2026-05-06). It is what the State — through the Department of Government Operations (DGO) — issues to a resident after identity proofing: a cryptographically verifiable assertion of the resident's core civil identity, bound to an identifier the holder created and controls.

SEDI's founding premise inverts the usual state-ID relationship: identity is "innate to the individual's existence and independent of the state… fundamental and inalienable" (§63A-20-101(1)). The state endorses an identity; it does not create one. So sedi-id's issuer field names the State, but the subject is bound by the holder's own personal digital identifier (a KERI AID) — withdraw the endorsement and the identity persists, because the holder, not the state, holds the key.

This credential is not a driver's license and not a general attribute bag. The statute lets the department verify and endorse exactly four attributes — name, birth date, image, and Utah residence address (§63A-20-301(2)(f)) — and forbids collecting anything unnecessary (§302(7)). sedi-id carries those four and nothing more; everything richer (age thresholds, eligibility, presentations, guardianship) is a separate credential that chains to this one. See the family.

Why an attribute (a) section — not an aggregate

sedi-id carries its attributes in a partially-disclosable attribute (a) section, where each attribute is its own nested, individually-blinded block (own SAID d and blinding nonce u). A holder discloses any subset by revealing those blocks and leaving the rest as bare block SAIDs; withheld values never cross the wire.

An earlier draft used the aggregate (A) section (mDL-style). It was reversed on first-principles grounds (see PR WebOfTrust/keripy#1505 and this.i @sdav5t): the only thing an aggregate buys over an attribute section is that its top-level blocks carry no meaningful labels, so block position can be arranged to not leak information. That helps only when a field's presence/identity/position is itself sensitive, or the field set is variable. The core identity credential is a fixed set of heterogeneous, meaningfully- labeled fields — nothing benefits from hiding labels — and it is the most-used credential, so the aggregate's real cost bites: you can no longer path to a field by label, so graduated-disclosure negotiation (apply/offer path lists) would need a label→index reverse-lookup, or you put the index in the path and defeat the very unlinkability the array was for. Labels are the right handle here; the aggregate is the right choice for the age credential instead.

The attributes

The a section holds the issuee (a.i = the holder's AID) plus one nested block per attribute:

Block Field(s) Statutory basis
Given-name nameGiven name — §301(2)(f)
Family-name nameFamily name — §301(2)(f)
Date-of-birth dob (date) birth date — §301(2)(f)
Image image = {digest, format, dim, captured} image — §301(2)(f)
Face-template faceTemplate = {digest, protocol} optional, consented dedup (not endorsed identity)
Residence-street residenceStreet (whole line) Utah residence address — §301(2)(f)
Residence-city residenceCity "
Residence-state residenceState "
Residence-postal residencePostal "
Residence-county residenceCounty (optional) "

The holder AID lives at a.i, the natural issuee location for an attributive ACDC — this is what a sedi-id → X chain checks (an I2I edge holds only when the chaining credential's issuer equals this credential's a.i). Note that attribute-form partial disclosure reveals a.i and the a-section metadata on any disclosure; for SEDI that is fine, because the holder binding is normally wanted.

Name and residence are decomposed — deliberately

Both name and the residence address are split into separate nested blocks, not carried as one lump or a single object — and decomposed is the sole production shape. Because a block discloses atomically, a single residence block would force over-disclosure on every verifier who only needs jurisdiction, making the statute's minimization duty (§501, §701) un-satisfiable at the schema level. "Prove Utah resident" (voter eligibility, in-state tuition, tax/jurisdiction, alcohol) is the most common residence predicate, and withholding the street line protects exactly the Title-20A withheld/at-risk-voter classes (DV victims, LEOs, protected persons). Not decomposing would also make SEDI a privacy regression from the ISO 18013-5 mDL it replaces.

Grain: keep the street line whole (residenceStreet) rather than parse it (PO boxes, rural routes, non-US formats are brittle; the useful predicates live in the clean jurisdiction fields); split only city/state/postal/county; go no finer. Naming is semantic interop, not field-name interop: the labels use Utah's statutory term "residence" (residenceCity, …), not ISO mDL's "resident" — align the component semantics with 18013-5 so a bridge crosswalk is mechanical, but keep the statute's vocabulary. Labels are flat and dot-free on purpose (a dotted key like residence.street signals a nesting the model doesn't have and is a cross-tooling footgun in jq/JSONPath/JS/MongoDB).

Image: committed by digest, bytes holder-carried

The image block carries a content digest plus minimal metadata (format, dim, captured); the actual portrait bytes are holder-carried and attached to a presentation under chain-link confidentiality, verified against the digest. A download locator was rejected (it breaks mandatory offline presentation §301(1)(d), is phone-home by another name §301(3)/§701, and creates an availability chokepoint), and inlining raw bytes was rejected as bloat. There is one canonical portrait (ISO/IEC 19794-5-grade) — no resolution ladder. The optional faceTemplate block is a different artifact: a cancelable/biohashed (ISO/IEC 24745-style) template for consented 1:N dedup, carrying a named biometricProtocol and disclosed only under a correlation-consent rule.

Selective (partial) disclosure — "prove Utah residence without the street"

To prove residence at jurisdiction grain, the holder reveals the residence-city/state/postal blocks and leaves the street, name, DOB, image, etc. as bare SAIDs. Illustratively, the disclosed a section looks like:

"a": {
  "d": "…", "u": "…", "i": "<Alice's AID>",
  "nameGiven":       "<block SAID>",           // withheld
  "nameFamily":      "<block SAID>",            // withheld
  "dob":             "<block SAID>",            // withheld
  "image":           "<block SAID>",            // withheld
  "residenceStreet": "<block SAID>",            // withheld — the street never appears
  "residenceCity":   { "d": "…", "u": "…", "residenceCity": "Salt Lake City" },
  "residenceState":  { "d": "…", "u": "…", "residenceState": "UT" },
  "residencePostal": { "d": "…", "u": "…", "residencePostal": "84101" },
  "residenceCounty": "<block SAID>"             // withheld
}

The verifier hashes each disclosed block, substitutes the withheld SAIDs, and recomputes a.d to confirm the disclosure is authentic. (This is shown as prose rather than a SAID-checked gallery file because the repo's oracle does not compact nested blocks — see the SAID note.)

Schema

See sedi-id.schema.json. The a section is oneOf(section SAID, section detail); the detail requires the endorsed attributes, each a nested block that is itself oneOf(block SAID, block detail) — so any subset can be disclosed as detail while the rest stay bare SAIDs.

Worked example

example.json is the fully-disclosed issued credential (all attributes present) — a private variant (top-level u), a version-stamped ACDC v2 acm issued by the State to the holder Alice. The invalid/ corpus rejects: a top-level extra property, a missing/a-section-less credential, a missing issuee, a missing required attribute, a block with an extra property, a block missing its blinding nonce, a non-date dob, an image missing its digest, and a non-string residenceState.

A note on SAIDs

sedi-id is an attributive ACDC v2 acm — the field-map ilk, so u and e are genuinely optional here, unlike the fixed-field acg of sedi-age. It carries a real v2 version string and a canonical d, both computed by this repo's oracle. One residual remains: the oracle does not compact nested partial-disclosure blocks, so a partial-disclosure form's a.d/d differ from the full form's. That is why the residence disclosure above is illustrative prose, and the machine-validated selective-disclosure gallery lives on sedi-age, where the AGID is stable across disclosures. See this.i @sdav5t / @lkfnoess.

The SEDI family

sedi-id is the root; the other members chain to it: sedi-age (the aggregate boolean age-threshold vector), sedi-present-age-portrait (the first holder-issued presentation pattern), and — on the roadmap — sedi-guardian (digital-guardian delegation) and sedi-bridge (foreign-credential reissuer). See this.i @sd4rkp.

Governance

SEDI's duty-of-loyalty and minimization obligations (§701, §702, Parts 4–6) are carried as the family-wide SEDI governance framework in the current rules.json, referenced by SAID from new SEDI examples. The original revision remains resolvable under SAID EA5O9z0TB932sm8kJIVdAIpwLpEWRWC5--VNS5r69frn. Issuing or accepting a SEDI credential is binding acceptance of its referenced revision.

Provenance and further reading

Design decisions live in this.i (@sd4rkp and children; the attribute-vs-aggregate reversal is @sdav5t). The reusable principle is distilled in docs/choosing-attribute-vs-aggregate.md. The executable reference for the disclosure + chain-link-confidentiality mechanics is keripy's tests/acdc/test_clc_disclosure.py.