EXTENSIONS
Built by operatives — models, drivers, vaults, and reports, the parts that plug into Swamp.
Filter by what you need and pull what fits.
Vikunja Kanban
Vikunja kanban orchestrator — creates tasks in a Vikunja project directly
Credential Expiry
Probe the credentials a fleet actually holds and report how long each has left, distinguishing expiry from an outage in progress
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. 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.
N8n
Read a running n8n deployment and report how far its pinned version has drifted behind the channel n8n itself calls stable. Both methods are strictly read-only — this extension never upgrades n8n, edits a workflow, touches a credential, or restarts a container; it tells you an update exists and stops there. `drift` takes the running version as an argument rather than reading it, because n8n does not disclose one: its `/rest/settings` answers 200 unauthenticated but with `settingsMode: public`, a reduced payload carrying no version field at all, and `versionCli` reaches authenticated callers only — so the version comes from the pinned image tag on the host, which is what the deployment actually is rather than what a process predating the current pin believes itself to be. The upgrade target is npm's dist-tag, never the newest published release: n8n ships its in-progress minor into the same GitHub release namespace flagged `prerelease: true` and promotes that line later, so on 2026-09-05 the newest release was 2.38.3 while stable was 2.37.10, and the 2.37 line had itself been prerelease through 2.37.6. GitHub releases are still read, but only to enumerate the stable releases that were missed and to link the notes, since the `prerelease` flag is the historical record of which line was stable when and dist-tags do not retain it. Image existence is verified against `registry-1.docker.io` rather than the `docker.n8n.io` mirror the pin names: that mirror 404s its own token endpoint, delegates auth to Docker Hub, and then answers 429 to every manifest request for tags that exist and tags that do not alike, so it cannot return a usable answer — and a 429 is raised as indeterminate rather than folded into `absent`, which would suppress every update forever while the check looked healthy. An exhausted GitHub rate limit, an unreachable registry, and an unparseable running version are all raised as errors rather than reported as a reassuring `current`.
Backrest
Keep a Backrest server's snapshot index current for restic repositories it does not itself back up, and report how fresh each one is. Backrest indexes snapshots only for repositories it runs backups for; one that is merely configured — the normal shape when restic runs from systemd timers on each host and Backrest is only the console — is never indexed at all, and reports no error while doing so. The repository simply stays empty in the UI, which reads as `no backups` for a host whose backups are in fact current. `sync` reads the operation log and reports each repository's newest indexed snapshot. `reindex` triggers Backrest's own TASK_INDEX_SNAPSHOTS for every configured repository, waits for them to appear, and reports the same shape; it reads repositories and never creates, forgets or prunes a snapshot, so it is safe to schedule. Four server behaviours shape the implementation because each one silently produces a wrong answer if ignored. GetOperations' repoId selector does not filter — a selector matching nothing returns the ENTIRE operation log rather than an empty set, so a per-repository query makes every repository report the whole fleet's totals; operations are therefore fetched once and grouped on each operation's own repoId. A failed index task leaves no trace in the operation log, only successes being recorded, so a repository the server cannot read is indistinguishable through the API from one whose task has not run yet — both are reported as `unindexed` with the reason named as the server log rather than invented. The task queue is serial, so triggering N repositories enqueues N tasks behind each other and one whose credentials were revoked does not fail fast but retries with exponential backoff for six minutes or more while everything behind it waits, which is why the wait is a deadline over the whole set rather than a per-task timeout. And a repository with no indexed snapshot at all is a different failure from one whose snapshots have stopped advancing — the first is a credential this server holds that no longer exists, the second is a backup that has stopped running — so they are counted separately as `unindexed` and `stale` instead of being folded into one unhelpful total. Because it reaches repositories through the server's own stored credentials rather than the ones the backup hosts use, disagreement with a host-side view is informative: it means exactly one of the two credential sets has gone bad.
Gitlab Token
Create, rotate, revoke and inventory GitLab personal, project and group access tokens through one model, with a threshold-gated rotation workflow and the one-time token value written to a vaulted sensitive field
Openobserve
Operate a self-hosted OpenObserve instance from swamp, and decide safely whether a newer upstream release should be rolled out to it. OpenObserve publishes release candidates into the same tag namespace as stable releases -- on 2026-08-29 the newest tag was v1.0.0-rc1, published eleven days AFTER the newest stable v0.92.2 -- so anything that reads the tag list and sorts it pins an RC. That is worse here than for a stateless app: OpenObserve is a log store, and an RC that migrates the on-disk schema is not undone by re-pinning the previous tag, because the previous binary can no longer read what the new one wrote. The `check_update` method reads GitHub *releases* rather than tags, excludes drafts and prereleases (by the GitHub flag OR a semver prerelease suffix, since the flag is hand-set and occasionally wrong), applies real semver precedence so a prerelease sorts before its own release, and reports the newer prereleases it skipped so a pending major stays visible without being auto-applied. It then confirms the candidate tag actually resolves in the registry before offering the update -- a GitHub release and a pushed image are separate events, and proposing a bump whose image does not exist yet fails the deploy at `compose pull`, after the running container has already been stopped. The `health` method probes /healthz, treating a refused connection as a health result rather than a model error so a down instance is distinguishable from a broken check.
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.
Unifi Dhcp Reservation
Declarative DHCP fixed-IP reservations on a local UniFi controller (UDM / UDM Pro / UDM SE). `sync` reads every reservation the controller holds; `drift` compares a desired set against it without writing, reporting missing, mismatched, unmanaged, duplicate and DHCP-pool-overlapping entries; `apply` reconciles the controller to the desired set and supports `dryRun`. `device_drift` does the same read-only comparison for adopted hardware, which cannot hold reservations at all and is addressed through device config instead — the one surface `drift` is blind to. Reservations live on the legacy Network API as `user` objects carrying `use_fixedip`/`fixed_ip`. Adds TOTP/MFA login support, which the upstream @mgreten/unifi auth flow lacks — UniFi SSO accounts with MFA reject password-only logins with MFA_AUTH_REQUIRED. Catches the failure mode where a reservation silently never takes effect because its address is already claimed by a statically-configured host. `forget_client` prunes stale client records the controller still remembers, refusing any MAC that holds a reservation or a live lease unless forced.
Unifi Fabric
Structural health monitoring for a UniFi fabric. The `@sntxrr/unifi-fabric/topology` model's `check` method compares a declared topology against live `/stat/device` rows and reports the failures that outcome-based monitoring cannot see: a device expected on the wire that has silently fallen back to a wireless mesh uplink, attachment to the wrong upstream device, links negotiated below their expected speed, ports carrying error counters, and — the one with no equivalent elsewhere — ports that are down but have carried real traffic before, which identifies a run that used to work. An access point that loses its wired uplink does not fail; it meshes, keeps serving clients, and every uptime check stays green while latency quietly goes from sub-millisecond to tens of milliseconds and jittery. `uplink.type` flipping from `wire` to `wireless` is a boolean, so it is asserted exactly rather than thresholded. Read-only: never writes to the controller. Emits a flat Prometheus-ready metric series alongside the verdict, including for healthy devices, so alerts can fire on a series dropping to zero rather than on a document changing shape. Authenticates with an API key over `X-API-KEY`, which sidesteps the HTTP 499 that MFA-enabled SSO accounts return for password logins.
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.
Restic Readiness
Rank a restic fleet by what has actually been proven restorable. A workflow-scope report that joins every @sntxrr/restic/repository step in a run — freshness, structural check, read-data verification, canary dump and restore drill — into one ranked findings list, where a rung that has never run is itself a finding. Read-only: it reads what the steps already wrote and never touches a repository.
Dependabot Sweep
Sweep an owner's repositories for open Dependabot pull requests and split the review queue from the abandoned-repo noise
Openwebui
Read a running OpenWebUI instance and report how far its version has drifted behind upstream. Both methods are strictly read-only — this extension never upgrades the instance, edits its settings, manages users, or touches the container; it tells you an update is available and stops there. `sync` records what the instance reports about itself: version plus the feature flags that decide what automation can reach it, including `enable_api_keys`, which when false blocks every token-authenticated integration and cannot be worked around with any credential — reported as `null` rather than `false` when the instance withholds it, which OpenWebUI has done for unauthenticated callers since v0.9.6, because absent is not the same as off. `drift` compares the running version against the repo's published GitHub releases and reports status (`current`/`behind`/`ahead`), how many releases were missed, and which ones. Needs no credentials at all: both endpoints it uses answer before login. Versions are compared numerically because OpenWebUI's break lexical ordering in both directions — `0.8.12` sorts above `0.11.0` and below `0.8.9` as strings — so a string compare reports an instance eleven releases behind as up to date. An exhausted GitHub rate limit, a missing repo, and an unparseable running version are all raised as errors rather than folded into a reassuring `current`, and a release page that fills up before reaching the running version is reported as `truncated` rather than passed off as a total.
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 Version
Read the swamp version a host or container is running and report how far it has drifted behind the published channel. Read-only — it never updates, installs, or restarts anything; it tells you a newer build exists and stops there. Fills the gap `swamp update --check` leaves: that answers for the binary invoking it, on the platform invoking it, which is no help when the install you care about is a pinned binary inside a container on another host. `sync` records what a target reports about itself — its version and, via `uname`, the artifact platform it runs on. `drift` resolves the newest build published on a channel for THAT platform and reports status (`current`/`behind`/`ahead`) plus the lag in hours. Targets are reached three ways: the local binary, `docker exec` (optionally through a named docker context, so one host can check a container on another), or SSH with BatchMode so an unattended run fails fast instead of hanging on a password prompt. The channel is resolved by a single HEAD against the `stable` alias, reading the resolved version out of its website-redirect metadata — one request rather than the ~90 MB the archive weighs — and the same pass verifies the platform-specific archive is actually fetchable, which is the precondition for pinning a rebuild to it. Versions are compared numerically because the build ordinal in `YYYYMMDD.HHMMSS.N` is not zero-padded: as strings `20260808.001107.10` sorts below `...9`, so a string compare reports a target one build behind as up to date the day a tenth build ships. `hoursBehind` is derived from the timestamps embedded in each version and is documented as a lag, not a count of missed builds — swamp publishes no release list, so the builds in between cannot be enumerated and this extension does not pretend otherwise. An unreachable target, an unparseable version at either end, a channel alias that answers without its redirect header, and a resolved archive that 404s are all raised as errors rather than folded into a reassuring `current`.
B2 Hygiene
Audit a scanned Backblaze B2 account for hidden-version retention gaps, over-scoped or orphaned application keys, and public buckets. Two reports: a method-scope audit of one b2-account scan, and a workflow-scope companion that joins those findings to @sntxrr/b2/files byte totals so each gap is ranked by what fixing it recovers. Read-only — both analyse resources already written and never call B2.
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 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.
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 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.
1password Connect
A swamp vault backend for 1Password Connect — read and write secrets over Connect's HTTP API with a bearer token, so vault.get() works headless in cron, containers, and swamp serve without the op CLI.
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.
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. Read-only: it reads what previous runs recorded and never invokes the failing target.