kitshn
Architecture

How kitshn works

All of kitshn's logic and state run in one Cloudflare Worker with one Durable Object. Machines do only machine work: tunnels, Compose apps, local sites and commands.

Parts

The parts

People and devices reach Cloudflare Access. Behind it are the kitshn Worker and Durable Object, static and Worker apps, R2, and one tunnel per machine. On the machine, the kitshn daemon keeps a WebSocket to the Durable Object and runs Compose apps and live sites.

kitshn Worker

Serves the map, checks Access tokens and GitHub OIDC tokens, and serves pages, files and the offline page.

Durable Object

Holds all state in SQLite. Keeps the sockets of the daemons and of open maps. An alarm queue writes changes to the Cloudflare API, at most 40 calls per run.

Cloudflare Access

Logs people in with an email code, GitHub and a passkey. It checks the circles at the edge, so a visit to an app never runs kitshn.

Daemon

One binary per machine. It runs cloudflared, deploys Compose apps in rootless Docker, checks local sites, and keeps 30 days of samples on its own disk.

Recipe and CLI

kitshn.config.ts in the app repo. In GitHub Actions, kitshn check turns it into JSON without running it, and kitshn deploy sends it.

GitHub App

Removes a preview when its pull request closes, writes deployment statuses, and starts the waiting run when you click Allow.

Access

Circles become Access policies

A card in the Friends circle gets an Access application with an allow policy for ana@example.com. A card in the Everyone circle gets a Bypass policy and opens with no login.

Each circle becomes one reusable Access policy for its people, and one for its devices and networks. Each card with an address becomes one Access application, with the policies of the circles that hold it. A card in Everyone gets a Bypass policy.

kitshn is the source and Access is the copy. A daily check repairs differences, and kitshn never touches an Access object that it did not make. A lower level takes effect at once: kitshn ends the open sessions.

The tunnel of a machine checks the Access token too, so a wrong DNS record cannot skip the login.

Deploy

A deploy, step by step

A push runs GitHub Actions. Actions sends the recipe and its OIDC token to the kitshn Worker. The app goes to Cloudflare or to a machine, and its card gets Access from its circles.
  1. GitHub Actions reads kitshn.config.ts as data and sends it with the run's OIDC token. kitshn accepts only repos of the GitHub accounts on its list.

  2. A repo that kitshn has never deployed waits for one click on Allow. After that, every push deploys. Block cuts a repo off at once.

  3. A static site or Worker app: Actions uploads the files, and kitshn uploads the Worker through the Cloudflare API. Static requests are free.

  4. A Compose app: kitshn picks the first online machine with the labels deploy and those of the recipe. The daemon gets the source through R2, checks the Compose file, builds at low CPU priority, and starts the app on a Unix socket.

  5. kitshn adds the Access application before the route, then the DNS record and the route. A removal runs the same steps in reverse, so an address is never open without its check.

State

Where state lives

State Lives in
Circles, members, shares, card places, history Durable Object SQLite. Never in Git.
The recipe The app repo. It is the only state in Git.
App values and secrets Durable Object SQLite, secrets encrypted. KITSHN_* GitHub values win.
Pages and files R2
Access applications, tunnel routes, DNS records Cloudflare, compiled from the map
Machine tokens and keys A file on the machine that only the daemon user reads
Compose data and logs, sample history The machine's disk
Budget

It fits the free plan

The reference setup is 5 machines, about 15 apps, about 50 pages, 2 people and a few guests. Estimates for it:

Limit Free kitshn uses
Worker requests 100,000 a day about 500 a day
Durable Object requests 100,000 a day about 4,200 a day
Access seats 50 people about 7. Guest seats free up after a month.
Service tokens 50 about 9: one per machine and device
Worker scripts 100 about 15. New previews stop at 90.

App traffic goes through Access and tunnels, not through the kitshn Worker. A paid plan only raises limits; no feature needs it.

More

Read further

SPEC.md holds every numbered requirement. docs/architecture.md has the request paths and the full budget.