DOCTOR
swamp doctor runs diagnostics that verify a Swamp installation and
repository's integrations are healthy. It has seven subcommands:
audit— verifies the AI-tool audit integrationdatastores— checks datastore health, checks that a v3 checkout's records are readable, and detects foreign namespace contamination in giga-swamp repositoriesextensions— verifies user-defined extensions load cleanly and inspects catalog stateinstall— checks installation health (binary path, ownership, writability, autoupdate status)secrets— scans behavior definitions for cleartext sensitive global arguments and reports how to migrate each to a vaultvaults— scans behavior definitions for sensitive resource outputs and verifies a vault is configured to store themworkflows— checks workflow YAML files load cleanly
Global flags
All subcommands accept these flags. Only subcommand-specific flags are listed in each section below.
| Flag | Description |
|---|---|
--json |
Output in JSON format (non-interactive). |
--log |
Force flat log output (no interactive tree). |
--log-level <level> |
Set log level (trace, debug, info, warning, error, fatal). |
-q, --quiet |
Suppress non-essential output. |
--no-telemetry |
Disable telemetry for this invocation. |
--show-properties |
Show structured properties in log output. |
--no-color |
Disable colored output. |
--server |
Run against a remote swamp serve instance (env: SWAMP_SERVE_URL). See remote availability for limitations. |
--token |
Server token in <name>.<secret> format; only with --server (overrides stored credentials and SWAMP_SERVER_TOKEN). |
--repo-dir <dir> |
Repository directory (env: SWAMP_REPO_DIR). |
-h, --help |
Show help. |
Remote availability
Not all doctor subcommands are available remotely via --server:
| Subcommand | Remote status |
|---|---|
secrets |
Available |
vaults |
Available |
workflows |
Available |
audit |
Not available — requires local process execution to verify the AI-tool integration |
datastores |
Available (without interactive --repair — serve is non-interactive) |
extensions |
Available (without interactive --repair — serve is non-interactive) |
doctor audit
Verifies that the AI-tool audit integration is healthy for the configured tool.
Flags
| Flag | Description |
|---|---|
--tool <tool> |
Override the tool from .swamp.yaml. Values: claude, cursor, kiro, opencode, codex, copilot, pi, antigravity, none. |
Exit codes
| Code | Condition |
|---|---|
0 |
All audit checks pass. |
1 |
One or more checks failed. |
Output
Healthy
✓ binary-on-path claude found at /Users/you/.local/bin/claude
✓ swamp-binary-on-path swamp is on PATH at /opt/homebrew/bin/swamp
✓ agent-config-loadable .claude/settings.local.json is present with both PostToolUse hooks wired
– default-agent-set does not apply to claude
✓ recording-smoke-test synthetic claude payload landed in today's audit JSONL
4 passed, 0 failed, 1 skipped — OVERALL: PASSFailed check
When a check fails, a hint suggests a remediation:
✓ binary-on-path cursor found at /Users/you/.local/bin/cursor
✓ swamp-binary-on-path swamp is on PATH at /opt/homebrew/bin/swamp
✗ agent-config-loadable .cursor/hooks.json is missing
hint: Run `swamp init --tool cursor --force` to regenerate.
– default-agent-set does not apply to cursor
✓ recording-smoke-test synthetic cursor payload landed in today's audit JSONL
3 passed, 1 failed, 1 skipped — OVERALL: FAILFor opencode, agent-config-loadable also fails on a
.opencode/plugins/swamp-audit.ts written before OpenCode 2 support:
✗ agent-config-loadable OpenCode plugin predates OpenCode 2 support (no default export with `setup`)
hint: The rewritten plugin needs OpenCode 1.3.4 or later, or OpenCode 2 — update OpenCode first if it is older, then run `swamp init --tool opencode --force` to rewrite the plugin.JSON shape
{
"tool": "claude",
"overallStatus": "pass",
"checks": [
{
"name": "binary-on-path",
"status": "pass",
"message": "claude found at /Users/you/.local/bin/claude",
"details": {
"binary": "claude",
"path": "/Users/you/.local/bin/claude"
}
},
{
"name": "agent-config-loadable",
"status": "pass",
"message": ".claude/settings.local.json is present with both PostToolUse hooks wired"
},
{
"name": "default-agent-set",
"status": "skip",
"message": "does not apply to claude"
},
{
"name": "recording-smoke-test",
"status": "pass",
"message": "synthetic claude payload landed in today's audit JSONL"
}
]
}overallStatus is "pass" when all checks pass, "fail" otherwise.
Each entry in checks includes:
name— check identifierstatus—"pass","fail", or"skip"message— human-readable resultdetails— optional object with check-specific datahint— present on failed checks when a remediation is available
Examples
swamp doctor audit # check the tool configured in .swamp.yaml
swamp doctor audit --tool kiro # check a specific tool
swamp doctor audit --json # machine-readable output for CIdoctor datastores
Checks that the configured datastore is healthy and flags any vault compatibility issues. For custom (remote) datastores with a namespace configured, also detects foreign namespace contamination — objects from other namespaces nested under the repository's own namespace prefix. For a v3 datastore, also runs the v3 content check. Also reports data names holding both kinds.
Flags
| Flag | Description |
|---|---|
--verbose |
Show detailed output. |
--retention |
Also scan storage for items older than their retention and show each kind's last pass. |
--repair |
Preview foreign namespace contamination cleanup, or, for a v3 datastore whose format marker is lost, preview restoring the marker. Add -y to execute. |
-y, --yes |
Execute the repair (without this, --repair shows a preview). --force / -f are also accepted. |
Exit codes
| Code | Condition |
|---|---|
0 |
Datastore is healthy and no contamination detected. A format marker repair also exits 0 on preview and on restore. |
1 |
One or more health checks failed, including a v3 checkout that could not be inspected, or contamination detected. |
Output
Healthy (filesystem datastore)
Checking datastore health…
Datastore type: filesystem
✓ Filesystem datastore at /path/to/repo/.swamp is healthy
✓ Every published data version is readable
✓ No data name holds versions of both kinds
✓ Catalog index covers all 7 record(s) on diskHealthy (v3 datastore)
Checking datastore health…
Datastore type: filesystem
✓ Filesystem datastore at /path/to/v3store is healthy
✓ Catalog index covers all 0 record(s) on disk
✓ Every record of this v3 checkout is readable, and no content is held asideHealthy (custom datastore with namespace)
Checking datastore health…
Datastore type: s3
✓ S3 datastore at s3://my-bucket is healthy
✓ No foreign namespace contamination detectedContamination detected
When foreign namespace objects are found under the repository's namespace prefix:
Checking datastore health…
Datastore type: s3
✓ S3 datastore at s3://my-bucket is healthy
✗ Foreign namespace contamination detected
2 foreign namespace(s) found under "my-namespace/":
other-team/ 148 objects
staging/ 23 objects
Run `swamp doctor datastores --repair` to preview cleanup.Data names holding both kinds
When a data name's history holds both file and resource versions:
✗ 1 data name(s) hold both file and resource versions, so the older kind is missing from swamp data list and type queries (spec/x). Read a hidden version with 'swamp data get <model> <name> --version <n>', then delete the name with 'swamp data delete' before writing it again.
Data names holding both kinds:
spec/x: v1 file 'blob', v2 resource 'meta'
Read a hidden version with 'swamp data get <model> <name> --version <n>', then delete the name with 'swamp data delete' before writing it again.Repair preview (--repair)
Previews what the repair would clean up without executing:
Namespace contamination repair — DRY RUN
Step 1: Delete foreign objects
other-team/ 148 objects to delete
staging/ 23 objects to delete
Step 2: Rebuild remote index
Step 3: Wipe local cache
Step 4: Re-pull scoped data
Step 5: Invalidate workflow run and catalog indexes
Total: 171 foreign objects to remove
Run `swamp doctor datastores --repair -y` to execute.Repair executed (--repair -y)
Namespace contamination repair
Step 1: Delete foreign objects ✓ 171 objects deleted
Step 2: Rebuild remote index ✓
Step 3: Wipe local cache ✓
Step 4: Re-pull scoped data ✓
Step 5: Invalidate workflow run and catalog indexes ✓
Repair complete. 3 workflow run(s) recovered.If repair fails mid-way, the error message identifies which step failed and how to restore:
Namespace contamination repair
Step 1: Delete foreign objects ✓ 171 objects deleted
Step 2: Rebuild remote index ✗ Failed: connection timeout
To restore local state, run: swamp datastore sync --pullJSON shape
{
"overallStatus": "pass",
"datastoreType": "filesystem",
"isCustom": false,
"healthFindings": [
{
"check": "health",
"passed": true,
"message": "Filesystem datastore at /path/to/repo/.swamp is healthy"
},
{
"check": "unreadable_versions",
"passed": true,
"message": "Every published data version is readable"
},
{
"check": "mixed_kind_names",
"passed": true,
"message": "No data name holds versions of both kinds"
},
{
"check": "catalog_completeness",
"passed": true,
"message": "Catalog index covers all 7 record(s) on disk"
}
],
"vaultMismatchFindings": [],
"contaminationFinding": null,
"unpushedFilesFinding": null,
"catalogCompletenessFinding": null,
"unreadableVersionsFinding": null,
"mixedKindNamesFinding": null,
"retentionFinding": null,
"v3ContentFinding": null,
"v3KeptLineagesFinding": null,
"v3ReplicaRestorableFinding": null
}overallStatus is "pass" when all checks pass and no contamination is
detected, "fail" otherwise.
datastoreType identifies the configured backend (e.g. "filesystem", "s3").
isCustom is true for remote/external backends, false for the default
filesystem backend.
Each entry in healthFindings includes:
check— check identifierpassed—trueorfalsemessage— human-readable result
vaultMismatchFindings contains entries when the vault configuration is
incompatible with the datastore backend.
v3ContentFinding is null when the datastore is not v3 or the
v3 content check passes.
mixedKindNamesFinding is null when no data name holds versions of both
kinds. Otherwise it lists each such name:
{
"mixedKindNamesFinding": {
"mixedNames": 1,
"names": [
{
"modelType": "@test/spec",
"modelId": "m-1",
"modelName": "spec",
"dataName": "x",
"versions": [
{ "version": 1, "kind": "file", "specName": "blob" },
{ "version": 2, "kind": "resource", "specName": "meta" }
]
}
]
}
}mixedNames— the number of data names holding both kindsnames— each name's behavior (modelType,modelId,modelName), itsdataName, and each live version with itsversion,kind(fileorresource) andspecName
A data name holding both kinds is missing its older kind from swamp data list
and dataType queries; that kind is readable only by version number. A delete
or rename ends a history, so versions before it are not counted. The log output
lists the first 20 names. --repair has no stage for this check: read any
hidden version with swamp data get <behavior> <name> --version <n>, then
delete the name with swamp data delete before writing it again. New writes
cannot mix kinds; see
One Kind per Data Name.
contaminationFinding is null when no contamination is detected or the
datastore does not support contamination checks (filesystem datastores). When
contamination is detected:
{
"contaminationFinding": {
"foreignNamespaces": [
{ "namespace": "other-team", "objectCount": 148 },
{ "namespace": "staging", "objectCount": 23 }
],
"totalForeignObjects": 171
}
}Each entry in foreignNamespaces includes:
namespace— the foreign namespace nameobjectCount— number of objects from that namespace
Repair (--json --repair)
Prints one JSON document in place of the health report, with a key for each repair stage, in the order the stages run:
{
"unmigrated": null,
"catalog": { "status": "catalog_not_needed" },
"duplicateLatest": { "status": "duplicate_latest_not_needed" },
"contamination": null
}Each value is that stage's status object, or null when the stage did not run.
A stage does not run when the datastore has no namespace, when its namespace has
the same name as a datastore layout directory, when the datastore does not
support contamination repair (filesystem datastores), or when the stage failed.
The exit code and the error on stderr tell these apart. The document is printed
even when a stage fails, so the stages that finished are still reported.
| Key | Status | Other fields |
|---|---|---|
unmigrated |
"unmigrated_preview" |
namespace, directories |
unmigrated |
"unmigrated_completed" |
namespace, result |
unmigrated |
"unmigrated_not_needed" |
|
catalog |
"catalog_preview" |
completeness, foreignRowsNotice |
catalog |
"catalog_completed" |
result, foreignRowsNotice |
catalog |
"catalog_not_needed" |
|
duplicateLatest |
"duplicate_latest_preview" |
duplicateCount |
duplicateLatest |
"duplicate_latest_completed" |
result |
duplicateLatest |
"duplicate_latest_not_needed" |
|
contamination |
"preview" |
namespace, contamination |
contamination |
"completed" |
namespace, result, steps |
contamination |
"not_needed" |
A *_preview status (or "preview") is printed without -y, a *_completed
status (or "completed") with it.
directories— unmigrated directories under the namespace that the repair removes.completeness—diskRecords,catalogRecords, andshortfalls, one entry per type withtypeNormalized,diskRecordsandcatalogRecords.foreignRowsNotice— a reminder that rebuilding the catalog index clears rows pulled from other namespaces, and thatswamp datastore catalog pullrestores them.duplicateCount— catalog rows with a duplicate latest flag.contamination— the same object ascontaminationFindingabove.steps— the contamination repair steps that ran, each withstep,totalanddescription.result— what the stage changed:
| Stage | result fields |
|---|---|
unmigrated |
removedDirectories, removedFiles |
catalog |
shortfalls, missingRecords |
duplicateLatest |
demotedRows |
contamination |
foreignNamespaces, deletedObjects, filesPulled, workflowRunIndexesInvalidated, catalogInvalidated |
The repair for a v3 datastore whose format marker is lost prints a single status object instead; see Format marker repair.
Retention
--retention scans workflow runs, snapshots, outputs, data versions and audit
logs for items older than the .swamp.yaml
garbageCollection
retention, and adds a retention check that fails when any are found. With
automatic on, an item counts only once it is two hours past its cutoff.
bookkeeping is listed but not scanned.
✓ No stored item is older than its retention
Retention: automatic on
workflowRuns 30d cutoff 2026-09-16T15:03:34.713Z ✓ none older than cutoff
last pass 2026-10-16T17:00:59.969Z, cutoff 2026-09-16T17:00:59.969Z, complete
evaluatedSnapshots 30d cutoff 2026-09-16T15:03:34.713Z ✓ none older than cutoff
last pass 2026-10-16T17:00:59.969Z, cutoff 2026-09-16T17:00:59.969Z, complete
outputs 7d cutoff 2026-10-09T15:03:34.713Z ✓ none older than cutoff
last pass 2026-10-16T17:00:59.969Z, cutoff 2026-10-09T17:00:59.969Z, complete
data no retention
audit 90d cutoff 2026-07-18T15:03:34.713Z ✓ none older than cutoff
last pass 2026-10-16T17:00:59.969Z, cutoff 2026-07-18T17:00:59.969Z, complete
bookkeeping no retentionlast pass is the most recent pass for that kind, with the cutoff it removed
items before. It reads incomplete when the pass stopped before finishing the
kind.
With --json, the result adds a retentionFinding object:
{
"retentionFinding": {
"configured": true,
"automatic": true,
"checkedAt": "2026-10-16T17:04:28.007Z",
"kinds": [
{
"kind": "outputs",
"retention": "7d",
"cutoff": "2026-10-09T15:04:28.007Z",
"scanned": true,
"overdue": 0,
"items": [],
"kept": 0,
"watermark": {
"lastPassAt": "2026-10-16T17:00:59.969Z",
"cutoff": "2026-10-09T17:00:59.969Z",
"complete": true
}
}
]
}
}v3 content check
For a v3 datastore, the v3_content check inspects this checkout's read model
and the datastore's chunks. It fails when it finds any of:
- Unreadable records — records whose content cannot be read because a chunk
is missing or damaged in the datastore. Reading one fails with the error code
datastore_v3_record_unreadable. - Checkout files held aside — checkout files moved into
.swamp/_read_model_v3/at-risk/because the datastore refused their chunks. They hold the only copy this checkout has of that content. - Torn chunks — chunks that the checkout's records name, stored at the wrong size.
The check line counts each kind and names the first three records. Below it,
each entry is listed, up to ten per kind, with advice for its record kind: for
example, write the data again or delete the version with swamp data delete,
save the definition again, or delete the at-risk directory to discard the copies
held aside.
When the checkout's state cannot be inspected, the check fails with
Could not inspect this v3 checkout's content: and the cause.
With --json, the check appears in healthFindings with check: "v3_content",
and a failing check sets v3ContentFinding to the full result:
unreadableRecords, atRiskCopies and tornChunks counts, unreadable,
atRisk and torn lists of the first ten entries (each unreadable and torn
entry carries its advice), atRiskDir and atRiskAdvice.
Format marker repair
A v3 datastore records its format in datastore-format.json beside its
commits.db. When .swamp.yaml configures a filesystem datastore with format 3
and that marker is missing or blank, every command is refused:
Error: The datastore config in .swamp.yaml says format 3, but the datastore has no readable format marker. It holds a v3 commit log, so a crash may have lost its format marker. Run `swamp doctor datastores --repair` to check the datastore and restore the marker. Nothing was changed.In that state, --repair checks the datastore's files and previews the restore
without changing anything:
$ swamp doctor datastores --repair
Checking the v3 datastore's format marker…
Lost datastore format marker:
/path/to/v3store/datastore-format.json is missing, beside the v3 commit log of /path/to/v3store
Run with -y to restore it.--repair -y writes the marker:
$ swamp doctor datastores --repair -y
Checking the v3 datastore's format marker…
✓ Datastore format marker restored:
Wrote /path/to/v3store/datastore-format.json (format 3)
Run `swamp doctor datastores` to check the datastore.With --json, the result is a single object whose status is
format_marker_preview, format_marker_restored, or format_marker_not_needed
(the marker was readable by the time the repair checked it):
{
"status": "format_marker_preview",
"datastorePath": "/path/to/v3store",
"markerPath": "/path/to/v3store/datastore-format.json",
"marker": "absent"
}marker is "absent" or "blank". A restore that fails exits 1 with the
error code format_marker_repair_failed.
On a v3 datastore whose marker is intact, --repair is refused and exits 1:
Error: `swamp doctor datastores --repair` is not available for v3 datastores yet. A v3 checkout rebuilds its records and catalog from the commit log; run `swamp doctor datastores` to check it. Nothing was changed.Examples
swamp doctor datastores # check this repo's datastore
swamp doctor datastores --json # machine-readable output for CI
swamp doctor datastores --verbose # show detailed output
swamp doctor datastores --repair # preview namespace contamination cleanup, or a lost v3 format marker restore
swamp doctor datastores --repair -y # execute the repairdoctor extensions
Verifies that user-defined extensions in a repository load cleanly and inspects
the catalog's aggregate state. Checks each registry (model, which holds
behaviors; vault; datastore; report), summarises per-extension source health,
detects orphaned catalog rows and bundle files, and optionally repairs the
catalog.
Flags
| Flag | Description |
|---|---|
--verbose |
Show per-source detail for each extension. |
--repair |
Prune Tombstoned catalog rows and evict unreferenced bundle files. |
--dry-run |
Preview repair operations without executing. Use with --repair. |
-y, --yes |
Skip the confirmation prompt. Use with --repair (--force / -f also accepted). |
Exit codes
| Code | Condition |
|---|---|
0 |
All registry checks pass. |
1 |
One or more registry checks failed. |
Output
No extensions installed
✓ model
✓ vault
✓ datastore
✓ report
Extension Catalog State
0 total source(s) across 0 extension(s), 0 healthy (Indexed)
4 passed, 0 failed — OVERALL: PASSHealthy catalog
✓ model
✓ vault
✓ datastore
✓ report
Extension Catalog State
1 total source(s) across 1 extension(s), 1 healthy (Indexed)
@swamp/1password@2026.04.22.2 [pulled] 1 source(s)
Indexed: 1
4 passed, 0 failed — OVERALL: PASSVerbose (--verbose)
Adds a Per-Source Detail section listing every source file, its state tag, fingerprint, bundle path, and registry kind.
✓ model
✓ vault
✓ datastore
✓ report
Extension Catalog State
1 total source(s) across 1 extension(s), 1 healthy (Indexed)
@swamp/1password@2026.04.22.2 [pulled] 1 source(s)
Indexed: 1
Per-Source Detail
vault .swamp/pulled-extensions/@swamp/1password/vaults/onepassword.ts Indexed fp:3398bcef6091 bundle:.swamp/vault-bundles/a52f5a8b/onepassword.js
4 passed, 0 failed — OVERALL: PASSRepair dry run (--repair --dry-run)
Previews what --repair would clean up without modifying the catalog.
✓ model
✓ vault
✓ datastore
✓ report
Extension Catalog State
1 total source(s) across 1 extension(s), 1 healthy (Indexed)
@swamp/1password@2026.04.22.2 [pulled] 1 source(s)
Indexed: 1
Repair DRY RUN
Nothing to clean up — catalog is healthy.
4 passed, 0 failed — OVERALL: PASSRepair apply (--repair or --repair --yes)
Without --yes, a confirmation prompt is shown before modifying the catalog.
With --yes, the prompt is skipped.
RowState values
Each source file in the catalog carries a state tag. The aggregate state section groups sources by these values.
| State | Meaning |
|---|---|
Indexed |
Source is registered in the catalog and healthy. |
Bundled |
Source has a compiled bundle but is not yet indexed. |
BundleBuildFailed |
Bundle compilation failed. The source is registered but not usable. |
ValidationFailed |
Source failed validation checks. |
EntryPointUnreadable |
The source file's entry point cannot be read from disk. |
OrphanedBundleOnly |
A bundle file exists with no corresponding catalog row. |
Tombstoned |
Source has been removed but the catalog row remains. Pruned by --repair. |
JSON shape
Base (--json)
{
"overallStatus": "pass",
"registries": {
"model": { "registry": "model", "status": "pass" },
"vault": { "registry": "vault", "status": "pass" },
"datastore": { "registry": "datastore", "status": "pass" },
"report": { "registry": "report", "status": "pass" }
},
"orphanFiles": [],
"aggregateState": {
"aggregates": [
{
"name": "@swamp/1password",
"version": "2026.04.22.2",
"origin": "pulled",
"sourceCount": 1,
"stateDistribution": {
"Indexed": 1,
"Bundled": 0,
"BundleBuildFailed": 0,
"ValidationFailed": 0,
"EntryPointUnreadable": 0,
"OrphanedBundleOnly": 0,
"Tombstoned": 0
}
}
],
"sourceDetails": [],
"catalogOrphans": [],
"bundleOrphans": [],
"totalSources": 1,
"healthySources": 1,
"orphanRowCount": 0,
"orphanBundleFileCount": 0
},
"warnings": [
{
"sourcePath": "extensions/behaviors/foo/bar.ts",
"category": "TypeExtractionFailed",
"message": "type field could not be extracted..."
}
]
}overallStatus is "pass" when all registries pass, "fail" otherwise.
warnings contains advisory diagnostics that do not affect overallStatus or
the exit code. Each entry includes:
sourcePath— path to the source file that triggered the warningcategory— warning type. Currently supported:TypeExtractionFailed— the extension'stypefield could not be extracted from the source file (e.g. the export is dynamically computed or uses an unsupported pattern). The extension still loads and functions normally.
message— human-readable description of the issue
The category field is extensible — future swamp versions may add new warning
types.
Each registry entry contains registry (the kind name) and status ("pass"
or "fail"). The per-registry status is derived from sourceDetails[] — a
registry is "fail" when any of its sources carry a failure-state tag
(ValidationFailed, BundleBuildFailed, EntryPointUnreadable).
sourceDetails[] is the single authoritative failure surface for extension
diagnostics.
Each entry in aggregates includes:
name— extension nameversion— installed versionorigin—"pulled"(from registry) or"local"(user-defined)sourceCount— total source files for this extensionstateDistribution— count of sources in each RowState
Verbose (--json --verbose)
sourceDetails is populated with per-source entries:
{
"sourceDetails": [
{
"sourcePath": ".swamp/pulled-extensions/@swamp/1password/vaults/onepassword.ts",
"stateTag": "Indexed",
"fingerprint": "3398bcef6091...",
"bundlePath": ".swamp/vault-bundles/a52f5a8b/onepassword.js",
"kind": "vault"
}
]
}Each source detail includes:
sourcePath— path to the source file relative to the repository rootstateTag— one of the RowState valuesfingerprint— content hash of the source filebundlePath— path to the compiled bundle (if any)kind— registry kind (modelfor behaviors,vault,datastore,report)lastError— error message explaining why the source is in a failure state. Present whenstateTagisValidationFailed,BundleBuildFailed, orEntryPointUnreadable; absent for healthy states.
Repair (--json --repair --dry-run)
Adds a repairReport object:
{
"repairReport": {
"mode": "dry-run",
"operations": [],
"prunedRowCount": 0,
"evictedFileCount": 0
}
}mode is "dry-run" or "applied". Each entry in operations describes a
pruned catalog row or evicted bundle file. prunedRowCount and
evictedFileCount are totals.
Examples
swamp doctor extensions # check this repo's extensions
swamp doctor extensions --json # machine-readable output for CI
swamp doctor extensions --verbose # show per-source detail
swamp doctor extensions --repair --dry-run # preview what repair would clean up
swamp doctor extensions --repair # apply repair (with confirmation)
swamp doctor extensions --repair --yes # apply repair without promptingdoctor install
Checks Swamp installation health: binary ownership, writability, and autoupdate status. Does not require a Swamp repository — runs from any directory.
Exit codes
| Code | Condition |
|---|---|
0 |
Installation is healthy. |
1 |
One or more installation checks are unhealthy. |
Output
Healthy
Installation Health Check
Binary path: /Users/you/.local/bin/swamp
Version: 20260518.215711.0-sha.d4a64cc1
Binary owner: you
Writable: ✓ Binary is owned by current user
Autoupdate
Enabled: yes
Cadence: daily
Scheduler type: LaunchAgent (user)
Scheduler: installed
Last check: 2026-05-18T22:40:18.898Z (up to date)
HEALTHYUnhealthy
When a check fails, the status is UNHEALTHY and the failing field shows the
error:
Installation Health Check
Binary path: /usr/local/bin/swamp
Version: 20260518.215711.0-sha.d4a64cc1
Binary owner: root
Writable: ✗ Binary is owned by root, not current user
Autoupdate
Enabled: no
Last check: 2026-05-18T22:04:59.653Z (error)
Last error: Cannot update /usr/local/bin/swamp: permission denied. Re-run with: sudo swamp update
UNHEALTHYUnhealthy conditions
The status is UNHEALTHY when any of these holds:
| Condition | Applies when autoupdate is |
|---|---|
| The binary is not writable by the current user. | enabled or disabled |
| The last autoupdate check ended in an error. | enabled or disabled |
| The scheduler is not installed. | enabled |
| The last check is stale (see below). | enabled |
The scheduler's last run exited with a code other than 0. |
enabled |
| macOS launchd refuses to start the job until it is registered again. | enabled |
A last check is stale when it is older than twice the cadence interval, and
never less than 72 hours. The Last check line then ends with
— no check for N days, whether or not autoupdate is enabled:
Last check: 2026-05-26T15:14:36.747Z (updated) — no check for 135 daysWith autoupdate enabled, a Last exit line follows Scheduler when the
scheduler's last run exited with a code other than 0. Code 78 is labelled
78 (EX_CONFIG: the scheduler refused to start swamp).
With autoupdate enabled and the scheduler installed, one warning names the problem, checked in this order:
| Problem | Warning |
|---|---|
| launchd refuses to start the job | ⚠ macOS is refusing to start the autoupdate job., then It still expects the code signature of the swamp binary that was there before the last update. |
| The last run exited non-zero and the binary is writable | ⚠ The autoupdate job's last run failed (exit N). |
| The last check is stale | ⚠ Autoupdate has stopped checking for updates. |
Each warning ends with the repair command:
Re-register it with: `swamp update --setup-auto`The command is sudo swamp update --setup-auto for a LaunchDaemon, a system
systemd timer or a root cron job.
JSON shape
{
"binaryPath": "/Users/you/.local/bin/swamp",
"owner": {
"uid": 501,
"username": "you",
"isRoot": false
},
"currentVersion": "20260518.215711.0-sha.d4a64cc1",
"writable": "pass",
"writableMessage": "Binary is owned by current user",
"autoupdate": {
"enabled": true,
"cadence": "daily",
"schedulerInstalled": true,
"schedulerType": "agent",
"lastEntry": {
"timestamp": "2026-05-18T22:40:18.898Z",
"versionBefore": "20260509.235714.0-sha.7ace6b02",
"versionAfter": "20260518.215711.0-sha.d4a64cc1",
"outcome": "updated"
},
"lastCheckStale": false,
"lastCheckAgeDays": 0,
"schedulerLastExitCode": 0,
"schedulerNeedsRepair": false
},
"overallStatus": "healthy"
}overallStatus is "healthy" when all checks pass, "unhealthy" otherwise.
writable is "pass" when the current user owns the binary, "fail"
otherwise.
autoupdate.schedulerType identifies the platform scheduler in use. Present
when a scheduler is installed. Possible values:
| Value | Display label |
|---|---|
agent |
LaunchAgent (user) |
daemon |
LaunchDaemon (system) |
systemd-user |
systemd timer (user) |
systemd-system |
systemd timer (system) |
cron-user |
cron (user) |
cron-root |
cron (root) |
autoupdate.lastEntry is present when an autoupdate has been attempted.
outcome is one of "updated", "up_to_date", or "error". When outcome is
"error", an error field contains the error message.
| Field | Type | Description |
|---|---|---|
autoupdate.lastCheckStale |
boolean |
The last check is older than twice the cadence interval, with a 72 hour floor. false when there is no check yet. |
autoupdate.lastCheckAgeDays |
number | null |
Whole days since the last check, or null when there is none. |
autoupdate.schedulerLastExitCode |
number | null |
Exit code of the scheduler's last run, or null when it has never exited. Omitted when the scheduler cannot report it. |
autoupdate.schedulerNeedsRepair |
boolean |
macOS launchd refuses to start the job until it is registered again. |
Examples
swamp doctor install # check installation health
swamp doctor install --json # machine-readable outputdoctor secrets
Scans behavior definitions for sensitive global arguments that hold a
cleartext literal value instead of a vault.get(...) expression. The scan is
read-only and never prints the secret value. It covers models/ (including
datastore-synced definitions) and .swamp/auto-definitions. It is the detection
complement to the write-time guard that rejects a literal value for a
sensitive global argument when a definition is saved.
A global argument is sensitive when the behavior type's globalArguments
schema marks it so. See the
Behavior Definitions,
Vaults, and
CEL Expressions references for the schema
marking and the vault.get expression syntax.
Flags
| Flag | Description |
|---|---|
-v, --verbose |
Show detailed output. |
Exit codes
| Code | Condition |
|---|---|
0 |
No cleartext sensitive global arguments found. |
1 |
One or more cleartext sensitive global arguments found. |
Output
Clean
Scanning definitions for cleartext sensitive global arguments…
✓ No cleartext sensitive global arguments found (0 definition(s) scanned)Findings
Each finding lists the definition, the leaked argument path, and value-free remediation steps. The secret value is never shown.
Scanning definitions for cleartext sensitive global arguments…
✗ 1 definition(s) hold a cleartext sensitive global argument (1 definition(s) scanned)
leaky [@demo/secret-demo]
• apiKey
1. Store the secret: swamp vault put my-vault apiKey <value>
2. Reference it: ${{ vault.get('my-vault', 'apiKey') }}
Each secret above sits in cleartext in its definition YAML. Migrate it to a vault, then re-save the definition.Unresolved
A definition whose extension type is not installed cannot be assessed — its type
schema is unavailable, so the scan cannot tell whether any argument is
sensitive. These are reported separately as a warning and do not affect
overallStatus or the exit code.
Scanning definitions for cleartext sensitive global arguments…
✓ No cleartext sensitive global arguments found (1 definition(s) scanned)
⚠ 1 definition(s) could not be assessed (type schema unavailable — install the extension to scan them)
• mystery [@demo/uninstalled-type]JSON shape
{
"overallStatus": "fail",
"scanned": 2,
"findings": [
{
"definitionId": "48d8e9d8-aca0-4678-9c07-64b09f4bae55",
"definitionName": "leaky",
"type": "@demo/secret-demo",
"leakedPaths": ["apiKey"],
"remediations": [
{
"path": "apiKey",
"vaultName": "my-vault",
"vaultKey": "apiKey",
"expression": "${{ vault.get('my-vault', 'apiKey') }}"
}
]
}
],
"unresolved": [
{
"definitionId": "faac276d-c74b-4c54-9ca6-b79bb151166f",
"definitionName": "mystery",
"type": "@demo/uninstalled-type"
}
]
}overallStatus is "pass" when findings is empty, "fail" otherwise.
unresolved does not affect overallStatus or the exit code.
scanned is the number of definitions inspected.
Each entry in findings includes:
definitionId— the definition's iddefinitionName— the definition's nametype— the behavior typeleakedPaths— the global-argument paths holding a cleartext sensitive valueremediations— one entry per leaked path
Each entry in remediations includes:
path— the leaked global-argument pathvaultName— the vault to store the secret invaultKey— the key to store the secret underexpression— thevault.get(...)expression to replace the literal with
Each entry in unresolved includes:
definitionId— the definition's iddefinitionName— the definition's nametype— the behavior type whose schema is unavailable
Examples
swamp doctor secrets # scan this repo's definitions
swamp doctor secrets --json # machine-readable output for CI
swamp doctor secrets --verbose # show detailed outputdoctor vaults
Scans behavior definitions for sensitive resource outputs and verifies a vault
is configured to store them. A behavior requires a vault when any of its
resource specifications has fields marked { sensitive: true } in the schema
metadata or the sensitiveOutput: true flag on the specification itself. It
also warns when those outputs fall back to the first of several vaults (see
Fallback warning).
The scan covers definitions in models/ (including datastore-synced
definitions) and .swamp/auto-definitions/.
Flags
| Flag | Description |
|---|---|
-v, --verbose |
Show detailed output. |
Exit codes
| Code | Condition |
|---|---|
0 |
All behaviors with sensitive outputs have a vault configured. |
1 |
One or more behaviors have sensitive outputs but no vault configured. |
Output
Clean
Scanning definitions for sensitive resource outputs without a vault…
✓ All behaviors with sensitive outputs have a vault configured (0 definition(s) scanned)Findings
Each finding lists the definition name and behavior type. Behaviors in this list will fail at runtime when writing sensitive data.
Scanning definitions for sensitive resource outputs without a vault…
✗ 1 definition(s) have sensitive resource outputs but no vault is configured (2 definition(s) scanned)
• my-keypair [test/sensitive]
These behaviors will fail at runtime when writing sensitive data. Create a vault: swamp vault create <type> <name>Fallback warning
In a repository with two or more user vaults and no default vault in effect —
defaultVault unset, or naming a vault that is not registered — the scan warns
about every definition whose sensitive outputs name no vault of their own (no
field, spec or definition resources vaultName). Those outputs are stored in
the first user vault by vault type, then by vault id, which is the order of
swamp vault list (see
vault resolution order). A
workflow step's dataOutputOverrides are not considered.
The warning does not affect overallStatus or the exit code. The pass line ends
with , 1 warning:
Scanning definitions for sensitive resource outputs without a vault…
✓ All behaviors with sensitive outputs have a vault configured (1 definition(s) scanned, 1 warning)
⚠ Sensitive outputs without a vault of their own are stored in 'alpha' (defaultVault is not set)
Without a registered default vault, swamp uses the first of 3 vaults by vault type, then id (the order of 'swamp vault list'): alpha, zeta, default.
• sm [@demo/secret-maker]
Name the vault to use: set defaultVault: <vault> in .swamp.yaml.When defaultVault names a vault that is not registered, the warning says so:
⚠ Sensitive outputs without a vault of their own are stored in 'alpha'
defaultVault in .swamp.yaml names 'prod-secrets', which is not a vault in this repository.
Without a registered default vault, swamp uses the first of 3 vaults by vault type, then id (the order of 'swamp vault list'): alpha, zeta, default.
• sm [@demo/secret-maker]
Name the vault to use: set defaultVault: <vault> in .swamp.yaml.Unresolved
A definition whose extension type is not installed cannot be assessed — its type
schema is unavailable, so the scan cannot tell whether any resource output is
sensitive. These are reported separately as a warning and do not affect
overallStatus or the exit code.
Scanning definitions for sensitive resource outputs without a vault…
✓ All behaviors with sensitive outputs have a vault configured (1 definition(s) scanned)
⚠ 1 definition(s) could not be assessed (type schema unavailable — install the extension to scan them)
• mystery [@demo/uninstalled-type]JSON shape
{
"overallStatus": "pass",
"scanned": 0,
"hasVault": false,
"findings": [],
"unresolved": []
}overallStatus is "pass" when findings is empty, "fail" otherwise.
unresolved does not affect overallStatus or the exit code.
scanned is the number of definitions inspected.
hasVault indicates whether any vault is configured in the repository.
Each entry in findings includes:
definitionId— the definition's iddefinitionName— the definition's nametype— the behavior type
Each entry in unresolved includes:
definitionId— the definition's iddefinitionName— the definition's nametype— the behavior type whose schema is unavailable
fallbackWarning is present only when the fallback warning
applies, and does not affect overallStatus or the exit code:
{
"fallbackWarning": {
"fallbackVault": "alpha",
"userVaults": ["alpha", "zeta", "default"],
"unregisteredDefaultVault": "prod-secrets",
"definitions": [
{
"definitionId": "facfa23b-0735-4276-9dbb-26b6664c0ba4",
"definitionName": "sm",
"type": "@demo/secret-maker"
}
]
}
}fallbackVault— the vault those sensitive outputs are stored inuserVaults— every user vault, in fallback orderunregisteredDefaultVault— thedefaultVaultvalue, present only when it names a vault that is not registereddefinitions— the definitions relying on the fallback, each withdefinitionId,definitionNameandtype
Examples
swamp doctor vaults # scan this repo's definitions
swamp doctor vaults --json # machine-readable output for CIdoctor workflows
Checks that workflow YAML files in a repository load cleanly.
Exit codes
| Code | Condition |
|---|---|
0 |
All workflows load successfully. |
1 |
One or more workflows failed. |
Output
No workflows
Checking workflows...
No workflow files found
0 passed, 0 failed — OVERALL: PASSHealthy
Checking workflows...
✓ hello
1 passed, 0 failed — OVERALL: PASSValidation failure
When a workflow has schema errors, the validation messages are shown inline:
Checking workflows...
✗ broken
→ [
{
"expected": "string",
"code": "invalid_type",
"path": [
"id"
],
"message": "Invalid input: expected string, received undefined"
},
{
"expected": "array",
"code": "invalid_type",
"path": [
"jobs"
],
"message": "Invalid input: expected array, received object"
}
]
✓ hello
1 passed, 1 failed — OVERALL: FAILJSON shape
Healthy
{
"overallStatus": "pass",
"workflows": [
{
"file": "workflows/workflow-....yaml",
"name": "hello",
"status": "pass"
}
],
"totalPassed": 1,
"totalFailed": 0
}Failed
{
"overallStatus": "fail",
"workflows": [
{
"file": "workflows/broken.yaml",
"name": "broken",
"status": "fail",
"error": "[\n {\n \"expected\": \"string\",\n ...\n }\n]"
},
{
"file": "workflows/workflow-....yaml",
"name": "hello",
"status": "pass"
}
],
"totalPassed": 1,
"totalFailed": 1
}Each entry in workflows includes:
file— path to the workflow YAML filename— workflow name from the YAMLstatus—"pass"or"fail"error— present on failed workflows, contains the validation error
Examples
swamp doctor workflows # check all workflows
swamp doctor workflows --json # machine-readable output for CI