Specifications
IPFS Daemon Hardening
Status: Phase 0 — normative. Donor pinning nodes refuse to start unless the embedded Kubo daemon's configuration satisfies the rules below. The coordinator's own embedded Kubo runs the same checks.
Purpose
A default Kubo install joins the public IPFS DHT, broadcasts every CID it provides, listens on mDNS, and exposes a Web UI. None of that is acceptable for Nova's private federation, where:
- Donor nodes must be unable to leak the federation's CID set to the outside world.
- Network observers must not be able to identify Nova nodes from bootstrap traffic.
- The Kubo HTTP API and Gateway must never bind to a non-loopback interface.
This document specifies the exact configuration that the donor and coordinator validators enforce on startup.
Required configuration
The hardened ~/.ipfs/config (or operator-specified equivalent) must
have at minimum the following keys set to the listed values. The
config validator reads the live config and refuses to start the
daemon if any rule is violated.
{
// No public bootstrap peers. The operator supplies private peers
// via the federation's own bootstrap mechanism (see below).
"Bootstrap": [],
// No public DHT. "none" disables DHT entirely; "dhtclient" can be
// used inside a private swarm if the operator wants peer discovery
// via private bootstrap nodes.
"Routing": { "Type": "none" },
// No mDNS broadcast on the LAN.
"Discovery": {
"MDNS": { "Enabled": false }
},
// Do not announce providership to anyone. The orchestrator
// tracks who pins what; we do not need DHT advertisement.
"Provider": { "Strategy": "" },
"Reprovider": { "Strategy": "" },
// Loopback-only API and Gateway. The federation HTTP traffic
// does not go through these; they are local-management interfaces.
"Addresses": {
"API": "/ip4/127.0.0.1/tcp/5001",
"Gateway": "/ip4/127.0.0.1/tcp/8080"
},
// Don't punch through NAT for outsiders.
"Swarm": {
"DisableNatPortMap": true
}
}
Private swarm key
The donor and coordinator daemons must share the operator's
IPFS_SWARM_KEY. With the swarm key in place, libp2p refuses to
exchange data with peers that lack it.
The swarm key file at ~/.ipfs/swarm.key:
/key/swarm/psk/1.0.0/
/base16/
<64-character hex-encoded 256-bit secret>
The donor node's container reads the key from the env var
IPFS_SWARM_KEY and writes it into the file at boot. If
IPFS_SWARM_KEY is unset and public_ipfs_dht: false (the default),
the donor refuses to start.
Generate the key via:
( echo "/key/swarm/psk/1.0.0/" \
; echo "/base16/" \
; tr -dc 'a-f0-9' </dev/urandom | head -c64 ; echo ) > swarm.key
The same swarm key is shared with every donor in the federation. It is not a secret in the cryptographic sense — anyone with the key can attempt to peer — but it is a coarse-grained access control that keeps the federation invisible to default IPFS clients.
Bootstrap peer requirements
Bootstrap is empty by default per the rules above, which means a
fresh Kubo daemon has no peers to connect to. The federation
substitutes operator-supplied private bootstrap entries injected at
boot time:
{
"Bootstrap": [
"/ip4/10.42.0.1/tcp/4001/p2p/<coordinator-peer-id>"
]
}
The startup validator accepts only entries whose multiaddr resolves to either:
- The loopback range (
127.0.0.0/8,::1). - An RFC 1918 private range (
10/8,172.16/12,192.168/16). - The operator-configured Nebula overlay subnet (set via
node.yaml: nebula_subnet).
Any entry pointing to a public IP — bootstrap.libp2p.io,
node.ipfs.io, anything in the Kubo defaults — is rejected. The
node logs the offending entry and refuses to start.
Validator algorithm
internal/ipfs.ValidateConfig(cfg KuboConfig, mode Mode) error is
called before the daemon starts. It walks the rules below and
returns the first failure (with a precise error message naming the
key) or nil for pass.
| Rule | Required value when mode == private (default) |
Required value when mode == public_archival_dht |
|---|---|---|
Discovery.MDNS.Enabled |
false |
false (operator may override) |
Provider.Strategy |
"" (empty) |
"all" allowed |
Reprovider.Strategy |
"" (empty) |
"all" allowed |
Routing.Type |
"none" or "dhtclient" |
"dht", "dhtclient", or "auto" |
Addresses.API |
starts with /ip4/127.0.0.1/ |
starts with /ip4/127.0.0.1/ |
Addresses.Gateway |
starts with /ip4/127.0.0.1/ |
starts with /ip4/127.0.0.1/ |
Swarm.DisableNatPortMap |
true |
unconstrained |
Bootstrap |
every entry resolves to loopback / RFC 1918 / operator overlay | unconstrained |
| Swarm key file | exists and is non-empty | unconstrained |
The validator runs before ipfs daemon is launched. It does not
hot-reload — the daemon must be restarted (or the container
recreated) to apply changes.
Public IPFS DHT mode
For deployments where joining the public IPFS network is the
intended behaviour (a public-facing nova-archive instance hosting
open data, for example), the operator sets
coordinator.public_ipfs_dht: true in operator.yaml. The validator
relaxes to the right-hand column above.
Public DHT mode is opt-in, requires explicit configuration in two
places (operator config and the donor node.yaml), and is logged
prominently at startup. It is not appropriate for any product that
hosts personal or potentially-infringing content.
Operational notes
- The Kubo HTTP API is loopback-only; this means
ipfsCLI commands must be run on the same host as the daemon. Operators who want remote management should tunnel via SSH, not by binding the API to a public interface. - The Kubo Gateway (
/ip4/127.0.0.1/tcp/8080) is not the same as Nova's read gateway. The coordinator's read gateway speaks the HTTP API documented inopenapi.yamland reads from the local Kubo via its loopback API. - IPFS garbage collection is the donor's local concern and runs on Kubo's normal schedule. The federation protocol does not direct GC.
Out of scope
- WireGuard or alternative mesh VPNs. Nova standardizes on Nebula; the validator does not check anything about the mesh layer.
- IPFS Cluster configuration. Cluster pin coordination is a separate
layer; its hardening is enforced by
cluster-config-validatein Phase 2. - Multi-tenant Kubo (one daemon, many federations). Each operator runs their own coordinator + Kubo + Cluster + swarm key.
Source:
docs/specs/KUBO_HARDENING.md