Skip to main content
← Back to list
01Issue
BugOpenSwamp CLI
AssigneesNone

Relationships

#1019 Vault push log overstates what left the machine: Pushed N vault file(s) to datastore counts every dirty file, not just vault files

Opened by jeremy · 7/7/2026

swamp vault put … (and any vault-write path) emits a log line like Pushed 1029 vault file(s) to datastore, but the count includes every dirty file the generic pushChanged() call happens to flush at the same time — not just vault files. On datastores that exclude secrets/vaults from sync (e.g. @swamp/s3-datastore), zero vault bytes are actually pushed, so the log is doubly misleading.

Reproduction

Against an @swamp/s3-datastore-backed repo with a local_encryption vault, run:

swamp vault put my-vault some-key

Type any value, hit enter. Observe:

info datastore·sync Syncing vault data from datastore... info vault·put Stored secret "some-key" in vault "my-vault" info datastore·sync Pushing vault changes to datastore... info datastore·sync Pushed 1029 vault file(s) to datastore

Verify what actually landed in the remote:

aws s3 ls s3://<bucket>/swamp/ --recursive | grep -iE 'vault|secret'
# → 0 matches

Cross-check the datastore's own Dirs: list — no secrets or vaults entry, so nothing under those prefixes is eligible for push in the first place.

Expected

The log line accurately describes what was pushed. Either the count reflects only files in vault/secret subdirs, or the message drops the "vault" qualifier so the count matches the wording.

Actual

The count is the total number of dirty files the generic sync service flushed under the global lock (data/, outputs/, workflow-runs/, definitions-evaluated/, etc.). Vault files themselves may contribute zero — @swamp/s3-datastore correctly skips secrets/vaults — but the log implies N vault files went out.

Root cause

src/cli/repo_context.ts at HEAD (8d598bd4), around line 1555–1570:

logger.info`Pushing vault changes to datastore...`;

const pushNs = config.namespace;
let pushed: number | void;
if (pushNs) {
  pushed = await customSyncService.pushChanged({ namespace: pushNs });
} else {
  pushed = await customSyncService.pushChanged();
}
if (pushed && pushed > 0) {
  logger.info("Pushed {count} vault file(s) to datastore", {
    count: pushed,
  });
} else {
  logger.info`Vault push complete, no changes`;
}

pushChanged() here is the same generic API used by the correctly-labeled flushSinglePhasePush earlier in the same file (around line 1258–1320), which logs the accurate Pushing changes to datastore... / Pushed {count} file(s) to datastore. The vault path added "vault" to both messages without narrowing the count.

Impact

Security-adjacent confusion. A user reading Pushed 1029 vault file(s) to datastore reasonably concludes that 1029 files worth of vault content left the machine and landed in the datastore. For a local_encryption vault whose whole purpose is single-machine confidentiality, this looks like a leak. The actual behavior is correct — nothing under secrets/vaults egresses — but the log actively contradicts what happened. Users seeing this could take unnecessary rotation / breach-response actions, or (worse) miss a real leak in the future because they've already been trained to distrust these lines.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

7/7/2026, 5:32:09 PM

No activity in this phase yet.

03Sludge Pulse

Sign in to post a ripple.