Connector
Host-side Open Connector gateway — read-only Google Workspace and personal GitHub tools whose OAuth tokens never enter the sandbox.
An optional, host-side OAuth action gateway. When configured, agents get read-only tools for Google Workspace (Gmail, Calendar, Sheets) and a personal GitHub account that execute on the host through a self-hosted Open Connector deployment — the provider’s OAuth tokens never enter the sandbox.
This complements, and does not replace, the vault: guest CLIs (gws,
gcloud, gh) keep working through vault credential projection, and the
platform GitHub App adapter (github_* tools) is untouched. Rationale and
platform comparison: docs/research/connector-platform-selection.md.
Configuration
Section titled “Configuration”| Env var | Meaning |
|---|---|
CONNECTOR_GATEWAY_URL | Base URL of the self-hosted Open Connector service |
CONNECTOR_RUNTIME_TOKEN | Runtime token; may execute only the reviewed action allowlist |
CONNECTOR_ADMIN_TOKEN | Admin token; OAuth onboarding and connection management |
All three normally live in the daemon environment (~/.mikan/mikan.env).
Without the runtime token the feature is disabled; without the admin token
existing connections keep working but onboarding/disconnect fails. The
onboarding page is served by the web portal, so LINK_PORT must be set.
Running the connector under pm2
Section titled “Running the connector under pm2”The pm2 deployment runs the connector by default as a sibling app of
mikan (declared in deploy/pm2/ecosystem.config.cjs): both autostart on
boot, but reloading/upgrading mikan never interrupts the connector’s OAuth
flows, token refresh, or SQLite writes — and the connector (a young project)
can be upgraded on its own cadence. One-time setup:
git clone https://github.com/oomol-lab/open-connector ~/.mikan/open-connector(cd ~/.mikan/open-connector && npm install) # requires Node 22+curl -o ~/.mikan/connector.env https://raw.githubusercontent.com/geminixiang/mikan/main/deploy/pm2/connector.env.examplechmod 600 ~/.mikan/connector.env # fill in keys/tokenspm2 start ecosystem.config.cjs && pm2 saveThe connector’s own settings live in ~/.mikan/connector.env
(OOMOL_CONNECT_*); mikan’s side stays in ~/.mikan/mikan.env
(CONNECTOR_GATEWAY_URL=http://127.0.0.1:3000 plus the same two tokens).
Upgrade independently of mikan:
(cd ~/.mikan/open-connector && git pull && npm install) && pm2 restart open-connectorThe pm2 app pins HOST=127.0.0.1. One path must still be publicly reachable:
users’ browsers land on <OOMOL_CONNECT_ORIGIN>/oauth/callback at the end of
each provider authorization, so expose exactly that path through your reverse
proxy / TLS termination and register it as the callback URL in your Google and
GitHub OAuth apps. The admin API, console, and /v1 stay private.
Local development
Section titled “Local development”Same pm2 app, same setup as production — the connector is a per-machine service, so a dev box runs the one instance and every mikan on that box (dev-state or otherwise) points at it. Do the one-time setup above, then:
pm2 start ecosystem.config.cjs --only open-connectorand run a dev-state mikan against it:
CONNECTOR_GATEWAY_URL=http://127.0.0.1:3000 \CONNECTOR_ADMIN_TOKEN=... CONNECTOR_RUNTIME_TOKEN=... \LINK_PORT=8787 \./dist/main.js --state-dir="$HOME/.mikan-dev" --sandbox=host ./workspace(the two tokens are the values from ~/.mikan/connector.env).
OAuth needs no public URL on a dev box: the default OOMOL_CONNECT_ORIGIN
is http://localhost:3000, your browser is on the same machine, and
Google/GitHub both accept localhost redirect URIs on dev OAuth apps —
register http://localhost:3000/oauth/callback there and enter the client
id/secret in the connector console (http://localhost:3000, sign in with
the admin token). Use dev OAuth apps and a throwaway account, not your
production client ids.
Everything else is the normal dev loop: /login in a private conversation →
“Connected services” → connect → the connector_gws / connector_github
tools go live for that conversation. Dev-state and production mikan share
the instance safely — connections are keyed by principal, and a dev state
dir has its own principals. Reset by deleting the connection in the console
(or the whole data dir). Automated tests never need a running connector —
they fake it at the fetch seam (src/test/connector-*.test.ts).
Hardening the connector deployment
Section titled “Hardening the connector deployment”Mikan treats the connector as trusted infrastructure; deploy it accordingly:
- set
OOMOL_CONNECT_ENCRYPTION_KEY— without it the connector stores credentials in plaintext SQLite; - set
OOMOL_CONNECT_ADMIN_TOKENand a separate runtime token; additionally pin the deployment-level action allowlist to mikan’s curated set (OOMOL_CONNECT_ALLOWED_ACTIONS, pre-filled inconnector.env.example) so even a leaked runtime token cannot execute anything else; - disable the raw proxy (
OOMOL_CONNECT_BLOCKED_PROXIES=*) — mikan never calls it; - bind the service to a private interface reachable only by the mikan host;
- register your own Google / GitHub OAuth apps in the connector, with the
connector’s
/oauth/callbackas the redirect URL — and list those services inOOMOL_CONNECT_ALLOWED_CUSTOM_OAUTH, or the connector refuses the client configuration; - back up
connect.sqlitetogether with the encryption key.
What the agent gets
Section titled “What the agent gets”Two tools, available on every platform once configured:
connector_gws—gmail_search,gmail_read_thread,calendar_list_events,sheets_read_rangeconnector_github—whoami,my_repositories(the connected personal account; distinct from the GitHub App’sgithub_*tools)
The allowlist is code (CURATED_ACTIONS in src/connector/gateway.ts):
read-only, one connector action per tool action, no write actions and no raw
proxy in this first iteration. Results are size-capped before they reach the
conversation.
Connecting an account
Section titled “Connecting an account”- Run
/loginin a private conversation and open the link. - Follow “Connected services” to the
/connectorpage. - Connect a service; authorize in the provider tab; the page confirms and the conversation is notified.
Connections are scoped to the conversation’s credential authorization key — the same principal that scopes its vault. One conversation (or, in host sandbox mode, one user) can never reach another’s connections; the model never supplies connection identifiers.
Migrating from vault-projected credentials
Section titled “Migrating from vault-projected credentials”Migration is re-authorization, not credential import:
- Deploy and harden the connector, set the
CONNECTOR_*env vars, restart. - Re-authorize each account through the
/connectorpage. - Workflows covered by the curated tools now run host-side; the agent needs no guest credential for them.
- Optionally remove the corresponding vault entries (
gws.json, GitHub OAuth tokens) from conversations that no longer need guest CLIs — keep them wherevergws/ghmust still run inside the sandbox. Nothing is removed automatically.
To disconnect, use the /connector page (removes both the mikan mapping and
the connector-side credential), or delete the connection in the connector
console plus the entry in <stateDir>/connector/connections.json.