Symptom
Calling update_stack_env with a variable flagged isSecret:false returns { success: true, count/added: N } but the variable never appears afterwards in get_stack_env or get_stack_env_raw, and is not injected into the container. Reproduced live on v1.9.0 (AFFiNE stack, hhdocker03) — non-secret MAILER/indexer vars had to be written as isSecret:true (DB secret) to make them stick.
Root cause (verified against source)
- mcp-dockhand
src/tools/stacks.ts update_stack_env sends all variables — including isSecret:false — to PUT /api/stacks/{name}/env.
- finsys/dockhand
setStackEnvVars stores them as DB rows (non-secrets with isSecret:false). BUT the GET handler (src/routes/api/stacks/[name]/env/+server.ts) reads non-secrets from the .env file, not the DB (for non-git stacks). So an isSecret:false DB row is an orphaned, invisible row: not surfaced by get_stack_env, never written to .env (so absent from get_stack_env_raw), never injected.
- Net: non-secret vars cannot be set via
update_stack_env at all — only via update_stack_env_raw.
Secondary bug — misleading description
The tool description states: "When false/omitted, value is written to the .env file as plain text" — this is false. The value goes to the DB /env endpoint and is then ignored on read. The description promises .env behaviour the endpoint does not deliver.
Impact
Silent data-loss-of-intent: callers believe non-secret config was set (success:true), but nothing persisted. The summary.added from #105 reports the payload count, not what actually persisted, so it doesn't catch this.
Proposed fix (options)
- Auto-route
isSecret:false vars to the .env (via the /env/raw path) so update_stack_env behaves as its description promises (unified setter), OR
- Reject/error on
isSecret:false vars with a clear message pointing to update_stack_env_raw.
Plus: correct the misleading description, and make summary/added reflect what actually persisted (secrets vs .env).
Follow-up to #105 (stack-env two-store ergonomics).
Symptom
Calling
update_stack_envwith a variable flaggedisSecret:falsereturns{ success: true, count/added: N }but the variable never appears afterwards inget_stack_envorget_stack_env_raw, and is not injected into the container. Reproduced live on v1.9.0 (AFFiNE stack, hhdocker03) — non-secret MAILER/indexer vars had to be written asisSecret:true(DB secret) to make them stick.Root cause (verified against source)
src/tools/stacks.tsupdate_stack_envsends all variables — includingisSecret:false— toPUT /api/stacks/{name}/env.setStackEnvVarsstores them as DB rows (non-secrets withisSecret:false). BUT the GET handler (src/routes/api/stacks/[name]/env/+server.ts) reads non-secrets from the .env file, not the DB (for non-git stacks). So anisSecret:falseDB row is an orphaned, invisible row: not surfaced byget_stack_env, never written to.env(so absent fromget_stack_env_raw), never injected.update_stack_envat all — only viaupdate_stack_env_raw.Secondary bug — misleading description
The tool description states: "When false/omitted, value is written to the .env file as plain text" — this is false. The value goes to the DB
/envendpoint and is then ignored on read. The description promises.envbehaviour the endpoint does not deliver.Impact
Silent data-loss-of-intent: callers believe non-secret config was set (
success:true), but nothing persisted. Thesummary.addedfrom #105 reports the payload count, not what actually persisted, so it doesn't catch this.Proposed fix (options)
isSecret:falsevars to the.env(via the/env/rawpath) soupdate_stack_envbehaves as its description promises (unified setter), ORisSecret:falsevars with a clear message pointing toupdate_stack_env_raw.Plus: correct the misleading description, and make
summary/addedreflect what actually persisted (secrets vs .env).Follow-up to #105 (stack-env two-store ergonomics).