Linux agent
On Ubuntu or Debian desktop, install the .deb and open
SpadeIDE Agent from the app grid — tray icon, window, pairing QR,
the same idea as the Mac menu-bar app. On a server, unpack the tarball and run
./install.sh (one account) or sudo ./install.sh --system
(shared host — each iPad signs in with a Unix user).
Which install should I use?
| Your situation | What to install | What the iPad does |
|---|---|---|
| Only you use this machine; Ubuntu / Debian desktop | .deb → SpadeIDE Agent from the app grid |
Scan QR → open a folder under your home. No Unix password. |
| Only you use this machine; server / SSH box | Tarball → ./install.sh |
Same: pair once, then only your $HOME. |
| Several people share one Linux host (or you want each tablet bound to a different Unix account) | Tarball → sudo ./install.sh --systemor enable the system unit after a .deb |
Scan QR → Unix Login once (username + password) → that account’s home only. |
./install.sh.
Desktop app
The package installs an icon, a .desktop launcher, and a GTK window that
talks to the agent over the loopback control API. If AppIndicator is available it also
sits in the tray; otherwise you still get a normal window.
sudo apt install ./spadeide-agent_1.0.0_amd64.deb
spadeide desktop # or click SpadeIDE Agent in Activities
Closing the window hides it to the tray. Quit leaves the agent running under systemd. Launch at login in the window controls whether the tray comes back when you sign in.
Install
Ubuntu / Debian (.deb)
sudo apt install ./spadeide-agent_1.0.0_amd64.deb
# desktop: open SpadeIDE Agent from the app grid, or:
spadeide desktop
Server (tarball)
The tarball is the right artifact for Ubuntu Server, Debian, and other systemd hosts.
Copy it over with scp, unpack, then run install.sh.
scp spadeide-agent-1.0.0-linux-amd64.tar.gz user@server:~/
ssh user@server
tar xzf spadeide-agent-1.0.0-linux-amd64.tar.gz
cd spadeide-agent-linux-amd64
./install.sh # this user → ~/.local/bin + systemd --user
sudo loginctl enable-linger "$USER" # keep running after logout
# or, privileged broker (shared host; each tablet Unix-logs in):
# sudo ./install.sh --system
sudo ./install.sh --system is a privileged broker: systemd
runs the agent as root, state lives in /var/lib/spade-agent, and each iPad
must sign in with a Unix username and password once. After that it only sees that
account’s home. See User vs system.
Any distribution (single binary)
# bare binary from the download page, if you are not using the tarball
chmod +x spadeide-agent-linux-amd64
sudo install -o root -g root -m 0755 spadeide-agent-linux-amd64 /usr/local/bin/spadeide
spadeide
sudo spadeide to start the listener.
Interactive root is refused. A user install runs as you. A system install is the
systemd unit (User=root, documented as risky) — talk to it with
sudo systemctl status spade-agent and
sudo spadeide pair --no-start.
Running it as a service
For a desktop machine, the user service starts the agent when you log in. For a server you want reachable without logging in, use the system service.
As your own user (recommended on a single-user desktop)
This is what the Ubuntu / Debian .deb enables by default. Same behaviour
on Ubuntu Desktop as on a laptop: one account, jailed to that $HOME, no
Unix login sheet.
systemctl --user enable --now spade-agent
systemctl --user status spade-agent
# keep it running after you log out
sudo loginctl enable-linger "$USER"
As a system service
Works on Ubuntu Desktop too when the machine is shared: several people each get their
own home after Unix login. Stop the user unit first so it does not fight for the port.
The unit is a privileged broker (User=root, state in
/var/lib/spade-agent). Pairing is still the QR. The first connect after
pair asks for a Unix username and password on the iPad. Wrong credentials drop the
session. Right credentials bind that tablet to that account (username only — never
the password). Later connects skip the form until the account is deleted or the
password changes.
chown them back. The
terminal is still that Unix user, never uid 0. Unix root cannot log in
from the iPad. Interactive sudo spadeide is refused. Needs
unix_chkpwd from libpam-modules (default on Ubuntu).
systemctl --user disable --now spade-agent # if a user install was running
sudo mkdir -p /var/lib/spade-agent && sudo chmod 700 /var/lib/spade-agent
sudo systemctl enable --now spade-agent
sudo systemctl status spade-agent
sudo journalctl -u spade-agent -f
hash -r # if bash still looks in ~/.local/bin
sudo spadeide pair --no-start # QR + status via the control API
User install vs system install
User (./install.sh) | System (sudo ./install.sh --system) | |
|---|---|---|
| Process | Your Unix account | systemd User=root (privileged broker) |
| State | ~/.spade-agent |
/var/lib/spade-agent |
| Who can use it | Paired iPads, jailed to your $HOME |
Each paired iPad Unix-logs in once, then is jailed to that home. Deleted user / password change → login again. |
| Root-owned files in home | Cannot rewrite (no extra privilege) | Writable; then owned by the Unix user so the PTY sees them as theirs |
| Terminal | Your login shell | That Unix user’s login shell — never root |
File API vs /etc |
Rejected | Rejected. A terminal as pi can still ls /etc; the editor cannot open it. |
This is not a kernel chroot. Pairing is still device identity. Unix login is extra on a system install. A stolen password plus a paired iPad is that Unix account. If you delete that Unix user, lock it, or change its password, the tablet must enter username and password again — the deleted account cannot sign in.
On the iPad — what you will see
-
Pair — scan the QR from
spadeide/sudo spadeide pair --no-start(system install) or the desktop window. This stores the host fingerprint; it is not a Unix password. -
Unix Login (system install only) — a sheet asks for the host
username and password. Sent over the already-encrypted session (TLS / QUIC);
the password is never stored on the iPad or in the agent state. Wrong password
disconnects. Unix
rootis refused. -
Open a project — pick a folder under that user’s home. The file
tree and editor stay inside the home. The terminal is that user’s normal login
shell (same groups as SSH — e.g.
dockerif they are in that group).
Later connects skip the Unix form until the account is deleted or its password
changes. Re-pairing is only needed if you wipe agent identity
(certs/ / state dir).
Day to day
| Task | What to run |
|---|---|
| Show QR / pair another tablet (user install) | spadeide |
| Show QR (system install) | sudo spadeide pair --no-start |
| Upgrade the agent from a new tarball | ./reinstall.sh — or sudo ./reinstall.sh if it was
--system. Keeps pairings; same layout as before. |
| Changed a Unix password / deleted a user | On the next connect the iPad asks for Unix login again. No host command needed. |
| Forget one tablet | spadeide unpair NAME (user) or via the system control CLI as root |
| Status / logs | spadeide status · journalctl -u spade-agent -f (system)
· journalctl --user -u spade-agent -f (user) |
sudo spadeide. If systemd is already
running the unit, that command is refused (or used to fight for the same port).
Command reference
| Command | What it does |
|---|---|
spadeide | Start if needed, then show the pairing QR and URL |
spadeide desktop | Open the tray + window (Ubuntu / Debian .deb) |
spadeide status | Paired and online tablets, without minting a new code |
spadeide devices | List paired tablets |
spadeide unpair <id|name> | Remove one tablet. Matches a unique id prefix or name |
spadeide reset | Forget every paired device |
spadeide remote on|off | Allow or deny reachability from outside your network. Default off |
spadeide remote status | Show the current remote-access setting and introducer |
spadeide stop | Stop the background agent |
spadeide serve --foreground | Run attached — this is what systemd calls |
spadeide version | Print the version |
serve flags
| Flag | Default | Meaning |
|---|---|---|
--port | 7422 | TLS port your iPads connect to |
--control-port | 7423 | Loopback control API port |
--foreground | off | Stay attached instead of daemonising. Required under systemd |
Environment variables
| Variable | Default | Meaning |
|---|---|---|
SPADE_AGENT_PORT | 7422 |
Same as --port. Useful under systemd where flags are fixed in the unit |
SPADE_AGENT_CONTROL_PORT | 7423 |
Same as --control-port |
SPADE_CONTROL_TOKEN | auto | Bearer token for the loopback control API. If unset, the agent mints one at
startup and writes it to ~/.spade-agent/control.token (mode 0600)
or, on a system install, /var/lib/spade-agent/control.token.
The API never runs unauthenticated. |
SPADE_AGENT_HOME | derived | Override the state directory. The system unit sets this to
/var/lib/spade-agent. |
SPADE_AGENT_MODE | unset | Set to system by the privileged systemd unit. Enables Unix
login and home jail. Interactive root without this is still refused. |
SPADE_RENDEZVOUS_URL | product default | Override the introducer for every remote connection. Same effect as
spadeide remote on --server wss://…, but from the environment |
SPADE_DISABLE_MDNS | unset | Set to 1 to stop advertising over mDNS. On macOS the menu-bar app
sets this because it advertises through the system Bonjour responder instead |
Ports and firewall
| Port | Bind | Purpose |
|---|---|---|
7422/tcp | All interfaces | TLS listener for your iPads |
7423/tcp | 127.0.0.1 only | Control API — token-gated, never reachable from the network |
5353/udp | Multicast | mDNS discovery, so the iPad can find the host by name on your LAN |
If you run a firewall, allow the agent port on your local network only:
sudo ufw allow from 192.168.0.0/16 to any port 7422 proto tcp
sudo ufw allow 5353/udp
spadeide remote on) — the agent dials out, so nothing has to be exposed.
Port forwarding puts your host's TLS listener in front of the whole internet for no
benefit.
LAN discovery
The iPad finds the agent over mDNS (_spadeide._tcp), matching the
certificate fingerprint it pinned at pairing. On systems running
avahi, the agent publishes through avahi-publish-service
rather than fighting the daemon for UDP 5353:
sudo apt install avahi-daemon avahi-utils
Without avahi it falls back to its own responder. If discovery fails entirely, the iPad still connects using the last known address, and you can always paste the pairing URL by hand.
Files on disk
| Path | Contents |
|---|---|
~/.spade-agent/state.json | Paired devices and their public keys, remote-access setting |
~/.spade-agent/certs/ | The agent's TLS certificate and key — this is its pinned identity |
~/.spade-agent/identity | Ed25519 key behind the agent id used for remote access |
~/.spade-agent/control.token | Control-API token, mode 0600 |
~/.spade-agent/agent.log | Log, mode 0600 |
~/.spade-agent/agent.pid | PID of the background agent |
/var/lib/spade-agent/ | Same files, for a --system install (pairings, identity, token) |
certs/ changes the agent's identity, and every paired iPad will
refuse to connect until you pair again. That is the pinning working as intended.
Remote access
Off by default. While it is off, the agent makes no outbound connections at all.
spadeide remote on # use the hosted introducer
spadeide remote on --server wss://rv.example.com # or your own
spadeide remote status
spadeide remote off
With it on, the agent holds one outbound connection to the introducer and heartbeats every 30 seconds. That connection carries no file data — it exists so a remote iPad can be introduced to you. See hosting your own.
Reinstalling on a server
From a new tarball, the short path is ./reinstall.sh — it keeps the same
user or system layout and leaves pairings alone. Or replace the binary by hand below.
Leave ~/.spade-agent / /var/lib/spade-agent alone unless you
want every iPad to pair again.
Which layout do you have?
Use the same method you used the first time. These three are the usual ones:
| How you installed | Binary | Service | State |
|---|---|---|---|
Tarball ./install.sh |
~/.local/bin/spadeide |
user unit | ~/.spade-agent |
Tarball sudo ./install.sh --system |
/usr/local/bin/spadeide |
system unit, User=root |
/var/lib/spade-agent |
Ubuntu / Debian .deb |
/usr/bin/spadeide |
system unit or --user |
the service user's ~/.spade-agent |
1. See what is already running
which -a spadeide
spadeide version
spadeide status
systemctl is-active spade-agent
systemctl --user is-active spade-agent
If the system unit is active, you installed with --system or enabled the
packaged spade-agent unit. If only the user unit is active, you used
./install.sh. After a system install, show the QR with
sudo spadeide pair --no-start — never start a second listener as root.
2. Stop the old agent
Two copies on the same ports fight: one holds the lock, the other restart-loops. Stop before you overwrite the file.
spadeide stop
sudo systemctl stop spade-agent
systemctl --user stop spade-agent
Those are harmless if a unit is not installed.
3. Put the new binary in the same place
Grab the matching artifact from the download page, copy it onto the server, then install over the existing path.
Server tarball — same layout as before
scp spadeide-agent-1.0.0-linux-amd64.tar.gz user@server:~/
ssh user@server
tar xzf spadeide-agent-1.0.0-linux-amd64.tar.gz
cd spadeide-agent-linux-amd64
./reinstall.sh # or: sudo ./reinstall.sh if it was --system
reinstall.sh detects whether you used a user or system install, stops
the agent, puts the archive binary in the same place, and restarts that unit.
Pairings stay in ~/.spade-agent or /var/lib/spade-agent.
Server tarball — first install / force a layout
./install.sh # this user
sudo ./install.sh --system # privileged broker
Ubuntu / Debian .deb
sudo apt install ./spadeide-agent_1.0.0_amd64.deb
Then restart the same unit you already had (step 4).
Bare binary
sudo install -o root -g root -m 0755 spadeide-agent-linux-amd64 /usr/local/bin/spadeide
If which spadeide pointed at ~/.local/bin/spadeide, copy
there instead — do not mix the two locations.
4. Restart the same service you had
install.sh already starts the unit it installed. For a .deb
or a bare-binary copy, restart so systemd loads the new file — a process that was
already running keeps the old binary in memory until it is restarted.
sudo systemctl restart spade-agent
sudo systemctl status spade-agent
# or, for a user install:
systemctl --user restart spade-agent
systemctl --user status spade-agent
If you never used systemd and just ran spadeide in a terminal, stop it and start it the same way.
5. Check
spadeide version # or: /usr/local/bin/spadeide version
sudo spadeide pair --no-start
spadeide status
version should match the build you just installed. status
should list the same paired tablets. The iPad reconnects by itself — you do not pair
again.
~/.spade-agent (or /var/lib/spade-agent for a system
install), not inside the binary.
Clean reinstall (new identity)
Only if you want a brand-new agent that no existing iPad will trust. Deleting
certs/ changes the pinned identity; wiping the whole directory also
forgets the device list and the remote-access setting.
spadeide stop
sudo systemctl stop spade-agent
systemctl --user stop spade-agent
rm -rf ~/.spade-agent # or: /var/lib/spade-agent
Then install and start as above, and pair every iPad again.
~/.spade-agent as part of a version upgrade. Do not run
sudo spadeide to start a second listener. Do not leave an old process
running while you start the new one.
Uninstalling
sudo systemctl disable --now spade-agent
sudo apt remove spadeide
rm -rf ~/.spade-agent /var/lib/spade-agent # removes pairings and identity too