Build Cozi Gold
YESreplaces $3.25/mosaves $39/yrback to the verdict
A family organiser where the technical work is unglamorous and the failure modes are social: a shared calendar that handles recurrence and time zones correctly, lists that two people can edit at once without losing an item, and a meal plan that produces the shopping list.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the server.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree locally
Why Shared household data with concurrent editors needs real transactions.
Get it Docker or your package manager. open ↗
Verify
psql connects and creates a table - installfree
Why Writing recurrence expansion yourself means reimplementing a specification whose edge cases are exactly the bugs you are trying to avoid.
Get it A maintained library for recurrence rules, plus your platform's zone data kept current.
Verify
A weekly rule expands correctly across a daylight transition in a test - accountfree tiers cover a household
Why Phase 5 needs delivery somewhere real, and reminder timing bugs only appear against an actual channel.
Get it Email through any transactional provider is sufficient, or push if you are building a mobile client later.
Verify
A test reminder arrives at the right time - have readyfree, and it will produce feedback
Why Concurrent list editing, notification fatigue and role restrictions are all social failures that a single tester cannot observe.
Get it Your own household, using it for real for two weeks including a supermarket trip with two people on the same list.
Verify
Three people have used it for two weeks and complained about something
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- households(id, name, timezone, created_at) - people(id, household_id, name, colour, role, login_email) · role is adult or child - events(id, household_id, title, kind, starts_at_utc, ends_at_utc, local_date, all_day, rrule, rrule_zone, created_by) - event_attendees(event_id, person_id) - event_exceptions(event_id, occurrence_date, cancelled, override_json) · because one instance always changes - lists(id, household_id, kind, title) · kind is shopping, todo or packing - list_items(id, list_id, text, checked, checked_by, ordinal, updated_at, deleted_at) - meals(household_id, date, slot, recipe_id, note) · slot is breakfast, lunch or dinner - recipes(id, household_id, title, ingredients_json, servings) - An all-day event stores a local date and no instant, because a birthday does not move when someone travels.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATABASE_URLsecret | required | postgres://localhost:5432/household | A family's calendar and whereabouts. Treat it accordingly. |
SESSION_SECRETsecret | required | a random 32-byte hex string | Signs login sessions. Rotating it logs the household out, which is the intended emergency action. |
DEFAULT_TIMEZONE | required | Europe/London | The household's zone. Individuals can differ, which is what phase 2 handles. |
QUIET_HOURS_DEFAULT | required | 21:00-07:00 | When reminders defer. The default that stops the tool being muted in week one. |
FEED_BASE_URL | required | https://home.example.com/feed | Where calendar feeds are served. The token in the path is the credential. |
SMTP_URLsecret | optional | smtp://user:pass@host:587 | Phase 5 only. Absent means reminders are visible in the application but not delivered. |
The build, in order
A household and the people in it
Several people share one household with appropriate access.
Attribution retrofitted later is a migration nobody enjoys.
Files
src/auth.tsAdult and child. More roles is a family tool nobody configures.
Files
src/roles.tssrc/roles.test.tsA permanent invite link is a permanent way into your family's calendar.
Files
src/invite.ts
done when · tick each as it passeswatch out- A single shared login for the household means you can never tell who added an event or checked an item, and there is no way to give a child limited access, which is the whole reason a family tool needs accounts at all. Per-person logins from the start; retrofitting attribution is painful.
Time that behaves correctly
Birthdays do not move and recurring events survive daylight saving.
Instant or floating date. Conflating them is the recurring bug in every calendar.
Files
src/events.tsMaterialising occurrences means editing the rule requires rewriting them all.
Files
src/recurrence.tssrc/recurrence.test.tsOne instance always changes. Without exceptions the answer is deleting the series.
Files
src/exceptions.ts
done when · tick each as it passeswatch out- Storing an all-day event as midnight UTC makes a birthday show on the wrong day for anyone west of the meridian, which in a family with a member abroad is an immediate and embarrassing bug. Store all-day events as a local date with no instant at all.
Lists two people can edit at once
Two people in a supermarket do not lose each other's items.
Add, check, reorder, delete. Never a whole-list write.
Files
src/lists.tsSo reconciliation deduplicates rather than duplicating.
Files
src/offline.tsA reconnecting client with a stale copy otherwise resurrects deleted items.
Files
src/lists.ts
done when · tick each as it passeswatch out- Saving the whole list on every change means the second person's save silently discards the first person's additions, and in a supermarket with two people on the same list this happens within minutes. Operate on items, never on the list as a document.
Meals that produce the shopping list
Planning the week's dinners fills the shopping list without retyping.
So regeneration touches only what it created.
Files
src/generate.tsQuantities added, units normalised where possible, left separate where not.
Files
src/ingredients.tsOnly its own, and only if nobody checked them off already.
Files
src/generate.ts
done when · tick each as it passeswatch out- Regenerating the shopping list after a plan change wipes the items someone added by hand, which is the household's actual shopping list and the thing they will be most annoyed to lose. Mark generated items with their source and only ever replace those.
Reminders that do not annoy the household
People are reminded usefully and can control when.
From the expanded recurrence, not from the series row.
Files
src/reminders.tsA dropped reminder is a missed appointment. Deferral is the correct behaviour.
Files
src/quiet.tsNotifying everyone is how a household mutes the application.
Files
src/notify.ts
done when · tick each as it passeswatch out- Sending a reminder for every event to everyone in the household means the notifications are muted within a week and the tool has quietly stopped working. Default to notifying only attendees, respect quiet hours per person, and make a child's notifications adult-configurable.
Calendar feeds and getting your family's data outproduct builder
The household calendar appears in whatever people already use, and the data can leave.
With last-fetched shown, so a forgotten subscription is visible.
Files
src/feed.tsIncluding exceptions. External calendars are strict about this.
Files
src/feed.tsIt is a family's data. Both directions should work fully.
Files
src/export.tssrc/deletion.ts
done when · tick each as it passeswatch out- A calendar feed URL is a bearer credential exposing your family's whereabouts to anyone who has it, and those URLs end up in the sync settings of every device a person has ever used. Make each revocable independently and show when each was last fetched, so an unfamiliar one is visible.
That is the whole plan for Cozi Gold. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Mobile applications.
- Two-way sync with external calendars.
- Recipe import from websites.
- The advertising-supported free tier.
- family adoption, mobile apps, reminders, sync, and years of household workflow polish
- frictionless family sync
- retailer and smart-home integrations
- large recipe or product database
- mobile polish
- A shared household inbox where anyone can drop a note that becomes an event or item
- Chores with rotation, which families ask for immediately and which the roles already support
- A week-ahead digest, which is where a shared calendar stops needing to be checked
Need the files? The project pack on the verdict page hands your agent the whole brief · more home.