Getting started
Add a box
Install berthd on a development machine with one line and pair with its link, or do both over SSH.
A box is any machine your agents run on, usually Linux: a VPS, a cloud
VM, the desktop under your desk. It runs berthd, Berth's daemon, as a user
service.
The laptop pairs with it once and from then on talks to it over pinned mutual
TLS; SSH is used at most once, to install.
What a box needs
- Linux (or macOS) on amd64 or arm64
git(repositories and worktrees) andtmux(sessions)- a Tailscale address, unless you choose to listen elsewhere (see Where berthd listens)
- optional: the agent CLIs you want to run (
claude,codex, …), andcloudflaredfor public shares
With the install command
On the box, as the user your agents will run as (no root needed):
curl -fsSL https://berthd.app/install | shIt downloads the latest berthd for the box's platform from GitHub, checks
it against the release's checksums.txt, puts it in ~/.local/bin, and runs
berthd install, which sets it up as a systemd user service (a launchd agent
on macOS), the same step berth add ssh runs. Then it prints a link:
berthd v0.1.0 is running. Paste this link into Berth on your laptop
(Add a box). It works once, for ten minutes:
berth://100.101.102.103:7444?code=…&fp=…
Or on your laptop: berth pair 'berth://100.101.102.103:7444?code=…&fp=…'On the laptop, paste it in the app (Add a box, under "Paste what it printed"; pasting all of the output is fine), or:
bin/berth pair 'berth://100.101.102.103:7444?code=…&fp=…' [--name my-box]The link carries the box's fingerprint, so the laptop knows exactly which box
to trust, and a single-use code that expires after ten minutes. For another
laptop, or once it expires, run berthd pair on the box for a fresh one
(--ttl changes the expiry, up to an hour). How the code is proven without
being sent is in the security model.
Installer options
Pass options after sh -s --, for example
curl -fsSL https://berthd.app/install | sh -s -- --listen 0.0.0.0:7444:
| Option | Environment | |
|---|---|---|
--version vX.Y.Z | BERTH_VERSION | Install that release instead of the latest |
--listen ADDR | BERTHD_LISTEN | Where berthd listens (default: the box's tailnet address; see below) |
--no-pair | BERTH_NO_PAIR=1 | Don't print a pairing link at the end |
--yes, -y | BERTH_YES=1 | Ask nothing (see below) |
--system | Put berthd in /usr/local/bin (the default when run as root); only the copy uses sudo, and berth upgrade then can't replace it, so upgrade by running the installer again | |
BERTH_BIN_DIR | Put berthd somewhere else |
It asks at most two things, and only on a terminal: whether to listen on
every interface when the box has no tailnet address (no, unless you say so),
and on Linux whether to turn on lingering so berthd keeps running after you
log out (sudo loginctl enable-linger $USER). With --yes, or with no
terminal to ask on, it never listens on every interface unless --listen
says so, and turns lingering on only if sudo needs no password.
Run it again any time: it upgrades berthd in place and restarts the service
(agent sessions keep running), keeping the address it listens on unless
that was a tailnet address. It stops with a plain reason, and changes
nothing, when the download doesn't match its checksum, the platform isn't
amd64 or arm64 Linux or macOS, curl/wget or tar is missing, or systemd
has no user session for you (log in as that user directly, over SSH, rather
than through su or sudo). The script is
site/install.sh;
read it before piping it to sh if you like.
Over SSH
If you'd rather not log in to the box, and you can ssh me@my-box, one
command on the laptop installs and pairs:
bin/berth add ssh me@my-boxIt:
- opens one SSH connection (asking any password or host-key question once; if your keys are refused it tries 1Password's SSH agent);
- checks the box's platform with
uname; - uploads the matching
berthd-<os>-<arch>from besideberthto~/.local/bin/berthd(make allbuilds the Linux ones); - runs
berthd install, which sets berthd up as a user service (systemd on Linux, launchd on macOS); - runs
berthd pairand pairs this laptop with the link it prints.
Paired with my-box at 100.101.102.103:7444. SSH is no longer needed for this box.Options:
| Flag | What it does |
|---|---|
--name N | What to call the box in Berth (default: its hostname). It becomes part of URLs like 3000.N.localhost. |
--network NET | Reach the box through one of your other tailnets. |
--listen ADDR | Passed to berthd install, for a box without a tailnet address. |
--address ADDR | The address the laptop should dial, when the one berthd guesses is wrong. |
-- OPTIONS | Anything after -- goes to ssh. |
Keep it running after you log out
berthd install warns when user lingering is off, because systemd then
stops user services at logout. Turn it on once with
sudo loginctl enable-linger $USER.
In the app
Add a box (in Settings → Boxes, the sidebar's Projects menu, or ⌘K), and the first-run setup, show one screen:
- On the box, run the install command, with a copy button.
- Paste what it printed: the link, or the whole output; Berth keeps the link. Enter pairs.
Below them, Or let Berth set it up over SSH takes user@host (Tab
completes hosts from ~/.ssh/config and machines on your tailnet), says
which SSH agent or keys it will use before connecting, and runs
berth add ssh with its output as it goes. When a box can't be reached and
might be on a tailnet this computer isn't signed in to, it offers to sign in
to that tailnet and tries again.
Where berthd listens
By default berthd listens on the box's tailnet address, port 7444, and refuses to start if the box has none. To accept connections from the internet, say so:
berthd install --listen 0.0.0.0:7444 # by hand
curl -fsSL https://berthd.app/install | sh -s -- --listen 0.0.0.0:7444 # with the installerPairing is still required, and every connection still pins both keys; the
flag only changes who can attempt a handshake. berthd doctor warns about it.
Boxes on another tailnet
A laptop's own Tailscale can only be on one tailnet. For boxes on another (a personal tailnet while the laptop is on work's), the laptop agent runs an embedded Tailscale node per network:
bin/berth network login personal # a one-time browser sign-in
bin/berth discover --network personal # machines there that could be boxes
bin/berth add ssh me@homelab --network personalEach box remembers the network it is reached through. Berth's own TLS still runs on top; the tailnet only provides the route.
Check it
bin/berth boxes # paired boxes and whether they are online
bin/berth ping my-box # it answers, and still trusts this laptop
bin/berth doctor my-box