Deploying to devices
A bundle goes to a Linux device as a release named by its manifest’s hash. On the device,
taktwerk agent verifies and activates releases under an install root and drives their systemd
units, or its own built-in supervisor where systemd does
not run; the host (taktwerk device, taktwerk deploy, the desktop app) calls it over SSH and
copies files by SFTP.
taktwerk device add line-1 --host 192.168.1.20 --user taktwerk --privilege sudo
taktwerk device verify line-1 # pins the host key, probes the device
taktwerk deploy plant --device line-1 # the plan
taktwerk deploy plant --device line-1 --yes
taktwerk device install-unit line-1 --yes # once, through sudo
taktwerk device start line-1 plant --yes # or --at 11:15 --yes: once, later
taktwerk device engine line-1 plant status # JSON, exit 1 unless it runs (see Operating a running engine)
taktwerk device tunnel line-1 plant # http://127.0.0.1:<port>/ to the engine
The desktop app and taktwerk serve do the same from the workbench’s
Devices page, on the same registry. The bundle file, the manifest, the
device layout, the units and the agent’s commands are specified in
Devices and bundles; a running engine is operated with the commands of
Operating a running engine.
Devices
The host keeps its devices in devices.toml in the user’s config dir (~/.config/taktwerk/ on
Linux, %APPDATA%\taktwerk\ on Windows, TAKTWERK_CONFIG_DIR overrides): how to reach each
one, its pinned host key and what the last verify found. No passwords.
[[device]]
name = "line-1"
host = "192.168.1.20"
port = 22
user = "taktwerk" # logs in, owns the install root
privilege = "sudo" # how unit changes get root: none, sudo or su
root = "/opt/taktwerk" # the install root (default)
host_key = "SHA256:…" # pinned by the first verify
[device.verified] # the last verify: written by verify, read by the app
arch = "aarch64"
glibc = "2.36"
systemd = true
engines = ["0.8.1"]
# also: at, machine, free_bytes, root_exists, root_writable, taktwerk, bundles, problems, notes
Login. An SSH agent’s identities (SSH_AUTH_SOCK; on Windows the OpenSSH agent’s pipe),
then key files (-i, else ~/.ssh/id_ed25519, id_ecdsa, id_rsa), then a password asked
for on the terminal. Nothing is stored.
Host keys. The first verify shows the device’s host key fingerprint and pins it after a
yes on the terminal, or when --accept-host-key <fingerprint> names it. Every later connection
refuses any other key. When a device was reinstalled, check its new key and pin it with
verify --accept-host-key.
Privilege. Only unit changes need root: start, stop, restart, enable, disable
and install-unit with --yes run the agent through the device’s method, and so does
creating a missing install root the login user cannot create. sudo runs with -S and asks
for the login user’s password only when sudo does; su runs on a PTY and asks for root’s.
Everything else runs as the login user, who should own the install root. Reading the journal
(logs) needs the login user in the systemd-journal group.
Verify probes over one SSH exec: uname -m, the glibc version against the engines’ floor
(2.25), systemd, whether the root exists and is writable, free space under it, installed engines
and /etc/os-release; then agent info when an engine is installed. Problems (an unsupported
architecture, no or too old glibc, a root the login user cannot use) stop a deploy; notes (no
systemd, so the built-in supervisor runs the bundles; a root the deploy will create) do not.
WSL2 distributions
On Windows a WSL2 distribution of the PC is a device without SSH: every command is a
wsl.exe -d <distro> -u <user> --exec sh -c …, every file the stdin or stdout of one, and root
is wsl -u root, which needs neither a password nor Windows admin rights. There is no host
key to pin; verify probes at once. taktwerk device add wsl-ubuntu --wsl Ubuntu (the user
defaults to the distribution’s default user) or Add as device on the Devices page writes:
[[device]]
name = "wsl-ubuntu"
transport = "wsl"
distro = "Ubuntu"
user = "fabian"
WSL stops a distribution shortly after its last wsl.exe ends. The workbench keeps each WSL
device’s distribution running while it is open; a deployed engine stops with it unless
instanceIdleTimeout=-1 is set under [general] in %UserProfile%\.wslconfig. With systemd
in the distribution ([boot] systemd=true in /etc/wsl.conf) enabled bundles start when the
distribution does; without it the built-in supervisor runs them.
Scan connects to the SSH port of every address of an IPv4 subnet (at most a /16, 64 at a time, 500 ms each) and lists the hosts that answer, with their banner. It never logs in.
Deploying a release
taktwerk deploy <bundle> --device <name> probes the device, checks the bundle for its
architecture, builds the manifest for this taktwerk version, reads the active release’s
manifest and the unit’s state, and prints the plan: the engine to install, the files to send
(those the active release lacks or holds with other content), bytes against free space, and
what stands in the way. A running unit is a problem: stop it first. With --yes:
- Create the install root if it is missing (through root when the login user cannot).
- If
bin/taktwerk-<version>is missing: upload the engine under a temporary name, make it executable, run it with--versionand refuse it unless it reports this host’s version, then rename it. - Upload the changed files to
bundles/<name>/staging/<hash>/and the manifest’s canonical bytes to.taktwerk/manifest.jsonthere. agent activate <bundle> <hash> --yes: the device takes the files the upload left out from the active release, verifies everything and switchescurrent.
Then, on the first deploy and when companions change, device install-unit --yes, and
device start <bundle> --yes, device enable <bundle> --yes to start at boot.
The engine comes from --engine <file>, else from a directory of per-architecture engines
(TAKTWERK_ENGINES, which the desktop app sets to its bundled engines/), laid out as the
release builds them: x86_64-linux-gnu/taktwerk, aarch64-linux-gnu/taktwerk,
armv7-linux-gnueabihf/taktwerk. A Linux taktwerk deploying to its own architecture can send
itself; it must have the http feature for the unit’s --http. The released device engines
are built without the host side (feature host: SSH, SFTP, device, deploy), which the
agent does not need.
Live view
taktwerk device tunnel <device> <bundle> reads the bundle’s http address from its env file
(through agent status), listens on a local port (--local, default any free loopback port)
and forwards each connection over SSH to that address on the device; it prints the URL and runs
until Ctrl-C. The write token stays on the device unless --token puts it in the URL. The
workbench forwards the same way and adds the token itself. On a WSL distribution each
connection is a wsl.exe running agent connect <address>, which pipes it to the engine, so
neither WSL’s localhost forwarding nor a Windows process on the same port is involved.
Records
The unit records with --record-dir <root>/bundles/<bundle>/records, so every file recorder of
the project writes there. taktwerk device records <device> <bundle> copies that directory by
SFTP into records/<device>/<bundle>/ of the current directory (--dir for another), where
taktwerk serve and its Records screens list the runs:
taktwerk device records line-1 plant # 12 copied (48211 bytes), 3 unchanged, in ./records/line-1/plant
- A file is copied when the local one is missing or differs: by size, then modification time,
then, for a closed file, the SHA-256 its run’s
SHA256SUMSlists. A copy is checked against that sum, written under a hidden.tmpname, given the device’s modification time and renamed into place. - Run it again at any time: it copies only what is new or grew, and continues a closed file a cut-off sync left half copied.
- A run still recording is copied as far as it is written; the readers stop at its last
complete line. Its open part is copied again while it grows, and the local
.opencopy goes once the device has closed the part. Per run the parts come first,CLOSEDlast. - Nothing on the device changes and nothing local is deleted but such an
.opencopy: parts the device’s retention removed stay. --jsonprints the report (copied,unchanged,bytes,removed). The device’s engine must haveagent records(deploy with this version).