berthdocs

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) and tmux (sessions)
  • a Tailscale address, unless you choose to listen elsewhere (see Where berthd listens)
  • optional: the agent CLIs you want to run (claude, codex, …), and cloudflared for 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 | sh

It 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:

OptionEnvironment
--version vX.Y.ZBERTH_VERSIONInstall that release instead of the latest
--listen ADDRBERTHD_LISTENWhere berthd listens (default: the box's tailnet address; see below)
--no-pairBERTH_NO_PAIR=1Don't print a pairing link at the end
--yes, -yBERTH_YES=1Ask nothing (see below)
--systemPut 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_DIRPut 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-box

It:

  1. opens one SSH connection (asking any password or host-key question once; if your keys are refused it tries 1Password's SSH agent);
  2. checks the box's platform with uname;
  3. uploads the matching berthd-<os>-<arch> from beside berth to ~/.local/bin/berthd (make all builds the Linux ones);
  4. runs berthd install, which sets berthd up as a user service (systemd on Linux, launchd on macOS);
  5. runs berthd pair and 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:

FlagWhat it does
--name NWhat to call the box in Berth (default: its hostname). It becomes part of URLs like 3000.N.localhost.
--network NETReach the box through one of your other tailnets.
--listen ADDRPassed to berthd install, for a box without a tailnet address.
--address ADDRThe address the laptop should dial, when the one berthd guesses is wrong.
-- OPTIONSAnything 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:

  1. On the box, run the install command, with a copy button.
  2. 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 installer

Pairing 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 personal

Each 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

Next

Your first project and worktree.

On this page