Watch
6
1
Fork
You've already forked swamp
0
mirror of https://github.com/systeminit/swamp.git synced 2026-09-11 17:21:38 +00:00
Stinemates, Watson, Adam, Mahir, Paul
  • TypeScript 99.3%
  • Shell 0.4%
  • Python 0.1%
  • CSS 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Paul Stack ae1ed1492d
fix(tokens): allow re-minting after revoke or expire (#2433)
## Summary

- Enrollment token `mint` unconditionally rejected names with existing
data, permanently reserving them after revocation or expiry. Now parses
the existing record and only rejects active tokens
(`unused`/`enrolled`).
- Server token `mint` allowed re-mint after revoke (since #2026) but not
after expire. Extended the guard to treat both `revoked` and `expired`
as terminal states.
- Both token types overwrite the old record with fresh credentials via
`vault.put` + `writeResource`.

Closes swamp-club#2078

## Test plan

- [x] Enrollment token: re-mint after revoke succeeds with fresh
credentials
- [x] Enrollment token: re-mint after expire succeeds with fresh
credentials
- [x] Enrollment token: mint while active (unused/enrolled) still
rejected
- [x] Server token: re-mint after expire now succeeds (previously
failed)
- [x] Server token: re-mint after revoke still works (existing behavior)
- [x] Server token: mint while active still rejected
- [x] All 20 enrollment token tests pass
- [x] All 39 server token tests pass
- [x] Full verification workflow green (lint, fmt, type-check, tests,
compile, code review, adversarial review, UX review)

Co-authored-by: Blake Irvin <blakeirvin@me.com>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Blake Irvin <blakeirvin@me.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-09-10 12:49:08 -07:00
.agents/skills feat: warn when model definitions use env var expressions (#784) 2026-03-19 23:05:48 +00:00
.claude feat(skills): expose swamp help to agents proactively (#2418) 2026-09-08 17:17:55 -07:00
.github fix(ci): restore PR approval in validate-attestation for auto-merge (#2429) 2026-09-10 09:45:18 -07:00
agent-constraints fix(ci): remove redundant CI jobs, keep adversarial review in CI (#2425) 2026-09-09 06:12:38 -07:00
contributing docs(design): reorganise design/ around the six primitives and fact-check every doc (#2315) 2026-08-29 00:09:28 +01:00
design feat(audit): Phase 4 — SIEM sinks, bulk export, HMAC (#2074) (#2427) 2026-09-10 15:35:49 +01:00
evals/promptfoo test: harden fitness tests, deepen property coverage, add contract gates (#2205) 2026-08-21 14:25:12 +01:00
extensions/models feat(extensions): include PR link and summary in notify comment (#2397) 2026-09-07 14:30:19 -07:00
integration fix(serve): use pre-warmed repos in all serve handlers (swamp-club#2085) (#2432) 2026-09-10 12:39:50 -07:00
packages fix(testing): add .warning() method to mock Logger (swamp-club#2098) (#2430) 2026-09-10 10:12:05 -07:00
scripts feat(skills): expose swamp help to agents proactively (#2418) 2026-09-08 17:17:55 -07:00
src fix(tokens): allow re-minting after revoke or expire (#2433) 2026-09-10 12:49:08 -07:00
verification fix(verification): tolerate missing VERDICT marker in passing reviews (#2414) 2026-09-08 15:08:37 -07:00
.gitattributes ci: add .gitattributes to pin line endings to LF (#1234) 2026-04-28 15:14:54 +01:00
.gitignore feat(serve): add web dashboard at /dashboard (#2208) 2026-08-22 01:36:54 +01:00
AGENTS.md fix(ci): remove redundant CI jobs, keep adversarial review in CI (#2425) 2026-09-09 06:12:38 -07:00
banner.png docs: Refresh README for open alpha (#307) 2026-02-12 23:51:15 +00:00
CLAUDE.md feat: add AGENTS.md for agent-neutral instruction discovery (#2365) 2026-09-03 01:43:46 +01:00
CODE_OF_CONDUCT.md chore: update copyright and branding to Elder Swamp Club (#1504) 2026-06-03 23:23:43 +01:00
CONTRIBUTING.md chore: update copyright and branding to Elder Swamp Club (#1504) 2026-06-03 23:23:43 +01:00
COPYING license: Add AGPLv3 copyright headers to all TypeScript files (#287) 2026-02-11 23:55:22 +00:00
COPYING-EXCEPTION Update COPYING-EXCEPTION (#1703) 2026-06-29 12:28:35 +02:00
COPYRIGHT docs: add OSS FAQ and update licensing/trademark docs (#1595) 2026-06-19 18:03:09 +01:00
deno.json feat(verification): add verify-skills workflow for local skill checks (swamp-club#1870) (#2334) 2026-09-01 18:06:24 +01:00
deno.lock feat(definitions): preserve YAML comments and add description field (#2265) 2026-08-26 18:25:23 +01:00
Dockerfile fix(docker): update Deno base image from 2.8.3 to 2.9.6 (swamp-club#1965) (#2356) 2026-09-02 19:00:06 +01:00
FILE-LICENSE-TEMPLATE.md chore: update copyright and branding to Elder Swamp Club (#1504) 2026-06-03 23:23:43 +01:00
LICENSE chore: update copyright and branding to Elder Swamp Club (#1504) 2026-06-03 23:23:43 +01:00
logo.png docs: add README with project logo (#1) 2026-01-28 10:40:57 -07:00
main.ts feat(logging): emit swamp logs as a native OTel logs signal (swamp-club#1158) (#1858) 2026-07-15 13:38:03 +01:00
main_test.ts chore: update copyright and branding to Elder Swamp Club (#1504) 2026-06-03 23:23:43 +01:00
OSS-FAQ.md docs: add OSS FAQ and update licensing/trademark docs (#1595) 2026-06-19 18:03:09 +01:00
README.md docs(design): reorganise design/ around the six primitives and fact-check every doc (#2315) 2026-08-29 00:09:28 +01:00
sc-mark.png docs(trademarks): use Swamp Club (SC) mark for reserved trademark (#1624) 2026-06-20 00:03:39 +01:00
SECURITY.md chore: update copyright and branding to Elder Swamp Club (#1504) 2026-06-03 23:23:43 +01:00
swamp-club-wordmark.png docs(trademarks): add SWAMP CLUB wordmark to reserved trademarks (swamp-club#802) (#1671) 2026-06-24 17:24:21 +01:00
TRADEMARKS.md docs(trademarks): add SWAMP CLUB wordmark to reserved trademarks (swamp-club#802) (#1671) 2026-06-24 17:24:21 +01:00

Swamp — AI Automation for Hackers

Swamp

Deterministic Automation for AI Agents.

Swamp is a CLI that supercharges AI agents to create operational workflows that are reviewable, shareable, and accurate. Built for agents, there to empower humans. All the data lives in the .swamp/ (the swamp).

Come join the swamp party on discord.

Getting Started

curl -fsSL https://swamp-club.com/install.sh | sh

Quick Start

swamp repo init                    # Claude Code (default)
swamp repo init --tool cursor      # Cursor
swamp repo init --tool opencode    # OpenCode
swamp repo init --tool codex       # Codex

Start your AI agent in the repo and tell it what you want to do. Just ask:

  • "Manage my EC2 fleet — inventory every instance across all regions and flag anything without a cost-center tag"
  • "Set up a workflow to check my bare metal Minecraft servers are online and under 80% memory"
  • "Audit our DNS records and compare them against what's actually running"
  • "Build a workflow that rotates database credentials and stores them in the vault"

The agent will create models, wire up workflows, and run them — all reviewable in .swamp/ before anything touches production.

Learn More

You can learn more about swamp by reading the manual. How Swamp Works is the best place to start - it provides an overview of the entire system.

Core Concepts

  • Models — Typed representations of external systems (cloud resources, CLI tools, APIs). Each model type defines metadata, arguments, methods, and inputs.
  • Definitions — YAML files that instantiate a model type with specific configuration. Support CEL expressions for dynamic values and cross-model references.
  • Workflows — Orchestrate model method executions across parallel jobs and steps, with dependency ordering and trigger conditions.
  • Data — Versioned, immutable artifacts (resources, logs, files) produced by method runs. Searchable by tags.
  • Vaults — Secure storage for secrets and credentials, referenced in definitions via CEL expressions.
  • Tags — Key-value labels on definitions, workflows, and data. Flow from definitions to produced data, overridable at runtime with --tag.

Everything lives in a .swamp/ directory inside a Git repository, with human-friendly symlink views under /models/ and /workflows/.

Local Execution

Swamp runs entirely on your machine. It picks up the environment variables from the shell you run it in — your AWS credentials, SSH keys, kubeconfig, whatever the task needs. No credentials leave your laptop unless a model explicitly calls an external API.


Update

Swamp can update itself in place:

swamp update

Shell Completions

Tab-completion for commands, model names, and workflow names:

# Bash — add to ~/.bashrc
eval "$(swamp completions bash)"

# Zsh with oh-my-zsh
mkdir -p ~/.oh-my-zsh/completions
swamp completions zsh > ~/.oh-my-zsh/completions/_swamp
rm -f ~/.zcompdump* && exec zsh

# Zsh without oh-my-zsh — add to ~/.zshrc
eval "$(swamp completions zsh)"

# Fish
swamp completions fish > ~/.config/fish/completions/swamp.fish

Completions are directory-dependent — they return names from the current directory's swamp repository.

Using Swamp with AI Agents

Swamp ships first-class skills for four AI coding tools:

Tool Init flag Skills dir Instructions file
Claude Code (default) .claude/skills/ CLAUDE.md
Cursor --tool cursor .cursor/skills/ .cursor/rules/swamp.mdc
OpenCode --tool opencode .agents/skills/ AGENTS.md
Codex --tool codex .agents/skills/ AGENTS.md

The skills are bundled into the swamp binary and written into the appropriate directory so your agent discovers them automatically. Each skill teaches the agent how to work with swamp — search for models, create definitions, run workflows, manage vaults, and more.

You can switch tools later or run multiple tools side-by-side — each tool's skills directory is independent and gitignored:

swamp repo upgrade --tool cursor

User-Defined Models

Extend Swamp with custom TypeScript models. Place them in extensions/models/ (or configure via SWAMP_MODELS_DIR or .swamp.yaml).

See the swamp skill in your skills directory (e.g. .claude/skills/swamp/SKILL.md for Claude Code) for details.

Developer Guide

Prerequisites

Commands

deno run dev          # Run the CLI from source
deno run test         # Run the test suite
deno check            # Type-check
deno lint             # Lint
deno fmt              # Format
deno run compile      # Compile the binary

License Compliance

deno run sbom generates a CycloneDX SBOM of every npm and JSR dependency for license-compliance scanning. See scripts/README.md for details on the generator, license resolution, and scanning with FOSSA.

Contributing

Swamp uses an issue-driven contribution model. We don't accept pull requests from external contributors — fork PRs are automatically closed. This isn't about gatekeeping; it's about supply chain security in the age of AI-generated code. When AI agents can produce large, plausible-looking changes, the only way to maintain quality and security is to tightly control the inputs to the development process.

Here's how it works:

  1. You file an issuebug reports and feature requests are very welcome. Be as detailed as you like.
  2. We triage it — A maintainer triages the issue locally using Claude, confirms bugs by tracing through the codebase, and generates a detailed implementation plan. Plans are revised interactively until the approach is solid.
  3. We build it — Elder Swamp Club engineers (with AI agents under our direct control) implement the plan, with full test coverage and code review.
  4. You get credit — We're happy to include you as a co-author on any PR generated from your request.

This means you get the feature you asked for, maintained over time, without having to worry about keeping a fork in sync. See CONTRIBUTING.md for the full details.

Datastores

By default, swamp stores all runtime data (model data, workflow runs, outputs, audit logs, etc.) in the local .swamp/ directory. You can configure a different datastore backend to share state across machines or centralise data.

Default (local filesystem)

When you run swamp repo init, the datastore is .swamp/ inside the repo. No extra configuration needed.

Setting up an external filesystem datastore

Move runtime data to a directory outside the repo (e.g. a shared NFS mount):

swamp datastore setup filesystem --path /mnt/shared/swamp-data

This migrates existing .swamp/ runtime data to the new path and updates .swamp.yaml. A file-based lock prevents concurrent access from multiple processes.

Setting up an S3 datastore

Store runtime data in S3 for team collaboration using the @swamp/s3-datastore extension:

swamp datastore setup extension @swamp/s3-datastore \
  --config '{"bucket":"my-swamp-bucket","prefix":"my-project","region":"us-east-1"}'

This pushes existing local data to S3 and updates .swamp.yaml. Subsequent commands automatically pull changes before execution and push changes after. A distributed lock (S3 conditional writes) prevents concurrent access.

Use --skip-migration on either setup command to skip the initial data migration.

Migrating between datastores

Run swamp datastore setup again with the new backend. For example, to move from a filesystem datastore to S3:

swamp datastore setup extension @swamp/s3-datastore \
  --config '{"bucket":"my-bucket","region":"us-east-1"}'

Or from S3 back to local filesystem:

swamp datastore setup filesystem --path /path/to/data

Each setup command migrates existing data to the new backend.

Checking datastore status

swamp datastore status        # Shows type, health, and config
swamp datastore sync          # Manual bidirectional sync (S3 only)
swamp datastore sync --pull   # Pull-only from S3
swamp datastore sync --push   # Push-only to S3

Stuck locks

If a process crashes without releasing the datastore lock, subsequent commands will wait up to 60 seconds before timing out (locks auto-expire after 30 seconds). To inspect or force-release a stuck lock:

swamp datastore lock status           # Show who holds the lock
swamp datastore lock release --force  # Force-release the lock

Environment variable override

For CI/CD, override the datastore without modifying .swamp.yaml:

export SWAMP_DATASTORE=s3:my-bucket/my-prefix
export SWAMP_DATASTORE=filesystem:/tmp/swamp-data

Repository Directory

Every command runs against a swamp repository. By default this is the current working directory. Pass --repo-dir to point at a different repo per invocation, or set SWAMP_REPO_DIR to persist the override across a shell session or CI job:

swamp model search --repo-dir /path/to/repo
export SWAMP_REPO_DIR=/path/to/repo
swamp model search

Priority order (highest to lowest): --repo-dir flag → SWAMP_REPO_DIR env var → current working directory.

Log Level

By default, swamp outputs at the info level. You can change this once rather than repeating a flag on every command.

Per-invocation flags (highest priority):

swamp -q workflow run my-workflow               # error level only
swamp --log-level debug workflow run my-workflow

Via environment variable (useful for CI/CD):

export SWAMP_LOG_LEVEL=warning
swamp workflow run my-workflow

Permanently for a repository — add to .swamp.yaml:

logLevel: error

Valid levels: trace, debug, info, warning, error, fatal.

Priority order (highest to lowest): -q / --log-level flag → SWAMP_LOG_LEVEL env var → .swamp.yaml logLevel → default (info).

Tracing

Swamp has native OpenTelemetry tracing for diagnosing slow or failing operations. Tracing is opt-in and has zero overhead when disabled.

Set OTEL_EXPORTER_OTLP_ENDPOINT to enable both signals through a shared collector, or configure a signal-specific endpoint:

# Send traces to a local Jaeger instance
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 swamp workflow run my-workflow

# Quick debug: print spans to stderr (no collector needed)
OTEL_TRACES_EXPORTER=console swamp workflow run my-workflow

Traces capture the full execution hierarchy — CLI command, workflow, job, step, model method, and driver execution — with automatic context propagation to in-process extensions and Docker containers via TRACEPARENT.

Logs

When a generic or logs-specific OTLP endpoint is configured, swamp also emits its structured log lines as native OpenTelemetry log records over OTLP. Each record is stamped with the active trace_id/span_id, so logs correlate with the spans they belong to and are filterable by trace, service, and severity in the backend — no need to read a local log file. This is most useful for long-running swamp serve daemons. Secrets registered for a run are redacted from log bodies and attributes before they leave the process, just as they are in the persisted per-run log files.

Opt out with OTEL_LOGS_EXPORTER=none (traces stay on). For high-volume swamp serve, set OTEL_BLRP_USE=1 to batch log exports instead of sending one request per line.

# Export both traces and correlated logs to a collector
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 swamp serve

# Traces only — disable the logs signal
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 OTEL_LOGS_EXPORTER=none swamp serve

Local development

Run Jaeger for a local trace UI:

docker run -d --name jaeger -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest

Then open http://localhost:16686 and search for the swamp service.

Configuration

Variable Purpose Default
OTEL_EXPORTER_OTLP_ENDPOINT Shared collector base URL (none)
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT Complete traces URL; overrides shared URL shared + /v1/traces
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT Complete logs URL; overrides shared URL shared + /v1/logs
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT Complete future metrics URL shared + /v1/metrics
OTEL_EXPORTER_OTLP_HEADERS Shared headers (key=val,key=val) (none)
OTEL_EXPORTER_OTLP_TRACES_HEADERS Trace headers; replace shared headers shared headers
OTEL_EXPORTER_OTLP_LOGS_HEADERS Log headers; replace shared headers shared headers
OTEL_EXPORTER_OTLP_METRICS_HEADERS Future metrics headers; replace shared shared headers
OTEL_TRACES_EXPORTER otlp, console, or none otlp
OTEL_LOGS_EXPORTER otlp, console (stderr), or none otlp
OTEL_SERVICE_NAME Service name for traces and logs swamp
OTEL_BLRP_USE Batch log exports (1 to enable) (per-record)

Signal-specific endpoint values are complete URLs and take precedence over the shared base URL. Signal-specific headers replace, rather than merge with, the shared headers. Empty signal-specific values fall back to the shared setting. The metrics variables establish the same configuration contract for future native metrics support; swamp currently emits traces and logs only.

For a backend such as Axiom that routes each signal to a different dataset:

export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.axiom.co/v1/traces
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="Authorization=Bearer TOKEN,X-Axiom-Dataset=swamp-traces"
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://api.axiom.co/v1/logs
export OTEL_EXPORTER_OTLP_LOGS_HEADERS="Authorization=Bearer TOKEN,X-Axiom-Dataset=swamp-logs"

# Reserved for future native metrics export
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://api.axiom.co/v1/metrics
export OTEL_EXPORTER_OTLP_METRICS_HEADERS="Authorization=Bearer TOKEN,X-Axiom-Metrics-Dataset=swamp-metrics"

Telemetry

Swamp collects anonymous usage telemetry to help us understand which commands are used, how long they take, and what errors occur. All user-identifiable values are redacted before transmission — nothing sensitive is ever sent.

Telemetry is user-global — events are spooled to a single directory regardless of which repository (if any) you are working in:

~/.config/swamp/telemetry/

The path is XDG-aware: if $XDG_CONFIG_HOME is set, the spool lives at $XDG_CONFIG_HOME/swamp/telemetry/ instead. Setting $SWAMP_HOME overrides both ($SWAMP_HOME/config/telemetry/).

Here is a complete example of a telemetry event:

{
  "event": "cli_invocation",
  "distinct_id": "a3f1b2c4-5678-9abc-def0-1234567890ab",
  "properties": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "invocation": {
      "command": "model",
      "subcommand": "create",
      "args": ["prompt", "<REDACTED>"],
      "optionKeys": ["--force"],
      "globalOptions": ["--json"]
    },
    "result": {
      "status": "success",
      "exitCode": 0
    },
    "startedAt": "2026-02-16T10:00:00.000Z",
    "completedAt": "2026-02-16T10:00:01.234Z",
    "durationMs": 1234,
    "swampVersion": "0.13.0",
    "denoVersion": "2.1.0",
    "platform": "linux"
  }
}

Note that positional arguments containing user data (model names, file paths, queries) are replaced with <REDACTED>. Only categorical values defined by swamp itself (like model types) are recorded. Option values are never recorded — only the option keys.

Viewing Telemetry Stats

swamp telemetry stats reads the user-global spool and summarizes recent activity. It does not require a repository context:

swamp telemetry stats           # last 2 days (default)
swamp telemetry stats --days 7  # last 7 days

Disabling Telemetry

Per-invocation:

swamp --no-telemetry workflow run my-workflow

Via environment variable (useful for CI/UAT environments):

export SWAMP_NO_TELEMETRY=1
swamp workflow run my-workflow

Permanently for a repository — add to .swamp.yaml:

telemetryDisabled: true

Permanently for all repo-less runs — create ~/.config/swamp/telemetry.yaml:

disabled: true

This suppresses telemetry when running swamp outside any repository (e.g. swamp telemetry stats, swamp auth login). Inside a repository the .swamp.yaml telemetryDisabled field takes precedence.

Priority order (highest to lowest): --no-telemetry flag → SWAMP_NO_TELEMETRY env var → .swamp.yaml telemetryDisabled: true (per-repo) → ~/.config/swamp/telemetry.yaml disabled: true (user-global, repo-less runs).

Migrating from Repo-Local Telemetry

Older versions of swamp stored telemetry in .swamp/telemetry/ inside each repository. Running swamp repo upgrade automatically migrates unflushed entries from the legacy repo-local spool to the user-global spool. Repositories that have telemetry disabled (telemetryDisabled: true in .swamp.yaml) are skipped. The old .swamp/telemetry/ directory is left in place but is no longer used.

License

Swamp is licensed under the GNU Affero General Public License v3.0 with the Swamp Extension and Definition Exception. No rights are granted for use of our trademarks outside our explicit policy See COPYRIGHT, CONTRIBUTING.md and Open Source FAQ for details.