Skip to content

bug(plugin/cloud): SessionStart-launched daemon can never autosync β€” no cloud env and no file-based toggleΒ #666

Description

@QSAS-Developer

πŸ“‹ Pre-flight Checks

  • I have searched existing issues and this is not a duplicate
  • I understand this issue needs status:approved before a PR can be opened

πŸ“ Bug Description

The engram Claude Code plugin starts the daemon from its SessionStart hook, but that daemon can never perform cloud autosync β€” and it fails completely silently.

Two independent causes combine:

1. The hook starts the daemon with no cloud environment.

plugin/claude-code/scripts/session-start.sh:26-30:

# Ensure engram server is running
if ! curl -sf "${ENGRAM_URL}/health" --max-time 1 > /dev/null 2>&1; then
  engram serve &>/dev/null &
  sleep 0.5
fi

The only thing sourced is _helpers.sh (line 17), which carries no cloud environment logic. The daemon therefore inherits the agent's environment, which does not contain ENGRAM_CLOUD_TOKEN or ENGRAM_CLOUD_AUTOSYNC. plugin/codex/scripts/session-start.sh:28 is identical.

2. There is no file-based way to enable autosync at all.

  • cmd/engram/main.go:804 gates autosync exclusively on an environment variable:
    if strings.TrimSpace(os.Getenv("ENGRAM_CLOUD_AUTOSYNC")) != "1" { return nil, nil }
  • cmd/engram/cloud.go:204-207 β€” cloudConfig persists only server_url and token:
    type cloudConfig struct {
        ServerURL string `json:"server_url"`
        Token     string `json:"token"`
    }

So even with a valid cloud.json, a hook-launched daemon can never enable autosync. The documented setup in docs/AGENT-SETUP.md:861-867 assumes an interactive shell (export ENGRAM_CLOUD_AUTOSYNC=1 …; engram serve), which is not how the plugin starts it.

3. The failure is invisible.

&>/dev/null on line 28 discards both [autosync] ERROR: cloud token is not configured (main.go:822) and [autosync] started (server=…) (main.go:845). Nothing surfaces anywhere. Related: #341.

πŸ”„ Steps to Reproduce

  1. Configure cloud sync normally: valid ~/.engram/cloud.json (server + token), enrolled projects, reachable cloud server.
  2. Put ENGRAM_CLOUD_SERVER / ENGRAM_CLOUD_TOKEN / ENGRAM_CLOUD_AUTOSYNC=1 in a shell env file, as the docs suggest for interactive use.
  3. Start Claude Code so the plugin's SessionStart hook launches the daemon.
  4. Save memories normally for a few days.
  5. Inspect the daemon's environment: tr '\0' '\n' < /proc/$(pgrep -f 'engram serve')/environ | grep ENGRAM β†’ no ENGRAM_* variables at all.
  6. curl -s "http://127.0.0.1:7437/sync/status?project=<P>"

βœ… Expected Behavior

The daemon started by the plugin hook should be able to autosync when cloud is configured β€” either by loading the cloud configuration from a file, or by the hook passing the environment through. A misconfiguration should be visible somewhere, not swallowed.

❌ Actual Behavior

last_sync_at stays null indefinitely on every project. phase never leaves pending/idle. No error is written anywhere the user can see.

Observed on a real installation: cloud sync silently did nothing for 27 days. Roughly 84% of 2,859 observations across 28 projects had never reached the server. The only data that ever arrived came from one manual engram sync --cloud --project <P> batch run months earlier from an interactive shell with the env sourced.

Note that mem_doctor reports status: ok throughout β€” its sync_mutation_required_fields check validates mutation schema, not whether the queue drains, so it does not catch this.

Operating System

Linux (Ubuntu/Debian)

Engram Version

1.20.0 (plugin plugin/claude-code/.claude-plugin/plugin.json β†’ 0.1.1)

Agent / Client

Claude Code

πŸ“‹ Relevant Logs

$ curl -s "http://127.0.0.1:7437/sync/status?project=<redacted>"
{
  "enabled": false,
  "last_sync_at": null,
  "phase": "idle",
  "consecutive_failures": 0,
  "last_error": "",
  "reason_code": "blocked_unenrolled"
}

$ tr '\0' '\n' < /proc/<daemon-pid>/environ | grep '^ENGRAM'
(no output)

πŸ’‘ Additional Context

Two possible fixes, both reasonable β€” I did not want to presume which direction the project prefers, so I am not opening a PR yet:

Option A β€” hook loads an optional env file. Have session-start.sh source ${ENGRAM_CONFIG_DIR:-$HOME/.config/engram}/cloud.env if it exists before launching the daemon. Smallest change, no Go code touched, but it introduces a new file convention the repo does not currently define.

Option B β€” persist the toggle in cloud.json. Add an autosync field to cloudConfig (cmd/engram/cloud.go:204-207) so main.go:804 can honour it without an environment variable. Configuration then lives where the server URL and token already live.

Either way, not discarding the daemon's stderr on line 28 would have surfaced this on day one (see #341).

Worth flagging for whichever direction is chosen: #464 (token stored in cleartext in agent configs) means Option A should probably not encourage putting the token anywhere new.

I am happy to implement whichever option a maintainer prefers, once this gets status:approved.

Related prior art: #326 and #328 covered autosync not starting under engram mcp, but not the daemon launched by the plugin hook. #279 covered the daemon dying on upgrade β€” same silent-failure class, different cause.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions