Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Keep the Docker build context small and free of host-only state.
.git
.venv*
**/__pycache__
**/*.pyc
*.egg-info
dincli/*.egg-info
dincli.egg-info
# .env at any depth (e.g. dincli/docker/node/.env) — never bake host config or
# secrets into the image. Keep .env.example out too; it isn't needed at build.
.env
.env.*
**/.env
**/.env.*
tasks/
scripts/
bashscripts/
changes/
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ tasks/local/0x4657105FC932625CD289107aAE7B2174a822b709/services/__pycache__/*
/dincli/README.md
foundry/.github/*

# local virtualenvs (e.g. baseline testing)
.venv*/

6 changes: 6 additions & 0 deletions Developer/issues/containerization.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ Suggested worker mounts:
| `~/.cache/dincli/<network>/model_<model_id>/worker/gi-<n>/output` | `/din/output` | read-write | Worker result JSON and any future local output bundle |
| `~/.cache/dincli/<network>/model_<model_id>/worker/gi-<n>/logs` | `/din/logs` | read-write | Worker logs and diagnostics |

> **Note:** In implementation, actual mounts differ from the design table above — see `run_worker_container` in `dincli/cli/worker.py:138-159`. Current mounts: `/din/model` (ro), `/din/model/<subdir>` (rw overlay), `/din/job/job.json` (ro), `/din/output` (rw), `/din/packages` (ro).

Do not mount:

- `~/.config/dincli` directly into the untrusted worker
Expand Down Expand Up @@ -337,6 +339,8 @@ These costs should be documented clearly so node operators can choose appropriat
- Document local Docker requirements and minimum resource ceilings.
- Add Docker Compose examples for start, stop, upgrade, and log collection.

> **Status (2026-06):** Implemented — see `dincli/cli/worker.py` and `dincli/docker/worker/`. This phase is done.

### Phase 2: Role Coverage

- Extend the worker flow to aggregator services.
Expand All @@ -345,6 +349,8 @@ These costs should be documented clearly so node operators can choose appropriat
- Add structured job input and output schemas.
- Add logs and error reporting that are usable from CLI and daemon flows.

> **Status (2026-06):** Already covered — `client.py`, `auditor.py`, and `aggregator.py` (t1/t2) all invoke `run_worker_container`.

### Phase 3: Manifest Policy

- Add manifest fields for runtime requirements and limits.
Expand Down
6 changes: 3 additions & 3 deletions Developer/issues/validator-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ DIN is still early, and the current CLI/devnet path is intentionally lightweight

Current gaps include:

- no standard Docker Compose run path for validators
- no standard Docker Compose run path for validators — **Addressed for devnet by `din-node`; see `dincli/docker/node/` (PR #12).**
- no documented one-command start, stop, and upgrade flow
- no clear production key-management guidance beyond local wallet configuration
- no published resource ceilings per role
Expand Down Expand Up @@ -208,8 +208,8 @@ The goal is to avoid asking early validators to join an empty or opaque network.

### Phase 1: Devnet Operator Baseline

- Add Docker Compose for local validator operation.
- Document start, stop, upgrade, logs, and volume cleanup.
- Add Docker Compose for local validator operation. — **Status (2026-06):** Addressed for devnet by `din-node` — see `dincli/docker/node/` (PR #12).
- Document start, stop, upgrade, logs, and volume cleanup. — **Status (2026-06):** Covered by `dincli/docker/node/README.md`.
- Replace plaintext private-key guidance with passphrase-protected keystore guidance.
- Publish initial resource guidance for aggregator and auditor roles.
- Add a basic slashing and failure-mode document.
Expand Down
19 changes: 19 additions & 0 deletions dincli/docker/node/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copy to .env and adjust for your host: cp .env.example .env
# Comments are on their own lines on purpose: some .env parsers fold a trailing
# `KEY=value # comment` into the value, which would break group_add / paths.

# Absolute host path holding ALL persistent dincli state. MUST be absolute (it
# is bind-mounted at the identical path inside the container). Create it and
# make it writable by DIN_UID/DIN_GID before first start:
# mkdir -p "$DIN_STATE_DIR" && sudo chown -R "$DIN_UID:$DIN_GID" "$DIN_STATE_DIR"
DIN_STATE_DIR=/home/youruser/.din-node

# Host docker socket group id — grants din-node access to the host daemon.
# Find it with: stat -c '%g' /var/run/docker.sock
DOCKER_GID=983

# Run din-node (and the worker containers it spawns) as this host uid/gid so
# files written into DIN_STATE_DIR stay owned by your host user.
# Find with: id -u and id -g
DIN_UID=1000
DIN_GID=1000
79 changes: 79 additions & 0 deletions dincli/docker/node/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# din-node — the trusted DIN host process, containerized.
#
# This image packages `dincli` (the trusted control plane that holds wallet /
# network config and decides what to submit on-chain) together with the Docker
# *client* binary, so that from inside this container `dincli` can shell out to
# `docker run` exactly like it does on a bare host today (see
# dincli/cli/worker.py). The worker containers it spawns run as SIBLINGS on the
# host Docker daemon via the mounted /var/run/docker.sock — NOT nested inside
# din-node. See dincli/docker/node/README.md (operator runbook) for the
# docker.sock trust tradeoff.
#
# Build (from repo root):
# docker build -f dincli/docker/node/Dockerfile -t din-node:dev .
# Normally you build/run this via dincli/docker/node/docker-compose.yml.

FROM python:3.12-slim

# ---- Pins (surfaced as build args so an operator/CI can override) ----------
# dincli version is pinned implicitly by the source tree copied in below
# (pyproject.toml -> version = "0.1.0"). Python is pinned by the base tag above.
# DOCKER_CLI_VERSION: e.g. "5:27.3.1-1~debian.12~bookworm"; empty = latest in repo.
ARG DOCKER_CLI_VERSION=""

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

# ---- Docker CLI client (no daemon) -----------------------------------------
# Installed from Docker's official apt repo. We install ONLY docker-ce-cli:
# din-node talks to the *host* daemon over the mounted socket, so it needs the
# client binary but never its own daemon.
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates curl gnupg; \
install -m 0755 -d /etc/apt/keyrings; \
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc; \
chmod a+r /etc/apt/keyrings/docker.asc; \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
> /etc/apt/sources.list.d/docker.list; \
apt-get update; \
if [ -n "$DOCKER_CLI_VERSION" ]; then \
apt-get install -y --no-install-recommends "docker-ce-cli=$DOCKER_CLI_VERSION"; \
else \
apt-get install -y --no-install-recommends docker-ce-cli; \
fi; \
rm -rf /var/lib/apt/lists/*; \
docker --version

# ---- dincli (built install, pinned deps) -----------------------------------
WORKDIR /opt/dincli
# Copy only what the build needs, in cache-friendly order: pinned requirements
# first (rarely change) so the heavy dependency layer is reused across edits.
COPY dincli/requirements.txt ./dincli/requirements.txt
RUN python -m pip install --no-cache-dir --upgrade pip \
&& python -m pip install --no-cache-dir -r dincli/requirements.txt

COPY pyproject.toml README.md MANIFEST.in ./
COPY dincli ./dincli
# Non-editable install: din-node is a release artifact; upgrades rebuild the
# image rather than mutating mounted source.
RUN python -m pip install --no-cache-dir . \
&& dincli version

# ---- Non-root user ---------------------------------------------------------
# Default uid/gid 1000 matches the typical first Linux user. The operator can
# override at runtime (compose `user:`) so files written into the bind-mounted
# state dir — and into worker output dirs, which workers create as din-node's
# uid (see worker.py:_host_user_flag) — stay owned by the host user.
ARG DIN_UID=1000
ARG DIN_GID=1000
RUN groupadd --gid "$DIN_GID" dinuser \
&& useradd --uid "$DIN_UID" --gid "$DIN_GID" --create-home --shell /bin/bash dinuser

USER dinuser
WORKDIR /home/dinuser

# dincli is a CLI, not yet a daemon (the `dind` daemon is a future phase). To
# keep a long-lived container that operators `docker compose exec` into, the
# main process just idles. When `dind` exists this CMD becomes its entrypoint.
CMD ["sleep", "infinity"]
Loading