Not in the middle: why a Context AI–style breach against PebbleFlow would yield nothing useful

Not in the middle: why a Context AI–style breach against PebbleFlow would yield nothing useful

2026-04-22 · PebbleFlow Team

On April 19, 2026, Vercel disclosed that a single employee's third-party AI tool had been compromised, and that the compromised OAuth token had been used to pivot into Vercel's internal environments. A limited subset of customers had non-sensitive environment variables exposed. Encrypted/sensitive environment variables were unaffected.

The tool was Context AI. The employee had granted it broad "Allow All" access to their corporate Google Workspace. That single OAuth grant, held in Context AI's servers, was the leverage point for everything that followed.

A threat actor on BreachForums has separately listed what they claim are 580 Vercel employee records for sale at $2M. Vercel has not verified that listing.

The incident is not over. But the architectural lesson is already clear, and it's a useful one for anyone evaluating an AI workspace.

The shape of the attack

The SaaS-AI model puts a vendor in the middle of every OAuth grant you give it.

When you install a third-party AI productivity tool and click through the OAuth consent screen, the access token and refresh token issued by Google (or Microsoft, or any identity provider) do not stay on your device. They are delivered to the vendor's servers, because the AI that needs them runs in the vendor's cloud. The vendor's infrastructure holds a continuously refreshed set of tokens for every one of their users, scoped to the "Allow All" permissions most users clicked through without reading.

That centralized token store is precisely what attackers target. Compromising the vendor once gives you Workspace access for thousands of customers. Vercel's own bulletin warns that the downstream effects could touch "hundreds of users across many organizations."

Reporting traces the original chain back to a Context AI employee whose personal device was compromised in February 2026, reportedly via a downloaded Roblox game exploit that carried Lumma Stealer infostealer malware. That malware exfiltrated the employee's Google Workspace and AWS credentials, which in turn unlocked the OAuth token vault. One infected personal device, one corporate SaaS vault, hundreds of downstream Workspaces.

Three architectural reasons a Context AI–style breach against PebbleFlow would yield nothing useful

PebbleFlow is a powerful, privacy-first workspace with an agentic orchestrator and chat interface that runs in a side panel. "Privacy-first" describes a specific architectural posture, not a marketing line. There are three concrete properties at work.

1. Your Workspace OAuth tokens are stored encrypted on your device, not on our servers

When you connect a Google or Microsoft account in PebbleFlow, you'll see Google's standard OAuth consent screen with the scopes you're granting. So far this looks identical to authorizing Context AI.

The structural difference is what happens to the tokens that come out of that consent flow. The access token and refresh token Google issues are stored encrypted on your device — in Keychain on macOS and iOS, in Android's Keystore on Android, in the browser's secure storage in the extension. They are not stored in PebbleFlow's central database. We do not have a token vault holding them on your behalf.

When the agentic orchestrator needs to read your calendar or search your inbox, the API call goes from your device directly to Google, with the token from your device's secure storage attached. Our infrastructure is not in the data path for any of your Workspace content.

You can verify this yourself in the source: src/shared/auth/connections-manager.ts is where Google/Microsoft OAuth connections are persisted. The OAuthConnection records, including accessToken and refreshToken fields, are written through the local storage adapter — not transmitted to a central token store.

2. The relay's message bus is end-to-end encrypted

When PebbleFlow components on different devices need to coordinate — your browser extension talking to your desktop app, your phone talking to your home server, the side-panel UI invoking a tool that runs natively on macOS — they do so through a relay we host at relay.pebbleflow.ai. The relay is the rendezvous point that lets your devices find each other across networks.

That relay is end-to-end encrypted. Each user's devices perform a key exchange through the relay envelope, and from then on, messages between your devices are ciphertext from the relay's perspective. The relay can route them but cannot read them.

This isn't aspirational. The Durable Object that handles WebSocket connections, cloud/relay/src/user-relay.ts, is documented as: "Manages WebSocket connections for a single user. Handles E2E encrypted messages (opaque to relay) and key exchange." The code paths that handle the ENCRYPTED message type are explicitly commented: "Encrypted message to specific client (we can't read it)." The relay is not architecturally able to inspect the contents of the messages it forwards.

3. The relay is single-tenant per user, by construction

PebbleFlow's relay is built on Cloudflare Durable Objects. Each user gets their own UserRelay instance — a separate, runtime-isolated piece of compute and storage. There is no shared multi-tenant database holding every user's live session state, because there is no multi-tenant database at all for that role. Each user's relay is a separate object.

This matters because it eliminates the single-shared-target failure mode. Even if an attacker found a way to compromise one user's Durable Object, they would not gain lateral access to any other user's data — there is no shared backing store to pivot through. Each user is, structurally, their own tenant.

What is and isn't in our central database

We do operate a central database (Cloudflare D1) for things that need to be central. Honesty matters here. The database stores:

  • Account identity: your email, your hashed password if you use email/password login, the provider ID returned by Google/Apple/Microsoft if you use a social-login button.
  • Billing state: your Stripe customer ID, subscription tier, license key, and subscription status.
  • Per-user API keys for AI inference providers (OpenRouter, specifically). These are inference-provider credentials — distinct from your Workspace OAuth tokens. They exist for the managed-credits flow and for users who want to bring their own OpenRouter key to use across PebbleFlow's AI features.
  • Device activation list for license enforcement.
  • Audit logs, kept for SOC 2 evidence requirements.
  • A routing table for opt-in inbound webhooks (WhatsApp, Telegram, etc.) — used only if you've configured those messaging integrations.

The database does not store:

  • Your Workspace OAuth tokens (Gmail, Calendar, Drive, Microsoft 365). Those are on your device only.
  • Your conversation content. The agentic orchestrator runs in your side panel; conversations live in your local storage.
  • Your Workspace data — emails, calendar events, Drive files. Those are read directly from Google/Microsoft to your device, never passing through our infrastructure.
  • The contents of any message that flows through the WebSocket relay. The relay sees ciphertext only.

A breach of our central database would expose account/billing identity, audit logs, and the OpenRouter inference-key column. It would not yield Workspace tokens, conversation content, or Workspace data, because those are not in there to take.

The honest caveats

This is a different threat model, not a magic shield. A few qualifiers we'd rather you hear from us than discover later.

The OAuth code-exchange briefly touches our relay. Google (and Microsoft, GitHub, Slack) require the OAuth client_secret to be presented at the token-exchange step, and that secret cannot be shipped in client code. So our stateless relay attaches the client_secret and forwards your authorization code to Google in exchange for the actual tokens. The tokens come back through the relay and are immediately returned to your device for storage. They are not persisted in our infrastructure. This is the same reason every native Google-integrated app you've ever used has some server component — it's a Google constraint, not a PebbleFlow design choice.

Custom endpoints let you bypass our OAuth client entirely. PebbleFlow supports configuring custom OAuth endpoints, which means a user willing to provision their own OAuth client in Google Cloud Console (or the Microsoft Entra equivalent) can avoid PebbleFlow's OAuth scopes and our relay's exchange step altogether. Once configured, the auth flow is between your device and Google with PebbleFlow nowhere in the loop. We don't surface this as a consumer-facing setting because most users don't need it and the default architecture already keeps your Workspace data off our servers. But it exists, and the implementation will be familiar to anyone who has set up an OAuth client in Google Cloud Console before.

Device compromise still matters. If your laptop is owned, your tokens are owned. Local-first moves the attack surface from "vendor's vault" to "your endpoint." That is a smaller and more defensible surface, but it is not zero.

Login-tier "Sign in with Google" is separate from Workspace-access. If you sign into PebbleFlow itself with Google or Apple, that flow is identity-only and creates a Basic connection scoped to your account. It is not the same path as the Workspace-access flow described above and does not grant the AI access to your inbox or calendar.

Supply-chain attacks on PebbleFlow itself are still conceivable. A compromised update channel could in principle ship code that exfiltrates tokens from your device. We mitigate this with code signing and notarization on every platform we ship, but the mitigation is different from the vendor-vault model — and so are the attackers' economics.

How to verify it yourself

The honest answer is that a vendor's privacy claim from a marketing page can't be proved to a skeptic. So here is how to confirm what we've claimed using your own tools.

Run a network monitor while using PebbleFlow. Little Snitch on macOS, GlassWire on Windows, or Wireshark on anything. Use PebbleFlow normally for an hour. The traffic you'll see, with what each connection means:

Destination When What it means
oauth2.googleapis.com, gmail.googleapis.com, calendar.googleapis.com, www.googleapis.com Whenever the agent reads your Workspace data Your device talking directly to Google with your on-device token. We are not in this path.
login.microsoftonline.com, graph.microsoft.com If you've connected Microsoft 365 Same shape, your device to Microsoft.
api.anthropic.com, openrouter.ai, api.openai.com, generativelanguage.googleapis.com, localhost:11434 (Ollama) When you send a chat or run a tool that uses an LLM Your device talking to whichever AI provider you've configured. We are not in this path.
relay.pebbleflow.ai (HTTPS) Brief, during initial Workspace OAuth setup and during periodic token refresh The stateless OAuth code-exchange. Tokens pass through, are not persisted server-side.
relay.pebbleflow.ai (HTTPS) Periodically License validation, release-notes / user-guide content fetches, model-intelligence data, subscription status checks.
relay.pebbleflow.ai (HTTPS) When you load the website or view connected-account status Standard API traffic.
relay.pebbleflow.ai (WebSocket) Continuously, if you have multiple PebbleFlow components installed (extension + desktop app, or mobile + desktop) The capability bridge that lets your devices talk to each other. End-to-end encrypted. We see ciphertext only.
relay.pebbleflow.ai/api/messaging/... Only if you've configured messaging integrations (WhatsApp, Telegram, Slack inbound) Webhook routing for incoming messages.

What you will not see, ever:

  • Your Workspace data (emails, calendar events, Drive files) flowing to relay.pebbleflow.ai. Those calls go from your device directly to *.googleapis.com.
  • Your conversation content flowing to relay.pebbleflow.ai. The orchestrator runs in your side panel; chat history is in your local storage.
  • Your AI provider API keys flowing to relay.pebbleflow.ai. They live on your device. (The exception: if you use the managed-credits flow, you're using a server-allocated OpenRouter sub-account, and the inference call is still made from your device.)

If you ever see a request from your device to our infrastructure that doesn't fit the table above, that's a finding. Tell us, and we'll explain it or fix it.

What to do if you are a Vercel or Context AI customer

The published guidance from Vercel, Context AI, and the security research community has converged on a consistent checklist:

  1. Rotate every secret stored as a non-encrypted Vercel environment variable. Encrypted/sensitive variables were reported unaffected, but rotation is cheap.
  2. Audit your team's third-party OAuth grants on Google Workspace (and Microsoft 365, if applicable). Revoke anything you do not recognize, and treat any tool you granted broad "Allow All" scopes to as a credential you need to replace.
  3. Tighten future consent. Prefer tools that request least-privilege scopes, and prefer workspaces whose architecture does not require you to hand over long-lived tokens at all.

The third one is the structural move, and the one this incident keeps quietly recommending.

Try PebbleFlow

If the lesson of the Vercel/Context AI incident is that the location of your OAuth tokens determines your blast radius when the vendor gets breached, the answer is to choose a workspace that doesn't hold them.

PebbleFlow is a powerful, privacy-first workspace with an agentic orchestrator and chat interface that runs in a side panel. Available as a browser extension, native macOS app, native iOS app, native Android app, and desktop app for Windows and Linux. Get started for free.


Sources: