Java implementation of UCAN (User Controlled Authorization Networks) - trustless, decentralized authorization using cryptographically signed capability tokens.
Handles delegation, invocation, validation, and policy evaluation for the UCAN v1.0.0-rc.1 specification.
Built on did-key-java for DID key operations, BouncyCastle for crypto, java-cid for IPLD content identifiers, and java-ipld-cbor for DAG-CBOR encoding.
Inspired by go-ucan and rs-ucan.
| Algorithm | DID prefix | Varsig code |
|---|---|---|
| Ed25519 | z6Mk | EdDSA |
| P-256 | zDn | ES256 |
| P-384 | z82 | ES384 |
| secp256k1 | zQ3s | ES256K |
Add the dependency and required repositories:
<dependency>
<groupId>dev.gitni</groupId>
<artifactId>ucan-java</artifactId>
<version>0.1.2</version>
</dependency><repositories>
<repository>
<id>github</id>
<url>https://maven.pkg.github.com/zZHorizonZz/ucan-java</url>
</repository>
<repository>
<id>danubetech</id>
<url>https://repo.danubetech.com/repository/maven-public/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>// Create principals (DID key pairs)Principalalice = Principal.generate(); // Ed25519 by defaultPrincipalbob = Principal.generate(DidKey.P_256); // or any supported algorithm// Delegate a capability from Alice to BobDelegationdlg = Ucan.v1().delegate()
.issuer(alice)
.audience(bob)
.subject(alice.did())
.command("/blog/post/create")
.policy(Policy.equals(".status", "draft"))
.expiration(Instant.now().plusSeconds(3600))
.build();
// Bob invokes the delegated capabilityInvocationinv = Ucan.v1().invoke()
.issuer(bob)
.subject(alice.did())
.command("/blog/post/create")
.args(Map.of("title", "Hello World", "status", "draft"))
.proof(dlg.cid())
.build();
// Validate the invocation against the proof chainValidationResultresult = Ucan.v1().validate(inv, List.of(dlg));
if (result.isValid()) {
// authorized
}// Alice -> Bob -> Carol (command attenuation)Delegationd1 = Ucan.v1().delegate()
.issuer(alice).audience(bob).subject(alice.did())
.command("/blog")
.build();
Delegationd2 = Ucan.v1().delegate()
.issuer(bob).audience(carol).subject(alice.did())
.command("/blog/post") // attenuated from /blog
.build();
Invocationinv = Ucan.v1().invoke()
.issuer(carol).subject(alice.did())
.command("/blog/post")
.proof(d1.cid()).proof(d2.cid())
.build();// ComparisonsPolicy.equals(".status","draft")
Policy.greaterThan(".priority",3)
Policy.like(".author.email","*@example.com")
// ConnectivesPolicy.and(Policy.equals(".status", "draft"),Policy.lessThan(".priority",10))
Policy.or(Policy.equals(".role", "admin"),Policy.equals(".role","editor"))
Policy.not(Policy.equals(".deleted", true))
// QuantifiersPolicy.all(".reviewers",Policy.like(".email", "*@example.com"))
Policy.any(".tags",Policy.equals(".", "news"))// Encode to DAG-CBORbyte[] encoded = dlg.encoded();
Cidcid = dlg.cid();
// Parse from DAG-CBOR (auto-detects type)Tokentoken = Ucan.v1().parse(encoded);
if (tokeninstanceofDelegationd) {
// handle delegation
} elseif (tokeninstanceofInvocationi) {
// handle invocation
}sdk env
mvn clean verify
Requires Java 25+ and Maven 3.9+.
Implements the UCAN v1.0.0-rc.1 specification:
- Delegation (spec): token creation, signing, parsing, and validation
- Invocation (spec): capability exercise with proof chain
- Policy language: all comparison operators, connectives (and/or/not), quantifiers (all/any), glob patterns, jq-style selectors
- DAG-CBOR encoding via java-ipld-cbor with canonical key ordering
- Varsig signature headers for algorithm-agile envelopes
- CIDv1 content addressing via java-cid (SHA-256 + DAG-CBOR multicodec)
- Delegation chain validation: signature verification, time bounds, chain linkage, command attenuation, policy evaluation
Not yet implemented: Revocation, Promise (pipeline), Receipt.
- UCAN specification
- Delegation sub-spec
- Invocation sub-spec
- ucan.xyz
- did-key-java (DID key operations)
- go-ucan (Go reference implementation)
- rs-ucan (Rust reference implementation)
Contributions are welcome. Please open an issue to discuss before submitting a PR.