Skip to content

Install the server

Run zidane-server from Docker, a release binary, npm, the zidane command or a checkout, then create the first token.

Every way below runs the same server.

WayGood forCommand
Docker imageServers, Compose, Coolifydocker run ghcr.io/tahul/zidane-server:8.1.0
Release binaryA VPS with systemd, no containerzidane-server start
npmA machine with Node, no install stepnpx zidane-server start
The zidane commandA machine that already has the terminal appzidane server start
From sourceWorking on zidanebun run server start

Run it#

Terminal
docker run -d --name zidane --restart unless-stopped \
  -v zidane-data:/data -p 127.0.0.1:4280:4280 \
  --memory 4g --pids-limit 2048 --security-opt no-new-privileges \
  ghcr.io/tahul/zidane-server:8.1.0

The image is public: no docker login. Images exist for linux/amd64 and linux/arm64. The agents run commands in the container: --memory and --pids-limit cap what they take from the host (raise them for heavy builds), and no-new-privileges keeps a setuid binary from gaining anything.

TagTracks
8.1.0That release. It never moves (use it)
8.1Its patch releases
8Its minor and patch releases
latestThe newest release

The release workflow publishes a pre-release under its own version only (8.2.0-beta.1), never under 8.2, 8 or latest.

The image sets ZIDANE_SERVER_BEHIND_PROXY=1, so a direct WebSocket connection to 127.0.0.1:4280 gets a 403. Put a TLS proxy in front (Compose with Caddy). Token commands and health checks run through docker exec and the unix socket, so they need no proxy.

Versions#

Pin an exact release (8.1.0), and upgrade the server and its clients together. A client and the server must share a protocol version. Each side speaks a range of them, so a client one release away usually still attaches, but a release can move the range past older clients (a patch release once changed the version, v7.0.10). On a moving tag, a plain redeploy can leave every client refused until it is upgraded too. The upgrade steps back up and wait for running work first.

What it prints at start#

One line on stderr, then a hint while no token exists:

Text
✓ zidane server listening on ws://127.0.0.1:4280 and /home/me/.config/zidane/server.sock (data /home/me/.config/zidane, pid 4242)
Next: create a client token: zidane server token create --name <device> (there is none yet. This works while the server runs.)

Without --data-dir, the server uses zidane's usual folders (where they are). A deployment passes --data-dir <dir> to keep everything under <dir>/.zidane.

The server listens on loopback by default and refuses any other interface without TLS or a proxy in front (Deploy).

Create the first token#

Create one token per device. The server prints it once on stdout and keeps only its hash.

Terminal
zidane-server token create --name laptop                    # binary
docker exec zidane zidane-server token create --name laptop # image

Keep it in a file only you can read (chmod 600). If you have no shell before the first deploy, use a bootstrap token. For scopes, rotation and revoking, see Tokens.

To run session commands on other computers, add each one as a machine. A join code, a one-time code from the server, adds it (servers and machines).

Check it runs#

On the server's machine:

Terminal
zidane-server health          # over the unix socket
zidane-server health --http   # GET /healthz on the server's port

Both exit 0 when the server is healthy (output).

Connect a window#

Clients send a token over ws:// only to 127.0.0.1 and localhost. To test from another machine before TLS is set up, open an SSH tunnel:

Terminal
ssh -L 4280:127.0.0.1:4280 box
zidane --host ws://127.0.0.1:4280 --token-file box.token

To save the server in an app or connect a browser, see Clients.

Web app#

The server can serve zidane's web app at / on its own port: a chat in the browser, with no other host to run. It is off by default. Turn it on with --web (or ZIDANE_SERVER_WEB=1, or "web": true in server.json), then log a browser in with a pairing code:

Terminal
zidane-server start --web
zidane-server pair             # prints a code like 7KQM-3XFD, valid 10 minutes

Open the server's address (https://box.example.com/, or http://127.0.0.1:4280/ through the SSH tunnel above) and type the code. The browser gets its own device token, browser: <name> in zidane-server token list. A chat code (the default) lets it chat and answer tool approvals, --scope admin gives it everything (pairing rules).

Where the app comes from:

WayApp
Docker imageBundled, in /usr/local/share/zidane-server/web-dist
Release binaryweb-dist/ in the archive. zidane server setup installs it next to the binary, and auto-update replaces both together. By hand, keep it next to the binary, or install it as /usr/local/share/zidane-server/web-dist beside /usr/local/bin/zidane-server: sudo mkdir -p /usr/local/share/zidane-server && sudo cp -R "zidane-server-v$V-$T/web-dist" /usr/local/share/zidane-server/
The zidane commandNot bundled: pass --web-root <dir>
From sourcebun run server:web builds web/ into server/web-dist/

--web-root <dir> (ZIDANE_SERVER_WEB_ROOT, webRoot in server.json) serves another build: a directory with an index.html. With --web on and no app found, the server refuses to start and says why. --no-web turns it off over the variable and the file.

The app connects back over the same origin (wss://<host>/), so it needs no entry in ZIDANE_SERVER_ALLOWED_ORIGINS. Every page carries a strict Content-Security-Policy (default-src 'self', frame-ancestors 'none'), X-Content-Type-Options: nosniff and Referrer-Policy: no-referrer. Hashed files under /assets/ are cached for a year, index.html never.