No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Stormzady b6c20e019c Merge pull request 'main' (#1) from Stormzady/scr:main into main
Reviewed-on: #1
Reviewed-by: Bingo <bingopond@gmail.com>
2026-10-29 13:41:36 +00:00
.gitignore scr: tiny service runner (scrd + scr CLI) with tmux sessions, runners and dependencies 2026-10-29 13:05:34 +00:00
LICENSE Add MIT license (Nytrix Labs) 2026-10-29 13:19:04 +00:00
README.md Update README.md 2026-10-29 13:39:49 +00:00
scr scr: tiny service runner (scrd + scr CLI) with tmux sessions, runners and dependencies 2026-10-29 13:05:34 +00:00
scr-logo.png Upload files to "/" 2026-10-29 13:37:36 +00:00
scrd README for public release; generic service unit; tmux per service, new runners, derive/external 2026-10-29 13:18:00 +00:00
scrd.service README for public release; generic service unit; tmux per service, new runners, derive/external 2026-10-29 13:18:00 +00:00

SCR Logo

A small service runner for a single Linux box. scrd is a daemon that systemd starts; it launches your services, restarts them when they crash, and keeps each one in its own tmux session. scr is the command line you use to control it.

scr register aire /srv/aire/main.py --runner uv --user bot --check http://127.0.0.1:8089/health
scr enable aire          # start now and at boot
scr status
scr send aire say hello  # type into the service's console
scr attach aire          # open its tmux session (detach with Ctrl-b d)

It is one Python file for the daemon and one for the CLI, standard library only. It was written for a Raspberry Pi (Debian 13, Python 3.13, tmux 3.5a) and has been tested there only.

Features

  • Restart with backoff. A crashed service restarts after 1 s, 2 s, 4 s ... up to 60 s. Five crashes in a row mark it failed and scr stops trying. A run longer than 30 s resets the counter.
  • Dependencies. --after caddy starts the other service first and waits until it is running (and its check passes). Stopping a service stops everything that depends on it.
  • Health checks. --check port:8090, --check http://127.0.0.1:8089/health, --check unit:caddy.service or --check proc:<user>:<pattern>. scr check <service> -j prints JSON and exits 0 only if the service is running and healthy.
  • A tmux session per service. scr attach and scr send work on every service that scr runs itself. There is one tmux server per service user.
  • Runs services as other users. --user drops privileges before the service starts.
  • Two sockets, so ordinary users can look and touch their own services but cannot change the setup (see Security).

Requirements

Linux, Python 3 (tested on 3.13), tmux, and systemd (only to start scrd at boot and for the systemd runner). Run scrd as root if you want --user to work; as a normal user it can only run services as that same user.

Install

sudo install -m 755 scrd /usr/local/bin/scrd
sudo install -m 755 scr  /usr/local/bin/scr
sudo install -m 644 scrd.service /etc/systemd/system/scrd.service
sudo systemctl daemon-reload
sudo systemctl enable --now scrd

scrd.service sets a PATH. Edit it so it includes the folders that hold the programs your services need (for example ~/.local/bin for uv).

Files:

What Where
Service list /etc/scr/services.json (written on every change)
Logs /var/log/scr/<name>.log
Sockets /run/scr/root.sock, /run/scr/user.sock
tmux sockets /run/scr/tmux/<user>/tmux.sock

The paths can be changed with the environment variables SCR_DIR, SCR_LOG and SCR_RUN (handy for testing; unix socket paths must stay under about 100 characters).

Commands

Command Does
scr register <name> <target> [options] add a service (root only)
scr enable <service> / disable start now and at boot / stop and don't start at boot
scr start / stop / restart <service> control a service
scr status [-j] table of all services, or JSON
scr check <service> [-j] state and health of one service
scr logs <service> [-n N] saved log plus the live screen
scr attach <service> open the tmux session
scr send <service> <text...> [--no-enter] type text (and Enter) into the service
scr remove <service> stop and delete it (root only)

<service> is a name or an ID. IDs are four digits (0001, 0002 ...), assigned in order, or chosen with --id. They are never reused.

Register options

--runner, --user (default root), --after SERVICE (repeatable), --check SPEC, --args "...", --cwd DIR, --label TEXT (display name), --stop-timeout SECONDS (grace after SIGTERM before SIGKILL, default 15), --id, plus --task (npm), --venv (python), --mem (java).

Runners

Runner Runs
exec (default) the target through /bin/sh -c
uv uv run <target> in the target's folder
npm npm run <--task>
python python3 <target>, or <--venv>/bin/python
node node <target>
java java [-Xmx<--mem>] -jar <target>
cargo cargo run --release in the target folder
systemd wraps an existing unit: scr starts, stops and watches it, systemd restarts it (no tmux session)
external not run by scr, only reported from its --check
derive started by another service (the target is that parent's name or ID); scr never starts it, and it counts as running only while the parent runs and its check passes

--args is appended after the program (after -- for npm and cargo).

Security model

  • /run/scr/root.sock is mode 600 and root only. It accepts every command.
  • /run/scr/user.sock is open to all local users, and checks who is calling with SO_PEERCRED. It allows status and check for every service, and logs, start, stop, restart, attach and send only for services that run as the caller. It cannot register, enable, disable or remove anything, so it cannot be used to run code as another user.
  • Services run in tmux sessions owned by their own user. Anyone who can attach to a session can type into it, so only the owning user and root can.
  • scr send types into the console exactly as given. Treat it like a shell for whatever the service is.

Limits

  • State such as restart counts lives in memory only. After a reboot scrd reads the service list and starts the enabled ones.
  • Stopping scrd stops the services it started.
  • The log file is written when a process exits or is stopped (from the tmux scrollback, up to 20 000 lines), not continuously. Use scr logs or scr attach for live output.
  • Not tested: scr attach from a real terminal on every setup, and very large numbers of services.

Contributing

Issues and pull requests welcome. There is no test suite yet; the daemon is written to be tried with dummy services under a throwaway SCR_DIR/SCR_RUN/SCR_LOG.

License

MIT, see LICENSE. Copyright (c) 2026 Nytrix Labs.