Status: Working design document — target architecture for the full DIN stack (DevNet → testnet)
Owner: Umer
Scope: How every DIN component — on-chain contracts, indexer, SDK, CLI, daemon, IPFS layer, and the on-device node/worker pair — fits together; which parts exist today and which are planned.
Roadmap anchors: P4 (SDK extraction, dind daemon, DIN Indexer); DIN-DAO deferred to post-mainnet (2026-08-04 — see DESIGN_DECISIONS.md DD-3), off-chain governance until then.
The wiki pages under DIN Components describe each component individually; this document is the one place that shows the whole system and the dependency order between the parts.
DIN is organized as six layers. Everything above the chain exists to make participating in on-chain coordination practical: reading state efficiently, submitting transactions safely, moving artifacts through IPFS, and running untrusted model-owner code without exposing keys or data.
| Layer | Components | Status |
|---|---|---|
| Governance | DIN-DAO contracts (Multisig, Timelock, Governance staking, Governor, Guardian) | ⏳ Deferred (post-mainnet; off-chain team/forum governance until then) |
| On-chain coordination | Platform contracts: DinCoordinator, DinToken, DinValidatorStake, DinModelRegistry |
✅ Deployed (Optimism Sepolia) |
Task contracts (per model): DINTaskCoordinator, DINTaskAuditor |
✅ Deployed per model | |
| Read layer | DIN Indexer (subgraph or lighter equivalent) | 📋 Planned (P4) |
| Library | DIN SDK (dincli/sdk/): contract interfaces, wallet/tx helpers, IPFS client, manifest loading |
📋 Planned (P4, extracted from dincli) |
| Applications | dincli (interactive CLI) |
✅ Exists |
dind daemon (always-on automation + personalized preference config) |
📋 Planned (P4) | |
| Storage | IPFS layer (own node / Filebase / custom provider) | ✅ Exists |
| Device runtime | din-node (trusted control plane container) + Worker Node (sandboxed job container) |
✅ Exists |
🔗 View as a live, zoomable diagram → (mermaidviewer.com — same source as below)
flowchart TB
subgraph chain["⛓ Blockchain — Optimism"]
DAO["DIN-DAO ⏳<br/>Multisig · Timelock · Governor · Guardian<br/>(deferred, post-mainnet)"]
subgraph platform["Platform contracts (deployed once)"]
COORD["DinCoordinator"]
TOKEN["DinToken"]
STAKE["DinValidatorStake"]
REG["DinModelRegistry"]
end
subgraph taskc["Task contracts (deployed per model)"]
TC["DINTaskCoordinator"]
TA["DINTaskAuditor"]
end
DAO -. "governs (replaces DIN-Representative key)" .-> platform
COORD -- "authorizes as slashers on DinValidatorStake" --> taskc
REG -- "model registered only after slasher auth" --> taskc
end
IDX["🔎 DIN Indexer 📋<br/>event → queryable entities<br/>(filtering, pagination, history, joins)"]
chain -- "contract events" --> IDX
IPFS[("🗂 IPFS layer<br/>service code · manifests ·<br/>model weights · ABIs")]
subgraph device["💻 User device (Docker host)"]
subgraph node["din-node — trusted control plane"]
DIND["dind daemon 📋<br/>event-driven automation"]
CLI["dincli<br/>interactive CLI"]
SDK["DIN SDK 📋<br/>contracts · wallet/tx · IPFS · manifest"]
CFG[("daemon config<br/>preferences: domain, risk,<br/>rewards, privacy")]
DIND --> SDK
CLI --> SDK
DIND --> CFG
end
WORKER["worker node<br/>ephemeral · sandboxed ·<br/>no network · no keys"]
node -- "spawns sibling container<br/>(host Docker socket)" --> WORKER
end
SDK -- "transactions + live reads (RPC)" --> chain
SDK -- "indexed queries" --> IDX
SDK -- "upload / fetch artifacts" --> IPFS
WORKER -- "job inputs (read-only) /<br/>outputs via mounted dirs only" --- node
📋 = planned component; ⏳ = deferred to post-mainnet; everything else exists on develop today.
Today the DIN-Representative admin key controls platform parameters (fees, slasher authorization, model registration approval, blacklisting, treasury withdrawal). The original plan was for the DIN-DAO to replace that single key with governance contracts — Multisig, Timelock, governance staking (locked non-transferable DIN), Governor, and a Guardian emergency path — rolled out in stages (Multisig shadowing from devnet 2.0, Timelock ownership from devnet 3.0, full Governor voting on testnet).
As of 2026-08-04, that staged rollout is deferred to post-mainnet. Abraham's decision (see DESIGN_DECISIONS.md DD-3): DIN follows Ethereum's off-chain governance model until well past testing — team coordination and public discussion (forums), no DinMultisig sitting between the community and protocol upgrades, no immutable signer set locked in before there's real demand or a legitimate selection process. Any near-term multisig is a plain Gnosis Safe scoped to treasury/fund management only, never the protocol-role authority (PROPOSER_ROLE/CANCELLER_ROLE) this section originally described. On-chain governance gets progressively revisited post-mainnet. Near-term contract work keeps using plain owner-controlled setters, as it already does — there is no Timelock to eventually govern them, so this is no longer a staging step toward one, just how the contracts stay.
The protocol backbone, deployed once per network by the DIN-Representative:
DinCoordinator— ETH↔DIN exchange and the slasher registry (authorizes task contracts to slash onDinValidatorStake).DinToken— ERC-20, minted on ETH deposit.DinValidatorStake— validator staking, unbonding, blacklist, slashing entry point.DinModelRegistry— model registration (open-source vs. proprietary fee tiers), manifest update approval.
Each model owner deploys a DINTaskCoordinator + DINTaskAuditor pair that runs that model's Global Iterations: role registration, local model submission, auditor scoring, two-tier aggregation, and slashing of non-participating validators. The pair must be authorized as slashers via DinCoordinator before the model can be registered in DinModelRegistry — this is the trust link between the task layer and the platform layer.
An off-chain read layer (Graph Protocol subgraph or a lighter equivalent) that consumes events from the platform and task contracts and turns them into queryable entities. It exists to kill the current pattern of RPC counter-enumeration loops in the CLI: filtering, sorting, pagination, historical views, and cross-contract joins belong off-chain, keeping the contracts minimal and event-focused. Consumers: the SDK (and through it the CLI), the daemon's event-driven job triggers, and future dashboards. Build order is deliberate — the indexer follows contract-stability milestones so mappings aren't rebuilt against moving targets.
The extraction of dincli's reusable core into a library layer (dincli/sdk/) any Python program can import: typed contract interfaces (platform + task), wallet/keystore and transaction-building helpers, the IPFS client abstraction, and manifest loading/CID resolution. The SDK is the only component that talks to the outside world — chain RPC for transactions and live reads, the indexer for bulk/historical queries, IPFS for artifacts. Everything above it (CLI, daemon, third-party tools) composes these primitives instead of reimplementing them.
dincli(exists) — the interactive Typer CLI with one sub-app per role (model-owner,client,auditor,aggregator,dinrep, …). Today it contains the functionality the SDK will extract; post-extraction its commands keep working unchanged as thin wrappers.dind(planned) — the always-on daemon that automates participation: it watches on-chain events (via the indexer), decides which jobs to take based on a personalized local configuration (preferences on domain, risk tolerance, expected rewards, privacy constraints), orchestrates sandboxed Worker Node jobs with resource awareness and failure recovery, and persists execution state across restarts. Ships withstart/stop/status, health endpoints, and structured logging. CLI and daemon coexist and share preferences/state through the SDK layer.
All CID-addressed artifacts — model-owner service code (model.py, client.py, auditor.py, aggregator.py, modelowner.py), manifests, model weights, ABIs — flow through one abstraction (dincli/services/ipfs.py, future sdk) with three interchangeable backends: an env-configured IPFS node, Filebase, or a fully custom Python provider. Only CIDs go on-chain; content lives here. Per-directory CID caches ensure artifacts are re-fetched only when their CID changes.
The on-device split enforces the protocol's core security boundary:
din-node(trusted control plane) — the containerized run path for validators. It holds the wallet, config, and state (singleDIN_STATE_DIR, bind-mounted at the same absolute path inside and outside the container), talks to chain and IPFS, and decides what to submit. Today its main process is idle and operators drive it viadocker compose exec dincli …; with P4,dindbecomes the main process.- Worker Node (untrusted execution) — an ephemeral
docker run --rmcontainer that executes model-owner code (training, scoring, aggregation) treated as hostile:--network none, no wallet/config/Docker-socket access, CPU/memory caps, read-only job inputs, outputs written to designated directories only. Workers are spawned as siblings on the host Docker daemon (din-node mounts the host Docker socket), which is why the shared state path must match on both sides.
┌──────────────────────────── User device (Docker host) ────────────────────────────┐
│ │
│ ┌───────────── din-node (trusted) ─────────────┐ ┌── worker node (untrusted) ─┐│
│ │ dind 📋 ──► DIN SDK 📋 ◄── dincli │ │ model-owner code: ││
│ │ │ │ │ │ train / score / aggregate ││
│ │ prefs & wallet · tx · IPFS · manifest │ │ ││
│ │ daemon ──────────────────────────── │ │ --network none ││
│ │ config state in DIN_STATE_DIR │ │ no keys · no socket ││
│ └──────┬──────────────┬────────────────────────┘ │ cpu/mem caps · --rm ││
│ │ │ └──────────▲─────────────────┘│
│ │ │ host Docker socket │ sibling spawn │
│ │ └───────────────────────────────────────┘ │
│ │ job dirs: inputs read-only ──► worker ──► outputs to designated dirs │
└─────────┼─────────────────────────────────────────────────────────────────────────┘
│
▼ (only din-node talks to the outside)
chain RPC · DIN Indexer 📋 · IPFS
Write path (transactions). CLI command or daemon decision → SDK builds and signs the tx (keystore in DIN_STATE_DIR) → chain RPC → platform or task contract. Workers never sign or submit anything.
Read path. Live, single-item state (current GI phase, a specific stake) → direct RPC. Bulk, filtered, or historical queries (all models in a domain, a validator's slash history) → indexer. Until the indexer exists, everything goes over RPC — that inefficiency is the indexer's motivation, not a design choice.
Artifact path. Model owner uploads service files + manifest → IPFS → CIDs referenced on-chain/in-manifest → SDK on a participant device fetches by CID (cache-checked) → daemon mounts them read-only into a worker → worker writes results → SDK uploads outputs to IPFS → CID submitted on-chain.
Automation path (target state). Contract event (phase opened, batch assigned) → indexer → dind matches it against the operator's preference config → spawns a worker for the job → collects the output → submits via SDK → records state for crash recovery.
The build sequence follows the arrows in the diagram, bottom-up on the off-chain side:
- Contracts stabilize first — indexer mappings and SDK contract interfaces are built against stable events/ABIs, not moving targets.
- SDK before daemon —
dindimports the SDK; extracting the SDK while the CLI keeps working is the low-risk first step of P4. - Indexer before event-driven automation —
dindcan launch in a degraded RPC-polling mode, but its preference-driven job selection assumes indexer queries. - DAO deferred, not sequenced into P4 at all — governance would wrap the platform contracts' existing owner surface (changing who calls the setters, not the setters themselves), but per §3.1 it's now post-mainnet work, not a P4 build step.
- Wiki: Platform Contracts · Task Contracts · DIN Indexer · DIN SDK · DIN Daemon · DIN DAO · DIN Node · Worker Node · IPFS Layer
- Current-state docs:
Documentation/public/workflows/din-workflow.md,Documentation/public/workflows/model-workflow.md,Documentation/public/guides/ipfs.md - Related design:
MECHANISM_DESIGN.md(staking/slashing/rewards/fees for DevNet 2.0)