docs/security.md:5-14.
Source repository
The security model, isolation rules, and the auth/signing primitives.
Isolation rules
- The shared control-plane PostgreSQL is available only to the master or validator
control-plane process that owns the deployment; its URL comes from
PLATFORM_DATABASE_URLor a Docker secret. - Challenges never receive master, validator, or central control-plane PostgreSQL credentials.
- Each challenge gets only its own SQLite database on its
/dataSwarm volume. - The submitter never receives master DB credentials.
- Internal challenge calls require per-challenge shared tokens.
- The public proxy strips sensitive headers and blocks internal challenge paths.
docs/security.md:7-14.
Authentication and signing
There are two distinct auth models on the public API, plus internal tokens used only between the master and its containers.Miner request signing
Signed miner actions preserve only four headers —X-Hotkey, X-Signature,
X-Nonce, and X-Timestamp — which are verified against a canonical message before
the request is bridged to the challenge.
Source: docs/security.md:14; src/platform_network/security/miner_auth.py:159-162, src/platform_network/security/miner_auth.py:96-111.
Admin token
Management routes depend onrequire_admin, which accepts the token in an
X-Admin-Token header or as an Authorization: Bearer credential and compares it in
constant time. The expected token is loaded from ADMIN_TOKEN or the file named by
ADMIN_TOKEN_FILE.
Source: src/platform_network/master/app_admin.py:121-130; src/platform_network/master/admin/auth.py:10-18, src/platform_network/master/admin/auth.py:28-29.
Per-challenge tokens
Internal challenge calls — including the master’s weight collection — require a per-challenge shared token. Public challenge routes are proxied without exposing the internal control routes. Source:docs/security.md:11; docs/architecture.md:57-59.
Validator trust
Validators run only the submit-only on-chain submitter; it reads the public/v1/weights/latest and needs no control-plane database credential. The submitter
logs only the public hotkey SS58 address, never the private key.
Source: docs/security.md:10; deploy/swarm/submitter/run_submitter.py:30-33, deploy/swarm/submitter/run_submitter.py:65-86.
Production policy boundaries
The production boundary is stricter than local development:- Dev/test/local runs may use SQLite for master state, but production control-plane
state must use PostgreSQL from a Docker secret or an explicit
PLATFORM_DATABASE_URL; SQLite is rejected for control-plane state. - Challenge runtime state is always SQLite on the challenge
/dataSwarm volume. - Production images must include a tag and a
sha256digest; untagged references and missing digests are rejected.
docs/security.md:18-23.
Swarm runtime boundary
Challenge services run on the manager node (node.role==manager); broker-dispatched
evaluation jobs run on worker nodes constrained by node.labels.platform.workload.
Broker-created challenge jobs must not receive the host Docker socket — the default
socket-grant allowlist is empty.
Source: docs/security.md:27; src/platform_network/config/settings.py:74-82.
Network isolation uses encrypted overlay networks created with MTU 1450. A job
requesting network: none is attached to a dedicated internal (no external routes)
encrypted overlay, because Swarm services cannot attach to the predefined none
network.
Source: docs/security.md:29.
Privileged escape hatch
A Swarm service cannot run--privileged or --gpus, so docker service create
never emits them. A challenge that legitimately needs a privileged Docker-in-Docker
job uses the capability-gated escape hatch: the broker runs the job as a direct local
docker run on a worker node. The escape hatch is the only path that grants
privilege, it is gated per challenge, and the container owns its own
/var/lib/docker volume rather than the host Docker socket.
Source: docs/security.md:33-35; src/platform_network/config/settings.py:70-73.
Secrets
Admin tokens, challenge tokens, the control-plane database URL, registry credentials, and wallet material must come from files, environment variables, or Docker secrets. Swarm secrets are mounted inside containers at/run/secrets/platform/<name>, and value-bearing secrets reach docker secret create
via stdin, never as argv.
Source: docs/security.md:49.
The control-plane database credential is written only into a Docker secret and must
not be printed in logs, service definitions, or evidence. Challenge services receive
only per-challenge runtime secrets.
Source: docs/security.md:31.
Miner env values submitted to the Agent Challenge are per-submission secrets owned by
the challenge, encrypted at rest, and cannot be retrieved after submission. The proxy
forwards the request body but must not parse, persist, or log submitted env values.
Source: docs/security.md:51.
Broker archive and cleanup security
Broker archive uploads are treated as untrusted input: the Swarm broker path rejects absolute paths, parent traversal, links, and device members before extraction, and malformed images are rejected before any service is created. Source:docs/security.md:41-43.
Cleanup is two-layered. The broker /v1/docker/cleanup path removes the Swarm
service and releases the workload and GPU ledger entries on success and failure; the
manager-only supervisor timeout-reaper independently reaps jobs that exceed their
timeout, so a crashed challenge cannot leak long-running services.
Source: docs/security.md:45.
Failure behavior
If a challenge fails health checks orget_weights, its contribution is zero for
that epoch; the master does not auto-disable it. For public challenge requests,
transport failures at ingress, proxy, or challenge discovery become safe 502
responses.
Source: docs/security.md:55-57.
Related
Broker
Job dispatch, the GPU contract, and archive validation.
Database and registry
Control-plane PostgreSQL versus per-challenge SQLite.
Sources
Citations reference thebase repository pinned at SHA
e33109bfa4f5054928c3b4d429be9cf35d36b166 (see SOURCES.md). Paths prefixed with
src/platform_network/ are the internal Python package.