Tuning Skywire for privacy or performance¶
Skywire is built on cryptographic identity: every visor is a public key, and every connection between visors is an encrypted transport to that key — there is no plaintext traffic. On top of transports, the routing layer carries your application traffic as source-routed, per-hop-forwarded packets. That routing layer is where you choose where on the privacy ↔ performance spectrum you want to sit — and it is almost entirely a matter of configuration.
This guide explains the levers and gives concrete recipes for three points on that spectrum: maximum performance (the default), balanced, and maximum privacy.
Defaults favor performance
Out of the box a visor is tuned for throughput and low latency, and to be a useful, rewards-eligible member of the network. Everything below is opt-in hardening for operators who want to trade some performance for privacy.
The three kinds of privacy¶
It helps to separate what you are actually trying to protect:
| Dimension | The concern | Where it's addressed |
|---|---|---|
| Data privacy | Can anyone read my traffic? | Always encrypted (Noise, post-quantum-hardened); some transport types are double-encrypted. Nothing to configure. |
| IP privacy | Which keys learn my machine's IP address? | Which visors you make transports to / accept transports from, and whether you register in the address resolver. |
| Metadata privacy | Can an observer correlate who is talking to whom / traffic patterns? | Hop count, multiplexed & rotating routes, exit-node choice. |
Most operators only care about performance. IP and metadata privacy are niche but real use cases, and Skywire supports pushing them hard.
Data privacy — already handled¶
Every transport carries a Noise-XK session (mutually authenticated by public key) with an ML-KEM post-quantum hybrid handshake. Several transport types are additionally double-encrypted — the Noise session rides on top of an already-encrypted transport (QUIC/TLS bound to the static key, WebTransport, or WebRTC/DTLS):
| Encryption | Transport types |
|---|---|
| Single (Noise over plaintext base) | stcpr, sudph, stcp, swsr (native ws) |
| Double (Noise + transport TLS/DTLS) | squicr, swtr (WebTransport), webrtc, swsr from a browser (wss) |
| Relay (end-to-end Noise via a dmsg server) | dmsg |
You do not configure this — it is on for all traffic. You can prefer
double-encrypted carriers with transport_preference (below).
The levers¶
All of these live in the visor config (skywire.json). Knobs that also have a
config gen / autoconfig flag are noted; the rest are set by editing the
config JSON (or skywire cli config gen and hand-editing).
Being reachable — public_autoconnect, --public, ar_transport_limit¶
--publicregisters the visor in the public-visor service discovery so others autoconnect to it. A public visor advertises reachability.public_autoconnect(defaulttrue) runs the periodic loop that builds transports to public visors — the mesh backbone. Disabling it (config gen --disable-public-autoconn) makes the visor connect only to peers you set up.ar_transport_limitcontrols the address-resolver (AR) entry — the record that lets an arbitrary visor resolve your PK to an address and dial you:0(default): stay registered — reachable inbound.N > 0: deregister after N transports exist.N < 0: never register with the AR at all — no one can look up your address, so no one can initiate a transport to you (and thus learn your IP) unless you dialed them first.
Rewards
A visor with public autoconnect disabled is not rewards-eligible. Max-privacy setups are deliberately outside the rewards model.
What transports the visor will create — no_direct_transports, transport_create_deny, persistent_transports¶
persistent_transports— a list of{pk, type}the visor always maintains (recreated across restarts). This is how you say "only ever connect to these specific, trusted visors."no_direct_transports(bool) — never create a direct peer-to-peer transport (stcpr/sudph/squicr/swsr/swtr/webrtc); dmsg (a relay, not direct p2p) is still allowed. A direct transport reveals your IP to the peer; disabling direct creation means you reach others only over the mesh / dmsg.transport_create_deny— advanced per-type deny list (may includedmsg), for forcing a pure-direct or fully-offline posture (mainly for testing).
How routes are built — min_hops, multiplexing, rotation¶
min_hops(visor-wide):0disables routing;1allows a direct 1-hop route when a transport exists;≥ 2forces every route through at least N intermediate visors, so no single hop sees both ends.- Multiplexed routes (
--mux N/--routes Nper app): split a flow across N disjoint routes. No single route carries the whole conversation, which is both a throughput win and a metadata win. This is the single best "both worlds" setting. - Route rotation (via routing policy): periodically drop and re-establish muxed legs across the eligible-peer set, so even the set of intermediates a flow uses changes over time — strong metadata protection.
- Asymmetric forward/reverse (
--forward-*/--reverse-*, per-dial): the forward and reverse directions can use different routes — e.g. a single direct forward leg for the request with a 4-way multiplexed multihop reverse for a bulk download. Supported per-dial; not applied automatically (see notes).
Which carriers to prefer — transport_preference¶
Order the transport types the route-finder and dialer prefer, e.g. to favor
double-encrypted squicr/swtr, or to avoid a family your network blocks.
Recipes¶
Maximum performance (default)¶
Nothing to do. Public autoconnect on, min_hops 1 (direct where possible),
single routes, all carrier families available. For heavy flows, add
multiplexing per app:
skywire cli vpn start --srv <server-pk> --mux 4 # 4 parallel routes
skywire cli proxy start --srv <server-pk> --routes 4
Multiplexing gives you more throughput and better metadata privacy at once — recommended for most people who want a little of both.
Balanced¶
Keep autoconnect/rewards, but push metadata privacy with multihop multiplexed, rotating routes for sensitive apps:
Add a routing policy with rotation for periodic leg turnover (see the routing-policy RFC).
Maximum privacy¶
The goal: no one learns your IP, and no observer can correlate your traffic. Combine the levers:
- Only connect to trusted visors. Pin them in
persistent_transportsand setno_direct_transports: trueso the visor never dials a direct p2p transport to anyone else. The only visors that ever learn your IP are the trusted ones you chose. - Be invisible inbound. Do not run
--public, and setar_transport_limit: -1so you never register in the address resolver — no one can resolve your PK to an address, so no one can initiate a transport to you and learn your IP. - Reach deployment services through a non-public dmsg server. Connect to a dmsg server that itself is not publicly listed and that relays to the rest of the deployment's dmsg servers, so those servers see the relay's IP, not yours. (Advanced/emerging — see status below.)
- Route with depth + spread + rotation. For any app traffic, use
--min-hops 4(or 5) with--routes 4and a rotating routing policy, so the full traffic is never on one path and the path set changes over time — defeating traffic correlation. - Rotate exits. When using a skysocks exit for clearnet, change the exit node intermittently.
This is deliberately off the beaten path
A max-privacy visor is not public, not rewards-eligible, and slower. It only
works if the trusted peers you pin are actually up and reachable. Verify your
connectivity (skywire cli tp ls, skywire cli route ls) after locking it
down.
Beyond the visor: anonymizing the dmsg client itself¶
The strongest posture treats even the visor's own dmsg control connection as something to anonymize. A dmsg client can be pointed at a dmsg server through a visor's skysocks-client — so the dmsg server sees the skysocks exit's IP, not the client's. With a sufficiently deep, multiplexed, rotating route to the exit (4–5 hops), traffic correlation back to the origin is not feasible, and the exit can be rotated intermittently. This is the logical end state of the recipe above and is an active area of work.
A note on asymmetric routing¶
Skywire's route groups already support different forward and reverse routes
(ForwardMinHops/ReverseMinHops, ForwardMuxRoutes/ReverseMuxRoutes), and
the data plane handles the two directions having different leg counts. This is
exposed per-dial today but is not chosen automatically — a dial uses the same
shape both ways unless you ask otherwise. Whether the visor should
automatically pick an asymmetric shape (e.g. a light direct forward leg for
requests + a fat multiplexed reverse for downloads) is an open design question;
for now it is a manual/policy tuning lever.
Feature status¶
Most levers here are established and shipping:
- ✅
public_autoconnect/--public,ar_transport_limit,min_hops,persistent_transports,transport_preference, per-app--mux/--routes/--min-hops,no_direct_transports/transport_create_deny, routing policy with rotation. - 🧪 Non-public dmsg server (relay-only, unregistered) and dmsg-client-over-skysocks egress are emerging / partially implemented — treat as advanced and verify behavior before relying on them for a threat model.
Config vs. flags
The privacy knobs (ar_transport_limit, min_hops, no_direct_transports,
transport_create_deny, transport_preference, persistent_transports) are
set today by editing skywire.json. config gen / autoconfig flags for
them are being added so the install-command generator and skywire autoconfig
can set them directly.
See also¶
- manual-routing.md — building routes by hand
- routing_policy_rfc.md — the per-dial routing-policy DSL (rotation, min-hops, distribution)
- socks5.md / vpn.md — the client apps whose flags carry the per-dial tuning