Releases: systeminit/swamp
swamp 20260311.170115.0-sha.0c667d10
What's Changed
- docs: remove logical views and fix .swamp/ references in design docs (#687)
docs: remove logical views and fix .swamp/ references in design docs
Summary
The datastore refactor moved source-of-truth files from .swamp/ to top-level models/, workflows/, vaults/ directories and replaced symlink-based "logical views" with a NoopRepoIndexService. The design docs were stale — they still referenced logical views and .swamp/ as the canonical storage for definitions/workflows/vaults.
This PR updates all 9 design docs to reflect the current architecture:
- high-level.md: Replace
.swamp/aggregate storage + logical views with top-level source-of-truth dirs + datastore - repo.md: Remove
.swamp/internal storage and logical view sections; documentNoopRepoIndexService; replace symlink directory structure with real file layout - agent.md: Replace logical views +
.swamp/direct access with source-of-truth directories + datastore + CLI abstraction - models.md: Fix definition paths (
models/), data paths (datastore), output paths (datastore); remove logical views section - workflow.md: Fix workflow paths (
workflows/), run paths (datastore), data paths (datastore); remove logical views section - vaults.md: Fix vault config path (
vaults/{type}/{id}.yaml); remove logical views; update secret storage to note datastore paths - expressions.md: Add datastore path notes for
data/and evaluated directories - extension.md: Note
.swamp/bundles/as a datastore path - datastores.md: Emphasize source-of-truth files are always top-level
Test plan
- All paths verified against
src/infrastructure/persistence/paths.ts,src/cli/repo_context.ts, and repository implementations - No remaining stale references to
/.swamp/definitions/,/.swamp/workflows/, or/.swamp/vault/ - Remaining "symlink" references are appropriate (latest data symlinks, extension safety checks, noop explanation)
- Docs-only change — no code changes, no tests/compilation needed
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.170115.0-sha.0c667d10/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/v20260311.170115.0-sha.0c667d10/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/v20260311.170115.0-sha.0c667d10/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/v20260311.170115.0-sha.0c667d10/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260311.165546.0-sha.5d8b2333
What's Changed
- fix: prevent 1Password CLI from reading exhausted stdin pipe (#686)
Summary
Fixes #668 — swamp vault put to a 1Password-backed vault stores an empty password field when the secret value is piped via stdin.
Root cause: The runOp() helper in OnePasswordVaultProvider spawned op subprocesses without setting stdin: "null", so they inherited the parent process's stdin. When stdin was a pipe (already consumed by readStdin()), the op CLI detected the pipe and attempted to read from it, getting an empty string that overwrote the field=value command-line argument.
Fix: Add stdin: "null" to both Deno.Command calls in the 1Password vault provider (runOp and checkOpInstalled) so child processes never inherit the parent's stdin. No other vault providers are affected — only the 1Password provider shells out to a CLI tool.
The inline KEY=VALUE syntax was unaffected because stdin remains a TTY in that path, so op never tries to read from it.
Test Plan
- Existing unit tests pass (
deno run test) deno check,deno lint,deno fmtall clean- The fix can be verified manually with:
echo "test-value" | swamp vault put <1p-vault> TEST_KEY
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.165546.0-sha.5d8b2333/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/v20260311.165546.0-sha.5d8b2333/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/v20260311.165546.0-sha.5d8b2333/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/v20260311.165546.0-sha.5d8b2333/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260311.162257.0-sha.7f330d44
What's Changed
- fix: handle symlinks and YAML type field in definition repository (#685)
Summary
Fixes #683 — after the datastore refactor, model definitions using symlinks or scoped extension types (e.g., @smith/kibana-dev) were no longer discovered.
Two bugs in yaml_definition_repository.ts:
-
Symlinks silently skipped:
Deno.readDir()returnsisFile: falsefor symlinks, sofindAll(),findByNameGlobal(), andfindAllGlobal()all missed symlinked YAML files. Fixed by also checkingentry.isSymlink, withassertSafePath()guarding against symlinks that escape the repo boundary. -
Type reconstructed from path instead of YAML:
collectAllDefinitions()andsearchDefinitionByName()reconstructed the model type from directory path segments, ignoring thetypefield in the parsed YAML. This produced wrong types for scoped extensions where the directory name doesn't match the full type (e.g., path sayskibana-devbut YAML says@smith/kibana-dev). Fixed by preferringdefinition.typefrom the YAML, falling back to path-based type only when absent.
Important: The path traversal boundary uses repoDir (not baseDir/models/) so that symlinks targeting extensions/models/ or .swamp/definitions/ — inside the repo but outside models/ — are correctly allowed while symlinks escaping the repo are still rejected.
User impact
Users with extension models installed via symlinks (or legacy symlink layouts from before repo upgrade) will have their models discovered again. Users with scoped extension types will see the correct type reported instead of a truncated path-based type.
Test plan
- Symlinked YAML file discovered by
findAll()(target outsidemodels/, in.swamp/definitions/) - Symlinked YAML file discovered by
findAllGlobal()(target inextensions/models/) - Symlinked YAML file discovered by
findByNameGlobal()(target inextensions/models/) - Combined scenario: symlinked extension with scoped type
@smith/kibana-dev— both symlink discovery and type resolution work together - Type read from YAML
typefield, not path, when present - Path-based type used as fallback when YAML
typefield is missing - Symlink pointing outside repo boundary is rejected (path traversal protection)
- All 2823 existing tests pass
-
deno check,deno lint,deno fmt,deno run compilepass
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.162257.0-sha.7f330d44/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/v20260311.162257.0-sha.7f330d44/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/v20260311.162257.0-sha.7f330d44/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/v20260311.162257.0-sha.7f330d44/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260311.151803.0-sha.f73d7a6d
What's Changed
- fix: respect --repo-dir for user model and vault loading (#684)
Summary
Fixes #682.
loadUserModels(), loadUserVaults(), and initTelemetryService() in src/cli/mod.ts hardcoded Deno.cwd() to locate the swamp repo at CLI startup — before Cliffy parses command options. This meant --repo-dir was completely ignored for:
- Extension model type registration → "Unknown model type" when running from a different directory
- User vault implementation loading
- Telemetry config and log level resolution from
.swamp.yaml
Approach
Pre-parse --repo-dir from the raw args array (following the existing getOutputModeFromArgs pattern for --json) and thread the resolved path through all four startup functions. This is the minimal fix — no command-level changes, no changes to requireInitializedRepo, and behavior is identical when --repo-dir is not specified.
Changes
src/cli/context.ts— AddedgetRepoDirFromArgs(args)that extracts--repo-dirfrom raw CLI args (supports both--repo-dir <value>and--repo-dir=<value>forms), resolves to an absolute path, defaults to cwdsrc/cli/mod.ts— CallgetRepoDirFromArgs(args)once at the top ofrunCli(), pass the result toloadUserModels(repoDir),loadUserVaults(repoDir),initTelemetryService(repoDir), and the marker read blocksrc/cli/context_test.ts— 6 new tests for the pre-parser (no flag, space separator, equals separator, relative path resolution, missing value, flag among other args)
Test Plan
- 6 new unit tests for
getRepoDirFromArgscovering all arg forms and edge cases - Full test suite passes (2811 tests, 0 failures)
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.151803.0-sha.f73d7a6d/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/v20260311.151803.0-sha.f73d7a6d/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/v20260311.151803.0-sha.f73d7a6d/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/v20260311.151803.0-sha.f73d7a6d/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260311.151523.0-sha.5c71d81f
What's Changed
- fix: reject path traversal in workflow names (#681)
Summary
WorkflowSchema.namewas missing the path-traversal validation thatDefinitionSchemaalready has —swamp workflow create "../../etc/passwd"silently succeeded (exit 0) instead of failing- Added
.refine()guard rejecting..,/,\, and null bytes (matching theDefinitionSchemapattern) - Added eager name validation in
Workflow.create()so path traversal is caught even when the full schema parse is skipped (empty-jobs code path) - 4 new unit tests covering all rejection cases
Test plan
- 26 workflow unit tests pass (including 4 new path traversal tests)
-
deno checkpasses -
deno lintpasses -
deno fmt --checkpasses
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.151523.0-sha.5c71d81f/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/v20260311.151523.0-sha.5c71d81f/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/v20260311.151523.0-sha.5c71d81f/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/v20260311.151523.0-sha.5c71d81f/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260311.135022.0-sha.55361247
What's Changed
Summary
Follow-up to #678 (datastore lock deadlock fix) and #675 (non-TTY interactive command fix). Addresses three issues identified in adversarial review of those PRs:
-
TOCTOU race in lock release (
datastore_lock.ts): AddedforceRelease(expectedNonce)to theDistributedLockinterface so each backend (S3Lock, FileLock) can verify the nonce and delete in the tightest possible sequence. Thedatastore lock releasecommand now delegates to this method instead of branching on config type with separate delete logic. This also removed the redundantS3Clientinstantiation andjoinimport from the command handler. -
Double marker file read in
requireInitializedRepo(repo_context.ts): The refactoring in #678 causedrequireInitializedRepoto read the.swamp.yamlmarker file twice — once insideresolveDatastoreForRepoand again directly.resolveDatastoreForReponow returns the marker it already read, andrequireInitializedReporeuses it, eliminating the redundant I/O. -
extension_search.tsmissed in #675: PR #675 updated 10 interactive search commands to useinteractiveOutputMode()but missedextension_search. Applied the same pattern so it won't crash with "Raw mode is not supported" in non-TTY contexts (e.g.echo "" | swamp extension search aws).
Test plan
- 6 new tests for
forceRelease(3 per lock backend): matching nonce, mismatched nonce, no lock exists - Updated
resolveDatastoreForRepotest to verify marker is returned - All 2805 tests pass
-
deno checkpasses -
deno lintpasses -
deno fmt --checkpasses
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.135022.0-sha.55361247/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/v20260311.135022.0-sha.55361247/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/v20260311.135022.0-sha.55361247/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/v20260311.135022.0-sha.55361247/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260311.132338.0-sha.398857eb
What's Changed
- fix: prevent datastore lock commands from deadlocking on stuck lock (#678)
Fixes #676
Summary
datastore lock statusanddatastore lock release --forcecalledrequireInitializedRepo()which acquires the datastore lock viaregisterDatastoreSync()— causing the breakglass commands to deadlock on the very lock they were meant to inspect or release- Added
resolveDatastoreForRepo()tosrc/cli/repo_context.ts— a lightweight function that resolves repo path + datastore config without acquiring the lock. The lock commands now use this instead - Refactored
requireInitializedRepo()to callresolveDatastoreForRepo()internally, keeping a single source of truth for repo validation and datastore config resolution - Zero user impact for normal usage — only the two broken breakglass commands are affected, and they now work
Test Plan
- New tests for
resolveDatastoreForRepo: returns correct config without acquiring lock, throwsUserErrorfor non-initialized repos - All 2799 tests pass
-
deno fmt --checkpasses -
deno lintpasses -
deno checkpasses - Verified no UAT test impact — no UAT tests exercise
datastore lockcommands
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.132338.0-sha.398857eb/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/v20260311.132338.0-sha.398857eb/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/v20260311.132338.0-sha.398857eb/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/v20260311.132338.0-sha.398857eb/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260311.105043.0-sha.fa48cb0c
What's Changed
- fix: gracefully handle non-TTY context in interactive commands (#675)
Fixes #674
Summary
-
Interactive search commands (model search, type search, workflow search, etc.) use Ink for terminal UIs which requires raw mode on stdin. In non-TTY contexts (piped input, CI, AI agents), Ink crashes with "Raw mode is not supported". This was a pre-existing bug — it existed before datastores — but the introduction of the datastore lock made it much worse: the crash now leaks the lock, blocking all subsequent swamp commands for ~60 seconds until timeout.
-
Fix 1 —
interactiveOutputMode()helper (src/cli/context.ts): A new function that returns"json"whenctx.outputModeis"json"OR when stdin is not a TTY. Applied surgically to all 10 interactive search commands so they fall back to structured JSON output instead of crashing. Non-interactive commands (version, model get, workflow run, etc.) are unaffected — they keep their normal output mode. -
Fix 2 — Safety net in
main.ts: AddedflushDatastoreSync()to the top-level catch block so the datastore lock is released even on unexpected crashes that bypassrunCli()'s own error handling.flushDatastoreSync()is idempotent (nulls its state on first call), so calling it a second time is harmless.
Commands updated with interactiveOutputMode:
model search,type search,vault search,vault type searchworkflow search,workflow run search,workflow history searchmodel method history search,model output search,data search
Test Plan
- All 2797 tests pass (zero failures)
-
deno fmt --checkpasses -
deno lintpasses -
deno checkpasses - Verified the global outputMode approach (auto-switching all commands) caused 6 test failures — confirmed the surgical per-command approach is correct
- Verified non-interactive commands retain log-mode output in non-TTY contexts
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260311.105043.0-sha.fa48cb0c/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/v20260311.105043.0-sha.fa48cb0c/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/v20260311.105043.0-sha.fa48cb0c/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/v20260311.105043.0-sha.fa48cb0c/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260310.230813.0-sha.fcc69c53
What's Changed
- fix: address adversarial review findings for distributed locking and S3 sync (#672)
Summary
Addresses findings from two rounds of adversarial review on the datastore PR.
Changes
Critical: Path traversal in S3 cache sync
- Added
assertSafePath()that validates resolved paths stay within the cache directory - Applied to
pullFile(),pullChanged(),pushFile(), andgetCachePath() - Prevents malicious S3 keys (e.g.,
../../../home/user/.bashrc) from writing outside the cache
High: Lock theft via unconditional heartbeat overwrites
- Added nonce-based fencing tokens to distributed locks
LockInfonow includes an optionalnoncefield (UUID generated on acquire)extend()reads the current lock and verifies the nonce matches before writing- If another process has acquired the lock (different nonce), the old holder self-revokes
- This prevents a paused process from overwriting a legitimately acquired lock after recovery
High: Stale lock detection TOCTOU
- Simplified stale lock cleanup to best-effort delete + conditional write retry
- The nonce fencing makes this safe: even if we accidentally delete a fresh lock, the old holder's
extend()will see the wrong nonce and self-revoke - Removed the ineffective double-check pattern that didn't actually close the race window
High: Degraded mode silently allows corruption
- Lock acquisition failure now throws instead of logging a warning and continuing
- Concurrent commands without locks would corrupt data; failing fast is the correct behavior
Medium: Hidden file exclusion asymmetry
pushChanged()andpushAll()now skip only specific metadata files (.datastore-index.json,.push-queue.json,.datastore.lock) instead of all dot-files- Matches
pullAll()behavior so hidden files like.gitkeepsync correctly
Medium: S3 bucket name validation
- Added regex validation for S3 bucket names (3-63 chars, lowercase, alphanumeric/hyphens/dots)
- Applied to both
SWAMP_DATASTOREenv var parsing and.swamp.yamlconfig loading
Medium: S3 client null body check
- Replaced
response.Body!non-null assertion with explicit null check and descriptive error
Medium: SIGINT handler timeout
- Lock release on Ctrl-C now has a 5-second timeout before force-exiting
- Prevents the process from hanging if S3 is unreachable
Medium: Force release holder verification
swamp datastore lock release --forcenow re-verifies the lock holder (via nonce) before deleting- Prevents accidentally deleting a lock that was legitimately acquired between inspect and delete
Test plan
-
deno checkpasses -
deno lintpasses -
deno fmtpasses -
deno run test— 2797 tests pass -
deno run compilesucceeds
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230813.0-sha.fcc69c53/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/v20260310.230813.0-sha.fcc69c53/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/v20260310.230813.0-sha.fcc69c53/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/v20260310.230813.0-sha.fcc69c53/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/swamp 20260310.230044.0-sha.2610458f
What's Changed
- fix: prevent auto-merge race with adversarial review (#673)
Summary
- Replaces
gh pr merge --auto --squashwithgh pr merge --squashin the CI auto-merge job - The
--autoflag enables GitHub's persistent auto-merge, which merges based on branch protection rules rather than the YAMLneeds:dependencies - This caused PR #669 to auto-merge after the normal review passed but before the adversarial review completed
Root cause
The auto-merge job has needs: [test, deps-audit, claude-review, claude-adversarial-review], but --auto tells GitHub "merge this PR whenever branch protection requirements are met." If claude-adversarial-review isn't a required status check in branch protection settings, GitHub merges as soon as the other checks pass — ignoring the YAML dependency.
Fix
Remove --auto so the merge command executes directly when the job runs. Since the job is gated by needs:, it only runs after all four dependencies (including adversarial review) succeed.
Test plan
- Verify CI workflow passes on this PR
- Confirm PRs no longer merge before adversarial review completes
🤖 Generated with Claude Code
Installation
macOS (Apple Silicon):
curl -L https://github.com/systeminit/swamp/releases/download/v20260310.230044.0-sha.2610458f/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/v20260310.230044.0-sha.2610458f/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/v20260310.230044.0-sha.2610458f/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/v20260310.230044.0-sha.2610458f/swamp-linux-aarch64 -o swamp
chmod +x swamp && sudo mv swamp /usr/local/bin/