# The token boundary

> A Chatfuel token can read and change every bot in an account, so it never leaves the server — and everything about the proxy follows from that.

Page: https://sdk.chatfuel.com/docs/concepts/token-boundary
Markdown: https://sdk.chatfuel.com/docs/concepts/token-boundary.md

A Chatfuel token can read and change every bot in an account. There is no read-only token and no
per-bot token, so a token in a browser is an account in a browser.

It therefore never gets there. Every call the app makes goes to its own origin under
`/chatfuel/*`, and a proxy running on the server side attaches the token. The client in the browser
is constructed without one — there is no code path that could send it, because it has nothing to
send.

## One proxy, three hosts [#one-proxy-three-hosts]

The proxy is one source tree with three ways to run it, which is why the gate, the fences and the
error codes are identical wherever you deploy.

| Where           | What runs it                                                            |
| --------------- | ----------------------------------------------------------------------- |
| Development     | a Vite plugin, inside the dev server                                    |
| Your own server | `server/entry.ts`, a Node HTTP server that also serves the built client |
| Vercel          | `api/chatfuel.ts`, a serverless function behind a rewrite               |

All three mount the same routes: a GraphQL `POST`, a WebSocket on the same path, and a REST
passthrough for file uploads.

## What the proxy strips, and what it adds [#what-the-proxy-strips-and-what-it-adds]

The outgoing request is built from scratch rather than copied and amended. That is the whole
mechanism: the browser's `Authorization` header and its cookies are not stripped by a filter that
someone could forget to update — they are never in the object that goes upstream at all. The only
thing added is the token.

Nothing prints it, either. The master token is never logged, and the one outbound-proxy string that
does reach a log line has its password replaced first.

A missing token is a configuration problem, not a crash: the proxy answers with a synthetic
`ProxyTokenMissing` envelope rather than taking the dev server down with it.

## The WebSocket is relayed, not forwarded [#the-websocket-is-relayed-not-forwarded]

Subscriptions cannot be a tunnel, because a tunnel would carry whatever the browser sent as its
credentials. So the relay reads frames. It accepts the subprotocol, waits for the browser's
`connection_init`, decides whether that connection may proceed, and only then opens the upstream
socket and sends its own `connection_init` carrying the token. Every other frame passes through
verbatim in both directions.

The consequence worth knowing: an unauthenticated socket never touches Chatfuel at all. When the
upstream socket does drop, the browser's socket is closed too and the client reconnects through a
fresh relay.

<Callout type="warn">
  Anything named `VITE_*` is compiled into the browser bundle at build time. Anything else is read
  by the proxy at runtime. A secret with a `VITE_` prefix is not a secret — which is why the token,
  the Supabase service-role key and the admin password do not have one. The full list of variables
  and which side reads each is in [environment
  variables](/docs/deploy/environments).
</Callout>

## What it costs [#what-it-costs]

Two things.

The hop is real: every call is your origin, then Chatfuel. On Vercel that hop is a function with a
duration limit, which is what bounds how long a subscription's socket lives — see
[Vercel](/docs/deploy/vercel).

And the proxy is code in your project, not a service we run. The whole tree is copied in under
`vendor/chatfuel-proxy/`, and your app declares `ws`, `undici` and `https-proxy-agent` as runtime
dependencies because of it. If you delete it, you have nothing to talk to Chatfuel with.

With the `auth` module installed the same proxy takes on a second job — deciding whose request this
is, not only where it goes. That is [the auth gate](/docs/concepts/auth-gate).
