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:
| Concept | Implementation |
|---|---|
| global state derivation | permissionlessly operable synchronizer |
| global bulletin board | Ethereum blobspace |
| Class-Action SDK | pexe/Rhai SDK |
| Class-Action protocol | podlang txlib |
| private statement proving system | POD2 |
POD2 is itself layered:
| Concept | Implementation |
|---|---|
| predicate language | podlang |
| statement derivation kernel | MainPod plonky2 circuit |
| zk proving system | plonky2 |
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/:
| Binary | What it is |
|---|---|
dobjd | Driver daemon with bundled browser UI (REST/SSE on :7717, optional MCP on :7718) |
dobj | Terminal CLI |
dobj-mcp-proxy | Stdio bridge for agents that only speak stdio (e.g. Claude Desktop) |
pexe | Packaging 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:
- craft-basics, a small crafting demo.
- craft-rocket, a larger factory tech tree.
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/binto 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
| Parameter | Value |
|---|---|
| Network | Sepolia (Ethereum testnet) |
| Start slot | 11491968 — the beacon slot the network began ingesting blobs from; changes with each testnet reset |
| Blob address | 0x4343434343434343434343434343434343434343 — 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.
| Service | Endpoint | Role |
|---|---|---|
| Synchronizer | synchronizer.digiobjects.net | Rebuilds canonical network state from Ethereum blobs; answers live / consumed queries |
| Relayer | relayer.digiobjects.net | Submits proof payloads to Ethereum as EIP-4844 blob transactions |
| Archiver | terminus.digiobjects.net:4001 | Stores 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:
| Variable | Required | Description |
|---|---|---|
RPC_URL | yes | Ethereum execution-layer RPC endpoint |
BEACON_URL | yes | Beacon API endpoint that serves blob sidecars |
FILTER_ADDRESS | yes | Blob address; only blobs sent to it are archived |
INIT_START_SLOT | yes | Slot to start archiving from on first run; afterwards it resumes from its stored progress. At or before the network's start slot |
BLOBS_PATH | yes | Directory where the blobs are stored (no database). Grows by ~128 KB per archived blob |
HTTP_BIND | no | Address 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:
| Variable | Required | Description |
|---|---|---|
RPC_URL | yes | Ethereum execution-layer RPC endpoint |
BEACON_URL | yes | Beacon API endpoint that serves blob sidecars |
TO_ADDRESS | yes | Blob address the network's transactions are sent to |
INIT_START_SLOT | yes | Genesis 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_URL | no | Archiver to read blobs from, e.g. http://localhost:3001. Unset, blobs are read from BEACON_URL, which only serves recent ones |
STATE_LOG_DB_URL | no | Postgres database holding the state log (default postgres://postgres@localhost:5432/synchronizer) |
CONTAINER_DB_PATH | no | RocksDB directory for the Merkle trees (default data/synchronizer-db). A rebuildable cache: Postgres holds the authoritative state |
HTTP_BIND | no | Address the HTTP API listens on (default 127.0.0.1:3000) |
RPC_RETRIES | no | How many times to ask again for a block or blobs a lagging node does not serve yet (default 6) |
RPC_RETRY_MS | no | Delay in ms between those requests (default 1000) |
SYNC_DELAY_MS | no | Delay in ms between slots once at chain head (default 173, derived from a 15 req/s RPC rate limit) |
CATCHUP_BATCH_SIZE | no | Slots 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:
| Variable | Required | Description |
|---|---|---|
RPC_URL | yes | Ethereum execution-layer RPC endpoint |
TO_ADDRESS | yes | Blob address the transactions are sent to |
PRIVATE_KEY | yes | Private key of the wallet that signs and pays for the blob transactions. Keep it funded |
DB_URL | no | Postgres database holding the relay job queue (default postgres://postgres@localhost:5432/relayer) |
HTTP_BIND | no | Address the HTTP API listens on (default 127.0.0.1:3200) |
MAX_ATTEMPTS | no | Submission attempts per proof before giving up (default 8) |
RETRY_INITIAL_SECS | no | Initial retry backoff in seconds (default 4) |
RETRY_MAX_SECS | no | Maximum retry backoff in seconds (default 300) |
RECEIPT_POLL_SECS | no | Interval between receipt polls in seconds (default 6) |
RECEIPT_TIMEOUT_SECS | no | Timeout for a submitted transaction's receipt (default: none) |
WORKER_IDLE_SLEEP_MS | no | Worker sleep when the queue is empty (default 1000) |
MAX_FEE_PER_BLOB_GAS | no | Override for the blob gas fee cap |
FEE_BUMP_AFTER_SECS | no | Seconds before replacing a stuck transaction with a higher fee (default: fee bumping disabled) |
FEE_BUMP_MULTIPLIER_PCT | no | Fee increase per bump, in percent (default and minimum 100, i.e. 2x) |
FEE_BUMP_MAX | no | Maximum 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:
| Variable | Required | Description |
|---|---|---|
TO_ADDRESS | yes | Blob address, shared by all three services (the archiver receives it as its FILTER_ADDRESS) |
RPC_URL | yes | Ethereum execution-layer RPC endpoint, used by all three services |
BEACON_URL | yes | Beacon API endpoint that serves blob sidecars, used by the synchronizer and the archiver |
INIT_START_SLOT | yes | Start slot for the synchronizer and the archiver. Use the network's start slot |
PRIVATE_KEY | relayer | Relayer wallet key; only needed when running the relayer |
POSTGRES_PASSWORD | no | Password for the bundled Postgres (default postgres); change it when the host is exposed |
IMAGE_TAG | no | Image tag to run (default latest); pin a release for reproducible deployments |
STATE_LOG_DB_URL | no | Use an external Postgres for the synchronizer instead of the bundled one |
DB_URL | no | Use an external Postgres for the relayer instead of the bundled one |
ARCHIVER_URL | no | Point the synchronizer at an external archiver instead of the bundled one |
RUST_LOG | no | Log 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 themodule_hashthat 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/:
| Example | What it is | Classes | Actions |
|---|---|---|---|
| craft-basics | Survival-crafting starter | 6 | 9 |
| craft-rocket | Factory tech tree with a rocket win condition | 53 | 84 |
| Bitcraft | Resource-production game, shipped as a desktop app with its own driver | 27 | 134 |
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
| Class | Description | |
|---|---|---|
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
| Action | Description |
|---|---|
FindLog | Discover a log object by proving a short VDF. |
CraftWood | Refine one log into a wood object with PoW quality checks. |
CraftSticks | Split one wood object into two stick objects. |
CraftWoodPick | Combine wood and a stick to craft a wood pick. |
MineStoneWithWoodPick | Mine stone using a wood pick (consumes durability). |
CraftStonePick | Combine stone and a stick to craft a stone pick. |
MineStoneWithStonePick | Mine 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 —
FindLogproves an iterated-hash delay function, so new logs cost real wall-clock time. - PoW quality checks —
CraftWoodgrinds 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*Pickhelpers 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.
MachineIunlocks tier-3 recipes and station builds;MachineIIunlocks tiers 4 and 5. Recipes prove possession of a machine (a hiddenTouch*subaction) without consuming it. - Stations unlock better recipes.
BlastFurnace,CircuitFab,CrackingUnit, andReactionChambereach enable faster or higher-yield variants of existing recipes. - Tools deplete.
DrillBit,SolderingIron, andPressureValvecarry durability that recipe variants consume — spend tool wear to skip grinding. - Recipe variants trade time, risk, and yield. Many products have a
Flashvariant (PoW-only gamble for throughput), aCrudevariant (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
| Class | Description | |
|---|---|---|
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:
| Platform | Installer |
|---|---|
| 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
| Group | Classes |
|---|---|
| Resources | Wood, Stone, Iron, Coal, Stick, Geode |
| Gems | Quartz, Amethyst, Sapphire, Emerald, Ruby, Diamond |
| Containers | BucketOfWood, BucketOfStone, BucketOfIron, BucketOfCoal, BucketOfQuartz, BucketOfBits, BarrelOfOil |
| Tools | Pocketknife, WoodAxe, StoneAxe, IronAxe |
| Facilities | Quarry, MiningDrill, Pumpjack |
| Currency | Bit |
What it demonstrates
- Proof of work bound to the tool. Every proof-of-work action grinds the
nonceof 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.rhaiandmanifest.tomlare generated bysrc/main.rs.apps/bitcraft/: the desktop app (Tauri host and game UI). Its README covers building and running it from a checkout, starting withjust bitcraft-dev.