Skip to content
ThinkWatch
Start typing to search the docs.
ThinkWatch Core · Server deployment

Running core on a server#

ThinkWatch Core runs without a desktop: on a Linux machine it is started by systemd from its configuration file, and ThinkWatch Lite on macOS, Windows or Linux connects to it over the network to show traffic and change settings. Clients anywhere on the network send their requests to the server’s gateway.

This page covers installing, configuring, starting, connecting and upgrading. Every field mentioned is described in the configuration reference.

Requirements#

  • Linux on x86_64 or aarch64, with glibc 2.35 or newer (Ubuntu 22.04, Debian 12, or later).
  • systemd.
  • On the server, the core version that the desktop app includes. The app connects only to a core that speaks the same version of the control-plane protocol: it checks this during the handshake and, when the versions differ, refuses the connection and shows both, the version on the server and the version the app needs. Upgrading describes how to switch versions.

1. Install#

curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh

This installs the latest release, which can be newer than the core version the desktop app includes. To install a particular version, pass it to the script:

curl -fsSL https://raw.githubusercontent.com/ThinkWatchProject/ThinkWatch-Core/main/scripts/install.sh | sudo sh -s -- --version <version>

<version> is a release number such as 0.47.0. When the server runs a different version, the app names the version it needs, and twcore upgrade --version switches the server to it.

The script:

  1. downloads twcore-<arch>-unknown-linux-gnu.tar.gz from the GitHub release and checks it against the release’s SHA-256 sum;
  2. installs the binary as /usr/local/bin/twcore;
  3. creates the system user thinkwatch and the data directory /var/lib/thinkwatch (mode 0700);
  4. installs /etc/systemd/system/twcore.service and an empty /etc/thinkwatch/env;
  5. runs twcore init as thinkwatch if there is no configuration yet;
  6. prints the next steps. It does not start the service.

Running it again is safe: it replaces the binary and the unit, and leaves the configuration, the environment file and the data alone. For later upgrades, twcore upgrade is the shorter way (see below).

To install by hand instead, download the tarball and its .sha256 from the releases page, check it with sha256sum -c, and follow the steps above; the unit file is in the tarball and in packaging/systemd/twcore.service.

Every twcore command that reads the configuration has to run as the service user with the service’s data directory. The examples below spell that out; a shell alias saves typing:

alias twc='sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch twcore'

2. Configure#

Open /var/lib/thinkwatch/config.yaml as root (sudoedit works) and change three things:

  1. Let clients on the network reach the gateway: listen.gateway.bind: all, and list their networks in listen.gateway.allow_from.

  2. Open the remote control port: listen.control.remote.enabled: true, and list the networks the desktop app connects from in its allow_from. twcore init writes this section with enabled: false and a random port between 20000 and 32000. The same can be done with a command, which also writes the section if the file has none:

    sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch twcore remote enable --allow 192.168.1.0/24

    --allow can be repeated and replaces the list; --bind and --port change the interface and the port. twcore remote disable closes the port again and keeps the rest, and twcore remote shows the current state. A running core follows within a second.

  3. Add at least one upstream under providers, or add it later from the desktop app.

version: 1
listen:
  gateway:
    bind: all
    port: 8788
    allow_from: [192.168.1.0/24]
  control:
    key: 9f2c…e41a            # written by twcore init; leave it as it is
    remote:
      enabled: true
      bind: all
      port: 23483             # written by twcore init, at random
      allow_from: [192.168.1.0/24]
clients:
  - name: default
    key: tw-…                 # written by twcore init
providers:
  - name: anthropic
    base_url: https://api.anthropic.com
    key: ${ANTHROPIC_API_KEY}

Check the result without starting anything:

sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch twcore check

Secrets in the environment#

${NAME} in the configuration reads the environment of the core process. Under systemd that is /etc/thinkwatch/env, one NAME=value per line:

sudoedit /etc/thinkwatch/env      # created by the installer: root:thinkwatch, 0640
ANTHROPIC_API_KEY=sk-ant-…
HTTPS_PROXY=http://proxy.example.com:3128

The file is read when the service starts: after changing it, restart the service. Proxy variables here are what proxy: system uses.

Network#

Neither port has TLS. The control port is encrypted and authenticated by its handshake; the gateway port carries requests in plain HTTP, like any local model server. Keep both reachable only from networks you trust: set allow_from, and open the two ports in the server’s firewall to those networks only. For access from outside, use a VPN or an SSH tunnel rather than exposing the ports.

3. Start#

sudo systemctl enable --now twcore
systemctl status twcore
journalctl -u twcore -f

The unit runs core as thinkwatch, restarts it if it fails, and keeps it out of the rest of the file system: it can write only its data directory.

Configuration changes need no restart. Core reloads the file within a second of a save, from an editor, twcore config, or the desktop app; a change that does not validate is refused and the previous configuration keeps serving.

4. Connect the desktop app#

Show the control key on the server:

sudo -u thinkwatch THINKWATCH_HOME=/var/lib/thinkwatch twcore control-key
9f2c…e41a
remote control: port 23483; connect to 192.168.1.20:23483
allowed sources: 192.168.1.0/24

The first line, the key, is the only thing on standard output, so $(twcore control-key) in a script gets just the key. The two lines after it go to standard error: the port, the addresses of this server’s interfaces that listen on it, and the allowed sources. When the port is closed the second line reads remote control: off (twcore remote enable opens it).

In the desktop app, open Settings → Connection → Add remote connection and enter:

  • Name: a name for the connection, such as home-server;
  • Address: the server’s host name or IP address;
  • Control port: listen.control.remote.port;
  • Key: the 64 characters twcore control-key printed.

Test connection completes the handshake and reads the server’s core version, without saving anything. Save and switch runs the same test first, saves the connection only when the test succeeds, and asks for confirmation before switching; Save stores the connection without testing it. Switching to a remote connection always tests it first, and the app stays on its current connection when the test fails. A failed test says what is wrong: no answer (address, port, firewall, enabled), connection closed (this computer’s address is probably not in allow_from), wrong key, or different versions (see Upgrading). allow_from for this port does not let the server itself in automatically; commands on the server use the local channel.

A source that fails the handshake five times within a minute is ignored for a minute. Removing a network from allow_from also closes the connections already open from it.

The desktop app stores the key in its data directory, in a file readable only by the user who runs the app. To replace it, run twcore control-key --rotate on the server; connections made with the old key are closed at once, and connected apps then have to be given the new key.

A remote connection can do everything the app does with its local core except three things, which the server refuses: stopping core (systemd runs it), taking the diagnostic bundle, and changing listen.control, the section it came in through. Do those on the server. ChatGPT accounts are signed in with a device code while the app is connected to a remote core, because a browser sign-in returns to the machine that runs core.

Point clients at the server#

Clients use the server’s gateway, http://<server>:8788, with a gateway key from clients. The desktop app can point the clients on the machine it runs on at the server (Clients page); on other machines, configure them by hand.

Upgrading#

The server has to run the core version that the desktop app includes. When the two differ, for example after the app updates to a release that includes a newer core, the app refuses the connection and shows both versions. Switch the server to the version the app needs:

sudo twcore upgrade --version <version> --restart

With --version, twcore upgrade installs the named release even when it is older than the installed one, so the same command moves the server forward or back. Recent versions of the app show this command, with the version filled in, when the versions differ.

Without --version, twcore upgrade compares with, and installs, the latest release, which can be newer than the version the app needs:

sudo twcore upgrade --check       # compare with the latest release, change nothing
sudo twcore upgrade --restart     # install the latest release and restart the service

twcore upgrade downloads the release for this machine, checks its SHA-256 sum, and replaces /usr/local/bin/twcore in one step, so a failed download never leaves a broken binary. It does not touch the configuration or the data. Without --restart it prints the command to restart the service; the running process keeps the old version until then. A version that stores request history in a different format from the previous one starts with an empty request history; the configuration is kept.

Uninstalling#

sudo systemctl disable --now twcore
sudo rm /etc/systemd/system/twcore.service /usr/local/bin/twcore
sudo systemctl daemon-reload
# The configuration, keys and request history:
sudo rm -r /var/lib/thinkwatch /etc/thinkwatch
sudo userdel thinkwatch

This page is published from docs/server.md in the ThinkWatch Core repository, at v0.56.0.