Cloud Deployment - Fighter90/career-ops-ui GitHub Wiki
Running the whole stack in the cloud
Most people run career-ops on their own laptop. But the pipeline is at its best when it is always on โ scanning boards while you sleep, keeping the tracker fresh, reachable in a browser from any device. This page is the end-to-end recipe for putting the whole stack on a small cloud server: the parent career-ops pipeline, the career-ops-ui viewer, and the engine that runs the AI โ either your Claude subscription (through the Claude Code CLI) or a local Hermes gateway.
This is operator how-to, not an app feature. The complete, security-focused checklist is
docs/integrations/HERMES.md; the in-app version is Help ยง31.
The three moving parts
| Part | What it is | Where it lives |
|---|---|---|
| career-ops (parent) | The AI job-search pipeline. Owns cv.md, config/, reports/, portals.yml. Driven by an agent CLI. |
the repo root |
| career-ops-ui (this app) | A read-mostly web viewer that surfaces the same files in a browser; writes back only on explicit actions. | career-ops/web-ui/ |
| the engine | Whatever answers the AI prompts โ a Claude subscription, a local Hermes gateway, or provider API keys. | on the box (CLI / gateway) or a remote API |
Two of the three are identical on a server and on your laptop โ only the engine choice and the network exposure change.
1. Provision and install
- A small VPS (1 vCPU / 1 GB RAM is plenty for the viewer), current Linux.
- Node โฅ 18 (22.5+ recommended โ it enables the parent's SQLite tracker index) and
git. - Install exactly like a local install: clone the parent
career-ops, then clone this repo inside it ascareer-ops/web-ui/. - Put provider keys in the parent's
.env(never commit it โ.env/.env.*are gitignored; start from.env.example). - Run the viewer with
npm start, bound to127.0.0.1โ you expose it through a proxy, not directly.
2. Pick your engine
career-ops is CLI-agnostic, so there are three honest options:
- Your Claude subscription โ install the Claude Code CLI on the box and
claude loginwith your Pro/Max plan. The parent's agent runs then use your subscription (no per-token API bill). - Hermes โ run
hermes gatewayon the same box (OpenAI-compatible API athttp://127.0.0.1:8642/v1) and setHERMES_API_KEYin App settings. career-ops-ui's live evaluations route through it (last in the auto provider order). - API keys โ set any of the eighteen providers (Anthropic โ Gemini โ OpenAI โ Qwen โ OpenRouter โ GitHub Models โ Hermes โ DeepSeek โ GLM (Z.ai) โ Kimi (Moonshot) โ MiniMax โ Mistral โ Grok (xAI) โ Together โ Fireworks โ Ollama โ BytePlus Ark โ Volcengine Ark) โ Kimi (Moonshot) โ MiniMax โ Mistral โ Grok (xAI) โ Together โ Fireworks โ Ollama) in the parent
.env, and the โก live actions work headlessly.
You can mix them: a Claude subscription for the parent's heavy agent work, and a cheap or local provider for the viewer's quick evaluations.
3. Expose it safely
Moving off 127.0.0.1 means the safety loopback gave you for free must now be built explicitly โ the code is identical; only the exposure changes.
- Keep the app on loopback; put a reverse proxy (nginx / Caddy) in front that terminates HTTPS (Let's Encrypt / automatic TLS) and forwards to
127.0.0.1:4317. - Run it under systemd or
pm2as a dedicated non-root user,Restart=on-failure. - Put authentication in front โ the app has no login of its own, so the proxy (basic-auth, SSO forward-auth, or a private network / VPN) is what keeps strangers out.
- When you set
HOST=0.0.0.0so the proxy can reach it, the built-in hardening that was a no-op on loopback switches on and becomes load-bearing: the LLM rate-limit, thesafeGetDNS-rebind defense, and path-name sanitizing.
Invariants that must survive the move (do not relax)
- CSP โ no inline scripts,
frame-ancestors 'none'. The proxy must not strip these headers. - SSRF guard โ every user-supplied URL fetch goes through
isValidJobUrl()+safeGet(no loopback, nofile://). - Markdown/XSS boundary โ
stripDangerousMarkdown()server-side + escape-firstUI.md()client-side. - No secrets in logs โ provider keys, tokens, PII never logged; a remote box with shipped logs makes this more important.
- Parent read-only contract โ the server only reads
cv.md/config//reports/and writes on explicit actions.
See also
- Getting Started ยท Security ยท Troubleshooting & FAQ
docs/integrations/HERMES.mdโ the full operator checklist + Telegram bridge + threat model.