Security

How we protect your repositories and your writing.

Last updated July 29, 2026

Vale CMS opens your project and writes changes back to it, so the bar for handling that content is high. This page is about how things are protected; the privacy policy covers what is collected and who else is involved.

The short version: your git tokens and your writing never reach our servers in a form we keep. The engine processes a request and forgets it. An injected script cannot run on this page at all — and if one somehow did, it could only talk to a short list of hosts we have named. Every claim below describes what the code does today.

Where your credentials live

None of these are held on our servers. That is a deliberate choice, not a default we never got around to changing.

CredentialWhere it livesEver on our servers?
GitHub / GitLab access tokenYour browserForwarded per request, used, discarded — never stored
Account sessionYour browserVerified per request; nothing kept beyond your account record
MCP tokenWherever you paste it, in your client's configMinted on request, never stored — copy it once or generate a new one
AI provider keyYour browser, left over from the assistant that is off in this versionNo — it was only ever sent from your browser to the provider you chose

The alternative — holding everyone's git tokens for them — would close one gap and open a larger one. A single breach of our infrastructure would expose every connected user's repository access at once, instead of one person's. We would rather run a design where breaking in gets you nothing worth having.

The trade-off is real, and worth naming. A token sitting in your browser can be read by anything that gets script execution on the page, or by anyone with hands-on access to your device.

There is published guidance on exactly this decision, so we will measure ourselves against it rather than ask you to take our word. Auth0's token storage guidance recommends keeping tokens in memory only, never in browser storage. We do not do that. It is a real departure from the recommendation and not a technicality, so here is what we do instead.

That guidance treats browser storage as a fallback for cases where iframe-based or cross-domain-cookie approaches are blocked by browser privacy restrictions. That is not our situation and we are not going to claim the exemption. What we have done is implement the mitigations it names for that fallback anyway: there is no third-party JavaScript on this site at all, and rather than integrity-check third-party scripts, section 2's fingerprinting covers every script on the page. Token lifetimes are short — section 5 lists them — though we should be precise that the guidance ties that particular mitigation to implicit and hybrid flows, which section 3 explains we do not use.

Only our own code runs on this page

Every script we intend to run is fingerprinted when the site is built, and your browser will only execute scripts matching one of those fingerprints. A script injected some other way — a compromised dependency, a malicious paste — does not match, so it never runs. That is the difference between hoping nothing gets injected and an injection being inert.

If one somehow did run, it still could not send your token anywhere it liked. The list of hosts this app may talk to is short and named: our engine, our sign-in provider, GitLab's login endpoint. We do not allow the whole web as a wildcard even though it would be simpler to write, and the same applies to images — a stolen token can otherwise be smuggled out through something as innocuous as an image URL, so image sources are named too.

This page also refuses to render inside anyone else's page, which rules out a class of attacks that work by putting an invisible frame over a button you meant to click.

Signing in

Both git hosts use the authorization code flow with PKCE — the flow the same guidance recommends for browser applications, in place of the older implicit flow that handed tokens straight back in the redirect. How each host applies it differs, for good reasons.

GitHub issues tokens through a confidential exchange: our server holds a secret that never reaches your browser, and only our server can complete the handshake. On top of that we use PKCE — a one-time random value your browser generates per sign-in. An intercepted authorization code is useless without either our server's secret or your browser's one-time value. The two mechanisms cover slightly different attacks, and neither is expensive to run alongside the other.

GitLab does not require a server secret for this kind of integration, so the whole exchange happens in your browser. There, PKCE is the only thing standing between an intercepted code and a stolen token, which is why it is not optional.

In both cases we verify a state value on the way back — the standard defence against someone else's sign-in being slipped into your session.

What protects the parts you cannot see

  • The engine fails closed. If the subscription check is ever misconfigured, repository access refuses to work rather than quietly opening to everyone. We would rather break a feature than leave a door open.
  • Sign-in tokens are bound to us specifically. A token has to be issued by our own auth project and carry a verified email, or the engine rejects it — so a token from an unrelated project, or an unconfirmed signup, is never mistaken for a paying account.
  • Requests we make on your behalf cannot be turned inward. When a feature fetches a URL you supply, such as syncing an external package, the engine refuses internal and local addresses — the ones an attacker would aim at infrastructure that was never meant to face the internet.
  • Every endpoint is rate-limited. Linting as you type gets generous headroom; expensive operations like running a script or rendering a template get a tighter cap; and a background limit catches anything spread thin across many requests.
  • Rule scripts run in a real sandbox. Bounded execution time and bounded memory, so one runaway or hostile rule cannot take the service down with it.

How long credentials last

A stolen credential should have a shrinking window of usefulness rather than an open-ended one.

CredentialLifetime
GitHub access tokenA few hours, refreshing automatically for as long as you keep working — around six months before you would reconnect
GitLab access tokenAbout two hours, also refreshing automatically
Account sessionAbout an hour, refreshed silently by your browser
MCP token30 days by default; regenerate or revoke it whenever you want
AI provider keyNo expiry — it is your key, on your terms. Remove it any time in Settings

What we deliberately don't do

No analytics, no trackers, no advertising SDKs, no third-party scripts. No server-side database of git tokens. No storing your documents anywhere once a request has finished. We do not sell your data and we do not train models on your content.

If a decision here ever looks like it trades your security for our convenience, treat that as a bug and tell us.

Honest limitations

We would rather state these than have you discover them.

  • Browser storage is not invisible to someone with hands-on access to your device, or to a browser extension you have granted broad permissions. Section 2 is real protection against remote attacks, not a claim that client-side storage is as strong as a hardware key.
  • We do not hold a SOC 2 report. Vale CMS is a small, independent product and we would rather say so than imply otherwise. If your team needs a security review, a data processing agreement, or a named list of subprocessors, email us and we will work through it with you — the privacy policy already names every processor we use.
  • Security work here is continuous rather than finished. This page reflects the current state, and we would rather keep it accurate than impressive.

Reporting a vulnerability

Email hello@vale.sh with what you found and enough detail to reproduce it. We will acknowledge it, fix what is real, and credit you if you would like us to. We are not going to argue with a good-faith report, and we will not pursue anyone for research that avoids privacy violations, data destruction, and service disruption.