Introduction

The Digital Objects Network is a decentralized network that facilitates the creation, execution, and exchange of Digital Objects. Digital Objects are fully programmable state machines that are owned and operated by Internet users, and that can be passed between mutually untrusting Internet users (for example via email) while maintaining their integrity and consistency, without relying on any central trusted authority.

The Digital Objects Network is a very general platform that many applications could be built on. Concretely, Digital Objects can be used to represent many different things: a record in a distributed database; an entire database; a unit of monetary value; an access permission; an attestation; the status of or a lock on another Digital Object or group of Objects; and more. Because Digital Objects are fully programmable and maintain their integrity and consistency across trust boundaries, applications whose data and procedures must execute across multiple untrusting entities, and that were previously prohibitively expensive to run (in trust, operational overhead, or other dimensions), can be operated on top of the Digital Objects Network. For example, transferring residency records between multiple countries or states with heterogeneous systems of record can be implemented as a single atomic action if Digital Objects exist to represent "ports" into each individual system. Likewise with money moving between different financial networks, or records on multiple different ERPs that a business would like to ensure update atomically.

Objects are files held privately by their owners. Each state transition is proved in zero knowledge, and the network publishes only opaque commitments to Ethereum blob data, so the chain sees neither an object's contents nor its code.

Stack

The concepts required to realize the Digital Objects Network, from higher to lower level, next to how they are implemented today:

ConceptImplementation
global state derivationpermissionlessly operable synchronizer
global bulletin boardEthereum blobspace
Class-Action SDKpexe/Rhai SDK
Class-Action protocolpodlang txlib
private statement proving systemPOD2

POD2 is itself layered:

ConceptImplementation
predicate languagepodlang
statement derivation kernelMainPod plonky2 circuit
zk proving systemplonky2

Contents

  • Concepts describes Digital Objects, the operations users can perform on them, and what the network needs to stay live.
  • Architecture describes the components that make up the network and the client configurations users assemble from them.
  • Installing the driver covers installing the driver, opening its browser UI, and adding a plugin.
  • Testnet lists the parameters of the current testnet and the hosted services that serve it.
  • Running the services covers running your own archiver, synchronizer, and relayer.
  • Examples describes how applications are packaged as plugins, and the example applications in the repository.

The source for the network and these docs is at github.com/0xPARC/digital-objects-network.

Concepts

Digital Objects

Digital Objects are arbitrarily programmable state machines that can be passed around between Internet users. Concretely, they exist as files containing data and code, in any encoding. The data encodes an arbitrary state (with arbitrary hierarchical structure and of arbitrary size), and the code specifies a set of state transition functions that are allowed for this particular object--i.e. rules that govern how this particular object came to be, and how it can and cannot be modified from its current state. The "type" of a Digital Object is uniquely identified by its state transition function.

The rules governing one DO may also express its allowed relations to other types of DOs--for example, one DO might specify that its creation requires the consumption/deletion of other DOs of certain types; a DO may also specify that it may only update in a certain way if simultaneously another DO of a related type is updated in a specific way in lockstep.

Every Digital Object is uniquely identified by an ID, and at any given time has a single canonical state. A mechanism exists such that, if there is ever a dispute between two users on the network on the state of a particular object, that dispute can always be unambiguously resolved from an objective network-level point of view requiring and leaking no special permissions or information, within about sixty seconds.

For a Digital Object to be live and usable, it is sufficient that a single person on the network possesses its contents--its current state and state transition function. Furthermore, any such person who possesses the contents of a Digital Object can modify it according to those rules; and any person who does not possess the contents of a Digital Object does not know anything about the Object (its contents, its type, or its state transition function) other than the fact that an Object of that ID exists on the network somewhere.

User capabilities

Any digital entity--a person, a business, a bot, an LLM agent--can be a user of the Digital Objects Network. Specifically, a user can access the network and interact with Digital Objects so long as they have the capability to perform a handful of specific cryptographic operations (mostly hashing and zero-knowledge proofs, which can be performed in seconds on a modern consumer laptop), and an Internet connection.

Concretely, any user who satisfies those requirements can:

  • Create a new Digital Object.
  • Execute the state transition function of a Digital Object (or a group of Digital Objects, if their state transition functions specify that they may only mutate via a multi-Object transaction).
  • Destroy a Digital Object, provided that they meet the conditions for destruction according to the Object's code.
  • Receive a Digital Object, meaning that they may receive an encoding of the state and code for a particular Object from another user. Once the receiver possesses a copy of the Object, they themselves now have the ability to Execute, Destroy, and Send this Object--notably, they may now have the ability to revoke those permissions from the original sender.
  • Send a Digital Object, meaning that they may send to another user the state and code for a particular Object.
  • Resolve a Digital Object, meaning that they can check a Digital Object that they possess for integrity (its state transition function has always been correctly executed in the past leading up to this point) and consistency (that its particular state at this point in time is valid and canonical according to an objective network-level view).
  • Present a Digital Object, meaning that they can respond to queries from others about an Object they possess, with cryptographically verifiable correctness.

Additionally, users who don't have the ability to perform cryptographic operations, or who don't have an Internet connection, can still participate partially. For example, users without an Internet connection can still Create and Present new Digital Objects in a limited fashion, and users without access to certain cryptographic capabilities may still Receive and Resolve a Digital Object from other users.

Network requirements

For the network to remain live and operational, the following two requirements must be met:

  • The Ethereum Data Availability network must be live. (The network makes use of Ethereum blobspace as a publicly-accessible broadcast channel.)
  • At all times, at least one Digital Objects Network user with access to upper-end consumer-grade hardware must be operating a piece of software infrastructure called a synchronizer.
    • Operating a synchronizer is not a permissioned role and confers no special network privileges. It's roughly analogous to how a file on the BitTorrent network is available if there is at least one seeder.
    • Many people can operate synchronizers, and this will not cause any conflicts.

The Architecture chapter describes the components that realize these guarantees.

Architecture

This chapter outlines the overall architecture of the network and how operationally you can realize the guarantees outlined in Concepts.

Digital Object (DO)

A Digital Object (DO) is a file with four parts:

  • State — the object's current data, of arbitrary structure and size.
  • Code — the rules that govern how the state may change, and the conditions under which the object may be created or destroyed.
  • Proof — evidence that every state change in the object's history followed the rules in its code.
  • Commitment — a public record, posted on the network's broadcast channel, that commits to the object's current state without revealing it.

Lifecycle. An object is created, transitioned any number of times, and eventually consumed. Each transition consumes the object's current commitment and publishes its successor in the same step, so the object stays live throughout, represented at any moment by a single current commitment.

Components

Five components together make the network work.

+----------------------+
| Digital Object files |
+----------------------+
            ^
            | read and write
            v
      +-----------+     submit update     +-----------+
      |   Driver  | --------------------> |  Relayer  |
      +-----------+                       +-----------+
            ^                                   |
            | canonical state                   | publish blob
            |                                   v
    +--------------+ read recent blobs +----------------+
    | Synchronizer | <---------------- | Ethereum blobs |
    +--------------+                   +----------------+
            ^                                   |
            | serve historical blobs            | persist blobs
            |                                   v
            |                             +-----------+
            +---------------------------- |  Archiver |
                                          +-----------+

Ethereum blobs

The network uses Ethereum blobs as its public broadcast channel. Blobs are append-only, publicly readable payloads attached to Ethereum consensus blocks: anyone may post one, anyone reading the chain receives them in canonical order, and once a blob has been included in a finalized block its place in history cannot be rewritten.

Blob data is retained directly by Ethereum consensus nodes for a limited window (roughly 18 days). The cryptographic commitment to each blob, however, lives permanently in its containing block header. The Archiver closes the resulting gap between permanent ordering and bounded data retention.

Archiver

A service that reads blobs from Ethereum while they are still available and stores their payloads durably. Its role is data availability: synchronizers bootstrapping for the first time, or recovering from a long outage, need blob payloads from beyond Ethereum's retention window, and the Archiver supplies them.

The Archiver requires no trust: its output is verifiable against the permanent blob commitments in Ethereum block headers, so a faulty or dishonest Archiver cannot inject false history. Anyone may run one, and any number may operate concurrently.

Synchronizer

A service that reads Ethereum blobs — recent ones directly from the chain, older ones from the Archiver — and from them reconstructs the canonical global state: two append-only sets, the commitments that have been published and the nullifiers that mark commitments as consumed. A commitment is live until a nullifier is posted, and the order in which nullifiers are published settles any double spend.

The synchronizer holds no secrets. Anyone with blob data can run their own synchronizer and arrive at the same answer. A single honest synchronizer is sufficient for the network to be live.

Relayer

A service that takes proposed digital object updates and submits them to Ethereum as blob payloads. The relayer is not trusted with object contents, it sees only the same opaque commitments and consumptions that will appear on-chain. Its job is convenience and availability: clients who do not want to maintain an Ethereum signer or pay gas directly can submit through any relayer they choose.

Driver

A service that manages the digital objects themselves. The driver stores object files, runs the code that transitions them, produces the evidence each transition requires, talks to the relayer to publish an update, and talks to the synchronizer to verify the status of objects (live or consumed).

The Driver holds the only copy of an object's contents; no other component stores them. If a holder loses their object file, the object is gone. Its commitment still shows as live on-chain, but the state needed to use it existed only in that file.

Clients

A client is a deployment a user assembles from these components. Every component except the Driver has no special access: none holds object contents and each is a deterministic function of public data.

They differ, though, in how cheaply their output can be checked — the Archiver's can be verified one blob at a time against its on-chain commitment, while the Synchronizer's global state can only be checked by re-deriving it from the blobs yourself (i.e. running one).

A user is therefore free to run as much or as little infrastructure as they like and delegate the rest to hosted services, trading operational cost against how little they have to trust.

Three different configurations on that spectrum are:

1. Full node — run everything

Run the Driver, Relayer, Synchronizer, and Archiver yourself, backed by your own Ethereum node. Reading blobs requires a consensus-layer beacon node while submitting blobs and reading block headers requires an execution node and a signer.

  • Trust: nothing but Ethereum itself, namely that it keeps producing finalized blocks and does not reorganize finalized history.
  • Guarantees:
    • You read blobs first-hand and run your own synchronizer over them, so the canonical state is computed from data you obtained yourself, giving you consistency and no double spend (no object can be spent twice).
    • You submit your own blobs through your own signer, so you cannot be censored.
    • You run your own archiver, so your data availability never depends on anyone else — you can bootstrap and recover entirely on your own.
    • Integrity and privacy hold as they always do: the Driver verifies every proof locally, and object contents never leave your machine.

This is the heaviest deployment and the only one that depends on no third party.

2. Light client — Driver only

Run only the Driver, pointed at hosted relayer, synchronizer, and archiver endpoints, which read and write Ethereum on your behalf. This is the lightest possible client.

What you keep because the Driver is local:

  • Privacy — object contents and code never leave your machine. Every hosted service sees only the opaque commitments and consumptions that appear on-chain.
  • Integrity — the Driver verifies the proof of every object you accept, so whoever sends you an object cannot give you one with invalid history without you detecting it.
  • Local autonomy — you run transitions and produce verifiable presentations of your objects entirely on your own machine.

What you now trust:

  • The synchronizer for consistency and no double spend. You take its word for which objects are live and which are consumed; a dishonest one could misreport canonical state — for instance, claim an object you are about to receive is still live after it was consumed.
  • The relayer for censorship-resistance. It cannot read or forge your objects, but it can drop or delay your updates and stop you from publishing.
  • The archiver for availability. Its output is checked so it cannot inject false history, but if it is unavailable while you bootstrap or recover, you cannot obtain historical blobs.

3. Contributing client — Driver + Archiver (+ Relayer)

Another possible configuration is to run the Driver and your own Archiver, and optionally your own Relayer. Your archiver needs blob reads, which it can get from your own beacon node or from a hosted beacon endpoint — because blob data is self-verifying against block-header commitments, reading it from a hosted endpoint adds no correctness trust, only a dependency on that endpoint for the read path.

Running an archiver makes you one of the N parties keeping the network's history available — the way a seeder keeps a torrent alive. Over the light client you gain:

  • Self-sufficient availability — your bootstrap and recovery no longer depend on any hosted archiver; historical blobs are in your own hands.
  • You add availability to the network — others can now read history from you, strengthening the network overall.

Running your own relayer or signer adds censorship-resistance: you submit your own blobs, so no hosted relayer can drop or delay your updates. What still depends on a hosted service is the synchronizer for consistency and no double spend.

Interfaces

The Driver exposes a uniform API, so it can be driven through any interface — a command-line tool, a desktop or web GUI, an automated agent, or another program.

Generating the zero-knowledge proof for each transition is the one computationally significant step, but it can be run on any modern consumer laptop — no specialized hardware, GPU, or server required.

Installing the driver

The driver is a service that manages the digital objects themselves. Installing it puts four binaries under ~/.dobj/bin/:

BinaryWhat it is
dobjdDriver daemon with bundled browser UI (REST/SSE on :7717, optional MCP on :7718)
dobjTerminal CLI
dobj-mcp-proxyStdio bridge for agents that only speak stdio (e.g. Claude Desktop)
pexePackaging tool for .pexe plugin archives

The driver installs with an empty action catalog: applications ship separately as plugins (.pexe files). The steps below install the binaries, open the browser UI with dobj ui, and add an example plugin such as craft-basics or craft-rocket.

Release binaries are configured with the hosted synchronizer and relayer endpoints, so a fresh install needs no configuration.

1. Install the binaries

The install script downloads the latest release into ~/.dobj/bin and prints a PATH hint. To pin a version, set DOBJ_VERSION (bash) / $env:DOBJ_VERSION (PowerShell) to a release tag first.

On macOS or Linux:

curl -fsSL https://raw.githubusercontent.com/0xPARC/digital-objects-network/main/install.sh | sh

On Windows (PowerShell):

irm https://raw.githubusercontent.com/0xPARC/digital-objects-network/main/install.ps1 | iex

Windows: the binaries aren't codesigned yet, so SmartScreen may show "Windows protected your PC". Click More info → Run anyway.

To install without the script, see Manual install below.

2. Open the browser UI

On macOS or Linux:

~/.dobj/bin/dobj ui

On Windows:

& "$env:USERPROFILE\.dobj\bin\dobj.exe" ui

dobj ui starts the daemon if needed and opens its default UI at http://127.0.0.1:7717/ui/. Run it again whenever you want to return to the UI. The release daemon bundles the frontend, so no Node.js or Vite setup is needed. The action catalog is empty until you install a plugin in the next step.

For CLI or agent use without opening a browser, use dobj start instead. dobj ui --no-open starts the daemon if needed and prints the URL.

Note: the first start builds ZK circuits and can take a few minutes. Later starts take seconds.

3. Install a plugin

dobj install takes a local .pexe path or an http(s) URL, validates it, and hot-reloads the running daemon's catalog, so the daemon does not need to restart. Two example plugins ship with each release:

Install either, or both. On macOS or Linux:

~/.dobj/bin/dobj install https://github.com/0xPARC/digital-objects-network/releases/latest/download/craft-basics.pexe
~/.dobj/bin/dobj install https://github.com/0xPARC/digital-objects-network/releases/latest/download/craft-rocket.pexe

On Windows:

& "$env:USERPROFILE\.dobj\bin\dobj.exe" install https://github.com/0xPARC/digital-objects-network/releases/latest/download/craft-basics.pexe
& "$env:USERPROFILE\.dobj\bin\dobj.exe" install https://github.com/0xPARC/digital-objects-network/releases/latest/download/craft-rocket.pexe

After installing a plugin, reload the browser tab opened by dobj ui to load its actions.

4. Verify

On macOS or Linux:

~/.dobj/bin/dobj status      # pid + HTTP healthcheck
~/.dobj/bin/dobj actions     # installed plugin actions
~/.dobj/bin/dobj state-root  # 64-hex root: the hosted synchronizer is reachable

On Windows:

& "$env:USERPROFILE\.dobj\bin\dobj.exe" status
& "$env:USERPROFILE\.dobj\bin\dobj.exe" actions
& "$env:USERPROFILE\.dobj\bin\dobj.exe" state-root

5. Connect a client

Every client talks to the same daemon:

  • Browser UI: dobj ui (starts the daemon if needed and opens your browser)
  • CLI: ~/.dobj/bin/dobj (add ~/.dobj/bin to your PATH)
  • Claude Code / MCP agents: claude mcp add --transport http dobj http://127.0.0.1:7718/mcp
  • Stdio-only agents (e.g. Claude Desktop): point them at ~/.dobj/bin/dobj-mcp-proxy

MCP is off by default. Before connecting an agent, enable it with dobj settings set --mcp on (use the full binary path if it is not on your PATH yet). See INSTALL.md for agent-specific setup.

After connecting MCP and starting a fresh agent session, you can optionally open the Digital Objects command menu. In Claude Code:

/mcp__dobj__start

In Codex:

Call dobj invoke_prompt with: {"name":"start","arguments":{"command":"help"}} Then follow the returned messages.

All MCP tools remain available without starting the command interface.

Installing with an agent

An MCP-aware agent (Claude Code, Cursor, ...) can perform the steps above. Give it this prompt:

Read https://raw.githubusercontent.com/0xPARC/digital-objects-network/main/INSTALL.md, install and start the Digital Objects driver, install the craft-rocket plugin, and configure MCP for this agent if supported.

INSTALL.md walks the agent through each step, including plugin install and MCP connection.

Manual install

To fetch the archives yourself, detect your platform's target triple, then download the three tarballs from the releases page (the dobjd tarball bundles dobj-mcp-proxy).

On macOS or Linux:

case "$(uname -s)-$(uname -m)" in
  Darwin-arm64)   TARGET=aarch64-apple-darwin ;;
  Darwin-x86_64)  TARGET=x86_64-apple-darwin ;;
  Linux-x86_64)   TARGET=x86_64-unknown-linux-gnu ;;
  *) echo "unsupported platform: $(uname -sm)"; exit 1 ;;
esac

RELEASE=https://github.com/0xPARC/digital-objects-network/releases/latest/download
mkdir -p ~/.dobj/bin
curl -fsSL "$RELEASE/dobjd-$TARGET.tar.gz" | tar -xz -C ~/.dobj/bin
curl -fsSL "$RELEASE/dobj-$TARGET.tar.gz"  | tar -xz -C ~/.dobj/bin
curl -fsSL "$RELEASE/pexe-$TARGET.tar.gz"  | tar -xz -C ~/.dobj/bin
chmod +x ~/.dobj/bin/dobjd ~/.dobj/bin/dobj ~/.dobj/bin/dobj-mcp-proxy ~/.dobj/bin/pexe

On Windows (PowerShell):

$TARGET  = "x86_64-pc-windows-msvc"     # the only Windows target built
$RELEASE = "https://github.com/0xPARC/digital-objects-network/releases/latest/download"
$DOBJ    = "$env:USERPROFILE\.dobj"
New-Item -ItemType Directory -Force -Path "$DOBJ\bin" | Out-Null
foreach ($name in @("dobjd", "dobj", "pexe")) {
    $tmp = "$DOBJ\$name-$TARGET.tar.gz"
    curl.exe -fsSL "$RELEASE/$name-$TARGET.tar.gz" -o $tmp
    tar -xzf $tmp -C "$DOBJ\bin"
    Remove-Item $tmp
}

Then continue from 2. Open the browser UI.

Other infrastructure

The hosted endpoints are defaults, not requirements. See Choosing services for how to point the driver at any synchronizer and relayer, including your own.

Testnet

The network currently runs as a testnet anchored to the Ethereum Sepolia testnet. Objects, proofs, and state on it are for testing only and may be reset.

Network parameters

ParameterValue
NetworkSepolia (Ethereum testnet)
Start slot11491968 — the beacon slot the network began ingesting blobs from; changes with each testnet reset
Blob address0x4343434343434343434343434343434343434343 — the address blob transactions are posted to

Services that follow the testnet need both values; see Running the services.

Hosted services

These services are run for the testnet. Release builds of the driver use the hosted synchronizer and relayer by default, so a fresh install needs no configuration.

ServiceEndpointRole
Synchronizersynchronizer.digiobjects.netRebuilds canonical network state from Ethereum blobs; answers live / consumed queries
Relayerrelayer.digiobjects.netSubmits proof payloads to Ethereum as EIP-4844 blob transactions
Archiverterminus.digiobjects.net:4001Stores historical blobs durably so synchronizers can bootstrap beyond Ethereum's retention window

Choosing services

A driver can use any synchronizer and relayer, including your own. Set the endpoints with the dobj CLI; the change applies immediately, without a restart:

dobj settings set \
  --synchronizer https://synchronizer.digiobjects.net \
  --relayer https://relayer.digiobjects.net

dobj settings get prints the current values. The driver only talks to a synchronizer and a relayer; the archiver is consumed by synchronizers (to fetch historical blobs), so drivers do not configure it.

Running the services

The network's chain-side services can be run by anyone: an archiver and a synchronizer are enough to verify the network's state from Ethereum chain data yourself, and a relayer lets you submit transactions with your own wallet.

To follow the public testnet, every service must use the network's blob address and the synchronizer must use its start slot; the archiver can start at or before it. Both are listed under Network parameters. The start slot changes over time, as the testnet is reset periodically during testing.

Running from source

Each service is run from a clone of the repository with a just recipe. The recipe builds the service in release mode and reads its configuration from services/<service>/.env; copy the service's .env.example there to start:

cp services/archiver/.env.example services/archiver/.env

The synchronizer and the relayer store their state in Postgres. Running just ensure-db creates the synchronizer and relayer databases on a local Postgres at localhost:5432 (user postgres).

Archiver

The archiver follows beacon blocks, stores the blobs sent to the blob address on its filesystem, and serves them over a beacon-compatible HTTP API, so that the synchronizer can read blobs older than the beacon's ~18-day retention window.

just archiver

Configure it in services/archiver/.env:

VariableRequiredDescription
RPC_URLyesEthereum execution-layer RPC endpoint
BEACON_URLyesBeacon API endpoint that serves blob sidecars
FILTER_ADDRESSyesBlob address; only blobs sent to it are archived
INIT_START_SLOTyesSlot to start archiving from on first run; afterwards it resumes from its stored progress. At or before the network's start slot
BLOBS_PATHyesDirectory where the blobs are stored (no database). Grows by ~128 KB per archived blob
HTTP_BINDnoAddress the HTTP API listens on (default 127.0.0.1:3001)

Synchronizer

The synchronizer reads the network's blobs from Ethereum, verifies their proofs, derives the network state (created objects, nullifiers, state roots), and serves it over HTTP to drivers, which use it to build and check their transactions.

just sync

Configure it in services/synchronizer/.env:

VariableRequiredDescription
RPC_URLyesEthereum execution-layer RPC endpoint
BEACON_URLyesBeacon API endpoint that serves blob sidecars
TO_ADDRESSyesBlob address the network's transactions are sent to
INIT_START_SLOTyesGenesis slot, where the state derivation starts on first run. Must be finalized and have an execution block. Use the network's start slot; it must not change once the database has synced
ARCHIVER_URLnoArchiver to read blobs from, e.g. http://localhost:3001. Unset, blobs are read from BEACON_URL, which only serves recent ones
STATE_LOG_DB_URLnoPostgres database holding the state log (default postgres://postgres@localhost:5432/synchronizer)
CONTAINER_DB_PATHnoRocksDB directory for the Merkle trees (default data/synchronizer-db). A rebuildable cache: Postgres holds the authoritative state
HTTP_BINDnoAddress the HTTP API listens on (default 127.0.0.1:3000)
RPC_RETRIESnoHow many times to ask again for a block or blobs a lagging node does not serve yet (default 6)
RPC_RETRY_MSnoDelay in ms between those requests (default 1000)
SYNC_DELAY_MSnoDelay in ms between slots once at chain head (default 173, derived from a 15 req/s RPC rate limit)
CATCHUP_BATCH_SIZEnoSlots fetched concurrently while catching up (default 7, derived from a 15 req/s RPC rate limit)

Once running, curl localhost:3000/sync-progress reports the last processed slot.

Relayer

The relayer accepts transaction proofs from drivers over HTTP and posts them to Ethereum as EIP-4844 blob transactions, signed and paid for by its own wallet.

just relayer

Configure it in services/relayer/.env:

VariableRequiredDescription
RPC_URLyesEthereum execution-layer RPC endpoint
TO_ADDRESSyesBlob address the transactions are sent to
PRIVATE_KEYyesPrivate key of the wallet that signs and pays for the blob transactions. Keep it funded
DB_URLnoPostgres database holding the relay job queue (default postgres://postgres@localhost:5432/relayer)
HTTP_BINDnoAddress the HTTP API listens on (default 127.0.0.1:3200)
MAX_ATTEMPTSnoSubmission attempts per proof before giving up (default 8)
RETRY_INITIAL_SECSnoInitial retry backoff in seconds (default 4)
RETRY_MAX_SECSnoMaximum retry backoff in seconds (default 300)
RECEIPT_POLL_SECSnoInterval between receipt polls in seconds (default 6)
RECEIPT_TIMEOUT_SECSnoTimeout for a submitted transaction's receipt (default: none)
WORKER_IDLE_SLEEP_MSnoWorker sleep when the queue is empty (default 1000)
MAX_FEE_PER_BLOB_GASnoOverride for the blob gas fee cap
FEE_BUMP_AFTER_SECSnoSeconds before replacing a stuck transaction with a higher fee (default: fee bumping disabled)
FEE_BUMP_MULTIPLIER_PCTnoFee increase per bump, in percent (default and minimum 100, i.e. 2x)
FEE_BUMP_MAXnoMaximum replacements per proof (default 5)

Running with Docker Compose

deploy/compose.yaml runs all three services from their published container images, together with a bundled Postgres. All services read their configuration from a single deploy/.env:

cp deploy/.env.example deploy/.env   # fill in the variables below
docker compose -f deploy/compose.yaml up -d

To only verify the network state, without a relayer or a wallet, start just the synchronizer (its archiver comes along):

docker compose -f deploy/compose.yaml up -d synchronizer postgres

The variables to fill in deploy/.env:

VariableRequiredDescription
TO_ADDRESSyesBlob address, shared by all three services (the archiver receives it as its FILTER_ADDRESS)
RPC_URLyesEthereum execution-layer RPC endpoint, used by all three services
BEACON_URLyesBeacon API endpoint that serves blob sidecars, used by the synchronizer and the archiver
INIT_START_SLOTyesStart slot for the synchronizer and the archiver. Use the network's start slot
PRIVATE_KEYrelayerRelayer wallet key; only needed when running the relayer
POSTGRES_PASSWORDnoPassword for the bundled Postgres (default postgres); change it when the host is exposed
IMAGE_TAGnoImage tag to run (default latest); pin a release for reproducible deployments
STATE_LOG_DB_URLnoUse an external Postgres for the synchronizer instead of the bundled one
DB_URLnoUse an external Postgres for the relayer instead of the bundled one
ARCHIVER_URLnoPoint the synchronizer at an external archiver instead of the bundled one
RUST_LOGnoLog filter (default info)

The services listen on 3000 (synchronizer), 3001 (archiver) and 3200 (relayer). The bind addresses, the RocksDB and blob store paths, and the database URLs are set by the compose file, with Docker volumes for their data. See deploy/README.md for hardware requirements and production notes.

Examples

Applications on the Digital Objects Network ship as plugins: .pexe archives the driver loads from ~/.dobj/actions/ at startup. A .pexe is a zip of two files:

  • manifest.toml: the plugin's name and version, its classes (the object types it defines, with emoji and descriptions), its actions, and the module_hash that pins the exact proof logic the script compiles to.
  • plugin.rhai: the action logic as a Rhai script. The SDK compiles it into pod2 predicates, so the same script defines both what an action does and what its zero-knowledge proof must show.

The example applications live in the repo under examples/:

ExampleWhat it isClassesActions
craft-basicsSurvival-crafting starter69
craft-rocketFactory tech tree with a rocket win condition5384
BitcraftResource-production game, shipped as a desktop app with its own driver27134

craft-basics and craft-rocket are installed into a driver with dobj install. Bitcraft is a desktop application that bundles its own driver and plugin; see its page.

Installing a plugin

dobj install takes a local .pexe path or an http(s) URL, validates it, and hot-reloads the running daemon's catalog, so no restart is needed:

# from a published release URL...
~/.dobj/bin/dobj install https://example.com/my-plugin.pexe

# ...or a local file
~/.dobj/bin/dobj install ./my-plugin.pexe

# confirm the new actions are in the catalog
~/.dobj/bin/dobj actions

From source

The pexe CLI builds plugin sources into archives and can install them in one step:

git clone https://github.com/0xPARC/digital-objects-network
cd digital-objects-network

just install-plugins   # build + install every plugin under examples/

# or a single plugin:
cargo run -p pexe --release -- build --install examples/craft-rocket

pexe build also rewrites the module_hash in each manifest to match the hash the compiled script actually produces, so the manifest always matches what the driver will accept.

Warning: an action's proof logic is pinned by its module_hash, and class and action names are part of that identity. Renaming a class or action in a published plugin invalidates every existing object and recorded proof of that type, so treat published names as frozen.

craft-basics

A miniature survival-game loop: discover raw material with verifiable work, refine it, assemble tools, and spend tool durability to mine harder material. Every item is a Digital Object; every craft is a zero-knowledge-proved action.

FindLog (VDF)                            → 🌲 Log
CraftWood (PoW)          🌲 Log          → 🪵 Wood
CraftSticks              🪵 Wood         → 🥢 Stick + 🥢 Stick
CraftWoodPick            🪵 + 🥢         → ⛏️ WoodPick (durability 100)
MineStoneWithWoodPick    ⛏️ wear -1      → 🪨 Stone
CraftStonePick           🪨 + 🥢         → ⛏️ StonePick (durability 200)
MineStoneWithStonePick   ⛏️ wear -1      → 🪨 Stone

Classes

ClassDescription
Log🌲A discovered log that can be refined into wood.
Wood🪵Refined wood used for sticks and basic tools.
Stick🥢A stick used as a handle in tool crafting.
Stone🪨Mined stone used to craft stronger tools.
WoodPick⛏️A wood pick that can mine stone while durability remains.
StonePick⛏️A stone pick that can mine stone while durability remains.

Actions

ActionDescription
FindLogDiscover a log object by proving a short VDF.
CraftWoodRefine one log into a wood object with PoW quality checks.
CraftSticksSplit one wood object into two stick objects.
CraftWoodPickCombine wood and a stick to craft a wood pick.
MineStoneWithWoodPickMine stone using a wood pick (consumes durability).
CraftStonePickCombine stone and a stick to craft a stone pick.
MineStoneWithStonePickMine stone using a stone pick (consumes durability).
UseWoodPick (hidden)Internal durability update and VDF proof for wood pick usage.
UseStonePick (hidden)Internal durability update and VDF proof for stone pick usage.

What it demonstrates

  • VDF-gated discovery — FindLog proves an iterated-hash delay function, so new logs cost real wall-clock time.
  • PoW quality checks — CraftWood grinds a key whose top limb must clear a difficulty target, proved with a 256-bit comparison.
  • Durability you can't fake — using a pick proves the old durability was positive and that the new value is exactly one less.
  • Hidden subactions — the Use*Pick helpers are composed into the mining actions rather than invoked directly.

Install

Install the driver first, then add this plugin. dobj install hot-reloads the catalog, so no restart is needed:

~/.dobj/bin/dobj install https://github.com/0xPARC/digital-objects-network/releases/latest/download/craft-basics.pexe

Or build it from a checkout of the repo, then install the archive:

cargo run -p pexe --release -- build examples/craft-basics   # -> target/pexe/craft-basics.pexe
~/.dobj/bin/dobj install target/pexe/craft-basics.pexe

Source: examples/craft-basics/

craft-rocket

A factory-builder tech tree in the spirit of automation games: mine and farm raw resources, refine them through five tiers of processing, unlock machines and stations, and assemble the final Rocket — the win condition. 53 classes and 84 actions, all proof-backed.

  PoW mines                     VDF farms
  🪨 Iron   🟧 Copper   🛢️ Oil   💧 Water   🌾 Cane   🌿 Hemp   🟡 Sulfur
      │                             │
      ▼  t1 — smelt / mash / refine ▼
  🧱 Ingot   🟫 Plate   🧻 Pulp   🧵 Fiber   🧪 Acid   (+ ⬛ Tar  ⛽ Fuel  💨 Gas)
      │
      ▼  t2 — forge / draw / weave / press
  ⚙️ Steel   ➰ Wire   🧶 Cloth   🪪 Board   🛞 Rubber   🧫 Extract   ⚙ Gear   🌀 Coil
      │
      ▼  t3 — requires 🤖 MachineI
  🔘 Bearing   🔌 Circuit   🪡 Canvas   🟦 Panel   🛠️ Pistons   🧴 Resin
      │
      ▼  t4 — requires 🤖 MachineII
  🚂 Engine   🚀 Casing   📦 Payload
      │
      ▼  t5
  🚀 Rocket — the win condition

Mechanics

  • Machines are level gates. MachineI unlocks tier-3 recipes and station builds; MachineII unlocks tiers 4 and 5. Recipes prove possession of a machine (a hidden Touch* subaction) without consuming it.
  • Stations unlock better recipes. BlastFurnace, CircuitFab, CrackingUnit, and ReactionChamber each enable faster or higher-yield variants of existing recipes.
  • Tools deplete. DrillBit, SolderingIron, and PressureValve carry durability that recipe variants consume — spend tool wear to skip grinding.
  • Recipe variants trade time, risk, and yield. Many products have a Flash variant (PoW-only gamble for throughput), a Crude variant (slower but recovers inputs), and a station-gated deterministic variant alongside the standard recipe.
  • Byproducts loop back. Slag becomes flux for double-yield smelting; tar refines into wax and grease; sludge feeds catalysts and binders.

Machines and stations

ClassDescription
MachineI🤖Tier-1 assembler. Unlocks t3 recipes and station builds.
MachineII🤖Tier-2 assembler. Unlocks t4 and t5 recipes.
BlastFurnace🏭Blast furnace. Enables faster steel and cast gears.
CircuitFab🏭Circuit fabrication station. Produces circuits in bulk.
CrackingUnit🏭Cracking unit. Refinery yields more product per oil.
ReactionChamber🏭Reaction chamber. Stable variants of extract, resin, engine, rocket.

The final assembly

CraftRocket:  🚂 Engine + 🚀 Casing + 📦 Payload + 🧴 Resin x2  →  🚀 Rocket

Or CraftRocketCat — the deterministic variant, run in the reaction chamber with a catalyst.

Install

Install the driver first, then add this plugin. dobj install hot-reloads the catalog, so no restart is needed:

~/.dobj/bin/dobj install https://github.com/0xPARC/digital-objects-network/releases/latest/download/craft-rocket.pexe

Or build it from a checkout of the repo, then install the archive:

cargo run -p pexe --release -- build examples/craft-rocket   # -> target/pexe/craft-rocket.pexe
~/.dobj/bin/dobj install target/pexe/craft-rocket.pexe

Source: examples/craft-rocket/

Bitcraft

Bitcraft is a resource-production game built on the Digital Objects Network. Every resource, tool, and production facility in it is a Digital Object, and every gather, craft, mine, and upgrade is a proved action. 27 classes and 134 actions.

Unlike the other examples, Bitcraft ships as a desktop application rather than a plugin alone. The app bundles its own driver daemon, the bitcraft plugin, and the game UI, so it does not need a separate driver install.

Installing

Installers are attached to each release:

PlatformInstaller
macOS (Apple Silicon)bitcraft-<version>-aarch64-apple-darwin.dmg
macOS (Intel)bitcraft-<version>-x86_64-apple-darwin.dmg
Windows (x86_64)bitcraft-<version>-x86_64-pc-windows-msvc.exe
Linux (x86_64)bitcraft-<version>-x86_64-unknown-linux-gnu.AppImage

The app keeps its objects, settings, and installed plugin in ~/.bitcraft/, separate from a standalone driver's ~/.dobj/, and runs its daemon on 127.0.0.1:7727. It uses the same hosted synchronizer and relayer as the standalone driver. On first launch it builds its ZK circuits and creates a starter pocketknife.

Gameplay

  • A pocketknife is created from no inputs and gathers wood, stone, and iron slowly, with a small proof of work per gather (about 15, 30, and 60 seconds on a modern CPU).
  • Sticks are made from wood. Axes (wood, then stone, then iron) chop 2, 3, or 4 wood per use and break after 100 uses.
  • Three production facilities can each be built and then upgraded from level 1 to level 10, with costs doubling at every level:
    • a quarry mines stone, more per action at higher levels;
    • a mining drill mines iron, coal, and geodes, consuming 5 wood per action;
    • a pumpjack pumps oil into a barrel for 5 coal. The player chooses the number of steps, and each step is serial (non-parallelizable) work.
  • A geode cracks open into one of six gems: quartz, amethyst, sapphire, emerald, ruby, or diamond.
  • Buckets hold a count of one resource, and a barrel holds oil. Facility upgrades spend buckets rather than individual objects.
  • Bits are the game's currency. Wood, stone, and iron convert to 1, 2, and 5 bits; gems convert into a bucket of bits, from 100 for a quartz to 320,000,000 for a diamond.

Classes

GroupClasses
ResourcesWood, Stone, Iron, Coal, Stick, Geode
GemsQuartz, Amethyst, Sapphire, Emerald, Ruby, Diamond
ContainersBucketOfWood, BucketOfStone, BucketOfIron, BucketOfCoal, BucketOfQuartz, BucketOfBits, BarrelOfOil
ToolsPocketknife, WoodAxe, StoneAxe, IronAxe
FacilitiesQuarry, MiningDrill, Pumpjack
CurrencyBit

What it demonstrates

  • Proof of work bound to the tool. Every proof-of-work action grinds the nonce of the tool it uses, after every other update to that tool, so the work bounds the tool's new commitment. A tool's first nonce is the grounding block's hash. Work toward a tool's next use can only start once its previous use is mined, and a nonce mined for one tool state proves nothing for any other, so resources cannot be premined.
  • Randomness from the work itself. A geode records the drill's new commitment, and cracking it compares that value against the geode's gem thresholds, so which gem a geode holds is decided by the mining work.
  • Serial work. Each pumpjack step is a computation that cannot be parallelized, so oil output cannot be sped up by adding cores.
  • Quantities in one object. Buckets and barrels carry a count, so a recipe that needs hundreds of a resource spends one object rather than hundreds.

Agents

The app installs bitcraft-dobj and bitcraft-dobj-mcp-proxy, wrappers around the CLI and the MCP proxy that connect to the running app's daemon. The game's toolbar can open Claude or Codex with the Bitcraft data folder selected and a player prompt filled in. For agents without shell access, enable MCP with bitcraft-dobj settings set --mcp on and connect the agent to http://127.0.0.1:7728/mcp.

Source

  • examples/bitcraft/: the plugin. plugin.rhai and manifest.toml are generated by src/main.rs.
  • apps/bitcraft/: the desktop app (Tauri host and game UI). Its README covers building and running it from a checkout, starting with just bitcraft-dev.