A ten-minute walkthrough: install Switchboard, start a router in E-mode, create an SVTN, publish a tmux session from one machine, and connect a console from another.
Audience: operators bringing up their first Switchboard deployment. Target release: v0.1.0-rc.1.
If you're looking for the full CLI surface, jump to docs/sbctl.md; for the concepts behind the pieces you're about to install, see docs/architecture.md.
A tmux session on one machine, your screen and keyboard on another, and a router in between that carries the encrypted circuit without being able to see inside it. On a single LAN today (the current MVP scope, E-mode), across the internet as PE routing matures — the shape is the same:
graph LR
subgraph laptop["operator laptop"]
SB["sbctl<br/>(§3, §4, §7)"]
CN["console daemon (§6)"]
end
R["router, E-mode :9090 (§2)<br/>blind relay for the circuit<br/>+ SVTN 'hello-svtn' (§3)"]
subgraph work["machine hosting the work"]
AN["access node daemon<br/>(§5)"]
TM["tmux session 'work'"]
AN --- TM
end
CN -- "keystrokes" --> R -- "keystrokes" --> AN
AN == "terminal output" ==> R == "terminal output" ==> CN
SB -. "admin: create SVTN,<br/>register keys" .-> R
SB -. "attach / detach" .-> CN
The thick arrows are the deployment's purpose — the session stream.
The dotted arrows are you operating it with sbctl; that's most of
what a tutorial does, but keep the distinction in view: the steps
below build the thick arrows.
Three keys, three roles (§3–§4): a control key for you the operator, an access key for the machine publishing the session, a console key for the laptop attaching to it. All three live in the SVTN — the named trust domain everything else refers to.
A note on words: a daemon is any long-running switchboard
process, named by its mode — the router daemon, the access daemon, the
console daemon. Each serves a management socket; sbctl (short-lived,
not a daemon) connects to one of those sockets per command. When this
tutorial says "start the daemon," it means the switchboard <mode>
process you just configured.
The step order is dependency order:
graph LR
S2["§2 router up"] --> S3["§3 create SVTN<br/>+ control key"] --> S4["§4 register access<br/>+ console keys"] --> S5["§5 publish session<br/>(access node)"] --> S6["§6 attach<br/>(console)"] --> S7["§7 observe paths<br/>+ metrics"]
You need:
- A
switchboardbinary — install via Homebrew (alpha channel) or build from source. See "1. Install" below. - just — the task runner, required
only for the from-source build.
brew install juston macOS. - Go 1.25+ — required only for the from-source build.
- tmux on the machine that will host the session.
- An Ed25519 SSH key pair for each participant (operator, access
node, console).
ssh-keygen -t ed25519produces one.
Two machines make the tutorial more interesting, but everything works on one machine with two terminals if that's what you have.
Alpha builds are cut from every push to develop, signed + notarized
on macOS, and published to the shared arcaven tap:
brew tap ArcavenAE/tap
brew install ArcavenAE/tap/switchboard-a
switchboard-a --versionThe binaries are installed as switchboard-a and sbctl-a — not switchboard / sbctl —
so they can live side-by-side with the canonical formula slots on the same tap. Also install
sbctl-a:
brew install ArcavenAE/tap/sbctl-a
sbctl-a --versionIf brew install ArcavenAE/tap/sbctl-a reports "No available formula", run brew update to
refresh the tap. Substitute switchboard-a for switchboard and sbctl-a for sbctl
in every command in the rest of this tutorial if you install this way.
git clone https://github.com/ArcavenAE/switchboard-blue.git
cd switchboard-blue
just buildThis produces bin/switchboard (the daemon) and bin/sbctl (the
operator CLI). Both binaries are single-file — copy or symlink them
onto $PATH on each machine that needs them:
sudo install bin/switchboard bin/sbctl /usr/local/bin/Confirm:
switchboard --versionThe router is the transport plane. In E-mode (edge-local, single-LAN deployment) it needs almost no config.
Write switchboard-router.yaml:
listen_addr: "0.0.0.0:9090"
management_socket: "/run/switchboard-router.sock"
# Timeslice tick — required. Allowed range: [5ms, 50ms].
# 10ms is a good starting value for interactive sessions.
tick_interval: 10ms
# E-mode: no upstream routers
upstream_routers: []Start the daemon:
sudo switchboard router --config switchboard-router.yamlThe router logs its listen address and management socket path on stderr (stdout is reserved for structured output). Leave it running.
The very first management call to a fresh daemon must use the daemon's bootstrap key. The daemon prints a bootstrap public key on first startup; store its matching private key in a safe place — you will need it to create SVTNs.
From an operator machine that can reach the router:
sbctl \
--target=/run/switchboard-router.sock \
--key=~/.ssh/switchboard-bootstrap \
admin svtn create --name=hello-svtnYou should see:
SVTN created:
svtn_id: a1b2c3d4e5f60102
bootstrap_fingerprint: SHA256:...
Save the svtn_id; you will paste its short-id prefix into confirmation
prompts later. The bootstrap_fingerprint is what SVTN control keys
verify against for emergency recovery.
Add your day-to-day operator key as a control-role key in the SVTN:
sbctl \
--key=~/.ssh/switchboard-bootstrap \
admin key register \
--svtn=hello-svtn \
--key="$(cat ~/.ssh/id_ed25519.pub)" \
--role=control \
--confirm=<paste svtn short-id>From now on you can use ~/.ssh/id_ed25519 for admin work; keep the
bootstrap key offline as your recovery credential.
The access node publishes tmux sessions. The console attaches to them. Each needs its own key registered in the SVTN with the appropriate role:
# Access node
sbctl \
--key=~/.ssh/id_ed25519 \
admin key register \
--svtn=hello-svtn \
--key="$(ssh-keygen -y -f ~/.ssh/switchboard-access)" \
--role=access \
--confirm=<svtn short-id>
# Console (operator laptop)
sbctl \
--key=~/.ssh/id_ed25519 \
admin key register \
--svtn=hello-svtn \
--key="$(ssh-keygen -y -f ~/.ssh/switchboard-console)" \
--role=console \
--confirm=<svtn short-id>Confirm the key set:
sbctl --key=~/.ssh/id_ed25519 admin list-keys --svtn=hello-svtnYou should see three entries: control, access, console.
On the machine that will host tmux, start a session:
tmux new -s work
# ...do some work in the session...In another terminal on the same machine, start the access daemon:
# switchboard-access.yaml
upstream_router: "10.0.0.1:9090" # the router's listen addr
node_key: "/etc/switchboard/access.key"
svtn: "hello-svtn"switchboard access --config switchboard-access.yamlThe access node authenticates to the router (using
/etc/switchboard/access.key), attaches to the running tmux server, and
advertises its published sessions. work will now appear in the SVTN's
session list.
Verify from the operator machine:
sbctl --key=~/.ssh/id_ed25519 sessions list --svtn=hello-svtnYou should see work listed.
On the operator laptop:
# switchboard-console.yaml
upstream_router: "10.0.0.1:9090"
node_key: "/home/me/.ssh/switchboard-console"
svtn: "hello-svtn"switchboard console --config switchboard-console.yamlThe console daemon dials the router, authenticates, and idles. In another terminal, attach to the remote session:
sbctl --key=~/.ssh/switchboard-console console attach --session=workYour terminal is now driving the remote tmux session. Detach with:
sbctl console detachCongratulations — you have a working Switchboard SVTN.
Observe path health from the operator side:
sbctl --key=~/.ssh/id_ed25519 paths list --svtn=hello-svtn
sbctl --key=~/.ssh/id_ed25519 router metrics --svtn=hello-svtnrtt_p99_ms may show "pending" for the first few seconds — that's
expected until ten RTT samples have been collected. See
docs/architecture.md — Multi-path routing.
Revoke the console key when you're done:
sbctl --key=~/.ssh/id_ed25519 admin key revoke \
--svtn=hello-svtn \
--key="$(ssh-keygen -y -f ~/.ssh/switchboard-console)" \
--role=consoleOr destroy the whole SVTN (requires the confirmation gate):
sbctl --key=~/.ssh/id_ed25519 admin svtn destroy \
--name=hello-svtn \
--confirm=<svtn short-id>--confirm=<svtn short-id> guards against typos; a non-interactive
script can pass --yes instead, but never both — see
docs/sbctl.md — Confirmation and non-interactive use.
E-NET-001on the first sbctl command — the router isn't listening, or--targetdoesn't point where you think. Checkmanagement_socketin the router config.E-ADM-010— the operator key is not registered in the SVTN. Confirm withsbctl admin list-keys.E-CFG-013— a scripted invocation reached a confirmation gate. Either pass--confirm=<svtn short-id>(the safe form) or--yes(bypass with warning).E-CFG-008on console-mode startup — a console-mode management socket bound to a non-loopback TCP address. Use a Unix socket or a loopback bind (127.0.0.1:<port>).E-CFG-001: tick_interval: value 0s is outside allowed range [5ms, 50ms]— thetick_intervalfield is required and was omitted (or set to zero). Addtick_interval: 10msto the router config (see §2 above).
Every error carries a stable taxonomy code — see docs/errors.md for the full catalog and their handling recommendations.
- Run the examples ladder — docker-compose topologies from a single router up to four nodes + console and a two-team isolation matrix, using the published alpha binaries.
- Skim docs/architecture.md to understand SVTNs, timeslice framing, half-channels, and multi-path routing.
- Read the full docs/sbctl.md reference for every verb, flag, and JSON schema.
- Contribute — CONTRIBUTING.md covers dev workflow, commit conventions, and CI.