Engineering

We Moved All AI Processing Server-Side. Here's Why Security Matters.

By Andres Muguira December 22, 2025 7 min read
CRM Security Server-Side AI Edge Functions PKCE
Summarize with AI

In the early days of SalesSheet, we made a decision that many AI startups make: we called AI APIs directly from the browser. The user's API key was stored in localStorage, and every chat message triggered a fetch request from the client straight to the AI provider. It was fast to build, easy to debug, and fundamentally insecure.

Six months ago, we rewrote the entire AI layer to run server-side through Supabase Edge Functions. API keys no longer touch the browser. Every AI call is proxied through an authenticated, CORS-restricted server function. This post explains why we made the move, what the architecture looks like, and the specific security measures we implemented.

The Problem with Client-Side AI Calls

When an application makes AI API calls from the browser, several security risks emerge:

For a CRM that handles sensitive sales data, these risks are unacceptable. Your contacts, deal amounts, email content, and pipeline data all flow through AI API calls. If the API key is compromised, all of that data is exposed.

The Server-Side Architecture

Our current architecture puts Supabase Edge Functions between the browser and the AI provider. The flow is simple in concept but robust in implementation.

Server-side AI architecture — Browser communicates with Edge Functions via authenticated HTTPS, Edge Functions hold API keys and call AI providers

Here is what happens when a user sends a chat message:

  1. The browser sends the message to a Supabase Edge Function via HTTPS, with a JWT token for authentication.
  2. The Edge Function verifies the JWT, confirming the user's identity and permissions.
  3. The function retrieves the user's AI provider settings and API key from encrypted secrets storage.
  4. It constructs the AI API call with the user's message, conversation history, and available tools.
  5. The AI provider processes the request and returns a response.
  6. The Edge Function validates the response, logs usage metrics, and returns the result to the browser.

The critical detail: the API key never leaves the server. It is stored as a Supabase secret, encrypted at rest, and only accessible within the Edge Function runtime. The browser never sees it, the network never carries it, and it cannot be extracted through DevTools or XSS attacks.

PKCE Authentication

We use Proof Key for Code Exchange (PKCE) for our authentication flow. PKCE is specifically designed to protect OAuth flows in public clients (like browser applications) where you cannot safely store a client secret.

In a standard OAuth flow, the client uses a secret to exchange an authorization code for a token. In a browser, that secret would be visible in the source code. PKCE solves this by having the client generate a random "code verifier" for each authentication attempt. The client hashes this verifier and sends the hash with the authorization request. When exchanging the code for a token, the client sends the original verifier, which the server validates against the hash.

This means that even if an attacker intercepts the authorization code, they cannot exchange it for a token without the code verifier, which was generated in-memory and never transmitted in a way that could be intercepted.

CORS: Restricting Who Can Call Our Functions

Cross-Origin Resource Sharing (CORS) headers control which domains can make requests to our Edge Functions. We configure strict CORS policies that only accept requests from our own domain: salessheets.ai.

Any request from a different origin is rejected before it reaches the function logic. This prevents a common attack vector where a malicious website tries to make authenticated API calls using the user's session. Even if a user is logged into SalesSheet and visits a malicious site, that site cannot call our Edge Functions because the CORS policy blocks the request.

We also restrict the allowed HTTP methods (POST only for AI calls) and headers, minimizing the attack surface further.

XSS Prevention

Cross-site scripting is one of the most common web vulnerabilities, and it is especially dangerous in an AI application. If an attacker can inject a script into our application, they could potentially read the AI responses (which may contain sensitive CRM data), modify the prompts being sent to the AI, or extract authentication tokens.

Our XSS prevention strategy has multiple layers:

Row-Level Security

At the database level, Supabase's Row-Level Security (RLS) ensures that users can only access their own data. RLS policies are enforced at the PostgreSQL level, meaning they cannot be bypassed by application logic — even if there is a bug in our code.

When an Edge Function queries the database on behalf of a user, the query runs with that user's permissions. A user querying contacts sees only their contacts. A user querying deals sees only their deals. There is no administrative override in the application code that could accidentally expose one user's data to another.

This is particularly important for the BYOK model, where each user has their own API key. The RLS policy ensures that User A's API key cannot be read by User B, even if they share the same Supabase instance.

SalesSheet security compliance checklist — six layers of protection from browser to database

Encryption at Every Layer

Data encryption in SalesSheet operates at multiple levels:

What Changed After the Migration

Moving from client-side to server-side AI calls required rearchitecting how the entire chat system works. The browser no longer manages AI state — it sends messages and receives responses. All tool execution, model routing, and response formatting happens on the server.

The trade-off was a small increase in latency. Adding a server hop adds about 50–100ms to each request. For most operations, this is imperceptible. For the simplest queries routed to Flash Lite, the total response time went from 400ms to about 500ms. We considered this an acceptable trade-off for the security improvements.

The benefits were immediate and measurable. No more API key exposure in DevTools. No more vulnerability to browser extensions. Full request logging and rate limiting. And the ability to add new security measures at the server level without requiring client updates.

Security as a Feature

For a CRM, security is not an afterthought — it is a feature. Your sales data is one of the most valuable assets your business owns. The contacts, relationships, deal values, and communication history in your CRM represent years of work. Any tool that handles this data needs to treat security as a first-class concern.

Moving AI processing server-side was one of the most important architectural decisions we have made. Combined with AI-powered email processing, calendar integration, and multi-provider AI support, the security infrastructure ensures that powerful AI features do not come at the cost of data safety.

Enterprise-Grade Security, Startup-Simple UX

SalesSheet keeps your data safe with server-side AI, PKCE auth, and row-level security — all invisible to you.

Try SalesSheet Free — No Credit Card