源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)

This commit is contained in:
agent-radar
2026-09-02 01:05:41 +08:00
commit 04bac1f6e2
2200 changed files with 489415 additions and 0 deletions
@@ -0,0 +1,58 @@
---
title: "Air-gapped deployment"
description: "Plan OpenWork deployments that limit or remove public internet access."
---
"Air-gapped" can mean several different things in OpenWork planning. Use these terms precisely so the network design, installer path, and feature expectations stay aligned.
## Deployment terms
| Term | Precise meaning |
| --- | --- |
| **Private-network / semi-air-gapped** | Employee desktops can reach the public internet and can also reach internal Den over VPN or private network. Den access is restricted to approved networks, but the desktop still has ordinary internet paths unless your endpoint policy blocks them. |
| **Isolated Den** | Den has no unrestricted public internet access. Client laptops may still have internet access for installers, model catalogs, npm packages, providers, and links. |
| **Fully air-gapped installer delivery** | Only the installer bytes are mounted or served from inside your network. This does **not** make the entire OpenWork product fully air-gapped. It only removes the GitHub release-asset download path for the installer. |
| **Fully isolated OpenWork deployment** | Every dependency used by the enabled OpenWork features is mirrored, provisioned, disabled, or replaced with an internally reachable service. This includes deployment artifacts, desktop runtime dependencies, model/provider catalogs, OAuth/provider endpoints, MCP servers, email, diagnostics, updates, and trust anchors. |
If desktops have internet access and Den is private, start with [Private network deployment](/start-here/private-network-deployment). If you need to inventory domains, use [Outbound network access](/start-here/outbound-network-access). If TLS inspection or private roots are involved, use [Certificate trust and proxies](/start-here/certificate-trust-and-proxies).
## Full isolation checklist
Use this checklist only when the goal is a fully isolated OpenWork deployment, not just private Den or internal installer delivery.
### Deployment artifacts
- Mirror the GHCR Helm chart into an internal OCI registry.
- Mirror all enabled images into an internal registry, including `openwork-den-api`, `openwork-den-web`, and `openwork-inference` if inference is enabled.
- Configure Helm image repositories and chart installation to use the internal registry. The public `ghcr.io` path is only needed when you are not mirroring the chart and images.
### Installer artifacts
- Mount the standard release installer files into Den and set `OPENWORK_INSTALLER_ARTIFACTS_DIR` to that mount path.
- Use `OPENWORK_INSTALLER_RELEASE_TAG` to select the expected standard filenames. The version inside each filename is the tag without a leading `v`.
- Use `OPENWORK_INSTALLER_RELEASE_REPO` only for the GitHub redirect path when mounted artifacts are not present; it is not an internal artifact registry override.
- Mount the same read-only installer artifact PVC across every Den API replica. Den streams the mounted file; it does not fetch, cache, rewrap, or ZIP installer bytes.
See [Installer delivery](/start-here/installer-delivery) for the exact filenames and delivery matrix.
### Desktop runtime dependencies
- Point `OPENCODE_MODELS_URL` at an internal mirror of the model catalog. The default catalog is `https://models.openworklabs.com/`.
- The packaged desktop can start the UI-control MCP with `npx -y openwork-ui-mcp`. In a fully isolated environment, provide an internal npm registry or npm mirror that can serve that package to `npx`. OpenWork does not currently document a separate OpenWork-specific registry override for this package; use standard npm configuration for the runtime account.
- Disable, mirror, or manage desktop updates and GitHub release assets through your internal release process. Do not assume a built-in updater mirror setting unless it has been verified for the desktop release you ship.
### Feature dependencies
- Optional providers, MCP servers, OAuth identity providers, and email services are unavailable unless they are internally reachable and approved. Examples include Google Workspace, Microsoft 365, GitHub plugin import, OpenRouter/OpenAI, Slack/Notion/Linear/Sentry/Stripe MCP presets, SMTP, and Resend.
- If you enable the GitHub connector, approve the exact GitHub API/webhook paths or mirror the workflow it needs. Start with the [GitHub connector Helm guide](/start-here/github-connector-helm).
- If you enable internal MCP URLs, review the private-network SSRF policy in [Private network deployment](/start-here/private-network-deployment). Enabling private MCP URLs is a trust decision, not an air-gap requirement.
### Security and diagnostics
- Install private roots and proxy configuration for the correct trust surface: desktop Electron/Chromium, spawned local runtimes, Den containers, and MySQL TLS do not all use the same store. See [Certificate trust and proxies](/start-here/certificate-trust-and-proxies).
- Use [Network diagnostics](/start-here/network-diagnostics) to separate desktop DNS/TLS/proxy issues from Den outbound checks.
- Helm already disables the external breached-password lookup by default for isolated self-hosted installs. Enable it only if your security team approves the external Have I Been Pwned range API path.
## What full isolation does not include automatically
Mounting installer artifacts is useful, but it only solves installer delivery. It does not mirror npm, model catalogs, provider APIs, GitHub plugin contents, OAuth metadata, email delivery, diagnostics, desktop updates, or certificate trust. Treat each enabled feature as a separate dependency review.
@@ -0,0 +1,77 @@
---
title: "Certificate trust and proxies"
description: "Configure private CAs and outbound proxies for desktop, local runtimes, Den, and strict MySQL TLS."
---
OpenWork has several network trust surfaces. Installing a private root for one surface does not automatically configure all of the others.
## The four trust surfaces
### 1. Desktop app HTTPS
The desktop app's external HTTPS calls should go through Electron's Chromium network stack. That path uses Chromium/Electron certificate trust and the system proxy configuration. The built-in browser panel is separate: it has its own explicit proxy surface for browsing traffic.
Enterprise roots should be installed in OS trust on Windows and macOS. On Linux, also import the root into the Chromium/Electron-compatible NSS or user trust database required by your distribution. Do not assume `/etc/ssl` alone is sufficient for Electron on every Linux image.
Real-machine validation found exactly this distinction: a Daytona Linux Electron E2E installed a private CA into Debian `/etc/ssl`. `curl` and Node with `--use-system-ca` trusted it; bare Node `fetch` failed with `UNABLE_TO_VERIFY_LEAF_SIGNATURE`; Electron `net.fetch` returned `ERR_CERT_AUTHORITY_INVALID` because Chromium used its NSS/user trust path on that image.
Bare Node `fetch` is not the sanctioned desktop external path. The desktop external-call guard routes new external desktop calls through `electronNet.fetch`, but that does not remove the Linux Chromium/NSS trust-store distinction.
### 2. Spawned local runtimes and sidecars
The desktop also starts the embedded OpenWork server plus local runtimes and sidecars. On each launch, OpenWork builds an additive `system-ca-bundle.pem` inside the user data directory from every OS trust source it can read: Node's system store, Windows LocalMachine and CurrentUser `Root`/`CA` certificate stores, and the macOS administrator-controlled System and SystemRoot keychains. The desktop merges and deduplicates those certificates before passing the bundle to child processes with `NODE_EXTRA_CA_CERTS`.
- If the user or launcher already set `NODE_EXTRA_CA_CERTS`, that user-provided value wins and OpenWork does not overwrite it.
- The Electron main process extends Node's default CA list with the same generated additions when the runtime supports it, so the embedded server's own outbound TLS diagnostics and probes use the same enterprise roots.
- The generated bundle is for spawned Node-compatible runtimes. It does not configure Electron's Chromium trust store.
- Do not assume every sidecar honors `HTTP_PROXY` or `HTTPS_PROXY` universally. Use proxy settings documented for the specific runtime or sidecar.
#### Automatic chain repair for the activated organization server
At startup, the desktop checks the activated organization server for a leaf-only TLS chain. If the leaf certificate points to a missing intermediate through AIA, OpenWork fetches that intermediate, verifies the leaf signature, and confirms the intermediate chains to bundled public roots before adding it to the generated `system-ca-bundle.pem`. It refuses private or corporate roots and any non-leaf-only TLS failure. The repair phase is bounded at 20 seconds by default and can be tuned with `OPENWORK_CHAIN_REPAIR_TIMEOUT_MS`. Set `OPENWORK_DISABLE_CHAIN_REPAIR=1` to turn this resilience behavior off. The server should still be fixed to serve its full chain; diagnostics continue to report what the server actually sends.
### 3. Den containers and Helm
For Kubernetes Den installs, use the Helm chart's `customCa` support when Den must trust a private CA for outbound TLS or strict database verification. The chart mounts the selected Secret or ConfigMap key at:
```text
/etc/openwork/custom-ca/ca-bundle.pem
```
It sets `NODE_EXTRA_CA_CERTS` to that file for `den-api`, `den-web`, enabled `inference`, and the migration Job. CA rotation requires restarting the running workloads so Node reloads the file.
The low-level values and conflict rules are in the Helm README's [Custom CA certificates](https://github.com/different-ai/openwork/blob/dev/packaging/helm/openwork-ee/README.md#custom-ca-certificates) section.
### 4. Strict MySQL TLS
For MySQL-compatible databases, encryption and certificate identity verification are separate decisions.
- `sslaccept=accept` keeps TLS enabled but does not verify the certificate chain. Use it only for smoke tests or transitional setup.
- `sslmode=require` encrypts the connection, but do not describe it as certificate identity verification.
- `sslmode=verify-ca`, `sslmode=verify-full`, and `sslaccept=strict` are certificate-verifying modes. Pair them with the correct CA bundle, usually through Helm `customCa`.
## Proxy guidance
### Desktop
- External desktop network calls use Chromium/system proxy behavior through `electronNet.fetch`.
- The built-in browser has its own explicit proxy controls for browser traffic.
- If a corporate proxy re-signs TLS, install the enterprise root for the matching trust surface. On Linux Electron, include the Chromium/NSS store where required.
### Den Node runtime
For Den running on Node.js 24.5 or newer, configure proxy settings before the process starts:
```dotenv
NODE_USE_ENV_PROXY=1
HTTPS_PROXY=http://proxy.example.internal:8080
NO_PROXY=localhost,127.0.0.1,.svc,.cluster.local,openwork-ee-den-api
```
Use `NODE_EXTRA_CA_CERTS` or Helm `customCa` when TLS inspection uses a private trust root. These are process-start settings; setting them in the browser or in-app environment editor is too late for Den.
## How to validate
- For Windows desktop reachability and TLS-chain evidence, use the PowerShell doctor in [Network diagnostics](/start-here/network-diagnostics).
- For Den's runtime path, use the Den outbound diagnostic in [Network diagnostics](/start-here/network-diagnostics).
- For destination inventories, use [Outbound network access](/start-here/outbound-network-access).
@@ -0,0 +1,59 @@
---
title: "Add a custom LLM"
description: "How to connect a custom provider with OpenWork"
---
<Note>
**Local provider setup.** A custom provider can stay entirely in your workspace. OpenWork remains free, and you do not need to sign in to OpenWork Cloud. For centrally managed team access, see [Adding a managed LLM provider](/cloud/share-with-your-team/managed-llm-provider).
</Note>
Because `OpenWork` is built on `OpenCode` primitives, we support everything that you could modify in `.opencode.json`, like adding a [model](https://opencode.ai/docs/models/). However, our recommendation is to add it to `/path-to-your-workspace/.config/opencode/opencode.json` instead of modifying `~/.config/opencode/opencode.json`.
Inside `/path-to-your-workspace/.config/opencode/opencode.json`:
```
{
"provider": {
"my-api": {
"npm": "@ai-sdk/openai-compatible",
"name": "My model",
"options": {
"baseURL": "https://your API address/v1"
},
"models": {
"model-name": {
"name": "Model name"
}
}
}
}
}
```
### Functional example: Ollama (Qwen3 8B)
This setup a local [Ollama](https://docs.ollama.com/api/openai-compatibility) instance running `qwen3:8b`.
```
{
"provider": {
"ollama": {
"npm": "@ai-sdk/openai-compatible",
"name": "Ollama",
"options": {
"baseURL": "http://localhost:11434/v1"
},
"models": {
"qwen3:8b": {
"name": "Qwen3 8B"
}
}
}
}
}
```
Pull the model first with `ollama pull qwen3:8b`, then make sure the Ollama server is reachable at `http://localhost:11434` with `ollama serve`
Open your desktop, change the provider name and voila! You have an fully local LLM running in your machine.
For teams, manage the provider in OpenWork Cloud under [Models > Bring your Own Keys](/cloud/share-with-your-team/managed-llm-provider), then make it available from `Settings > Cloud Providers` in the desktop app.
@@ -0,0 +1,50 @@
---
title: "Add a custom MCP server"
description: "Connect a custom or local MCP server that is not available through OpenWork Connect"
---
<Note>
**Local workspace setup.** Custom MCP servers are supported without an OpenWork Cloud account, so this path stays free and local to the current workspace. For a connection managed and assigned by your organization, use [OpenWork Cloud Connectors](/cloud/share-with-your-team/shared-mcp-connections).
</Note>
Use this guide only when you need a custom or local MCP server that is not available through OpenWork Connect.
For Gmail, Google Calendar, Google Drive, Slack, Notion, Linear, and other
managed services, follow [Connect your services](/start-here/connect-your-stack/connect-services).
Connecting a custom app with OpenWork takes four clicks.
- In the desktop app, open `Settings` \> `Library`, expand `Advanced settings`, then click `Add workspace MCP`
<Frame>
![Library page with Advanced settings expanded, showing the Add workspace MCP button](/images/library-advanced-settings.png)
</Frame>
The MCP is added to the current workspace.
<Frame>
![Add workspace MCP dialog with app name, server URL, and OAuth options](/images/library-add-mcp-modal.png)
Indicate whether the server requires OAuth.
</Frame>
This setup is intended for servers that support dynamic OAuth client registration.
<Frame>
![OAuth approval page for MCP server](/images/mcp-oauth-approval-page.png)
</Frame>
Complete the provider's OAuth approval flow in your browser. It should redirect
back to OpenWork. If a custom MCP needs behavior OpenWork does not support,
[open an issue](https://github.com/different-ai/openwork/issues/).
### Troubleshooting
Some MCP servers do not support dynamic client registration. If the server gives you a pre-registered OAuth client ID and client secret, add it through `Add workspace MCP` > `OAuth on this device`.
For an advanced local Slack setup, follow
[Connect Slack as a custom MCP](/start-here/connect-your-stack/connect-slack-mcp).
<Frame>
![Error when MCP server does not support dynamic client registration](/images/mcp-dynamic-registration-error.png)
</Frame>
@@ -0,0 +1,57 @@
---
title: "Add Anthropic API key"
description: "Create an Anthropic API key and use it in OpenWork"
---
<Note>
**Local provider setup.** The key you add here is for your desktop app. OpenWork remains free, and you do not need to sign in to OpenWork Cloud. To manage Anthropic access for a team, use [a managed LLM provider](/cloud/share-with-your-team/managed-llm-provider).
</Note>
Anthropic has disabled 3rd party authentication for their OAuth. In order to continue supporting Anthropic based models in OpenWork, we now default to manual API key entry for Anthropic.
That means you create the key in Anthropic first, then paste it into OpenWork.
## Before you start
- You need an Anthropic account at [platform.claude.com](https://platform.claude.com).
- Your Claude subscription and your Anthropic API billing are not always the same thing, so make sure API access is enabled on your Anthropic account.
- Treat the key like a password. Anthropic will only show the full value when you create it.
If this key should be managed for a team, add Anthropic once under `Models > Bring your Own Keys` in OpenWork Cloud. Members receive it through `Settings > Cloud Providers` instead of pasting it into each workspace separately.
## Create the key in Anthropic
1. Sign in at [platform.claude.com](https://platform.claude.com).
2. Open the `API Keys` page.
<Frame>
![Anthropic API Keys page](/images/anthropic-key.png)
</Frame>
3. Click `Create Key`.
4. Give the key a recognizable name like `OpenWork`.
<Frame>
![Anthropic Create Key dialog](/images/anthropic-openwork-key.png)
</Frame>
5. Copy the new key right away. It should look like `sk-ant-...`.
## Add the key to OpenWork
1. Open `Settings` in OpenWork.
2. Go to `Connect Provider` and choose `Anthropic`.
3. Paste the API key into the manual key field.
4. Save the provider, then choose an Anthropic model in chat.
<Frame>
![How to add a new provider in OpenWork](/images/openwork-providers.png)
</Frame>
## Common issues
- If the key is rejected, make sure you copied the full value without extra spaces.
- If you cannot see `API Keys`, check that your Anthropic account has API access and billing set up.
- If you lose the key, create a new one in Anthropic and update OpenWork with the new value.
The same general process applies to most providers that use manual API keys: create the key in the provider's developer console, copy it once, and paste it into OpenWork.
@@ -0,0 +1,71 @@
---
title: "Connect your services"
description: "Sign in to OpenWork and connect the accounts your agent can use"
---
<Info>
**OpenWork Cloud organization setup.** This path requires an OpenWork Cloud sign-in and an organization that has made services available to you. To stay local, keep using OpenWork for free and [add a custom MCP server](/start-here/connect-your-stack/add-an-mcp-server) to your workspace. Admins can manage team connections with [Connectors](/cloud/share-with-your-team/shared-mcp-connections).
</Info>
Use `Settings` > `OpenWork Connect` for services your organization makes available,
including Gmail, Google Calendar, Google Drive, Slack, Notion, and Linear. This
is the recommended setup for everyday integrations.
## Before you start
Connect requires signing in to an OpenWork account and joining an organization
that has Connect enabled. This is separate from signing in to an LLM provider
such as ChatGPT.
1. Open the OpenWork desktop app.
2. Click `Sign in`, or open `Settings` > `OpenWork Connect` and choose `Sign in`.
3. Complete sign-in in your browser and return to OpenWork.
4. Choose the OpenWork organization you want to use if prompted.
If Connect is not available for your organization, ask an organization admin
to enable it.
## Connect an account
1. Open `Settings` > `OpenWork Connect`.
2. Find the service under `Needs your sign-in`.
3. Click `Connect` and approve access on the provider's sign-in page.
4. Return to OpenWork. The service moves to `Ready to use` automatically.
Some connections are managed by your organization. If a service is not
listed, ask an organization admin to publish it from the OpenWork Cloud
`Connectors` page and grant you access.
## Ask your agent to use it
You do not need to know an MCP server name or tool name. Ask for the outcome
you want, for example:
- “Summarize my five newest emails.”
- “Find the Q3 launch page in Notion.”
- “What meetings do I have tomorrow?”
The agent searches the live capabilities available to your signed-in account,
then executes an exact match. If an account still needs attention, it should
send you back to `Settings` > `OpenWork Connect` with the action to take instead of
telling you to add an MCP server.
## Connect, connectors, and connections
- **Connect** is the member-facing page in the desktop app for signing in to
services and checking connection readiness.
- A **connector** is an MCP service your organization adds on its own, without
wrapping it in a plugin. Organization admins publish connectors from the
OpenWork Cloud `Connectors` page and grant member or team access.
- A **connection** is your own signed-in instance of a connector — one
connector, instantiated per user with that user's account.
- The **MCP Gateway** is the hosted endpoint that lets external MCP clients
use capabilities from an OpenWork organization. See
[OpenWork MCP Gateway](/cloud/run-in-the-cloud/cloud-mcp).
- **Add an MCP server** is the advanced path for a custom or local server that
your organization does not provide through Connect.
If you are building a custom integration, continue with
[Add an MCP server](/start-here/connect-your-stack/add-an-mcp-server). If you
are an organization admin, see
[Sharing MCP connections with your team](/cloud/share-with-your-team/shared-mcp-connections).
@@ -0,0 +1,126 @@
---
title: "Connect Slack as a custom MCP"
description: "Advanced local setup for Slack's MCP server with a pre-registered OAuth app"
---
This is the advanced local setup. For the recommended member experience,
sign in to OpenWork and start with
[Connect your services](/start-here/connect-your-stack/connect-services).
Slack's official MCP server does not support automatic OAuth client registration. To connect it as a custom local MCP in OpenWork, ask a Slack admin to create or approve a Slack app for MCP, then add the app's OAuth client credentials in OpenWork.
You do not need the old Slack bot tokens (`xoxb-...` or `xapp-...`) for this flow.
## Before you start
You need:
- A Slack workspace where you can install or request approval for Slack apps.
- A Slack app that is published in the Slack Marketplace or marked as an internal app.
- A Slack OAuth client ID and client secret from that app.
- The Slack MCP server URL: `https://mcp.slack.com/mcp`.
Keep the client secret out of chats and source control. Treat it like any other workspace secret.
## Create or approve the Slack app
In Slack, an admin should create or approve an app for OpenWork's MCP access.
1. Open [Slack API apps](https://api.slack.com/apps) and create a new app, or open the existing internal app you want to use.
2. Go to `OAuth & Permissions`.
3. Add this redirect URL: `http://127.0.0.1:19876/mcp/oauth/callback`.
4. Add the `User Token Scopes` for the Slack MCP tools your team wants OpenWork to use.
5. Save the app and install or approve it for the workspace.
6. Go to `Basic Information` > `App Credentials` and copy the `Client ID` and `Client Secret`.
Slack MCP uses user token scopes. Choose the smallest set that matches what your team wants agents to do.
For a useful read-first setup, start with:
```text
search:read.public search:read.private search:read.mpim search:read.im search:read.files search:read.users channels:history groups:history mpim:history im:history users:read users:read.email channels:read groups:read mpim:read
```
Add write scopes only if you want agents to take action in Slack:
```text
chat:write reactions:write channels:write groups:write im:write mpim:write canvases:read canvases:write
```
Use this table to pick scopes by capability:
| Slack MCP capability | User token scopes |
| --- | --- |
| Search public and private messages/channels | `search:read.public`, `search:read.private`, `search:read.mpim`, `search:read.im` |
| Search files | `search:read.files` |
| Read files | `files:read` |
| Search emoji | `emoji:read` |
| Search users | `search:read.users` |
| Send messages | `chat:write` |
| Read channels and threads | `channels:history`, `groups:history`, `mpim:history`, `im:history` |
| Create conversations/channels | `channels:write`, `groups:write`, `im:write`, `mpim:write` |
| Add reactions | `reactions:write` |
| Read and write canvases | `canvases:read`, `canvases:write` |
| Read user profiles and email | `users:read`, `users:read.email` |
| List channel members | `channels:read`, `groups:read`, `mpim:read` |
## Open the Library
In OpenWork, open your workspace and go to `Settings` > `Library`, expand `Advanced settings`, and click `Add workspace MCP`.
<Frame>
![OpenWork Library page listing skills, apps, and connections](/images/library-slack-connection.png)
</Frame>
## Add the Slack MCP server
In the `Add workspace MCP` dialog:
1. Set `App name` to `slack`.
2. Keep `Type` set to `Remote server (URL)`.
3. Set `Server URL` to `https://mcp.slack.com/mcp`.
4. Open `OAuth on this device`.
5. Paste the Slack OAuth client ID.
6. Paste the Slack OAuth client secret.
7. Paste the same user token scopes you added in Slack, separated by spaces.
8. Click `Add App`.
<Frame>
![Add workspace MCP dialog filled in for Slack with OAuth on this device expanded](/images/slack-mcp-advanced-oauth.png)
</Frame>
OpenWork writes the MCP entry with the OAuth client credentials, similar to this:
```json
{
"mcp": {
"slack": {
"type": "remote",
"enabled": true,
"url": "https://mcp.slack.com/mcp",
"oauth": {
"clientId": "your-slack-client-id",
"clientSecret": "your-slack-client-secret"
}
}
}
}
```
If you entered scopes, OpenWork also saves them as `oauth.scope`. This is the scope list Slack sees during OAuth consent.
## Sign in to Slack
After the MCP app is added, reload the engine if OpenWork asks you to. Then start Slack MCP sign-in from the Slack app card or the MCP auth prompt.
Slack opens in your browser and shows the scopes you configured. Approve the Slack app for the workspace you want OpenWork to use, then return to OpenWork when authorization finishes.
## Troubleshooting
If you see an error like `Incompatible auth server: does not support dynamic client registration`, the Slack MCP entry is missing a pre-registered OAuth client ID and client secret. Reopen the Slack MCP setup, add those values under `Advanced OAuth`, reload the engine, and try sign-in again.
If Slack says the redirect URL is invalid, add `http://127.0.0.1:19876/mcp/oauth/callback` to the Slack app's `OAuth & Permissions` redirect URLs.
If Slack says a scope is invalid or unavailable, remove that scope from both the Slack app and OpenWork's `OAuth scopes` field, then retry. Slack workspace policies and plan settings can limit which scopes an app may request.
If your Slack workspace blocks app installation, ask a Slack admin to approve the app first. OpenWork cannot bypass Slack workspace approval policies.
@@ -0,0 +1,10 @@
---
title: "Enable Exa search"
---
[Exa](https://exa.ai) is a very fast search engine for AI. We support it as part of a global setting that is currently in `Settings` -> `Advanced`.
<Frame>
![Enable Exa web search toggle in Settings > Advanced](/images/exa-search-toggle.png)
</Frame>
@@ -0,0 +1,43 @@
---
title: "OpenWork vs OpenCode models"
description: "Understand the difference between OpenWork inference and OpenCode Go or Zen models"
---
OpenWork and OpenCode are different model providers. A subscription or API key for one does not automatically give access to the other.
| Provider | Where to manage it | What it controls |
| --- | --- | --- |
| OpenWork inference | [OpenWork inference dashboard](https://app.openworklabs.com/dashboard/inference) | OpenWork-managed model access and organization usage limits |
| OpenCode Go / Zen | [OpenCode Go](https://opencode.ai/go) and [OpenCode auth](https://opencode.ai/auth) | OpenCode-managed models, subscription, and API access |
## How to tell whether you are subscribed to OpenWork inference
Open the [OpenWork inference dashboard](https://app.openworklabs.com/dashboard/inference).
- If the page shows `Subscribe`, you are not subscribed to OpenWork inference.
- If the page shows usage limits and usage remaining, you are subscribed to OpenWork inference.
<Frame>
![OpenWork inference usage limits](/images/openwork-inference-usage-limits.png)
</Frame>
## How OpenWork usage buckets work
OpenWork inference usage is tracked with separate usage buckets. You may see buckets such as:
- `5 hour usage limit` — a short-term bucket that resets during the day.
- `Weekly usage limit` — a weekly bucket that resets on the listed date.
- `Monthly usage limit` — a monthly bucket that resets on the listed date.
The dashboard shows how much remains in each bucket and when each bucket resets. Usage is shared across your whole OpenWork organization, not just your personal desktop app.
Organization limits scale with the number of active members in the org. For example, adding active members increases the shared organization allowance, and usage by any member draws from the same shared buckets.
## If you meant to use OpenCode models
Connect the `OpenCode Go` provider in OpenWork Desktop instead of using OpenWork inference. Follow [Using OpenCode Zen & Go Models](/start-here/connect-your-stack/using-opencode-zen-go-models) to add the provider.
Use these OpenCode links to manage that account:
- [opencode.ai/go](https://opencode.ai/go)
- [opencode.ai/auth](https://opencode.ai/auth)
@@ -0,0 +1,34 @@
---
title: "Sign in with ChatGPT"
description: "Use ChatGPT OAuth as a local model provider in OpenWork"
---
<Note>
**Local provider setup.** This connects ChatGPT directly to the desktop app. OpenWork remains free, and you do not need to sign in to OpenWork Cloud. For model access managed by your organization, see [Adding a managed LLM provider](/cloud/share-with-your-team/managed-llm-provider).
</Note>
Connecting ChatGPT with OpenWork takes four clicks.
- Go to `Settings` \> `Connect Provider` \> `OpenAI` \> `ChatGPT Pro/Plus`
<Frame>
![Settings page showing Connect Provider button](/images/chatgpt-connect-provider-settings.png)
</Frame>
Then sign in to OpenAI with the email for your valid subscription.
<Frame>
![OpenAI authentication login page](/images/chatgpt-openai-auth-login.png)
</Frame>
Once you have finished the auth, you can change the model below in the chat.
<Frame>
![Model selector dropdown in chat](/images/chatgpt-model-selector.png)
</Frame>
You will be able to choose among any model offered by all the providers you're connected to:
<Frame>
![Model list showing available models from all providers](/images/chatgpt-model-list.png)
</Frame>
@@ -0,0 +1,34 @@
---
title: "Using OpenCode Zen & Go Models"
description: "Connect your OpenCode Go provider to OpenWork Desktop"
---
OpenCode Zen and OpenCode Go models are provided by OpenCode, not OpenWork. If you have an OpenCode subscription or API key, connect the `OpenCode Go` provider in the OpenWork desktop app.
<Note>
OpenCode and OpenWork model subscriptions are separate. If you are unsure which one you have, see [OpenWork vs OpenCode models](/start-here/connect-your-stack/openwork-vs-opencode-models).
</Note>
## Connect OpenCode Go in OpenWork Desktop
1. Open OpenWork Desktop.
2. Click your avatar in the bottom-left corner.
3. Open `Settings`.
4. Go to `AI Providers`.
5. Click `Connect`.
6. Search for `opencode`.
7. Choose `OpenCode Go`.
8. Click `Connect` and enter your OpenCode API key when prompted.
<Frame>
![OpenCode Go provider in the Connect providers dialog](/images/opencode-go-connect-provider.png)
</Frame>
After the provider is connected, choose an OpenCode model from the model picker in a chat.
## Manage your OpenCode account
- Learn about OpenCode Go at [opencode.ai/go](https://opencode.ai/go).
- Manage your OpenCode account or authentication at [opencode.ai/auth](https://opencode.ai/auth).
If OpenWork says the OpenCode key is invalid or the model is unavailable, check your OpenCode account first. OpenWork does not manage OpenCode Go or OpenCode Zen billing.
@@ -0,0 +1,17 @@
---
title: "Control the browser"
description: "How to use OpenWork to automate the built-in browser"
---
Computer-use in OpenWork currently works through the built-in OpenWork Browser. It does not yet mean full desktop control of Ubuntu, macOS, or Windows apps.
OpenWork can open pages, click, fill forms, read page content, and take screenshots inside that browser. If you connect to a remote OpenWork server, the browser runs on that remote worker.
Today, the supported path is the first-party `OpenWork Browser` extension.
## Requirements
- Use the desktop app or a remote worker that exposes the OpenWork Browser.
- Keep `OpenWork Browser` enabled in `Settings > Library`.
Then go back to the session view and ask OpenWork to open a website.
@@ -0,0 +1,59 @@
---
title: "Create a skill from chat"
description: "Turn a useful OpenWork chat into a reusable skill"
---
<Note>
**Choose where the skill lives.** A local workspace skill is free and requires no OpenWork Cloud sign-in. When you are signed in and ask OpenWork to create a skill “for me,” it creates a private Cloud plugin that only you can access. Owners and admins can publish managed team skills through [Collections](/cloud/share-with-your-team/collections).
</Note>
Do the work once in chat before you turn it into instructions.
## Turn a useful chat into a personal Cloud skill
1. Finish the task in chat and keep the thread open.
2. Ask in plain English: `Turn what we just did into a reusable skill for me`.
3. OpenWork saves the skill in your OpenWork Cloud organization as a private plugin. You receive access automatically; it is not written into your workspace or shared with anyone else.
4. Give the skill a clear name if OpenWork asks, then let it confirm what it saved. In the desktop app you get a rich confirmation card with the skill name and a link to it in your Library.
5. Use it later by name in chat, or describe the same kind of task and let OpenWork trigger it when it matches.
<Frame>
![Chat with the skill-created confirmation card showing the new skill, its plugin, and an Open in Library link](/images/skill-created-mcp-app-card.png)
</Frame>
## Other ways to create a skill
Every path creates the same thing: a skill inside a plugin in your OpenWork Cloud organization.
- **Desktop, from the Library**: open `Settings` > `Library`, filter to `Skills`, and click `Add skill`. This requires being signed in to OpenWork Cloud; the skill is created as a private plugin in your organization.
<Frame>
![Library filtered to Skills with the Add skill button](/images/library-skills-add-skill.png)
</Frame>
<Frame>
![Create a skill modal with name, description, skill body, and who can use it](/images/library-create-skill-modal.png)
</Frame>
- **Cloud dashboard**: organization admins create and edit skills inside a plugin from `Plugin Directory` — open a plugin and add a skill to it.
<Frame>
![Cloud dashboard plugin page with its skills and the Add skill button](/images/cloud-plugin-add-skill.png)
</Frame>
<Frame>
![Cloud dashboard skill editor reached from a plugin's Add skill button](/images/cloud-skill-editor.png)
</Frame>
- **From chat**, as described above.
## Change or share it only when you choose
1. To refine it, ask for the change in chat, such as `Make the skill ask before sending email`.
2. To share it with teammates, add its plugin to a Collection and grant that Collection by following [Publish and copy a skill](/start-here/do-work-with-it/publish-and-copy-a-skill).
<Note>Creating the skill does not publish it. Sharing is a separate step you choose.</Note>
## Keep a skill local to one workspace
A local skill is a `SKILL.md` file inside your workspace (for example `.opencode/skills/<name>/SKILL.md`). If you explicitly ask for a local skill in chat, OpenWork creates only that file and does not make a Cloud copy. Local skills need no Cloud account and remain available in `Settings > Library`, but OpenWork Cloud cannot assign or update them for your team.
@@ -0,0 +1,40 @@
---
title: "Cross-chat memory"
description: "How OpenWork can answer questions about past sessions."
---
OpenWork's cross-chat memory currently comes from saved session history. It is not a separate long-term memory database, and it does not automatically inject every old chat into every new prompt.
When an agent is running inside the OpenWork app, it can use OpenWork's semantic UI actions to look up past chats:
- `session.list_sessions` lists recent sessions across the workspaces the app knows about.
- `session.open` opens a matching session by ID.
- `session.read_transcript` reads recent messages from the currently open session.
- `session.latest_message` reads the newest message in the currently open session.
That means you can ask things like:
- `What did I say in the customer migration session?`
- `Find the session about Ramki and remind me what we decided.`
- `Open the onboarding bug chat and summarize the next steps.`
- `What did I ask you to do in session ses_abc123?`
## What happens
1. The agent lists sessions and matches by session ID, title, workspace, or topic words you provided.
2. If there is one clear match, the agent opens that session.
3. The agent reads the transcript and answers from the returned messages.
4. If there are multiple likely matches, the agent asks which session you meant.
Because this uses the OpenWork UI, the agent may briefly navigate away from your current session to inspect another one. You can ask it to return to the current session afterward.
## Current limits
- Session lookup is based on the sessions OpenWork has loaded for the current app and workspace context.
- Transcript reads return a bounded set of recent messages from the opened session. If an older part of a long chat is not returned, the agent should say that instead of guessing.
- This is not semantic search over every message yet. Clear titles, session IDs, workspace names, or topic clues help the agent find the right chat.
- The agent can only read sessions available to the current OpenWork app and user.
## How to ask
Give the agent whatever clue you have: a session ID, title, person name, project name, workspace, or rough topic. For example: `Use the OpenWork app UI to find the customer migration chat and tell me what I said about customer references.`
@@ -0,0 +1,26 @@
---
title: "Publish and copy a skill"
description: "Publish a skill through a Cloud Collection or copy it without changing the shared original"
---
<Note>
**Personal or managed.** Local workspace skills remain free and need no OpenWork Cloud sign-in. Cloud members can also create personal skills for themselves today. Publishing a skill for other people is a managed Cloud flow for owners and admins by default.
</Note>
## Publish a skill for your team
1. In the Cloud dashboard, open `Plugin Directory` and create or open a plugin.
2. Add a skill inside the plugin. Give it a clear name, describe when the agent should use it, and write the instructions.
3. In the plugin's `Share` section, choose a `Collection`. If you need a new one, open `Collections`, click `New collection`, and then return to the plugin.
4. Open that Collection and use `Who can access this` to grant it to everyone in the organization, selected teams, or individual members.
5. Assigned members find the plugin and its skill in `Settings > Library` in the desktop app. The plugin creator keeps access automatically.
Only owners and admins can create and publish skills for other people by default. Members can currently create personal skills for themselves. An admin restriction that prevents members from creating their own skills is in development.
Follow [Team quickstart](/cloud/team-quickstart) for the complete first-time setup. Use [Collections](/cloud/share-with-your-team/collections) when you need to group several plugins, reuse the same audience, or manage more complex assignments.
## Copy without changing the shared skill
Create a new private plugin, copy the skill instructions into a new skill, and edit that copy. Do not edit the team plugin unless everyone assigned to its Collection should receive the change.
For a small personal adjustment, you can instead create a new skill that tells the agent to use the shared skill first and then apply your extra instructions.
@@ -0,0 +1,18 @@
---
title: "Share skills with your team"
description: "Find personal and team skills in the Library, then share managed skills through Cloud Collections"
---
<Note>
**Local or managed.** Local workspace skills remain free and require no OpenWork Cloud sign-in; copy their files yourself when you only need an unmanaged handoff. For centrally assigned team skills and updates, publish a plugin through an OpenWork Cloud [Collection](/cloud/share-with-your-team/collections).
</Note>
OpenWork no longer shares skills from a `Workspace > Skills` screen. The desktop app shows what is already available to your agent.
## Find shared skills
1. Open `Settings > Library` in the desktop app.
2. Use the `Skills` filter or search to find skills from your workspace and organization.
3. Click a skill and choose `View details` to read its description; local skills can be revealed or removed, while OpenWork Connect skills are read-only.
4. Click `Refresh` if an admin just updated a Collection or changed your access.
5. To publish or customize a skill for teammates, follow [Publish and copy a skill](/start-here/do-work-with-it/publish-and-copy-a-skill). For larger setups with several plugins or audiences, see [Collections](/cloud/share-with-your-team/collections).
@@ -0,0 +1,46 @@
---
title: "Connectors, skills, and plugins"
description: "Use connectors for systems and plugins for the skills that make a system usable"
---
OpenWork gives agents a small set of building blocks. The connector reaches the system; the skill is the playbook; the plugin is the bundle; the Library is where you see what you have.
## Choose the right unit
| Use | Means | Reach for it when |
| --- | --- | --- |
| Connector | An MCP service your organization adds standalone, without wrapping it in a plugin. | The agent needs to act in that system after you connect it once. |
| Connection | Your own signed-in instance of a connector, one per user. | You sign in so the agent acts as you in that system. |
| Skill | Written instructions the agent loads when a task matches. | One repeatable way of doing work needs a playbook. |
| Plugin | A bundle — skills, MCP servers, and other components packaged and distributed as one thing. | Several related pieces should travel together. |
| Library | The list of active elements you have access to, in the desktop app and the Cloud dashboard. | You want to see or manage what is already available to you. |
MCP is the open standard underneath many connectors, and the [OpenWork MCP Gateway](/cloud/run-in-the-cloud/cloud-mcp) is how all of it reaches your agent — but day to day you can think in terms of connectors, skills, plugins, and your Library.
## Connect the system first
Use connectors for the apps themselves. In the desktop app, start in `Settings` > `OpenWork Connect`; for the click-by-click flow, see [Connect your services](/start-here/connect-your-stack/connect-services).
Everything your agent can use is listed in `Settings` > `Library`, under `Available apps` and `Your apps`. Use `Add Custom App` to connect something we do not list yet.
## Add skills when the connector is thin
A connector may reach your ticketing system but still be awkward: an incident may need exact fields, a priority change may need a safe sequence, or one ticket action may behave badly.
Write skills for those system rules:
- how to file an incident with the right fields;
- how to update a customer note without losing context;
- how to work around an unreliable ticket action.
These skills describe the system, not one person's style, so everyone who uses that system needs them.
## Bundle system skills into a plugin
Everything is a plugin under the hood. When you ask OpenWork to create a skill, OpenWork creates a small plugin containing that one skill, so “skill or plugin?” is not a decision most people need to make.
The useful plugin case is a thin connector with several system skills. If half a dozen ticketing skills make one system usable, bundle them into one plugin — the ticketing unit — and share that single thing. For distribution options, see [Publish and copy a skill](/start-here/do-work-with-it/publish-and-copy-a-skill).
## Keep personal skills separate
Use connectors for systems. Use plugins to bundle the skills that make a system usable. Personal or workflow-specific skills can stay individual unless a team should receive them together.
@@ -0,0 +1,23 @@
---
title: "What uses tokens"
description: "Understand when OpenWork model usage starts and how to control it."
---
OpenWork uses model tokens when a member actually runs work. Setup actions do not consume tokens by themselves.
## Know when usage starts
1. Sending a prompt runs the selected model and consumes tokens. Running a skill does the same because the skill is loaded into the task and the model follows its instructions.
2. There is no result cache today: rerunning the same task sends a new model request.
3. Usage scales with the context the agent has to read, including long transcripts, pasted text, file attachments, and tool results.
4. Tasks that make more tool calls usually cost more because the agent reads results and decides the next step.
5. Prompt cards do not cost anything while they sit in a policy, no matter how many an admin configures. Installing skills or publishing plugins through Collections also costs nothing until a member runs a task that uses them.
6. A good skill can reduce cost by making repeat work shorter and more deterministic, with fewer exploratory tool calls.
## Control spend today
1. Keep prompt cards short and specific.
2. Prefer focused skills for repeat workflows.
3. Attach only the files and transcript context the task needs.
4. Ask for narrow outputs before broad analysis.
5. Watch for tasks that loop through many tools, then turn the successful path into a skill.
@@ -0,0 +1,10 @@
---
title: "Workflows"
description: "Use chat and session groups to keep OpenWork organized."
---
Workflows are how you turn normal OpenWork chats into organized, repeatable work. In the UI, every session can be renamed, pinned, archived, reordered, or moved into a custom group folder from the session menu. Group folders are workspace-specific: create a group like `Research`, `Ready for review`, `Shipped`, or `Bugs`, then move sessions into that group as the work changes.
You can also describe the workflow in the composer while you work. For example: `When this is done, rename the session to "Auth cleanup", move it to Ready for review, and pin it`, or `If this turns into a bug investigation, put it in Bugs; otherwise archive it when finished.` Those instructions map to OpenWork's session management primitives: `rename` changes the session title, `move to group` stores a session-to-group assignment, `pin` keeps important sessions at the top, `archive` hides finished sessions without deleting them, and manual ordering lets you arrange sessions and groups around your process.
This makes session organization dynamic instead of manual bookkeeping. A team can keep groups for `Triage`, `In progress`, `Needs human review`, and `Done`, then ask OpenWork in chat to classify future sessions into those buckets as work completes. The exact groups and actions are configurable from the UI, so the workflow can match how you already work instead of forcing a fixed project-management structure.
+12
View File
@@ -0,0 +1,12 @@
---
title: "Downloads"
description: "Download OpenWork for macOS, Windows, and Linux — stable releases and the rolling alpha channel."
icon: "download"
mode: "wide"
---
import { OpenWorkDownloads } from "/snippets/openwork-downloads.jsx";
Every OpenWork release is published on GitHub Releases; downloads below always reflect what is live right now.
<OpenWorkDownloads />
@@ -0,0 +1,76 @@
---
title: "Enterprise desktop deployment"
description: "Deploy the OpenWork Enterprise binary with your existing software distribution, optionally pushing one configuration file through MDM."
---
Enterprise deployment is the standard **OpenWork Enterprise binary** plus an optional MDM push of one configuration file. There is no customer-specific installer build, no wrapping, and no repackaging — the signed release artifacts are never modified, so normal macOS and Windows signature verification is preserved.
## The enterprise binary
OpenWork ships a dedicated enterprise distribution alongside the public one. Both are published on [GitHub Releases](https://github.com/different-ai/openwork/releases); enterprise artifacts follow the `enterprise` update channel.
| Platform | Artifact |
| --- | --- |
| macOS (Apple Silicon / Intel) | `openwork-enterprise-mac-arm64-<version>.dmg`, `openwork-enterprise-mac-x64-<version>.dmg` |
| Windows (x64 / ARM64) | `openwork-enterprise-win-x64-<version>.exe`, `openwork-enterprise-win-arm64-<version>.exe` |
| Linux | `openwork-enterprise-linux-x86_64-<version>.AppImage`, `openwork-enterprise-linux-arm64-<version>.AppImage`, `.tar.gz` variants |
The enterprise distribution differs from the public build in ways that are baked into the package metadata at build time — not toggled by configuration:
- The product is named **OpenWork Enterprise**.
- Sign-in is always required.
- **Activation is always required**: before an administrator-provisioned activation completes, the app exposes only the minimal surface needed to accept a connect link and read or write its bootstrap configuration. Chat, tools, and workspace access stay locked.
- Auto-update follows the `enterprise` release channel, so enterprise installs never pick up public-channel artifacts.
A packaged public build can never be turned into an enterprise build with environment variables or configuration files; the distribution flavor is read only from immutable package metadata.
## Optional MDM push: `desktop-bootstrap.json`
If you manage the fleet with MDM (Jamf, Intune, or similar), distribute the enterprise binary the way you distribute any other app, and optionally write one JSON file during provisioning so the first launch already knows your Den server:
| OS | Canonical path |
| --- | --- |
| Windows | `%LOCALAPPDATA%\openwork\desktop-bootstrap.json` (`%XDG_CONFIG_HOME%\openwork\desktop-bootstrap.json` wins if set) |
| macOS / Linux | `$XDG_CONFIG_HOME/openwork/desktop-bootstrap.json`, falling back to `~/.config/openwork/desktop-bootstrap.json` |
```json
{
"baseUrl": "https://openwork.example.com",
"apiBaseUrl": "https://openwork.example.com/api/den",
"requireSignin": true,
"brandAppName": "Example Corp OpenWork",
"writtenAt": "2026-08-20T12:00:00.000Z"
}
```
| Field | Required | Meaning |
| --- | --- | --- |
| `baseUrl` | Yes | Your Den web origin. The desktop derives API and MCP paths through this origin's `/api/den` proxy. |
| `apiBaseUrl` | No | Separate Den API base, only if you publish one. In a single-origin topology, omit it or use the `/api/den` proxy path. |
| `requireSignin` | Yes | Whether the app requires organization sign-in before use. Enterprise builds enforce sign-in regardless of this value. |
| `requireActivation` | No | Ignored by enterprise builds, which always require activation. Lets a public build opt into the activation gate. |
| `brandAppName`, `brandLogoUrl`, `brandIconUrl` | No | Organization branding shown in the app. |
| `writtenAt` | Yes | ISO 8601 timestamp. When more than one bootstrap file exists, the valid configuration with the newest `writtenAt` wins. |
Deployment details worth knowing:
- Older builds read a legacy `~/.config/openwork` path on every OS. Current builds still read it for compatibility; the newest `writtenAt` decides which file applies.
- Clearing cloud configuration in the app removes bootstrap files entirely, so a fresh MDM-dropped config wins on the next launch.
- `OPENWORK_DESKTOP_BOOTSTRAP_PATH` points a machine at an isolated bootstrap file for testing; legacy global configuration never crosses that boundary.
## What you do not push through MDM
- **Activation.** The app writes `enterpriseActivation` into `desktop-bootstrap.json` only after a signed activation claim verifies. Do not provision this record yourself; a hand-written entry is not proof of a provisioned tenant, and the app treats it as incomplete.
- **Certificates.** The desktop builds its trust bundle from the OS trust stores — Windows machine and user Root/CA stores, and the macOS administrator-controlled System keychains. Deploy enterprise CAs the way you already do; no OpenWork-specific certificate file exists. See [Certificate trust and proxies](/start-here/certificate-trust-and-proxies).
- **Desktop policies.** Version pinning and feature policies are delivered by your Den server after sign-in and enforced by the app automatically — no MDM scripting. Manage them in the organization dashboard.
## Choosing between install links and MDM
| | Organization install links | Enterprise binary + MDM |
| --- | --- | --- |
| Best for | Self-serve onboarding, no device management required | Managed fleets with existing software distribution |
| Binary | Standard public installer, delivered by Den or GitHub ([Installer delivery](/start-here/installer-delivery)) | Enterprise artifacts, distributed by your MDM |
| Server configuration | Browser deep-link handoff after install | `desktop-bootstrap.json` written at provisioning time |
| First launch | User confirms the exact organization and server | App already targets your Den server |
Both paths end at the same place: normal organization sign-in against your Den deployment. For fully isolated networks, combine this page with the [Air-gapped deployment](/start-here/air-gapped-deployment) checklist — with MDM distribution, installer bytes never need to transit Den at all.
+44
View File
@@ -0,0 +1,44 @@
---
title: "Get started"
description: "Privacy-first, open-source alternative to Claude Cowork."
---
<Note>
**OpenWork stays free when you use it locally.** Download the desktop app, connect your own model provider, and add custom MCP servers without signing in to OpenWork Cloud. Sign in only when you want organization-managed providers, skills, connections, or policies. See [OpenWork Cloud](/cloud/get-started) for those team features.
</Note>
Most people should start with the [OpenWork app](https://openworklabs.com/download). It lets you use your own LLMs, skills, plugins, and MCP servers locally, then add Cloud when your team needs shared management.
## Start
Get started by connecting your LLM provider of choice and sending your first message:
<CardGroup cols={3}>
<Card title="Connect a provider" icon="plug" href="/start-here/connect-your-stack/sign-in-with-chatgpt">
Sign in with ChatGPT or bring your own LLM key in a few clicks.
</Card>
<Card title="Connect your services" icon="plug" href="/start-here/connect-your-stack/connect-services">
Join an OpenWork Cloud organization, then connect the managed services it makes available.
</Card>
<Card title="Control the browser" icon="globe" href="/start-here/do-work-with-it/control-the-browser">
Let OpenWork run and screenshot tasks in Chrome for you.
</Card>
</CardGroup>
## Choose your setup
<CardGroup cols={3}>
<Card title="Download OpenWork" icon="download" href="https://openworklabs.com/download">
Start free with the desktop app, your own provider, and no OpenWork Cloud account.
</Card>
<Card title="OpenWork Cloud" icon="users" href="/cloud/get-started">
For teams: Collections, managed connections and models, organization policies, and hosted workspaces.
</Card>
<Card title="Enterprise" icon="building" href="/cloud/enterprise">
Get support with an enterprise deployment if you need a private rollout or custom requirements.
</Card>
</CardGroup>
---
[GitHub](https://github.com/different-ai/openwork) · [Changelog](/changelog) · [Product Roadmap](/roadmap) · [Missing docs](/start-here/missing-docs) · [Missing languages](/start-here/missing-languages)
@@ -0,0 +1,129 @@
---
title: "GitHub connector for Helm"
description: "Create a GitHub App and configure the OpenWork EE Helm chart for repository connectors."
---
The GitHub connector lets a self-hosted OpenWork deployment install a GitHub App, list repositories the installation can read, discover plugin or marketplace manifests, import selected repositories, and keep imported content fresh from GitHub webhooks.
This is separate from GitHub social sign-in. Social sign-in uses `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` for Better Auth. The repository connector uses the `GITHUB_CONNECTOR_APP_*` settings below.
## Public URLs
Use the same public hosts that you set in the Helm chart:
```yaml
config:
public:
webOrigin: "https://openwork.example.com"
apiOrigin: "https://api.openwork.example.com"
```
The GitHub App needs these OpenWork URLs:
| GitHub App field | Value |
| --- | --- |
| Homepage URL | `https://openwork.example.com` |
| Setup URL | `https://openwork.example.com/dashboard/integrations/github` |
| Webhook URL | `https://api.openwork.example.com/v1/webhooks/connectors/github` |
Enable **Redirect on update** for the setup URL. GitHub redirects back to the setup URL with `installation_id` and `state`; Den web completes the installation by calling Den API.
No OAuth callback URL is required for the current repository connector flow. If you also enable GitHub user authorization later, treat that as a separate OAuth setup from this connector.
## Create the GitHub App
In GitHub, create a new GitHub App for the account that should own the connector.
Use these settings:
- **Webhook**: active.
- **Webhook URL**: `https://api.openwork.example.com/v1/webhooks/connectors/github`.
- **Webhook secret**: generate a strong random secret and keep it for Helm.
- **Setup URL**: `https://openwork.example.com/dashboard/integrations/github`.
- **Redirect on update**: enabled.
- **Repository permissions**:
- **Contents**: read-only.
- **Metadata**: read-only. GitHub grants this automatically.
- **Subscribe to events**:
- `push`
- `installation`
- `installation_repositories`
- `repository`
After creating the app, copy:
- **App ID**
- **Client ID**
- **Client secret** if you generated one
- **Private key** (`.pem`)
- **Webhook secret**
The connector currently requires the App ID and private key for live GitHub App calls. The webhook secret is required for webhook ingestion. The client ID and client secret are exposed by the Helm chart because GitHub Apps have them, but the current install and repository sync path does not depend on them.
## Configure Helm
Add the GitHub App values to your production values file:
```yaml
config:
githubConnector:
appId: "123456"
clientId: "Iv1.example"
secret:
values:
githubConnectorAppClientSecret: "github-app-client-secret-if-used"
githubConnectorAppPrivateKey: |-
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
githubConnectorAppWebhookSecret: "replace-with-the-github-webhook-secret"
```
You can also paste the private key with escaped newlines (`\n`). Den normalizes escaped newlines before signing GitHub App JWTs.
If you use an existing Kubernetes Secret instead of `secret.values`, keep `config.githubConnector.appId` in values and include these secret-backed keys in that Secret:
```yaml
secret:
create: false
existingSecret: openwork-ee-secrets
```
Required deployment values:
- `GITHUB_CONNECTOR_APP_ID`
- `GITHUB_CONNECTOR_APP_PRIVATE_KEY`
- `GITHUB_CONNECTOR_APP_WEBHOOK_SECRET`
Optional connector keys:
- `GITHUB_CONNECTOR_APP_CLIENT_ID`
- `GITHUB_CONNECTOR_APP_CLIENT_SECRET`
The chart writes `GITHUB_CONNECTOR_APP_ID` and `GITHUB_CONNECTOR_APP_CLIENT_ID` through the shared ConfigMap, and writes `GITHUB_CONNECTOR_APP_PRIVATE_KEY`, `GITHUB_CONNECTOR_APP_WEBHOOK_SECRET`, and `GITHUB_CONNECTOR_APP_CLIENT_SECRET` through the shared Secret.
## What OpenWork does with it
When an admin clicks **Connect GitHub** in OpenWork:
1. Den API calls `/v1/connectors/github/install/start`.
2. Den API reads the GitHub App metadata with the app ID and private key.
3. Den API sends the admin to the GitHub App installation URL with a signed `state`.
4. GitHub redirects to `/dashboard/integrations/github?installation_id=...&state=...`.
5. Den web calls `/v1/connectors/github/install/complete`.
6. Den API stores the GitHub installation as a connector account and lists repositories visible to that installation.
When GitHub sends a webhook to `/v1/webhooks/connectors/github`, Den API verifies the `x-hub-signature-256` header with `GITHUB_CONNECTOR_APP_WEBHOOK_SECRET` before enqueueing connector sync work.
## Validate the setup
After deploying the chart, sign in to Den web and open:
```text
https://openwork.example.com/dashboard/integrations
```
Choose GitHub, install the GitHub App on a test account or organization, select a repository, and start discovery. A working setup should return to OpenWork, show repositories visible to the installation, and allow discovery on a selected repository.
For webhook validation, push to a connected repository and check Den API logs for an accepted or intentionally ignored GitHub webhook delivery. A `503` response from `/v1/webhooks/connectors/github` means the webhook secret is not configured in the Den API pod.
@@ -0,0 +1,40 @@
---
title: "Installer delivery"
description: "Choose how organization install links deliver standard OpenWork desktop installers."
---
Organization install links help members install the standard OpenWork desktop and connect it to the correct Den organization. Den does not build a customer-specific installer or wrap the installer in a ZIP. The implementation and security details remain in the repository's [Organization Install Links guide](https://github.com/different-ai/openwork/blob/dev/docs/org-install-links.md#installer-delivery).
## Delivery matrix
| Deployment style | Download behavior | Required client access |
| --- | --- | --- |
| Internet-connected | Den validates the organization install token, then redirects the browser to the exact standard GitHub release asset under `OPENWORK_INSTALLER_RELEASE_REPO` and `OPENWORK_INSTALLER_RELEASE_TAG`. The organization token is not forwarded. | Den web/API, `github.com`, `release-assets.githubusercontent.com`, and `objects.githubusercontent.com` for legacy or rollback paths. |
| Semi-air-gapped installer delivery | Den streams the matching standard installer from `OPENWORK_INSTALLER_ARTIFACTS_DIR`. | Den web/API only for installer download. Desktop runtime dependencies may still need internet unless mirrored or disabled. |
| Fully internal installer delivery | Same mounted-artifact path, with Den web/API and installer bytes available entirely inside the isolated network. | Internal Den web/API only for installer download. Full product isolation still requires the broader checklist in [Air-gapped deployment](/start-here/air-gapped-deployment). |
## Mounted artifact behavior
When `OPENWORK_INSTALLER_ARTIFACTS_DIR` points at a mounted directory and the expected file exists, Den streams that file directly. It does not fetch from GitHub, cache on first request, rewrap, ZIP, or hold the whole installer in memory.
`OPENWORK_INSTALLER_RELEASE_TAG` selects the expected release version. The standard filenames use the release tag without a leading `v`:
- `openwork-mac-arm64-<version>.dmg`
- `openwork-mac-x64-<version>.dmg`
- `openwork-win-x64-<version>.exe`
- `openwork-linux-x86_64-<version>.AppImage`
- `openwork-linux-arm64-<version>.AppImage`
`OPENWORK_INSTALLER_RELEASE_REPO` controls the GitHub redirect path when Den does not find a mounted artifact. It is not an internal artifact registry setting.
For multi-replica Den API deployments, mount the same read-only PVC at the same path on every replica. Connection grants are stored in MySQL, so preview and acceptance can land on different replicas safely, but installer bytes must be equally visible to each replica.
## One-origin steady state and install-link exchange
For normal signed-in desktop traffic, prefer one desktop-facing Den web origin. The desktop derives API and MCP paths through that origin's `/api/den` proxy.
If `DEN_API_PUBLIC_URL` is configured as a separate API origin, the one-time **Open OpenWork** install-link exchange must also reach that API origin. External MCP clients that use the published MCP URL must reach it too. This does not mean every steady-state desktop request needs both origins in the single-origin topology.
## Managed fleets: enterprise binary + MDM
This page covers install-link delivery of the standard installer. Managed fleets typically skip the install-link handoff entirely: deploy the **OpenWork Enterprise binary** through MDM and write `desktop-bootstrap.json` during provisioning so the first launch already targets your Den server. See [Enterprise desktop deployment](/start-here/enterprise-desktop-deployment) for the artifact names, bootstrap file reference, and what MDM does not need to push.
@@ -0,0 +1,93 @@
---
title: "Migrate from Claude Cowork"
description: "Move your files, skills, plugins, MCP servers, and team setup to OpenWork."
---
You do not need to rebuild your setup to move from Claude Cowork to OpenWork. Start with one real task, bring over the capabilities it needs, and keep Cowork available until that task works end to end.
## What carries over
| From Claude Cowork | In OpenWork | What to do |
| --- | --- | --- |
| Local files and folders | Workspace files | Open the same folder or repository as an OpenWork workspace. |
| `SKILL.md` skills | Skills | Copy the skill folder into your workspace or install it as a plugin. |
| Claude-compatible plugins | Plugins | Import the plugin or marketplace manifest; OpenWork normalizes its skills, commands, and MCP dependencies. |
| MCP servers | Connectors and custom apps | Reconnect the server through OpenWork Connect or add its URL as a custom app. |
| Claude models | Anthropic provider | Add an Anthropic API key, or provision Anthropic centrally through OpenWork Cloud. |
| Scheduled tasks | Scheduled tasks | Recreate each schedule and verify its workspace, permissions, and output location. |
<Note>
Conversation history does not transfer automatically. Keep any important instructions in workspace files or skills rather than relying on an old thread.
</Note>
## 1. Install OpenWork and open the same work
1. [Download OpenWork](https://openworklabs.com/download) for macOS, Windows, or Linux.
2. Create a workspace from the folder or repository you used with Cowork.
3. Ask OpenWork to summarize the workspace before changing anything. This confirms it can read the files you expect.
Your files stay where they already are. OpenWork does not require you to upload a duplicate copy before you can work with them.
## 2. Choose your models
OpenWork is model-agnostic. You can keep using Anthropic models, add another provider, or let your organization provision models centrally.
- To keep using Claude, follow [Add an Anthropic API key](/start-here/connect-your-stack/add-anthropic-api-key).
- To use ChatGPT, follow [Sign in with ChatGPT](/start-here/connect-your-stack/sign-in-with-chatgpt).
- For another provider, follow [Add a custom LLM](/start-here/connect-your-stack/add-a-custom-llm).
- For a team-managed provider, follow [Managed LLM provider](/cloud/share-with-your-team/managed-llm-provider).
Your Claude subscription and Anthropic API billing may be separate. Confirm API access before removing Cowork from your workflow.
## 3. Bring over skills and plugins
OpenWork uses the same `SKILL.md` format. Copy a skill directory into the workspace, or import the plugin that contains it.
After importing each skill:
1. Open the skill and check any file paths, app names, or environment variables.
2. Run the smallest task that should trigger it.
3. Confirm the output before importing the next skill.
See [Connectors, skills, and plugins](/start-here/do-work-with-it/skills-plugins-and-mcp) for how OpenWork organizes these pieces. For team distribution, see [Publish and copy a skill](/start-here/do-work-with-it/publish-and-copy-a-skill).
## 4. Reconnect apps and MCP servers
Credentials do not move between products. Reconnect each service so OpenWork receives its own authorized access.
- For Gmail, Calendar, Drive, Slack, Notion, Linear, and other managed services, use [Connect your services](/start-here/connect-your-stack/connect-services).
- For another remote or local MCP server, use [Add a custom MCP server](/start-here/connect-your-stack/add-an-mcp-server).
- For an organization-wide setup, use [Shared MCP connections](/cloud/share-with-your-team/shared-mcp-connections).
Test read-only actions first. Then test actions that modify data, such as sending a message or updating a record, with a disposable example.
## 5. Recreate schedules and browser tasks
For every recurring task, record:
- the prompt;
- its schedule and time zone;
- the workspace and files it can use;
- the connected apps it needs;
- where the result should be saved or sent.
Recreate one schedule at a time and run it manually before enabling recurrence. For browser-driven work, follow [Control the browser](/start-here/do-work-with-it/control-the-browser) and verify the target site while you are present.
## 6. Move a team without configuration drift
Do not ask every teammate to repeat your local setup by hand. In OpenWork Cloud, publish approved skills and connections once, assign them to the right teams, and apply desktop policies centrally.
Start with the [team quickstart](/cloud/team-quickstart), then review [desktop policies](/cloud/share-with-your-team/desktop-policies) and [members and roles](/cloud/members-and-rbac).
## Cutover checklist
- [ ] The same workspace opens and the agent can read the expected files.
- [ ] At least one model is connected and billing is understood.
- [ ] Each required skill triggers on a representative task.
- [ ] Each connector or MCP server is authorized and tested.
- [ ] Scheduled and browser tasks have been run manually once.
- [ ] Team capabilities are published centrally instead of copied machine by machine.
- [ ] Important Cowork instructions are saved in files or skills.
- [ ] Cowork remains available until the representative workflow passes in OpenWork.
Once the checklist passes, make OpenWork the default for that workflow and migrate the next one.
+14
View File
@@ -0,0 +1,14 @@
---
title: "Missing docs?"
description: "Request a page or contribute one yourself."
---
Two ways to fix it.
## Ask us to write it
[Email the team](mailto:team@openworklabs.com?subject=missing%20documentation&body=hey%20my%20github%20is%20%7Busername%7D%2C%20I%27m%20interested%20in%20%7Bfeature%7D%20from%20openwork%20but%20I%27m%20missing%20documentation%20on%20it%2C%20could%20you%20create%20it%3F%0A%0AThanks) with the feature you're trying to use and we'll write the page.
## Write it yourself
OpenWork docs live in the [openwork repo](https://github.com/different-ai/openwork/tree/dev/packages/docs). Open a PR with a new `.mdx` page and we'll review it.
@@ -0,0 +1,54 @@
---
title: "Missing languages"
description: "See which OpenWork languages ship today and help us improve them."
---
OpenWork already ships with a growing set of languages, and we want to keep expanding that list.
<Frame>
<iframe
width="100%"
height="400"
src="https://www.youtube.com/embed/orGF117Snu8"
title="OpenWork missing languages overview"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
</Frame>
## Languages available today
Watch the video above for a quick overview, then use this list as the current source of truth for languages supported in the app:
- English (`en`)
- Japanese (`ja`)
- Simplified Chinese (`zh`)
- Vietnamese (`vi`)
- Portuguese (Brazil) (`pt-BR`)
- Thai (`th`)
- French (`fr`)
- Catalan (`ca`)
- Spanish (`es`)
## Improve current translations
If you want to improve an existing translation, start in [`apps/app/src/i18n/locales`](https://github.com/different-ai/openwork/tree/dev/apps/app/src/i18n/locales).
Each language lives in its own file, so most fixes are simple copy updates inside one locale file such as `apps/app/src/i18n/locales/fr.ts` or `apps/app/src/i18n/locales/es.ts`.
## Add a new language
If you want to add a new language to OpenWork, the basic file changes are:
- Create a new locale file in `apps/app/src/i18n/locales/<language-code>.ts`.
- Register that locale in `apps/app/src/i18n/index.ts` by adding the import, language code, language option, and translation map entry.
- Re-export the locale from `apps/app/src/i18n/locales/index.ts`.
- Add the new locale code to `scripts/i18n-audit.mjs` so the audit script checks it.
After that, run the audit script to spot missing keys:
```bash
node scripts/i18n-audit.mjs --missing
```
If you want to follow broader localization work, language requests, or upcoming improvements, check the [i18n roadmap](https://github.com/different-ai/openwork/issues/1385).
@@ -0,0 +1,70 @@
---
title: "Network diagnostics"
description: "Diagnose DNS, TLS, proxy, catalog, and Den outbound issues in locked-down OpenWork deployments."
---
Use this page when OpenWork reaches the wrong server, cannot complete TLS, is blocked by a proxy, or needs proof that Den can reach an approved diagnostic origin. For non-network runtime, MCP, and stale-tool problems, use [Diagnostic prompts](/start-here/troubleshooting/diagnostic-prompts).
## Windows desktop network doctor
`scripts/support/openwork-doctor.ps1` is a Windows PowerShell 5.1-compatible, no-admin, read-only report for customer IT. It checks DNS, TCP 443, the live TLS certificate chain with `SslStream`, served certificates with `openssl` when available, WinHTTP/.NET proxy settings, PowerShell/OS version, and `NODE_EXTRA_CA_CERTS`.
Run it directly from the raw repository URL when the machine can reach GitHub:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -Command "`$p=Join-Path `$env:TEMP 'openwork-doctor.ps1'; Invoke-WebRequest -UseBasicParsing 'https://raw.githubusercontent.com/different-ai/openwork/dev/scripts/support/openwork-doctor.ps1' -OutFile `$p; & `$p -WebUrl 'https://openwork.example.com' -ApiUrl 'https://api.openwork.example.com' -ExpectedIssuerMatch 'DigiCert'"
```
If raw download is blocked, copy `scripts/support/openwork-doctor.ps1` to the machine through your approved offline channel and run:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File .\openwork-doctor.ps1 -WebUrl 'https://openwork.example.com' -ApiUrl 'https://api.openwork.example.com' -ExpectedIssuerMatch 'DigiCert'
```
Use your Den web origin for `-WebUrl`. Use the separate Den API origin only if your deployment publishes one; in a single-origin deployment, the API path is usually behind the web origin's `/api/den` proxy.
### Interpreting common verdicts
| Verdict | What it usually means | First fix to check |
| --- | --- | --- |
| `LIKELY TLS INTERCEPTION` | The leaf issuer is an internal or proxy CA instead of the expected public issuer. | Allowlist the OpenWork hosts through the proxy, or install the enterprise root in every OpenWork trust surface that needs it. |
| `LIKELY MISSING INTERMEDIATE OR UNTRUSTED ROOT` | The server may be serving a leaf without the intermediate, or the machine lacks the issuing root. | Fix the server `fullchain`/certificate bundle first, then verify client trust. |
| `MISSING INTERMEDIATE CONFIRMED BY OPENSSL` | OpenSSL could not build the served chain. | Replace the server certificate bundle with a complete chain. |
| `LIKELY DNS ISSUE` | The hostname does not resolve on that machine. | Check VPN state, split-horizon DNS, and whether the internal hostnames exist. |
| `PROXY DETECTED` | WinHTTP or .NET routes the URL through a proxy. | Verify proxy authentication, host allowlisting, and expected system proxy behavior. |
## Cloud catalog diagnostic trust
Settings → Debug includes a cloud catalog diagnostic that probes the managed `openwork-cloud` MCP entry and verifies that Cloud exposes exactly `search_capabilities` and `execute_capability`.
For self-hosted Den origins, set `OPENWORK_AGENT_DIAGNOSTICS_TRUSTED_ORIGINS` before OpenWork launches:
```bash
OPENWORK_AGENT_DIAGNOSTICS_TRUSTED_ORIGINS=https://openwork.example.com
```
The value is a comma-separated list of bare origins: scheme, host, and optional port only. Entries must use `https:` except loopback origins, which may use `http:`.
If the Den origin is not trusted, the diagnostic is skipped and sends no credentialed request to that untrusted origin. Cloud MCP itself is unaffected; this variable only controls the diagnostic probe. Set it in the launcher, MDM profile, service wrapper, or shell environment before launch because the in-app environment store strips `OPENWORK_*` and `OPENCODE_*` keys.
## Den outbound diagnostic
Den can run an optional, admin-triggered outbound diagnostic from the Den process. It exercises the real container DNS, proxy, TLS trust, firewall, service mesh, and NetworkPolicy path.
- The default diagnostics origin is `https://diagnostic.openworklabs.com`.
- A workspace owner or super-admin starts the check; it is not a background dependency for Den.
- The diagnostic token must be synthetic and at least 24 characters. Do not use provider or customer credentials.
- To use an internal diagnostics origin, deploy the diagnostics app at an internally reachable supported runtime/origin, then set `DEN_DIAGNOSTICS_ORIGIN` and `DEN_DIAGNOSTICS_BEARER_TOKEN` for Den. The service README currently documents its runtime and retention model; do not assume a generic Kubernetes package unless your deployment path has been validated.
```dotenv
DEN_DIAGNOSTICS_ORIGIN=https://diagnostics.example.internal
DEN_DIAGNOSTICS_BEARER_TOKEN=<24-or-more-character-synthetic-token>
```
On Node.js 24.5 or newer, Den proxy configuration belongs in process-start environment such as `NODE_USE_ENV_PROXY=1`, `HTTPS_PROXY`, and `NO_PROXY`. Use `NODE_EXTRA_CA_CERTS` or Helm `customCa` when TLS inspection uses a private root. See [Certificate trust and proxies](/start-here/certificate-trust-and-proxies).
## Related diagnostics
- [Outbound network access](/start-here/outbound-network-access) lists the destination inventory and what breaks when a host is blocked.
- [Private network deployment](/start-here/private-network-deployment) explains the common VPN/private Den topology and internal MCP policy switch.
- [Diagnostic prompts](/start-here/troubleshooting/diagnostic-prompts) covers non-network runtime, MCP, and stale-tool debugging.
@@ -0,0 +1,132 @@
---
title: "Brand an on-prem deployment"
description: "Configure an organization name, wordmark, accent color, and desktop icon without depending on a public CDN."
---
Organization branding lets an owner customize the OpenWork experience for everyone signed in to an on-prem Den organization. Uploaded images stay inside the deployment: Den stores the binary image data in the existing MySQL database and serves signed, versioned URLs from your Den API surface.
No separate asset PVC, public object store, or public image CDN is required.
## What branding changes
| Setting | Where members see it |
| --- | --- |
| Application name | Download, setup, window title, and desktop screens |
| Wordmark | Top-left of the desktop sidebar |
| Square app icon | Windows title bar, Alt-Tab, and taskbar; macOS Dock; Linux window icon |
| Accent color | Supported buttons and accent highlights |
<Note>
Branding changes the displayed organization experience. It does not rename or re-sign the installed executable, and the signed application identity remains OpenWork.
</Note>
## Configure the on-prem origins
Normal signed-in desktop traffic can use one Den web origin. In that topology, the desktop points at `config.public.desktopDenBaseUrl`, and API, MCP, and signed branding asset URLs are reached through the web origin's `/api/den` proxy.
For Helm installations using that single-origin shape, keep public values aligned with the internally reachable HTTPS name:
```yaml
config:
public:
webOrigin: "https://openwork.example.internal"
```
This maps `config.public.webOrigin` to `DEN_BASE_URL`; Den derives the Better Auth URL, CORS/trusted origins, API defaults, MCP resource defaults, and web-app hosts from it. Do not set it to `localhost`, a Kubernetes service name, or an internet hostname that member desktops cannot reach.
If you publish a separate Den API origin, set `config.public.apiOrigin` as the `DEN_API_PUBLIC_URL` compatibility override. Member devices must also resolve and trust that API origin for the one-time install-link exchange, external MCP clients, and signed branding asset URLs. See [Private network deployment](/start-here/private-network-deployment) for the single-origin nuance.
If your organization uses a private certificate authority or TLS-inspecting proxy, configure the relevant desktop, sidecar, Den, and database trust surfaces in [Certificate trust and proxies](/start-here/certificate-trust-and-proxies). On Linux, do not assume installing a root into `/etc/ssl` alone is sufficient for Electron; Chromium/Electron may require the distribution's NSS or user trust database.
## Upload the brand
You must be an organization owner, and the deployment must entitle the organization to desktop policies.
1. Open your on-prem Den web URL and select the organization.
2. Go to `Org settings` and find `Brand Appearance`.
3. Enter the application name you want members to see.
4. Upload a wordmark:
- PNG or JPEG
- 128×32 through 4096×4096
- horizontal aspect ratio between 1.5:1 and 8:1
- under 2 MB
5. Upload a square app icon:
- PNG or JPEG
- square, from 64×64 through 4096×4096
- under 2 MB
6. Select an accent color and click `Save settings`.
Den validates and normalizes each image before saving it. Replacing an image produces a new SHA-256 versioned URL so desktops do not retain stale cached bytes.
<Frame>
![Brand Appearance showing a wordmark stored in the Example Corp Den](/images/on-prem-branding/brand-assets-saved-in-den.png)
</Frame>
## When members receive a new icon
A desktop restart is **not required** to apply a new icon after the desktop has received the updated organization configuration.
There are two separate timing steps:
| Step | Expected timing |
| --- | --- |
| Organization configuration reaches a running desktop | Immediately after a Cloud session/settings refresh, otherwise on the background refresh that runs once per hour |
| Windows applies an icon after receiving the configuration | Image download and validation, then about 500 ms of deliberate Explorer taskbar staging plus normal disk and operating-system work |
In the real Windows validation flow, the visible taskbar and Alt-Tab update completed without a restart in about 5.4 seconds after a controlled configuration refresh. Treat that as a validation result rather than a network SLA.
An already-running desktop that receives no refresh event can therefore remain on the old organization configuration for up to one hour. Closing and reopening OpenWork starts an immediate configuration fetch, so reopening is the fastest user action when an update appears stale.
On Windows, OpenWork briefly removes the live taskbar button, stages the new icon and AppUserModelID, and re-adds the button. The two 250 ms waits prevent Explorer from collapsing the operation and retaining the executable's stock icon. This behavior uses Electron's [`setSkipTaskbar`, `setAppDetails`, and `setIcon`](https://www.electronjs.org/docs/latest/api/browser-window) APIs.
<Frame>
![A company icon displayed in the real Windows taskbar and Alt-Tab switcher](/images/on-prem-branding/windows-taskbar-and-alt-tab.png)
</Frame>
## Relaunch and offline behavior
After the first successful download, the desktop caches a normalized PNG and, on Windows, a multi-resolution ICO. A relaunch applies that cached identity before the first window becomes visible. The branded icon therefore survives a desktop restart and can appear while Den is temporarily unreachable.
Windows also creates or replaces a per-user `OpenWork Organization` Start Menu shortcut with the same AppUserModelID and icon. This does not require administrator access. Microsoft uses the AppUserModelID and [`RelaunchIconResource`](https://learn.microsoft.com/en-us/windows/win32/properties/props-system-appusermodel-relaunchiconresource) to associate taskbar grouping, pinning, relaunch information, and the icon. See Microsoft's [Application User Model ID guidance](https://learn.microsoft.com/en-us/windows/win32/shell/appids) for the underlying Windows behavior.
<Tip>
If Windows still shows a separate, older stock pinned shortcut, unpin that stale shortcut and pin the currently running branded taskbar button once. The live window does not otherwise require re-pinning or a restart.
</Tip>
## Backup and restore
Brand image bytes are stored as raw binary data in the `organization_brand_asset` MySQL table. Organization metadata stores the active version and signed Den URL.
Include these in the same operational plan:
- MySQL backups and restore testing
- the Kubernetes Secret or secret-manager value containing `BETTER_AUTH_SECRET`
- the private CA and DNS configuration used by desktop devices
Restore the same `BETTER_AUTH_SECRET` with the database. Existing asset URLs contain signatures derived from that secret; changing it invalidates those URLs until the assets are uploaded again.
Because every Den API replica reads the same database, multiple replicas do not need a shared filesystem volume for branding.
## Clear or replace branding
- Use `Replace image` and save to publish a new immutable version.
- Use `Clear` and save to return to stock OpenWork branding.
After the next desktop configuration refresh, clearing the icon removes the cached organization icon and the per-user Windows shortcut, then restores the stock taskbar identity. Reopen the desktop if you want to force the configuration fetch immediately.
## Troubleshooting
### The dashboard saved, but a desktop still shows the old icon
1. Confirm the member is signed in to the intended organization.
2. Reopen OpenWork to force an immediate configuration fetch instead of waiting for the hourly background refresh.
3. From the member device, confirm the configured Den origin resolves and the signed asset URL is reachable.
4. Confirm the private CA is installed in the correct desktop trust surface. On Linux Electron, include the Chromium/Electron-compatible NSS or user trust store when required by the distribution.
### The title bar changed but the Windows taskbar did not
Confirm the desktop release includes the organization taskbar identity support. Older releases only changed the window icon. If an old pinned shortcut remains as a separate stock button, unpin it and pin the running branded button.
### Images disappear after a Den container replacement
Check the MySQL connection and migrations. Brand assets are database rows, not files inside the Den container, so replacing a Den application container must not remove them. A database replacement without restoring its data will.
@@ -0,0 +1,84 @@
---
title: "Outbound network access"
description: "Customer IT allowlist guidance for OpenWork desktop, Den, deployment artifacts, and opt-in providers."
---
OpenWork works best when customer IT approves outbound access by component. The canonical machine-readable source is [`docs/enterprise/outbound-access.json`](https://github.com/different-ai/openwork/blob/dev/docs/enterprise/outbound-access.json). CI guards that file with `node scripts/check-outbound-access.mjs`; this page is the published human-readable summary.
## Minimum hosted desktop allowlist
For a practical OpenWork Cloud desktop install, allow TCP 443 to these hosts:
```text
app.openworklabs.com
api.app.openworklabs.com
github.com
release-assets.githubusercontent.com
objects.githubusercontent.com
registry.npmjs.org
models.openworklabs.com
```
Common misses:
- GitHub release downloads redirect away from `github.com`. Allow `release-assets.githubusercontent.com`, which is the current verified GitHub release-asset host, and `objects.githubusercontent.com`, which is kept for older clients and rollback paths.
- `registry.npmjs.org` is used when packaged desktop builds start `npx -y openwork-ui-mcp`. If it is blocked, the desktop can still open, but UI-control MCP is unavailable.
- `models.openworklabs.com` is the default OpenCode model catalog. Set `OPENCODE_MODELS_URL` to an approved internal mirror if the public catalog is blocked.
## Desktop and local runtime destinations
| Host | Requirement | Why it is reached | What breaks when blocked |
| --- | --- | --- | --- |
| `app.openworklabs.com` | Required for OpenWork Cloud | Hosted Cloud web origin. The desktop uses one base URL and derives Den API/MCP calls through `/api/den/...`. | Cloud sign-in, org settings, Collections, and remote workspace flows cannot load. |
| `api.app.openworklabs.com` | Required for hosted install and OpenWork Connect | Hosted public API and OpenWork Connect MCP endpoint used by install and external-client flows. | Hosted install checks and public OpenWork Connect fail. |
| `app.openwork.software`, `api.openwork.software` | Required only for legacy hosted configurations | Older desktop configurations may still derive Cloud and OpenWork Connect traffic from these legacy hosted origins. | Legacy configurations cannot reconcile OpenWork Connect or native MCP Apps until moved to the current hosted origins. |
| Your self-hosted Den web origin | Required for self-hosted desktop | Replaces `app.openworklabs.com` for signed-in desktop traffic. In the single-origin shape, API and MCP use `/api/den/...` on the same origin. | The desktop cannot sign in or reach Den. |
| Your separate Den API origin, if configured | Required only for that topology | Used by one-time install-link exchange and external MCP clients when `DEN_API_PUBLIC_URL` points at a separate origin. | Install-link handoff or external MCP clients cannot complete. |
| `github.com` | Required in practice unless mirrored | Installer/updater release URLs, GitHub issue links, and GitHub plugin source URLs. | Install/update downloads cannot start; GitHub links may not open. |
| `release-assets.githubusercontent.com` | Required in practice unless mirrored | Current GitHub release-asset redirect target. | Downloads start from `github.com` and fail partway through. |
| `objects.githubusercontent.com` | Required in practice for legacy/rollback | Legacy GitHub release-asset redirect target. | Older clients or rollback downloads can fail partway through. |
| `registry.npmjs.org` | Required in practice unless npm is mirrored | `npx` package resolution for `openwork-ui-mcp` and development-only helper commands. | OpenWork UI-control MCP is unavailable. |
| `models.openworklabs.com` | Required in practice unless mirrored | Default OpenCode model catalog passed as `OPENCODE_MODELS_URL`. | Model selection can be stale, incomplete, or unavailable. |
| `cdn.simpleicons.org` | Optional | Provider icons when no local icon exists. | Some icons are missing. |
| `www.google.com` | Optional | Favicon image service and embedded browser new-tab page. | Favicons or the browser panel new tab may not load. |
| `us.i.posthog.com` | Optional | Product analytics when analytics are enabled. | Analytics are dropped silently. |
| `openworklabs.com`, `openwork.dev`, `discord.gg`, `ollama.com`, `opencode.ai`, `linear.app`, `mail.google.com` | Optional or link-only | Docs, feedback, community, provider help, local model links, sample links, and generated Gmail links. | Links do not open; core app behavior is unaffected. |
`code.claude.com` and `schemas.agentskills.io` appear in source or schema metadata. They are findable in the manifest, but OpenWork does not fetch them for normal runtime behavior.
## Den runtime destinations
Approve Den separately from desktop clients. A self-hosted Den behind VPN or inside Kubernetes uses the server network, not the user's laptop network.
| Destination | Requirement | Why it is reached | What breaks when blocked |
| --- | --- | --- | --- |
| Your MySQL-compatible database host | Required | Den control-plane state. | Den cannot start, migrate, sign users in, or store organization state. |
| Your SMTP host or Resend | Optional | Transactional email when configured. | Invites, lifecycle, or notification email cannot send. |
| `api.github.com`, `raw.githubusercontent.com` | Opt-in | GitHub-hosted plugin import and sync. | Plugin import cannot inspect repos or download manifests, skills, commands, agents, or MCP configs. |
| Google Workspace hosts | Opt-in | Google OAuth and APIs. | Google sign-in or Workspace actions fail. |
| Provider and MCP hosts | Opt-in | Calls to enabled model providers, OAuth/token endpoints, and MCP servers. | Only the enabled provider or MCP connection fails. |
Google Workspace hostnames in the manifest are `accounts.google.com`, `oauth2.googleapis.com`, `www.googleapis.com`, `gmail.googleapis.com`, and `chat.googleapis.com`.
## Deployment and image-pull destinations
| Host | Requirement | Why it is reached | What breaks when blocked |
| --- | --- | --- | --- |
| `ghcr.io` | Required unless mirrored | OpenWork Helm chart and container images. | Kubernetes cannot pull the chart or `openwork-den-api`, `openwork-den-web`, and optional `openwork-inference` images. |
| Internal registry mirror | Required when public GHCR is blocked | Your mirrored chart and images. | Installs and upgrades fail until the mirror has the requested version. |
For fully isolated deployments, also mirror installer artifacts, npm packages used by `npx`, and the model catalog. See [Air-gapped deployment](/start-here/air-gapped-deployment) and [Installer delivery](/start-here/installer-delivery).
## Opt-in provider and connection hostnames
These hosts are only needed when the corresponding feature, provider, connection, or import path is enabled:
| Feature group | Hostnames | What breaks when blocked |
| --- | --- | --- |
| OpenAI direct features | `api.openai.com` | Direct OpenAI Realtime voice and image-generation features fail for users who configured OpenAI. |
| Google Workspace | `accounts.google.com`, `oauth2.googleapis.com`, `www.googleapis.com`, `gmail.googleapis.com`, `chat.googleapis.com`, `mail.google.com` | OAuth, profile, Calendar, Drive, Gmail, Chat, and generated Gmail links fail as applicable. |
| Built-in MCP presets | `mcp.notion.com`, `mcp.linear.app`, `mcp.sentry.dev`, `mcp.stripe.com`, `mcp.context7.com`, `mcp.slack.com` | That specific MCP preset cannot connect. |
| Example or custom MCP URLs | `api.githubcopilot.com` or the customer-configured MCP URL | Only the connection that points at that URL fails. |
| GitHub plugin import | `api.github.com`, `raw.githubusercontent.com`, and optionally `github.com` links | GitHub plugin import cannot preview or download plugin contents. |
The JSON manifest remains the ownership point for exact host metadata, requirement level, override name, and blocked effect. Use this page for review conversations and the JSON file for automation.
@@ -0,0 +1,87 @@
---
title: "Pricing and licensing"
description: "OpenWork's public pricing — Free, Team, and Enterprise — and how the MIT + OpenWork EE License split works."
---
Our pricing is public, like the rest of our handbook. The same prices apply
whether you use OpenWork Cloud or self-host. There are no deployment fees, and
support is included in every paid plan.
## Plans
| | Free | Team | Enterprise |
| --- | --- | --- | --- |
| **Price** | $0 | $20 per seat / month | $50 per user / month |
| **Users** | Up to 5 | Up to 100 | Unlimited (volume pricing above 100 — [talk to sales](mailto:sales@openworklabs.com)) |
| **Desktop app (MIT, open source)** | ✓ | ✓ | ✓ |
| **Self-host the full platform** | ✓ | ✓ | ✓ |
| **Usage and adoption analytics** | — | — | ✓ |
| **SSO / SAML, SCIM, RBAC** | — | — | ✓ |
| **Desktop policies and version controls** | — | — | ✓ |
| **Internal white-labeling (your name, logo, branding)** | — | — | ✓ |
| **OpenWork Web** | — | — | ✓ |
| **Spend observability** | — | — | ✓ |
| **Support** | Community | Standard, included | Standard SLA, included |
Analytics, SSO/SCIM, desktop policies, and white-labeling are the
**Enterprise Features** — defined in the
[subscription terms](https://openworklabs.com/terms/subscription) and
reserved for the Enterprise plan (and for evaluations and pilots, so you can
test what you would buy).
Enterprise white-labeling covers your own internal deployment. External
distribution, managed service use, or resale under your branding requires a
separate written agreement.
Add-ons (each with a 30-day free trial) are listed at
[openworklabs.com/pricing](https://openworklabs.com/pricing).
## How licensing works
The [repository](https://github.com/different-ai/openwork) uses a
directory-split license, similar to GitLab:
- **Everything outside `ee/` is MIT.** The desktop app and core platform are
open source and free for any use.
- **Everything under `ee/` (OpenWork Den, the org control plane) is
source-available under the
[OpenWork EE License](https://github.com/different-ai/openwork/blob/dev/ee/LICENSE).**
The code is public so your security team can audit exactly what you deploy.
Under the EE License, no subscription is required for:
1. **Production use by organizations with up to 5 users** — this matches the
Free plan. Enterprise Features (analytics, SSO/SCIM, desktop policies,
white-labeling) are excluded and require a plan.
2. **Internal evaluation for up to 30 days**, at any user count, with all
features — extendable in writing.
3. **Development and testing.**
Beyond those grants, production use requires a subscription per the
[subscription terms](https://openworklabs.com/terms/subscription). Every
`ee/` release additionally converts to MIT two years after publication.
Versions released before the EE License was adopted remain under their
original license (FSL-1.1-MIT), and each release is governed by the license
text it shipped with — license updates are never retroactive.
For the promises we make about how this split is stewarded over time — what
can and cannot move between tiers, and how the MIT conversion works — see
[Stewardship](/start-here/stewardship).
## Evaluating and pilots
You can evaluate self-serve for free for 30 days. The [pull-only Docker Compose
stack](/self-host/evaluate-with-docker-compose) is the quickest way to deploy
the control plane in your own environment without cloning or building the
repository. Need longer? Evaluation extensions are granted in writing —
[ask us](mailto:sales@openworklabs.com). If you want a supported pilot
instead (a dedicated engineering contact, deployment guidance, and response
targets), [talk to sales](mailto:sales@openworklabs.com).
## Why the enterprise code is public
Our buyers are security teams. Publishing the enterprise source means you can
read every line that runs inside your network before you run it. What a
subscription pays for is the right to run it in production at scale, plus a
supported, patched release stream and a vendor who is contractually
accountable.
@@ -0,0 +1,72 @@
---
title: "Private network deployment"
description: "Run OpenWork when laptops have internet and VPN access while Den stays inside a private network."
---
Most enterprise OpenWork installs are not fully air-gapped. The common shape is:
- Employee laptops have normal internet access and VPN access.
- Den web and Den API run inside the customer's private network.
- The desktop is configured with one customer Den URL over the VPN.
This page is for IT and platform teams approving those two network paths. For full isolation terminology and checklist items, see [Air-gapped deployment](/start-here/air-gapped-deployment).
## What the desktop must reach
Point the desktop at one Den web origin. The signed-in desktop reads `${DEN_BASE_URL}/api/runtime-config` and uses its `denApiUrl` value as the source of truth for Den API and MCP traffic. Den Web returns `DEN_API_PUBLIC_URL` when it is configured; otherwise it derives the API origin as `api.${DEN_BASE_URL}`:
- API default: `https://api.<den-web-host>/v1/...`
- MCP default: `https://api.<den-web-host>/mcp/...`
Set `DEN_BASE_URL` to the Den web origin. `DEN_API_BASE` is the server-only URL Den Web uses to reach Den API, such as an in-cluster service URL; it is never advertised to browsers or desktops. If browsers, desktops, or external MCP clients require a separate externally reachable API origin, set `DEN_API_PUBLIC_URL`, for example `https://api.openwork.example.internal`. Use an origin without a path because the current web client normalizes this value to its origin.
If you publish a separate Den API origin, the one-time install-link exchange and external MCP clients must be able to reach that origin too.
External MCP clients such as Cursor, Claude Code, VS Code, ChatGPT, and OpenCode are separate from the desktop. They must reach the MCP endpoint you configure for them, usually `<DEN_API_PUBLIC_URL>/mcp/agent`. See [OpenWork Connect MCP](/cloud/run-in-the-cloud/cloud-mcp) for the external-client flow.
Unless you mirror or replace public dependencies internally, laptops may also need outbound access for installers, release assets, npm packages used by `npx`, and the OpenCode model catalog. Keep that allowlist in [Outbound network access](/start-here/outbound-network-access), and use [Installer delivery](/start-here/installer-delivery) when installer downloads must stay inside Den.
## What Den must reach
The hard requirements are small:
- The MySQL-compatible database endpoint configured for Den.
- The OpenWork chart and images at deploy or image-pull time, unless you mirror the Helm chart and enabled images into an internal registry.
Everything else is feature-specific and can be blocked until that feature is approved: Google Workspace, Microsoft 365, GitHub plugin import, transactional email, model providers, MCP presets, custom MCP servers, and OAuth/token endpoints. Keep the customer IT hostname inventory in [Outbound network access](/start-here/outbound-network-access).
If the private network uses TLS inspection, private roots, or an outbound proxy, configure the correct trust surface instead of setting browser-only values. See [Certificate trust and proxies](/start-here/certificate-trust-and-proxies).
## Three surprises for operators
### Internal MCP servers are refused
**Symptom:** adding or testing an MCP server on a private address fails with `MCP_URL_BLOCKED` and the operator action says: "Use a public HTTPS MCP URL or change the deployment's private-network policy through security review."
**Why:** Den fetches MCP URLs server-side. By default, it blocks private and reserved addresses as SSRF protection for multi-tenant deployments.
**Fix:** set `DEN_ALLOW_PRIVATE_MCP_URLS=1` on Den, or the matching Helm public config value. This disables that SSRF protection, so use it only when Den's network position is trusted and the people allowed to add MCP connections are trusted.
### The Cloud MCP diagnostic is skipped
**Symptom:** `Settings → Debug` reports that the live cloud catalog was not observed, and the detail says the endpoint is outside the diagnostics trust policy.
**Why:** the diagnostic only sends a credentialed probe to trusted origins. By default, it trusts the hosted OpenWork origins, not your self-hosted Den origin.
**Fix:** trust your Den origin before OpenWork launches. This affects the diagnostic only; Cloud MCP itself can still work in real chats without this variable, so do not treat a skipped probe as proof that the deployment is broken. See [Network diagnostics](/start-here/network-diagnostics) for exact syntax and launch-surface guidance.
### Den's outbound self-check points at a public host
**Symptom:** an admin-triggered Den outbound diagnostic cannot reach the default public diagnostic host from a VPN-isolated Den deployment.
**Why:** `DEN_DIAGNOSTICS_ORIGIN` defaults to the public OpenWork diagnostic service.
**Fix:** deploy the diagnostics app at an internally reachable supported runtime/origin, then point Den at it with `DEN_DIAGNOSTICS_ORIGIN` and `DEN_DIAGNOSTICS_BEARER_TOKEN`. Do not assume a generic Kubernetes package unless your deployment path has been validated. The check is optional and admin-triggered; it is not a background dependency for Den. See [Network diagnostics](/start-here/network-diagnostics).
## Verification checklist
| Chain | Success looks like | First place to look if it fails |
| --- | --- | --- |
| Installer activates against internal Den | The Den install page opens over VPN, the installer download completes, and **Open OpenWork** shows the expected organization and server before changing local configuration. | If the app opens, use `Settings → Debug` to confirm the Den web and API endpoints. If it never opens, return to the Den install page and check [Installer delivery](/start-here/installer-delivery). |
| Desktop signs in and reaches a ready workspace | The user signs in through the internal Den URL, sees the correct organization, and can open a ready workspace or start a chat. | `Settings → Debug` for server endpoints, sign-in state, proxy, and TLS/certificate clues. Use [Network diagnostics](/start-here/network-diagnostics) for desktop DNS/TLS/proxy evidence. |
| Cloud MCP works in a real chat | In a chat, the agent can search available OpenWork capabilities and execute one that the user is allowed to run. | `Settings → Connect` for provider sign-in or admin setup. Use `Settings → Debug` to interpret diagnostics, especially a skipped catalog probe. |
+152
View File
@@ -0,0 +1,152 @@
---
title: "Self-host"
description: "Run OpenWork on your own server or infrastructure."
---
Most users just use the [desktop app](https://openworklabs.com/download). Self-hosting is for teams that want OpenWork to run on their own server, VPC, or private cloud and then connect the desktop app to it remotely.
Want hosted workspaces, team Collections, or shared providers without running infra? Use [OpenWork Cloud](/cloud/get-started) instead. Rolling this out across a company? [Book a call](https://calendar.app.google/86QpCENvhfEzDFLu5) about [Enterprise](/cloud/enterprise).
<Note>
Want to try the self-hosted control plane before planning production infrastructure? Start the [pull-only Docker Compose evaluation stack](/self-host/evaluate-with-docker-compose). It uses published images and does not require cloning or building the repository.
</Note>
## What you deploy
OpenWork is split into normal services:
- **OpenWork app**: the desktop or web client people use.
- **Den web**: the web app for sign-in, worker launch, and connect links if you want a cloud-style setup.
- **Den controller**: the control plane for auth, workers, and provisioning.
- **Inference service**: optional OpenWork Models proxy and metering service.
If you only need one private remote workspace, deploy the worker runtime. If you want OpenWork Cloud-style accounts, teams, and organization management, deploy Den web, Den controller, and optionally the inference service.
For Kubernetes installs, use the published Helm chart:
```bash
helm upgrade --install openwork-ee oci://ghcr.io/different-ai/charts/openwork-ee \
--version <chart-version> \
-f values.prod.yaml
```
The chart pulls the matching GHCR images for `openwork-den-api`, `openwork-den-web`, and optional `openwork-inference` unless you mirror them into an internal registry.
Choose a tested provider path in [Deploy to your cloud](/self-host/deploy-to-your-cloud/overview). The public guides cover AWS, Azure, and Google Cloud, including managed MySQL, ingress, DNS, TLS, migrations, and secure creation of the first administrator.
## Plan the network
Use the dedicated Self-host planning pages instead of copying hostname, certificate, proxy, and diagnostics details into this overview:
- [Private network deployment](/start-here/private-network-deployment): the common topology where laptops have internet plus VPN access and Den stays private.
- [Air-gapped deployment](/start-here/air-gapped-deployment): definitions and the checklist for full isolation.
- [Outbound network access](/start-here/outbound-network-access): the canonical customer IT destination inventory and "what breaks when blocked" guidance.
- [Certificate trust and proxies](/start-here/certificate-trust-and-proxies): desktop, sidecar, Den, and MySQL trust surfaces.
- [Enterprise desktop deployment](/start-here/enterprise-desktop-deployment): the enterprise binary, MDM distribution, and the `desktop-bootstrap.json` reference.
- [Installer delivery](/start-here/installer-delivery): GitHub redirect and mounted artifacts for organization install links.
- [Network diagnostics](/start-here/network-diagnostics): Windows doctor, Cloud catalog diagnostic trust, and Den outbound checks.
If you want self-hosted repository imports and sync from GitHub, configure the [GitHub connector for Helm](/start-here/github-connector-helm) after the core Den web and Den controller hosts are reachable.
To keep organization names, wordmarks, and desktop icons inside your deployment, follow [Brand an on-prem deployment](/start-here/on-prem-branding).
## How we deploy hosted workers
Our hosted deployment uses Render for long-running services and worker provisioning. Render gives us service deploys, env vars, health checks, logs, and worker builds without asking us to operate raw AWS services directly. If you want to run closer to AWS yourself, use the same service boundaries on ECS/Fargate, EC2, Kubernetes, or another container platform.
In production, hosted OpenWork runs the Cloud app and Den controller as scaled Render services with at least two instances. Render load balances traffic across healthy instances, removes failing instances from routing, and restarts failed instances. Den controller application instances are stateless replicas; shared control-plane state is stored in PlanetScale.
The production shape is straightforward:
- Put HTTPS in front of every public service.
- Expose the OpenWork server/worker URL, not raw opencode.
- Keep workspace and data paths on durable storage.
- Configure `DEN_BASE_URL`, tokens, and provider credentials with env vars. Den derives the Better Auth URL, CORS/trusted origins, API defaults, MCP resource defaults, and web-app host allowlist from `DEN_BASE_URL` unless you set compatibility overrides for a split-origin deployment.
- Run Den database migrations before the Den controller receives traffic.
For containerized services, start from the [Docker packaging](https://github.com/different-ai/openwork/tree/dev/packaging/docker) in the repo.
## Databases and storage
- Worker runtimes use filesystem state and opencode SQLite data inside the mounted workspace/data paths.
- The Den control plane uses a MySQL-compatible database. Local Docker uses MySQL 8.4; production can use standard MySQL or PlanetScale-compatible credentials.
- Hosted OpenWork uses PlanetScale for Den control-plane state. PlanetScale production clusters provide one primary and at least two replicas across three availability zones, managed failover, and automated backups.
- Postgres is not required by the current Den deployment path.
For production, configure encryption explicitly:
- Enable encryption at rest for the MySQL-compatible database, database backups, object storage, worker volumes, and logs that may contain operational metadata.
- Require TLS for application-to-database traffic. `sslmode=require` encrypts the connection but is not certificate identity verification. For standard MySQL with certificate verification, use `sslmode=verify-ca`, `sslmode=verify-full`, or a strict equivalent such as `sslaccept=strict`, and provide the CA bundle through the database platform or Helm `customCa`.
- Keep the local Docker MySQL URL (`mysql://root:password@127.0.0.1:3306/openwork_den`) for development only; it is not a production security posture.
- Set a unique `DEN_DB_ENCRYPTION_KEY` of at least 32 characters. OpenWork uses it to encrypt selected sensitive database columns, but it does not replace infrastructure-level encryption at rest.
For a self-hosted production deployment, use a managed MySQL-compatible database with automated failover and backups, or provide equivalent redundancy yourself. Document the restore process, backup retention, recovery time objective, recovery point objective, and the person or team responsible for failover and restore.
## Auth and SSO
Den uses Better Auth. It runs in your deployment, uses your database, and is configured with your `BETTER_AUTH_SECRET`, `DEN_BASE_URL`, and optional GitHub/Google OAuth credentials. `BETTER_AUTH_URL`, trusted origins, CORS origins, and web-app hosts are derived from `DEN_BASE_URL`; set the older variables only when you are intentionally migrating or running a split-origin topology.
Private deployments should keep public signup disabled and use the one-time [first-administrator setup flow](/self-host/deploy-to-your-cloud/first-administrator). Configuring an owner or platform-admin email does not create an account or password, and OpenWork does not ship a default administrator password.
SSO (SAML/OIDC) is in the current rollout and is designed to run against your own identity provider. When enabled, callback URLs and trusted origins should point at your self-hosted Den web/controller hosts.
For isolated Kubernetes deployments without approved outbound access to the Have I Been Pwned Pwned Passwords range API, the Helm chart disables external breached-password lookup by default. Email/password sign-in lockout remains local. If your deployment approves that external lookup, enable it in Helm. See [Air-gapped deployment](/start-here/air-gapped-deployment) for the broader isolation checklist.
If you do not deploy Den, worker access is token-based with `OPENWORK_TOKEN` and `OPENWORK_HOST_TOKEN`.
## Minimal working stack
### OpenWork Cloud and the desktop app
OpenWork cloud-style self-hosting is composed of three main pieces:
- Next.js frontend
- Node.js backend (Hono) with MySQL DB
- Electron/React desktop app
The desktop app should be pointed at the Den web URL. On startup it reads `${DEN_BASE_URL}/api/runtime-config` from Den Web and uses the returned `denApiUrl` as the source of truth for Den API and MCP traffic. Den Web returns `DEN_API_BASE` when you set it; otherwise it deterministically derives the API origin as `api.${DEN_BASE_URL}`.
- API default: `https://api.<den-web-host>/v1/...`
- MCP default: `https://api.<den-web-host>/mcp/...`
For self-hosted deployments, configure the desktop `baseUrl` to the public Den web origin and make sure `${DEN_BASE_URL}/api/runtime-config` is reachable from member desktops. If the API is not available at `api.${DEN_BASE_URL}`, set `DEN_API_BASE` on Den Web to the externally reachable API origin or proxy path you want desktops to use.
These three pieces cover most features, but do not include:
- Remote code execution through sandboxes
- Analytics
- Telemetry through OpenTelemetry
- Transactional email for invitations and lifecycle messages
## Optional vendors
OpenWork does not require many third-party services for the core runtime. These are optional or deployment-specific:
- Render: our current hosted service and worker deployment path.
- Daytona: optional sandbox provider for cloud workers.
- Vercel: optional Den web hosting and worker DNS automation.
- PostHog: optional analytics for public web surfaces. You can point it at self-hosted PostHog with `NEXT_PUBLIC_POSTHOG_HOST`, or remove/clear the key in your self-hosted web build if you do not want analytics.
- Polar: optional billing/paywall for hosted cloud workers.
- Loops: optional user sync and lifecycle messaging.
- GitHub/Google OAuth: optional social sign-in providers for Better Auth.
- GitHub connector app: optional repository connector for importing and syncing plugins or marketplaces from GitHub. For Helm installs, use the [GitHub connector setup guide](/start-here/github-connector-helm).
## Don't want to run infra?
Skip the server. [**OpenWork Cloud**](/cloud/get-started) runs hosted workers for your team.
Cloud also gives you org-wide primitives you'd otherwise have to build: [Collections](/cloud/share-with-your-team/collections), [shared LLM providers](/cloud/share-with-your-team/managed-llm-provider), [RBAC](/cloud/members-and-rbac), and [shared workspaces](/cloud/run-in-the-cloud/shared-workspace).
## Looking for support for a larger rollout?
If you're rolling OpenWork out across a company of 150\+ employees, a single self-hosted server VM might be harder to manage.
At that scale our customers typically want:
- **SSO (SAML/OIDC)** so people sign in with the identity your company already uses
- **Audit logs** across sessions, tool calls, and model usage
- **Model and tool allowlists** enforced centrally, not per-workspace
- **VPC / on-prem deployment** with your own networking and compliance constraints
- **Support and rollout help** from our team and custom deployment, not just in GitHub.
You can check the [**Enterprise**](/cloud/enterprise) page or [book a 30-minute call](https://calendar.app.google/86QpCENvhfEzDFLu5).
+60
View File
@@ -0,0 +1,60 @@
---
title: "Stewardship"
description: "The promises behind OpenWork's open-core split: what stays open, what can move between tiers, and how the MIT conversion works."
---
OpenWork is an open-core product: the desktop app and core platform are MIT,
and the `ee/` directory (OpenWork Den, the org control plane) is
source-available under the
[OpenWork EE License](https://github.com/different-ai/openwork/blob/dev/ee/LICENSE).
That split only works if you can trust how we steward it, so we make these
promises in public.
## Our promises
1. **The MIT core stays MIT.** We do not relicense code that has shipped under
MIT. Features do not move from the MIT core into `ee/` retroactively — a
feature you already have under MIT is yours under MIT.
2. **Released versions keep their license.** Every release is governed by the
license text it shipped with. License changes apply to future releases
only, never to code you already deployed.
3. **Every EE release becomes MIT after two years.** The EE License contains
an irrevocable grant: each version of the `ee/` code converts to MIT on
the second anniversary of its public release. The grant is written into
the license itself and requires no future action or approval from us —
your long-term rights to the software never depend on anyone's roadmap,
including ours.
4. **The free grants are floor commitments.** Free production use for
organizations of up to 5 users (core features), free 30-day evaluation at
any scale with all features (extendable in writing), and free development
and testing use are commitments we intend to keep. If the boundaries ever
change, the change applies to future releases only (see promise 2).
5. **Paid tiers do not shrink mid-term.** If we move a capability between
tiers, the change is prospective and never removes a feature from your
current paid plan during a paid term.
6. **The enterprise source stays readable.** We publish the `ee/` source so
security teams can audit exactly what they deploy, before they deploy it.
## Trademarks and white-labeling
The OpenWork name, logo, and product names are trademarks of Different AI,
Inc. The EE License and MIT license grant rights to the *code*, not the
brand:
- **Allowed without permission:** factually identifying that you use or built
on OpenWork ("powered by OpenWork", "built with OpenWork"), unmodified use
of the app with its own branding, and nominative references in
documentation, reviews, and talks.
- **Requires an Enterprise plan (internal white-labeling):** replacing OpenWork
product names, logos, or accent branding with your own inside the deployed
product — for example a custom app name, logo, or brand color for your
organization's workspace.
- **Requires a separate written agreement (external resale):** distributing,
operating as a managed service, or reselling a white-labeled deployment to
third parties under your branding.
- **Never allowed:** implying that Different AI, Inc. endorses your product
or service, using the marks in a confusingly similar product name, or
removing copyright and license notices from the source.
Questions about a use case the above does not cover:
[sales@openworklabs.com](mailto:sales@openworklabs.com).
@@ -0,0 +1,160 @@
---
title: "Diagnostic prompts"
description: "Paste-able prompts that let your agent debug OpenWork issues on its own."
---
When something in OpenWork looks wrong, the fastest debugger is usually the agent itself: it can read its own configuration, probe servers, and verify fixes in-band. This page collects paste-able diagnostic prompts. Copy the whole block for your situation into a chat and let the agent work through it.
Each prompt is safe by default: it gathers evidence first, redacts secrets, and asks before changing anything.
If the symptom is DNS, TCP 443, TLS chain, proxy, or Den outbound reachability, start with [Network diagnostics](/start-here/network-diagnostics) instead.
## 1. Cloud search shows old or missing capabilities
**Symptoms**
- `search_capabilities` returns fewer tools than you expect (for example, only `postCapabilitiesGoogleWorkspaceGmailDrafts` when newer Google Workspace capabilities should exist).
- `execute_capability` returns `unknown_capability` for a tool name that should exist.
- The cloud dashboard says a connection is "Connected as you", but the agent reports `connected: false`.
**What is usually wrong**
Your agent's `openwork-cloud` MCP entry is a snapshot: it stores a server URL and token from the moment it was connected. If it was created against a local development server or an old deployment — including via a leftover `desktop-bootstrap.json` file that overrides server URLs beneath the Settings UI — the agent keeps querying that old world forever, while the dashboard shows the current one.
**Paste this to your agent**
````text
Debug and fix my OpenWork Cloud MCP connection. It may be pointed at a stale or local server instead of my real OpenWork Cloud server. Work through ALL phases, show your findings after each, and ask before anything destructive. Never print bearer tokens or Authorization headers — redact them.
My expected cloud web URL: https://app.openworklabs.com (replace with your self-hosted Den web URL if you run your own).
## Phase 1 — Evidence: what am I actually connected to?
1. Print the `openwork-cloud` MCP entry's `url` (redact headers) from every config you can find:
- `~/.config/opencode/opencode.json`
- any `.opencode/opencode.json` or `opencode.jsonc` in the current project
- your own runtime config if you have an export tool
2. Inspect these files if they exist (they can silently override server URLs beneath the Settings UI), but report only `baseUrl`, `apiBaseUrl`, `requireSignin`, and `writtenAt`. Never print `handoff`, `claimLinks`, grants, tokens, or other credential-bearing fields:
- `~/.config/openwork/desktop-bootstrap.json`
- `~/Library/Application Support/com.differentai.openwork/desktop-bootstrap.json`
3. If the url from step 1 is a localhost address, find what serves that port:
- `lsof -nP -iTCP:<port> -sTCP:LISTEN`
- for each PID: `ps -p <PID> -o pid=,command=` and `lsof -a -p <PID> -d cwd -Fn | tail -1`
## Phase 2 — Probe both servers directly (no auth needed)
4. The server from Phase 1, through the Den web proxy:
`curl -s -m 5 <that-origin>/api/den/openapi.json | python3 -c "import json,sys; d=json.load(sys.stdin); ps=[p for p in d.get('paths',{}) if 'google-workspace' in p]; print(len(ps)); [print(p) for p in ps]"`
5. The expected cloud web URL, same command. Compare the two lists.
## Phase 3 — Probe through your own MCP tools (in-band ground truth)
6. Call search_capabilities with {"query":"google workspace","limit":10} and report the match names.
7. Call execute_capability with {"name":"getCapabilitiesGoogleWorkspaceCalendarEvents"} and report the exact error or result shape.
## Phase 4 — Diagnose
State which world you are in:
- openwork-cloud url is localhost/127.0.0.1, OR search returned only gmail-drafts, OR execute returned unknown_capability → you are bound to a STALE server, not the expected cloud.
- a desktop-bootstrap.json contains localhost URLs → that file is the root cause (it is the desktop app's Cloud URL source of truth).
## Phase 5 — Fix (confirm with me before each destructive step)
8. If a desktop-bootstrap.json contains stale/localhost URLs: show it, then delete that file.
9. If a stale local server process is still listening: show the process, then stop it, so nothing can silently serve old answers again.
10. Tell me to do these steps in OpenWork, then wait:
- Restart the OpenWork app.
- In the affected workspace, open Settings → Library and click Refresh (re-mints the token and rewrites the Cloud MCP config).
- Click Show hidden, then verify OpenWork Cloud Control under Your apps.
- Run Reload OpenCode config from the command palette.
- Start a NEW task so it receives the current tool set.
## Phase 6 — Verify (run in the NEW session)
11. search_capabilities {"query":"google workspace","limit":10} → PASS = the full current capability set (calendar, drive, and gmail capabilities ranked on top), not just drafts.
12. execute_capability {"name":"getCapabilitiesGoogleWorkspaceCalendarEvents","query":{"timeMin":"<today ISO>","timeMax":"<+3 days ISO>"}} → PASS = real events, or a clean needs_connection / missing-permission message — NOT unknown_capability.
13. Print a final report: root cause, what changed, and the before/after of probes 6/7 vs 11/12.
````
**Expected passing state**
- The `openwork-cloud` entry's `url` points at your real server and ends in `/mcp/agent` (either a direct API host or the web app's `/api/den` proxy).
- `search_capabilities` reflects your server's current catalog.
- `execute_capability` on a known capability returns data or an actionable connection message — never `unknown_capability`.
If the agent completes all phases and verification still fails, [open an issue](https://github.com/different-ai/openwork/issues) and paste the agent's final report (it is already redacted).
## 2. Cloud MCP is configured, but its tools are missing
**Symptoms**
- Settings shows an `openwork-cloud` MCP entry, but the agent calls `openwork_docs_search` for requests involving connected services.
- The agent claims that `search_capabilities` and `execute_capability` are only "conceptual capabilities" rather than callable tools.
- A connected service such as ServiceNow appears ready in OpenWork Cloud, but the task does not expose `openwork-cloud_search_capabilities` or `openwork-cloud_execute_capability`.
**What this diagnostic checks**
The prompt below runs OpenWork's bundled OpenCode executable against its generated runtime config and checks whether a fresh OpenCode process can connect to `openwork-cloud`. It does not attach to the OpenCode server already running inside OpenWork, so it combines that result with the live status shown in OpenWork and a forced tool call from a new task.
**Paste this to your agent**
````text
Diagnose whether OpenWork Cloud MCP tools are available in this workspace. Gather evidence only: do not modify configuration, authenticate, log out, remove anything, restart services, or print configuration-file contents.
## Phase 1 — Test the task's actual tools
1. Attempt to call `openwork-cloud_search_capabilities` with `{"query":"ServiceNow incident","type":"mcp","limit":10}`. Do not use documentation or explain alternatives. Record whether the runtime accepts the tool call and its exact non-secret result or rejection.
2. Attempt to call `openwork_extension_list_actions`. Record whether the runtime accepts the call. Do not infer tool availability from an answer to "list your tools"; models can omit or invent tools.
## Phase 2 — Find OpenWork's exact OpenCode runtime
3. Detect whether this machine is Windows, macOS, or Linux.
4. Find the exact OpenCode executable used by the OpenWork desktop app. Prefer the bundled executable in OpenWork's `resources/sidecars` directory; do not assume an unrelated `opencode` on `PATH` is the same binary. If multiple OpenWork builds are running, use the process associated with this task when that can be established safely; otherwise report the ambiguity.
5. Find OpenWork's generated `runtime-opencode-config.json` beside the active `server.json`. Respect discovered `OPENWORK_SERVER_CONFIG`, `OPENWORK_RUNTIME_DB`, or XDG overrides. Typical locations are:
- Windows: `%APPDATA%\openwork\runtime-opencode-config.json`
- macOS/Linux: `${XDG_CONFIG_HOME:-$HOME/.config}/openwork/runtime-opencode-config.json`
6. Report whether the runtime config exists and its last-modified time. Never display, copy, parse, or summarize its contents: it can contain Authorization headers and other credentials.
## Phase 3 — Run a safe fresh-process probe
7. From this task's workspace root, set `OPENCODE_CONFIG` to the discovered runtime config for the subprocess only, then run:
- `<exact-opencode-executable> --version`
- `<exact-opencode-executable> mcp list`
8. Do not run `mcp add`, `mcp auth`, `mcp logout`, `debug config`, or any command that changes or prints configuration. Do not use `--print-logs` if it could expose headers or tokens.
9. Report:
- operating system
- exact OpenCode executable path and version
- runtime config path, existence, and last-modified time
- whether `openwork-cloud` is listed
- its exact status and non-secret error
- whether the failing workspace is the primary managed workspace or may be a secondary workspace configured dynamically
Important: `opencode mcp list` starts a fresh process. Do not claim that it attaches to or tests the OpenCode server already running inside OpenWork. Describe it only as a fresh-process probe using the same binary and generated config.
## Phase 4 — Classify the result
- Fresh probe omits `openwork-cloud`: the generated config is missing, the wrong runtime config was selected, or this is a secondary workspace whose MCPs are registered dynamically.
- Fresh probe reports `failed` or `needs_auth`: preserve the exact non-secret error; investigate token, endpoint, TLS, or authentication.
- Fresh probe reports `connected`, but Phase 1 rejects the Cloud tool: the stored endpoint and token work, but the live desktop engine or task has stale/missing tool registration.
- Both Phase 1 calls are rejected: investigate whether the running engine loaded the injected config and plugins, plus project/global tool-deny policy.
- The forced Cloud call works, but a natural request chooses documentation: tool exposure is healthy; investigate system-prompt steering.
Finish with a concise redacted report. Never reveal bearer tokens, Authorization values, cookies, passwords, API keys, local server tokens, or strings beginning with `ow_mcp_at_`.
````
**Verify inside OpenWork**
1. In the affected workspace, open **Settings → Library** and click **Refresh**.
2. Click **Show hidden**, then find **OpenWork Cloud Control** under **Your apps**. This row shows live OpenCode MCP status; the catalog card only proves that the entry is configured.
3. Expand the row and retain its status and non-secret error: **Ready**, **Issue**, **Sign in needed**, **Offline**, or **Paused**.
4. Return to the task, open the command palette, run **Reload OpenCode config**, and create a new task in the same workspace.
5. Repeat the forced Cloud tool call from Phase 1.
**How to interpret both results**
| Fresh `opencode mcp list` | OpenWork status / forced call | Likely boundary |
| --- | --- | --- |
| Missing or failed | Missing or failed | Generated config, endpoint, token, TLS, or authentication |
| Connected | In-app status failed | Live engine registration or workspace routing |
| Connected | In-app Ready, forced call rejected | Stale task tool snapshot or engine compatibility |
| Connected | Forced call succeeds, natural request uses docs | Agent steering rather than MCP exposure |