Skip to main content

Auth & secrets

Connectors talk to other people’s services. That requires credentials — and credentials must never end up in your app’s source, in the model’s context, or in plaintext on disk. This page is the contract for how Vibely handles all of that.

Where secrets live

Secret typeStored inVisible to
OAuth access + refresh tokensVibely’s managed OAuth vaultVibely backend at execution time
Connector API keys (you paste them)Vibely’s secrets table, encrypted at restVibely backend at execution time
Stripe / Razorpay / Resend / Inngest keysSupabase Edge Function secretsEdge function runtime
Backend env keys (E2B, LLM keys)Vibely server’s .envVibely backend; never injected into your app
Custom secrets you addSupabase Edge Function secretsEdge function runtime
Three places. None of them are your app’s source code.

What the agent never sees

The agent operates inside the sandbox. It can read and write your project files, but it cannot read raw connector secrets. When it generates a wrapper like sendSlackMessage, the call goes through Vibely’s backend, which injects the live OAuth token before forwarding to Slack. The agent’s prompt window never contains your Slack token. If a tool you’re using needs a key (e.g. web_scrape requires FIRECRAWL_API_KEY), that key is set on the Vibely server, not handed to the model.

OAuth scopes and per-permission policy

When you start a new connection, expand Advanced settings on the connector’s panel — you’ll see the real OAuth scopes the provider exposes (Gmail’s gmail.readonly / gmail.send / gmail.compose, Slack’s chat:write / channels:read, Linear’s read/write classes, and so on). Identity plumbing (openid, email, refresh tokens) is requested silently and isn’t shown in the list. Each scope has a three-way toggle:
ChoiceWhat happens
AllowThe scope is requested at connect time and the agent can use it whenever it needs to.
AskThe scope is granted, but the agent has to prompt you in chat the first time an action would exercise it.
DenyThe scope is stripped from the OAuth request. The agent’s permission gate then blocks the matching class — deny wins over allow inside the same class.
Scopes marked with a Required pill are non-negotiable — they’re either identity plumbing or the minimum capability the connector advertises (e.g. Gmail gmail.readonly). You can’t deny them; Vibely keeps their class allowed even if you Deny another scope in the same class. The three buckets the permission gate enforces at runtime are read / write / send. Your per-scope choices collapse into one policy per class on connect — you don’t have to manage that mapping by hand. You can re-open Advanced settings to audit (or revoke) scopes per connector at any time from the Connectors drawer. Revoking is immediate.

Verifying credentials before connecting

For API-key connectors, Vibely makes a probe call against the provider before saving the key, so a typo or expired secret fails loudly on the spot instead of breaking the first agent turn that tries to use it.

Bring your own OAuth client

A handful of MCP-class connectors require a Dynamic Client Registration step (Figma, for example). The new-connection panel surfaces a Bring your own OAuth client block where you paste client_id (and client_secret where applicable) — Vibely uses that pair for the handshake instead of a shared Vibely-owned app. The block is marked required when the provider mandates it, advanced when it’s optional.

API-key connectors

Some services don’t support OAuth (e.g. an internal API, a niche SaaS). For these:
  1. You paste the key in the Connectors drawer when you add the connector.
  2. Vibely encrypts it at rest with a workspace-scoped key.
  3. At runtime, the connector layer decrypts the key in memory and uses it for the call. The decrypted value never lands on disk or in logs.
Rotation: paste a new key in the drawer; the old one is overwritten and invalidated. There’s no separate rotation UI because there’s no separate stored history.

MCP auth

MCP / Chat connectors authenticate independently of App connectors — connecting Linear as an MCP server doesn’t give your deployed app Linear access, and vice versa.
MCP connection typeHow it authenticatesWhere the credential lives
Default-URL service, OAuthOAuth dance handled by the MCP server (Notion, Linear, Slack, Figma, …)Vibely’s managed OAuth vault, scoped per workspace
Default-URL service, API keyPaste the key in the Chat / MCP tabVibely’s encrypted secrets store, scoped per workspace
Custom MCP serverPaste the endpoint URL + optional bearer token in the Chat / MCP tabVibely’s encrypted secrets store, scoped per workspace
For custom MCP servers, the bearer token (when set) is sent as Authorization: Bearer <key> on every probe and tool call. MCP tokens are scoped per workspace, not per project. Every project in the workspace can use a connected MCP server. Revoking a connection from the Connectors drawer invalidates it for all of them, immediately. There’s no per-project MCP override today — if you need a service connected differently for one project, fork the workspace.

Workspace scoping

Connectors are scoped to your workspace, not your project. That means:
  • Once you connect Slack to your workspace, every project in that workspace can use it.
  • Different workspaces have independent connections — connecting Slack in workspace A doesn’t expose anything to workspace B.
  • Removing yourself from a workspace removes your access to its connectors but not the connectors themselves (other workspace admins can re-grant).

App-side secrets vs build-side secrets

Some apps need a secret at runtime that isn’t a connector — e.g. a private API your app talks to directly, a webhook signing secret, a third-party HMAC key. Add these via the Secrets panel (project → Backend → Secrets). They land in Supabase Edge Function environment, accessible via Deno.env.get("MY_SECRET"). The agent will reference them by name in code:
const apiKey = Deno.env.get("INTERNAL_API_KEY")
The agent never writes the literal value into a file. If you spot a hard-coded key in the diff, that’s a bug — please file an issue.

What if a secret leaks?

A few defenses, in order:
  1. Vibely detects common secret formats (Stripe sk_live_…, AWS keys, Slack xoxb-…) in any file the agent writes and refuses the write. This catches the majority of accidental commits.
  2. Pre-publish security scan runs security_scan automatically before you publish. It re-scans for keys, leaked PII, and unsafe patterns.
  3. GitHub secret scanning is enabled on exported repos by default — GitHub will tell you (and the issuing service, where supported) if a key sneaks past us.
If a secret does leak, rotate it at the source first (Stripe dashboard / Slack admin / etc.), then update the value in Vibely’s drawer. The compromised key is invalidated immediately on rotation.

Self-hosting / BYOK

For workspaces with stricter compliance needs:
  • Bring your own LLM keys. Set FIREWORKS_API_KEY / GOOGLE_AI_API_KEY / ANTHROPIC_API_KEY on your Vibely deployment. Calls to LLMs go directly with your billing.
  • Bring your own E2B. Self-host E2B (E2B_DOMAIN=https://your-control-plane) so sandboxes never leave your network.
  • Bring your own Supabase. Vibely never needs the managed Supabase Cloud; pointing at a self-hosted Supabase is a config swap.
This is what makes the Configuration → Self-hosting path possible — every external dependency can be replaced with one you control.