Skip to content

CLI reference

The runwisp binary is the daemon, the TUI launcher, the config scaffolder, and a small handful of one-shot helpers. Run it with no arguments to start the daemon and attach an interactive TUI; pass a subcommand for everything else.

All subcommands share the same set of global flags and honour the same environment variables.

The two-verb model: exec runs a task in this CLI process (no daemon needed); run-task asks a running daemon to start one. They never collide — exec refuses if a daemon is already attached to the data dir, and run-task requires one.

CommandWhat it does
runwisp (no args)Spawn the daemon if not running and attach the TUI. The everyday command.
runwisp daemonHeadless start for systemd / Docker.
runwisp cloudHeadless start in cloud mode. Requires RUNWISP_CLOUD_TOKEN.
runwisp tuiAttach a fresh TUI to an already-running daemon.
runwisp exec <task>Run a task in this process (no daemon), stream its output.
runwisp run-task <task>Trigger a run via the running daemon’s REST API.
runwisp listPrint configured tasks as a table. Reads runwisp.toml only.
runwisp statusHit the daemon’s /health endpoint. Exit 0 = alive.
runwisp initScaffold an annotated runwisp.toml and a random password.
runwisp validateParse and check runwisp.toml without starting anything.
runwisp openapiPrint the OpenAPI 3.1 spec for the daemon’s REST API to stdout.

There is no runwisp reload. To pick up changes to runwisp.toml, restart the daemon. The reload-without-restart story is on the roadmap; today it is restart-only.

Terminal window
runwisp

What it does, in order:

  1. Resolves the password — RUNWISP_PASSWORD env var, then data/password, else generates one and writes it to data/password.
  2. Pings http://<host>:<port>/health.
    • Already running: connects and attaches the TUI.
    • Port held by something else: prints what’s holding it and exits 1.
    • Port free: spawns a background daemon, waits up to 10s for it to become healthy, then attaches the TUI.

Quitting the TUI does not stop the daemon — the daemon was spawned in the background and keeps running. Use Ctrl+C while the daemon is attached inline (i.e., the spawn fell back to inline mode) or kill it explicitly. The TUI’s own status footer tells you which mode you’re in.

Headless start. Runs the scheduler, REST API, and Web UI without an attached TUI — the right verb for systemd ExecStart, Docker CMD, and anywhere else stdin isn’t a terminal. For an attached TUI, run runwisp with no subcommand instead.

Terminal window
ExecStart=/usr/local/bin/runwisp daemon --config /etc/runwisp/runwisp.toml --data /var/lib/runwisp

Exits with status 1 on startup error (port in use, invalid config, unwritable data dir). Otherwise runs until SIGTERM / SIGINT.

Headless start in cloud mode — the daemon connects outbound to a RunWisp cloud control plane (or any peer that speaks the AsyncAPI protocol) for observability push and ad-hoc dispatch. The local cron scheduler is not started; scheduling is owned by the peer.

runwisp cloud [--token <token>] [--url <url>] [--env-file <path>] [--no-tui]
FlagTypeDefaultWhat it does
--tokenstring(env)Cloud token (overrides RUNWISP_CLOUD_TOKEN).
--urlstring(env)Cloud API URL (overrides RUNWISP_CLOUD_URL). Defaults to the production endpoint.
--env-filestring.envPath to a dotenv file. Loaded into the process environment before flag resolution.
--no-tuiboolfalseRun headless. Logs to stderr instead of attaching a UI.

Refuses to start if RUNWISP_CLOUD_TOKEN is unset after env-file load. The cloud connection retries with backoff on transport failure and never blocks task execution — see Prime Directive #4 (offline-complete).

Attach a fresh TUI to a daemon that’s already running on --host:--port. Useful when SSHing in to inspect a daemon started by systemd / docker run -d.

runwisp tui [--password <password>]
FlagTypeDefaultWhat it does
--passwordstring(empty)CHAP password. Falls back to RUNWISP_PASSWORD, then data/password.

Exits 1 if the daemon is unreachable, the password is wrong, or auth is rate-limited.

runwisp exec <task-name>

Runs the task inside the CLI processnot against the running daemon. The CLI:

  • loads runwisp.toml,
  • instantiates an in-process executor and task manager,
  • triggers the task,
  • streams stdout/stderr line by line to your terminal,
  • exits with the task’s exit code.

This makes exec the right command for one-off testing and CI smoke runs.

runwisp exec opens the local SQLite store directly. If a daemon is already running on the same data dir, the command refuses with an error — two writers on one SQLite file silently corrupts state, so the daemon’s PID file is checked before anything else happens. If you need to fire a task at the running daemon, use runwisp run-task instead.

runwisp run-task <task-name>

Asks the running daemon to start a fresh run for <task-name> via the REST API. The CLI authenticates with the resolved password (RUNWISP_PASSWORDdata/password), POSTs the trigger, and prints the new run’s ULID. It does not stream output — for that, attach the TUI (runwisp tui) or open the Web UI.

The complement to runwisp exec: exec runs in-process and refuses when a daemon is up; run-task requires a daemon to be reachable.

runwisp list

Loads runwisp.toml (no daemon required) and prints a table:

NAME SCHEDULE CONCURRENCY POLICY API DESCRIPTION
backup-postgres 0 3 * * * 1 queue yes Nightly logical dump
api-worker (service x3) 1 skip yes Three workers consuming the job queue
deploy-hook (manual) 1 skip yes Triggered after each release
  • SCHEDULE shows the cron expression for tasks, (service xN) for services with instances = N, or (manual) for tasks without a cron.
  • API reflects api_triggerno means the task can only fire on its cron, never from CLI/UI/REST.
  • DESCRIPTION is truncated to 50 characters.

Exit 0 on success, 1 if the file can’t be parsed.

Pings /health first; on success, fetches /api/system for the version, uptime, host, and CPU summary.

runwisp status
RunWisp is healthy at :9477
Version: 0.4.0
Uptime: 3h 12m
CPU: 4 cores
Host: builder-01

Exit 0 if the daemon answers 200 OK on /health, 1 otherwise. The extra metadata is best-effort — if /api/system is unreachable or unauthorised, only the first line prints. Useful in shell scripts and Docker HEALTHCHECK lines:

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD runwisp status || exit 1

Scaffold a runwisp.toml with worked examples and generate a one-time random password.

runwisp init [--force]
FlagShortTypeDefaultWhat it does
--force-fboolfalseOverwrite an existing runwisp.toml.

Refuses to clobber an existing file without --force. The generated password is printed once on stdout — copy it somewhere safe; the daemon also writes it to data/password on first run.

runwisp validate

Parses runwisp.toml, runs the full schema validation, and exits. Does not start the daemon, touch the database, or open ports. Pair it with a pre-commit hook or CI job:

Terminal window
runwisp validate --config ./runwisp.toml

Exit 0 if valid, 1 on parse or schema error. The error message names the offending key.

Pretty-prints the OpenAPI 3.1 spec for the daemon’s REST API to stdout. The same file is committed at apps/runwisp/openapi.json; this command is useful for producing it fresh from a development build.

Terminal window
runwisp openapi > openapi.json

Pipe it into a code generator (openapi-typescript, oapi-codegen, etc.) to drive a typed client.

Every subcommand accepts these — they bind on rootCmd and persist.

FlagShortTypeDefaultWhat it does
--config-cstringrunwisp.tomlPath to the TOML config file.
--datastringdataData directory — SQLite, log files, JWT secret, password file, PID file.
--port-pint9477HTTP port for REST API + Web UI + SSE log streams.
--hoststring127.0.0.1Bind address. Use 0.0.0.0 to expose on the LAN; pair with a reverse proxy.

Both --config and --data accept relative or absolute paths. Relative paths are resolved against the current working directory at startup — not the binary’s location, and not the directory holding runwisp.toml.

VariableWhat it overridesNotes
RUNWISP_PASSWORDThe login password used by Web UI / TUI / API.Highest precedence: env > data/password > generated.
RUNWISP_FINGERPRINTThe daemon’s instance fingerprint.Otherwise read from SQLite, then computed from machine-id + cwd.
RUNWISP_TRUST_PROXYTrust X-Forwarded-Proto from a reverse proxy.Comma-separated CIDR list (e.g. 127.0.0.1/32,::1/128). 0.0.0.0/0 rejected.
GOMEMLIMITGo runtime soft heap limit.Defaults to 128MiB to keep the daemon RAM-frugal on small VPSes.

RunWisp follows POSIX convention.

CodeMeaning
0Success.
1Generic CLI error — config invalid, daemon unreachable, password wrong, etc.
Nrunwisp exec only: returns the underlying task’s exit code on failure.

The TUI propagates exit codes from the daemon process when running in-line.

#!/usr/bin/env bash
set -euo pipefail
runwisp validate --config ./runwisp.toml
runwisp exec smoke-test --config ./runwisp.toml

validate ensures the file parses, then exec runs the task in-process and the script exits with the task’s exit code — useful for catching shell errors in run = """…""" blocks before deploying the file to a running daemon.