Lemon SqueezyHow it works

How accounts work together

An account is a person, and it joins a kitchen to cook in. Two phones in one kitchen keep their own copies and agree; the kitchen’s owner says what each member sees; a share or a guest needs no kitchen at all.

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
The page an invite link opens on a phone once it has let the account in: You are in The Wilsons, and one button, Start cooking.
An invite, accepted. Photographed at commit fdac8cbf2, 26 October 2026.

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 share

You can send someone a recipe by making a share link, including one you imported.

From PRIVACY.md, Sharing a recipe

A link runs out after a small number of opens.

From PRIVACY.md, Sharing a recipe

Someone who opened your link and kept the recipe has their own copy in their own account, the same way a friend you texted a recipe to has it written down.

From PRIVACY.md, Sharing a recipe
A shared recipe opened on a phone by someone with no account: the ring, the dish’s name, a link to the original recipe and its ingredients by aisle, with a line saying nothing is kept once the tab closes and the button Cook it now.
A share, opened with no account. Photographed at commit 2f09074fd, 26 October 2026.

Sharing a recipe

PRIVACY.md, the whole section

You can send someone a recipe by making a share link, including one you imported. It is the only thing that puts a recipe of yours in front of another person, and it never happens on its own: there is no public page, no profile, no listing of anyone's recipes, and Lemon Squeezy never makes a share you did not ask for. What you are allowed to share, and what we do when an author objects, is in the terms; this section is what a share is made of and what we keep about it.

What the person opening it gets is the recipe, credited, with a link to where it came from: the ingredient lines, the steps in Lemon Squeezy's own words, and anything you wrote or photographed yourself. Your own notes on that recipe travel with it, so treat them as part of what you are sending. What does not travel is anything else about you. Not your email address, not your other recipes, not your cooking history, not a note you wrote on anything else. The original author's prose and photographs do not travel either, which is a rule in the terms' import policy rather than a privacy one.

A share is pinned to the version you sent. It hands over the recipe as it was when you shared it, so editing it afterwards does not rewrite what somebody was already given.

What we store about a share is five things: the link itself, which recipe it points at, which version, how many times it has been opened, and when you made it. That is the entire record.

We store nothing whatever about the person who opened it. No identifier, no address, no time of opening, no location, no read receipt. A share knows how many times it was taken and nothing at all about who took it, which is why Lemon Squeezy cannot tell you whether your friend has looked at it yet — there is nothing to show, so no screen shows it. That is a deliberate limit rather than a feature we have not built.

A link runs out after a small number of opens. A chat app's link preview, a mail scanner reading the link or a browser fetching the page ahead of time spends nothing: an open is counted only once the page is actually open on a screen. Reading your own share costs nothing either.

Somebody who opened it can keep reading it without spending another open, for as long as that browser session lasts. That is the second of Lemon Squeezy's two cookies, described under Cookies below: it names the one share and nothing else.

Deleting the recipe, or your account, takes its share links with it.

What sharing cannot do is come back. Someone who opened your link and kept the recipe has their own copy in their own account, the same way a friend you texted a recipe to has it written down. Deleting yours does not reach theirs, and neither does a takedown.

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
The page a guest’s link opens on a phone: Sarah is cooking Bucatini all’Amatriciana and asked you to help, a field for your name and the button Join the cook.
A guest arriving at someone’s cook. Photographed at commit 2f09074fd, 26 October 2026.

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.