An account, then a kitchen
An account is minted with no kitchen, and onboarding asks whether to create one or join one — owner ruling, 19 August 2026.
From KITCHENS.md, An account is minted with no kitchen
Asking once costs a screen and makes the household case identical to the solo one — you have exactly the kitchen you meant to have.
From KITCHENS.md, An account is minted with no kitchen
An account may be in up to five kitchens, and may make, switch, leave, delete, invite and join within that.
From KITCHENS.md, Five kitchens, leaving and deleting
An account is minted with no kitchen
KITCHENS.md, the whole section
An account is minted with no kitchen, and onboarding asks whether to create one or join one — owner ruling, 19 August 2026. Minting a home kitchen with the account is invisible to a solo cook and a tax on the second person: the partner who signs up to join a household would arrive owning an empty kitchen nobody asked for, and spend the rest of the app's life with two of them, a switcher to understand, and a question about which one a recipe went into. Asking once costs a screen and makes the household case identical to the solo one — you have exactly the kitchen you meant to have. So "this account has no kitchen yet" is an ordinary state rather than a fault, and every reader of it sends the cook to onboarding rather than refusing them.
Five kitchens, leaving and deleting
KITCHENS.md, the whole section
An account may be in up to five kitchens, and may make, switch, leave, delete, invite and join
within that. Owner ruling, 12 September 2026, overruling the earlier one that kept making a kitchen
to first run. The ceiling is KITCHENS_PER_ACCOUNT in libs/server, enforced in the store on both
making and joining and sent to the screen so it can say so before the server refuses. Leaving and
deleting are two ways out for two standings, never offered together: a member of a shared kitchen
leaves, taking nothing the kitchen holds; a cook alone in one deletes it, taking everything, because a
kitchen nobody is in is unreachable forever and a household's book is not one member's to destroy.
SCREENS §12 is the screen and deleteKitchen in libs/store-pg the one
home for what a deletion reaches.
Two phones in one kitchen
You are in one kitchen at a time, not looking at a merged view of every kitchen you belong to, and which one is per device, not per account (owner, 13 October 2026: switching at the lake house must not switch the laptop at home).
From KITCHENS.md, One kitchen at a time, and the shelf that shows every home book
When a sync happens: a page load, a reconnect, a write, a return to the tab, a clock — and the other phone’s word.
From ARCHITECTURE.md, When a sync happens
An installed app is loaded once and then lives for days, so a page load and a reconnect were not enough: a night planned on one phone sat owed until that phone was next reloaded, and the other phone never asked.
From ARCHITECTURE.md, When a sync happens
Sync has a face, because a mechanism nobody can see is not trust.
From ARCHITECTURE.md, Sync has a face
One kitchen at a time, and the shelf that shows every home book
KITCHENS.md, the whole section
You are in one kitchen at a time, not looking at a merged view of every kitchen you belong to, and
which one is per device, not per account (owner, 13 October 2026: switching at the lake house must
not switch the laptop at home). A new device lands in the person's home kitchen, a device says where it
is by a cookie it sends with every request, and the server checks membership every time, so the cookie
is only ever a hint. A union would make list span tenants, so every recipe would have to carry the
kitchen it came from just so a later get could be routed, and a recipe id is only unique within a
kitchen (every kitchen holds a recipe_amatriciana): the shape
ARCHITECTURE.md refuses a tenant parameter for. One at a time
keeps the store scoped to a single id and the plan and the list unambiguous: a house with three
shopping lists is worse off than a house with one.
The shelf is the one place that is not one at a time (owner, 13 October 2026, after three adversarial reviews of the model against a family week at a vacation house: "I want there to be no friction."). A kitchen shows its own recipes plus the whole home book of every member, grouped by person (Ours, Mum's, Jen's), so nothing is brought ahead. A member of a recipe's home kitchen edits the original wherever they cook it; anyone else who changes it keeps a copy of their own. A trip kitchen is not a kind: it is an ordinary kitchen that lives as long as its people keep it. The plan, the list and the hob stay the current kitchen's.
When a sync happens
ARCHITECTURE.md, the whole section
When a sync happens: a page load, a reconnect, a write, a return to the tab, a clock — and the other
phone's word. lib/sync-now.ts is the one caller of sync(), one attempt at a time with one more after
it for whoever asked mid-flight; hooks/use-sync.ts is the one home for the occasions. An installed app is
loaded once and then lives for days, so a page load and a reconnect were not enough: a night planned on
one phone sat owed until that phone was next reloaded, and the other phone never asked. So every write
through the synced store announces itself (lib/local-writes.ts, told by createSyncedStore's onWrite
after a cook's own write and never after a pull) and earns a sync SYNC_SOON_MS later (its docblock holds
the owner's timing of two phones: "I want maximum speed"), and that sync carries the taps appended since
the last push and not the evening's whole log (SyncPending.events); a tab coming back into view asks at once; and
while the tab is in front a timer asks every 45 seconds. Nothing reaches the network while the tab is
hidden, and a device the server last told has no account keeps its clock, its writes and its stream quiet
(mayHaveAccount); only a sign-in, which ends in a full page load, changes that answer.
Every wait on the server has an end (lib/server-patience.ts), because a network that accepts a
request and never answers gives fetch nothing to fail on. The one in front of a paint is
lib/store.ts asking which kitchen's cache to open on a phone that does not know yet: after
OPEN_PATIENCE_MS it opens as an offline phone's does and paints from what it holds, the late answer
still remembered for the next load. A sync's round trip is given up on after SYNC_PATIENCE_MS, so
one hung request cannot hold sync-now.ts's single place in line.
Sync has a face
ARCHITECTURE.md, the whole section
Sync has a face, because a mechanism nobody can see is not trust. The journal carries lastSyncedAt
beside the cursors, by the device's own clock, since what it is read for is "how long ago" on the phone
doing the reading, and views/sync-state.ts folds it together with the last attempt into a standing.
Most of the standings are an app working normally: signed out, signed in with no kitchen yet, offline, and
up to date. Signed out is not a failure and never reads as one; a 401 forever is this design working, and
a count of what is waiting would tell a cook with no account that their cooking is on its way somewhere.
The 409 beside it is a separate standing rather than the same one worded loosely: it is answered to
somebody who is signed in, and until an account has a kitchen every attempt gets it.
What a member may see
The kitchen’s owner says what each member may see of it, and only of it — owner rulings of 20 and 21 October 2026: "You are giving permission to view the recipes and lists in this kitchen. This changes nothing about their own lists in their own kitchen."
From KITCHENS.md, What a member may see: roles
The week’s plan is not a role: it stays one plan per kitchen, so a night whose dish a member may not view shows its name only.
From KITCHENS.md, What a member may see: roles
The server is the enforcement and the phone only mirrors it
From KITCHENS.md, What a member may see: roles
What a member may see: roles
KITCHENS.md, the whole section
The kitchen's owner says what each member may see of it, and only of it — owner rulings of 20
and 21 October 2026: "You are giving permission to view the recipes and lists in this kitchen. This
changes nothing about their own lists in their own kitchen." The owner is whoever made the kitchen
until they hand it to another member (the paragraph on removing, below, has how). A member's roles are one switch for the recipes (all of this kitchen's
recipes and what is made of them: versions, drafts, evenings, the home books opened through it), one
switch per list by its id, and one for lists made later. The week's plan is not a role: it stays
one plan per kitchen, so a night whose dish a member may not view shows its name only. Members who were
in a kitchen before roles existed keep everything, lists made later included; a member who joins after
starts on the recipes and the grocery list, every other list off. The server is the enforcement and the
phone only mirrors it: membershipOf carries each kitchen's roles, makeStoreFor will not build a
store without them, a route that reads or writes the kitchen's recipes answers 403 to a member who may
not view them (needs: 'recipes'), and sync cuts both directions. A write the member may not make is
dropped from a sync push rather than refused, so a stale phone keeps syncing the rest. The phone is
told what it may see in every answer (sees) and drops what it holds beyond it; a member without the
recipes is also sent the title of each dish the plan names (withheldDishes, read at the pull and never
stored, no other recipe's name), which is how a night on the shared plan still says what it is.
A guest at the stove
A family kitchen is durable and shares everything; a reunion shares one evening.
From KITCHENS.md, A reunion is a cook, not a kitchen
the recipe stays in the host’s kitchen, and a guest is invited to the session rather than to the kitchen. That invitation grants two things — read of one recipe version, and append to one session’s log — and expires with the evening.
From KITCHENS.md, A reunion is a cook, not a kitchen
A reunion is a cook, not a kitchen
KITCHENS.md, the whole section
A family kitchen is durable and shares everything; a reunion shares one evening. What the cousins need is a schedule that re-derives around six pairs of hands and one log everyone appends to, which is group cooks exactly and needs no durable row: the recipe stays in the host's kitchen, and a guest is invited to the session rather than to the kitchen. That invitation grants two things — read of one recipe version, and append to one session's log — and expires with the evening. Cousins who cook together every year can of course also make a kitchen, at which point they are a household by this model, which is the right answer rather than a loophole.
That distinction is what stops kitchen time being somebody else's: a guest's evening lands in
neither book as a total. Not in the guest's, because the session belongs to the host's kitchen,
against a recipe that is not in the guest's book — there is nothing of theirs to attach a total to,
and manufacturing one would mean copying somebody else's recipe into their library uninvited. And not
in the host's as a stranger's evening, because there is only one evening: the invitation is to append
to the host's session, never to open a session of your own in a kitchen you are not in. A guest's
taps stay theirs, because the events carry their cook, which is what lets the host's kitchen say
who did what.
The same reasoning answers the case of a kitchen people share from rather than cook in — a food
writer's, say. Accepting a share puts a copy in the recipient's kitchen (below), so their
evenings feed their own figure and nobody ever cooks inside the sender's. A signed-out cook is safe
for a different reason: nothing of that evening survives the visit, so there is no total to file
anywhere. Three protections, and each is a copy rather than a reference — a well-meant optimisation
that turned any of them into a reference is exactly how a sender's figure would start moving under
them. A writer who wants "this takes people about forty minutes" is asking for the across-kitchens
aggregate, which is the everyone tier and a different feature with its own threshold and opt-out.
What is paid
Cooking, planning, notes and shopping lists have no limits and never will: the paid tier exists only for the parts that cost money every time they run.
From TERMS.md, Paid plans
Paid plans
TERMS.md, the whole section
Lemon Squeezy is free to use. Cooking, planning, notes and shopping lists have no limits and never will: the paid tier exists only for the parts that cost money every time they run.
Free gives you 10 recipe imports a month and 5 model-backed assistant questions a day. Plus is $5 a month and lifts both: imports, subject to a ceiling that exists to stop scripts rather than to meter anybody, and the assistant's daily cap, with no ceiling at all.
Cancelling never removes data. An import is expensive once and nearly free to keep, so a lapsed subscription keeps every recipe you have ever imported. You drop back to the free tier's limits on new work, and nothing you already have goes away.
Payments are handled by Paddle, which is the seller of record. Your contract for the payment itself is with them, they issue the invoice, and their terms and refund policy apply to it. We never see your card.
Prices can change. If they do, we will tell you before your next renewal, and you can cancel.