Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 71 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,77 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged.

## Unreleased

### Desktop setup shows progress and works around occupied local ports

Downloads show transferred bytes and elapsed time, Back preserves saved connections, and repair
stays under Installation options after setup. OpenBot selects and remembers usable local ports,
including when Windows reserves a default port. Setup can create a CopilotKit project and offers
Google Gemini and xAI API-key choices, plus OAuth sign-in with automatic token refresh. Google
OAuth requires the distributor's desktop client and quota-project configuration. Unsupported Bun
installations are replaced with the pinned runtime; startup errors retain useful details and provide a configurable setup-help link.
Docker image downloads can find the credential helper bundled beside Docker even when it is
missing from the desktop app's PATH. Existing Docker credentials and helper preferences are preserved.
### A model provider's own sign-in can stand in for an API key

`OPENBOT_MODEL_OAUTH_FILE` names a credential file holding a Google or xAI OAuth grant. Set it and
the server mounts `POST /api/model-provider/v1/chat/completions`, which answers an ordinary Chat
Completions request using that grant, refreshing the access token 120 seconds before it expires and
again on a provider 401, and persisting the rotation under a lock the writer and the desktop share.
Leave it unset, which is every deployment that does not set it, and the route does not exist.

The caller authenticates with a separate local bearer taken from that file, compared in constant
time, never with the provider's own token: a refresh token never leaves the server process. The file
itself is refused unless it is a regular file under 64KB owned readable by nobody else, and a Google
grant must name a quota project. The upstream host, path and headers are fixed, so nothing a caller
sends can redirect the request.

Google is reached through its native generation API rather than a compatibility endpoint, with a
translation layer that carries streaming, tool calls and their results, inline images and
function-call thought signatures across in both directions. An API-key connection is unchanged and
still uses the compatibility endpoint.

**A provider 403 no longer reads as an expired sign-in.** It usually means a missing project or
resource permission, which signing in again cannot fix, so only a 401 now raises "sign in again".

### Desktop setup shows progress, chooses its own local ports, and can sign in to a provider

Downloads report transferred bytes, every running step reports elapsed time, and running, completed
and failed stages are told apart. Back preserves the connections already entered.

OpenBot now chooses its local ports by binding them rather than assuming them, holds them until the
whole set is settled, and writes them to the deployment's `.env` so a restart keeps the same
addresses. That covers a port another program holds and a port Windows has reserved, neither of
which the old fixed defaults survived. A port that becomes unavailable between choosing and starting
now says so and asks for another Start, where it previously refused before trying.

Setup can create a CopilotKit project, and offers Google Gemini and xAI as API-key choices alongside
OAuth sign-in. Google sign-in requires this build's own registered desktop client and quota project,
set at build time or by environment variable, and refuses with a message saying so when it has
neither; xAI falls back to a public client and works in any build. `desktop/PROVIDER_OAUTH.md`
describes what a distributor configures.

A deployment left behind by an earlier installation can be detected and reset from the app, which
removes that deployment's database volume and nothing else.

Startup failures keep enough of the log to name the cause, with every secret value redacted, and
carry a support link a whitelabel build can point elsewhere.

### A Bot's image pull finds the Docker credential helper beside Docker

A Docker install whose credential helper sits next to the `docker` binary rather than on the desktop
app's own PATH failed the pull with a PATH error naming the helper. The directory holding the
resolved `docker`, and the directory holding what it points at when it is a symlink, are now appended
to the PATH the engine is invoked with. Appended, so an existing helper still wins, and the inherited
PATH is now kept rather than replaced, which it was not before.

### OpenBot starts only on the Bun it pins

An installed or cached Bun that is not the pinned version is no longer accepted, on install and on
every start, and OpenBot acquires its own copy instead. The version already on the machine is left
exactly as it is and simply not used.

### Organization sign-in survives a callback that arrives in pieces

The loopback listener that receives an organization or provider sign-in read the callback once and
gave up if the whole request had not arrived, and on Windows the accepted socket inherited the
listener's non-blocking mode, so a timeout did not apply. A good sign-in could be answered "Sign-in
did not match". Both paths now read until the request line is complete, with a real timeout.

### Compose file lists separate correctly on Windows

The separator between Compose files fell back to `:` everywhere, which is right on macOS and Linux
and wrong on Windows, where a drive letter contains one. It now follows the platform. This is
reachable on every platform now that a port overlay is passed, where before it was macOS only.

## 0.0.14

Expand Down
27 changes: 27 additions & 0 deletions charts/openbot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,33 @@ install rather than found as a browser that fails on every page.

## Upgrades

### Coming from a release before 0.0.14: a computer now runs as uid 1001

`computers.podSecurityContext` runs a Bot's computer as `pwuser`, uid 1001, in both `shared` and
`sandbox` mode. It was root before. The volumes an existing release created are root-owned, so
something has to hand them over, and that something is `fsGroup`: the kubelet takes ownership of a
volume's contents on mount.

**It does that only where the volume plugin says it can.** The EBS, PD and Azure Disk CSI drivers
do. `hostPath` does not, and `hostPath` is what rancher/local-path-provisioner hands out, which is
the default StorageClass on k3s. There the computer starts as 1001, finds a directory it cannot
write, and refuses to start rather than coming up healthy with a browser profile Chromium silently
replaced. The message names this. Two ways out:

```sh
# Either hand the existing directories to 1001 on the node, or go back to root:
helm upgrade openbot ./charts/openbot --set computers.podSecurityContext=null
```

**`--reuse-values` will not pick this up.** A new key is not in the old release's values, so an
upgrade run that way keeps running as root and says nothing. Pass the value, or drop the flag.

`fsGroupChangePolicy: OnRootMismatch` is deliberate. Unset means `Always`, which walks every file on
every mount, and a real Chromium profile is tens of thousands of small ones; in `sandbox` mode that
pass would run again on every resume from idle.

### How a rollout is sequenced

Migrations run as a `pre-install,pre-upgrade` Job, so no replica ever serves in front of a schema it
has not seen. An init container would mean every replica racing to migrate the same database.

Expand Down
5 changes: 5 additions & 0 deletions desktop/PROVIDER_OAUTH.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ apply.
References: [xAI's OpenCode integration](https://x.ai/news/grok-opencode) and
[xAI OAuth discovery](https://auth.x.ai/.well-known/openid-configuration).

## Other build-time settings

`VITE_OPENBOT_SUPPORT_URL` replaces the support link a startup failure offers. Unset, it points at
this repository's issues. A whitelabel build points it at its own support channel.

## Credential lifecycle

The desktop's provider credentials remain in its local deployment; React does
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ Start with the root [README](../README.md), then use these references:
- [Kubernetes](../charts/openbot/README.md): the Helm chart, what a cluster needs before it, and the values that differ per cloud.
- [Releasing](releasing.md): how a release is proposed, reviewed and published.
- [Windows desktop signing](windows-signing.md): protected Azure Key Vault signing and verification of the app and NSIS installer.
- [Desktop provider OAuth](../desktop/PROVIDER_OAUTH.md): what a distributor configures to offer Google and xAI sign-in in a desktop build.
- [Desktop telemetry](../desktop/TELEMETRY.md): every event the desktop app sends, and what it carries.

Do not include credential values, customer data, transcripts, or local-only notes in public docs.
1 change: 1 addition & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,5 +346,6 @@ Connector credentials are stored through the credential vault and referenced by
- `KEY_ENCRYPTION_KEY` must be a base64-encoded 32-byte value. The example key is refused with `NODE_ENV=production`.
- Credential plaintext is encrypted at rest, never returned by APIs, and redacted from audit events.
- Browser navigation allows `http` and `https`; cloud metadata addresses are refused under every configuration.
- `POST /api/model-provider/v1/chat/completions` exists only when `OPENBOT_MODEL_OAUTH_FILE` names a model credential file, which the desktop app sets and nothing else does. It answers a Chat Completions request using a stored Google or xAI OAuth grant. It does not use the session guard: the caller presents a separate local bearer taken from that file and compared in constant time, and the provider's own refresh token never leaves the server. The credential file is refused unless it is a regular file under 64KB with no group or other permission bits, and the upstream host, path and headers are fixed so a caller cannot redirect the request.
- `AGENT_COMPUTER_ALLOW_PRIVATE_HOSTS=true` is for local development only, and a deployment running with `NODE_ENV=production` refuses to start while it is set.
- Computer tokens and supervisor tokens must be long random values outside local development.
26 changes: 26 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ at `agent-langgraph` on a laptop.
| `AUDIT_RETENTION_DAYS` | unset | Whole number of days to keep audit rows; older ones are removed. Unset keeps the trail forever. |
| `WORKER_SHARED_SECRET` | unset; `start.sh` uses a fixed local default | The secret the routines worker presents to fire a due routine. Without it the server refuses every handoff, whether or not a worker exists to send one. |
| `OPENBOT_GENERATIVE_UI` | unset (capability on) | Set `false` or `0` to stop Bots from answering with generated interfaces. |
| `OPENBOT_ACCESSIBILITY_DISABLED` | `true` or `1` stops naming OpenBot on the analytics the runtime already sends. |
| `COMPOSIO_API_KEY` | unset | One key for the whole deployment, for the broker that holds people's accounts for a few hundred apps. Unset, there is nothing to connect, nothing to grant and no Composio tool for a Bot to call; what remains is one row that goes nowhere, under **More apps** on the admin Plugins page, naming this variable. See [Composio](plugins/composio.md). |

**`OPENBOT_GENERATIVE_UI`** enables generated interfaces by default: streamed HTML/CSS/JavaScript
Expand Down Expand Up @@ -179,6 +180,15 @@ Two things are worth knowing before pointing a deployment at any gateway. Not ev
| `INITIAL_ADMIN_EMAILS` | Comma-separated administrators. **Required** with any provider. |
| `OPENBOT_PUBLIC_URL` | Public address of this API. Defaults to `BETTER_AUTH_URL`. |
| `OPENBOT_APP_URL` | Where the browser app is served. Defaults to the first `TRUSTED_ORIGINS` entry. |
| `SIGNIN_ALLOWED_EMAIL_DOMAINS` | Comma-separated email domains admitted at sign-in. Empty means no opinion. Exact, no wildcards. |
| `OPENBOT_ORGANIZATION_AUTH_URL` | An OpenBot deployment that verifies employee identity and roles. Decides sign-in ahead of `OPENBOT_SINGLE_USER`. |

**Some variables belong to the desktop app, not to you.** A desktop installation writes these into
its own deployment's `.env` and owns their values: `OPENBOT_MODEL_OAUTH_FILE`, `CHATGPT_AUTH_FILE`
and `CLAUDE_CODE_OAUTH_TOKEN`. When a model provider is connected by OAuth rather than by key, the
desktop also points `OPENAI_BASE_URL` at OpenBot's own loopback route and sets `OPENAI_API_KEY` to a
local proxy credential rather than a provider key, so those two do not mean what the table above says
in that mode. A server you configure yourself is unaffected by all of this.

**With no provider at all, `OPENBOT_SINGLE_USER=true` is required.** A deployment that configures
nothing to sign anybody in and does not say that was deliberate refuses to start, naming what to
Expand All @@ -194,6 +204,22 @@ address and a `.local` name are what this flag is mostly used for, and anybody o
the administrator. A value that cannot be parsed as a URL counts as public, because nobody checked
it.

**`SIGNIN_ALLOWED_EMAIL_DOMAINS` decides who may sign in**, as distinct from who is an
administrator once in. Matching is exact with no wildcards, so `example.com` admits neither
`sub.example.com` nor `evil-example.com`, and both sides go through the same IDNA normalisation, so
a rule may be written `@Example.COM.` or in punycode and still mean what it says. Two arrangements
are refused at start-up rather than documented and hoped for: a list that normalises to nothing,
which `@` and a stray `.` both produce, because it is a non-empty list no address can match; and a
list combined with a `MICROSOFT_OAUTH_TENANT_ID` that names no directory (`common`, `organizations`
or `consumers`), because there the address the list is checked against is one the signing-in tenant
writes for itself. A production deployment that names no domains and leaves the tenant multi-tenant
is warned rather than refused.

**`OPENBOT_ORGANIZATION_AUTH_URL` names an authority, not a provider.** It must be an HTTPS OpenBot
origin, or HTTP on loopback, and a bare origin: a username, password, query, fragment or any path
other than `/` is refused at start-up. Naming one settles sign-in by itself, ahead of
`OPENBOT_SINGLE_USER`.

**Any one provider turns sign-in on**, and several may be configured at once. Each provider's id and
secret must be set together, Okta additionally needs its issuer, and any of them requires
`BETTER_AUTH_SECRET`, `BETTER_AUTH_URL` and `INITIAL_ADMIN_EMAILS`. Every incomplete combination is
Expand Down
Loading