ID: task_240626_0a
Assigned to: Santiago Rodriguez Cetran
Created: 2026-06-24
Status: Closed
Roadmap ref: P3 — Devnet Operator Baseline (validator-operations)
Target: dincli/docker/node/
PR: #12 (Santiagocetran:feature/din-node-container) — MERGED
DIN (InfiniteZero) is a federated-learning network coordinated by Solidity contracts on Optimism Sepolia. Participants run dincli, a Python/Typer CLI, to act as clients (local training), auditors (scoring), or aggregators (combining models). Raw training data never leaves a participant's machine — only model artifacts (pinned to IPFS) and on-chain coordination state are shared.
Each model owner supplies their own Python service code (training/scoring/aggregation logic), fetched from IPFS and executed locally by whoever is running dincli. Because that service code is supplied by a third party (the model owner), it is treated as untrusted — it must not get access to wallets, private keys, or the host filesystem beyond what a specific job needs.
The team has already built the untrusted-execution piece:
dincli/cli/worker.py— builds/runs a Docker worker image (din-worker:dev), installs manifest-pinnedrequirements.txtper role into a shared package cache, writes a structured job file, runs the worker container with--network none, CPU/memory limits, read-only mounts (only specific subdirectories are writable), and reads back a structuredresult.json.dincli/docker/worker/— the worker image itself: a minimal Python entrypoint that loads the model owner's function and executes it inside the container.dincli/cli/client.py,auditor.py,aggregator.py— all three roles (train-lms,lms-evaluation evaluate,aggregate-t1,aggregate-t2) already call into this worker layer to run untrusted training/scoring/aggregation code in isolated containers.
So: per-job sandboxing already exists and works. What's missing is the operator side — there is no packaged way to run dincli itself as a long-lived service with a sane start/stop/upgrade/log/volume story. Today, dincli runs preferably on host. However. based on feedback from other advisers/contributers, the best way to go is running everything in docker. Currently ~/.config/dincli / ~/.cache/dincli are managed by throgh dincli commands, but we want to move that into docker too.
Relevant docs (heads up — these are working drafts, not specs; they're known to be incomplete and in places inconsistent with the actual code, so treat the code as the source of truth and call out doc/reality mismatches you find):
Developer/issues/containerization.mdDeveloper/issues/validator-operations.mdDeveloper/CONTRIBUTING.md,Developer/DEVELOPMENT_SETUP.md
This work maps to Phase 1: Devnet Operator Baseline of the validator-operations roadmap item — the near-term goal of giving operators (clients/auditors/aggregators today, validators later) a real containerized run path instead of an ad-hoc local Python install. It's also a prerequisite for later DIN daemon (dind) work described in the P4 roadmap, which assumes a persistent host process exists to manage.
Build a din-node main container — a Docker image with dincli installed — plus an operator-facing run path around it (start/stop/upgrade/logs/volume cleanup), so a participant can run dincli as a containerized host process instead of installing Python locally.
dincli already spawns short-lived worker containers (din-worker:dev) per job for untrusted client/auditor/aggregator service code (see §2). The din-node container you're building is the trusted host process — it holds config/wallet state and issues docker run commands for those worker containers as siblings, the same way dincli does today when run directly on a host. To make that work from inside a container, din-node needs access to a Docker daemon: mount the host's /var/run/docker.sock into the din-node container and install the docker CLI in its image. Worker containers it spawns will then run as siblings on the host's Docker daemon, not nested inside din-node. This is a deliberate simplification for devnet — document it as a known tradeoff (the din-node container effectively gets root-equivalent access to the host Docker daemon), not something to "solve" in this trial.
-
din-nodeDocker image (e.g.dincli/docker/node/Dockerfile)- Installs
dincli(editable or built install — your call, document which and why). - Installs the
dockerCLI (just the client binary, not a full daemon) so it can shell out todocker run/docker buildexactly likedincli/cli/worker.pydoes today. - Non-root default user where practical (note any place you had to deviate, e.g. Docker socket group access).
- Pin Python and
dincliversions.
- Installs
-
Persistent volume layout —
dinclialready usesplatformdirsfor three directories (seedincli/cli/utils.py):CONFIG_DIR(wallet/config),CACHE_DIR(manifests/models/jobs),DOCKER_CACHE_DIR(worker package installs). Map these to named volumes or host bind mounts in your Compose file and document the mapping explicitly — this is the most important part of the deliverable, since getting it wrong either breaks persistence across upgrades or leaks state into the wrong place. -
Docker Compose file with:
- The
din-nodeservice, with the Docker socket mounted (/var/run/docker.sock:/var/run/docker.sock). - Named volumes (or documented bind mounts) for config/cache/docker-cache.
- One-command start, stop, upgrade flows — these can just be documented
docker composeinvocations (e.g.docker compose up -d,docker compose down,docker compose pull && docker compose up -d --build), they don't need custom scripting unless you find scripting genuinely clearer. - Documented log access (
docker compose logs -f din-node). - Documented volume cleanup (what's safe to delete, what isn't — e.g. wallet/config should survive an upgrade, worker package cache can be safely wiped to reclaim disk).
- The
-
A short operator runbook (markdown, lives under
Developer/or wherever you think fits — your call, just say why) covering:- First-time setup (build/pull image, first
dincli system configure-network/ wallet setup inside the container). - Start/stop/upgrade/logs.
- What state lives where and what survives an upgrade vs. a full teardown.
- The Docker-socket tradeoff called out explicitly, with what it means for anyone deploying this (don't run
din-nodeon a host you don't trust the operator of).
- First-time setup (build/pull image, first
-
Doc correction pass — while building this, you'll be reading
Developer/issues/containerization.mdandDeveloper/issues/validator-operations.mdclosely. Where you find concrete inaccuracies (e.g. references to mechanisms that don't match current code, or Phase 1 items that turn out to be already done elsewhere), call them out as a short list in your PR description or a small doc patch — whichever is less effort. We'd rather have a short accurate note than a large doc rewrite.
- No changes to the worker container layer (
dincli/cli/worker.py,dincli/docker/worker/) — that part already works. - No Solidity/contract changes.
- No
/healthHTTP endpoint,systemd/launchdunits, or vault/remote-signer integration — those are later phases. - No new automated tests are required for this task (it's primarily an image/Compose/docs deliverable), but if you add any small Python helper code (e.g. an entrypoint script) and it's easy to cover, a couple of tests are welcome — not required.
# Fork https://github.com/InfiniteZeroFoundation/DevNet and clone your fork
git checkout develop
git pull
git checkout -b feature/din-node-container
# Local (non-container) baseline, to understand current behavior first
pip install -e .
dincli --helpRead, in this order, before writing any Docker/Compose code:
Developer/issues/containerization.md(Phase 1 + Docker Packaging Requirements sections)Developer/issues/validator-operations.md(Blocking Requirements §1 + Phase 1)dincli/cli/worker.pyanddincli/docker/worker/Dockerfile— this is the pattern to follow for the newdin-nodeimage.dincli/cli/utils.py—CONFIG_DIR,CACHE_DIR,DOCKER_CACHE_DIRdefinitions.
You'll need Docker installed locally to test the din-node image and Compose file end-to-end, including spawning a worker container from inside din-node (verify the docker.sock mount actually lets you do this — that's the core risk in this task).
-
din-nodeDockerfile, builds cleanly —dincli/docker/node/Dockerfile - Docker Compose file wiring
din-node+ docker.sock + volumes —dincli/docker/node/docker-compose.yml - Verified: from inside the running
din-nodecontainer, adinclicommand that spawns a worker container actually launches a sibling container on the host Docker daemon - Volume layout documented (what maps to
CONFIG_DIR/CACHE_DIR/DOCKER_CACHE_DIR, what's safe to wipe) —dincli/docker/node/README.md - Start/stop/upgrade/logs documented as
docker composecommands —dincli/docker/node/README.md - Operator runbook (markdown) including the docker.sock tradeoff —
dincli/docker/node/README.md - Short list of doc inaccuracies found in
containerization.md/validator-operations.md, with a fix if it's a one-liner
When done:
- Push your feature branch to your fork.
- Open a pull request from your fork's
feature/din-node-containerbranch intodevelopon https://github.com/InfiniteZeroFoundation/DevNet. - PR description: what you built, the docker.sock tradeoff and why you went with it, anything you'd do differently with more time, and the doc-inaccuracy notes from §6.
- Send us the PR link (or the repo + branch link if you'd rather not open the PR until we've reviewed) once it's up.
If anything in this task is ambiguous or you think a different approach is better, say so in the PR or reach out first — we'd rather you push back on a bad assumption here than silently work around it.