What happened
With MEMMACHINE_CONFIG_API or --with-config-api (server/app.py), the PUT routes and the resource POST and DELETE routes in server/api_v2/config_router.py call ConfigService methods that edit the process's own Configuration object in place and then _persist_config() (server/api_v2/config_service.py:394-400; called from add_embedder, add_language_model, remove_embedder, remove_language_model, update_memory_config, update_episodic_memory_config, update_long_term_memory_config, update_short_term_memory_config and update_semantic_memory_config). Configuration.save (common/configuration/__init__.py, write_text at :619) re-serializes every section with to_yaml and overwrites the file the process was loaded from.
Consequences:
- Only the replica that handled the request changes. The others keep the old values, so a new session's episodic defaults depend on which replica serves
POST /projects (create_session merges the process's episodic_memory configuration).
- Each worker writes the file from its own in-memory copy, so one worker's write reverts another's change. The Helm chart mounts the file from a ConfigMap subPath (
deployments/helm/templates/memmachine-deployment.yaml:101), which Kubernetes mounts read-only, so the write fails with a logged warning and the change is lost on restart; docker-compose mounts it read-write (docker-compose.yml:136).
to_yaml unwraps every SecretStr (common/configuration/mixin_confs.py:207), and environment references such as api_key: $OPENAI_API_KEY were resolved at load (:66), so the rewritten file contains the literal secrets and loses the references, along with comments and key order.
Expected
Runtime configuration either lives in the database and is read per request by every replica, or the API is documented as single-process and stays off by default. In no case should a save replace environment references with resolved secrets.
Notes
Code read at d6068cdbf (main), paths under packages/server/src/memmachine_server/. The API is opt-in; recorded because MEMMACHINE_WORKERS already makes the per-process mutation observable on one host.
🤖 Written by Claude Code (Claude Fable 5.1) on behalf of @edwinyyyu.
What happened
With
MEMMACHINE_CONFIG_APIor--with-config-api(server/app.py), the PUT routes and the resource POST and DELETE routes inserver/api_v2/config_router.pycallConfigServicemethods that edit the process's ownConfigurationobject in place and then_persist_config()(server/api_v2/config_service.py:394-400; called fromadd_embedder,add_language_model,remove_embedder,remove_language_model,update_memory_config,update_episodic_memory_config,update_long_term_memory_config,update_short_term_memory_configandupdate_semantic_memory_config).Configuration.save(common/configuration/__init__.py,write_textat:619) re-serializes every section withto_yamland overwrites the file the process was loaded from.Consequences:
POST /projects(create_sessionmerges the process'sepisodic_memoryconfiguration).deployments/helm/templates/memmachine-deployment.yaml:101), which Kubernetes mounts read-only, so the write fails with a logged warning and the change is lost on restart; docker-compose mounts it read-write (docker-compose.yml:136).to_yamlunwraps everySecretStr(common/configuration/mixin_confs.py:207), and environment references such asapi_key: $OPENAI_API_KEYwere resolved at load (:66), so the rewritten file contains the literal secrets and loses the references, along with comments and key order.Expected
Runtime configuration either lives in the database and is read per request by every replica, or the API is documented as single-process and stays off by default. In no case should a save replace environment references with resolved secrets.
Notes
Code read at
d6068cdbf(main), paths underpackages/server/src/memmachine_server/. The API is opt-in; recorded becauseMEMMACHINE_WORKERSalready makes the per-process mutation observable on one host.🤖 Written by Claude Code (Claude Fable 5.1) on behalf of @edwinyyyu.