ADR-A84: Standalone Minting Libraries

Status: Proposed Date: 2026-09-23 Supersedes: none Related: ADR-A82 (point 5, as amended), ADR-A77, ADR-A79, ADR-A71, ADR-A29, identity minting specification, iri-identity-patterns.md Drafted by: Agent, following decisions taken with the human while scoping the identity-minting unit (sketch, plan). Pending human ratification.

Context

ADR-A82, as amended, has the persistence compiler produce minting recipes and conformance vectors, and leaves executing a recipe to runtime code. That code runs in more than one language: the Control Plane is Java, the workers are Python, and other runtimes are expected. An adopter may use neither LATTICE runtime and still want to mint identifiers exactly as LATTICE would. Every implementation must produce byte-identical IRIs for the same recipe and input, or one key gets two identities.

Two languages disagree by default on the operations minting depends on. Java has no NFKC_Casefold and no full case folding. The two languages define whitespace differently. Java measures strings in UTF-16 units. Each runtime ships its own Unicode version.

Decision

  1. Minting libraries live under packages/minting/: packages/minting/python and packages/minting/java. packages/ is a top-level root that ADR-A77 already reserves. They are libraries meant to be embedded, so they belong neither in tools/ (design-time toolchains) nor in platform/ (the LATTICE runtime).
  2. Each library builds and runs standalone. The Python library depends only on the Python 3.14 standard library, and the Java library only on JDK 25, with test-scope dependencies alone. The Java library has its own pom.xml, not a child of platform/pom.xml. No native code, no network access and no other LATTICE component is needed at build or run time. mise tasks build and test both (ADR-A29).
  3. Recipes are the only configuration input. A library reads a recipe document (contracts/identity/recipe.schema.json), verifies its digest, and mints from it. It never reads the adopter’s configuration graph.
  4. Unicode behaviour comes from pinned tables, not from the runtime. Every Unicode-dependent step except NFC uses tables generated from the Unicode Character Database, version 16.0.0, recorded with the source files’ digests. NFC uses the runtime’s implementation, which is safe because Unicode’s normalization stability policy fixes the normalization of assigned code points. Both libraries reject code points unassigned in 16.0.0, and refuse to start on a runtime whose Unicode data is older than 16.0.0. Moving to a newer Unicode version is a new pipeline version, and so a re-key.
  5. Conformance is defined by vectors, not by our code. Both libraries must pass the anchor vectors in contracts/identity/anchor-vectors.json, which are verified with independent tools, and the vectors the compiler generates per recipe. An implementation written from the specification alone is conformant on exactly the same terms.
  6. Secrets never enter a recipe or a library’s state. A recipe names a key identifier. The caller supplies the key’s bytes per call, through a minimal interface. A LATTICE runtime adapts ADR-A71’s SecretProvider to it.
  7. The libraries are MPL-2.0 (ADR-A71), so adopters can embed them in proprietary systems. Publishing to Maven Central or PyPI is out of scope until decided separately.

Consequences