Skip to content

Releases: systeminit/swamp

swamp 20260317.001439.0-sha.adf8dd48

17 Mar 00:15
Immutable release. Only release title and notes can be modified.
adf8dd4

Choose a tag to compare

What's Changed

  • feat: automatic extension resolution with trusted collectives (#725)

Summary

Closes #665

When users clone a repo with model or vault configurations referencing extension types that aren't installed locally, commands fail with cryptic "Unknown model type" or "Unsupported vault type" errors. This PR adds lazy auto-resolution: when swamp encounters an unknown type whose collective is on a trusted allowlist, it transparently searches the extension registry, installs the matching extension, hot-loads it into the live registries, and continues execution — no manual swamp extension pull needed.

What changes

  • New trustedCollectives config in .swamp.yaml — defaults to ["swamp", "si"] so official extensions auto-resolve out of the box. Users can add more collectives or set [] to disable.
  • ExtensionAutoResolver domain service — standalone service with port interfaces (ExtensionLookupPort, ExtensionInstallerPort, AutoResolveOutputPort) that keeps the domain layer clean of CLI/presentation imports.
  • resolveModelType() / resolveVaultType() helper functions — drop-in replacements for modelRegistry.get() at 7 choke points across CLI commands and the workflow execution service.
  • Hot-loading supportUserModelLoader.loadModels() and UserVaultLoader.loadVaults() gain a skipAlreadyRegistered option so re-running model discovery after install doesn't error on already-registered types.
  • Vault auto-resolution in VaultService.fromRepository() — resolves missing @-prefixed vault types before registerVault(), keeping registerVault() itself sync.
  • Clear UX output — users always see what's happening: searching, installing, installed (with model count), or actionable error messages for not-found/network failures. Both log and JSON output modes supported.

Design decisions

  1. Standalone helper, not embedded in registriesModelRegistry and VaultTypeRegistry remain pure sync data structures. Resolution is a domain service that choke points call explicitly. This is more DDD-aligned: the registry is a repository (stores/retrieves), resolution is a domain service (orchestrates).

  2. Port interfaces for clean architecture — The domain service defines ExtensionLookupPort, ExtensionInstallerPort, and AutoResolveOutputPort interfaces. Concrete adapters in the CLI layer wire the HTTP client, installExtension(), model loaders, and output renderers. This keeps domain → CLI/presentation dependency arrows pointing the right direction.

  3. Two-step type-to-extension resolution — First tries direct lookup by progressively stripping trailing segments (e.g., @swamp/aws/ec2/instance → try @swamp/aws/ec2, then @swamp/aws). Falls back to search with collective filter if direct lookup fails. This handles both exact extension names and partial matches.

  4. Always install latest — Auto-resolution always installs the latest version unless the user has explicitly pinned via extension pull @name@version. This is the right default for trusted collectives where you control releases.

  5. Re-entrancy guard — A Set<string> of types currently being resolved prevents infinite loops if transitive dependencies trigger further resolution.

  6. Collective not allowlisted = silent skip — No auto-resolution is attempted for non-allowlisted collectives. The existing "Unknown model type" error shows as-is. This is intentional — we don't want to suggest the feature exists for untrusted collectives.

User impact

  • Zero-friction onboarding: Clone a repo that uses @swamp/digitalocean/droplet, run swamp model method run, and it just works — the extension installs automatically on first use.
  • No breaking changes: Existing repos work identically. The default trustedCollectives: ["swamp", "si"] only kicks in for @swamp/* and @si/* types. Users who don't use extensions see no difference.
  • Explicit opt-out: Set trustedCollectives: [] in .swamp.yaml to disable entirely.
  • Transparent operation: Every auto-resolution step is logged so users understand why a command takes longer the first time.

Testing

Automated tests (14 new)

  • Skips non-allowlisted collectives (silent, no output)
  • Resolves via direct lookup (strips segments correctly)
  • Tries intermediate candidates before shorter ones (@swamp/aws/ec2 before @swamp/aws)
  • Falls back to search when direct lookup fails
  • Shows notFound output when nothing matches
  • Shows networkError output on fetch failures (TypeError, timeouts)
  • Re-entrancy guard prevents infinite loops
  • Handles non-@ prefixed types (e.g., swamp/echo/v2)
  • Skips types without a collective (single-word types)
  • resolveModelType returns existing definitions without resolver
  • resolveModelType returns undefined for unknown types without resolver
  • resolveVaultType returns true for existing vault types
  • resolveVaultType returns false for unknown types without resolver
  • resolveVaultType skips non-@ types

Manual end-to-end test

Compiled the binary, initialized a fresh repo in /tmp, and ran:

swamp model create @swamp/digitalocean/droplet my-droplet

Result: auto-resolved @swamp/digitalocean from the registry, installed @swamp/digitalocean@2026.03.16.1, hot-loaded 32 models, and successfully created the my-droplet definition — all in one command with clear status output:

INF extension·auto-resolve Extension type "@swamp/digitalocean/droplet" not found locally, searching registry...
INF extension·auto-resolve Found extension "@swamp/digitalocean" (DigitalOcean infrastructure models)
INF extension·auto-resolve Installing "@swamp/digitalocean"@"2026.03.16.1"...
INF extension·auto-resolve Installed "@swamp/digitalocean"@"2026.03.16.1" (32 models registered)
Created: my-droplet (@swamp/digitalocean/droplet)

Full suite

All 3018 tests pass (14 new + 3004 existing), including architecture boundary and DDD layer rule tests.

Verification

  • deno check — passes
  • deno lint — passes
  • deno fmt — passes
  • deno run test — 3018 passed, 0 failed
  • deno run compile — binary compiled successfully

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260317.001439.0-sha.adf8dd48/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260317.001439.0-sha.adf8dd48/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260317.001439.0-sha.adf8dd48/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260317.001439.0-sha.adf8dd48/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260316.225105.0-sha.210fdbaf

16 Mar 22:52
Immutable release. Only release title and notes can be modified.
210fdba

Choose a tag to compare

What's Changed

  • fix: resolve dual Zod instance in compiled binary (#724)

Summary

Fixes #723.

In the compiled binary (deno compile), extension bundles with externalized import { z } from "npm:zod@4" resolve to a different Zod module instance than swamp's own embedded Zod. This causes instanceof z.ZodType checks, z.toJSONSchema(), and _zod property lookups to fail — even though both are the same Zod version (4.3.6). Works fine in dev mode (deno run) because Deno resolves both to the same cached module.

Problem

The --external npm:zod@4 flag in deno bundle was meant to make extensions share swamp's Zod instance. In dev mode, the dynamic import("npm:zod@4") in the bundle resolves to the same cached module. But in the compiled binary, deno compile resolves it to a separate module instance, breaking:

  • schema instanceof z.ZodTypefalse
  • z.toJSONSchema(schema)Cannot read properties of undefined (reading '_zod')
  • Any cross-instance Zod operations (.passthrough(), .default(), etc.)

Fix

Post-process extension bundles to replace external Zod imports with references to swamp's Zod instance exposed via globalThis.__swamp_zod:

Before (bundle output):

import { z } from "npm:zod@4";

After (rewritten):

const { z } = globalThis.__swamp_zod;

Changes

  • src/domain/models/bundle.ts: Add installZodGlobal() (sets globalThis.__swamp_zod to swamp's Zod module) and rewriteZodImports() (regex replaces Zod imports — handles named, aliased, star imports, versioned/unversioned specifiers). Applied in bundleExtension() for non-selfContained bundles.
  • src/domain/models/user_model_loader.ts: Call installZodGlobal() before loading bundles; apply rewriteZodImports at import-time for old cached bundles.
  • src/domain/drivers/user_driver_loader.ts: Same pattern.
  • src/domain/vaults/user_vault_loader.ts: Same pattern.
  • src/domain/models/bundle_test.ts: 9 new tests — unit tests for rewriteZodImports (named, aliased, star, unversioned, multiple, non-zod untouched, idempotency, single-quoted) and end-to-end test verifying z.toJSONSchema() works on bundled schemas.

Design decisions

  • Rewrite at both bundle-time AND import-time: Bundle-time ensures new cached bundles are correct. Import-time catches old cached bundles that still have raw import statements. The rewrite is idempotent.
  • selfContained bundles are unaffected: They inline Zod and never have external imports.
  • No changes to extension author workflow: Extensions still write import { z } from "npm:zod@4" — the rewrite is transparent.

Alternatives rejected

  • Stop externalizing Zod: Bloats every bundle by ~500KB and doesn't fix the problem — swamp's own code still uses its z for instanceof/toJSONSchema.
  • Duck-typing instead of instanceof: Much larger change surface across 6+ files, fragile, loses Zod's built-in schema capabilities.
  • Import maps: Don't work in compiled binaries.

Test plan

  • deno check — type checking passes
  • deno lint — linting passes
  • deno fmt — formatting passes
  • deno run test — 3004/3004 tests pass
  • deno run compile — binary compiles
  • Standalone PoC validates approach in both dev mode and compiled binary
  • Manual test with compiled binary: swamp extension pull @dougschaefer/cisco-collaboration-endpointsswamp model type describe shows JSON schema without _zod errors

🤖 Generated with Claude Code

Co-authored-by: Douglas Schaefer dougschaefer6@users.noreply.github.com


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.225105.0-sha.210fdbaf/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.225105.0-sha.210fdbaf/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.225105.0-sha.210fdbaf/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.225105.0-sha.210fdbaf/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260316.173839.0-sha.1abd500d

16 Mar 17:39
Immutable release. Only release title and notes can be modified.
1abd500

Choose a tag to compare

What's Changed

  • feat: per-model datastore locks for concurrent model operations (#713)

Summary

Replaces the single global datastore lock with per-model locks so that operations on different models can run concurrently. This fixes the core issue where running model method run on one model blocked all other model operations, even on completely unrelated models.

  • Introduces per-model lock files at data/{modelType}/{modelId}/.lock — operations on different models no longer block each other
  • Refactors the sync coordinator from module-level singletons to a Map<string, SyncEntry> supporting multiple concurrent locks
  • Adds model-scoped S3 sync (pullChangedForModel) so S3 datastores only pull/push the relevant model's files
  • Extracts model references from workflows to acquire only the locks needed, with automatic fallback to global lock for dynamic CEL references
  • Updates breakglass commands (lock status, lock release --model) to show and manage per-model locks

Closes #706

Why per-model locks?

The global lock was designed for simplicity, but it creates an O(n) bottleneck for fleet operations. When managing 100+ VMs where each method call takes 30-90 seconds, a single global lock means:

  • Before: Running healthCheck on prod-vm-1 blocks a quick checkService on dev-vm-2 — the second command waits or times out
  • After: Both run concurrently because they acquire separate locks (data/command/shell/prod-vm-1/.lock and data/command/shell/dev-vm-2/.lock)

Structural commands (data gc, repo init, model create/delete) still use the global lock, and per-model lock acquisition checks for a held global lock first — so data integrity is preserved.

Impact on users

  • model method run on different models runs concurrently (the primary win)
  • workflow run acquires only the locks for models referenced in the workflow
  • model evaluate and workflow evaluate use per-model locks for single-model/workflow evaluation
  • S3 datastores benefit equally — model-scoped pull on lock acquisition, brief global lock only during push (seconds, not minutes)
  • No breaking changes — the lock file format and CLI interface are unchanged; existing repos work without migration
  • Deadlock prevention — locks are always acquired in sorted order when multiple models are involved

What changed

File Change
datastore_sync_coordinator.ts Refactored to Map<string, SyncEntry> with named lock support
repo_context.ts Added requireInitializedRepoUnlocked(), createModelLock(), acquireModelLocks()
s3_cache_sync.ts Added pullChangedForModel() for model-scoped S3 sync
model_method_run.ts Uses per-model lock instead of global lock
workflow_run.ts Extracts model refs, acquires per-model locks (falls back to global for dynamic refs)
model_evaluate.ts Single-model evaluate uses per-model lock; --all keeps global
workflow_evaluate.ts Single-workflow evaluate uses per-model lock; --all keeps global
datastore_lock.ts lock status scans per-model locks; lock release --model added
datastore_output.ts Shows lock scope (global vs per-model) in status output
model_reference_extractor.ts New: extracts model references from workflows for lock targeting

Test plan

  • All 2968 existing tests pass
  • deno check, deno lint, deno fmt clean
  • New unit tests for coordinator, repo context helpers, and model reference extractor
  • Manual verification: two concurrent model method run on different models both proceed without blocking, each with its own .lock file
  • Binary compiles successfully

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.173839.0-sha.1abd500d/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.173839.0-sha.1abd500d/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.173839.0-sha.1abd500d/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.173839.0-sha.1abd500d/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260316.173713.0-sha.6dc69231

16 Mar 17:38
Immutable release. Only release title and notes can be modified.
1abd500

Choose a tag to compare

What's Changed

  • docs: add z.infer type casts to readResource skill examples (#721)

Summary

  • Update all readResource() examples in the swamp-extension-model skill to use z.infer<typeof Schema> casts instead of accessing properties on untyped Record<string, unknown>
  • Fix outdated vault resolution docs in api.md — vault expressions have been automatically resolved since #712, not "returned as-is"
  • Add type aliases (VpcData, DropletData, CustomerData, BucketData) next to each Zod schema so the cast pattern is immediately visible

Why this is the right fix for #716

The issue asks for typed readResource() to eliminate as any casts in extension models. The root cause isn't a missing framework feature — it's that the skill examples Claude uses to generate extension models were accessing readResource() results without type narrowing. Since extension models already define Zod schemas for their resources, z.infer<typeof Schema> derives the TypeScript type at zero runtime cost (erased during bundling). The as cast is safe because data was already validated against that same schema on write.

By fixing the examples, Claude will generate typed code from the start, and extension authors won't hit the lint violations described in #716.

Closes #716

Test plan

  • Verify examples in api.md, examples.md, and scenarios.md are consistent — every readResource() call has a corresponding z.infer type alias and cast
  • Verify vault resolution docs in api.md match actual behavior from #712

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.173713.0-sha.6dc69231/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.173713.0-sha.6dc69231/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.173713.0-sha.6dc69231/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.173713.0-sha.6dc69231/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260316.163214.0-sha.4955f3d2

16 Mar 16:33
Immutable release. Only release title and notes can be modified.
4955f3d

Choose a tag to compare

What's Changed

  • fix: resolve multi-line imports in local_import_resolver (#719)

Summary

  • Fix IMPORT_PATTERN regex in local_import_resolver.ts to match across newlines (.*?[\s\S]*?), resolving #715
  • The clover code generator writes multi-line imports (e.g., import {\n createResource,\n deleteResource,\n} from "./_lib/aws.ts"), which the previous single-line regex silently skipped
  • This caused shared dependencies like _lib/aws.ts to be excluded from extension packages during swamp extension push, making all @swamp/aws/* extensions fail on pull with Module not found errors

Changes

  • src/domain/models/local_import_resolver.ts: Changed IMPORT_PATTERN from /(?:import|export)\s+.*?from\s+["'](\.\.?\/[^"']+)["']/g to /(?:import|export)\s+[\s\S]*?from\s+["'](\.\.?\/[^"']+)["']/g so the regex matches import/export statements that span multiple lines
  • src/domain/extensions/extension_import_resolver_test.ts: Added test case "resolveLocalImports resolves multi-line imports" that creates a _lib/aws.ts dependency imported via a multi-line import statement and verifies it is correctly discovered

Test plan

  • New multi-line import test passes
  • All 8 import resolver tests pass
  • Full test suite passes (2979 tests)
  • deno check — type checking passes
  • deno lint — linting passes
  • deno fmt — formatting passes
  • deno run compile — binary compiles successfully

Fixes #715

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.163214.0-sha.4955f3d2/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.163214.0-sha.4955f3d2/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.163214.0-sha.4955f3d2/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.163214.0-sha.4955f3d2/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260316.161521.0-sha.7fc8a69a

16 Mar 16:16
Immutable release. Only release title and notes can be modified.
7fc8a69

Choose a tag to compare

What's Changed

  • fix: skip auth check in extension push --dry-run (#718)

Summary

  • swamp extension push --dry-run now works without authentication
  • Auth loading (step 3) and collective membership validation (step 4) are wrapped in a !dryRun guard so they only run for real pushes
  • Unblocks CI validation workflows that don't have swamp-club credentials

What changed

In src/cli/commands/extension_push.ts, the auth check and collective membership validation previously ran unconditionally before the dry-run exit point. Since --dry-run only builds the archive locally and never contacts the registry, authentication is unnecessary.

The credentials variable is now typed as | undefined and only populated when not in dry-run mode. The existing non-dry-run code paths (steps 13 and 17) already have their own if (!options.dryRun) guards, so credentials are never accessed in dry-run mode.

Trade-off

Dry-run will skip collective ownership validation. This is acceptable because:

  • Dry-run validates the build, not permissions — its purpose is to verify the extension compiles and packages correctly
  • The real push still enforces collective membership — no security bypass
  • CI pipelines just need to verify the extension archives correctly without needing registry credentials

Test plan

  • deno check passes
  • deno lint passes
  • deno fmt passes
  • deno run test passes
  • deno run compile passes
  • swamp extension push <manifest> --dry-run succeeds without auth
  • swamp extension push <manifest> without auth still fails with "Not authenticated"

Fixes #717

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.161521.0-sha.7fc8a69a/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.161521.0-sha.7fc8a69a/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.161521.0-sha.7fc8a69a/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260316.161521.0-sha.7fc8a69a/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260315.010849.0-sha.5a9897f6

15 Mar 01:09
Immutable release. Only release title and notes can be modified.
5a9897f

Choose a tag to compare

What's Changed

  • feat: add --global-arg flag to model create command (#714)

Summary

  • Adds a repeatable --global-arg key=value option to swamp model create, eliminating the need to manually edit definition YAML to set globalArguments after creation
  • Reuses the existing parseKeyValueInputs parser, supporting dot notation for nested objects (config.db.host=localhost), file references (key=@path), and values containing =
  • Validates provided args against the model type's globalArguments Zod schema (when one exists) at create time, failing early with clear errors

User impact

Before this change, setting globalArguments required a two-step workflow:

swamp model create aws/ec2 my-vpc
swamp model edit my-vpc  # manually add globalArguments in YAML

Now it's a single command:

swamp model create aws/ec2 my-vpc \
  --global-arg region=us-east-1 \
  --global-arg config.timeout=30

Nested objects are supported via dot notation:

--global-arg config.db.host=localhost --global-arg config.db.port=5432
# → { config: { db: { host: "localhost", port: "5432" } } }

Invalid args are caught immediately:

swamp model create mytype foo --global-arg badformat
# Error: Invalid input format: "badformat". Expected key=value format.

Test plan

  • deno check — type checking passes
  • deno lint && deno fmt — clean
  • deno run test src/cli/commands/model_create_test.ts — 12 tests pass (9 new)
  • deno run test — full suite passes (2978 tests)
  • deno run compile — binary compiles
  • Manual testing: simple args, multiple args, nested dot notation, values with =, error cases (missing =, empty key), default behavior without flag

Closes #699

🤖 Generated with Claude Code

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


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260315.010849.0-sha.5a9897f6/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260315.010849.0-sha.5a9897f6/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260315.010849.0-sha.5a9897f6/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260315.010849.0-sha.5a9897f6/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260315.000926.0-sha.aa09224f

15 Mar 00:10
Immutable release. Only release title and notes can be modified.
aa09224

Choose a tag to compare

What's Changed

  • fix: resolve vault expressions in readResource() (#712)

Closes #710

Summary

  • Add resolveVaultRefsInData() to transparently resolve vault expression strings (${{ vault.get('...', '...') }}) back to their original secret values when reading resource data via readResource()
  • Wire VaultService and SecretRedactor from the execution context into createResourceReader() in RawExecutionDriver
  • Add runtime validation that JSON.parse returns a plain object (not an array or primitive), fixing a type safety gap where the Record<string, unknown> return type was not enforced

Why this is needed

When extension models write resource data with sensitive fields, processSensitiveResourceData() replaces actual values with vault expression strings like ${{ vault.get('vault-name', 'key') }}. Before this change, readResource() returned these as literal strings, which meant:

  1. Leaked vault internals — extension authors had to know about and parse vault expression syntax
  2. Confusing errors — code expecting an API key got a template string instead, causing opaque failures downstream
  3. Broken round-trips — write a secret, read it back, get something completely different

How it works

  • After JSON parsing, resolveVaultRefsInData() recursively walks all string values in the result object
  • Strings matching the vault ref pattern (anchored regex — must be the entire string) are resolved via VaultService.get()
  • Resolved secrets are registered with SecretRedactor.addSecret() to prevent log leakage
  • Without a VaultService, behavior is unchanged (backward compatible)
  • The JSON parse type safety fix ensures corrupted/manually-edited data files fail fast with a clear error instead of silently returning wrong types

Files changed

File Change
src/domain/models/data_writer.ts Add resolveVaultRefsInData(), update createResourceReader() signature and implementation
src/domain/drivers/raw_execution_driver.ts Pass vaultService and redactor to createResourceReader()
src/domain/models/model.ts Update JSDoc to reflect new behavior
src/domain/models/data_writer_test.ts 10 new tests covering vault resolution, backward compat, type safety

Test plan

  • deno check — type checking passes
  • deno lint — no lint errors
  • deno fmt — properly formatted
  • deno run test — all 2967 tests pass (48 in data_writer_test.ts)
  • deno run compile — binary compiles successfully

🤖 Generated with Claude Code

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


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260315.000926.0-sha.aa09224f/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260315.000926.0-sha.aa09224f/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260315.000926.0-sha.aa09224f/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260315.000926.0-sha.aa09224f/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260314.235538.0-sha.0cbfc0e8

14 Mar 23:56
Immutable release. Only release title and notes can be modified.
0cbfc0e

Choose a tag to compare

What's Changed

  • feat: add readResource() to MethodContext (#711)

Summary

  • Add symmetric readResource(instanceName, version?) convenience method to MethodContext, eliminating the JSON.parse(new TextDecoder().decode(context.dataRepository.getContent(...))) boilerplate every extension model repeats when reading stored data
  • Create createResourceReader() factory in data_writer.ts alongside the existing createResourceWriter() for symmetry
  • Wire readResource into the raw execution driver and add a stub in the Docker runner (returns null with warning since containers lack host data access)
  • Update all extension model docs (API reference, SKILL.md, examples, scenarios) to use readResource as the preferred read path

User impact

Extension model authors can now read stored resource data with one call:

// Before (every model repeated this)
const content = await context.dataRepository.getContent(
  context.modelType, context.modelId, "vpc");
if (!content) throw new Error("...");
const data = JSON.parse(new TextDecoder().decode(content));

// After
const data = await context.readResource!("vpc");
if (!data) throw new Error("...");

Vault reference expressions are returned as-is (not resolved). The low-level getContent() remains available for binary data.

Test plan

  • 4 unit tests for createResourceReader (happy path, null for missing data, version passthrough, vault reference preservation)
  • 1 test verifying readResource is available on context during method execution
  • deno check passes
  • deno lint passes
  • deno fmt passes
  • Full test suite passes (2957 tests, 0 failures)
  • deno run compile succeeds

Closes #709
Closes #708

🤖 Generated with Claude Code

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


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260314.235538.0-sha.0cbfc0e8/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260314.235538.0-sha.0cbfc0e8/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260314.235538.0-sha.0cbfc0e8/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260314.235538.0-sha.0cbfc0e8/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

swamp 20260313.235152.0-sha.bb94615c

13 Mar 23:52
Immutable release. Only release title and notes can be modified.
bb94615

Choose a tag to compare

What's Changed

  • fix: add 'install' as alias for 'extension pull' (#704)

Summary

  • Add .alias("install") to the extension pull command so swamp extension install works as expected
  • AI agents naturally try swamp extension install which previously failed with a confusing error

Closes #702

Test plan

  • deno check passes
  • deno lint passes
  • swamp extension --help shows pull, install in the command list
  • swamp extension install <ext> routes to the pull handler

🤖 Generated with Claude Code


Installation

macOS (Apple Silicon):

curl -L https://github.com/systeminit/swamp/releases/download/v20260313.235152.0-sha.bb94615c/swamp-darwin-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

macOS (Intel):

curl -L https://github.com/systeminit/swamp/releases/download/v20260313.235152.0-sha.bb94615c/swamp-darwin-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (x86_64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260313.235152.0-sha.bb94615c/swamp-linux-x86_64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/

Linux (aarch64):

curl -L https://github.com/systeminit/swamp/releases/download/v20260313.235152.0-sha.bb94615c/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/