What runs where, why the Google key is kept on the server, and how to deploy, roll back, and change settings.
This is the technical page. The three guides before it describe using the app; this one describes the machinery underneath, for whoever has to change or repair it.
The short version: a small web page, and one program on Cloudflare’s network that serves that page and does the one job the page cannot safely do itself — talking to Google.
Your browser lineentry.traveller.org
the form ─── POST /api/sheets ──▶ Cloudflare Worker "lineentry"
│ holds the Google key
│ signs a request to Google
▼
Google Sheets API
│
▼
the Budget spreadsheet
Everything is one deployable. The Worker serves the built web page and answers the one
API address, /api/sheets. There is no separate server, no database, and nothing else to
keep running — the spreadsheet is the only data store.
Requests for ordinary files — the page, its stylesheet, its icons — are served straight
from Cloudflare’s edge and never wake the Worker at all. Only /api/sheets, and requests
for things that do not exist, reach the program.
This is the single most important thing to understand before changing anything.
The app signs in to Google as a dedicated service account, using a private key. That key
grants read and write access to the entire Budget workbook — not just the expense
tab, but every tab in it, including the ones holding compensation and severance figures.
Until August 2026 that key was built into the web page itself. Anything in a web page is readable by anyone who opens it, so the key was effectively published: no password, no trick required, just view the source. It has since been replaced, and the exposed one revoked.
So the rule now, and the reason the Configure window cannot change the spreadsheet:
The key exists only in the Worker’s settings on Cloudflare. It is never sent to the browser, and nothing that reaches the browser may ever contain it.
The deploy refuses to publish if it finds a key in the built files — a check that exists precisely because this went wrong once and nobody noticed for weeks.
The app has no login, so anyone who knows the address /api/sheets can call it. It is
deliberately built to make that not matter much. It can:
It cannot:
=, +, - or @
is forced to plain text, because a live formula could otherwise pull data out of tabs
the app is not allowed to readThe worst an outsider can do is add junk rows to the expense tab. That is annoying rather than dangerous, and Google Sheets’ own version history undoes it. Adding a password is the fix if it ever stops being acceptable.
Pushing to the main branch is what publishes. Nothing else does.
This is instant and does not need a code change. The same instructions are on the app’s own page, under How to update this app.
Four settings live on the Worker rather than in the code, under Workers & Pages → lineentry → Settings → Variables and Secrets:
| Setting | What it is |
|---|---|
SHEETS_SPREADSHEET_ID | which spreadsheet to write to |
SHEETS_ALLOWED_TABS | which tabs may be written to, separated by commas |
GOOGLE_SA_EMAIL | the Google account the app signs in as |
GOOGLE_SA_PRIVATE_KEY | its private key — stored as a Secret, so it can be replaced but never read back |
The first three are also written into the project’s configuration file, so a deploy restores them. The key is not, and must never be.
Changing the spreadsheet or the tabs is covered step by step in When Something Goes Wrong, and inside the app under Configure. No code change is needed for either.
Do this if the key is ever exposed, or as routine hygiene.
GOOGLE_SA_PRIVATE_KEY and paste the new key’s private_key
value, including its \n sequences.One trap worth knowing. The Worker remembers its Google sign-in for up to an hour, so for a while after a key change it may keep working on the old one. A successful test straight after a change proves less than it appears to. To force a genuine test, redeploy — that starts the program fresh — and then check.
| Part | Where |
|---|---|
| The page you see | src/ — React and TypeScript, built with Vite |
| The Worker | worker/index.ts (routing) and worker/sheets.ts (the Google work) |
| Hosting configuration | wrangler.toml |
| Publishing | .github/workflows/cloudflare-deploy.yml |
| These guides | docs/guides/ |
| Full technical notes | CLAUDE.md in the repository |
CLAUDE.md is the deep reference — the decisions, the things that fail silently if
changed, and the incident history. Read it before changing the code.
It began as a Bolt.new project published by dragging a folder onto Netlify. In August 2026
it moved to a Netlify site that rebuilt from GitHub automatically, and the Google key was
moved out of the web page and onto the server. In September 2026 it moved again, to a
single Cloudflare Worker at lineentry.traveller.org, which is where it lives now. Netlify
has been retired and the key that was held there deleted.