Repository navigation
Mesh Containers
The published Coyote image runs the Coyote Mesh like any other Coyote: opt-in, per session, through the
same mesh: block. This page gives you what the image ships for the mesh, what it deliberately leaves out, the
environment variables that shape the daemon inside it, and how to mount the two directories the mesh needs.
A few Reticulum words come up throughout. rnsd is the Reticulum daemon, a small background program that owns the network interfaces so several programs on one machine can share them; the image starts one for you. A relay is a node that forwards traffic between others. Loopback is 127.0.0.1, the machine itself. A propagation node holds a message for a recipient who is offline until they fetch it; that is what "store-and-forward" means below. An announce is the periodic "I am here" broadcast by which nodes learn of each other. AutoInterface is Reticulum's automatic discovery of other machines on the same local network.
The root Dockerfile builds FROM docker/sandbox-templates:shell-docker, installs the prebuilt musl release tarball
and, from v0.10.4, a Reticulum daemon (rnsd, uv tool install "rns==1.5.2" under a uv-managed CPython 3.12), and
ends with USER 1000, WORKDIR /home/agent/workspace and
ENTRYPOINT ["/usr/bin/tini", "-s", "-g", "--", "/usr/local/bin/coyote-entrypoint"]. It sets
NO_PROXY=localhost,127.0.0.1,::1,172.17.0.0/16. There is no EXPOSE, no VOLUME, and no compose file in the
repository.
That is on purpose:
- The mesh is off by default, and the image is a Docker Sandboxes template whose users already mount the config dir.
- The only listener is
rnsdon127.0.0.1:4242inside the container, which Coyote dials over the container's own loopback. On the default bridge network nothing outside the container can reach it, so there is no port to publish; never publish4242. Other hosts are reached through a relay (COYOTE_MESH_RELAY, below), not by exposing this one. Under--network hostthat loopback is the host's: any local process can attach, as with a daemon written byscripts/mesh-relay.sh. - No
VOLUMEis added because the two directories below are chosen by environment variable, not fixed paths.
Before v0.10.4 the image shipped only the Coyote binary and no daemon, so the shipped private 127.0.0.1:4242
default could not connect inside it.
The image runs rnsd for you. On every start the entrypoint starts it beside the main command (coyote, or the
sh/bash/path you pass, the Docker Sandboxes keep-alive included), and the shipped default
private 127.0.0.1:4242 dials it with no mesh configuration at all. Three environment variables shape the daemon:
| Variable | Effect |
|---|---|
COYOTE_MESH_RELAY=host:port |
Adds a [[Team Relay]] TCPClientInterface to your team relay or propagation node; this is how a container reaches anything outside itself. |
COYOTE_MESH_LAN=1 |
Adds the AutoInterface for link-local discovery. Only useful with --network host on Linux: the default bridge does not carry link-local multicast. With it the daemon is a transport node for every Reticulum peer on that LAN, and bridges the LAN into the Team Relay when both are set; the entrypoint prints a note saying so. |
COYOTE_MESH_RNSD=0 |
Starts no daemon. Point mesh.interfaces at a relay yourself, as before v0.10.4. |
The daemon's config is rendered once, from /opt/coyote/reticulum.config.tmpl into ~/.reticulum/config
(enable_transport = True, [logging] loglevel = 4, the loopback [[Coyote Sessions]] listener with
ingress_control = No, plus the optional stanzas above), and never overwritten: with ~/.reticulum on a volume, a
later change to the variables does not reach the file (the entrypoint says so on stderr); edit it there or remove it.
A mounted ~/.reticulum has to be writable by uid 1000 like the two directories below: when the config still has to
be rendered and cannot be written, the entrypoint prints a WARNING: and starts no daemon. The daemon logs raw and at
debug level to the container's stderr (docker logs; give long-running containers a --log-opt max-size), and
rnstatus inside the container attaches to it. A daemon that fails to start never stops the main command; .mesh on
failing with its interface error is where you see it. The image works with --read-only given
--tmpfs /home/agent/.reticulum --tmpfs /tmp. COYOTE_MESH_RELAY takes a hostname or an IPv4 address; an IPv6
literal is refused with a WARNING: and no relay stanza.
A node behind a relay needs no published port. Mount a config dir and a cache dir, point the two variables at them, and name the relay:
docker run -it \
-e COYOTE_CONFIG_DIR=/home/agent/.config/coyote \
-e COYOTE_CACHE_DIR=/home/agent/.cache/coyote \
-e COYOTE_MESH_RELAY=relay.example:4242 \
-v <host-config>:/home/agent/.config/coyote \
-v <host-cache>:/home/agent/.cache/coyote \
darkalex17/coyoteThe paths are yours to pick; the environment variables are what matter. chown 1000:1000 both host directories
first: the image runs as uid 1000, and the first .mesh on creates mesh/identity.key owner-only (0600) inside a
0700 mesh/ directory, which fails on a root-owned bind mount.
Without COYOTE_MESH_RELAY the container is a mesh of one: its daemon hears nothing but the sessions inside it. A
daemon on the Docker host written by scripts/mesh-relay.sh listens on the host's loopback only
([[Coyote Sessions]] binds 127.0.0.1), so it is not reachable from a container either; name the team relay or the
propagation node, or add a listener on a host address to that daemon's config yourself.
Signals: docker stop sends TERM, which tini delivers to the main command directly as long as it stays in the
entrypoint's process group (coyote and sh -c do; an interactive shell with job control moves itself out and has to
be stopped on its own terms); the entrypoint waits for it, stops the daemon, and exits with the main command's code
(143 for a command that dies of the TERM, coyote's own otherwise). A Ctrl-C under docker run -it, or
docker kill -s INT, reaches the main command and never the daemon, which runs in its own session.
The config dir holds the identity and trust.yaml; the cache dir holds the peer table, knocks, correlations and
instance locks. Two containers sharing one config dir do start, since the lock a running node holds on the identity
key is shared; with different sessions they run as two destinations of one identity. What collides is one cache dir
together with one session: the instance lock is per session and per cache dir, so a second container using the same
session on the same cache dir is refused. The same session on two cache dirs is not refused: both containers
announce one destination, since the instance id lives in the session file under the shared config dir. Give each
container its own session, or .mesh on --fresh in one of them. Two separate identities need two config dirs,
exactly like two identities on one host (see Two sessions on one host).
"Private" is about reachability, not access control: the relay authenticates nobody, so anyone who can reach its port joins it and sees every announce. Keep the relay behind the firewall or VPN; Coyote's trust list is what gates status cards and messages.
The AutoInterface is IPv6 link-local multicast on :42671 (discovery 29716), which does not cross the Docker
bridge. It works only with --network host, and then only one process on the host can hold it, the container
included. In the image that process is the daemon: COYOTE_MESH_LAN=1 with --network host gives it the
AutoInterface, and sessions keep the loopback default; a type: lan entry in the same container would compete with
it for the port. Prefer a relay.
The image's NO_PROXY lists ::1 unbracketed but not [::1]. An in-container HTTP client that formats loopback as
[::1] still goes through the proxy. Reticulum uses plain TCP sockets, not HTTP, so this does not touch the mesh; it
touches HTTP tooling running in the same container. Add [::1] to NO_PROXY if you hit it.
Inside an sbx sandbox the same rules apply, with the sandbox's egress policy on top; see
Sandboxes.
The relay-and-store-and-forward daemon has its own image under
deployment/propagation-node.
It publishes 4242 and keeps its identity on a volume; a host's rnsd dials it with --relay, a container's with
COYOTE_MESH_RELAY=<node host>:4242, and a container running with COYOTE_MESH_RNSD=0 can point type: private at
it directly. What Coyote does and does not do with propagation nodes today is on
Mesh Deployment.
Like the relay, it authenticates nobody for transport or posting; its allowed list gates fetching only.