YUME operations
This page covers provisioning, deployment and maintenance after the first local connection in the quick start. Implementation status lists the covered service checks and remaining distribution, real-network and release qualification.
Provision a kit
yume-setup init creates one server tree and one client bundle in a new
directory. It never overwrites an existing path.
yume-setup init --host vpn.example.net --port 443 \
--output "$PWD/yume-kit" --client-name laptop
yume-doctor --config yume-kit/server/yumed.json
yume-doctor --config yume-kit/client/yume.json
The native guide lists every generated file and its purpose. Move the client bundle to its machine over an authenticated channel. Remove offline CA material from the server host, and never commit a kit, capture or diagnostic artifact.
Each device gets its own composite identity and access PSK. Issue another client bundle from the server tree, then reload the daemon so it reads the extended store:
yume-setup add-client --server yume-kit/server --host vpn.example.net \
--output "$PWD/tablet" --client-name tablet --max-sessions 2
add-client writes a new bundle, the client’s public key and PSK under the
store’s authorized/ directory, and a replacement authorized-keys.json that
grants the kit’s standard tcp and udp services. The store is replaced last,
but a failure while flushing a directory after that replacement returns exit
status 3. The store and the files it names remain visible, but directory
durability was not confirmed. Validate the server tree before reloading it.
Files it adds to the server tree keep the store’s owner. --host
must be the name the server certificate was issued for.
--max-sessions and --weight are optional, and init accepts both, as well
as --max-egress-mbps for the server and --preset for the kit’s tuning
(the configuration reference lists the
presets).
To revoke a device, remove it and reload. Its sessions end at once:
yume-setup remove-client --server /etc/yume --client-name tablet
systemctl reload yumed
remove-client replaces the store atomically, then deletes the client’s
public key and PSK. It refuses to remove the last client, because the daemon
requires at least one.
Join servers into a cluster
Servers of one operator can form a cluster: each checks a server list the
operator signs and keeps an authenticated link to every other server, and
circuits run over the links through two or three servers. A yume client
with configured circuits builds its routes over those links. Keep the operator directory, which holds the
signing key, off the servers, and run the commands on the operator’s copies
of the server directories:
yume-setup cluster-init --output "$PWD/operator"
yume-setup cluster-add --cluster operator --server north/server \
--name north --host north.example.net
yume-setup cluster-add --cluster operator --server south/server \
--name south --host south.example.net --exit
yume-setup cluster-sign --cluster operator --days 30
cluster-add writes a PSK for each direction between the new server and each
earlier one into their credentials/cluster/ directories, and adds a
cluster section to the new server’s yumed.json. With --exit the server
carries circuits’ streams to destinations through its direct_tcp adapter’s
destination policy. cluster-sign signs the next list and the routes view
that clients choose routes from, and copies both to every server. A server
whose host does not resolve when you sign needs --address at
cluster-add. add-client --circuits gives a new client a circuits section: its
SOCKS5 connections then leave through circuits of three servers, with that
server as the entry. When no route of three hops can be built, the client
refuses connections until you accept its proposal: yume --config PATH
--status shows it, with what the shorter route gives up, and yume
--config PATH --accept-route ID accepts it. Set circuits.min_hops to
approve a shorter route in advance on a machine nobody watches. Deploy each server’s
credentials/cluster/ directory (and a new member’s yumed.json) with its
files owned by the daemon’s account and mode 0600, restart a new member and
reload the others. Sign again before not_after, 30 days by
default: a server whose list has expired closes its links, and it refuses to
start or reload until it has a newer list. cluster-remove --cluster operator
--name south takes a server out; sign and deploy afterwards. With
control.socket set, yumed --config PATH --status shows each link.
Cluster 1 gives the formats and limits.
The generated cluster configuration uses cluster-state.json beside
yumed.json. For a packaged server under /etc/yume, set its cluster.state
file to /var/lib/yume/cluster-state.json, because the unit makes /etc/yume
read-only.
Run the daemon as a service
The Debian yume-daemon package installs /usr/bin/yumed, a credential-free
bootstrap /etc/yume/yumed.json and a hardened, disabled yumed.service that
runs as the locked yume account. Install only the kit’s server/ contents
under /etc/yume, keeping its credentials/ and cover-site/ layout. Private
files must be owned by yume with mode 0600, and credentials/ should be
mode 0700. Then validate as the service account and start the service:
runuser -u yume -- yume-doctor --config /etc/yume/yumed.json
runuser -u yume -- yumed --config /etc/yume/yumed.json --validate
systemctl start yumed.service
The unit repeats validation before every start, grants only
CAP_NET_BIND_SERVICE for port 443, and reads /etc/yume without being able to
modify it. Enable it at boot only once the deployment is ready. The static
cover site is served in-process; no separate HTTP backend runs. If a proxy sits
in front of yumed, use TCP passthrough; terminating TLS breaks admission,
which is bound to the TLS connection. The package’s README.Debian holds the
complete checklist.
Client identities and limits
credentials/authorized-keys.json lists the client identities the daemon
accepts. Each entry names a composite identity with its SHA-256 fingerprint,
its access PSK file, and the configured services it may open. An entry may add
max_sessions, an integer from 1 to 1024. When a new session of that identity
would exceed it, the daemon ends the identity’s oldest session. A client that
reconnects after a network change therefore replaces its own half-open session
instead of being refused. Without the field an identity is bounded only by the
daemon’s session capacity below. admin-keys.json is a separate store of
second-factor identities and grants no traffic.
The server’s limits.max_egress_mbps, from 1 to 1000000 megabits per second,
caps the stream payload the daemon relays. Each byte counts once, in either
direction and for every service. Protocol framing and control messages do not
count. Clients share the rate by weight. A busy client gets its entry’s
weight, a number from 0.1 to 100 that defaults to 1, divided by the total
weight of the clients sending at that moment, and a client that stops sending
leaves its share to the others. When another client starts sending, transfers
already scheduled keep their earlier share, so the total can exceed the rate
until they have started. Without the field the daemon does not limit the rate
and weights have no effect.
SIGHUP, or systemctl reload yumed with the packaged unit, makes the daemon
read both stores and its composite and ML-KEM keys again without dropping other
clients. Later sessions authenticate against the new stores. Established
sessions use the new grants for their next stream and a changed weight for
their next transfer. Sessions of a removed
identity, or beyond a lowered max_sessions, end at once. An invalid store is
refused and the previous credentials stay in force. The daemon logs the outcome
either way. TLS certificate and admission key changes still need a restart,
and a changed admission key is refused. The schema-1 limits object bounds each session’s frames,
streams, queued bytes, pending opens, rekey work, control messages and packets;
the configuration reference owns
those fields.
Capacity
One yumed holds up to 1024 client sessions. The connections their direct
routes open to destinations share one pool of up to 16384, of which up to 1024
may be connecting at once. Each session and connection takes a descriptor, so
the daemon raises its soft open-file limit to the hard one at start, keeps 512
descriptors for itself, gives sessions at most half of the rest and the pool
what remains. The packaged unit’s LimitNOFILE=65536 allows the full sizes,
and a limit of 1024 leaves 256 sessions and 256 connections. One authenticated
identity may reserve at most half of the provider’s pending and active route
limits, rounded up, across its sessions and TCP/UDP services. The reservation
is released when an open fails, is canceled or its channel closes. At a
circuit exit the counted identity is the previous hop.
Sessions spread over endpoint.event_loops threads, by default one for each
CPU the daemon may run on, up to 16, and each session stays on one of them,
so one session moves at what one core encrypts and several share the cores.
A unit that pins the daemon with CPUAffinity= also sets how many loops it
runs by default. A cluster member or a server with a packet adapter runs one
loop. yumed --status shows the sessions on each loop. An idle connection holds
no read buffer. A session streaming to a slower client can hold its
limits.max_queued_bytes of outbound data, and up to twice that, at least
8 MiB, of records it received, so plan memory for the sessions that stream at
the same time, not for every session, and bound the process as below.
For hard process ceilings, use systemd rather than in-process settings:
# systemctl edit yumed.service
[Service]
CPUQuota=800%
MemoryHigh=768M
MemoryMax=1G
TasksMax=1024
Keep MemoryMax above expected session, socket, TLS and library memory.
Too tight a ceiling lets the kernel terminate the daemon instead of the daemon
refusing work.
TCP autotuning on long paths
Native YUME leaves remote TCP socket buffers to the kernel’s autotuning.
On Linux, a low receive or send ceiling can constrain fast and max on
long paths. Compare checked transfers with ss -tinm at both endpoints:
snd_wnd shows the peer’s advertised receive window. If it stops growing
at the current ceiling, raise the third value of net.ipv4.tcp_rmem and
net.ipv4.tcp_wmem, preserving their minimum and initial values, and measure
again. Choose ceilings within the host’s memory budget and expected concurrent
connections; larger allowed buffers can consume more memory under load.
Keep TCP autotuning enabled. Do not force fixed SO_RCVBUF or SO_SNDBUF
values: those requests disable the corresponding autotuning. The
net.core.rmem_max and net.core.wmem_max ceilings govern explicit socket
buffer requests; changing those alone does not raise TCP’s autotuning maxima.
A loss-free emulated path can establish a ceiling effect, but a production
setting needs measurements on that host and path.
Forward a local port
A client forward adapter gives a program without SOCKS5 support one fixed
path through the tunnel. Add it to the client’s adapters:
{
"kind": "forward",
"service": "tcp",
"listen_address": "127.0.0.1",
"listen_port": 2222,
"destination": {"host": "git.example.net", "port": 22}
}
Every connection to 127.0.0.1:2222 becomes a stream to git.example.net port
22, which the server’s direct_tcp destinations must permit. To keep the
listener away from other local users, use listen_path with a socket path in
a directory only this user can write, such as $XDG_RUNTIME_DIR/yume/git.sock,
instead of an address and port. yume-doctor checks the adapter, and yume
reports a directory with the wrong owner or mode at startup.
Run a module
A server module adapter hands one stream service’s streams to a program
that systemd runs from the packaged yume-module@.socket and
yume-module@.service templates, as modules describes. Enable
the module’s socket before starting yumed, which refuses a missing socket.
Each module runs as a dynamic user of its own in its own control group, so
yumed’s limits do not count it, and it cannot read the daemon’s
credentials. Its messages and exits are in its own journal, as with
journalctl -u yume-module@echo.service.
Name resolution
The client resolves a server host that is a name, and the daemon resolves the
destination names that clients request through direct TCP/UDP adapters and
SOCKS5. Both use the system resolver configuration, including /etc/hosts and
NSS modules, in a separate helper process. It appears as a yume-resolver
child after the first lookup. The helper holds no connections, keeps no
capabilities, and runs each lookup on its own thread, up to 64 at once. Later
lookups wait for a free thread, up to 1024 of them, instead of failing their
connections. Stopping yume or
yumed kills it, so a stalled DNS server or NSS module never delays shutdown.
A numeric host or endpoint.connect_address needs no lookup. A client with
endpoint.socks5_proxy looks nothing up either: it connects to the proxy’s
numeric address and the proxy resolves the server’s name. A client with a
managed TUN adapter needs a numeric first hop, the proxy or else the server,
so that the route to it can be excluded before the tunnel changes routing. Every address a destination name resolves
to must be permitted before the daemon connects.
Release verification
A Linux x86-64 release publishes yume-amd64-linux.tar.xz, holding yume,
yume-setup, yume-doctor, the license, third-party notices, quick start and
a manifest.json, and the standalone daemon yumed-amd64-linux. Each artifact
has a .sha256 file, and the release adds SHA256SUMS.txt and
release-manifest.json. Both artifacts, the aggregate checksums and the
manifest require detached .sig files.
The release workflow verifies the annotated tag and its commit against the
tracked public key in third_party/prebuilt/yume-release-key.asc, pinned to
primary fingerprint 967278FF6FA436F504CBB0058A1588B5E2598DB1. The tag must
point directly to the checked-out commit. Publication requires the matching
GPG_PRIVATE_KEY secret, GPG_PASSPHRASE when needed, and approval of the
release gates.
Verify the signing key’s primary fingerprint through an authenticated source before checking downloaded signatures:
sha256sum -c SHA256SUMS.txt
gpg --verify yumed-amd64-linux.sig yumed-amd64-linux
gpg --verify release-manifest.json.sig release-manifest.json
scripts/release_integrity.py signs the artifacts and aggregate checksums
before generating the manifest, then signs the manifest itself. The inventory
lists artifacts under files and aggregate checksums under checksums, with
their signature filenames. Its own signature stays outside the inventory.
Missing or inconsistent checksums, inventory entries or trusted signatures
stop publication.
Before uploading, the helper rechecks local signatures, the remote tag object and every existing asset name. Any matching name stops the upload. Uploads never replace assets. A failed multi-file upload can leave a partial set and requires operator review before another attempt.
Public endpoints
Do not advertise a public endpoint until its users can detect replacement or downgrade. Publish at least the hostname and port, the daemon release, the TLS certificate identity, the server’s composite identity fingerprint, and the services and fair-use limits it offers. Whether an operator keeps logs is a policy claim that clients cannot verify remotely.
Troubleshooting
--validate checks configuration, credentials, destination policy with its
egress lists, and module sockets without opening a network connection. yume-doctor also checks the cover site and TLS
material. Both programs exit with 0 after a requested stop, 1 for a runtime
failure, and 2 for a usage or configuration error.
If a client cannot connect:
- confirm TCP reachability to the daemon’s port from the client network;
- confirm no HTTP proxy terminates TLS in front of
yumed; - check that the client’s
endpoint.hostmatches the server certificate and that client and server come from the same kit, since the admission key and TLS trust are per deployment; - check that the client’s identity is listed in
authorized-keys.jsonwith the services it opens; - check that the cover site still contains the browser profile’s assets, which
yume-doctorverifies.
If a session connects but a destination fails, the SOCKS5 reply reports a refusal or route failure without its detail. Through circuits the reply comes before the exit has answered, so such a failure closes the connection after a success reply instead. Check that the destination, and every address its name resolves to on the daemon host, lies inside the direct adapter’s configured networks and outside its deny lists, and that the client’s identity is granted that service. The daemon does not log individual opens. It reads egress lists only when it starts, so restart it after a list file changes.