config.yaml
rings init writes the node configuration to ~/.rings/config.yaml (or the path given to
--location) with every section stated explicitly, so the generated file is the one place an
operator edits. This is the file it writes, with $HOME expanded to /home/operator:
network_id: 1
delegatee_key: /home/operator/.rings/delegatee_key
internal_api_port: 50000
external_api_addr: 127.0.0.1:50001
endpoint_url: http://127.0.0.1:50000
allow_remote_external_api: false
ice_servers: stun://stun.l.google.com:19302
stabilize_interval: 15
online_node_heartbeat_interval_secs: 30
online_node_ttl_secs: 90
online_node_type: Native
advertise_presence: true
advertise_onion_relay: false
advertise_onion_exit: false
onion_exit_heartbeat_interval_secs: 30
onion_exit_ttl_secs: 90
onion_exit_services:
- tcp
- https
onion_exit_policy:
allowed_targets: []
denied_targets: []
max_sessions: 0
max_bytes_per_minute: 0
onion_http_proxy_header_timeout_secs: 10
onion_http_proxy_max_connections: 1024
gateway:
enabled: false
plan:
addresses:
- 100.64.0.1/32
included_routes: []
mtu: 1280
max_flows: 1024
flow_idle_timeout: 300
tcp_buffer_bytes: 65536
interface_name: null
route_ledger_path: /home/operator/.rings/gateway-routes.json
unix_helper_socket: /home/operator/.rings/gateway-helper.sock
wintun_dll_path: null
status_refresh_secs: 2
bootstrap:
peers: []
dht_virtual_nodes: 160
origin_quota:
dht_control:
message_rate_per_second: 8
message_burst: 32
byte_rate_per_second: 4194304
byte_burst: 67108864
max_records: 1024
storage:
message_rate_per_second: 8
message_burst: 32
byte_rate_per_second: 4194304
byte_burst: 67108864
max_records: 1024
e2e:
message_rate_per_second: 8
message_burst: 32
byte_rate_per_second: 4194304
byte_burst: 67108864
max_records: 1024
application:
message_rate_per_second: 8
message_burst: 32
byte_rate_per_second: 4194304
byte_burst: 67108864
max_records: 1024
data_storage:
path: /home/operator/.rings/data
capacity: 200000000
measure_storage:
path: /home/operator/.rings/measure
capacity: 200000000
Every field has the meaning below. Fields absent from a file take the value shown above, except where noted.
Identity and network
-
network_id: the Rings overlay this node joins. Signatures are bound to it, so nodes on different overlays do not verify each other’s messages. -
delegatee_key: path of the delegatee signing key file thatrings initwrites next to the config. The delegatee key is derived from your ECDSA key and signs every message on your behalf; keep the file private. This field is path-only: a raw session dump and the removedecdsa_keyandsession_managerfields are rejected rather than interpreted as compatibility fallbacks. -
ice_servers: STUN or TURN servers used to establish WebRTC connections, separated by;. STUN is ordinary discovery; TURN is optional and never a gateway prerequisite.ice_servers: turn://user:[email protected]:3478;stun://stun.l.google.com:19302 -
stabilize_interval: seconds between Chord stabilization rounds. -
dht_virtual_nodes: virtual DHT positions this node owns for storage placement;0disables virtual positions. -
origin_quota: runtime-local final-destination rate limits. Each ofdht_control,storage,e2e, andapplicationsets message rate/burst, byte rate/burst, and the maximum retained origin records for that lane. Every value must be non-zero. The key uses the verified origin account and final destination, not the delegation or immediate relay. Token balances are deliberately reset on restart; durable replay state is separate. A protocol may declare delegated admission when it is registered: for its own traffic from the authenticated neighbour that originated it, theapplicationmessage limit is skipped, while theapplicationbyte limits and record bound still apply, each such message is charged at least 16 KiB (DELEGATED_MIN_CHARGE), and it shares the byte bucket with that neighbour’s other application traffic. A protocol may also declare delegated replay (#1081): its traffic from that same neighbour is then admitted in a volatile replay window of the connection, and the protocol guards its own replay. Delegation is not configured here. -
external_ip,webrtc_udp_port_min,webrtc_udp_port_max: optional reachability hints, an externally visible address and a UDP port range for ICE. The two port bounds must be given together.
Control API
internal_api_port: loopback JSON-RPC listener for theringsCLI and local tooling; every route requires the Bearer token. The existing/statusroute also provides the compact operator observability projection.external_api_addr: JSON-RPC listener peers dial for the HTTP handshake.nodeDidandanswerOfferare public; status and registry reads, including the compact observability projection, require the token.endpoint_url: the internal endpoint the CLI connects to.api_token_path: optional path of the Bearer token file; by defaultapi-tokennext to the config file. Relative paths are resolved next to the config file.api_allowed_origins: exact browser origins permitted to call the authenticated API; empty denies every browser origin.allow_remote_external_api: required to bindexternal_api_addrto a non-loopback address.
Presence and onion registries
online_node_heartbeat_interval_secs,online_node_ttl_secs,online_node_type,advertise_presence: how this node publishes its online-node descriptor. A descriptor is a signed lease of 10 minutes whose bytes stay fixed, so each heartbeat re-appends the same descriptor and writes no removal (#918); the TTL is how long after the last heartbeat a reader still lists it. A heartbeat interval must be below the TTL, and below half the lease, 300 seconds, so a lease spans more than two heartbeats; the TTL must be at most the lease, since no reader lists a longer freshness window. The same bounds apply toonion_exit_heartbeat_interval_secsandonion_exit_ttl_secs. A node refuses to start otherwise. A descriptor that someone else re-appends after its node stopped is listed at most until its lease ends.advertise_onion_relay: advertise onion relay capability. The capability carries the node’s process epoch, drawn afresh at every start, so layers sealed for an earlier process are rejected after a restart. It requiresadvertise_presence.advertise_onion_exit,onion_exit_heartbeat_interval_secs,onion_exit_ttl_secs,onion_exit_services,onion_exit_policy: whether and how this node serves as an onion exit. Every exit is also a relay:advertise_onion_exitrequiresadvertise_onion_relay, and each exit descriptor carries the same process epoch as the relay capability. Clients select an exit only while both agree. A browser node interpretshttpsonly, so a browser exit offeringtcpis rejected when it starts. Service names form a closed set:tcp(byte streams, every tunnel and TLS included) andhttps(one HTTPS request/response fetch; never a tunnel). Any other name, includingrelay(advertised throughadvertise_onion_relayinstead), is rejected, so restrict an exit’s reach with the policy targets rather than with a custom service name: an exit that should only carry HTTPS registerstcpwith the allowed target*:443. A policy target ishost:port,*:port(any host on that port) or*:*(any target). An emptyallowed_targetslist is a closed policy that admits no target, so advertising an exit requires at least one allowed target; deny entries override allows; a0limit is unspecified. The policy takes only these four keys: any other, including the removedmax_circuitsandmax_streams_per_circuit, is refused as an unknown field. A*is recognised only as a whole host (*:port,*:*); any other*in a host is refused. Deny entries match the requested authority, not the addresses it resolves to: an IP literal or another name for a denied host is not denied by a name entry, so deny by the name the client requests, and rely on the exit’s public-address floor for private and loopback ranges.
HTTP CONNECT proxy
onion_http_proxy_addr: optional local HTTP CONNECT listener that routes client TCP streams through onion exits; absent means no proxy. The CONNECT exit service is alwaystcp, so the formeronion_http_proxy_servicekey (and itsONION_HTTP_PROXY_SERVICEvariable) is removed: a config naming it is refused as an unknown field. A refused CONNECT answers502 Bad Gatewaywith no reason.onion_http_proxy_header_timeout_secs,onion_http_proxy_max_connections: limits of that proxy. A CONNECT tunnel is a byte stream, so it always uses atcpexit; there is no service to choose.
The route length is not configurable. Every onion route is a loop that leaves and returns through
the client’s entry guard, with two relays around the exit on each side:
guard → relay → exit → relay → guard. It needs four distinct live relays besides the client, one
of which registers the exit service; with fewer, route selection fails instead of shortening the
path.
Gateway
The gateway section configures the native TUN gateway described in
Native Gateway. Presence of the section is not consent to start a TUN
device:
gateway starts ⟺ section present ∧ (enabled = true ∨ rings run --gateway)
enabled: whether plainrings runstarts the gateway. Absent meansfalse.plan.addresses: IPv4/32host addresses of the virtual interface. The generated100.64.0.1/32is drawn from the RFC 6598 shared address space, so it collides with neither LAN nor VPN ranges.plan.included_routes: the only destination prefixes routed into the gateway. Empty captures nothing; a default route or a set covering all of IPv4 is rejected.plan.mtu: interface MTU. The generated1280is the IPv6 minimum link MTU, which every underlay carries.max_flows,flow_idle_timeout,tcp_buffer_bytes: concurrent TCP flow limit, per-flow idle timeout in seconds, and per-flow buffer size.interface_name,wintun_dll_path: Windows interface name and Wintun DLL path;nullselects the defaults. On Unix the foreground helper’s--interfaceis authoritative.route_ledger_path: durable route journal, used directly on Windows; on Unix the helper’s--ledgeris authoritative.unix_helper_socket: control socket of thegateway-config-unixforeground helper on Linux and macOS; must match the helper’s--socket.status_refresh_secs: refresh interval of onion-exit availability in/gateway/status. Captured flows are byte tunnels, so they always use atcpexit, and route length follows the onion loop shape (see HTTP CONNECT proxy).
Any other key in the section is rejected rather than ignored, so a misspelt key cannot fall back to its default.
Services and DNS relay
Two optional sections, absent from the generated file, tie the node to DNS relays (#1049); see DNS Relay for both:
services: the ports the node serves at<did>.<zone>, each mapped to a local address and optionally preceded by a PROXY protocol v2 header (proxy_protocol: v2), and the TXT records it publishes under its name. The node signs and publishes them everyheartbeat_interval_secs; an empty section withdraws its name.dns_relay: a DNS relay this node runs: its zones, IPv6 prefix, ingress ports and state directory.
Any other key in either section is rejected rather than ignored.
Bootstrap
The bootstrap section lists peers that rings run keeps reachable for the life of the
process. rings connect node and rings connect seed connect once; a managed target is redialed
through its HTTP endpoint whenever the node can no longer reach it through the overlay, so a
long-running node that loses its transport to a seed rejoins without a restart.
peers: managed targets, each{ did, url }with an optionalapi_token, the same shape as an entry of a seed document. Everydidmust parse, everyurlmust be a public HTTP(S) endpoint, and the node’s own DID is skipped; an entry repeated verbatim is merged, while a DID listed with conflicting entries, or an endpoint listed under two DIDs, is rejected as ambiguous. A violation stopsrings runbefore it listens.rings run --bootstrap-seed <url>appends the peers of a seed document (afile://orhttp(s)://URL, read once at startup) to this list for one run.
How a managed target is supervised:
- Reachability is a routed successor lookup for the target’s DID. A target whose admission was announced, or one that some peer can route to, is left alone, so a target is never forced into a direct edge merely because it is not a finger.
- An unreachable target is redialed five times, each attempt at least two seconds after the previous one settled, then every five minutes plus up to thirty seconds of jitter, until the peer is admitted again.
- A redial counts as successful only once the swarm admits the peer. An endpoint answering as a different DID is refused before any offer is created. A redial refused because a handshake to the target is already in flight, whichever side started it, is not an error, but consumes a turn of the burst like a failed dial.
- A target leaving the local DHT, whatever caused the loss, triggers an immediate reassessment, and a later loss restarts the burst.
- At most one handshake per target is in flight, targets retry independently, and shutdown cancels any handshake in progress.
Storage
data_storage: path and capacity in bytes of the DHT data store.measure_storage: path and capacity in bytes of the peer measurement store.
The daemon keeps four more stores in directories beside the data store, with fixed budgets:
transaction-replay (the replay windows, authoritative), relay-inbox (the relay carriers this
node holds or owns, retained: flushed and never evicted, 64 MiB, #1021), dht-removals (the
removals of the data topics this node stores, retained: flushed and never evicted until they
are collected, 24 MiB, #920, #907) and onion-entry-guards (a durable cache). Back them up and remove
them together with the data store; removing relay-inbox drops the mail held there, and
removing dht-removals makes this node a fresh replica of every topic it stores.
Files written before a field existed still load: an omitted field takes its default, and an
omitted gateway section loads as no gateway at all.