diff --git a/content/docs/integrations/ai-provider-keys.mdx b/content/docs/integrations/ai-provider-keys.mdx
new file mode 100644
index 0000000..4cd1188
--- /dev/null
+++ b/content/docs/integrations/ai-provider-keys.mdx
@@ -0,0 +1,53 @@
+---
+title: AI Provider Keys
+description: Bring your own Vercel AI Gateway, Anthropic, OpenAI, or OpenRouter key so Mogplex model usage bills to your provider account.
+---
+
+By default, Mogplex bills model usage to your Mogplex plan. Add your own
+provider key to send usage to your own provider account instead, or to use
+models and harnesses that your plan does not include.
+
+## Supported providers
+
+| Provider | Used for | Key format |
+| --- | --- | --- |
+| Vercel AI Gateway | Every model and both CLI harnesses, through one key | AI Gateway API key |
+| Anthropic | Claude models and the [Claude Code](/integrations/claude-code-and-codex) harness | `sk-ant-…` |
+| OpenAI | OpenAI models and the [Codex](/integrations/claude-code-and-codex) harness | `sk-…` |
+| OpenRouter | OpenRouter models | `sk-or-…` |
+
+## Add a key
+
+1. Open **Settings → Provider Keys**.
+2. Paste the key for the provider and select **Save**.
+3. Select **Verify** to confirm that the provider accepts the key.
+
+Personal keys belong to your account. To set keys for a team, open
+**Team Settings → Provider Keys**. Team permissions control who can manage
+them. Keys are stored encrypted and are never shown again after you save them.
+
+## Which key a run uses
+
+For sandbox harness runs (Claude Code and Codex), Mogplex chooses the first
+match:
+
+1. **Your AI Gateway key.** Usage bills to your gateway account.
+2. **Mogplex platform AI**, if your plan includes it. Usage bills to your
+ Mogplex balance.
+3. **Your Anthropic key** (Claude Code) or **OpenAI key** (Codex). Usage bills
+ to that provider.
+
+If none of these is available, the harness is unavailable, and the automation
+harness picker disables it.
+
+Chat and native Mogplex agent runs use the provider of the selected model.
+Models served through OpenRouter need an OpenRouter key.
+
+## Troubleshooting
+
+| Message | Fix |
+| --- | --- |
+| "Hosted AI requires a positive billing balance" | Add funds or choose a plan in **Settings → Billing**, or add your own key. |
+| "No Anthropic API key configured" | Add an Anthropic or AI Gateway key, or choose a different harness. |
+| "No OpenRouter API key configured" | Add an OpenRouter key, or choose a model from another provider. |
+| Verification fails when you save | The key is invalid, revoked, or lacks API access on the provider's side. |
diff --git a/content/docs/integrations/browserbase.mdx b/content/docs/integrations/browserbase.mdx
new file mode 100644
index 0000000..325c3c5
--- /dev/null
+++ b/content/docs/integrations/browserbase.mdx
@@ -0,0 +1,42 @@
+---
+title: Browserbase
+description: Connect Browserbase so Mogplex agents can drive cloud browsers to test pages, scrape content, and automate web flows.
+---
+
+Connect Browserbase to give agents a real cloud browser. Agents can load pages,
+click through flows, take screenshots, and extract content.
+
+| | |
+| --- | --- |
+| Type | Remote MCP server, `https://mcp.browserbase.com/mcp` |
+| Credential | Browserbase API key (`bb_live_…`) |
+| Where tools load | Every surface that loads [connection tools](/configure-and-extend/connections-and-mcp#where-connection-tools-load) |
+
+## Connect
+
+1. In Browserbase, copy an API key from the dashboard. See
+ [Browserbase MCP setup](https://docs.browserbase.com/integrations/mcp/setup).
+2. In Mogplex, open **Connections → Integrations**.
+3. Under **Quick Add — MCP Presets**, select **+ Add** on **Browserbase**.
+4. Paste the key, then select **Add**.
+5. Test the saved connection and confirm that it lists tools.
+
+Mogplex sends the key to Browserbase as the `browserbaseApiKey` query parameter.
+The key is stored encrypted and is not shown again.
+
+## Use it
+
+- "Open the preview URL for this PR, sign up with a test account, and report
+ any step that fails."
+- "Capture the pricing page on staging and production and list the
+ differences."
+
+Browser sessions use your Browserbase plan and bill to your Browserbase
+account.
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| Test fails | The key is invalid or the Browserbase account has no remaining sessions. |
+| The agent cannot reach a preview URL | The preview is protected. Give the agent a URL it can open without signing in, or a bypass token. |
diff --git a/content/docs/integrations/claude-code-and-codex.mdx b/content/docs/integrations/claude-code-and-codex.mdx
new file mode 100644
index 0000000..69772e4
--- /dev/null
+++ b/content/docs/integrations/claude-code-and-codex.mdx
@@ -0,0 +1,59 @@
+---
+title: Claude Code and Codex
+description: Run Anthropic's Claude Code or OpenAI's Codex as the agent harness for Mogplex runs, automations, and Slack tasks.
+---
+
+A harness is the agent program that does the work inside a sandbox. Mogplex
+runs three harnesses:
+
+| Harness | ID | What runs |
+| --- | --- | --- |
+| Mogplex | `mogplex` | Mogplex's native agent. It is always available and uses any model in your catalog. |
+| Claude Code | `claude-code` | Anthropic's `claude` CLI, in the sandbox. |
+| Codex | `codex` | OpenAI's Codex, in the sandbox. It runs through the Agent Client Protocol (ACP). |
+
+Mogplex installs a pinned version of Claude Code or Codex in the sandbox for each
+run, gives it the repository checkout, and streams its events into
+[Observability](/web/observability). Claude Code runs also receive your MCP
+connections; see [where connection tools load](/configure-and-extend/connections-and-mcp#where-connection-tools-load).
+
+## Requirements
+
+Claude Code and Codex need model access. Each harness is available when you have
+one of these:
+
+- **Claude Code:** an AI Gateway key, Mogplex platform AI on your plan, or an
+ Anthropic key.
+- **Codex:** an AI Gateway key, Mogplex platform AI on your plan, or an OpenAI
+ key.
+
+Add keys in **Settings → Provider Keys**. See
+[AI provider keys](/integrations/ai-provider-keys) for the order in which keys
+are used.
+
+## Choose a harness
+
+| Where | How |
+| --- | --- |
+| [Automations](/web/automations) | Select the agent node, then choose **Harness** in the inspector. Unavailable harnesses are disabled. |
+| Slack | `/mogplex harness claude-code`, `/mogplex harness codex`, or `/mogplex harness mogplex`. The choice applies to your future repository runs in that channel. |
+| [Hosted API](/reference/api) and [MCP](/mcp) | Pass `harness` when you start a run. The API defaults to `codex`. |
+
+## Run modes
+
+Claude Code and Codex runs accept a mode that controls what the harness can do
+in the sandbox:
+
+- `SAFE`: the harness does not change the checkout.
+- `AUTO` (default): the harness edits files and runs commands.
+- `YOLO`: the harness runs without approval prompts. Claude Code still blocks
+ its deny-listed tools and commands.
+
+The Mogplex harness takes no mode. See [Start a run](/reference/api#start-a-run)
+for the details.
+
+## Use Mogplex from Claude Code or Codex on your machine
+
+This page covers Claude Code and Codex running **inside** Mogplex. To let a
+local Claude Code, Codex, Cursor, or OpenCode session call Mogplex, install the
+[Mogplex MCP endpoint](/mcp/install).
diff --git a/content/docs/integrations/dependabot.mdx b/content/docs/integrations/dependabot.mdx
new file mode 100644
index 0000000..ec34a81
--- /dev/null
+++ b/content/docs/integrations/dependabot.mdx
@@ -0,0 +1,63 @@
+---
+title: Dependabot
+description: Have Mogplex open a reviewed fix PR when GitHub reports a new Dependabot alert, using the Dependabot autopilot workflow template.
+---
+
+When GitHub creates a Dependabot alert, Mogplex can start an agent that prepares
+the smallest safe dependency fix and opens a PR for review. GitHub still detects
+vulnerabilities and decides when an alert is fixed.
+
+## Requirements
+
+- The [GitHub](/integrations/github) integration is connected, and the Mogplex
+ GitHub App is installed on the repository.
+- **Dependabot alerts** are turned on for the repository in GitHub.
+- The GitHub App has the **Dependabot alerts: Read-only** permission and
+ subscribes to the **Dependabot alert** event. The hosted Mogplex App already
+ has both. For a self-hosted App, see
+ [GitHub App setup](/self-hosting/github-app). Each installation must accept
+ new permissions.
+
+## Set it up
+
+1. Open [Automations](/web/automations) and create a workflow from the
+ **Dependabot autopilot** template.
+2. Select the GitHub installation, the repository scope, the agent, and the
+ model.
+3. Publish the workflow.
+4. Optional: after you confirm that it works, turn off **Dependabot security
+ updates** in GitHub, so that Mogplex is the only tool that opens remediation
+ PRs. Keep **Dependabot alerts** on.
+
+## What the agent does
+
+1. Reads the alert from GitHub. If the alert is already closed, the agent stops.
+ If no patched version exists, the agent reports that the alert needs a human
+ decision.
+2. Checks open PRs to avoid duplicate work.
+3. Uses the repository's own package manager to make the smallest manifest and
+ lockfile change.
+4. Runs the available install, lint, typecheck, test, and build checks.
+5. Opens a PR that lists the alert, GHSA and CVE IDs, severity, package, old and
+ new versions, and check results.
+
+The agent never merges the PR and never dismisses the alert. Major upgrades,
+risky indirect dependency changes, and failed checks are reported for a human
+to decide.
+
+## Alert events
+
+The workflow start node can match `created`, `dismissed`, `fixed`, `reopened`,
+`reintroduced`, and `auto_dismissed`. The template selects only `created`.
+
+- An omitted action list also means `created`. An empty list matches nothing.
+- The other actions record the alert's state. They do not start remediation,
+ close PRs, or cancel runs.
+- Each GitHub delivery starts each matching workflow at most once.
+ Redelivering the same event does not create a second run.
+
+## Verify
+
+In the GitHub App's **Advanced → Recent deliveries**, find a
+`dependabot_alert.created` delivery and confirm that it started one run in
+[Observability](/web/observability).
diff --git a/content/docs/integrations/github.mdx b/content/docs/integrations/github.mdx
index 328632c..3a3773f 100644
--- a/content/docs/integrations/github.mdx
+++ b/content/docs/integrations/github.mdx
@@ -86,6 +86,12 @@ rules, including entry-agent behavior in automations.
- **PR review work starts in the wrong shape**: use an Assignment for standing
repo responsibility, not a one-off Trigger.
+## Dependabot alerts
+
+GitHub can also start work when it reports a vulnerable dependency. See
+[Dependabot](/integrations/dependabot) to have Mogplex open a reviewed fix PR
+for each new alert.
+
## Read next
@@ -93,4 +99,5 @@ rules, including entry-agent behavior in automations.
+
diff --git a/content/docs/integrations/index.mdx b/content/docs/integrations/index.mdx
index 03a6099..ab385bd 100644
--- a/content/docs/integrations/index.mdx
+++ b/content/docs/integrations/index.mdx
@@ -1,62 +1,63 @@
---
title: Integrations
-description: Connect Mogplex to GitHub, Vercel, Slack, CI, the hosted API, MCP clients, and local agent hosts.
+description: Every system Mogplex connects to, including GitHub, Slack, Linear, Notion, Sentry, Supabase, Neon, model providers, agent harnesses, webhooks, the API, and MCP clients.
---
-Integrations connect Mogplex to the systems that own source code, identity,
-runtime credentials, automation, and external tools.
+Integrations connect Mogplex to the systems that own your code, your
+conversations, your tools, and your models. Start with GitHub. Add the others
+when a workflow needs them.
+
+## Source control and chat
+
+
+
+
+
+
+
+## Tool connections
+
+Agents call these services as tools during chat, Control, Slack, and sandbox
+runs. Add them from **Connections → Integrations**.
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+## Models and agents
+
+
+
+
+
+
+
+## Start work from anywhere
+
+
+
+
+
+
## Integration order
-Start with GitHub identity, GitHub App coverage, and managed account access
-before debugging webhook routing. Add optional Vercel metadata, Slack, MCP
-servers, and CI entry points only after the account can see and cover the repo
-that should run.
+1. Connect GitHub and install the Mogplex GitHub App. Confirm coverage in
+ [Installations](/web/installations) before you debug webhook routing.
+2. Add Slack if your team requests work in chat.
+3. Add tool connections for the services agents must read or change.
+4. Add provider keys only if you want usage billed to your own provider
+ account.
+
+Running Mogplex on your own infrastructure? Each integration needs your own
+app or credentials. See [Self-hosting](/self-hosting).
diff --git a/content/docs/integrations/linear.mdx b/content/docs/integrations/linear.mdx
new file mode 100644
index 0000000..8407996
--- /dev/null
+++ b/content/docs/integrations/linear.mdx
@@ -0,0 +1,47 @@
+---
+title: Linear
+description: Connect Linear so Mogplex agents can read and update issues, projects, and cycles while they work on code.
+---
+
+Connect Linear to let agents read the issue they are working on, search related
+issues, and update issues, projects, and comments as the work moves.
+
+| | |
+| --- | --- |
+| Type | Remote MCP server, `https://mcp.linear.app/mcp` |
+| Credential | Linear personal API key or OAuth access token (`lin_api_…`) |
+| Where tools load | Every surface that loads [connection tools](/configure-and-extend/connections-and-mcp#where-connection-tools-load) |
+
+## Connect
+
+1. In Linear, create a personal API key. See
+ [Linear MCP](https://linear.app/docs/mcp).
+2. In Mogplex, open **Connections → Integrations**.
+3. Under **Quick Add — MCP Presets**, select **+ Add** on **Linear**.
+4. Paste the key, then select **Add**.
+5. Test the saved connection and confirm that it lists tools.
+
+Agents act as the Linear user who owns the key. Use a key from an account whose
+access matches what agents should be able to change.
+
+## Use it
+
+- "Read LIN-482, implement the fix, and open a PR that links the issue."
+- "List open bugs in the Platform project and summarize which ones touch the
+ billing code."
+- In an [automation](/web/automations), give an agent node an instruction to
+ comment on the Linear issue when the PR opens.
+
+## What it does not do
+
+Linear is a tool connection only. Mogplex does not receive Linear webhooks, so
+Linear events cannot start runs. Start work from GitHub, Slack, a schedule, a
+[signed webhook](/integrations/webhooks), or the [API](/reference/api).
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| Test fails with an auth error | The key was revoked or copied incompletely. Create a new key and update the connection. |
+| Tools are missing in one repo | The connection may be excluded from that repo. See [scope resolution](/configure-and-extend/connections-and-mcp#scope-resolution). |
+| Tools are missing in workspace chat or Slack | The connection is set to **Ask before running tools**. Those surfaces cannot ask, so use Control. |
diff --git a/content/docs/integrations/meta.json b/content/docs/integrations/meta.json
index e7bc780..e0fb2cc 100644
--- a/content/docs/integrations/meta.json
+++ b/content/docs/integrations/meta.json
@@ -3,8 +3,25 @@
"root": true,
"pages": [
"index",
+ "---Source Control and Chat---",
"github",
+ "dependabot",
+ "slack",
+ "---Tool Connections---",
+ "linear",
+ "notion",
+ "sentry",
+ "supabase",
+ "neon",
+ "browserbase",
+ "sanity",
+ "zapier",
+ "trigger-dev",
+ "---Models and Agents---",
+ "ai-provider-keys",
+ "claude-code-and-codex",
"vercel",
- "slack"
+ "---Triggers---",
+ "webhooks"
]
}
diff --git a/content/docs/integrations/neon.mdx b/content/docs/integrations/neon.mdx
new file mode 100644
index 0000000..fb3e2d6
--- /dev/null
+++ b/content/docs/integrations/neon.mdx
@@ -0,0 +1,44 @@
+---
+title: Neon
+description: Connect Neon so Mogplex agents can manage Postgres projects and branches and run SQL.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+Connect Neon to let agents create database branches for a change, inspect
+schemas, run SQL, and prepare migrations.
+
+| | |
+| --- | --- |
+| Type | Remote MCP server, `https://mcp.neon.tech/mcp` |
+| Credential | Neon API key |
+| Where tools load | Every surface that loads [connection tools](/configure-and-extend/connections-and-mcp#where-connection-tools-load) |
+
+## Connect
+
+1. In Neon, create an API key under
+ [**Account settings → API keys**](https://console.neon.tech/app/settings/api-keys).
+ The preset's **Get a token** link opens this page.
+2. In Mogplex, open **Connections → Integrations**.
+3. Under **Quick Add — MCP Presets**, select **+ Add** on **Neon**.
+4. Paste the key, then select **Add**.
+5. Test the saved connection and confirm that it lists tools.
+
+
+ A Neon API key can manage and delete projects the account owns. Prefer an
+ organization key with limited reach, scope the connection to the repos that
+ need it, and consider **Ask before running tools**.
+
+
+## Use it
+
+- "Create a Neon branch from `main`, apply the migration in this PR to it, and
+ report any errors."
+- "Explain why this query on the `events` table is slow and suggest an index."
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| Test fails with an auth error | The key was revoked. Create a new key and update the connection. |
+| Agent cannot see a project | The key belongs to a different account or organization. |
diff --git a/content/docs/integrations/notion.mdx b/content/docs/integrations/notion.mdx
new file mode 100644
index 0000000..6282a60
--- /dev/null
+++ b/content/docs/integrations/notion.mdx
@@ -0,0 +1,38 @@
+---
+title: Notion
+description: Connect Notion through OAuth so Mogplex agents can search and update pages, databases, and comments.
+---
+
+Connect Notion to let agents read specs and runbooks, search the workspace, and
+write results back to pages and databases.
+
+| | |
+| --- | --- |
+| Type | Remote MCP server, `https://mcp.notion.com/mcp` |
+| Credential | OAuth. You sign in to Notion and choose which pages to share. |
+| Where tools load | Every surface that loads [connection tools](/configure-and-extend/connections-and-mcp#where-connection-tools-load) |
+
+## Connect
+
+1. Open **Connections → Integrations**.
+2. Under **Quick Add — MCP Presets**, select **+ Add** on **Notion**, then
+ **Connect**.
+3. Sign in to Notion, select the workspace and pages to share, and approve.
+4. Back in Mogplex, test the saved connection and confirm that it lists tools.
+
+Mogplex registers itself with Notion automatically (dynamic client
+registration with PKCE). You do not need a Notion integration token.
+
+## Use it
+
+- "Read the 'Checkout v2' spec in Notion and list what the current code is
+ missing."
+- "After the PR merges, add a row to the Releases database with the PR link and
+ a summary."
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| A page is not found | The page was not shared during the OAuth consent step. Reconnect and include it. |
+| Tools stopped working | The authorization expired or was revoked in Notion. Reconnect from **Connections**. |
diff --git a/content/docs/integrations/sanity.mdx b/content/docs/integrations/sanity.mdx
new file mode 100644
index 0000000..e50eb8e
--- /dev/null
+++ b/content/docs/integrations/sanity.mdx
@@ -0,0 +1,37 @@
+---
+title: Sanity
+description: Connect Sanity CMS so Mogplex agents can query and edit structured content, media, and releases.
+---
+
+Connect Sanity to let agents query documents with GROQ, draft and edit content,
+and work with releases next to the code that renders them.
+
+| | |
+| --- | --- |
+| Type | Remote MCP server, `https://mcp.sanity.io` |
+| Credential | Sanity API token |
+| Where tools load | Every surface that loads [connection tools](/configure-and-extend/connections-and-mcp#where-connection-tools-load) |
+
+## Connect
+
+1. In Sanity, create an API token for your project. Give it the least access
+ the agents need, such as **Viewer** for read-only work. See
+ [Sanity MCP server](https://www.sanity.io/docs/ai/mcp-server).
+2. In Mogplex, open **Connections → Integrations**.
+3. Under **Quick Add — MCP Presets**, select **+ Add** on **Sanity CMS**.
+4. Paste the token, then select **Add**.
+5. Test the saved connection and confirm that it lists tools.
+
+## Use it
+
+- "Find every `post` document without an SEO description and draft one for
+ each."
+- "Add a `heroVideo` field to the `landingPage` schema and update the page
+ component to render it."
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| Writes fail with permission errors | The token's role is read-only. Create a token with **Editor** access if agents should write. |
+| Test fails | The token was deleted, or it belongs to a different project. |
diff --git a/content/docs/integrations/sentry.mdx b/content/docs/integrations/sentry.mdx
new file mode 100644
index 0000000..721e0df
--- /dev/null
+++ b/content/docs/integrations/sentry.mdx
@@ -0,0 +1,39 @@
+---
+title: Sentry
+description: Connect Sentry through OAuth so Mogplex agents can read issues, events, and traces and fix the errors they find.
+---
+
+Connect Sentry to let agents look up the error behind a bug report, read stack
+traces and events, and then fix the code in the same run.
+
+| | |
+| --- | --- |
+| Type | Remote MCP server, `https://mcp.sentry.dev/mcp` |
+| Credential | OAuth. Requested scopes: `org:read`, `project:write`, `team:write`, `event:write`. |
+| Where tools load | Every surface that loads [connection tools](/configure-and-extend/connections-and-mcp#where-connection-tools-load) |
+
+## Connect
+
+1. Open **Connections → Integrations**.
+2. Under **Quick Add — MCP Presets**, select **+ Add** on **Sentry**, then
+ **Connect**.
+3. Sign in to Sentry, choose the organization, and approve.
+4. Back in Mogplex, test the saved connection and confirm that it lists tools.
+
+The write scopes let agents update issues, for example to resolve or assign
+them. To keep agents read-only, set the connection to
+[**Ask before running tools**](/configure-and-extend/connections-and-mcp#asking-before-a-connections-tools-run)
+and approve calls in Control.
+
+## Use it
+
+- "Find the top unresolved Sentry issue in `web-app` from the last 24 hours,
+ reproduce it, and open a fix PR."
+- "Read the stack trace for this Sentry issue URL and explain the root cause."
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| The organization is missing | The Sentry account that approved the connection is not a member of it. Reconnect with the right account. |
+| Tools stopped working | The authorization expired or was revoked. Reconnect from **Connections**. |
diff --git a/content/docs/integrations/supabase.mdx b/content/docs/integrations/supabase.mdx
new file mode 100644
index 0000000..1b3d00a
--- /dev/null
+++ b/content/docs/integrations/supabase.mdx
@@ -0,0 +1,46 @@
+---
+title: Supabase
+description: Connect Supabase so Mogplex agents can inspect and manage databases, auth, storage, and edge functions.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+Connect Supabase to let agents inspect schemas, run queries, read logs, and
+manage edge functions in your Supabase projects.
+
+| | |
+| --- | --- |
+| Type | Remote MCP server, `https://mcp.supabase.com/mcp` |
+| Credential | Supabase personal access token (`sbp_…`) |
+| Where tools load | Every surface that loads [connection tools](/configure-and-extend/connections-and-mcp#where-connection-tools-load) |
+
+## Connect
+
+1. In Supabase, create a personal access token under **Account → Access
+ Tokens**. See [Supabase MCP](https://supabase.com/docs/guides/getting-started/mcp).
+2. In Mogplex, open **Connections → Integrations**.
+3. Under **Quick Add — MCP Presets**, select **+ Add** on **Supabase**.
+4. Paste the token, then select **Add**.
+5. Test the saved connection and confirm that it lists tools.
+
+
+ A personal access token can act on every project the account can reach,
+ including production. Scope the connection to the
+ [repos that need it](/configure-and-extend/connections-and-mcp#scope-resolution),
+ and consider **Ask before running tools** so that Control asks before each
+ call.
+
+
+## Use it
+
+- "Check the `orders` table schema in Supabase and write a migration that adds
+ a `refunded_at` column."
+- "Read the last hour of edge function logs for `stripe-webhook` and find why
+ it returns 500."
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| Test fails with `401` | The token was revoked or copied incompletely. Create a new token. |
+| A project is missing | The token's account is not a member of that organization. |
diff --git a/content/docs/integrations/trigger-dev.mdx b/content/docs/integrations/trigger-dev.mdx
new file mode 100644
index 0000000..a5593aa
--- /dev/null
+++ b/content/docs/integrations/trigger-dev.mdx
@@ -0,0 +1,50 @@
+---
+title: Trigger.dev
+description: Connect Trigger.dev so Mogplex agents can inspect and manage tasks, runs, schedules, and deploys in your Trigger.dev projects.
+---
+
+Connect Trigger.dev to let agents investigate failed runs, replay or cancel
+them, manage schedules, and check deploys in **your** Trigger.dev projects.
+
+| | |
+| --- | --- |
+| Type | Server-side API tools, plus the Trigger.dev MCP server in sandboxes and the CLI |
+| Credential | Trigger.dev personal access token (`tr_pat_…`) |
+| Where tools load | API tools in Control, workspace chat, the Slack agent, and native-harness runs. MCP server in Claude Code sandbox runs and the Mogplex CLI. |
+
+## Connect
+
+1. In Trigger.dev, create a personal access token under
+ [**Account → Tokens**](https://cloud.trigger.dev/account/tokens). The
+ preset's **Get a token** link opens this page.
+2. In Mogplex, open **Connections → Integrations**.
+3. Under **Quick Add — MCP Presets**, select **+ Add** on **Trigger.dev**.
+4. Paste the token, then select **Add**.
+5. Test the saved connection. The test confirms that Trigger.dev accepts the
+ token and reports how many API tools are available.
+
+## How it works
+
+One token gives agents two tool sets:
+
+- **API tools that need no sandbox.** Mogplex calls the Trigger.dev management
+ API directly, so the tools work in chat and Slack without a running sandbox.
+- **The Trigger.dev MCP server.** In sandboxes and the CLI, Mogplex starts
+ `npx -y trigger.dev@latest mcp` with your token. The command is fixed in the
+ preset, so nothing you type is ever executed.
+
+See [How the Trigger preset works](/configure-and-extend/connections-and-mcp#how-the-trigger-preset-works)
+for the full list of API tool areas.
+
+## Use it
+
+- "Why did the `sync-orders` task fail in prod in the last hour? Show the error
+ and the run IDs."
+- "Pause the `nightly-report` schedule until Monday."
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| Test fails | The token was revoked, or it is a project secret key (`tr_prod_…`) instead of a personal access token (`tr_pat_…`). |
+| A project is missing | The token's account is not a member of that organization. |
diff --git a/content/docs/integrations/vercel.mdx b/content/docs/integrations/vercel.mdx
index b39119a..8f4a96e 100644
--- a/content/docs/integrations/vercel.mdx
+++ b/content/docs/integrations/vercel.mdx
@@ -1,86 +1,66 @@
---
title: Vercel
-description: Configure repo-linked Vercel project metadata, environment sync, and preview troubleshooting for Mogplex.
+description: How Mogplex uses Vercel Sandbox for agent compute, how to configure a repo's runtime and environment variables, and which Vercel features are not available.
---
-Vercel is optional integration metadata for repos that need project-aware
-preview settings.
-
-Mogplex-managed billing owns sandbox access. Use the Vercel integration when a
-repo needs Vercel-linked project settings for previews, root directories, and
-environment sync. Do not use it as an external sandbox account path.
-
-## Where Vercel is configured
-
-Vercel setup is primarily repo state:
-
-- [Projects](/web/spaces) owns repo-specific Vercel team, project, root
- directory, env mapping, install command, dev command, port, and timeout
- settings.
-- [Sandboxes](/web/sandboxes) shows live and pending runtime state after a
- sandbox exists.
-- [Observability](/web/observability) shows the call, job, sandbox, model, and
- cost trail after work starts.
-
-## Billing model
-
-Sandbox usage is billed through Mogplex. A linked Vercel project can provide
-preview metadata and environment sync, but it is not the billing source for
-Mogplex sandboxes.
-
-That means you can configure GitHub coverage, repo import, agents, triggers,
-assignments, and automations without asking users to attach their own sandbox
-account.
-
-## Repo-linked project settings
-
-Use [Projects](/web/spaces) when one repo needs Vercel-specific runtime
-configuration:
-
-- Vercel team and project mapping
-- root directory for monorepos
-- install command
-- dev command
-- dev port
-- sandbox timeout override
-- environment mapping mode
-- manual environment variables in `.env`-style `KEY=value` format
-
-This matters most for monorepos and preview apps where the default repo root is
-not the app Mogplex should run.
-
-## Environment sync
-
-Repo settings control how sandbox environment variables are assembled.
-
-Use manual repo variables when Mogplex needs values that are not sourced from a
-Vercel project. Use Vercel-linked sync when the repo should inherit environment
-state from the selected Vercel project, with repo settings acting as the
-Mogplex-side control point.
-
-If a preview boots but behaves like the wrong app or misses expected env vars,
-check the repo root, env mapping mode, Vercel project mapping, and manual repo
-vars together.
+import { Callout } from 'fumadocs-ui/components/callout';
+
+Mogplex runs every agent sandbox on
+[Vercel Sandbox](https://vercel.com/docs/vercel-sandbox), inside Mogplex's own
+Vercel project. **You do not need a Vercel account.** Sandbox usage bills
+through your [Mogplex plan](/plans-and-billing).
+
+
+ Mogplex cannot yet bill sandboxes to your own Vercel account or import
+ environment variables from your Vercel projects. Both features need an
+ API-capable Vercel integration that does not exist yet. "Sign in with Vercel"
+ is an identity grant only and cannot read projects or env vars. Add sandbox
+ variables by hand, as described below.
+
+
+## Configure a repo's runtime
+
+Open the repo in [Projects](/web/spaces) and edit its settings. These settings
+control how a sandbox installs and starts your app:
+
+| Setting | Use it when |
+| --- | --- |
+| Launch Branch | Sandboxes should start from a branch other than the default. |
+| Runtime | Auto-detection picks the wrong runtime. |
+| Root Directory | The app lives in a subfolder of a monorepo, such as `apps/web`. |
+| Install Command | The default install command for the package manager is wrong. |
+| Dev Command | The preview needs a specific start command. |
+| Dev Port | The dev server does not use the auto-detected port. |
+| Env Mapping | **Sandbox only** passes only your variables. **Sandbox + preview conventions** also sets Vercel preview variables (`VERCEL=1`, `VERCEL_ENV=preview`, `VERCEL_URL`, `NEXT_PUBLIC_APP_URL`, and similar) unless you define them yourself. |
+| Environment Variables | The app needs env vars. Paste `.env`-style `KEY=value` lines. Surrounding quotes are removed. |
+| Sandbox Billing | Always Mogplex billing. |
## Preview and sandbox troubleshooting
Start from the surface that owns the failure:
- Repo configuration problem: [Projects](/web/spaces)
-- Runtime already exists but looks stuck: [Sandboxes](/web/sandboxes)
-- Need the call or job trail behind the runtime: [Observability](/web/observability)
-- Account lacks managed sandbox access: [Plans & Billing](/plans-and-billing)
-
-Common failure modes:
-
-- **Sandbox launch says access is unavailable**: the account plan may not
- include managed sandbox access yet.
-- **Preview opens the wrong app**: set the repo root directory, install
- command, dev command, and dev port explicitly.
-- **Env values are missing**: inspect env mapping mode, linked Vercel project
- state, and manual repo variables in Projects.
-- **Runtime exists but workspace open lands wrong**: open the workspace from
- [Sandboxes](/web/sandboxes) so it binds to the exact sandbox instance.
+- The sandbox exists but looks stuck: [Sandboxes](/web/sandboxes)
+- You need the call or job trail behind the sandbox:
+ [Observability](/web/observability)
+- The account cannot launch sandboxes: [Plans & Billing](/plans-and-billing)
+
+Common failures:
+
+- **Sandbox launch says access is unavailable**: the plan or balance does not
+ include sandbox compute yet.
+- **Preview opens the wrong app**: set Root Directory, Install Command, Dev
+ Command, and Dev Port explicitly.
+- **Env values are missing**: add them under Environment Variables in the repo
+ settings. Mogplex does not read them from a Vercel project.
+- **The workspace opens the wrong sandbox**: open it from
+ [Sandboxes](/web/sandboxes), so that it binds to that exact sandbox.
+
+## Self-hosting
+
+A self-hosted deployment runs sandboxes on **your** Vercel team, through the
+`PLATFORM_VERCEL_*` variables. See
+[Sandboxes and models](/self-hosting/sandboxes-and-models).
## Read next
diff --git a/content/docs/integrations/webhooks.mdx b/content/docs/integrations/webhooks.mdx
new file mode 100644
index 0000000..50cc809
--- /dev/null
+++ b/content/docs/integrations/webhooks.mdx
@@ -0,0 +1,72 @@
+---
+title: Signed Webhooks
+description: Start a Mogplex automation from any system that can send an HTTP POST, using an HMAC-signed webhook.
+---
+
+A signed webhook lets any system start a Mogplex automation, including CI,
+monitoring alerts, internal tools, Zapier, or a cron job. The system sends JSON
+to a URL unique to the workflow. Mogplex verifies the signature and starts the
+published workflow with the payload.
+
+## Set up the workflow
+
+1. Open [Automations](/web/automations) and select the workflow's start node.
+2. Set the trigger event to **Signed webhook**.
+3. Select the repository that runs should use.
+4. Copy the **Endpoint**:
+ `https://mogplex.com/api/webhooks/flows/`.
+5. Select **Generate signing secret** and copy the secret. Mogplex shows it
+ only once. **Rotate signing secret** replaces it.
+6. Write each agent node's instructions. Use the test payload field to try it.
+7. Publish the workflow. Webhooks start only active, published workflows.
+
+## Send a request
+
+Every request must:
+
+- Use `POST` with a JSON **object** body, at most 1 MB.
+- Include `x-mogplex-delivery`: a unique ID for this event, at most 200
+ characters. Mogplex starts at most one run per delivery ID, so retries are
+ safe.
+- Include `x-mogplex-signature`: `sha256=` followed by the hex HMAC-SHA256 of
+ the raw request body, keyed with the signing secret.
+
+```bash
+BODY='{"prompt":"Investigate the failing checkout test","build":"4821"}'
+SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$MOGPLEX_WEBHOOK_SECRET" -hex | sed 's/^.* //')"
+
+curl -X POST "https://mogplex.com/api/webhooks/flows/$WORKFLOW_ID" \
+ -H "content-type: application/json" \
+ -H "x-mogplex-delivery: build-4821" \
+ -H "x-mogplex-signature: $SIG" \
+ --data "$BODY"
+```
+
+Sign the exact bytes you send. Re-serializing the JSON after signing changes
+the body and fails verification.
+
+```ts
+import { createHmac } from 'node:crypto';
+
+const body = JSON.stringify(payload);
+const signature = `sha256=${createHmac('sha256', secret).update(body).digest('hex')}`;
+```
+
+## Use the payload
+
+The payload is available to the workflow under `metadata.webhook`. Refer to
+its fields in agent instructions and branch conditions.
+
+## Responses
+
+| Status | Meaning |
+| --- | --- |
+| `202` | Accepted. A run was queued. |
+| `200` | Accepted, but no new run started. The response `reason` explains why, for example `REPO_NOT_FOUND` or a delivery ID that was already used. |
+| `400` | The `x-mogplex-delivery` header is missing or too long, or the body is not a JSON object. |
+| `401` | The signature is invalid. |
+| `404` | The workflow has no signing secret, is not active and published, or its start node is not **Signed webhook**. |
+| `413` | The body is larger than 1 MB. |
+
+Treat the signing secret like a password. Store it in your sender's secret
+store, and rotate it if it leaks.
diff --git a/content/docs/integrations/zapier.mdx b/content/docs/integrations/zapier.mdx
new file mode 100644
index 0000000..1e13850
--- /dev/null
+++ b/content/docs/integrations/zapier.mdx
@@ -0,0 +1,42 @@
+---
+title: Zapier
+description: Connect your Zapier MCP server so Mogplex agents can run actions across thousands of apps.
+---
+
+Connect Zapier to give agents the actions you configured in Zapier MCP, such as
+sending email, creating calendar events, updating a CRM, or posting to a channel.
+
+| | |
+| --- | --- |
+| Type | Remote MCP server, at a URL unique to your Zapier account |
+| Credential | The secret Zapier MCP server URL |
+| Where tools load | Every surface that loads [connection tools](/configure-and-extend/connections-and-mcp#where-connection-tools-load) |
+
+## Connect
+
+1. In Zapier MCP, choose the actions that agents may run. Then copy your
+ server URL from the **Connect** tab. See
+ [Use Zapier MCP with your client](https://help.zapier.com/hc/en-us/articles/36265392843917-Use-Zapier-MCP-with-your-client).
+2. In Mogplex, open **Connections → Integrations**.
+3. Under **Quick Add — MCP Presets**, select **+ Add** on **Zapier**.
+4. Paste the full server URL, then select **Add**.
+5. Test the saved connection and confirm that it lists your actions.
+
+The URL is the credential. Anyone who has it can run your actions, so Mogplex
+stores it encrypted. If the URL leaks, rotate it in Zapier and update the
+connection.
+
+## Use it
+
+- "When the deploy finishes, email the release notes to the team using Zapier."
+- "Create a HubSpot task for each customer issue mentioned in this thread."
+
+Agents can use only the actions you enabled in Zapier. To add an action, enable
+it in Zapier, then test the connection again.
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| An action is missing | It is not enabled in Zapier MCP. |
+| Test fails | The URL was rotated in Zapier. Paste the new URL. |
diff --git a/content/docs/meta.json b/content/docs/meta.json
index 3eb1296..aa6ab60 100644
--- a/content/docs/meta.json
+++ b/content/docs/meta.json
@@ -16,6 +16,7 @@
"configure-and-extend",
"integrations",
"guides",
- "reference"
+ "reference",
+ "self-hosting"
]
}
diff --git a/content/docs/self-hosting/background-jobs.mdx b/content/docs/self-hosting/background-jobs.mdx
new file mode 100644
index 0000000..ad06c2a
--- /dev/null
+++ b/content/docs/self-hosting/background-jobs.mdx
@@ -0,0 +1,118 @@
+---
+title: Background Jobs
+description: Connect a Trigger.dev project for automations and long agent runs, deploy the Mogplex tasks, and schedule the cron routes.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+Mogplex runs long and scheduled work outside the web request cycle. Two pieces
+do this work:
+
+- **Trigger.dev tasks** (`trigger/`): automations, Slack runs, agent runs,
+ model syncs, billing reconciliation, and scheduled cleanup.
+- **Cron routes** (`/api/cron/*`): small HTTP endpoints that repair and reap
+ stuck jobs and sandboxes.
+
+## Trigger.dev
+
+Use a project on [Trigger.dev Cloud](https://trigger.dev), or
+[self-host Trigger.dev](https://trigger.dev/docs/self-hosting/overview). The
+self-hosted stack includes a webapp, Postgres, Redis, ClickHouse, object
+storage, a container registry, and worker nodes.
+
+### 1. Configure the app
+
+```bash
+TRIGGER_PROJECT_REF=proj_xxxxxxxxxxxxxxxxxxxx
+TRIGGER_SECRET_KEY=tr_prod_...
+```
+
+
+ If `TRIGGER_PROJECT_REF` is unset, `trigger.config.ts` falls back to the
+ hosted Mogplex project ref. Your deploy then targets a project you do not
+ own and fails.
+
+
+For a self-hosted Trigger.dev instance, also set `TRIGGER_API_URL` to your
+instance URL, both in the app's environment and when you deploy.
+
+### 2. Deploy the tasks
+
+```bash
+pnpm trigger:deploy
+```
+
+The wrapper loads `.env` and `.env.local`, plus any comma-separated files you
+list in `TRIGGER_CLI_ENV_FILES`. Then it runs the Trigger.dev CLI version that
+matches the installed SDK. Authenticate the CLI first with
+`pnpm dlx trigger.dev login`, or set `TRIGGER_ACCESS_TOKEN` in CI.
+
+Deploy the tasks every time you deploy the app, from the same commit. Tasks and
+the app share code in `lib/`, and mismatched versions cause hard-to-trace
+failures.
+
+For local development, `pnpm trigger:dev` runs the tasks against your
+development environment.
+
+### 3. Give the tasks their environment
+
+Tasks import the same server code as the app. They need the same server
+environment: database URLs, AI keys, `CRON_SECRET`, Sentry DSNs, and the GitHub
+and Slack credentials.
+
+- **On Vercel:** when a Vercel token and project ID are available at deploy
+ time (`VERCEL_ACCESS_TOKEN` or `PLATFORM_VERCEL_TOKEN`, plus
+ `VERCEL_PROJECT_ID` or `PLATFORM_VERCEL_PROJECT_ID`, and optionally
+ `VERCEL_TEAM_ID`), the deploy syncs the Vercel project's env vars into
+ Trigger.dev.
+- **Otherwise:** copy the server variables into the Trigger.dev project's
+ environment variables by hand. Update them there whenever you change them in
+ the app.
+
+### Task runtime
+
+`trigger.config.ts` sets Node 22, a maximum run duration of 30 minutes, and one
+attempt per task. Scheduled tasks register themselves on deploy:
+
+| Task | Schedule (UTC) |
+| --- | --- |
+| `sync-models` | `0 6,18 * * *` |
+| `sandbox-reaper` | every 5 minutes |
+| `zombie-row-reaper` | every 5 minutes |
+| `reconcile-ai-call-costs` | every 10 minutes |
+| `vercel-link-reconciliation` | every 15 minutes |
+| `reconcile-sandbox-billing` | every minute |
+| `ai-spend-divergence-check` | `0 5 * * *` |
+| `sandbox-baseline-snapshot-reaper` | `0 5 * * *` |
+| `grant-annual-included-usage` | `30 6 * * *` |
+| `slack-tool-execution-retention` | `17 * * * *` |
+
+## Cron routes
+
+Each cron route accepts `GET` with `Authorization: Bearer `. If
+`CRON_SECRET` is unset, the route returns `503 CRON_SECRET_NOT_CONFIGURED`. A
+wrong token returns `401`.
+
+| Route | Schedule |
+| --- | --- |
+| `/api/cron/sync-models` | `0 6 * * *` |
+| `/api/cron/sandbox-reaper` | `*/5 * * * *` |
+| `/api/cron/repair-jobs` | `*/5 * * * *` |
+| `/api/cron/reconcile-job-runs` | `*/5 * * * *` |
+| `/api/cron/zombie-row-reaper` | `*/5 * * * *` |
+
+**On Vercel**, `vercel.json` schedules these routes and sends the secret for
+you.
+
+**On Docker or another host**, schedule them yourself. Example crontab:
+
+```bash
+*/5 * * * * curl -fsS -H "Authorization: Bearer $CRON_SECRET" https:///api/cron/sandbox-reaper
+*/5 * * * * curl -fsS -H "Authorization: Bearer $CRON_SECRET" https:///api/cron/repair-jobs
+*/5 * * * * curl -fsS -H "Authorization: Bearer $CRON_SECRET" https:///api/cron/reconcile-job-runs
+*/5 * * * * curl -fsS -H "Authorization: Bearer $CRON_SECRET" https:///api/cron/zombie-row-reaper
+0 6 * * * curl -fsS -H "Authorization: Bearer $CRON_SECRET" https:///api/cron/sync-models
+```
+
+Without these routes, stuck jobs are not repaired and idle sandboxes keep
+running and billing.
diff --git a/content/docs/self-hosting/database-and-auth.mdx b/content/docs/self-hosting/database-and-auth.mdx
new file mode 100644
index 0000000..047455b
--- /dev/null
+++ b/content/docs/self-hosting/database-and-auth.mdx
@@ -0,0 +1,117 @@
+---
+title: Database and Auth
+description: Provision Postgres, apply Mogplex migrations, set up a least-privilege runtime role, and configure Better Auth sign-in providers and email.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+Postgres holds all application state. Better Auth handles sign-in and sessions
+and stores its data in the same database.
+
+## Postgres requirements
+
+- Postgres 17.
+- The `vector` and `pg_trgm` extensions must be **available**. The migration
+ script creates them.
+- An owner role that can create schemas, extensions, and roles.
+
+Production runs on [Neon](https://neon.tech). Any Postgres that meets these
+requirements works.
+
+## Connection URLs
+
+| Variable | Used by | Notes |
+| --- | --- | --- |
+| `DATABASE_URL` | Migrations, and the app as a fallback | Owner connection. Use the pooled URL. |
+| `DATABASE_URL_UNPOOLED` | The app as a fallback | Direct, unpooled URL. `pg LISTEN` needs it. |
+| `MOGPLEX_RUNTIME_DATABASE_URL` | The app (preferred) | Pooled URL for the least-privilege `service_role`. |
+| `MOGPLEX_RUNTIME_DATABASE_URL_UNPOOLED` | The app (preferred) | Direct URL for `service_role`. |
+
+The app uses the first one it finds: `MOGPLEX_RUNTIME_DATABASE_URL`, then
+`DATABASE_URL`. Migrations always use `DATABASE_URL`.
+
+### Use a least-privilege runtime role
+
+Running the app as the database owner works, but production should not. The
+migrations harden a `service_role` that can read and write data but cannot
+create databases, roles, or schema objects. The migrations leave the role
+without `LOGIN`, so enable it yourself through an owner connection:
+
+```sql
+alter role service_role with login password '';
+```
+
+Then point `MOGPLEX_RUNTIME_DATABASE_URL` and
+`MOGPLEX_RUNTIME_DATABASE_URL_UNPOOLED` at `service_role`. Never put this
+password in a migration or a checked-in file.
+
+## Apply migrations
+
+```bash
+# Local: load .env.local
+pnpm exec tsx --env-file=.env.local scripts/apply-neon-migrations.ts
+
+# CI or production: DATABASE_URL already exported
+pnpm exec tsx scripts/apply-neon-migrations.ts
+
+# Preview without changing anything
+pnpm exec tsx --env-file=.env.local scripts/apply-neon-migrations.ts --dry-run
+```
+
+The script does the following:
+
+1. Creates a ledger table, `neon_migrations.schema_migrations`.
+2. On an empty database (no tables in `public`), applies `neon/baseline.sql` and
+ records every migration the baseline covers as applied.
+3. Applies pending `neon/migrations/*.sql` files in filename order, each in its
+ own transaction, and records each one.
+4. Exits with code 1 on the first failure.
+
+The app never changes the schema at startup. Run this script before first boot
+and before you deploy each upgrade.
+
+
+ A statement that cannot run inside a transaction, such as
+ `CREATE INDEX CONCURRENTLY`, must run separately.
+
+
+## Better Auth
+
+| Variable | Required | Value |
+| --- | --- | --- |
+| `BETTER_AUTH_SECRET` | Yes | `openssl rand -base64 32` |
+| `BETTER_AUTH_URL` | No | Defaults to `NEXT_PUBLIC_APP_URL`. |
+
+Better Auth is mounted at `/api/auth/*`. It trusts only the configured app URL
+as an origin.
+
+## Sign-in providers
+
+Configure at least one provider. A provider turns on only when both its client
+ID and secret are set.
+
+| Provider | Variables | Callback URL to register |
+| --- | --- | --- |
+| GitHub | `AUTH_GITHUB_CLIENT_ID`, `AUTH_GITHUB_CLIENT_SECRET` | `https:///api/auth/callback/github` |
+| Google | `AUTH_GOOGLE_CLIENT_ID`, `AUTH_GOOGLE_CLIENT_SECRET` | `https:///api/auth/callback/google` |
+| Microsoft | `AUTH_MICROSOFT_CLIENT_ID`, `AUTH_MICROSOFT_CLIENT_SECRET`, `AUTH_MICROSOFT_TENANT_ID` (default `common`) | `https:///api/auth/callback/microsoft` |
+| Email and password | Resend (below) | None |
+
+GitHub sign-in is identity only. Repository access comes from a separate GitHub
+OAuth app and your [GitHub App](/self-hosting/github-app).
+
+## Email
+
+Email and password sign-up requires address verification. Verification and
+password-reset emails go through [Resend](https://resend.com). If sending fails,
+sign-up fails. It does not skip the check.
+
+| Variable | Purpose |
+| --- | --- |
+| `RESEND_API_KEY` | Sends all transactional email. |
+| `WAITLIST_FROM_EMAIL` | Default sender. It must be on a domain you verified in Resend. |
+| `AUTH_FROM_EMAIL` | Sender for auth email. Falls back to `WAITLIST_FROM_EMAIL`. |
+| `BILLING_FROM_EMAIL` | Sender for billing email. Falls back to `WAITLIST_FROM_EMAIL`. |
+| `EMAIL_UNSUBSCRIBE_SECRET` | Signs unsubscribe links. Required in production. |
+
+If you skip Resend, use social sign-in only.
diff --git a/content/docs/self-hosting/environment-variables.mdx b/content/docs/self-hosting/environment-variables.mdx
new file mode 100644
index 0000000..51da6b5
--- /dev/null
+++ b/content/docs/self-hosting/environment-variables.mdx
@@ -0,0 +1,157 @@
+---
+title: Environment Variables
+description: Every environment variable a self-hosted Mogplex reads, grouped by feature, with required, build-time, and default values marked.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+`.env.example` in the repository is the starting template. Copy it to
+`.env.local` for local development, or to `.env` for Docker.
+
+
+ Next.js compiles every `NEXT_PUBLIC_*` value into the client bundle when it
+ builds. Changing one at runtime has no effect. Rebuild after each change. With
+ Docker, pass them as `--build-arg`.
+
+
+Trigger.dev tasks need the same **server** variables as the app. See
+[Background jobs](/self-hosting/background-jobs#3-give-the-tasks-their-environment).
+
+## Required
+
+| Variable | Notes |
+| --- | --- |
+| `MOGPLEX_DATA_BACKEND` | `neon`. |
+| `NEXT_PUBLIC_MOGPLEX_DATA_BACKEND` | `neon`. Must match `MOGPLEX_DATA_BACKEND`. Build-time. |
+| `NEXT_PUBLIC_APP_URL` | Public URL of the deployment, such as `https://mogplex.example.com`. Build-time. |
+| `DATABASE_URL` | Owner connection. Migrations use it, and the app falls back to it. |
+| `DATABASE_URL_UNPOOLED` | Direct connection, for `pg LISTEN`. |
+| `BETTER_AUTH_SECRET` | `openssl rand -base64 32`. |
+| `CRON_SECRET` | Bearer token for `/api/cron/*` and machine-only routes. |
+| `INTERNAL_API_SECRET` | Server-to-server sandbox actions. |
+| `CONNECTIONS_ENCRYPTION_KEY` | 32-byte hex key (`openssl rand -hex 32`) that encrypts saved connection credentials. Do not rotate without a plan: see [Operations](/self-hosting/operations#secrets). |
+| `EMAIL_UNSUBSCRIBE_SECRET` | Signs unsubscribe links. The app throws in production without it. |
+| One sign-in provider | `AUTH_GITHUB_*`, `AUTH_GOOGLE_*`, `AUTH_MICROSOFT_*`, or Resend for email and password. |
+
+## Database
+
+| Variable | Notes |
+| --- | --- |
+| `MOGPLEX_RUNTIME_DATABASE_URL` | Preferred app connection, as the least-privilege `service_role`. |
+| `MOGPLEX_RUNTIME_DATABASE_URL_UNPOOLED` | Direct URL for `service_role`. |
+
+See [Database and auth](/self-hosting/database-and-auth#use-a-least-privilege-runtime-role).
+
+## Auth and email
+
+| Variable | Notes |
+| --- | --- |
+| `BETTER_AUTH_URL` | Defaults to `NEXT_PUBLIC_APP_URL`. |
+| `BETTER_AUTH_API_KEY` | Optional. Connects the better-auth.com dashboard. |
+| `AUTH_GITHUB_CLIENT_ID`, `AUTH_GITHUB_CLIENT_SECRET` | GitHub sign-in. Callback: `/api/auth/callback/github`. |
+| `AUTH_GOOGLE_CLIENT_ID`, `AUTH_GOOGLE_CLIENT_SECRET` | Google sign-in. Callback: `/api/auth/callback/google`. |
+| `AUTH_MICROSOFT_CLIENT_ID`, `AUTH_MICROSOFT_CLIENT_SECRET` | Microsoft sign-in. Callback: `/api/auth/callback/microsoft`. |
+| `AUTH_MICROSOFT_TENANT_ID` | Default `common` (any Microsoft account). |
+| `RESEND_API_KEY` | Required for email and password sign-up (verification email). |
+| `WAITLIST_FROM_EMAIL` | Default sender, on a domain verified in Resend. |
+| `AUTH_FROM_EMAIL`, `BILLING_FROM_EMAIL` | Optional senders. Fall back to `WAITLIST_FROM_EMAIL`. |
+
+## GitHub
+
+| Variable | Notes |
+| --- | --- |
+| `GITHUB_APP_ID` | GitHub App ID. |
+| `GITHUB_APP_NAME` | GitHub App slug, as in `github.com/apps/`. |
+| `GITHUB_APP_PRIVATE_KEY` | PEM. Write newlines as `\n` in single-line env files. |
+| `GITHUB_WEBHOOK_SECRET` | Must match the App's webhook secret. |
+| `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` | GitHub OAuth app for repository listing and reauthorization. Not in `.env.example`. |
+
+See [GitHub App](/self-hosting/github-app).
+
+## Background jobs
+
+| Variable | Notes |
+| --- | --- |
+| `TRIGGER_PROJECT_REF` | Your Trigger.dev project ref. Always set it. |
+| `TRIGGER_SECRET_KEY` | Secret key for the Trigger.dev environment. |
+| `TRIGGER_API_URL` | Only for a self-hosted Trigger.dev instance. |
+| `TRIGGER_CLI_ENV_FILES` | Optional. Extra comma-separated env files for `pnpm trigger:deploy`. |
+
+## Sandboxes
+
+| Variable | Notes |
+| --- | --- |
+| `PLATFORM_VERCEL_TOKEN` | Vercel token that creates sandboxes. |
+| `PLATFORM_VERCEL_TEAM_ID` | Vercel team that owns the sandboxes. |
+| `VERCEL_PROJECT_ID` or `PLATFORM_VERCEL_PROJECT_ID` | Vercel project for the sandboxes. |
+| `PLATFORM_SANDBOX_COST_CENTS_PER_SECOND` | Optional. Cost-reporting rate. Default `0.0028`. |
+
+## Models and agent features
+
+| Variable | Notes |
+| --- | --- |
+| `AI_GATEWAY_API_KEY` | Platform model access through Vercel AI Gateway. |
+| `OPENROUTER_API_KEY` | Optional. Model catalog sync. |
+| `OPENAI_API_KEY` | Optional. Memory embeddings. Without it, memory search uses plain text matching. |
+| `EXA_API_KEY` | Optional. Agent web search and page extraction. Server-side only. |
+| `EXA_RESEARCH_SECRET` | Optional. Lets Trigger.dev workers use web search through the app. |
+| `AUTOMATION_GATEWAY_CACHING` | Optional. `off` turns off prompt caching for automation jobs. |
+| `AUTOMATION_GATEWAY_FALLBACK_MODELS` | Optional. Comma-separated fallback models for automation jobs. |
+| `DECISION_MODES`, `DECISIONS_DISABLED` | Optional. Overrides for the decision layer. |
+| `MOGPLEX_HARNESS_ACP` | Optional. `off` runs Codex through its CLI instead of the Agent Client Protocol. |
+
+## User access
+
+| Variable | Notes |
+| --- | --- |
+| `PLATFORM_ACCESS_EMAILS` | Comma-separated emails with platform AI and sandbox access. |
+| `PLATFORM_ACCESS_EMAIL_DOMAINS` | Comma-separated domains, such as `example.com`. |
+| `PLATFORM_ACCESS_USER_IDS` | Comma-separated Mogplex user IDs. |
+
+See [Give users access](/self-hosting/sandboxes-and-models#give-users-access).
+
+## Slack
+
+| Variable | Notes |
+| --- | --- |
+| `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET` | Slack app OAuth credentials. |
+| `SLACK_SIGNING_SECRET` | Verifies requests to `/api/webhooks/slack`. |
+| `SLACK_REDIRECT_URI` | Optional. Default `${NEXT_PUBLIC_APP_URL}/api/integrations/slack/callback`. |
+
+## Public API and MCP
+
+| Variable | Notes |
+| --- | --- |
+| `MOGPLEX_API_URL` | Optional. Overrides the host for the app's calls to its own API. |
+| `MOGPLEX_MCP_RESOURCE_URL` | Optional. OAuth protected-resource URL. Default `${NEXT_PUBLIC_APP_URL}/api/v1/mogplex/mcp`. |
+| `MOGPLEX_MCP_ALLOWED_ORIGINS` | Optional. Comma-separated browser origins that may call the MCP endpoint. Server-side MCP clients do not need it. |
+
+## Observability
+
+| Variable | Notes |
+| --- | --- |
+| `NEXT_PUBLIC_SENTRY_DSN` | Browser DSN. Build-time. |
+| `SENTRY_DSN` | Server DSN. Defaults to `NEXT_PUBLIC_SENTRY_DSN`. |
+| `SENTRY_ORG`, `SENTRY_PROJECT`, `SENTRY_AUTH_TOKEN` | Source map upload during builds. |
+| `SENTRY_ENVIRONMENT`, `SENTRY_TRACES_SAMPLE_RATE` | Optional overrides. |
+| `NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE`, `NEXT_PUBLIC_SENTRY_REPLAYS_SESSION_SAMPLE_RATE`, `NEXT_PUBLIC_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE` | Optional browser sampling. Build-time. |
+
+## Billing (optional)
+
+Leave these unset to run without billing. Then grant access with the
+[allowlists](#user-access).
+
+| Variable | Notes |
+| --- | --- |
+| `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET` | Your Stripe account. Webhook: `/api/webhooks/stripe`. |
+| `CAPACITY_BILLING_OPERATIONS_ENABLED` | Default `false`. Turns on capacity billing operations. |
+| `CAPACITY_BILLING_LIVE_WRITES_ENABLED` | Default `false`. Required with a live Stripe key. |
+| `CAPACITY_BILLING_PILOT_ACCOUNT_IDS` | Comma-separated billing account IDs for the reserved-concurrency pilot. |
+
+## Other
+
+| Variable | Notes |
+| --- | --- |
+| `NEXT_PUBLIC_C15T_URL` | Optional. Hosted cookie-consent backend. If unset, consent is stored only in the browser. Build-time. |
+| `NEXT_PUBLIC_VERCEL_APP_CLIENT_ID`, `VERCEL_APP_CLIENT_SECRET` | Optional. **Sign in with Vercel** (identity only). Callback: `/api/auth/vercel/callback`. Build-time client ID. |
+| `NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY` | Legacy Supabase deployments only. Leave them empty for Neon. |
diff --git a/content/docs/self-hosting/github-app.mdx b/content/docs/self-hosting/github-app.mdx
new file mode 100644
index 0000000..a5e3728
--- /dev/null
+++ b/content/docs/self-hosting/github-app.mdx
@@ -0,0 +1,132 @@
+---
+title: GitHub App
+description: Create and configure the GitHub App and OAuth app that give a self-hosted Mogplex repository access, webhooks, checks, and PR reviews.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+A self-hosted deployment needs its **own** GitHub App. The hosted Mogplex App
+sends its webhooks to mogplex.com, not to your deployment.
+
+GitHub has two separate jobs in Mogplex. Configure both:
+
+| Piece | What it unlocks | Variables |
+| --- | --- | --- |
+| GitHub App | Installation-backed repo access, webhooks, check runs, PR reviews, triggers, and automations | `GITHUB_APP_ID`, `GITHUB_APP_NAME`, `GITHUB_APP_PRIVATE_KEY`, `GITHUB_WEBHOOK_SECRET` |
+| GitHub OAuth app | Repository listing and import, and reauthorization when a token needs new scopes | `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` |
+
+GitHub *sign-in* (`AUTH_GITHUB_*`) is a third, identity-only setting. See
+[Database and auth](/self-hosting/database-and-auth#sign-in-providers).
+
+## 1. Create the GitHub App
+
+Open **GitHub → Settings → Developer settings → GitHub Apps → New GitHub App**.
+To create the App under an organization, use the organization's settings.
+The repository has no App manifest, so enter these values by hand.
+
+| Field | Value |
+| --- | --- |
+| Homepage URL | `https://` |
+| Setup URL | `https:///api/auth/github/callback` (also select **Redirect on update**) |
+| Webhook | Active |
+| Webhook URL | `https:///api/webhooks/github` |
+| Webhook secret | A random value. Store the same value as `GITHUB_WEBHOOK_SECRET`. |
+| Where can this App be installed? | **Any account**, if users outside your organization will install it |
+
+### Repository permissions
+
+| Permission | Access | Used for |
+| --- | --- | --- |
+| Contents | Read and write | Clone, push branches, read files |
+| Pull requests | Read and write | Open, update, review, and merge PRs |
+| Issues | Read and write | Read and update issues, post comments |
+| Checks | Read and write | The Mogplex PR review check run |
+| Commit statuses | Read and write | Automation `set_status` actions |
+| Actions | Read-only | Read CI workflow runs and job logs |
+| Metadata | Read-only | Required by GitHub |
+| Dependabot alerts | Read-only | [Dependabot remediation](/integrations/dependabot) (optional) |
+
+### Organization permissions
+
+| Permission | Access | Used for |
+| --- | --- | --- |
+| Members | Read-only | Lists org members when you create a team |
+
+### Subscribe to events
+
+- Pull request
+- Push
+- Issues
+- Issue comment
+- Pull request review comment
+- Commit comment
+- Check run
+- Workflow run
+- Repository
+- Dependabot alert (optional, for Dependabot remediation)
+
+GitHub always sends `installation` and `installation_repositories` events to an
+App. Mogplex uses those events to keep repository coverage in sync.
+
+## 2. Generate a private key
+
+On the App page, select **Generate a private key**. Store the PEM as
+`GITHUB_APP_PRIVATE_KEY`. In a single-line env file, replace each newline with
+`\n`.
+
+```bash
+GITHUB_APP_ID=123456
+GITHUB_APP_NAME=my-mogplex # the App's URL slug: github.com/apps/
+GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIE...\n-----END RSA PRIVATE KEY-----\n"
+GITHUB_WEBHOOK_SECRET=...
+```
+
+Mogplex treats the App as configured only when `GITHUB_APP_ID`,
+`GITHUB_APP_PRIVATE_KEY`, and `GITHUB_APP_NAME` are all set.
+`GITHUB_APP_NAME` builds the install link
+`https://github.com/apps//installations/new`.
+
+## 3. Create the GitHub OAuth app
+
+Open **Developer settings → OAuth Apps → New OAuth App**.
+
+| Field | Value |
+| --- | --- |
+| Homepage URL | `https://` |
+| Authorization callback URL | `https:///api/auth/github/callback` |
+
+Mogplex requests the `repo`, `read:org`, `read:user`, and `user:email` scopes.
+Store the credentials:
+
+```bash
+GITHUB_CLIENT_ID=...
+GITHUB_CLIENT_SECRET=...
+```
+
+
+ GitHub accepts any redirect path under an OAuth app's callback URL. If you set
+ the callback URL to `https:///api/auth`, one OAuth app serves both
+ `/api/auth/github/callback` and GitHub sign-in at
+ `/api/auth/callback/github`. Then use the same credentials for
+ `GITHUB_CLIENT_*` and `AUTH_GITHUB_*`.
+
+
+## 4. Install and verify
+
+1. Redeploy so the app reads the new variables.
+2. Sign in, open **Settings → Account**, and connect GitHub. This installs the
+ App on the accounts or organizations that own your repositories.
+3. Open [Installations](/web/installations) and confirm coverage. Then import a
+ repository in [Projects](/web/spaces).
+4. In the App's **Advanced → Recent deliveries**, confirm that deliveries return
+ `2xx`. A `401` means `GITHUB_WEBHOOK_SECRET` does not match the App's
+ webhook secret.
+
+## Troubleshooting
+
+| Symptom | Check |
+| --- | --- |
+| **Connect GitHub** opens an OAuth screen instead of the App install | One of `GITHUB_APP_ID`, `GITHUB_APP_PRIVATE_KEY`, or `GITHUB_APP_NAME` is missing. |
+| Repos are listed but triggers never fire | The App is not installed on that owner, or the webhook URL points somewhere else. |
+| `invalid_state` after the GitHub redirect | The callback reached a different domain than the one that started the flow. Make `NEXT_PUBLIC_APP_URL` match the public URL. |
+| New permissions have no effect | Each installation must accept the new permissions on GitHub. |
diff --git a/content/docs/self-hosting/index.mdx b/content/docs/self-hosting/index.mdx
new file mode 100644
index 0000000..935798b
--- /dev/null
+++ b/content/docs/self-hosting/index.mdx
@@ -0,0 +1,74 @@
+---
+title: Self-Hosting
+description: Run Mogplex on your own infrastructure. Learn which services it depends on, what each one unlocks, and which deployment shape to choose.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+Mogplex is open source under Apache-2.0. The code in
+[github.com/Mogplex/mogplex](https://github.com/Mogplex/mogplex) is the same code
+that runs [mogplex.com](https://mogplex.com). Self-hosting has no license fee, no
+seat limit, and no gated features.
+
+You run the Next.js app. Your own accounts supply the database, background jobs,
+sandboxes, models, and GitHub App. You control the data, the credentials, and
+the bill.
+
+
+ Self-hosting has no SLA. Ask questions in
+ [GitHub Discussions](https://github.com/Mogplex/mogplex/discussions) and file
+ bugs as [issues](https://github.com/Mogplex/mogplex/issues). The project treats
+ friction in the self-hosting path as a bug.
+
+
+## How a deployment fits together
+
+| Part | Runs on | Required? | Without it |
+| --- | --- | --- | --- |
+| Web app (Next.js) | Vercel, or any container host | Yes | Nothing runs. |
+| Postgres 17 + Better Auth | [Neon](https://neon.tech) or any Postgres 17 with `vector` and `pg_trgm` | Yes | The app cannot boot or sign anyone in. |
+| Email ([Resend](https://resend.com)) | Resend | Yes, for email sign-in | Email and password sign-up cannot verify addresses. Social sign-in still works. |
+| GitHub App | Your own GitHub App | For repo work | No repo access, webhooks, PR reviews, or GitHub triggers. |
+| Background jobs | [Trigger.dev](https://trigger.dev), cloud or self-hosted | For agent work | Automations, Slack runs, syncs, and long agent runs do not start. |
+| Sandboxes | [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox) | For code execution | Agents cannot clone, edit, or run code. |
+| Models | [Vercel AI Gateway](https://vercel.com/docs/ai-gateway) key, or per-user provider keys | For agent work | Chat and agents have no model to call. |
+| Slack app | Your own Slack app | Optional | No Slack integration. |
+| Sentry | Your own Sentry project | Optional | No error tracking. |
+| Stripe | Your own Stripe account | Optional | No billing. See [Give users access](/self-hosting/sandboxes-and-models#give-users-access). |
+
+The app boots with only Postgres configured. Each additional service unlocks one
+feature area, so you can bring Mogplex up in stages.
+
+
+ Hosted AI and sandboxes are gated per user. A user needs a positive billing
+ balance, a profile flag, or an entry in the `PLATFORM_ACCESS_*` allowlists.
+ Without Stripe, nobody has a balance, so add your users to the allowlist. If
+ you skip this step, every run fails with "requires a positive billing
+ balance". See [Give users access](/self-hosting/sandboxes-and-models#give-users-access).
+
+
+## Choose a deployment shape
+
+**Vercel (recommended).** Fork the repo, link it to a project in your own Vercel
+team, set the environment variables, and deploy. Production runs this way every
+day. Vercel also runs the cron routes and syncs env vars to Trigger.dev for you.
+
+**Docker.** Build the image from the repo and run it on any container host. The
+image contains only the web app. You provision every backing service yourself,
+schedule the cron routes yourself, and copy env vars into Trigger.dev by hand.
+Sandboxes still need a Vercel account.
+
+## Time to plan
+
+- **App shell** (Postgres, auth, one sign-in provider): about 15 minutes.
+- **Full product** (GitHub App, Trigger.dev, sandboxes, models, Slack): an
+ afternoon. Each service has its own sign-up, billing, and credential rotation.
+
+## Read next
+
+
+
+
+
+
+
diff --git a/content/docs/self-hosting/meta.json b/content/docs/self-hosting/meta.json
new file mode 100644
index 0000000..e275724
--- /dev/null
+++ b/content/docs/self-hosting/meta.json
@@ -0,0 +1,17 @@
+{
+ "title": "Self-Hosting",
+ "root": true,
+ "pages": [
+ "index",
+ "quickstart",
+ "---Services---",
+ "database-and-auth",
+ "github-app",
+ "background-jobs",
+ "sandboxes-and-models",
+ "slack-app",
+ "---Reference---",
+ "environment-variables",
+ "operations"
+ ]
+}
diff --git a/content/docs/self-hosting/operations.mdx b/content/docs/self-hosting/operations.mdx
new file mode 100644
index 0000000..8a96f72
--- /dev/null
+++ b/content/docs/self-hosting/operations.mdx
@@ -0,0 +1,123 @@
+---
+title: Operations
+description: Upgrade a self-hosted Mogplex safely, manage its secrets, expose the MCP endpoint, and understand the known constraints.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+## Upgrades
+
+Release notes on the [releases page](https://github.com/Mogplex/mogplex/releases)
+list breaking changes to env vars and migrations. For each upgrade:
+
+1. Pull `main` or a tagged release into your fork.
+2. Add any new variables to the app **and** to Trigger.dev.
+3. Apply migrations:
+ `pnpm exec tsx scripts/apply-neon-migrations.ts`.
+4. Deploy the app.
+5. Deploy the Trigger.dev tasks from the same commit: `pnpm trigger:deploy`.
+
+Migrations stay compatible with the previous app release, so apply them
+**before** you deploy the new code. Production uses the same order.
+
+
+ `.github/workflows/deploy-production.yml` is a working example. It applies
+ migrations, deploys with the Vercel CLI, and then calls
+ `/api/cron/production-smoke` to detect schema drift. Point its secrets at your
+ own accounts to reuse it.
+
+
+## Secrets
+
+| Secret | Rotation |
+| --- | --- |
+| `BETTER_AUTH_SECRET` | Rotating it signs out every user. |
+| `CRON_SECRET`, `INTERNAL_API_SECRET` | Rotate freely. On Docker, update your cron jobs at the same time. |
+| `EMAIL_UNSUBSCRIBE_SECRET` | Rotating it invalidates unsubscribe links already sent. |
+| `CONNECTIONS_ENCRYPTION_KEY` | Encrypts saved connection tokens and API keys. **Re-encrypt before you rotate** (below). |
+| `GITHUB_WEBHOOK_SECRET`, `SLACK_SIGNING_SECRET` | Change the value in GitHub or Slack and in your env at the same time. |
+
+Store secrets in your platform's secret store. Never commit them.
+
+### Rotate `CONNECTIONS_ENCRYPTION_KEY`
+
+If you change the key without re-encrypting, every saved connection becomes
+unreadable. Use the rotation script. It runs a dry run by default and backs up
+the original ciphertexts.
+
+```bash
+OLD_CONNECTIONS_ENCRYPTION_KEY= \
+NEW_CONNECTIONS_ENCRYPTION_KEY=$(openssl rand -hex 32) \
+DATABASE_URL= \
+ pnpm exec tsx scripts/rotate-connections-encryption-key.ts # dry run
+# Review the counts, then run again with --execute
+```
+
+Take a database snapshot first, and rotate during low traffic. Switch the env
+var and redeploy right after the script finishes. Then run the script once more
+to re-encrypt rows that were written in between.
+
+## Hosted API and MCP endpoint
+
+Your deployment serves the same [hosted API](/reference/api) and
+[MCP endpoint](/mcp) as mogplex.com, at
+`https:///api/v1/mogplex/mcp`. Users authenticate with a Mogplex key
+from **Settings → Mogplex Keys**, or through OAuth.
+
+Better Auth is the OAuth authorization server. It supports dynamic client
+registration, PKCE, and refresh tokens. No extra setup is needed when the
+public URL is `NEXT_PUBLIC_APP_URL`. Verify it:
+
+```bash
+curl https:///.well-known/oauth-protected-resource/api/v1/mogplex/mcp
+curl https:///.well-known/oauth-authorization-server
+```
+
+The first response must name your domain as both the resource and the
+authorization server.
+
+- Set `MOGPLEX_MCP_RESOURCE_URL` only when the MCP endpoint is served from a
+ different URL than `NEXT_PUBLIC_APP_URL`.
+- Set `MOGPLEX_MCP_ALLOWED_ORIGINS` for browser-based MCP clients.
+
+When you [install MCP clients](/mcp/install), replace `mogplex.com` with your
+domain.
+
+## Mogplex CLI
+
+To point the [Mogplex CLI](/cli) at your deployment instead of mogplex.com, set
+`MOGPLEX_URL`:
+
+```bash
+export MOGPLEX_URL="https://mogplex.example.com"
+```
+
+See [CLI automation](/cli/automation#base-url).
+
+## Known constraints
+
+- **Sandboxes require Vercel Sandbox.** No other sandbox backend exists yet. A
+ pluggable backend would be a welcome contribution. Open a
+ [discussion](https://github.com/Mogplex/mogplex/discussions) before you start
+ so the interface is agreed first.
+- **The Docker image has no health check.** Probe `/` or `/login` from your
+ orchestrator.
+- **Cron routes are not self-scheduling.** Outside Vercel, you must
+ [schedule them](/self-hosting/background-jobs#cron-routes).
+- **Bring-your-own Vercel compute for end users is not available.** Sandboxes
+ always bill to the deployment's `PLATFORM_VERCEL_*` account.
+- **Team-shared connections are not available yet.** Connections belong to
+ personal accounts.
+
+## Troubleshooting
+
+| Symptom | Likely cause |
+| --- | --- |
+| Runs fail with "requires a positive billing balance" | The user is not in the [access allowlist](/self-hosting/sandboxes-and-models#give-users-access). |
+| "Sandbox service not configured" | `PLATFORM_VERCEL_TOKEN` or the project ID is missing. |
+| Automations and Slack runs never start | Trigger.dev is not configured, the tasks are not deployed, or the tasks are missing env vars. |
+| `pnpm trigger:deploy` targets the wrong project | `TRIGGER_PROJECT_REF` is unset. |
+| GitHub deliveries return `401` | `GITHUB_WEBHOOK_SECRET` does not match the App. |
+| A changed `NEXT_PUBLIC_*` value has no effect | Rebuild. These values are fixed at build time. |
+| Auth routes return errors | `DATABASE_URL` and `MOGPLEX_RUNTIME_DATABASE_URL` are both unset, or migrations have not run. |
+| Email sign-up never completes | `RESEND_API_KEY` is missing, or the sender domain is not verified. |
diff --git a/content/docs/self-hosting/quickstart.mdx b/content/docs/self-hosting/quickstart.mdx
new file mode 100644
index 0000000..18d7d06
--- /dev/null
+++ b/content/docs/self-hosting/quickstart.mdx
@@ -0,0 +1,150 @@
+---
+title: Quickstart
+description: Deploy a self-hosted Mogplex on Vercel or Docker, sign in, and add the services that unlock agent work.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
+
+This guide gets a signed-in deployment running first. Then it adds the services
+that let agents work on repositories.
+
+## Prerequisites
+
+- Node.js `22.22.x` (see `.nvmrc`) and pnpm `10` through Corepack
+ (`corepack enable`).
+- A Postgres 17 database with the `vector` and `pg_trgm` extensions available.
+ [Neon](https://neon.tech) has both. For local development, the
+ `pgvector/pgvector:pg17` Docker image works.
+- A public HTTPS domain for the deployment. OAuth callbacks and webhooks must
+ reach it.
+
+## 1. Get the code
+
+```bash
+git clone https://github.com/Mogplex/mogplex.git
+cd mogplex
+pnpm install
+cp .env.example .env.local
+```
+
+Fork the repository first if you plan to change code or deploy from Git.
+
+## 2. Configure the minimum environment
+
+Set these in `.env.local`. Generate each secret with `openssl rand -base64 32`,
+except `CONNECTIONS_ENCRYPTION_KEY`, which must be 32 bytes of hex
+(`openssl rand -hex 32`).
+
+```bash
+# Backend switches. Both must be "neon". The public one is baked in at build time.
+MOGPLEX_DATA_BACKEND=neon
+NEXT_PUBLIC_MOGPLEX_DATA_BACKEND=neon
+
+NEXT_PUBLIC_APP_URL=https://mogplex.example.com
+
+# Database. DATABASE_URL is the owner connection that migrations use.
+DATABASE_URL=postgres://owner:...@host/db?sslmode=require
+DATABASE_URL_UNPOOLED=postgres://owner:...@direct-host/db?sslmode=require
+
+# Auth
+BETTER_AUTH_SECRET=...
+AUTH_GITHUB_CLIENT_ID=... # or AUTH_GOOGLE_* / AUTH_MICROSOFT_*
+AUTH_GITHUB_CLIENT_SECRET=...
+
+# Secrets the app requires
+CRON_SECRET=...
+INTERNAL_API_SECRET=...
+CONNECTIONS_ENCRYPTION_KEY=... # 64 hex characters
+EMAIL_UNSUBSCRIBE_SECRET=... # required in production
+```
+
+[Database and auth](/self-hosting/database-and-auth) explains the database URLs,
+the least-privilege runtime role, and each sign-in provider.
+
+## 3. Apply migrations
+
+```bash
+pnpm exec tsx --env-file=.env.local scripts/apply-neon-migrations.ts
+```
+
+On an empty database, the script applies `neon/baseline.sql` first. Then it
+applies every pending file in `neon/migrations/`. Run it as the database owner,
+because it creates schemas, extensions, and roles. Add `--dry-run` to preview.
+
+## 4. Deploy
+
+
+
+
+`vercel.json` turns off automatic Git deployments, so deploy with the Vercel CLI
+or from your own CI:
+
+```bash
+vercel link # link to a project in your own Vercel team
+vercel env add ... # or paste .env.local into Project → Settings → Environment Variables
+vercel deploy --prod
+```
+
+To deploy on every push instead, remove `"git": { "deploymentEnabled": false }`
+from `vercel.json` in your fork. Vercel runs the cron routes listed in
+`vercel.json` for you.
+
+
+
+
+`NEXT_PUBLIC_*` values are compiled into the client bundle at **build** time.
+Pass them as build args, and rebuild whenever they change.
+
+```bash
+cp .env.local .env
+docker compose build # reads NEXT_PUBLIC_* build args from .env
+docker compose up -d # serves on port 3000
+```
+
+Without Compose:
+
+```bash
+docker build \
+ --build-arg NEXT_PUBLIC_APP_URL=https://mogplex.example.com \
+ --build-arg NEXT_PUBLIC_MOGPLEX_DATA_BACKEND=neon \
+ -t mogplex .
+
+docker run --env-file .env -p 3000:3000 mogplex
+```
+
+The image runs Next.js in standalone mode on Node 22 as a non-root user. It
+contains no database, job runner, or cron scheduler. Put a TLS-terminating proxy
+in front of it, and
+[schedule the cron routes](/self-hosting/background-jobs#cron-routes) yourself.
+
+
+
+
+Open the deployment and sign in. If sign-in works, the app shell is done.
+
+## 5. Add services for agent work
+
+Add these in order. Each one is independent.
+
+1. [GitHub App](/self-hosting/github-app): repository access, webhooks, PR
+ reviews, and GitHub triggers.
+2. [Background jobs](/self-hosting/background-jobs): a Trigger.dev project for
+ automations, Slack runs, and long agent runs.
+3. [Sandboxes and models](/self-hosting/sandboxes-and-models): Vercel Sandbox,
+ an AI Gateway key, and **user access**.
+4. [Slack app](/self-hosting/slack-app) (optional).
+
+
+ After step 3, add your users to `PLATFORM_ACCESS_EMAILS` or
+ `PLATFORM_ACCESS_EMAIL_DOMAINS`. Without Stripe billing, no user has hosted
+ AI or sandbox access until you grant it.
+
+
+## 6. Verify
+
+- Sign in, then open **Settings → Account** and select **Run checks**.
+- Connect GitHub, install your GitHub App, and import a repository.
+- Start a run against that repository. Confirm in
+ [Observability](/web/observability) that a sandbox starts and the job
+ completes.
diff --git a/content/docs/self-hosting/sandboxes-and-models.mdx b/content/docs/self-hosting/sandboxes-and-models.mdx
new file mode 100644
index 0000000..a7ddcc1
--- /dev/null
+++ b/content/docs/self-hosting/sandboxes-and-models.mdx
@@ -0,0 +1,95 @@
+---
+title: Sandboxes and Models
+description: Configure Vercel Sandbox compute, model access through AI Gateway or provider keys, and per-user access on a self-hosted Mogplex.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+Agents need two things to work on code: a sandbox to run in, and a model to
+call. On a self-hosted deployment, your accounts provide both. You also decide
+which users may use them.
+
+## Sandboxes (Vercel Sandbox)
+
+Each agent session runs in a [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox)
+microVM. Mogplex has no local or container substitute. Without these variables,
+sandbox features return "Sandbox service not configured".
+
+| Variable | Value |
+| --- | --- |
+| `PLATFORM_VERCEL_TOKEN` | A Vercel access token that can create sandboxes in your team |
+| `PLATFORM_VERCEL_TEAM_ID` | The team that owns and pays for the sandboxes |
+| `VERCEL_PROJECT_ID` or `PLATFORM_VERCEL_PROJECT_ID` | The Vercel project the sandboxes belong to. This can be the project that hosts Mogplex. |
+
+Sandbox usage bills to that Vercel team. Optionally, set
+`PLATFORM_SANDBOX_COST_CENTS_PER_SECOND` (default `0.0028`) so that Mogplex's
+cost reporting matches your Vercel pricing.
+
+## Models
+
+Mogplex resolves model access in this order:
+
+1. **The user's own AI Gateway key** (Settings → Provider Keys). The user's
+ gateway account pays for the usage.
+2. **The platform AI Gateway key**, `AI_GATEWAY_API_KEY`, for users who have
+ [platform access](#give-users-access). Your gateway account pays for the
+ usage.
+3. **The user's direct provider keys**: Anthropic for Claude Code and Claude
+ models, OpenAI for Codex and OpenAI models, and OpenRouter for OpenRouter
+ models.
+
+| Variable | Purpose |
+| --- | --- |
+| `AI_GATEWAY_API_KEY` | Platform model access through [Vercel AI Gateway](https://vercel.com/docs/ai-gateway). |
+| `OPENROUTER_API_KEY` | Refreshes the model catalog. |
+| `OPENAI_API_KEY` | Memory embeddings. Without it, memory search falls back to plain text matching. |
+| `EXA_API_KEY`, `EXA_RESEARCH_SECRET` | Agent web search and page extraction (optional). |
+
+The model catalog syncs twice a day. To populate it on a new deployment, call
+`/api/cron/sync-models` once (see [Cron routes](/self-hosting/background-jobs#cron-routes)).
+
+## Give users access
+
+Platform AI (`AI_GATEWAY_API_KEY`) and platform sandboxes are gated per user. A
+user has access when **any** of these is true:
+
+- The user's billing account has a positive balance (requires Stripe).
+- The user's ID, email, or email domain is in an allowlist variable.
+- The user's profile row has `allow_platform_ai` or `allow_platform_sandbox` set
+ to `true`.
+
+Without Stripe, no account has a balance. Use the allowlists:
+
+```bash
+PLATFORM_ACCESS_EMAIL_DOMAINS=example.com # everyone at example.com
+PLATFORM_ACCESS_EMAILS=alice@example.org,bob@example.org
+PLATFORM_ACCESS_USER_IDS= # Mogplex user IDs
+```
+
+The allowlists grant both AI and sandbox access. To grant one without the
+other, set the profile flag directly:
+
+```sql
+update public.profiles
+set allow_platform_sandbox = true, allow_platform_ai = false
+where email = 'alice@example.org';
+```
+
+
+ Users without access still see the app. Their runs fail with "Hosted AI
+ requires a positive billing balance" or "Hosted sandbox compute requires a
+ positive billing balance". These messages usually mean the user is missing
+ from the allowlist, not that billing is broken.
+
+
+A user without platform AI access can still add their own AI Gateway,
+Anthropic, OpenAI, or OpenRouter key under **Settings → Provider Keys**. See
+[AI provider keys](/integrations/ai-provider-keys).
+
+## Agent harnesses
+
+Runs use the native Mogplex agent, Claude Code, or Codex. Claude Code and
+Codex install inside the sandbox at run time and authenticate with the model
+access above. Codex runs over the Agent Client Protocol by default. Set
+`MOGPLEX_HARNESS_ACP=off` to fall back to the one-shot Codex CLI. See
+[Claude Code and Codex](/integrations/claude-code-and-codex).
diff --git a/content/docs/self-hosting/slack-app.mdx b/content/docs/self-hosting/slack-app.mdx
new file mode 100644
index 0000000..8e9c4ba
--- /dev/null
+++ b/content/docs/self-hosting/slack-app.mdx
@@ -0,0 +1,122 @@
+---
+title: Slack App
+description: Create the Slack app for a self-hosted Mogplex, with a ready-to-paste manifest, the scopes, events, slash commands, and environment variables.
+---
+
+import { Callout } from 'fumadocs-ui/components/callout';
+
+Slack is optional. A self-hosted deployment needs its own Slack app, because the
+hosted app sends its events to mogplex.com. One app can be installed in many
+workspaces. Users install it from **Connections → Slack** in your deployment.
+
+## 1. Create the app from a manifest
+
+Open [api.slack.com/apps](https://api.slack.com/apps), select **Create New App →
+From a manifest**, and paste the manifest below. Replace `mogplex.example.com`
+with your domain.
+
+```yaml
+display_information:
+ name: Mogplex
+ description: Run Mogplex agents from Slack
+features:
+ app_home:
+ messages_tab_enabled: true
+ messages_tab_read_only_enabled: false
+ bot_user:
+ display_name: mogplex
+ always_online: true
+ slash_commands:
+ - command: /mogplex
+ url: https://mogplex.example.com/api/webhooks/slack
+ description: Mogplex commands
+ usage_hint: help | status | cancel | repo | prs | issues | usage | model | harness | agent
+ should_escape: false
+ - command: /mogplex-cancel
+ url: https://mogplex.example.com/api/webhooks/slack
+ description: Cancel your active Mogplex run in this channel
+ usage_hint: "[run-id]"
+ should_escape: false
+ - command: /harness
+ url: https://mogplex.example.com/api/webhooks/slack
+ description: View or choose your repository runner
+ usage_hint: "[mogplex|codex|claude-code]"
+ should_escape: false
+oauth_config:
+ redirect_urls:
+ - https://mogplex.example.com/api/integrations/slack/callback
+ scopes:
+ bot:
+ - app_mentions:read
+ - channels:history
+ - channels:read
+ - chat:write
+ - commands
+ - files:read
+ - groups:history
+ - groups:read
+ - im:history
+ - im:read
+ - mpim:history
+ - mpim:read
+ - users:read
+ - users:read.email
+settings:
+ event_subscriptions:
+ request_url: https://mogplex.example.com/api/webhooks/slack
+ bot_events:
+ - app_mention
+ - message.channels
+ - message.groups
+ - message.im
+ - message.mpim
+ interactivity:
+ is_enabled: true
+ request_url: https://mogplex.example.com/api/webhooks/slack
+ org_deploy_enabled: false
+ socket_mode_enabled: false
+ token_rotation_enabled: false
+```
+
+One endpoint, `/api/webhooks/slack`, receives events, slash commands, and
+interactivity. Deploy the app with the Slack variables set **before** you save
+the manifest, so that Slack's URL verification succeeds.
+
+To distribute the app to workspaces other than the one that created it, turn on
+**Manage Distribution → Public Distribution**.
+
+## 2. Set the environment variables
+
+From **Basic Information → App Credentials**:
+
+```bash
+SLACK_CLIENT_ID=...
+SLACK_CLIENT_SECRET=...
+SLACK_SIGNING_SECRET=...
+# Optional. Defaults to ${NEXT_PUBLIC_APP_URL}/api/integrations/slack/callback
+SLACK_REDIRECT_URI=
+```
+
+Set the same variables in your [Trigger.dev environment](/self-hosting/background-jobs#3-give-the-tasks-their-environment).
+Slack runs execute as Trigger.dev tasks.
+
+## 3. Install and verify
+
+1. In your deployment, open **Connections**, find **Slack**, and select
+ **Install to Slack**.
+2. Link a channel to a repository, and turn on repo-agent runs.
+3. In Slack, run `/mogplex help`. It responds without a linked account.
+4. Mention `@mogplex` with a real instruction in the linked channel. Confirm
+ that a run appears in [Observability](/web/observability).
+
+For channel links, user mapping, and run controls, see
+[Slack](/integrations/slack).
+
+## Scope changes
+
+
+ Changing code cannot expand an existing Slack OAuth grant. When a release adds
+ a scope, add it to the app, then reconnect each workspace from
+ **Connections → Slack**. For example, `files:read` lets agents read images
+ attached to thread messages, and older installs must reconnect to get it.
+
diff --git a/src/lib/layout.shared.tsx b/src/lib/layout.shared.tsx
index e4aaaba..a25cfae 100644
--- a/src/lib/layout.shared.tsx
+++ b/src/lib/layout.shared.tsx
@@ -7,7 +7,9 @@ export const docsNavItems = [
{ label: 'Guides', href: '/guides' },
{ label: 'CLI', href: '/cli' },
{ label: 'Web app', href: '/web' },
+ { label: 'Integrations', href: '/integrations' },
{ label: 'Reference', href: '/reference' },
+ { label: 'Self-hosting', href: '/self-hosting' },
] as const;
export const docsSearchLinks: [string, string][] = [