Summary
update_stack's [REDACTED] restoration reads its "original" values from client.getStackFile() (GET /api/stacks/:name/file — Swarmpit's stored stack-file record) rather than client.getStackCompose() (GET /api/stacks/:name/compose — the live, currently-running service state). If a secret/env var was ever changed on the live service through a path that doesn't touch the stack file (e.g. update_service_env, or editing directly in the Swarmpit UI), the stack file becomes stale relative to reality. Any later update_stack call that leaves that var as [REDACTED] will silently restore the stale stack-file value, overwriting the correct live value — with no error, no warning, and no diff shown.
Root cause
src/tools/stacks.ts, update_stack handler:
const current = await client.getStackFile(name).catch(() => ({ compose: "" }));
const restored = restoreRedactedValues(yaml, current.compose);
getStackFile (src/client.ts) hits /api/stacks/:name/file. getStackCompose (used by the get_stack_compose tool) hits /api/stacks/:name/compose — a different endpoint that reflects live service state. These two can diverge indefinitely; nothing keeps the stack file in sync with update_service/update_service_env changes.
Reproduction
- Fix a secret on a live service via
update_service_env (bypasses the stack file entirely).
- Some time later, use
update_stack for an unrelated change (e.g. adding a healthcheck), leaving the affected var as [REDACTED] in the submitted compose (the documented, expected usage per the tool's own description: "Leave [REDACTED] for unchanged secrets").
- The var reverts to whatever was in the stack file before step 1's fix — silently.
In our case this redirected a service's RPC_URL to a stale, wrong blockchain endpoint, with no error surfaced anywhere in the tool's response.
Suggested fix
Use getStackCompose (live state) instead of, or in addition to, getStackFile (stored record) as the restoration source in update_stack. If the two-endpoint distinction is intentional for other reasons, at minimum warn/diff when they disagree for a var being restored, since silent restoration of stale secrets is a correctness footgun with no error signal.
Related
Similar in spirit to #4 (update_service round-tripping [REDACTED] literally) — both are cases where the redaction/restoration layer's guarantee ("your secrets are safe to round-trip") doesn't actually hold under realistic multi-path update usage.
Summary
update_stack's[REDACTED]restoration reads its "original" values fromclient.getStackFile()(GET /api/stacks/:name/file— Swarmpit's stored stack-file record) rather thanclient.getStackCompose()(GET /api/stacks/:name/compose— the live, currently-running service state). If a secret/env var was ever changed on the live service through a path that doesn't touch the stack file (e.g.update_service_env, or editing directly in the Swarmpit UI), the stack file becomes stale relative to reality. Any laterupdate_stackcall that leaves that var as[REDACTED]will silently restore the stale stack-file value, overwriting the correct live value — with no error, no warning, and no diff shown.Root cause
src/tools/stacks.ts,update_stackhandler:getStackFile(src/client.ts) hits/api/stacks/:name/file.getStackCompose(used by theget_stack_composetool) hits/api/stacks/:name/compose— a different endpoint that reflects live service state. These two can diverge indefinitely; nothing keeps the stack file in sync withupdate_service/update_service_envchanges.Reproduction
update_service_env(bypasses the stack file entirely).update_stackfor an unrelated change (e.g. adding a healthcheck), leaving the affected var as[REDACTED]in the submitted compose (the documented, expected usage per the tool's own description: "Leave [REDACTED] for unchanged secrets").In our case this redirected a service's
RPC_URLto a stale, wrong blockchain endpoint, with no error surfaced anywhere in the tool's response.Suggested fix
Use
getStackCompose(live state) instead of, or in addition to,getStackFile(stored record) as the restoration source inupdate_stack. If the two-endpoint distinction is intentional for other reasons, at minimum warn/diff when they disagree for a var being restored, since silent restoration of stale secrets is a correctness footgun with no error signal.Related
Similar in spirit to #4 (update_service round-tripping [REDACTED] literally) — both are cases where the redaction/restoration layer's guarantee ("your secrets are safe to round-trip") doesn't actually hold under realistic multi-path update usage.