How Dawnlet actually works
These pages describe the design of each part of the app — the data it keeps, the rules it enforces, and the decisions that shaped it. They're blueprints, not source code: enough to understand the mechanism and argue with it, without reproducing the implementation.
Start here
If you only read one, read Architecture — the rest assume its vocabulary (repositories, view models, the module pattern every feature repeats).
Architecture
The shape every module repeats: protocol-backed repositories, observable view models, and the shared row components that make one habit look identical in three places.
Data & sync
Where your data lives, how row-level security scopes every table to exactly one account, and the grant that has to accompany it or nothing works.
Agenda & the day model
How one day is assembled from eleven modules, why the sections are reorderable, and why the whole screen is a list rather than a scroll view.
Habits & streaks
An append-only log of days, with every streak, week strip and percentage derived from it rather than stored — and what that buys you.
Todos & events
One recurrence engine serving three modules, the difference between a definition and an occurrence, and how skipping works without corrupting a series.
Money
Per-month budgets with no rollover, recurring transactions that generate real rows, and the redaction rule that keeps amounts out of notifications.
Time tracking
Why a session stores accumulated segments instead of a start time, and how switching activities stays atomic.
Notifications
The longest write-up, and the most interesting: planning a two-week horizon into 64 scheduling slots, on-device, with the app closed.
Categories & tags
One taxonomy across eight modules, scoped by which modules a category applies to.
Purchases & gating
One non-consumable product, no server-side tier, and why the gate is enforced twice.
The shape of the whole thing
Dawnlet is a native SwiftUI app talking to a Supabase Postgres database. There is no backend service of ours in between: the app authenticates against Supabase directly and queries tables over PostgREST, with row-level security doing the work a server-side authorization layer would otherwise do.
| Layer | What it is | What it deliberately isn't |
|---|---|---|
| Client | SwiftUI, iOS 17+, one app target | Not cross-platform, not a web view. Widgets and a Watch app are anticipated but not built. |
| Data | Supabase Postgres, one table per concept, RLS on every one | No API service of ours, so no endpoint to secure separately or keep in sync with the client. |
| Auth | Sign in with Apple, exchanged directly for a Supabase session | No password, no email, no custom token minting, no name or email stored. |
| Privileged work | One Edge Function, for account deletion | Not a general-purpose API. It exists because deleting an auth user needs a key that must never ship in an app. |
| Notifications | Computed and scheduled entirely on the device | No APNs, no push payloads, no notification server. |
| Purchases | StoreKit, one non-consumable product | No subscription, and no server-side record of who bought what. |
Every piece of server-side machinery is a thing that can break, leak, or need paying for. The parts of Dawnlet that genuinely need a server — durable storage and cross-device sync — use one. The parts that don't, like deciding whether to remind you about a habit tonight, run where the data already is.
A note on honesty
The app is under active development, and these pages describe what's built, not what's planned. Where something is a placeholder — the journal module, widgets, the Watch app — the relevant page says so rather than describing it in the present tense.