Pre-submission checklist
Bug Description
The bridge persists config.yaml with API keys masked to __memos_secret__ (maskSecrets() in core/pipeline/memory-core.ts) and strips empty secrets from patches (stripEmptySecrets()). Nothing ever re-reads the real value back: when the daemon restarts, loadConfig() parses the mask as the literal API key, every LLM call fails auth, and the bridge restart-loops with lastOkAt: null while skill crystallize stays stuck (skill.crystallize.failed ... timed out after 120000 ms spam).
How to Reproduce
- Configure an
llm.apiKey via the viewer/UI (which masks it to __memos_secret__ on disk).
- Restart the bridge daemon.
- Observe:
lastOkAt: null, LLM auth failures on every call, skill.crystallize.failed errors, restart loop.
Root Cause
resolveConfig() (the single choke point for both disk-loaded and in-memory patched configs) never resolves masked/placeholder secret values back from the environment. Read-side resolution is missing entirely — only the write-side masking exists.
Environment
- Bridge:
@memtensor/memos-local-plugin 2.0.12-beta.1 (source matches apps/memos-local-plugin)
- Trigger: migration to an openai-compatible provider (opencode-go,
deepseek-v4-flash). Config written via the viewer masks the key; restart loops; curl with the env key works while the bridge with the masked key fails.
- Platform: Debian 12 LXC (Hermes CT100), node 22, systemd
memos-bridge.service
Additional Context
Observed live 2026-08-11 with 290 candidate skills backlogged and skill.crystallize.failed ... openai_compatible timed out after 120000 ms in the journal.
Willingness to Implement
Fix is in open PR #2235 (fix(config): resolve masked apiKey from env on load) — read-side only, on-disk masking preserved. Includes 6 unit tests; 54/54 pass.
Pre-submission checklist
Bug Description
The bridge persists
config.yamlwith API keys masked to__memos_secret__(maskSecrets()incore/pipeline/memory-core.ts) and strips empty secrets from patches (stripEmptySecrets()). Nothing ever re-reads the real value back: when the daemon restarts,loadConfig()parses the mask as the literal API key, every LLM call fails auth, and the bridge restart-loops withlastOkAt: nullwhile skill crystallize stays stuck (skill.crystallize.failed ... timed out after 120000 msspam).How to Reproduce
llm.apiKeyvia the viewer/UI (which masks it to__memos_secret__on disk).lastOkAt: null, LLM auth failures on every call,skill.crystallize.failederrors, restart loop.Root Cause
resolveConfig()(the single choke point for both disk-loaded and in-memory patched configs) never resolves masked/placeholder secret values back from the environment. Read-side resolution is missing entirely — only the write-side masking exists.Environment
@memtensor/memos-local-plugin2.0.12-beta.1 (source matchesapps/memos-local-plugin)deepseek-v4-flash). Config written via the viewer masks the key; restart loops;curlwith the env key works while the bridge with the masked key fails.memos-bridge.serviceAdditional Context
Observed live 2026-08-11 with 290 candidate skills backlogged and
skill.crystallize.failed ... openai_compatible timed out after 120000 msin the journal.Willingness to Implement
Fix is in open PR #2235 (
fix(config): resolve masked apiKey from env on load) — read-side only, on-disk masking preserved. Includes 6 unit tests; 54/54 pass.