Skip to main content

Pets

@psftw/petsv2026.08.12.4beta· 2mo agoBEHAVIORSREPORTS
01README

Minimalist configuration management for "pet" Debian Linux servers. Declare intent with primitives, group primitives into a role, then check/apply across a fleet.

🤖 experimental 🤖

This was built to learn about swamp extensions, don't use it!

Every model takes a fleet (@swamp/ssh model instance), and a hosts selector (all, name:X, tag:Y). Convergence logic streams to each host's python3 over SSH stdin; nothing is installed on the targets. check is read-only; apply converges with sudo. Statuses: compliant, non_compliant, applied (real changes made), failed.

Quickstart by example: PostgreSQL from PGDG repositories

Define a fleet:

swamp extension pull @swamp/ssh
swamp model create @swamp/ssh my-fleet
swamp model edit my-fleet
transport:
  kind: ssh
  user: debian
  identityFile: ~/.ssh/id_ed25519
hosts:
  - name: build
    address: build.example.com
    tags: [build]

Declare our APT repository (see: PGDG):

swamp model create @psftw/pets/apt-repository pgdg-repo
swamp model edit pgdg-repo
fleet: my-fleet
name: pgdg-repo
uris: [https://apt.postgresql.org/pub/repos/apt]
suites: [trixie-pgdg]
components: [main]
gpgKeyUrl: https://www.postgresql.org/media/keys/ACCC4CF8.asc
signedBy: /etc/apt/keyrings/pgdg.asc

Declare our package install:

swamp model create @psftw/pets/apt postgres-package
swamp model edit postgres-package
fleet: my-fleet
packages:
  - postgresql-18

Sequence these into a role:

swamp model create @psftw/pets/role postgres-role
swamp model edit postgres-role
fleet: my-fleet
members:
  - pgdg-repo
  - postgres-package

Apply the role to our server:

swamp model method run postgres-role apply --input hosts=all
[INF] @psftw/pets/role·apply: [1/2] pgdg-repo: apply on all
[INF] @psftw/pets/apt-repository·apply: running on all via my-fleet (sudo)
[INF] @psftw/pets/apt-repository·apply: build: applied — write /etc/apt/sources.list.d/pgdg-repo.sources; fetch key to /etc/apt/keyrings/pgdg.asc
[INF] @psftw/pets/role·apply: [1/2] pgdg-repo: applied=1
[INF] @psftw/pets/role·apply: [2/2] postgres-package: apply on all
[INF] @psftw/pets/apt·apply: running on all via my-fleet (sudo)
[INF] @psftw/pets/apt·apply: build: applied — install postgresql-18
[INF] @psftw/pets/role·apply: [2/2] postgres-package: applied=1
[INF] @psftw/pets/role·apply: build: applied — pgdg-repo: write /etc/apt/sources.list.d/pgdg-repo.sources; pgdg-repo: fetch key to /etc/apt/keyrings/pgdg.asc; postgres-package: install postgresql-18
[INF] @psftw/pets/role·apply: summary[all]: applied

View the compliance report:

swamp report get @psftw/pets/fleet-compliance --model postgres-role

Fleet compliance — postgres-role · apply (succeeded)

Aggregate: applied across 1 host(s) — selector all — 2026-08-11T00:00:00.000Z

Host Status Changes
build applied 3

Members

Host Member Status
build pgdg-repo applied
build postgres-package applied

Changes

  • build: pgdg-repo: write /etc/apt/sources.list.d/pgdg-repo.sources
  • build: pgdg-repo: fetch key to /etc/apt/keyrings/pgdg.asc
  • build: postgres-package: install postgresql-18
02Release Notes

Fix: template-file apply converged owner/group/mode silently when the content hash already matched, reporting compliant with no changes recorded. It now touches owner/mode only when drifted, reports the run as applied, and records each chown/chmod in changes; apply facts gained a performed list. changed still means content changed, so onChange semantics are unaffected.

03Behaviors7
@psftw/pets/rolev2026.08.12.4role.ts

Global Arguments

ArgumentTypeDescription
fleetstring@swamp/ssh model providing inventory and transport
requireCharDevicesarrayCharacter devices that must exist; apply refuses without them
membersarrayGranular @psftw/pets model instances (each implementing check and apply), in execution order
fn check(hosts: string)
Run every member's check and roll up per-host drift (read-only, no sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)
fn apply(hosts: string)
Verify required devices, then run every member's apply in order (sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)

Resources

state(infinite)— Per-host aggregate role compliance (member states hold detail)
summary(infinite)— Selector-keyed roll-up across all hosts (stable name for CEL/data.latest consumers)
@psftw/pets/aptv2026.08.12.4apt.ts

Global Arguments

ArgumentTypeDescription
fleetstring@swamp/ssh model providing inventory and transport
packagesarrayPackages that must be installed
fn check(hosts: string)
Report missing packages, flagging any without an install candidate (read-only, no sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)
fn apply(hosts: string)
Refresh the apt index and install missing packages (sudo); no-op when compliant
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)

Resources

state(infinite)— Per-host package compliance
@psftw/pets/apt-repositoryv2026.08.12.4apt_repository.ts

Global Arguments

ArgumentTypeDescription
fleetstring@swamp/ssh model providing inventory and transport
namestringFilename stem for /etc/apt/sources.list.d/<name>.sources
urisarrayRepository base URLs
suitesarrayDistribution suites (e.g. trixie-pgdg)
componentsarrayRepository components (e.g. main)
architectures?arrayRestrict to these architectures
gpgKeyUrl?stringKey fetched to signedBy when absent (.asc target: stored armored as-is)
signedBy?stringKeyring path referenced by Signed-By
fn check(hosts: string)
Compare .sources content hash and key presence (read-only, no sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)
fn apply(hosts: string)
Install the .sources file and fetch the signing key if absent (sudo); no-op when compliant
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)

Resources

state(infinite)— Per-host apt source compliance
@psftw/pets/directoryv2026.08.12.4directory.ts

Global Arguments

ArgumentTypeDescription
fleetstring@swamp/ssh model providing inventory and transport
dirsarrayDirectories that must exist
fn check(hosts: string)
Report drift on all managed directories (read-only, no sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)
fn apply(hosts: string)
mkdir/chown/chmod drifted directories (sudo); refuses non-directory paths
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)

Resources

state(infinite)— Per-host directory compliance
@psftw/pets/group-memberv2026.08.12.4group_member.ts

Global Arguments

ArgumentTypeDescription
fleetstring@swamp/ssh model providing inventory and transport
usernamestringUser whose membership is managed
groupstringSupplementary group the user must belong to
fn check(hosts: string)
Report whether the user is in the group (read-only, no sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)
fn apply(hosts: string)
Append the user to the group with usermod -aG (sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)

Resources

state(infinite)— Per-host membership compliance
@psftw/pets/systemdv2026.08.12.4systemd.ts

Global Arguments

ArgumentTypeDescription
fleetstring@swamp/ssh model providing inventory and transport
servicestringUnit name (e.g. nginx or getty@tty1)
enabledbooleanUnit must be enabled at boot
ensureenumDesired active state
fn check(hosts: string)
Report unit enabled/active drift (read-only, no sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)
fn apply(hosts: string)
systemctl enable/disable + start/stop to match desired state (sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)

Resources

state(infinite)— Per-host unit state compliance
@psftw/pets/template-filev2026.08.12.4template_file.ts

Global Arguments

ArgumentTypeDescription
fleetstring@swamp/ssh model providing inventory and transport
pathstringDestination file on the host
templatePathstringLiquid template, relative to the repo root
ownerstringFile owner
groupstringFile group
modestringOctal mode (e.g. "0644")
variablesrecordExtra render-context values, merged with per-host `host`
validateCommand?stringRun against the staged file as $FILE before install (e.g. nft -c -f "$FILE")
onChange?stringRun after install, only when the content hash changed
fn check(hosts: string)
Render locally and compare remote hash/owner/mode (read-only, no sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)
fn apply(hosts: string)
Stage, validate, atomically install, run onChange when the hash changed (sudo)
ArgumentTypeDescription
hostsstringFleet host selector (name:X or tag:Y)

Resources

state(infinite)— Per-host rendered-file compliance
04Reports1
@psftw/pets/fleet-compliancemethod
fleet_compliance.ts

Per-host role compliance: aggregate, member statuses, and changes

complianceaudit
05Previous Versions3
2026.08.12.3

Fix: template-file and apt-repository apply encoded staged content with bare btoa, which throws on non-Latin1 characters (e.g. an em dash in a template) and would corrupt 0x80–0xFF characters relative to the UTF-8 content hash. Content is now base64-encoded over UTF-8 bytes.

2026.08.12.2
2026.08.12.1
06Stats
A
93 / 100
Downloads
12
Archive size
82.9 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License1/2partial

    no SPDX license declared

  • Verified public repository2/2earned
07Platforms
08Labels