Added
GET /api/tasks/{taskName}returns a single task without paging through the full task list.
Changed
- The CLI command that runs a task once is now
runwisp run <task>(wasrunwisp exec), matching therun =config key and freeingexecfrom colliding withcompose_mode = "exec". - Disable authentication with
RUNWISP_AUTH=off(wasRUNWISP_NO_AUTH=1), matching thetls = "off"convention.RUNWISP_AUTHandRUNWISP_PASSWORDremain mutually exclusive. [notify]retention keys are nowkeep_notificationsandkeep_for(werehistory_keepandhistory_keep_for), matching thekeep_runs/keep_forvocabulary tasks already use.[notify]knobsretry_budgetandkeep_occurrences(weredefault_timeoutandoccurrence_ring) name the outbound retry cap and the coalesce-occurrence count by what they do rather than by their implementation.- A compose block’s import strategy is now
import = "services" | "stack"(wasmode), so it reads distinctly from a task’scompose_mode. - Run timestamps on the API are now
startedAt/endedAt(werestartAt/endAt), including the?sortField=startedAtvalue — matching the database columns and the neighbouringcreatedAt. - The task parameter field is
allowCustomon the REST API (wasallow_custom), matching the API’s camelCase convention. The TOML key staysallow_custom. nextRunAton the tasks API is an RFC3339 timestamp (was a pre-formatted string), like every other timestamp field.- Metrics samples use
timestamp/cpuUsage/memUsage(werets/cpu/mem), matching the system-stats fields. - The unread-count stream event is
notification.unreadCountChanged(wasnotifications.unread_count_changed), matching the othernotification.*events. - List endpoints share one
{ items, … }envelope —GET /api/tasks,/api/runs,/api/system/metrics, and log search (wasruns/hits/ bare arrays). - Per-run REST endpoints are addressed by run ID alone —
/api/runs/{runId}and its/stop,/log,/log/raw,/log/stream, and/log/line/{lineNumber}/historysub-paths (were nested under/api/tasks/{taskName}/runs/{runId});/api/tasks/{taskName}/runsstill lists a task’s runs. The daemon log stream moves to/api/daemon/log/stream. - Hard-kill is spelled
killin both policies —on_overlap = "kill"(wasterminate) andlog_on_full = "kill"(waskill_task). - Auth endpoints use one noun per concept — login is
POST /api/auth/login(wasPOST /api/auth), and a launch ticket is both minted (POST) and redeemed (GET) at/api/auth/launch-ticket(redeem wasGET /api/auth/launch). - Daemon-identity endpoints are grouped under
/api/daemon— the daemon overview isGET /api/daemon(was/api/info) and the local identity probe isGET /api/daemon/identity(was/api/instance); host resource stats stay at/api/system. - A run’s control-plane execution ID is
executionId(wasexternalExecutionId) on the REST API, matching the control-plane protocol. - A run’s end reason is
succeeded(wassuccess), matching the past-tense form of every other end reason. - The concurrency queue cap is
max_queued(wasqueue_max). - The manual-trigger gate is
manual_trigger(wasapi_trigger), since it also gates the CLI and web UI, not just the REST API. - A notifier’s TLS setting is
tls_mode(wastls), naming the handshake mode it picks rather than just “on/off”. - Notification routes are
[[route]]blocks with anotifierslist (were[[notification_route]]withnotify), and notifiers are declared as[notifiers.<id>]tables keyed by id (was[[notifier]]blocks with anidfield). - Config reload is
POST /api/daemon/reload(was/api/reload), grouped with the other daemon-lifecycle endpoints. - The app event stream is
GET /api/events/stream(was/api/stream). - The system metrics endpoint is
GET /api/system/metrics(was/api/system/history), matching itsMetricsSamplepayload. - The unread-notification-count endpoint is
/api/notifications/unreadCount(was/unread-count), matching the API’s camelCase convention. runOnStartis always present on the tasks API (previously omitted whenfalse), matchingmanualTriggerandautostart.- A run that never started (skipped, missed, or interrupted by a DST fold) reports as
skipped, notfailed, on the control-plane connection.
Fixed
env_fileandsecrets_filevalues are read literally, so passwords and tokens containing$,${...}, or#are passed to the task exactly as written.- A log stream resumed after a disconnect replays the most recent missed lines, keeping a reconnecting browser continuous with the live tail even across a large gap.
- Outbound notification channels (Slack, Telegram) send periodic check-ins during a sustained incident on the default
keep_occurrencescadence. storage.max_sizeenforcement accounts for rotated log segments when trimming run history to the configured cap.- Starting the daemon no longer reprints the startup log a second time after a successful start. The log was already streamed live during startup; only a failed or timed-out start now prints the tail for diagnosis.
- Running
runwispnon-interactively with norunwisp.tomlin the current directory now fails immediately with a clear error. - The config JSON Schema for
stop_signalnow accepts the short forms (TERM,INT,KILL, …) the daemon already did, so editor validation stops flagging configs that were always valid. - The unread notification badge now updates across other open tabs/sessions when a mutation (e.g. mark-all-read) changes the count without shipping a notification.
POST /api/tasks/{taskName}/run?wait=true’swaitTimeoutnow maxes out at 240s (default 120s) so it can no longer exceed the server’s write timeout and drop the response after the run had already finished.- Sorting runs by duration (
sortField=duration) now returns them in the correct order instead of leaving them unsorted. - Two
runwisp daemonprocesses started against the same data dir can no longer both proceed — ownership of the data dir is now claimed atomically. - A cron day-of-week field using a bare
7with a step (e.g.7/2) now always means Sunday alone, instead of the step wrapping into other days. - Daemon shutdown no longer hangs on an unresponsive Docker/Podman engine during container or compose task cleanup.
- A run’s log output from just before a rotation is no longer deleted the instant the run ends — it stays available until the run itself is purged by retention.
- Viewing log lines from before a run’s most recent log rotation now returns their actual content instead of coming back empty.
- The unread notification count no longer drifts out of sync — it’s taken directly from the server on every event instead of computed from local deltas.
- A failed log fetch now shows as an error in the log viewer instead of silently rendering as if the run produced no output.
- Logging out mid-session now disconnects the live event stream immediately instead of letting it keep applying pushed updates behind the login screen.
- Reloading a task away from
on_overlap = "queue"no longer leaves its already-queued runs stuck pending forever. - The daemon’s identity fingerprint no longer changes when the binary is moved or the hostname changes — only the machine ID and working directory are hashed.
- The daemon’s live log/event streams no longer have a narrow gap where a line or notification published right as a client (re)connects could be delivered to nobody.
- Reconnecting to the app after a dropped connection now resyncs notifications from the server instead of silently missing anything that happened during the gap.
- Marking all notifications read no longer discounts a notification that was created while the request was still in flight.
- The runs list sorted oldest-first no longer undercounts newly arrived runs against the list’s total.
- A cron task reclaimed by the system’s own crond while queued in the daemon’s start-spread window could still fire once more — it’s now dropped instead of double-running for that tick.
- A run stopped because its log hit the disk free-space guard is now recorded as a policy-initiated stop instead of looking like a crash.
- A log rotation that fails immediately after freeing the old segment no longer risks silently destroying already-captured output on the next write.
- A run’s output capture that stops due to a real I/O error is now logged and noted in the run’s own log instead of looking like the process simply exited.
- A container-execution task’s own
RUNWISP_-prefixed environment variables are no longer silently dropped when the task also had other env vars or secrets configured. - A notification delivery that timed out is no longer silently dropped without a retry or failure record — only a real daemon shutdown is now treated as a shutdown.
- A run’s status update to the cloud control plane is no longer silently lost if the connection dropped at the exact moment it was being sent.
- The TUI no longer freezes while confirming an action (restart/stop/trigger/retry a run) if the daemon is slow to respond.
- Switching between runs’ logs in the TUI no longer occasionally drops or misattributes lines from the run you just left.
- Jumping to a log search result in the TUI no longer highlights the wrong run’s line if you navigated away before it finished opening.
- Browsers without the Web Locks API (but with
BroadcastChannel) no longer open a duplicate SSE connection per tab instead of sharing one. - The run picker’s select-all checkbox no longer shows “all selected” after paging or filtering changed which rows were visible.
- The run filter popover’s exit-code field no longer shows a stale value after the filter was cleared elsewhere (e.g. removing its chip).
- The notification bell’s error indicator no longer lights up for unrelated unread notifications when an actual error notification had already been read.
- Modal and Drawer dialogs now trap keyboard focus while open and restore it on close, instead of letting Tab escape to the page behind them.
- Select, Dropdown, and Popover menus now keep keyboard focus inside them while open instead of letting Tab jump to an unrelated part of the page.
Security
- Hardened
service installunit generation, task-process environment isolation, config-file trust checks, privilege dropping, and outbound-request address filtering. Internal safeguards with no configuration changes. - HTTP-task run logs redact request/response credentials —
Authorization,Cookie, API-key headers, and URL passwords are shown as[redacted]instead of being persisted to disk. - Control-plane dispatch is validated and bounded at the boundary — peer-supplied shell
umask/interpreter are re-validated, log-search size and the ephemeral dispatch queue are capped, and running the control-plane connection over plaintext (RUNWISP_CLOUD_ALLOW_INSECURE) now warns loudly at startup. allow_cloud_dispatchnow also gates HTTP-type ad-hoc dispatch and cloud-created services, not just shell/container/compose, since HTTP dispatch makes a peer-directed network call too.- Log search runs under a request deadline. Internal safeguard with no configuration changes.
- A shutdown race and a panic risk in the notification retry/coalescing path are fixed. Internal safeguard with no configuration changes.
RUNWISP_TRUSTED_PROXIESrejects an IPv4-mapped IPv6 range that would cover every IPv4 address (e.g.::ffff:0:0/96), closing a gap that let a peer spoofX-Forwarded-For/X-Forwarded-Protopast the trusted-proxy check.
- v1.5.1 latest
- v1.5.0
- v1.4.0
- v1.3.1
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.1
- v1.0.0
- v0.16.4 pre
- v0.16.3 pre
- v0.16.2 pre
- v0.16.1 pre
- v0.16.0 pre
- v0.15.1 pre
- v0.15.0 pre
- v0.14.0 pre
- v0.13.2 pre
- v0.13.1 pre
- v0.13.0 pre
- v0.12.0 pre
- v0.11.0 pre
- v0.10.0 pre
- v0.9.0 pre
- v0.8.0 pre
- v0.7.0 pre
- v0.6.0 pre
- v0.5.0 pre
- v0.4.0 pre
- v0.3.0 pre
- v0.2.0 pre
- v0.1.2 pre
- v0.1.0 pre