module.exports = { apps: [ { name: 'officer', script: 'bun', args: 'start', watch: false, }, // The Anthropic credential proxy. Despite the old name (`officer-claude`) this process does NOT // run agents — it holds the proxy secret and forwards to api.anthropic.com. The process that runs // agents is `officer-agent` below. { name: 'officer-anthropic-proxy', script: 'bun', args: 'run src/servers/sidecar/claude/index.ts', watch: false, }, // The process that actually runs `claude`. It used to be spawned on demand by the main server, // which made every agent session a grandchild of `officer` and killed it on every restart. As a PM2 // peer it survives them. It resolves the owner from the database and the proxy secret from the // proxy's state file, so it needs nothing from `officer` in order to start. { name: 'officer-agent', script: 'bun', args: 'run src/servers/sidecar/claude/user-instance.ts', watch: false, }, { name: 'officer-opencode', script: 'bun', args: 'run src/servers/sidecar/opencode/index.ts', watch: false, }, { name: 'officer-email', script: 'bun', args: 'run src/servers/sidecar/email/index.ts', watch: false, }, // The only sidecar run by `node` rather than `bun`, and the only one that is not TypeScript: node-pty // is a native addon. It also does not use sidecar/connect.ts, and carries its own copy of the // reconnect loop. { name: 'officer-pty', script: 'node', args: 'src/servers/sidecar/pty/index.mjs', watch: false, }, { name: 'officer-vnc', script: 'bun', args: 'run src/servers/sidecar/vnc/index.ts', watch: false, }, { name: 'officer-music', script: 'bun', args: 'run src/servers/sidecar/music/index.ts', watch: false, }, { name: 'officer-vault', script: 'bun', args: 'run src/servers/sidecar/vault/index.ts', watch: false, }, { name: 'officer-slskd', script: 'bun', args: 'run src/servers/sidecar/slskd/index.ts', watch: false, }, { name: 'officer-headscale', script: 'bun', args: 'run src/servers/sidecar/headscale/index.ts', watch: false, }, { name: 'officer-transmission', script: 'bun', args: 'run src/servers/sidecar/transmission/index.ts', watch: false, }, { name: 'officer-invoiceshelf', script: 'bun', args: 'run src/servers/sidecar/invoiceshelf/index.ts', watch: false, }, // The photo library. Wraps a self-hosted Immich and holds its API key; the platform sees none of it. { name: 'officer-photos', script: 'bun', args: 'run src/servers/sidecar/photos/index.ts', watch: false, }, // The bitcoin wallet. Holds seed material (sealed under an owner passphrase) and node credentials, so // it is the one sidecar whose restart has a security-relevant side effect: every wallet relocks. // The one place anything leaves this machine to tell the owner something: push (APNs + FCM) and the // Discord webhook, behind one interface. A sidecar rather than platform code because the producers // are spread across sidecars, and a platform-owned notifier would make every one of them call back in. { name: 'officer-notify', script: 'bun', args: 'run src/servers/sidecar/notify/index.ts', watch: false, }, { name: 'officer-wallet', script: 'bun', args: 'run src/servers/sidecar/wallet/index.ts', watch: false, }, ], };