From 0a7fae60cd658c5da251d80a6ba3248ed6a6da4f Mon Sep 17 00:00:00 2001 From: Johannes du Plessis Date: Tue, 2 Jun 2026 15:55:35 -0700 Subject: [PATCH] docs: document dashboard + refresh local dev setup (#1376) * docs: document dashboard + refresh local dev setup in INSTALLATION - add the web dashboard (ui/) and its FastAPI backend API to the setup flow - document dashboard-login OAuth (GITHUB_APP_CLIENT_ID/SECRET, second callback URL) as distinct from the LangSmith-brokered agent-runtime OAuth - add new env vars: DASHBOARD_API_BASE_URL/BASE_URL/JWT_SECRET/ALLOWED_ORIGINS, CONFIGURED_ADMINS, X_SERVICE_AUTH_JWT_SECRET, LANGGRAPH_URL, SANDBOX_TYPE, REVIEWER_OUTCOMES_DATASET, PUBLIC_REPO_ORG_GATE, SLACK_CLIENT_ID/SECRET/TEAM_ID, SLACK_REPO_OWNER/NAME - add "Sign in with Slack" OIDC account-linking setup - rewrite local dev: make dev serves 3 graphs + FastAPI on :2024; new step for running the UI (bun) on :3000, incl. the required DASHBOARD_ALLOWED_ORIGINS CORS setting and cookie wiring - correct production langgraph.json to three graphs; document Vercel UI deploy - add dashboard verify + troubleshooting sections - README: dashboard feature bullet + updated install blurb * docs: clarify dashboard API setup Co-authored-by: open-swe[bot] --------- Co-authored-by: open-swe[bot] --- INSTALLATION.md | 162 ++++++++++++++++++++++++++++++++++++++++++------ README.md | 3 +- 2 files changed, 145 insertions(+), 20 deletions(-) diff --git a/INSTALLATION.md b/INSTALLATION.md index 96443d2e..9f902159 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -1,7 +1,12 @@ # Installation Guide -This guide walks you through setting up Open SWE end-to-end: local development, GitHub App creation, LangSmith configuration, webhooks, and production deployment. +This guide walks you through setting up Open SWE end-to-end: local development, GitHub App creation, LangSmith configuration, webhooks, the web dashboard, and production deployment. + +Open SWE has two runnable pieces: + +- **The backend** — a LangGraph app (three graphs: `agent`, `reviewer`, `analyzer`) plus a FastAPI app (`agent.webapp:app`) that owns the webhooks and the dashboard API. Both are served together by `langgraph dev`. +- **The dashboard** — a TanStack Start + Vite web app in `ui/` (package name `open-swe-dashboard`). It's a thin client over the FastAPI dashboard API (`/dashboard/api/*`): GitHub-login, per-user model/profile settings, team defaults, enabled-repo and review-style management, user mappings, and the Agents chat UI. It's optional for pure webhook-driven use, but recommended. > **The steps are ordered to avoid forward references.** Each step only depends on things you've already completed. @@ -11,6 +16,7 @@ This guide walks you through setting up Open SWE end-to-end: local development, - [uv](https://docs.astral.sh/uv/) package manager - [LangGraph CLI](https://langchain-ai.github.io/langgraph/cloud/reference/cli/) - [ngrok](https://ngrok.com/) (for local development — exposes webhook endpoints to the internet) +- [Bun](https://bun.sh/) (only if you want to run the dashboard UI locally — see step 8). Node 20+ also works, but `ui/bun.lock` is the canonical lockfile. ## 1. Clone and install @@ -56,7 +62,9 @@ Write this down. You'll use it in the callback URL below and again in step 4 whe 2. Fill in: - **App name**: `open-swe` (or your preferred name) - **Homepage URL**: This can be any valid URL — it's only shown on the GitHub Marketplace page (which you won't be using). Use something like `https://github.com/langchain-ai/open-swe` - - **Callback URL**: `https://smith.langchain.com/host-oauth-callback/` — replace `` with the ID you chose in step 3a (e.g. `https://smith.langchain.com/host-oauth-callback/your-org-github-oauth`) + - **Callback URL**: GitHub Apps allow multiple callback URLs (one per line). Add **both**: + 1. `https://smith.langchain.com/host-oauth-callback/` — replace `` with the ID you chose in step 3a (e.g. `https://smith.langchain.com/host-oauth-callback/your-org-github-oauth`). This is the **agent-runtime** OAuth callback, brokered by LangSmith (step 4b). + 2. `http://localhost:2024/dashboard/api/auth/callback` — the **dashboard-login** OAuth callback (step 8). For production, also add `https:///dashboard/api/auth/callback`. This is a separate, direct GitHub OAuth flow (not via LangSmith), so it needs its own callback URL. - **Request user authorization (OAuth) during installation**: ✅ Enable this - **Webhook URL**: `https:///webhooks/github` — use the ngrok URL from step 2 - **Webhook secret**: generate one and save it — you'll need it later as `GITHUB_WEBHOOK_SECRET`: @@ -83,6 +91,10 @@ After creating the app: 1. **App ID** — shown at the top of the app's settings page. Save this as `GITHUB_APP_ID`. 2. **Private key** — scroll down to **Private keys** → click **Generate a private key**. A `.pem` file will download. Save its contents as `GITHUB_APP_PRIVATE_KEY`. +3. **Client ID** — shown near the top of the app's settings page (starts with `Iv...`). Save this as `GITHUB_APP_CLIENT_ID`. +4. **Client secret** — under **Client secrets** → **Generate a new client secret**. Save it as `GITHUB_APP_CLIENT_SECRET`. + +> `GITHUB_APP_CLIENT_ID` / `GITHUB_APP_CLIENT_SECRET` power the **dashboard login** flow (the direct GitHub OAuth in 3b's second callback URL). They are independent of the LangSmith OAuth provider in step 4b — the dashboard talks to GitHub directly, while the agent runtime resolves per-user tokens through LangSmith. ### 3d. Install the app on your repositories @@ -118,7 +130,7 @@ Open SWE uses [LangSmith](https://smith.langchain.com/) for: ### 4b. Configure GitHub OAuth (optional but recommended) -This lets each user authenticate with their own GitHub account. Without it, all operations use the GitHub App's installation token (a shared bot identity). +This is the **agent-runtime** OAuth provider: it lets each agent run authenticate with the triggering user's own GitHub account, brokered by LangSmith. (It is separate from the dashboard-login OAuth, which uses `GITHUB_APP_CLIENT_ID`/`GITHUB_APP_CLIENT_SECRET` directly — see step 3c.) Without it, all agent operations use the GitHub App's installation token (a shared bot identity). **What this affects:** - **With per-user OAuth**: PRs and commits show the triggering user's identity; each user's GitHub permissions are respected @@ -299,7 +311,8 @@ Users can also override the team/project mapping per-comment by including `repo: }, "oauth_config": { "redirect_urls": [ - "https://smith.langchain.com/host-oauth-callback/" + "https://smith.langchain.com/host-oauth-callback/", + "http://localhost:2024/dashboard/api/slack/callback" ], "scopes": { "bot": [ @@ -350,7 +363,17 @@ Users can also override the team/project mapping per-comment by including `repo: **Default repo:** -Slack messages are routed to the default repo (`DEFAULT_REPO_OWNER`/`DEFAULT_REPO_NAME` — see step 6) unless the user specifies one with `repo:owner/name` in their message. +Slack messages are routed to the Slack default repo (`SLACK_REPO_OWNER`/`SLACK_REPO_NAME`, falling back to `DEFAULT_REPO_OWNER`/`DEFAULT_REPO_NAME` — see step 6) unless the user specifies one with `repo:owner/name` in their message. + +**"Sign in with Slack" account linking (optional):** + +The dashboard can let a user link their Slack identity to their GitHub login via Slack OIDC ("Sign in with Slack"). This is what lets a Slack-triggered run resolve to the right GitHub user. To enable it: + +1. The manifest above already registers the OIDC redirect (`.../dashboard/api/slack/callback`). Under **OpenID Connect** (or **Sign in with Slack**) make sure the `openid`, `email`, and `profile` user scopes are available. +2. From **Basic Information → App Credentials**, save the app's **Client ID** as `SLACK_CLIENT_ID` and **Client Secret** as `SLACK_CLIENT_SECRET`. +3. (Optional) Set `SLACK_TEAM_ID` (your workspace ID, `T...`) to restrict linking to a single workspace. + +If `SLACK_CLIENT_ID`/`SLACK_CLIENT_SECRET` are unset, the "Sign in with Slack" link is simply disabled; the rest of Slack triggering still works. ## 6. Environment variables @@ -381,10 +404,18 @@ GITHUB_APP_INSTALLATION_ID="" # From step 3d # === GitHub Webhook (required) === GITHUB_WEBHOOK_SECRET="" # The secret you generated in step 3b -# === GitHub OAuth via LangSmith (optional) === -# Without these, all operations use the GitHub App's bot token. -# With these, each user authenticates with their own GitHub account. +# === Dashboard GitHub OAuth (required for the dashboard) === +# Direct GitHub OAuth used by the dashboard login flow (not via LangSmith). +GITHUB_APP_CLIENT_ID="" # From step 3c +GITHUB_APP_CLIENT_SECRET="" # From step 3c + +# === Agent-runtime GitHub OAuth via LangSmith (optional) === +# Without these, all agent operations use the GitHub App's bot token. +# With these, each agent run authenticates as the triggering user. GITHUB_OAUTH_PROVIDER_ID="" # The provider ID from steps 3a / 4b +# Secret used to mint short-lived service JWTs that ask LangSmith to resolve a +# specific user's GitHub token. Needed for per-user token resolution in deployed mode. +X_SERVICE_AUTH_JWT_SECRET="" # === Repo Allowlist (optional) === # Comma-separated list of GitHub orgs the agent is allowed to operate on. @@ -403,6 +434,28 @@ ALLOWED_GITHUB_REPOS="" # e.g. "some-user/their-repo,another-org/ DEFAULT_REPO_OWNER="" # Default GitHub org (e.g. "my-org") DEFAULT_REPO_NAME="" # Default GitHub repo (e.g. "my-repo") +# === Dashboard (required to run the web dashboard) === +# Public URL that browsers use for /dashboard/api/* and OAuth callbacks. +# Use the FastAPI backend URL for local/cross-origin direct API calls. +# Use the dashboard frontend URL when a same-origin frontend rewrite proxies /dashboard/api/*. +# Its scheme drives cookie security: http:// => SameSite=Lax (local); +# https:// => Secure + SameSite=None (production). +DASHBOARD_API_BASE_URL="http://localhost:2024" +# Public base URL of the dashboard frontend (the ui/ app). Default post-login redirect. +DASHBOARD_BASE_URL="http://localhost:3000" +# HMAC secret for all dashboard JWTs (session cookie, OAuth state, account-link tokens). +DASHBOARD_JWT_SECRET="" # Generate with: openssl rand -hex 32 +# Comma-separated origins allowed for credentialed CORS and post-login redirects. +# Required whenever the frontend and API are on different origins — including local +# dev (UI :3000 -> API :2024 is cross-origin). CORS is only enabled when this is set. +DASHBOARD_ALLOWED_ORIGINS="http://localhost:3000" # prod: your frontend origin(s) +# Comma-separated email allowlist for admin dashboard endpoints (matched against the +# logged-in user's GitHub email). Empty => nobody is an admin. +CONFIGURED_ADMINS="" # e.g. "alice@my-org.com,bob@my-org.com" +# URL of the LangGraph server the FastAPI side calls to trigger/stream runs. +# Defaults to http://localhost:2024 locally; set to your deployment URL in prod. +LANGGRAPH_URL="http://localhost:2024" + # === Linear (if using Linear trigger) === LINEAR_API_KEY="" # From step 5 LINEAR_WEBHOOK_SECRET="" # From step 5 @@ -412,11 +465,28 @@ SLACK_BOT_TOKEN="" # From step 5 SLACK_BOT_USER_ID="" SLACK_BOT_USERNAME="" SLACK_SIGNING_SECRET="" +# Optional: Slack-specific default repo (falls back to DEFAULT_REPO_OWNER/NAME). +SLACK_REPO_OWNER="" +SLACK_REPO_NAME="" +# Optional: "Sign in with Slack" account linking (GitHub <-> Slack). See step 5. +SLACK_CLIENT_ID="" +SLACK_CLIENT_SECRET="" +SLACK_TEAM_ID="" # Optional; restrict linking to one workspace (T...) # === Exa (optional — enables web search tool) === EXA_API_KEY="" # From https://dashboard.exa.ai +# === Reviewer / Analyzer (optional) === +# LangSmith dataset where reviewer finding outcomes are recorded and read back by +# the analyzer. Defaults to "openswe-reviewer-outcomes" if unset. +REVIEWER_OUTCOMES_DATASET="" +# Single GitHub org whose members may trigger the agent on *public* repos. +# Empty => no public-repo gate (back-compat). Distinct from ALLOWED_GITHUB_ORGS. +PUBLIC_REPO_ORG_GATE="" + # === Sandbox (optional) === +# Provider: langsmith (default), modal, daytona, runloop, or local. See CUSTOMIZATION.md. +SANDBOX_TYPE="langsmith" DEFAULT_SANDBOX_SNAPSHOT_ID="" # Required when SANDBOX_TYPE=langsmith (see step 4c) DEFAULT_SANDBOX_SNAPSHOT_FS_CAPACITY_BYTES="" # Root FS size in bytes (default: 32 GiB) DEFAULT_SANDBOX_VCPUS="" # vCPUs per sandbox (default: 4) @@ -452,15 +522,16 @@ invalidating already-stored GitHub tokens: and the user will be re-prompted to authenticate — same UX as if the thread had never authed. -## 7. Start the server +## 7. Start the backend -Make sure ngrok is still running from step 2, then start the LangGraph server in a second terminal: +Make sure ngrok is still running from step 2, then start the backend in a second terminal: ```bash -uv run langgraph dev --no-browser +make dev # uv run langgraph dev +# or: uv run langgraph dev --no-browser ``` -The server runs on `http://localhost:2024` with these endpoints: +`langgraph dev` serves **all three graphs** (`agent`, `reviewer`, `analyzer`) *and* the FastAPI app (`agent.webapp:app`) together on `http://localhost:2024`. The FastAPI app owns both the webhooks and the dashboard API: | Endpoint | Purpose | |---|---| @@ -469,9 +540,35 @@ The server runs on `http://localhost:2024` with these endpoints: | `GET /webhooks/linear` | Linear webhook verification | | `POST /webhooks/slack` | Slack event webhooks | | `GET /webhooks/slack` | Slack webhook verification | +| `GET /dashboard/api/auth/login` | Dashboard GitHub OAuth login | +| `GET /dashboard/api/auth/callback` | Dashboard GitHub OAuth callback (registered on the App in step 3b) | +| `GET /dashboard/api/*` | Dashboard API (profiles, team settings, repos, review styles, threads, …) | | `GET /health` | Health check | -## 8. Verify it works +> `make run` (`uvicorn agent.webapp:app --port 8000`) serves the FastAPI app **without** the LangGraph runtime, on port 8000. The dashboard's Agents chat features call LangGraph, so for full local dev use `make dev` on `:2024`, not `make run`. + +## 8. Run the dashboard (optional) + +The dashboard is the web app in `ui/`. It's a static TanStack Start client that calls the FastAPI dashboard API from step 7. Run it in a third terminal: + +```bash +cd ui +bun install +cat > .env <<'EOF' +VITE_DASHBOARD_API_BASE_URL="http://localhost:2024" +EOF +bun run dev # vite dev --port 3000 -> http://localhost:3000 +``` + +The dashboard needs `VITE_DASHBOARD_API_BASE_URL` in `ui/.env` pointing at the backend for local dev. The file is intentionally untracked because `.env*` files are gitignored. + +The client calls `${VITE_DASHBOARD_API_BASE_URL}/dashboard/api/*` with `credentials: "include"`, so the backend's `osw_session` cookie rides along. Because the UI (`:3000`) and API (`:2024`) are different origins, the backend needs **CORS** enabled for the UI origin — set `DASHBOARD_ALLOWED_ORIGINS="http://localhost:3000"` (CORS is off unless this is set). Keep `DASHBOARD_API_BASE_URL` on an `http://` URL locally so the cookie uses `SameSite=Lax` rather than `Secure`. + +For the dashboard login to succeed, you need (from steps 3c / 6): `GITHUB_APP_CLIENT_ID`, `GITHUB_APP_CLIENT_SECRET`, `DASHBOARD_JWT_SECRET`, `DASHBOARD_API_BASE_URL`, `DASHBOARD_BASE_URL`, and `DASHBOARD_ALLOWED_ORIGINS`. To reach the admin pages (user mappings, etc.), add your GitHub email to `CONFIGURED_ADMINS`. + +Other UI scripts: `bun run build`, `bun run typecheck`, `bun run lint`, `bun run test`. + +## 9. Verify it works ### GitHub @@ -497,21 +594,31 @@ The server runs on `http://localhost:2024` with these endpoints: 2. Mention the bot: `@open-swe what's in the repo?` 3. You should see a reply in the thread with the agent's response. -## 9. Production deployment +### Dashboard -For production, deploy the agent on [LangGraph Cloud](https://langchain-ai.github.io/langgraph/cloud/) instead of running locally: +1. With the backend (step 7) and UI (step 8) both running, open `http://localhost:3000` +2. Click **Sign in with GitHub** — you'll be sent through the GitHub OAuth flow and back to the dashboard +3. You should land logged-in and be able to see your profile/settings. If your email is in `CONFIGURED_ADMINS`, the **Admin** pages (e.g. User mappings) are available. + +## 10. Production deployment + +Production runs the backend and dashboard separately. + +**Backend** — deploy on [LangGraph Cloud / Platform](https://langchain-ai.github.io/langgraph/cloud/): 1. Push your code to a GitHub repository 2. Connect the repo to LangGraph Cloud -3. Set all environment variables from step 6 in the deployment config -4. Update your webhook URLs (Linear, Slack, GitHub App) to point to your production URL (replace the ngrok URL) +3. Set all environment variables from step 6 in the deployment config. Set `DASHBOARD_BASE_URL` and `LANGGRAPH_URL` to your production URLs (all `https://`). Set `DASHBOARD_API_BASE_URL` to the URL browsers use for dashboard API requests and OAuth callbacks: either the backend URL for direct cross-origin calls, or the dashboard/Vercel URL when a same-origin rewrite proxies `/dashboard/api/*`. +4. Update your webhook URLs (Linear, Slack, GitHub App) and the GitHub App / Slack OAuth callback URLs to your production URLs (replace the ngrok / localhost values). The dashboard GitHub App callback must be `/dashboard/api/auth/callback`. -The `langgraph.json` at the project root already defines the graph entry point and HTTP app: +The `langgraph.json` at the project root defines the three graphs and the HTTP app: ```json { "graphs": { - "agent": "agent.server:get_agent" + "agent": "agent.server:get_agent", + "reviewer": "agent.reviewer:get_reviewer_agent", + "analyzer": "agent.analyzer:get_analyzer" }, "http": { "app": "agent.webapp:app" @@ -519,6 +626,10 @@ The `langgraph.json` at the project root already defines the graph entry point a } ``` +**Dashboard** — the `ui/` app deploys to [Vercel](https://vercel.com/). The recommended production setup uses **same-origin** requests to `/dashboard/api/*` (leave `VITE_DASHBOARD_API_BASE_URL` empty), and `ui/vercel.json` rewrites those to the hosted LangGraph deployment. In this mode, set both `DASHBOARD_API_BASE_URL` and the GitHub App dashboard callback URL to the Vercel/dashboard origin (for example, `https://your-dashboard.vercel.app/dashboard/api/auth/callback`). The OAuth callback response then sets the `osw_session` cookie on the dashboard host, and later same-origin `/dashboard/api/*` requests include it. Update the rewrite `destination` in `ui/vercel.json` to your own LangGraph deployment URL. + +Alternatively, you can run the dashboard as a direct cross-origin client: set `VITE_DASHBOARD_API_BASE_URL` to the hosted backend origin, set `DASHBOARD_API_BASE_URL` to that same backend origin, and include the dashboard origin in `DASHBOARD_ALLOWED_ORIGINS`. + ## Troubleshooting ### Webhook not receiving events @@ -534,6 +645,19 @@ The `langgraph.json` at the project root already defines the graph entry point a - Ensure the GitHub App is installed on the target repositories - Check that the private key includes the full `-----BEGIN RSA PRIVATE KEY-----` and `-----END RSA PRIVATE KEY-----` lines +### Dashboard login fails or won't stay logged in + +- `500 GITHUB_APP_CLIENT_ID not configured` (or client secret): set `GITHUB_APP_CLIENT_ID` / `GITHUB_APP_CLIENT_SECRET` (step 3c) and `DASHBOARD_JWT_SECRET`. +- OAuth `redirect_uri` mismatch: the GitHub App must list `/dashboard/api/auth/callback` as a callback URL (step 3b). Locally that's `http://localhost:2024/dashboard/api/auth/callback`. +- Login redirects but the session doesn't stick: this is almost always a cookie problem. Locally, keep `DASHBOARD_API_BASE_URL` on `http://` (so cookies are `SameSite=Lax`); in prod use `https://` for both API and frontend and add the frontend origin to `DASHBOARD_ALLOWED_ORIGINS`. +- Login rejected with an org error: `ALLOWED_GITHUB_ORGS` gates dashboard login (and requires the App's Organization → Members: Read-only permission). See step 5. +- Admin pages 403: add your GitHub email to `CONFIGURED_ADMINS`. + +### Dashboard UI can't reach the backend + +- Confirm the backend is running via `make dev` on `:2024` (not `make run` on `:8000`). +- Confirm `ui/.env` has `VITE_DASHBOARD_API_BASE_URL=http://localhost:2024`. If it's empty, the UI falls back to relative `/dashboard/api/*`, which only works behind the Vercel rewrite, not in local dev. + ### Sandbox creation failures - Verify `LANGSMITH_API_KEY_PROD` is set and valid diff --git a/README.md b/README.md index c182cc2c..ae23d10c 100644 --- a/README.md +++ b/README.md @@ -134,12 +134,13 @@ This is an area where you can extend Open SWE for your org: add deterministic CI - **GitHub OAuth built-in** — authenticates with your GitHub account automatically - **Opens PRs automatically** — commits changes and opens a draft PR when done, linked back to your ticket - **Subagent support** — the agent can spawn child agents for parallel subtasks +- **Web dashboard** — a companion app (in `ui/`) for GitHub login, per-user model/profile settings, team defaults, enabled-repo and review-style management, user mappings, and an Agents chat UI --- ## Getting Started -- **[Installation Guide](INSTALLATION.md)** — GitHub App creation, LangSmith, Linear/Slack/GitHub triggers, and production deployment +- **[Installation Guide](INSTALLATION.md)** — local dev (backend + dashboard), GitHub App creation, LangSmith, Linear/Slack/GitHub triggers, and production deployment - **[Customization Guide](CUSTOMIZATION.md)** — swap the sandbox, model, tools, triggers, system prompt, and middleware for your org ## License