Identity and Credentials
Overview
ULedgerNET implements a standards-compliant identity layer on top of its wallet and entity system:
- Decentralized Identifiers (DIDs): W3C DID Core standard identifiers for wallets and entities.
- Verifiable Credentials (VCs): W3C Verifiable Credential Data Model 2.0 signed claims about DID subjects.
These standards allow ULedgerNET identities to interoperate with external identity infrastructure, comply with W3C and emerging regulations, and serve as the basis for verifiable, machine-readable attestations about agents, entities, and their permissions.
Decentralized Identifiers (DIDs)
Format
Every wallet and entity on ULedgerNET has a DID:
did:ulnet:<chainId>:<address>
Examples:
did:ulnet:ca930ce44643b66ae33d54351cc0557d1d904b85ec9256fb7ee34fe5e3c318b4:8e620db359101eb28bf9db4f08c2677349f7747d1c59528774f2193318b8c1c7
did:ulnet:ca930ce44643b66ae33d54351cc0557d1d904b85ec9256fb7ee34fe5e3c318b4:<entity-address>
ulnet: the ULedgerNET DID method.<chainId>: the blockchain ID (hex hash) identifying the specific chain.<address>: the wallet address (SHA256 of public key) or entity address (transaction ID of creation tx).
DID Document
A DID document is the machine-readable description of a DID subject. It contains verification methods (public keys) and the capability relationships between the subject and other DIDs.
{
"@context": ["https://www.w3.org/ns/did/v1"],
"id": "did:ulnet:<chainId>:<walletAddress>",
"verificationMethod": [
{
"id": "did:ulnet:<chainId>:<walletAddress>#key-1",
"type": "EcdsaSecp256k1VerificationKey2019",
"controller": "did:ulnet:<chainId>:<walletAddress>",
"publicKeyJwk": { ... }
}
],
"authentication": ["did:ulnet:<chainId>:<walletAddress>#key-1"],
"assertionMethod": ["did:ulnet:<chainId>:<walletAddress>#key-1"],
"capabilityDelegation": ["did:ulnet:<chainId>:<parentWalletAddress>"]
}
For post-quantum keys (ML-DSA-87), the verification method uses publicKeyMultibase instead of publicKeyJwk.
DID Document Metadata
Entity-specific information is included in didDocumentMetadata, not in the document itself:
{
"didDocumentMetadata": {
"entityType": "AGENT",
"name": "alice-research-agent",
"capabilities": [...],
"created": "<block-height>",
"enabled": true
}
}
This separation keeps the DID document itself W3C-compliant while exposing ULedgerNET-specific extensions through metadata.
Key Algorithms in DID Documents
| Key Type | DID Representation | JWS Algorithm |
|---|---|---|
secp256k1 | publicKeyJwk (EC, crv: secp256k1) | ES256K |
ed25519 | publicKeyJwk (OKP, crv: Ed25519) | EdDSA |
mldsa87 | publicKeyMultibase | ML-DSA-87 (draft) |
bls12377 | (refused for credential issuance) | N/A |
Verifiable Credentials (VCs)
What They Are
A Verifiable Credential is a cryptographically signed claim about a DID subject. In ULedgerNET's context, credentials serve as machine-readable attestations about:
- What entity an agent wallet belongs to.
- What role and permissions a wallet holds.
- What capabilities the wallet is authorized to exercise.
- That a specific transaction occurred at a specific block (authentication credentials).
Credential Types
1. Identity Credential (ULedgerIdentityCredential)
Attests to the identity and organizational role of a wallet:
{
"@context": ["https://www.w3.org/ns/credentials/v2"],
"type": ["VerifiableCredential", "ULedgerIdentityCredential"],
"issuer": "did:ulnet:<chainId>:<issuerAddress>",
"validFrom": "<ISO-8601>",
"credentialSubject": {
"id": "did:ulnet:<chainId>:<walletAddress>",
"entityType": "AGENT",
"role": "admin",
"capabilities": [
{
"action": "contract:invoke",
"resource": "<contract-address>",
"notAfter": 100000
}
],
"validUntilBlock": 100000
}
}
Use cases:
- Presenting an agent's credentials to an external system that needs to verify its scope.
- Compliance documentation: proof that an agent was authorized to act at a given time.
- Onboarding: machine-readable credential a new agent presents to a contract to self-register.
2. Authentication Credential (ULedgerAuthenticationCredential)
Attests that a specific wallet signed a specific transaction:
{
"@context": ["https://www.w3.org/ns/credentials/v2"],
"type": ["VerifiableCredential", "ULedgerAuthenticationCredential"],
"issuer": "did:ulnet:<chainId>:<nodeAddress>",
"credentialSubject": {
"id": "did:ulnet:<chainId>:<walletAddress>",
"transactionId": "<txId>",
"blockHash": "<blockHash>",
"audience": "<targetDID>"
}
}
Use cases:
- Proving to an external service that a wallet signed a specific action.
- Cross-system authentication: a wallet on ULedgerNET authenticates to an external API by presenting a credential signed by the node.
JWT/JWS Format
Credentials are encoded as JWTs:
Header: { "alg": "ES256K", "typ": "vc+jwt" }
Payload: { ...credential claims... }
Signature: signed by issuer's private key
The typ: "vc+jwt" header is required by the W3C VC 2.0 specification.
Issuance
Credentials are issued via the node's credential endpoint:
POST /credentials/{chainId}/issue
- Rate-limited per IP.
- Requires the requesting wallet to be registered and enabled on the chain.
- Returns a signed JWT credential.
Revocation
Revocation uses the W3C Bitstring Status List specification. A revocation status entry is embedded in the credential, pointing to a status list maintained by the issuing node. Checking revocation status requires fetching the status list from the node.
Node-Local State
Critical: The credential registry is node-local. It is not replicated across the council. Each node independently issues and tracks its own credentials. The Load operation during node reconcile deliberately excludes the credential registry to prevent a peer's state from overwriting a node's local credential records.
Implications:
- A credential issued by Node 1 is tracked by Node 1's registry, not by Node 2.
- Revocation status must be checked against the issuing node.
- For cross-node credential portability, the credential JWT itself (which is self-contained) is the portable artifact, not the registry entry.
Identity Lifecycle (Go SDK)
The cmd/identity runner in the Go SDK covers the full identity lifecycle:
go run ./cmd/identity --config identities.json
Steps performed:
- DID creation: register wallets, creating their on-chain DID.
- Entity creation: create the AGENT entity linked to the owner wallet.
- Child entity creation: create a child entity (e.g., a sub-agent).
- Metadata alteration: update entity metadata via
ALTER_ENTITY. - Recovery configuration: set recovery wallets and threshold.
- Access list management: add trusted counterparties or blacklist entries.
- Ownership transfer: transfer entity to new owner wallet.
- Credential issuance: issue an
ULedgerIdentityCredentialfor the agent. - Recovery execution: simulate key loss; execute recovery via threshold approval.
The runner reports pass/fail for each step and saves a timestamped JSON report.
Identity Config (identities.json)
{
"nodeEndpoints": [...],
"blockchainId": "ca930ce4...",
"chainKeyType": "mldsa87",
"wallets": {
"owner": "./wallets-uis-mldsa/<hash>.ukey",
"admin": "./wallets-uis-mldsa/<hash>.ukey",
"newOwner": "./wallets-uis-mldsa/<hash>.ukey",
"agent": "./wallets-uis-mldsa/<hash>.ukey",
"recovery": [
"./wallets-uis-mldsa/<hash1>.ukey",
"./wallets-uis-mldsa/<hash2>.ukey",
"./wallets-uis-mldsa/<hash3>.ukey"
]
},
"entity": {
"name": "merchant-entity",
"type": "ORGANIZATION",
"metadata": { "description": "Demo merchant organization" }
},
"child": {
"name": "alice-agent",
"type": "AGENT",
"metadata": { "description": "Shopper agent" }
},
"recovery": { "threshold": 2 },
"credentials": {
"enabled": true,
"operatorPassword": "...",
"operatorSalt": "..."
}
}