Captures the stock-LangGraph deployment of this fork at Sea Haven: - systemd/open-swe.service: langgraph dev (:2024) + store seed ExecStartPost - seed_store.sh: re-seeds team_settings + user_mappings (in-memory store resets on restart); env-parameterized, no secrets - nginx/openswe.conf: dashboard SPA + scoped /dashboard/api proxy (security boundary; agent API not exposed) - aegra/: deferred self-hosted-runtime alternative (not active on stock) - DEPLOYMENT.md: full runbook (models, build, ingress, OAuth callback) Secrets and internal infra identifiers are intentionally excluded (public fork); real values live in private IT docs.
5.6 KiB
Sea Haven — Open SWE self-hosted deployment
How this fork is deployed at Sea Haven. The runtime is the stock LangGraph dev
server (not the Aegra path — see Aegra). Internal addresses,
ARNs, and account IDs are shown as <PLACEHOLDERS>; the real values live in the
private IT docs (Confluence "AWS Architecture Map") — do not commit them to this
public fork.
Topology
GitHub / Slack ──▶ hooks.seahavenind.com ──┐
│ (public ALB :443, host+path rule)
Browser ─────────▶ openswe.seahavenind.com ─┤
▼
AWS ALB ──(Site-to-Site VPN)──▶ on-prem VM
├─ nginx :80 (dashboard SPA + /dashboard/api proxy)
└─ langgraph dev :2024 (3+ graphs + FastAPI webapp)
└─▶ LangSmith cloud sandbox (build/git/PR)
- The VM is internet-closed; all inbound rides the existing ALB over the VPN.
- Webhooks (
hooks.seahavenind.com) → ALB listener rule scoped to/webhooks/*only → VM:2024. The unauthenticated LangGraph API (/threads,/runs,/assistants,/store) is never path-forwarded. - Dashboard (
openswe.seahavenind.com) → ALB → VM:80(nginx). nginx is the security boundary: it serves the static SPA and proxies only/dashboard/api/to:2024; the agent API is not reachable through it.
VM components
| Component | What |
|---|---|
langgraph dev |
systemd open-swe.service — --host 0.0.0.0 --port 2024 --no-browser --no-reload. In-memory runtime. |
| Store seeding | seed_store.sh as ExecStartPost (re-seeds team settings + user mappings, which the in-memory store loses on restart). |
| nginx | nginx/openswe.conf — SPA from /var/www/openswe, proxy /dashboard/api/ → :2024. |
| Postgres | present (was for the Aegra path); unused by the stock in-memory runtime. |
| swap | 8 GB swapfile — required: the dashboard (ui/) Nitro build OOMs on an 8 GB box without it. |
Models
Model selection is store-driven, not env. LLM_MODEL_ID is effectively dead for
runtime selection; the team_settings/default store doc wins (then per-user profile,
then per-thread). Defaults seeded by seed_store.sh:
- builder:
anthropic:claude-opus-4-8(efforthigh) - reviewer (cross-family):
openai:gpt-5.5(efforthigh) —openai:gpt-4.1is not in this fork'sSUPPORTED_MODELS(agent/dashboard/options.py); a raw value is silently rewritten to gpt-5.5. Add it toSUPPORTED_MODELSfirst if you need 4.1. analyzergraph is hardcoded to the code default and ignores team settings.
Build & deploy the dashboard (ui/)
ui/ is a TanStack Start + Nitro app (build with bun, not plain Vite):
cd ui
export PATH="$HOME/.bun/bin:$PATH"
export NODE_OPTIONS=--max-old-space-size=6144 # + the 8 GB swapfile, or the build OOMs
bun install
bun run build # -> .output/public (static SPA, _shell.html)
sudo cp -r .output/public/. /var/www/openswe/ # served by nginx
Served as a static SPA (per ui/vercel.json); the Nitro .output/server is unused.
Install / wire-up checklist
- App config in
.env(gitignored — never commit): LLM keys, GitHub App creds,LANGSMITH_API_KEY*+DEFAULT_SANDBOX_SNAPSHOT_ID(SANDBOX_TYPE=langsmith— the only sandbox provider with working in-sandbox git/gh auth),LANGGRAPH_URL=http://127.0.0.1:2024, dashboard vars (DASHBOARD_JWT_SECRET,CONFIGURED_ADMINS,DASHBOARD_*_URL=https://openswe.seahavenind.com). systemd/open-swe.service→/etc/systemd/system/,seed_store.shon the VM withOPENSWE_*env exported (owner login/email, default repo, model ids).nginx/openswe.conf→/etc/nginx/sites-available/openswe, symlink intosites-enabled, remove the default site,nginx -t && systemctl reload nginx.- AWS (real IDs in Confluence): IP target groups →
<VM_LAN_IP>:2024and:80; ALB SG egress rules to those ports (the ALB SG is allow-listed — health checks time out without them);:443listener rules for the two hostnames; Route53 ALIAS records → ALB. Webhook rule must stay path-scoped to/webhooks/*. - GitHub App: webhook URL
https://hooks.seahavenind.com/webhooks/github; subscribe to the events the install guide lists (Issue comment, PR review×2, check_run/suite, workflow_run, status) — add Issues too if you want issue-title/body triggers. - GitHub App OAuth callback (manual, UI-only — not API-settable):
https://openswe.seahavenind.com/dashboard/api/auth/callback— without it, dashboard login fails with aredirect_urimismatch.
Triggering
Start a task by mentioning @openswe in a GitHub issue comment (the documented
intake — the open-swe label path needs the Issues event subscription). The
commenter must have a user_mappings entry or the run is skipped.
Aegra (deferred)
aegra/aegra.json + aegra/aegra_entry.py are the self-hosted-runtime alternative
(Apache-2.0, avoids the LangGraph-Platform Elastic license). Not active on the stock
deployment. To use: place both at the repo root, run aegra serve (:2026), and point
LANGGRAPH_URL at :2026. Aegra gives a Postgres-backed durable store/checkpointer,
which removes the need for seed_store.sh and survives restarts (paused HITL
interrupts persist).