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.