Quick Start
Get verifiable, content-addressed storage running in under 20 lines of Swift. Start with the data layer and scale to a full blockchain when you're ready.
1. Add Dependencies
In your Package.swift:
Package.swift
dependencies: [
.package(url: "https://github.com/treehauslabs/Acorn.git", from: "1.0.0"),
.package(url: "https://github.com/treehauslabs/AcornMemoryWorker.git", from: "1.0.0"),
.package(url: "https://github.com/treehauslabs/AcornDiskWorker.git", from: "1.0.0"),
]
2. Create a Storage Chain
main.swift
import Acorn
import AcornMemoryWorker
import AcornDiskWorker
let memory = MemoryCASWorker(capacity: 10_000)
let disk = try DiskCASWorker(
directory: URL(filePath: "/tmp/lattice-store"),
capacity: 100_000
)
let chain = await CompositeCASWorker(
workers: ["memory": memory, "disk": disk],
order: ["memory", "disk"]
)
How chaining works
The CompositeCASWorker initializer automatically links workers: memory.far = disk, disk.near = memory. You don't need to call link() manually.
3. Store and Retrieve Content
let data = Data("Hello, Lattice!".utf8)
let cid = ContentIdentifier(for: data)
await chain.store(cid: cid, data: data)
if let retrieved = await chain.get(cid: cid) {
print(String(data: retrieved, encoding: .utf8)!) // "Hello, Lattice!"
}
The first get() might hit disk. The second will hit memory — content automatically backfills toward the fast end of the chain.
4. Add Networking
To fetch content from peers, add Ivy and Tally:
Package.swift (additional deps)
.package(url: "https://github.com/treehauslabs/Tally.git", from: "1.0.0"),
.package(url: "https://github.com/treehauslabs/Ivy.git", from: "1.0.0"),
Networked node
import Ivy
let config = IvyConfig(
publicKey: myPublicKey,
listenPort: 4001,
bootstrapPeers: [
PeerEndpoint(publicKey: seedKey, host: "seed.example.com", port: 4001)
]
)
let node = Ivy(config: config)
try await node.start()
// Get the network worker and add it to the chain
let netWorker = await node.worker()
let fullChain = await CompositeCASWorker(
workers: ["memory": memory, "disk": disk, "network": netWorker],
order: ["memory", "disk", "network"]
)
Now fullChain.get(cid:) tries memory → disk → network. Content fetched from peers automatically backfills to disk and memory.
5. Hot-Path Reads
For performance-critical paths, bypass the actor executor entirely:
// Sync API: 6-28ns instead of ~7.5µs
if let data = memory.syncGet(cid: someCID) {
// Fast path: no async/await, no actor hop
process(data)
} else {
// Slow path: fall through to full chain
if let data = await fullChain.get(cid: someCID) {
process(data)
}
}
When to use sync vs async
Use the sync API (syncGet, syncHas, syncStore) when you're in a tight loop or on a hot path where the ~7µs actor hop overhead matters. For general application code, the async API is simpler and composes with the chain naturally.