Verify for yourself
Last updated: May 2026
How the guarantee is enforced
Three independent layers stack on top of each other. Each one alone would block survey data from reaching a Recense server; the combination is what makes the claim reproducible.
Layer 1 — Server-side 403
Every request to /api/* on a local-auth deployment is rejected at the SvelteKit
hook with a 403 response, except for one endpoint that returns the published policy itself
(/api/v1/local-only-policy). Two narrow exceptions exist for specific build
variants — /api/v1/org/byok when the build flag PUBLIC_ORG_BYOK_ENABLED is
on, and /api/v1/dev/* when the dev server is running. The Canva design-partner
build ships with both flags off.
Layer 2 — Client-side network guard
At browser bootstrap, the app patches every outbound network primitive
(fetch, XMLHttpRequest, WebSocket, EventSource, navigator.sendBeacon) and routes each call through a policy classifier. Any request
that doesn't match the published allow-set throws synchronously. Because the patches install
before any feature code runs, no application path can issue a network call without going
through the guard.
Layer 3 — Content Security Policy
The HTTP response sets a Content-Security-Policy header that restricts connect-src to a closed list. In local-auth mode the list is: your own origin,
your Supabase auth host, the four AI provider hosts you may BYOK to, and four ws://localhost origins for the optional desktop bridge. Any other destination is
blocked by the browser before the network guard or the application code has a say.
Verify it in five minutes
Step 1 — Inspect the CSP header
- Open the local-auth Recense URL in a fresh browser profile.
- Open DevTools → Network tab → reload the page.
- Click the document request (the one whose
Typeisdocument). - In Response Headers, find
Content-Security-Policy. - Read the
connect-srcdirective. The full list of allowed destinations is right there. Anything not on that list is browser-blocked.
For the Canva design-partner build, the directive should resolve to:
connect-src 'self'
<your-supabase-auth-host>
https://api.anthropic.com
https://api.openai.com
https://api.fireworks.ai
https://generativelanguage.googleapis.com
ws://localhost:9752 ws://127.0.0.1:9752
ws://localhost:9753 ws://127.0.0.1:9753 If you see an origin not on that list, the claim is broken — please email security@recense.ai.
Step 2 — Watch the Network tab during a real session
- Keep DevTools → Network open and clear it.
- Sign in.
- Import a
.savfile from your machine. - Build a few crosstabs.
- Configure a BYOK API key in Account → AI and run an agent prompt.
- Save your project locally and export.
Every outbound request you see should be one of:
- Your Supabase auth host, on a path under
/auth/v1/*(sign-in, sign-out, session refresh). - Your AI provider's API origin (only the one matching the BYOK key you entered), for the agent's API call.
- Cloudflare's CDN origin for static assets (HTML, JS, fonts, the WebAssembly engine).
You should not see a request to a Recense-controlled origin carrying a survey payload. AI conversations go directly to the provider, not via a Recense proxy.
Step 3 — Try to break it
From the browser console, in a local-auth session, run:
fetch('https://example.com') The call should reject synchronously with an error that names the layer that blocked it (network guard) or be blocked by the browser's CSP (which logs to the console). Either way, the request never goes out.
Step 4 — Inspect the bootstrap order
The network guard installs at module top-level in src/hooks.client.ts. Any application code that runs in the browser does so via
later imports, so there is no app code that can reach the network before the guard. If you
are auditing the source under a design-partner agreement, the relevant entry points are:
src/hooks.server.ts— Layer 1 catch-all 403.src/lib/network-guard.ts— Layer 2 monkey patches.src/lib/local-only-policy.ts— single source of truth for allow-lists, also used to render the CSPconnect-src.
Two-layer enforcement
The disabled-features list in the Proposal Appendix B names ten flags. Six of them
(sentry, builtin-agent, billing-heartbeat, account, support, sharing) are checked directly at the
feature surface — search the frontend for isLocalOnlyFeatureDisabled to find the
gate. Four (hosted-mcp, cloud-projects, cloud-datasets, marketing-network) have no dedicated check at their feature surfaces; their
corresponding network calls are blocked by the server catch-all 403 (Layer 1) and the client
network guard (Layer 2) above. We list them in the disabled-features array so the published
count stays consistent with the Proposal and so an auditor reading the source sees the
intent. A reader grepping for the flag name lands on this comment, not on a dead constant.
What would break the claim
- An origin appearing in
connect-srcthat isn't on the published list. - A successful outbound request to any host other than Supabase auth, your BYOK provider, or Cloudflare CDN, during normal product use.
- The build flag
PUBLIC_LOCAL_ONLY_PROFILEset to anything other thanlocal-authon the Canva deployment. - A new
/api/*route landing without an explicit allow-list entry. The repository CI runs an enumeration test that fails when this happens, but you can also confirm in the browser by issuing a request and seeing the 403.
Disclosed exceptions
- Single GET to
/api/v1/local-only-policy. Returns the policy itself so the client can render the same disabled-features list a reader sees here. - Single GET / POST to
/api/v1/org/byok. Disabled by build flag on the Canva deployment. When enabled, it stores an organisation-shared encrypted BYOK provider key — passphrase-derived AES-GCM ciphertext only, never plaintext. ws://localhost:9752–9753. Local desktop bridge for users who run the optional Recense MCP companion process. The browser will not accept incoming connections from any other origin.
Questions
If anything in your verification doesn't match the published claim, contact security@recense.ai. Reproducible counter-examples are gratefully received.