# did:julia Technical Specification

This document describes the `did:julia` protocol at the design level: singleton structure, state model, transaction families, message model, presentation modes, and security properties. The companion code-level reference is **[Chialisp Code Reference (Doc #19)](http://doc_19_chialisp_reference.md)**, which documents the shipped Chialisp puzzle set.

---

## 1. Scope

`did:julia` is a DID method implemented on Chia using singleton coins and Chialisp puzzles. A Julia DID is a long-lived on-chain identity whose stable identifier is a singleton launcher ID. Authentication configuration, recovery configuration, custody configuration, and DID Document location are carried forward as state in each singleton generation.

The protocol supports:

- single-key and multi-key ownership
- multi-class multisig for organizational governance
- DIDs custodied by a separate DID
- routine rekeying
- time-locked recovery
- pre-rotated-key recovery
- DID-controlled asset coins
- delegated credential issuance
- subject and holder credential presentation modes
- delegated credential presentation
- DID-signed message announcements
- DID Document pointer announcements
- off-chain verifiable presentations that cannot be submitted on-chain
- faucet-funded user operation without requiring users to hold XCH

---

## 2. Chia Execution Model

Every Chia coin has a puzzle and a solution. Spending the coin evaluates the puzzle with the solution and produces conditions. The blockchain does not store conditions; a verifier replays the spend to derive them.

The protocol rests on three Chia mechanisms:

- **Singletons:** A singleton coin has a permanent launcher ID and one live generation at a time. The launcher ID becomes the stable DID identifier.
- **Announcements and messages:** Puzzle announcements support block-scoped attestations; `SEND_MESSAGE` / `RECEIVE_MESSAGE` support spend-bundle-scoped coordination between coins.
- **BLS signature aggregation:** `did:julia` uses BLS12-381 signatures throughout. Any number of signatures from any number of signers aggregate into one fixed-size signature, with no coordination between the signers. A verifier checks one aggregate signature per spend bundle no matter how many signers it represents.

The DID state is represented as curried data. A spend that changes state creates the next singleton generation with a different puzzle hash. A spend that does not change state can remain compatible with concurrent spends because its puzzle hash remains the same.

---

## 3. DID Singleton Model

### 3.1 Identifier

The DID identifier is the singleton launcher ID of the Julia DID coin. The launcher ID remains stable while the singleton coin is recreated across generations.

### 3.2 State

The DID singleton carries the following logical state:

| Field | Purpose |
|-------|---------|
| DID puzzle hash | Identifies the DID method puzzle |
| DID launcher ID | Stable DID identifier |
| Recovery status | Indicates no recovery or active pending recovery |
| Authentication configuration | Key Merkle tree, class structure, and quorum requirements |
| Custodian DIDs | DIDs authorized to control this DID through custody |
| Recovery configuration | Recovery participants, quorum rules, delay, and prerotation data |
| DID Document pointer | DataLayer singleton launcher ID for the DID Document |
| Pending recovery parameters | New authentication, custodian, and recovery values committed during active recovery |

The shipped code represents this state as a Chialisp list and commits to it with `sha256tree`. Doc #19 gives the exact current field ordering.

### 3.3 Genesis and Lineage

`did:julia` uses a prelauncher before the standard singleton launcher. The prelauncher embeds a BLS public key, making the Julia DID launcher ID a commitment to the original key used to create the DID.

Because the launcher ID commits to the original key and every state change is a recorded spend, a DID's full key history is verifiable from genesis. Section 14 describes the fully offline presentations this enables.

### 3.4 Permanence

A Julia DID cannot be melted. The launcher ID is permanent. An owner can brick a DID, removing all keys, recovery agents, and custodians, but the singleton and its history remain on chain.

Melt is excluded by design. A melt is permanent: if a malicious recovery took temporary control of a DID, the attacker could melt it and leave the owner nothing to recover. A brick request gets no such shortcut, and recovery agents asked to brick a DID will ask a lot of questions first.

---

## 4. Fast-Forward Compatibility

Many identity operations do not change DID state. `did:julia` is designed so those operations can remain valid even if another spend of the same DID is confirmed first, as long as the state is unchanged.

The design avoids parent-specific authorization in regular DID spends:

- It uses `ASSERT_MY_PUZZLEHASH` rather than asserting the specific parent coin ID.
- It uses `AGG_SIG_PUZZLE` for DID-owner authorization rather than coin-ID-bound signatures.
- It binds authorization to the puzzle hash, which commits to the current DID state.

If a spend changes state, the next singleton generation has a different puzzle hash, and old spend bundles tied to the old state no longer apply. If the state does not change, multiple spend bundles can target the same logical DID generation, and the first to confirm does not invalidate the rest.

---

## 5. Authentication Model

`did:julia` supports three normal authentication modes.

### 5.1 Single-Key Authentication

A single signer proves membership in the DID's authentication key tree. The DID configuration must require one class and no more, and the selected class must require one member and no more.

The signature is over the tree hash of the operation solution, binding the signer to the specific operation being authorized.

### 5.2 Multi-Key and Multi-Class Authentication

Multi-class multisig allows both threshold-style and organizational governance arrangements. Keys are leaves in a Merkle tree. Internal nodes at a configured depth represent classes, and each class can require a specified number of members.

Validation checks:

- each participant's Merkle path reaches the committed root
- each participant belongs to a valid class
- no key is counted twice
- enough members satisfy enough required classes

This supports configurations such as "two engineering keys and one executive key" rather than only flat M-of-N thresholds. The ownership models these modes serve are covered in [Delegation and Organizational Identity (6C)](http://doc_06C_delegation.md).

### 5.3 Custodied Authentication

A DID can authorize one or more custodian DIDs. A custodied DID does not verify a direct BLS signature from the subject; instead it requires a spend-bundle message from an authorized custodian DID that commits to the requested operation.

Custody is still DID-native: the custodian is identified by DID launcher ID, and the receiving DID verifies that the command came from the expected custodian DID puzzle hash. Every custodian action is attributable: the custodied DID's spend record identifies the commanding custodian DID.

A custodian cannot rotate the owner's keys, cannot participate in recovery on behalf of the custodied identity, and cannot extend custodial control to a third identity. These restrictions preserve the owner's ultimate authority. Custody serves power-of-attorney and guardianship arrangements, commercial enterprise custody, and keyless DIDs for devices and physical assets; [Delegation and Organizational Identity (6C)](http://doc_06C_delegation.md) covers the use-case families.

---

## 6. Operation Routing

The DID singleton delegates to an authentication entrypoint, and the authenticated path delegates to operation puzzles. In the shipped implementation, routing is enforced with embedded child puzzle hashes. At the protocol level, the important property is that a DID spend authorizes a bounded set of operation families rather than arbitrary code injection.

Operation families include:

- message signing
- asset coin control
- invalidation for off-chain presentations
- DID Document announcement and update
- guarded arbitrary condition passthrough
- routine rekey
- custodian command
- issuer key launch and management
- credential presentation
- delegated credential presentation
- recovery authorization

Normal authentication operations are blocked while a time-locked recovery is active. Recovery cancellation and completion remain available through the recovery paths.

Multiple compatible operations can combine in a single DID spend: one spend can sign a message, present credentials, control asset coins, and announce the DID Document pointer together. Operations that change DID state (rekey, recovery, DID Document pointer update) combine with announcements and passthrough but not with other state-changing operations.

---

## 7. Cross-Coin Communication

`did:julia` uses a `JDID` namespace for protocol messages and announcements.

At the protocol level there are two communication classes:

- **Spend-bundle-scoped messages:** `SEND_MESSAGE` / `RECEIVE_MESSAGE` pairs require both participating coin spends to appear in the same spend bundle and to agree on message content.
- **Block-scoped announcements:** `CREATE_PUZZLE_ANNOUNCEMENT` / `ASSERT_PUZZLE_ANNOUNCEMENT` pairs let one spend assert that another puzzle emitted a specific announcement in the same block.

Message-style communication is used for command and control, such as DID-owned asset coins, custody, recovery participant authorization, issuer key launch, revocation updates, and issuer key melting.

Announcement-style communication is used for attestations, such as credential presentation, holder/delegation proof, issuer key liveness, DID Document location, signed messages, timestamps, and nonces.

The exact prefixes and mode values are code-version details. They are documented in Doc #19 where each shipped puzzle uses them.

---

## 8. DID-Owned and Credential-Controlled Coins

### 8.1 DID-Owned Coins

A DID-owned coin can use a Julia DID inner puzzle. The coin spends when it receives a command from the owning DID that commits to the conditions it should emit.

This allows the DID to control XCH, CATs, NFTs, DataLayer singletons, or other Chia assets without putting the full DID authentication logic inside every asset coin. Because ownership binds to the launcher ID rather than a key, rekey and recovery carry asset control with the identity. [Identity Architecture (6A)](http://doc_06A_identity_architecture.md) covers DID asset ownership and the web front-end model it supports.

### 8.2 Credential-Controlled Coins

A credential-controlled coin can be spendable by a party that presents a qualifying credential. The coin requires both:

- proof that the spender's DID presented the required credential claim
- a DID command authorizing the exact output conditions

This supports pay-to-credential patterns: the control criterion is not a specific key but possession of a valid credential. Because the credential lifecycle governs access, granting and expiring spend rights are off-chain operations, and a revocation update cancels rights in batches with one on-chain transaction. The coin itself sees on-chain activity only when a right is exercised. [Credentials, Presentations, and Selective Disclosure (6B)](http://doc_06B_credentials.md) covers the pay-to-credential pattern, and [Delegation and Organizational Identity (6C)](http://doc_06C_delegation.md) gives a worked example with delegated agent access.

---

## 9. DID Documents

A W3C DID Document is the resolvable description of a DID: verification methods, service endpoints, and other metadata, published in a DID-method-specific way and retrieved through a DID-method-specific resolver. The DID is distinct from its DID Document. The only required element of a DID Document is the DID value itself, which is implicit, so a `did:julia` DID is usable with no DID Document lookup at all.

`did:julia` encodes DID Document contents as structured key-value pairs and publishes them through Chia DataLayer. The DID singleton state carries a pointer (a DataLayer singleton launcher ID) rather than the document contents. A DID can:

- announce the current DID Document pointer during a spend
- update the pointer as an authenticated state change

The pointer mechanism is part of the shipped protocol. The publication path that writes DID Document contents to DataLayer and resolves them for readers is planned and not yet implemented. The DataLayer substrate itself is in production in the Chia ecosystem, so the remaining work is integration rather than new infrastructure.

---

## 10. Credentials and Issuer Keys

Credential issuance is delegated to issuer key singletons. An issuer key singleton is tied to an issuer DID and contains:

- issuer DID launcher ID
- issuer key singleton launcher ID
- BLS public key
- revocation state
- key validity window
- maximum credential expiration
- allowed credential property set
- optional payment requirement for liveness presentation

The issuer key singleton is also the protocol's issuance-delegation mechanism, the on-chain structure behind the issuer-delegation use cases in [Delegation and Organizational Identity (6C)](http://doc_06C_delegation.md). A DID launches an issuer key for its own signing operations or for a delegate, with the validity window, maximum credential expiration, and allowed property set bounding what the delegate can issue. The issuer DID keeps lifecycle control over keys it launched and can melt the key if needed, invalidating every credential that key ever signed.  The issuer DID can also revoke individual credentials by ID.

An issuer key can attest that it is live and unmelted. Credential presentation depends on this liveness check, so melting an issuer key invalidates the practical presentability of credentials signed by that key. The attestation spend is available to anyone, but requires a fee, set by the issuer.

Revocation is represented by a Merkleized bitfield. An issuer key holder can update revocation on its own, or the issuer DID can command an update. The shipped Chialisp code defines the exact bitfield structure and update modes; see Doc #19.

---

## 11. Property Names and Value Encodings

Every credential claim names one property and commits to one value. The property name is a URI of the form `encoding:authority/path`. The credential claim itself stores `sha2-256(property name)` rather than the cleartext name, and the claim value is reduced through the encoding pipeline to a 32-byte commitment.

### 11.1 Property Name Grammar

A property name has three components, matched to URI structure:

| Component | Role | Example |
|-----------|------|---------|
| Encoding | The value-encoding pipeline, left of `://` | `sha2-256\|CBOR` |
| Authority | The DID whose namespace defines the property semantics | `.` or a base58 DID |
| Path | The issuer's taxonomy for the property | `/v1/pii/age_range_18_20` |

Examples of complete property names:

- `cleartext://./.julia-payment`
- `notbot://./v1/notbotN`
- `sha2-256|CBOR://./v1/domain_name`

The authority is the DID that defines the semantics of the property. When the authority is the issuing DID, `.` denotes it and the full DID is omitted; otherwise the authority is a DID encoded in base58 without the `did:julia` prefix. The path is the issuer's own taxonomy and carries the property's semantic identity, for example `/v1/pii/age_range_18_20`.

### 11.2 Property Name Hashing

The cleartext property name is included in every presentation of the claim. The signed claim commits only to `sha2-256(property name)`. A verifier re-hashes the expected cleartext name and matches it against the hash in the claim. The chain does not reveal the cleartext property name.

### 11.3 Encoding Pipeline

The encoding segment specifies the pipeline that reduces the cleartext value to the 32-byte claim value. Encodings compose with `|`, applied left to right: `sha2-256|CBOR` CBOR-encodes the value, then applies SHA2-256. A verifier reverses or replays the pipeline against the presented value and matches the result to the claim's value commitment.

The encoding registry defines the following stages:

| Encoding | Role |
|----------|------|
| cleartext | Identity. The value fits in 32 bytes and is not transformed. |
| sha2-256 | Hash. Commits to value data that does not fit in 32 bytes. |
| argon2id | Key-derivation function. Hardened commitment for low-entropy values. |
| aes256gcm | Symmetric encryption. |
| rsa3072 | Asymmetric encryption. Restricts decryption to a specific verifier. |
| zip | Compression. |
| CBOR | Serialization. dCBOR encoding of structured data. |

Raw parameters for each stage, including the Argon2id memory, iteration, and parallelism constants, salt encoding, and AES and RSA nonce and fingerprint conventions, are specified in [Chialisp Code Reference (Doc #19)](http://doc_19_chialisp_reference.md).

### 11.4 Reserved Namespaces

The `julia` and `notbot` property prefixes are reserved for Julia Social. Decoding a value under a reserved prefix requires Julia Social software.

---

## 12. Credential Presentation

A credential claim presentation validates:

- the claim subject or holder mode
- issuer DID
- subject DID
- issuer-delegated public key
- delegate authorized to issue claim with this property
- valid claim expiration date
- valid claim inception date
- non-revocation
- antecedent requirements met
- valid claim signature

An antecedent requirement conditions one claim on another: the claim is valid for presentation only when its specified antecedent claims are presented with it. Antecedents are the mechanism behind cascade revocation and structural disclosure; the credential-level treatment is in [Credentials, Presentations, and Selective Disclosure (6B)](http://doc_06B_credentials.md).

A presentation can compose claims from any number of independent issuers. Each claim carries its issuer key's BLS signature, and in a spend bundle every required signature, the presenter's authentication and all of the claim signatures, verifies as one fixed-size aggregate. The verifier checks one signature no matter how many credentials the presentation contains, and no issuer coordinated with any other. This composition property is the reason `did:julia` requires BLS12-381; [Why Chia?](http://doc_10_why_chia.md) makes the public case.

The presentation emits puzzle announcements that other spends can assert.

### 12.1 Subject and Holder Presentations

A subject presentation says the presenting DID is the subject of the credential. A holder presentation says the presenting DID holds or relays the credential; the holder need not be the subject.

### 12.2 Delegated Presentations

A delegatable claim can be passed through a chain of delegations. Each link must authorize the relevant claim for the next delegatee. The terminal delegatee then presents the claim as its own authority for the limited delegated purpose.

Delegation allows organizations, representatives, agents, or systems to act under an authority first issued to another DID, while preserving an auditable chain back to the issuer and subject.

Delegated presentation, and signing on another identity's behalf, has no separate on-chain mechanism: the authority travels as ordinary credentials. It therefore inherits the full credential lifecycle, including expiration, antecedent requirements, subdelegation through the standard chain, and revocation through the issuer key's revocation state. Mandate credentials, subdelegation, and worked examples (an AI travel-booking cascade, a structured mandate for a personal finance agent) are in [Delegation and Organizational Identity (6C)](http://doc_06C_delegation.md).

---

## 13. Signed Messages

A `did:julia` signature is the signer's entire identity acting, not a detached key. The signer's DID is embedded in the signature. A verifier resolves the signer from the signature itself against the chain, with no PKI, no certificate, and no external key directory. Whatever ownership model the identity defines, single-key, multi-key, or multi-class multisig, is the model that must be satisfied to produce the signature.

### 13.1 What a Signature Embeds

A signature embeds the signer's DID and the signatures that satisfy the identity's ownership model, so the signature carries the signer's identity rather than a bare key. The signer also embeds a message, the content the signer attests to. A signature can embed two further elements:

- a self-attested timestamp (optional; see 13.2);
- a nonce, a number used once (optional).

Each is embedded in the signature and cannot be separated from it, so a verifier cannot strip the timestamp or the nonce off the signature and cannot lift the signature onto other content. When the signer attests to structured content rather than a plain string, the signer encodes that content as the message using dCBOR, for example a document hash together with the metadata that scopes it.

### 13.2 Self-Attested Timestamp

The timestamp is the signer's own clock. It is self-attested, not notarized and not supplied by a third party. A verifier weighs it as a trust property of the signer rather than as independent time. It is always UTC, so no timezone field is carried.

### 13.3 Extensibility

A signature can embed elements beyond the message, timestamp, and nonce. Each additional element binds into the signature on the same terms, embedded and inseparable.

### 13.4 Embedded Request

When a signature answers a request, the request travels inside the response. A not.bot Verify signature embeds a complete copy of the signed request that prompted it: the requesting server's DID, the server's domain-name credential, the nonce, and the claims the server asked for. The request is itself a signed message from the Verify server, so the response carries the server's own signature over its request.

The embedded request binds on the same terms as every other element in section 13.1. A verifier cannot separate it from the response, and cannot move the response onto a different request. Reading the signature recovers both parties to the exchange: the human identity that produced the signature and the server identity that requested it, together with the claim set that server demanded.

The embedded request is present in every Verify signature. No tooling extracts or displays it yet, so it is a latent property of the signature rather than an exposed feature.

---

## 14. Off-Chain Presentations

`did:julia` supports spend bundles that prove DID control and credential possession without being intended for blockchain submission.

### 14.1 Invalidated Spend Bundles

The invalidation operation adds an impossible condition, making the spend bundle fail if submitted on-chain. A verifier can still replay the spend bundle on its own machine to confirm that all signatures, assertions, credential proofs, and DID state commitments line up.

Optional time-window assertions can bound the validity period of an off-chain presentation.

This is the mechanism behind fully offline or online-off-chain verifiable presentations: the proof is a well-formed Chia spend bundle except for the deliberate invalidating condition.

### 14.2 Self-Certifying Lineage Proof

The prelauncher's key commitment (section 3.3) makes a DID self-certifying: its full key history can be verified with no blockchain access. The presenter reveals the original BLS public key and every subsequent spend that mutated the DID, with one aggregated BLS signature covering them all. The presentation spend is the last spend in the chain. All prior spends are proof that the keys in the current version of the coin are the correct keys for the DID with that launcher ID; the presentation spend itself exercises those keys.

A fully offline verifier walks the chain from the prelauncher forward and ends holding the DID's current state and the proof that the presenter controls it.

A signed message or presentation stays verifiable after the signer rotates keys. Verification resolves the signer's lineage from the launcher commitment rather than from a current key, so a signature produced under a retired key still verifies against the DID it named.

### 14.3 Offline Credential Verification

Verifiable credentials state claims by an issuer about a subject, and the subject is identified by DID. A verifier holding a recent snapshot of an issuer's credential-signing keys and revocation bitfields can verify a credential presentation from a DID it has never seen before, while fully offline.

---

## 15. Funding and Transaction Mechanics

### 15.1 Faucet

Creating a DID requires small amounts of XCH to fund the prelauncher, launcher, and initial singleton coins, plus blockchain fees. The faucet mechanism supplies this without the user holding XCH. Julia Social mints batches of faucet coins. To fund a user operation, the service signs a faucet coin spend with a concurrent-spend assertion referencing the user's coin, producing an incomplete spend bundle. The user's spend asserts back at the faucet coin. Neither spend can confirm without the other, so the faucet coin cannot be diverted to any other purpose. The faucet coin uses RESERVE_FEE to ensure the value can only be used to pay the blockchain fee.  The user never holds, receives, or accounts for XCH.

### 15.2 Transaction Rate

Chia produces a transaction block about every 52 seconds, and a singleton can be spent at most once per transaction block. Fast-forward compatibility keeps this from becoming a bottleneck: any number of unchanged-state spend bundles can be submitted at the same time without coordination, and off-chain presentations never consume a blockchain spend, so they can be created in unlimited quantities. For on-chain operations, combining several operations in one spend (section 6) keeps the per-transaction-block limit from binding in practice.

### 15.3 Dummy Transactions

Julia Social operates a decoy service that submits faucet-funded spends performing DID creation, rekey, and recovery operations. The decoys match the structure of real user transactions, and an outside observer cannot tell them apart. Their purpose is privacy: without decoys, several alias DIDs rekeying in the same block would form a distinctive on-chain pattern linking them to one identity. The decoys add noise that defeats timing analysis, clustering, and identity correlation. The formal observer-protection assertions live in [Privacy Architecture](http://doc_07_privacy_architecture.md).

---

## 16. Recovery

`did:julia` supports three recovery-related flows. This section describes the on-chain mechanics; the user-facing recovery design is covered in [Recovery (6D)](http://doc_06D_recovery.md).

### 16.1 Time-Locked Assisted Recovery

Recovery participants are DIDs, not raw keys. A recovery configuration specifies participant classes and quorum requirements. To initiate recovery, the required participants authorize a proposed replacement state.

Initiation commits pending replacement values and starts a delay. During the delay, normal authenticated operations are blocked. After the delay, anyone can complete the recovery, applying the pending replacement state.

### 16.2 Recovery Cancellation

The configured recovery participants can cancel a pending recovery before completion. Cancellation clears the pending recovery state and restores normal operation under the existing authentication configuration.

### 16.3 Pre-Rotated-Key Recovery

The DID can also commit to a future key configuration before it is needed. If current keys are compromised or lost, the pre-rotated keys can take over at once, with no assisted-recovery delay.

The pre-rotation flow also commits the next pre-rotated configuration so the safety property continues after use.

---

## 17. Security Properties

### 17.1 Hash-Enforced Routing

The implementation restricts delegated child puzzles by hash. This prevents an authenticated DID spend from substituting arbitrary behavior into the identity-layer call graph.

### 17.2 Namespace Guard

The protocol reserves the `JDID` namespace for identity-layer messages and announcements. The guarded passthrough operation rejects user-supplied protocol-looking messages and announcements so application-level conditions cannot spoof DID, credential, issuer, or recovery signals.

### 17.3 Nonce Protection

Credential and message presentations can include nonce announcements. Nonces prevent replay and protect against BLS signature-subtraction attacks where an attacker attempts to extract a reusable component from an aggregate signature.

### 17.4 Recovery Lockout

Normal authenticated operations are blocked while assisted recovery is pending. This prevents a compromised current key holder from racing arbitrary state changes during the recovery window.

### 17.5 State-Bound Authorization

Authorization is bound to puzzle hashes that commit to DID state. A state change changes the puzzle hash, invalidating old state-bound authorizations.

### 17.6 Issuer Key Melt

Credential issuance authority is separated into issuer key singletons. Melting an issuer key gives the issuer DID a coarse emergency revocation mechanism for all credentials signed by that issuer key.

---

## 18. Relationship to Doc #19

This document omits, by design, exact Chialisp signatures, line counts, child-puzzle allowlists, error-code tables, and per-file implementation details. Those details belong in **[Chialisp Code Reference (Doc #19)](http://doc_19_chialisp_reference.md)** and may change when the open-source puzzle set changes.

Protocol facts that should remain stable here:

- Julia DIDs are Chia singletons identified by launcher ID.
- DID state is committed into puzzle hashes.
- Authentication supports single-key, multi-class multisig, and custody.
- Recovery supports time-locked assisted recovery, cancellation, and pre-rotation.
- Credential issuance uses issuer key singletons.
- Credential presentation is Chia-spend-verifiable and can run on-chain or carry a deliberate invalidating condition for off-chain use.
- DID-controlled coins are commanded by DID spends rather than embedding all DID logic in each asset coin.
- Presentations compose claims from independent issuers under one aggregate BLS signature.
- User operations are faucet-funded; users never hold XCH.
- A Julia DID cannot be melted; the launcher ID is permanent.
