Architecture - claude-mem-house¶
Component view¶
flowchart TB
subgraph Primary["ANDYGREATROOMPC (primary, always-on)"]
direction TB
Docker["Docker Desktop"]
PG[("Postgres 17\nclaudemem db")]
VK[("Valkey 8")]
CMS["claude-mem-server\n:37877\nREST /v1/* + /v1/mcp"]
Worker["generation worker"]
Docker --- PG
Docker --- VK
Docker --- CMS
Docker --- Worker
CMS --- PG
CMS --- VK
Worker --- PG
end
subgraph Standby["House PC (warm standby)"]
direction TB
DockerS["Docker Desktop"]
PGS[("Postgres 17\n(mirrored)")]
CMSs["claude-mem-server\n:37877"]
Sync["claude-mem-standby-sync.ps1\nscheduled task, hourly"]
DockerS --- PGS
DockerS --- CMSs
end
subgraph ClientPC["Any house PC (client)"]
Claude["Claude Code CLI"]
Settings["~/.claude-mem/settings.json\nCLAUDE_MEM_SERVER_URL / _API_KEY / _PROJECT_ID"]
Claude --- Settings
end
Sync -- "hourly pg_dump --clean --if-exists\n(pull over LAN :5432)" --> PG
Claude -- "MCP + REST, Bearer key" --> CMS
Claude -. "claude-mem-switch-server.ps1\n(failover / failback)" .-> CMSs
Vault[["Andrew's vault\nAPI key + Postgres password"]]
Vault -. "CLAUDE_MEM_HOUSE_API_KEY\nCLAUDE_MEM_HOUSE_PG_PASSWORD" .-> ClientPC
Vault -. "CLAUDE_MEM_HOUSE_API_KEY\nCLAUDE_MEM_HOUSE_PG_PASSWORD" .-> Standby
Script responsibilities¶
| Script | Runs on | Purpose |
|---|---|---|
claude-mem-client-setup.ps1 |
any house PC | Point Claude Code at the primary server, verify REST + MCP access |
claude-mem-standby-prereqs.ps1 |
future standby, elevated | Install WSL2 + Docker Desktop only |
claude-mem-standby-setup.ps1 |
future standby | Build + run the full stack locally, install the hourly sync task |
claude-mem-switch-server.ps1 |
any client | Repoint an existing client at a different server URL (failover/failback) |
Sequence: hourly standby sync¶
sequenceDiagram
participant Task as Scheduled Task<br/>(claude-mem-standby-sync)
participant Sync as claude-mem-standby-sync.ps1
participant LocalPG as Standby Postgres (Docker)
participant PrimaryPG as Primary Postgres :5432
Task->>Sync: trigger (logon + every 60 min)
Sync->>Sync: resolve password (env var, else standby's own .env)
Sync->>Sync: resolve -Source hostname -> IPv4
Sync->>PrimaryPG: pg_dump --clean --if-exists (via local container)
alt primary unreachable
PrimaryPG--xSync: connection refused / timeout
Sync->>Sync: log SKIP, exit 0 (local data untouched)
else dump succeeds
Sync->>Sync: archive timestamped copy (keep last 48)
Sync->>LocalPG: terminate active connections
Sync->>LocalPG: psql restore from dump
Sync->>Sync: log OK, prune old archives
end
Failover / failback flow¶
- Primary goes down -> pick a standby, run
claude-mem-switch-server.ps1 -ServerUrl http://<standby>:37877on every client. - Standby's hourly sync fails harmlessly while the primary is down (aborts before touching local data).
- Primary recovers -> on the primary, run the standby's sync script with
-Source <standby>to pull back anything written during the outage. - Repoint clients to the primary again with
claude-mem-switch-server.ps1.
See the header comments in scripts/claude-mem-standby-setup.ps1 and
scripts/claude-mem-switch-server.ps1 for the full troubleshooting notes.