Skip to content

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

  1. Primary goes down -> pick a standby, run claude-mem-switch-server.ps1 -ServerUrl http://<standby>:37877 on every client.
  2. Standby's hourly sync fails harmlessly while the primary is down (aborts before touching local data).
  3. Primary recovers -> on the primary, run the standby's sync script with -Source <standby> to pull back anything written during the outage.
  4. 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.