A comprehensive guide to using the Autonomi network with the Java SDK.
# Prerequisites
# - Java 21+: https://adoptium.net/
# - antd daemon running (ant dev start)Gradle (build.gradle.kts):
dependencies {
implementation("com.autonomi:antd-java:0.1.0")
}Maven (pom.xml):
<dependency>
<groupId>com.autonomi</groupId>
<artifactId>antd-java</artifactId>
<version>0.1.0</version>
</dependency>Or scaffold a new project:
ant dev init java --name my-projectimport com.autonomi.antd.AntdClient;
// REST transport (default) — implements AutoCloseable
try (var client = AntdClient.create()) {
// use client
}
// Custom endpoint
try (var client = AntdClient.builder()
.transport("rest")
.baseUrl("http://localhost:8082")
.timeout(Duration.ofSeconds(30))
.build()) {
// use client
}
// gRPC transport
try (var client = AntdClient.builder()
.transport("grpc")
.target("localhost:50051")
.build()) {
// use client
}AntdClient implements AutoCloseable. Always use try-with-resources to ensure proper cleanup.
var status = client.health();
System.out.println("Healthy: " + status.ok());
System.out.println("Network: " + status.network()); // "local", "default", "alpha"Response types are Java records with accessor methods.
import java.nio.charset.StandardCharsets;
// Store
byte[] payload = "Hello, Autonomi!".getBytes(StandardCharsets.UTF_8);
var result = client.dataPutPublic(payload);
System.out.println("Address: " + result.address());
System.out.println("Cost: " + result.cost() + " atto tokens");
// Retrieve
byte[] data = client.dataGetPublic(result.address());
System.out.println(new String(data, StandardCharsets.UTF_8)); // "Hello, Autonomi!"
// Cost estimation — returns UploadCostEstimate with size, chunks, gas, payment mode
UploadCostEstimate est = client.dataCost(payload);
System.out.println("Estimate: " + est.fileSize() + " bytes in " + est.chunkCount()
+ " chunks, " + est.cost() + " atto, gas " + est.estimatedGasCostWei()
+ " wei, mode " + est.paymentMode());// Store (self-encrypting)
byte[] secret = "secret message".getBytes(StandardCharsets.UTF_8);
var result = client.dataPut(secret);
String dataMap = result.dataMap(); // Keep this secret!
// Retrieve (decrypt)
byte[] data = client.dataGet(dataMap);
System.out.println(new String(data, StandardCharsets.UTF_8));// Upload a file
var result = client.filePutPublic("/path/to/file.txt");
System.out.println("File address: " + result.address());
// Download a file
client.fileGetPublic(result.address(), "/path/to/output.txt");
// Cost estimation — returns UploadCostEstimate with size, chunks, gas, payment mode
UploadCostEstimate est = client.fileCost("/path/to/file.txt");A pointer is an owner-signed address you can move to new data. antd signs with the owner keys it loaded at startup (antd --pointer-keys <file-or-dir>, key files from ant pointer keygen), so owner is the pointer's address, never a key. Every create, update and transfer pays (about the price of one chunk) and is never retried; reads are free. A transfer hands the pointer over to another pointer for good. Needs antd 0.16.0 or later (the first release with pointers). Walk-through: Tutorial: Point at a Public File and Update It.
String owner = client.pointerKeys().get(0); // a pointer antd can write
String v1 = client.dataPutPublic("version 1".getBytes()).address();
String v2 = client.dataPutPublic("version 2".getBytes()).address();
client.pointerCreate(owner, v1); // pays; counter 0
client.pointerUpdate(owner, v2); // pays again; counter 1
PointerTarget target = client.pointerResolve(owner); // target.target() equals v2 (free)
PointerFinality finality = client.pointerFinality(owner); // free
System.out.println(finality.rawStatus()); // "open"Counters are BigInteger, because a final pointer sits at 2^64 − 1. pointerTransfer, pointerGet, pointerController, pointerPrepare and pointerFinalize complete the set, with Async variants on AsyncAntdClient and the same methods on GrpcAntdClient.
Runnable example: antd-java/examples/src/main/java/com/autonomi/examples/Example08Pointers.java. Set ANTD_POINTER_OWNER to an address the daemon holds a key for, one it has not written yet (each run creates the pointer and pays for its writes), and ANTD_BASE_URL for a daemon other than http://localhost:8082:
cd antd-java
ANTD_POINTER_OWNER=<address> ./gradlew :examples:run -PmainClass=com.autonomi.examples.Example08Pointersimport com.autonomi.antd.*;
try {
client.dataGetPublic("nonexistent");
} catch (NotFoundException e) {
System.out.println("Not found");
} catch (PaymentException e) {
System.out.println("Payment issue");
} catch (NetworkException e) {
System.out.println("Network unreachable");
} catch (AntdException e) {
System.out.println("Error (" + e.statusCode() + "): " + e.getMessage());
}Exception hierarchy (all extend AntdException, which extends RuntimeException):
| Exception | HTTP Code | When |
|---|---|---|
BadRequestException |
400 | Invalid parameters |
PaymentException |
402 | Insufficient funds |
NotFoundException |
404 | Resource not found |
AlreadyExistsException |
409 | Duplicate creation |
ForkException |
409 | Version conflict |
TooLargeException |
413 | Payload too large |
InternalException |
500 | Server error |
NetworkException |
502 | Network unreachable |
A manifest describes a set of files as one ant://manifest/ link, or as a .ant file when the link would be too long. Each entry is either a public file's address or an embedded DataMap (the manifest is then the key to a private file), with an optional path. Building and decoding upload and pay for nothing: upload the files first, then describe them. Needs antd 0.16.0 or later. Walk-through: Tutorial: Share a Set of Files as One Link.
String one = client.dataPutPublic("one".getBytes()).address(); // pays
String two = client.dataPutPublic("two".getBytes()).address(); // pays
BuiltManifest built = client.buildManifest(List.of( // free
ManifestEntryInput.ofAddress(one).withPath("notes/one.txt"),
ManifestEntryInput.ofAddress(two).withPath("notes/two.txt")),
BuildManifestOptions.DEFAULT.withName("example"));
System.out.println(built.link()); // null when over 1,500 bytes: share built.manifest() instead
DecodedManifest decoded = client.decodeManifestLink(built.link(), true); // free
decoded.entries().forEach(e -> System.out.println(e.effectiveName() + ": " + e.exactSize()));decodeManifest(byte[] bytes, boolean resolve) takes raw .ant bytes. ManifestEntryInput.ofDataMap(hex) makes an embedded entry; BuildManifestOptions has keepShrunkDataMaps (default false: roots are embedded). AsyncAntdClient has Async-suffixed variants and GrpcAntdClient the same methods.
Every upload result (DataPutResult, DataPutPublicResult, FilePutResult, FilePutPublicResult and, for external-signer uploads, FinalizeUploadResult) also has sha256() (null when none): the plaintext's SHA-256 (hex). To record it in a manifest, pass it as the entry's sha256: ManifestEntryInput.withSha256(hex) records one, and ManifestEntryInfo.sha256() reads it back. Requires antd 0.17.0 or later; the SDK sends it only when set and does not check it.
Runnable example: antd-java/examples/src/main/java/com/autonomi/examples/Example09Manifests.java. Its two uploads pay; the manifest calls do not.
cd antd-java
ANTD_BASE_URL=http://localhost:8082 ./gradlew :examples:run -PmainClass=com.autonomi.examples.Example09Manifestscd antd-java
# Gradle
./gradlew run --args="1" # Connect
./gradlew run --args="2" # Public data
./gradlew run --args="3" # Chunks
./gradlew run --args="4" # Files
./gradlew run --args="6" # Private data
./gradlew run --args="all" # Run all examples
# Maven
mvn exec:java -Dexec.args="1"Or use the dev CLI:
ant dev example data -l java
ant dev example all -l java