SpadeIDE

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 situationWhat to installWhat 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 --system
or enable the system unit after a .deb
Scan QR → Unix Login once (username + password) → that account’s home only.
Start with a user install unless you need multi-user. A system install is a privileged broker (the listener runs as root). Safer default: one person, one account, ./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
Do not run 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.

This is risky by design: the listener has root DAC so it can rewrite root-owned files inside that user’s home, then 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

  1. 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.
  2. 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 root is refused.
  3. 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. docker if 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

TaskWhat 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)
Never start a second agent with sudo spadeide. If systemd is already running the unit, that command is refused (or used to fight for the same port).

Command reference

CommandWhat it does
spadeideStart if needed, then show the pairing QR and URL
spadeide desktopOpen the tray + window (Ubuntu / Debian .deb)
spadeide statusPaired and online tablets, without minting a new code
spadeide devicesList paired tablets
spadeide unpair <id|name>Remove one tablet. Matches a unique id prefix or name
spadeide resetForget every paired device
spadeide remote on|offAllow or deny reachability from outside your network. Default off
spadeide remote statusShow the current remote-access setting and introducer
spadeide stopStop the background agent
spadeide serve --foregroundRun attached — this is what systemd calls
spadeide versionPrint the version

serve flags

FlagDefaultMeaning
--port7422TLS port your iPads connect to
--control-port7423Loopback control API port
--foregroundoffStay attached instead of daemonising. Required under systemd

Environment variables

VariableDefaultMeaning
SPADE_AGENT_PORT7422 Same as --port. Useful under systemd where flags are fixed in the unit
SPADE_AGENT_CONTROL_PORT7423 Same as --control-port
SPADE_CONTROL_TOKENauto 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_HOMEderived Override the state directory. The system unit sets this to /var/lib/spade-agent.
SPADE_AGENT_MODEunset Set to system by the privileged systemd unit. Enables Unix login and home jail. Interactive root without this is still refused.
SPADE_RENDEZVOUS_URLproduct default Override the introducer for every remote connection. Same effect as spadeide remote on --server wss://…, but from the environment
SPADE_DISABLE_MDNSunset 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

PortBindPurpose
7422/tcpAll interfacesTLS listener for your iPads
7423/tcp127.0.0.1 onlyControl API — token-gated, never reachable from the network
5353/udpMulticastmDNS 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
Do not forward 7422 from the internet. Turn on remote access instead (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

PathContents
~/.spade-agent/state.jsonPaired 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/identityEd25519 key behind the agent id used for remote access
~/.spade-agent/control.tokenControl-API token, mode 0600
~/.spade-agent/agent.logLog, mode 0600
~/.spade-agent/agent.pidPID of the background agent
/var/lib/spade-agent/Same files, for a --system install (pairings, identity, token)
Deleting 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 installedBinaryServiceState
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.

Pairings survive a reinstall. The identity and device list live in ~/.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.

Do not delete ~/.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