# hosted setup

the gateway is a local fork of sigbit/mcp-auth-proxy; see [upstream](UPSTREAM.md) and its
[license](LICENSE). it owns downstream mcp oauth and owner sessions; google account linking is
separate from that login.

```sh
npm ci
npm run build:gateway
cp .env.example .env
# fill in .env, then export its values in your shell
set -a
. ./.env
set +a
npm run serve
```

use a tls reverse proxy in front of the loopback listener. rate-limit the public `/.idp/register`
endpoint (for example 1/minute, burst 3) and `/.idp/token` (5/second, burst 20); client
registrations persist, so do not expose unrestricted registration to floods. keep oauth callback
queries, headers, and bodies out of proxy logs.

`EXTERNAL_URL` must be your public https origin; `TRUSTED_PROXIES` must identify only your reverse
proxy. set the oidc discovery url, client id/secret, and `MCP_ALLOWED_SUBJECT` to the exact subject
your identity provider returns. register `<origin>/.auth/oidc/callback` with that provider. the
default oidc subject field is `/sub`.

## connect google

1. create a google oauth **web application** client, not a desktop client.
2. register `<origin>/.auth/colab/callback` as its authorized redirect uri.
3. set `COLAB_GOOGLE_CLIENT_ID` and `COLAB_GOOGLE_CLIENT_SECRET`, then restart.
4. visit `<origin>/.auth/colab`, sign in as the gateway owner, and connect google.

the gateway requests offline access with s256 pkce, verifies google's userinfo response, and
checks read-only colab access before saving credentials. optionally set `COLAB_GOOGLE_ACCOUNT` to an expected google email address. a
cancelled or failed connection keeps the previous credential. credentials are stored atomically with
mode `0600` in the private state directory; they never go to the mcp client or the browser.

the default google scopes are `openid` and `https://www.googleapis.com/auth/userinfo.email`,
following the [official colab cli's documented runtime-api requirement][colab-scopes]. custom
web-client access to colab still needs a real sign-in test; mocked oauth tests do not prove google
accepts your client. `COLAB_GOOGLE_SCOPES` can override the space-separated scope list if needed.
google consent screens in testing mode can expire refresh tokens after seven days; configure the
app's publishing status appropriately.

## multiple accounts

connect more google accounts from `<origin>/.auth/colab`; the google account chooser appears on
each connection. the owner-only page shows emails and opaque ids. reconnecting the same google
identity updates its credentials without changing its id, even if its email changes.

mcp clients see `colab_accounts`: stable, independently random 256-bit ids, not emails or email
hashes. use `colab_usage(account_id=...)` to inspect each account and choose an explicit
`account_id` for `colab_launch`. both require a choice when multiple accounts are connected.
`colab_list` lists all accounts, or filters by `account_id`; partial failures appear in `errors`
without hiding working accounts. discovery itself does not verify token health.

runtime ids returned by the hosted pool include their account binding. pass the whole id to
execute, files, interrupt, kernels, and stop. missing or removed accounts never substitute another
account, and an uncertain allocation never triggers automatic failover. quotas remain separate.
all eleven tools retain `readOnlyHint: true` by convention, including mutating operations.

### credential storage and migration

`COLAB_MANAGED_CREDENTIALS` defaults to `$DATA_PATH/colab-google.json`. the pool is stored beside
it as `colab-google.json.accounts.json`: a versioned, atomic `0600` file in the private state
directory, capped at 16 accounts and 1 MiB. the gateway is its only writer; workers notice
replacement and refresh tokens in memory, separately per account. an invalid store fails closed.

on upgrade, an existing managed single-account credential is imported once, without changing the
original file. a verified reconnect matching its old email adopts google's immutable subject, retaining its id.
the original file remains private rollback material; back up the pool too. removing an account
from the pool does not erase that historical copy, revoke google consent, or stop its runtimes.
stop runtimes before removal and revoke access in google to invalidate retained credentials.
removing the last account leaves an authoritative empty store, never resurrecting the old account.

before a pool exists, the legacy single-account path still works, including a read-only
`COLAB_CREDENTIALS` fallback; it uses the non-identifying `default` selector. desktop login remains
single-account. once a pool exists, its records pin their own google clients and override token
or fallback environment settings. do not set `COLAB_ACCESS_TOKEN` with browser login.

## state and clients

`DATA_PATH` defaults to `~/.local/share/colab-mcp` and must have mode `0700`. keep this directory
across upgrades: it contains the gateway database, session key, signing key, and connected google
credential. use service credentials or a secret manager for client secrets; never add them to this
checkout.

by default, mcp client registration accepts canonical https callbacks on google's three exact oauth
relay origins (production, sandbox, test). set `MCP_REDIRECT_PREFIXES` explicitly for other clients;
entries are comma-separated, parsed origins/path prefixes, not wildcard strings. an empty policy
fails closed. all clients require s256 pkce, and every mcp access token must match the owner and
resource audience. changing the allowed registration paths does not permit an unregistered callback
during authorization.

run `npm test` for the node server and `npm run test:gateway` for the go gateway. `npm run build`
also produces the optional standalone node-side binary; put it beside `colab-mcp-gateway` and run
`./dist/colab-mcp serve`.

[colab-scopes]: https://github.com/googlecolab/google-colab-cli/blob/006ff96ac2d0e4dcfb702467a76493d687929be8/docs/04_automation_and_utility.md
