Connect real providers
Resolve real provider credentials on the stock self-hosted executor using environment variables or the local encrypted vault — no custom server code.
The stock executor server (pnpm --filter @eyeball/executor start) resolves real
provider credentials from configuration alone. You select a credential source
with the EYEBALL_CREDENTIALS environment variable; you do not write a
custom server entry point or call createExecutorApp yourself.
Choose a credential mode
EYEBALL_CREDENTIALS selects the credential provider the stock server
constructs at boot. It must be one of four values:
| Mode | EYEBALL_CREDENTIALS | Credentials live in | Scope | Durable |
|---|---|---|---|---|
| Deterministic mocks (default) | mock | Nowhere (empty) | — | — |
| Environment | env | Process environment | One project + one user | Re-supplied at boot |
| Local encrypted vault | local-vault | An AES-256-GCM file | One project, many users | Yes, on disk |
| Cloud | cloud | Hosted vault over HTTP | Multi-tenant | Yes (Cloud) |
This page covers env and local-vault, the two OSS self-host paths. mock is
covered by the local quickstart; cloud
requires eyeball Cloud and is described in the
cloud boundary.
Before you start
Build the workspace once:
pnpm install
pnpm buildPick one identity and reuse it for every command on this page. The examples use:
- Project id
proj_local - User id
local_user - Executor API key
eyeball_local_key
The project id must be identical everywhere a credential is written and read. Credentials are partitioned by project, so a mismatch between the id you connect under and the id the executor runs under resolves to "no credential".
Option A — Environment credentials
EYEBALL_CREDENTIALS=env maps deterministic per-toolkit environment variables
into credentials for exactly one project and one user. It needs no CLI and no
files; it is the simplest way to give a single service identity real access.
Set the identity and the per-toolkit secrets, then start the stock server. The variable names are derived from each toolkit's auth class:
| Auth class | Required variables | Example toolkit |
|---|---|---|
oauth2 | EYEBALL_CRED_<TOOLKIT>_ACCESS_TOKEN (optional _EXPIRES_AT, _SCOPES) | EYEBALL_CRED_GMAIL_ACCESS_TOKEN |
api_key | EYEBALL_CRED_<TOOLKIT>_<FIELD> | EYEBALL_CRED_STRIPE_API_KEY |
basic | EYEBALL_CRED_<TOOLKIT>_USERNAME + _PASSWORD (twilio uses _ACCOUNT_SID/_AUTH_TOKEN) | EYEBALL_CRED_TWILIO_ACCOUNT_SID |
<TOOLKIT> is the toolkit slug uppercased with hyphens replaced by underscores
(google-calendar becomes GOOGLE_CALENDAR). A long-lived personal access
token for an OAuth toolkit (GitHub, Notion) is supplied through its
_ACCESS_TOKEN variable.
export EYEBALL_CREDENTIALS=env
export EYEBALL_PROJECT_ID=proj_local
export EYEBALL_USER_ID=local_user
# api_key toolkit
export EYEBALL_CRED_STRIPE_API_KEY='<stripe-secret-key>'
# oauth2 toolkit supplied with a personal access token
export EYEBALL_CRED_GITHUB_ACCESS_TOKEN='<github-personal-access-token>'
# oauth2 toolkit that enforces scopes (Gmail requires gmail.modify)
export EYEBALL_CRED_GMAIL_ACCESS_TOKEN='<gmail-access-token>'
export EYEBALL_CRED_GMAIL_SCOPES='https://www.googleapis.com/auth/gmail.modify'
EYEBALL_API_KEYS="eyeball_local_key:proj_local" \
PORT=3000 \
pnpm --filter @eyeball/executor startOption B — Local encrypted vault
EYEBALL_CREDENTIALS=local-vault reads credentials from an AES-256-GCM
encrypted file. It stores many users under one project, survives restarts, and
runs OAuth refresh for records that carry an expiry. Use the eyeball-auth CLI
to write records, then start the stock server against the same file and key.
1. Initialize the vault
pnpm eyeball-auth init --vault .eyeball/vault.jsonThis creates an empty vault and prints the two export lines you need — the encryption key and the file path:
Initialized empty local vault at .eyeball/vault.json.
Store this key in your secret manager; it is not recoverable from the vault:
export EYEBALL_VAULT_KEY='<base64-32-byte-key>'
export EYEBALL_VAULT_PATH='.eyeball/vault.json'Export the key, path, and the shared project id so every CLI command and the executor agree:
export EYEBALL_VAULT_KEY='<base64-32-byte-key>' # from the init output
export EYEBALL_VAULT_PATH='.eyeball/vault.json'
export EYEBALL_PROJECT_ID='proj_local'The CLI writes records under EYEBALL_PROJECT_ID (default local). Set it
explicitly to the same id your executor will run under.
2a. API-key providers
For toolkits whose auth class is api_key (Stripe, SendGrid, Resend,
Telegram), add the secret directly. The secret is read from the flag value and
never printed:
pnpm eyeball-auth add stripe --user local_user --secret 'apiKey=<stripe-secret-key>'2b. OAuth providers
For toolkits whose auth class is oauth2 and where you want a real,
scope-carrying grant (Gmail, Google Calendar, Google Drive, Slack), register an
OAuth application with the provider, then run the connect flow. The CLI opens a
loopback callback (default http://127.0.0.1:53682/callback) and exchanges the
code:
export EYEBALL_OAUTH_GMAIL_CLIENT_ID='<gmail-oauth-client-id>'
export EYEBALL_OAUTH_GMAIL_CLIENT_SECRET='<gmail-oauth-client-secret>'
pnpm eyeball-auth add gmail --user local_userThe grant carries the scopes the provider returns, so scope-enforcing providers
(Gmail requires gmail.modify) pass. Use --manual for a copy/paste code
exchange, --redirect-uri to override the callback, and --public-client for
apps without a client secret.
2c. Personal access tokens as OAuth bearers
GitHub, Notion, Linear, and Airtable declare oauth2 auth, but each also
accepts a long-lived bearer token (a GitHub PAT, a Notion internal-integration
token, a Linear or Airtable PAT). eyeball-auth add always runs a full OAuth
flow for an oauth2 toolkit, so use the token injector instead. The secret is
read only from an environment variable, never from argv:
EYEBALL_STORE_ACCESS_TOKEN='<github-personal-access-token>' \
pnpm eyeball-vault-put-token --user local_user --toolkit githubThis stores an access-token-only oauth2 record with no scopes. That is
correct for GitHub, Notion, Linear, and Airtable because their operations
declare no required scopes. It is not sufficient for scope-enforcing
providers such as Gmail — connect those with eyeball-auth add (2b) so the
grant carries scopes.
3. Start the executor against the vault
Run the stock server with the same file, key, and project id you connected under:
EYEBALL_CREDENTIALS=local-vault \
EYEBALL_PROJECT_ID=proj_local \
EYEBALL_VAULT_PATH=.eyeball/vault.json \
EYEBALL_VAULT_KEY="$EYEBALL_VAULT_KEY" \
EYEBALL_API_KEYS="eyeball_local_key:proj_local" \
PORT=3000 \
pnpm --filter @eyeball/executor startVerify
Before starting the server, a read-only probe confirms a stored credential resolves and reaches the provider (it runs only tools annotated read-only):
pnpm eyeball-auth test github --user local_user \
--tool github.list_issues --input '{"projectId":"owner/repo"}'End to end, call the running executor's POST /v1/execute with your project
key. Use a read-only operation so nothing is mutated:
curl -s http://127.0.0.1:3000/v1/execute \
-H "Authorization: Bearer eyeball_local_key" \
-H "Content-Type: application/json" \
-d '{"tool":"gmail.search_emails","userId":"local_user","mode":"sync","input":{"query":"in:inbox","pageSize":5}}'You can also drive the same executor from the SDK or an MCP client.
Provider base URLs
Most providers have a fixed public base URL and need no configuration. Four providers are tenant- or instance-scoped and ship a placeholder host, so a real deployment must point them at your instance with the toolkit's base-URL override:
| Toolkit | Required override |
|---|---|
odoo | EYEBALL_ODOO_BASE_URL |
shopify | EYEBALL_SHOPIFY_BASE_URL |
smtp | EYEBALL_SMTP_BASE_URL |
zendesk | EYEBALL_ZENDESK_BASE_URL |
Every other toolkit also exposes an EYEBALL_<TOOLKIT>_BASE_URL override, but
only for testing or proxying — for example pointing a toolkit at
Mockhouse. Manifest endpoint.baseUrlOverrideEnv is the
only trusted endpoint override; a request cannot supply an arbitrary origin.