---
title: Install the T1 daemon
order: '32'
summary: "Turn any Linux, macOS, or Windows machine into a ScaiClip device \u2014\
  \ clipboard sync in the background, autostart on login."
path: tutorials/install-the-t1-daemon
status: published
---

The T1 daemon is a headless background process that hooks your OS clipboard. Copy on your laptop → paste on your phone. Copy on your phone → paste on your workstation. All encrypted end-to-end.

Runs anywhere Python runs. Install-and-forget on Linux (systemd user unit) and macOS (LaunchAgent); Windows Task Scheduler works too.

## Prerequisites

- Python 3.10 or newer with `pip`.
- A ScaiGrid API key with scopes `scaiclip:read` and `scaiclip:write`.
- Linux: one of `xclip`, `xsel`, or `wl-copy` (Wayland). The daemon auto-detects.

Install the daemon package (this is currently distributed as the ScaiGrid repo — a standalone PyPI wheel lands in a follow-up):

```bash
git clone https://github.com/scailabs/scaigrid.git
cd scaigrid
pip install -e .
```

Test the daemon runs:

```bash
export SCAIGRID_HOST=https://scaigrid.scailabs.ai
export SCAIGRID_API_KEY=sgk_...
python -m modules.scaiclip.tools.t1_daemon --profile $(hostname) &
```

You should see the daemon connect, subscribe to your personal channel, and start watching the clipboard. Copy something and check the log — `[pub] seq=1 len=N recips=1`.

## Linux — systemd user service

The daemon ships a hardened systemd unit file:

```bash
# Copy the unit to your user's systemd dir.
mkdir -p ~/.config/systemd/user/
cp modules/scaiclip/tools/installers/scaiclip-daemon.service \
   ~/.config/systemd/user/

# Put your API key in the environment file.
mkdir -p ~/.config/scaiclip/
cat > ~/.config/scaiclip/env <<'EOF'
SCAIGRID_HOST=https://scaigrid.scailabs.ai
SCAIGRID_API_KEY=sgk_your_key_here
EOF
chmod 600 ~/.config/scaiclip/env

# Enable + start.
systemctl --user daemon-reload
systemctl --user enable --now scaiclip-daemon.service

# Check status.
systemctl --user status scaiclip-daemon.service
journalctl --user -u scaiclip-daemon.service -f
```

The unit is hardened — restricted paths, no new privileges, memory + CPU caps. Reads its API key from `~/.config/scaiclip/env` rather than the unit file so you can rotate keys without touching systemd.

## macOS — LaunchAgent

```bash
# Copy the plist to your LaunchAgents dir.
cp modules/scaiclip/tools/installers/com.scailabs.scaiclip.plist \
   ~/Library/LaunchAgents/

# Edit and set your SCAIGRID_API_KEY in the plist.
open -e ~/Library/LaunchAgents/com.scailabs.scaiclip.plist

# Load + start.
launchctl load ~/Library/LaunchAgents/com.scailabs.scaiclip.plist

# Check logs.
tail -f /tmp/scaiclip-daemon.err.log
```

## Windows — Task Scheduler

Windows-native support is coming; for now use Task Scheduler:

1. Open Task Scheduler → **Create Task**.
2. **General:** name `ScaiClip Daemon`. Check "Run whether user is logged on or not" if you want autostart before login; otherwise leave default.
3. **Triggers:** add "At log on".
4. **Actions:** `Start a program`. Program: `python`. Arguments: `-m modules.scaiclip.tools.t1_daemon --profile %COMPUTERNAME%`. Start in: your ScaiGrid repo root.
5. **Environment:** set `SCAIGRID_HOST` and `SCAIGRID_API_KEY` in the user's `System Properties → Environment Variables` before creating the task.

Log output goes to whatever console the task runs in; redirect to a file in the Arguments field if you want persistence.

## Enrolling a second device

Once the daemon runs on device A, enrol device B by running the daemon there with a distinct `--profile` name (usually the machine's hostname). First launch calls `POST /devices/me` to register B's keys with ScaiKey. Both devices' KEM public keys land in the directory; the next clip published on either device gets wrapped for both.

If device B was offline when A first published a clip, that specific clip won't be readable on B — its wrap wasn't included. Future clips will be. The onboarding-broker flow that back-fills clips for late-joining devices is on the roadmap.

## Verifying it works

Copy a distinctive string on device A. Within ~350 ms plus network RTT, the same string should appear in device B's clipboard. Paste it into a text editor to confirm.

If nothing happens:

```bash
# Both devices — check daemon logs.
journalctl --user -u scaiclip-daemon.service -n 50    # Linux
tail -50 /tmp/scaiclip-daemon.err.log                 # macOS

# Look for [pub] on the sender, [recv] on the receiver.
```

Common causes:

- **`no OS clipboard backend detected`** — install `xclip` or `wl-copy` on Linux. macOS/Windows should work out of the box.
- **`[warn] directory lookup failed`** — API key doesn't have `scaiclip:read` scope, or the ScaiGrid endpoint isn't reachable.
- **`[recv] no matching wrap`** — the publishing device didn't include a wrap for the receiver. Usually means the receiver enrolled after the clip was published; try copying something new.

## Uninstall

**Linux:**

```bash
systemctl --user disable --now scaiclip-daemon.service
rm ~/.config/systemd/user/scaiclip-daemon.service
rm -rf ~/.config/scaiclip/
rm -rf ~/.scaiclip/     # device keys — deletes device from local; ScaiKey still lists it as revoked
```

**macOS:**

```bash
launchctl unload ~/Library/LaunchAgents/com.scailabs.scaiclip.plist
rm ~/Library/LaunchAgents/com.scailabs.scaiclip.plist
rm -rf ~/.scaiclip/
```

To also delete the device from your ScaiKey account, use `DELETE /v1/modules/scaiclip/devices/me/{device_id}` — the daemon will refuse to reconnect after that.

## Optional tray UI

The tray UI is a PySide6 GUI companion that embeds the daemon in a background thread. Shows a system-tray icon with:

- Recent clips (last 20) — click to re-copy.
- Pause/resume publish toggle.
- Notifications on inbound clips.
- About + quit.

Install PySide6 and run the tray instead of the daemon:

```bash
pip install PySide6
python -m modules.scaiclip.tools.t1_tray --profile $(hostname)
```

Without PySide6 installed, the tray script falls back to the headless daemon with a friendly message.

For persistent autostart with the tray, replace the `ExecStart=` line in `scaiclip-daemon.service` with `-m modules.scaiclip.tools.t1_tray` (Linux) or edit the plist arguments accordingly (macOS).

## Image clip support

The daemon publishes and receives PNG images on Linux via `xclip` or `wl-copy`. Copy an image → daemon detects, encrypts, uploads to scratch storage, publishes the envelope with a `ref_scratch` pointer. Receiver fetches from scratch, decrypts, writes the PNG back to the OS clipboard.

macOS + Windows image read/write is a follow-up (NSPasteboard + DataFormats gymnastics).

## Team-channel onboarding

The daemon fetches team-channel keys on connect (`key.wraps.get`). If no wrap exists for its device yet, the onboarding broker fires `wrap.request` to sibling devices. Any sibling running the daemon responds with `key.wraps.put`, the grid pushes `wraps.ready`, and the new daemon refetches. Result: fully automatic team-channel onboarding across devices.

## What the daemon doesn't do yet

- **No file clips.** Only text and PNG images. File-URI clipboard integration varies wildly across desktop environments and is deferred.
- **No macOS/Windows image support.** Text works everywhere; image read/write on those platforms needs native APIs.
- **No T2 native platform integration.** Deeper OS notifications, deeper platform hooks — separate track.

## What's next

- [Two-device round trip](./two-device-round-trip.md) — the manual client walkthrough the daemon is a productionized version of.
- [Channels, envelopes, wraps, claims](../concepts/channels.md) — what the daemon is doing under the hood.
- [Troubleshooting](../troubleshooting.md) — common error paths.