EXTENSIONS
Credential Expiry
Probe the credentials a fleet actually holds and report how long each has left, distinguishing expiry from an outage in progress
Github Vikunja
Convert GitHub issues to Vikunja kanban cards — bridge personal issue tracking between GitHub and Vikunja kanban boards with a single import_issue method.
Traefik
Keep Traefik reverse proxies, the images beside them (CrowdSec and others) and every plugin they load current, through pull requests to the repository each proxy is deployed from -- never by touching a running proxy. `inspect` reads the live proxy over SSH or its API: running version, router and middleware status, and whether plugins failed to load since this start. `check` compares the repository's pins with what is running and with upstream releases, and reports what is behind plus how the deployment is exposed: plugins downloaded from the catalog at every start (a failed download disables every plugin, and with them every router that uses one), pins that never deployed, an insecure API published on a host port, routers that are down. `bump`, dry-run by default, opens one pull request per component: an image tag, a remote plugin's version, or a re-vendored local plugin with its commit and tarball checksum recorded. It defers to an open Dependabot PR that already reaches the target, names one that falls short as superseded, refuses major upgrades unless asked, and refuses deployments that still download plugins at startup unless asked. Plugin middleware configuration, which can carry a CrowdSec LAPI key, is never read into a resource, and neither is a plugin middleware's error text or a partial API response; the container's environment is never read. Unknown is never current: health is null while routers or the plugin-failure log could not be read, and an uncomparable version is a finding.
Uptime Kuma
Read-only Uptime Kuma monitor history for triage: the last N heartbeats of one monitor -- status, time, message, ping -- plus a summary of the current status, since when, and how many times it changed in the window. Kuma has no REST endpoint for heartbeat history, and its socket.io API needs the instance's only kind of login, an administrator, so any credential that can read history can also delete monitors and read notifier secrets. This model instead reads kuma.db over SSH with the host's python3, opening the database mode=ro with PRAGMA query_only=ON so SQLite itself refuses any write, and selects named columns from the monitor and heartbeat tables only: never notifier settings, credentials, monitor URLs or hostnames, or heartbeat response bodies. Monitors are found by id or by name (exact, else a unique case-insensitive substring; ambiguity is an error listing the candidates). It needs no Kuma login, never enters the container, never copies the database off the host, and has no write method.
Docker Ps
A read-only container listing of a Docker host over SSH, narrow enough to hand to an unattended triage agent. It answers the first question an alert usually raises -- what is running on that host, and which container holds that port -- with name, image, state, status, published ports and creation time per container. It runs `docker ps` with an explicit, per-field `--format` template and rebuilds each row from those fields alone, so no environment variable and no label value ever leaves the host: `docker inspect` returns every container's environment, which is where compose files keep database passwords and API tokens, and `docker ps --format '{{json .}}'` includes the Labels field. The type has exactly one method and no free-form command or format argument, so a model instance exposes no write verb at all. Host key checking stays on by default.
Log Tail
Read the last lines of a log chosen by NAME from an allowlist fixed in the model definition, with secret values redacted. Built for an alert-triage agent that has no shell: an alert names a log, the cause is usually one line near its end, and the alternatives are a human tailing it for the agent or a general file-read tool that can read anything. The caller passes a name, never a path; the name-to-path map lives in the definition, where only its owner can change it. A name is matched with Object.hasOwn and a name that is not name-shaped is never echoed, because `--input name=@file` would otherwise turn a refusal into a read. The configured path must be absolute, normalised, a regular file, not a symlink and equal to its own realpath, and the opened file's device and inode must match the lstat taken before the open, so a symlink swapped in between is refused rather than followed. Output is capped at 200 lines. Redaction drops values and keeps names (`password=[REDACTED]`) across key=value, JSON, YAML, CLI flags, URL userinfo, Authorization headers and bare token shapes, and fails closed: a line mentioning a password manager CLI or a private key is withheld whole, a line still credential-shaped after redaction is withheld whole, and an error in redaction fails the method. Both methods are read-only.
Cloudflare X402
Pay for and inspect x402-protected HTTP resources and MCP tools — the Cloudflare agents payment standard — with a local EVM wallet, plus an x402 spend report
Docker Reclaim
Measure what a Docker host's disk is actually spent on, plan what can be freed without destroying a rollback target, and free it. The command people reach for is the wrong one: `docker image prune` — dangling or `-a` — never touches the BUILD CACHE, and on a host that builds its own images the build cache is usually most of the footprint. On the host this was written against it was 59.5GB of a 77GB reclaim, which no amount of image pruning would have recovered. Three behaviours are deliberate because each one corresponds to a way the obvious command does damage or lies. `docker image prune -a` removes every image no container references, which is precisely the set of previous versions a rollback needs; this model instead protects the newest `keepVersions` tags of each repository and removes specific image IDs, so reclaiming space does not quietly cost you the ability to go back. `docker builder prune -af` deletes the cache that makes the next build fast, and cache that is minutes old is the most valuable cache there is; pruning is therefore age-filtered via `--filter until=`, so a scheduled run takes cold layers and leaves the working set. The prune runs with `-a` and the age filter as its guard, because without `-a` BuildKit leaves out records shared with the image store, and on a Docker engine an age-filtered prune then frees almost nothing. The prune names the engine's own builder (`--builder "$(docker context show)"`) rather than whichever buildx builder is current, and an `engine-builder` pre-flight check refuses `reclaim` unless that builder uses the docker driver, so cache is never pruned on an engine the model did not measure. And Docker reports sizes as rounded human strings where 1000 versus 1024 is a real 7% at terabyte scale, so parsing is explicit and unit-aware and the freed figure is measured by observing free space before and after rather than by summing estimates. Each method writes its own data instance — `usage`, `plan`, `reclaim` — so an expression always gets the schema it expects rather than whichever method ran last. `observe` and `plan` are read-only and safe to schedule at any frequency; `reclaim` is dry-run unless passed `apply: true`, and even then never removes an image that any container references, running or stopped. Host key checking is left ON by default — `StrictHostKeyChecking=no` is available but is not the default, because a model whose entire purpose is running destructive commands as a privileged user is the last place to accept an unverified host key. For a Docker engine on the machine swamp itself runs on, such as Docker Desktop on a workstation, set `sshHost: local`: each command runs through `sh -c` as the invoking user, with no SSH server required, and the commands and protections are identical to the SSH path.
Vikunja Kanban
Vikunja kanban orchestrator — creates tasks in a Vikunja project directly
Cloudflare Cf
Drive Cloudflare's agent-first cf CLI from swamp — intent search, per-command API schemas, credential checks, and any of its 3,000+ API operations with writes dry-run by default, plus a cf activity report
Aws Access Key
Mint, inventory, deactivate and delete AWS IAM access keys — the step @swamp/aws/iam cannot model — delivering a new key straight into a named vault item, verified by read-back and rolled back if delivery fails
Oci Instance
Power-control an Oracle Cloud Infrastructure (OCI) compute instance — reboot (SOFTRESET/RESET), start, stop, and sync lifecycle state via the OCI Core API with RSA-SHA256 request signing.
B2 Bucket
Manage one Backblaze B2 bucket via the B2 Native API v4 — sync, create, update, idempotent delete, and get/set event notification rules — with first-class lifecycle rules so restic-pruned hidden file versions are actually deleted.
B2 Account
Inventory a Backblaze B2 account — one read-only scan method emits a resource per bucket and per application key, plus a summary, via the B2 Native API v4.
Rclone Archive
Archive a Synology share to S3 Glacier Deep Archive with rclone, driven over SSH into a container on the NAS so no binary is installed on DSM. A cost-ordered ladder — inventory and cost projection, copy, metadata verification, then a two-phase restore drill that is the only rung proving recovery. Never deletes: sync, move and purge are refused at the runner, because a source that fails to mount presents as empty and sync would empty the destination unrecoverably while still billing the 180-day minimum.
Prometheus Pushgateway
Push metrics from a scheduled swamp method into a Prometheus Pushgateway. Scheduled jobs are exactly what Pushgateway exists for: a batch that runs, computes numbers, and exits long before any scrape could reach it. The `@sntxrr/prometheus/pushgateway` model's `push` method takes a flat `{name, value, labels}` series — the shape several models already emit alongside their verdicts — renders it as text exposition format, and writes it to a grouping key. Defaults to PUT so a series that disappears from the source disappears from the gateway, rather than lingering at its last value forever. Validates metric and label names and rejects non-finite values before sending, because Pushgateway answers 400 without naming the offending series and a single NaN discards the whole batch. Handles the base64 grouping-key escape for values containing a slash or empty values, which would otherwise corrupt or collapse a path segment. Emits an optional heartbeat counter, because pushed metrics go stale silently: the gateway serves the last value forever, so a job that stops running leaves every dashboard green while nothing is being checked.
Career Ops
Mirrors the career-ops job-application tracker into swamp with bidirectional status write-back, posting URL + legitimacy enrichment, and a pipeline funnel report.
B2 Transfer
Backblaze B2 data-plane transfers via the Native API v4 — the thirteen upload, download and large-file operations, guarded. Inventories the interrupted large uploads B2 bills for invisibly, proves a bucket is readable and writable end to end, and refuses to move more than 100 MB without an explicit override.
B2 Key
Manage a Backblaze B2 application key via the Native API v4 — sync, create, and idempotent delete. The one-shot applicationKey secret is delivered straight to 1Password Connect and never written to a resource snapshot; create fails closed when no destination is configured.
B2 Files
Inventory and manage Backblaze B2 file versions via the Native API v4 — an aggregate scan that separates current bytes from the non-current versions a bucket with no hidden-version lifecycle rule pays for forever, plus sync, copy, and gated hide, delete and Object Lock updates.
Restic Repository
Validate one restic repository from a neutral host — snapshot freshness and backup-scope drift, structural check, read-data verification against bitrot, a dump canary, and a size-capped restore drill that proves the backup can actually be restored. Strictly read-only: it refuses every restic write command and never takes a repository lock, so it can never break the backup it validates.
Swamp Triage
Investigate why any swamp model or workflow is failing, without knowing anything about the domain it automates. Every method and workflow run leaves a @swamp/method-summary or @swamp/workflow-summary report behind as versioned model data, written on failure as well as success — so the full history of a target is already on disk with nothing extra instrumented. `investigate` resolves a target by name at call time, walks that history back to the boundary where it stopped working, and classifies the error into auth / unreachable / timeout / tls / rate_limit / not_found / config, each with a concrete next step. The distinction it exists to make is auth vs unreachable: a remote answering 403 is healthy and rejecting your credential (go to the vault), while a remote that never answers is a network problem (go to the host) — the two read almost identically in a notification and lead opposite ways. Unrecognised errors are reported as `unknown` verbatim rather than filed under a plausible-looking category. A run that failed without recording any error text is reported as `unrecorded`, never as `none`. `recent` answers the question an alert with no name leaves open -- what just broke? -- by listing every model and workflow that failed within a window -- still failing first, then those that have since recovered, with when -- each with its instance name and model type and classified the same way. Both methods tell the caller's mistakes apart from the fleet's failures: `recent` flags the investigator's own failed runs (selfInflicted) and a failure fixed by an immediate retry of the same method (quickRetry), reports each target's definition path, and lists scheduled workflows with no recorded run at all (a schedule that never fires leaves no failure anywhere else); `investigate` reports a target with no runs as never_run -- failing when it is scheduled to run here -- suggests the closest names for one that matches nothing, and separates a rejected call argument (caller_args) from a definition problem (config). A failed workflow step is reported with the error its own run recorded, and an expression that failed to evaluate classifies as config. `resolve` maps free alert text to the model and workflow names it most likely means -- token overlap with names, types, tags and descriptions, top matches with score -- for an alert that does not name its target. Read-only: every method reads what previous runs recorded and never invokes the failing target.
Jetkvm
Operate a JetKVM from swamp: health checks and Tailscale updates. The JetKVM web UI does almost everything over a WebRTC data channel, so video, USB, version and settings are not on HTTP and there is no snapshot endpoint; this model reads the unauthenticated /metrics page for the app version and NTP state, and everything else over SSH as root (developer mode, key auth, host key pinned or already known; never trust-on-first-use). health flags app and system firmware updates (via JetKVM's release API), USB gadget not enumerated, low disk, clock skew, and Tailscale down, offline, out of date or missing its boot script; with the optional tailnetIp it also fetches /metrics over the tailnet, which catches a node deleted from the tailnet that tailscaled still reports as Running. Reads and writes are separate methods: tailscale reports status or plans an update and never changes the device; tailscaleUpdate always acts, behind labeled live pre-flight checks (installed, boot script, free space, tailnet path, no swap already running). The device has no CA store, so `tailscale update` cannot run on it: tailscaleUpdate downloads the official arm tarball on the swamp host, checks its sha256, streams it over SSH, backs up the running binaries, swaps them in the layout `tailscale configure jetkvm` expects, restarts tailscaled without a reboot, and rolls back by itself unless the node rejoins Running and online with the same tailnet IP (when the old one was readable) and, with tailnetIp, answers over the tailnet from the swamp host. Every update outcome is recorded as its own update instance, so a later status run never hides a failed update.
Glinet Kvm
Operate GL.iNet KVMs (Comet GL-RM1 / GL-RM1PE) from swamp: health, screenshots, ATX power and virtual media. The firmware is a fork of PiKVM's kvmd, and three of its behaviours shape this model. The factory TLS certificate is self-signed, names CN=localhost and expired in 1979, so it cannot be validated or pinned; HTTP goes through curl and the viewer through openssl, with credentials on stdin rather than argv. The video streamer sleeps until a viewer connects, so a bare snapshot request answers 503 whether or not there is a signal; screenshot and health open a receive-only viewer session first and never send input. Without the ATX add-on board, the API still reports power 'off' for a running machine; this model reports the state as unknown and refuses to press a button wired to nothing. health flags available firmware updates, missing HDMI signal, offline USB HID, forgotten virtual media that could hijack the next boot, clock skew, Tailscale (the device's own state, plus an optional probe of its tailnet IP that catches a node deleted from the tailnet, which the device still reports as Running), and security posture (2FA off, browser root shell on, untrusted TLS). Reads and writes are separate methods: atx and msd report state or record a plan and never change the device; atxPress and msdChange act, behind labeled live pre-flight checks (ATX board present and not busy; virtual media enabled and not busy).