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:
- downloads
twcore-<arch>-unknown-linux-gnu.tar.gzfrom the GitHub release and checks it against the release’s SHA-256 sum; - installs the binary as
/usr/local/bin/twcore; - creates the system user
thinkwatchand the data directory/var/lib/thinkwatch(mode0700); - installs
/etc/systemd/system/twcore.serviceand an empty/etc/thinkwatch/env; - runs
twcore initasthinkwatchif there is no configuration yet; - 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:
-
Let clients on the network reach the gateway:
listen.gateway.bind: all, and list their networks inlisten.gateway.allow_from. -
Open the remote control port:
listen.control.remote.enabled: true, and list the networks the desktop app connects from in itsallow_from.twcore initwrites this section withenabled: falseand 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--allowcan be repeated and replaces the list;--bindand--portchange the interface and the port.twcore remote disablecloses the port again and keeps the rest, andtwcore remoteshows the current state. A running core follows within a second. -
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-keyprinted.
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.