Plimmery · system map
Plimmery, under the hood
Explore how a lesson, a payment and a release move through Plimmery, the reading app we built. Play the tour, pick one of the 12 journeys, or select any part to see what it does.
Verified against the live system, 12 October 2026
System explorer
Journeys12 journeys through the system
Every lesson starts with the child. Follow their prompts, answers and progress through 42 parts and 64 connections.
By the numbers
- third-party services, from 7 companies
- 10
- live payment systems
- 2
- API routes in production
- 21
- tables, all with row-level security
- 10
- scheduled database jobs
- 5
- audio clips in two AI voices
- 2,543
- test files, plus 23 browser journeys
- 328
- lines of code, 66k of them tests
- 163k
Checked against the code and production on 12 October 2026.
How each number was counted
- 10 third-party services, from 7 companies
- Cloudflare, Supabase, GitHub, Resend, Lemon Squeezy, Google (Play, Pub/Sub, sign-in and Cloud TTS) and OpenAI, 12 October 2026.
- 2 live payment systems
- Lemon Squeezy on the website and Google Play in the Android app, both switched on in production, 12 October 2026.
- 21 API routes in production
- The route handlers in the code, less two that answer only on a developer’s machine, 12 October 2026.
- 10 tables, all with row-level security
- The database migrations, and the production database itself, 12 October 2026.
- 5 scheduled database jobs
- The production scheduler: four call the Worker, one deletes old error records, 12 October 2026.
- 2,543 audio clips in two AI voices
- 1,272 US clips from OpenAI and 1,271 UK clips from Google Cloud, in the shipped audio folder, 12 October 2026.
- 328 test files, plus 23 browser journeys
- The test and browser-journey files in the app, 12 October 2026.
- 163k lines of code, 66k of them tests
- TypeScript and script lines in the app, components, libraries and scripts, 12 October 2026.
The journeys, step by step
The same 12 journeys the map plays, written out. Each step names the parts it connects.
01A child plays a lessonOn the device · 6 steps
- 1The child picks a lesson. The player builds it from the curriculum and keeps the session short.Child → Lesson player
- 2Every prompt is spoken. The player asks for the exact pre-made clip; nothing is synthesized live.Lesson player → Audio player
- 3The service worker hands over the clip straight from the device if it already has it.Audio player → Service worker
- 4If not, it fetches the MP3 from Cloudflare’s edge cache and keeps a copy for next time.Service worker → Site & audio files
- 5Answers and progress are saved on the device first, so a lesson never waits for the network.Lesson player → On-device storage
- 6If a parent is signed in and has given consent, the progress is merged into Supabase. Row-level security keeps each family to its own rows.Cloud sync → Postgres
02A parent signs inSupabase · 6 steps
- 1The parent types their email address.Parent → Parent screens
- 2The browser asks Supabase Auth for a code directly. No Plimmery server is involved.Parent screens → Auth
- 3Supabase sends the email through Resend, using Plimmery’s own template.Auth → Resend
- 4A 6-digit code arrives. The parent types it in; there is no link to click.Resend → Parent
- 5On the website, Google sign-in is the other way in. It is hidden inside the Android app.Parent screens → Google sign-in; Google sign-in → Auth
- 6Signed in, the device downloads the family’s children, progress and consent, and merges them with its own copy.Cloud sync → Postgres
03Parental consentPeople · 7 steps
- 1Before any child data leaves the device, the parent passes an adult check.Parent → Parent screens
- 2The browser writes a “granted” event. The log is append-only, and the database stamps the time itself.Cloud sync → Postgres
- 3The browser asks the Worker to send a confirmation.Parent screens → Consent email API
- 4The Worker checks who is asking, then sends the email through Resend.Consent email API → Resend
- 5The email confirms the consent and links to where it can be managed or withdrawn.Resend → Parent
- 6Withdrawing writes a “withdrawn” event, and any subscription is stopped at both stores.Cloud sync → Postgres; Parent screens → Web payments API
- 7From then on the database refuses new child data for that family, and other devices wipe their copy when they next sync.Cloud sync → Postgres
04Buying on the websiteOutside services · 8 steps
Live in production
- 1The parent picks monthly, annual or lifetime on the pricing page.Parent → Parent screens
- 2The browser asks the Worker for a checkout. These calls are rate-limited.Parent screens → Web payments API
- 3The Worker creates a Lemon Squeezy checkout tied to the family’s account.Web payments API → Lemon Squeezy
- 4The browser moves to Lemon Squeezy’s hosted checkout, which takes the card and handles VAT.Parent screens → Lemon Squeezy
- 5Lemon Squeezy calls the webhook. The Worker checks its signature before trusting anything in it.Lemon Squeezy → Web payments API
- 6The Worker records the entitlement with a server-only key. No browser can write this table.Web payments API → Postgres
- 7The browser reads its own entitlement and the paid lessons open.Cloud sync → Postgres
- 8If a payment notice goes missing, the browser asks for a repair, and the Worker looks the purchase up at Lemon Squeezy.Parent screens → Web payments API; Web payments API → Lemon Squeezy
05Buying in the Android appOutside services · 8 steps
Live in production
- 1Inside the Android app, the store sells through Google Play. The website checkout never appears there.Parent → Parent screens
- 2The app opens Google Play billing through the Digital Goods API.Parent screens → Google Play billing
- 3Google Play takes the payment on the phone.Google Play billing → Google Play
- 4The purchase goes to the Worker, straight after buying and again at every launch.Google Play billing → Play payments API
- 5The Worker re-reads the purchase from Google itself. It trusts nothing the phone sent.Play payments API → Google Play
- 6The purchase is recorded, then acknowledged to Google. A purchase nobody claims is never acknowledged, so Google refunds it.Play payments API → Postgres
- 7Renewals, refunds and cancellations are published to Google Cloud Pub/Sub.Google Play → Google Pub/Sub
- 8Pub/Sub pushes each one to the Worker, which checks Google’s signature and re-reads the purchase.Google Pub/Sub → Play payments API
06The scheduled jobsSupabase · 7 steps
- 1The scheduler starts each job on time. Those that call the Worker use credentials kept in Vault.Scheduler → Vault
- 2They call the Worker: a daily error digest, a regular Google Play sweep, an idle-account check and a QC clean-up.Scheduler → Scheduled jobs API
- 3The Play sweep applies refunds, retries anything unsettled, and checks the store prices against the website.Scheduled jobs API → Google Play
- 4Every job reads and writes Postgres and records that it finished, so a job that goes quiet gets noticed.Scheduled jobs API → Postgres
- 5Old QC notes are removed together with their screenshots.Scheduled jobs API → Storage
- 6An account idle for two years gets an email notice, and is deleted only if it stays unused after that.Scheduled jobs API → Resend
- 7The fifth job never leaves the database: old error records are deleted on schedule.Scheduler → Postgres
07When something breaksCloudflare · 7 steps
- 1A crash anywhere in the app is reported with the steps that led to it and the release it came from.Lesson player → Error log API
- 2The reports are rate-limited and stored by the Worker. Server errors land in the same table.Error log API → Postgres
- 3A failed uptime check, live robot run or release probe writes a line there too.Live checks → Error log API
- 4Every morning the digest job runs.Scheduler → Scheduled jobs API
- 5It reads the last day of errors and checks that every scheduled job has reported in.Scheduled jobs API → Postgres
- 6Errors and warnings go into one email. A quiet day sends nothing.Scheduled jobs API → Resend
- 7The owner reads it the same morning.Resend → Owner & QC testers
08Making the voiceMaking the voice · 7 steps
- 1A script walks the curriculum and collects every line a lesson can say: 1,161 of them. Interface lines, praise, letter names and 24 speech sounds are added.Curriculum → Spoken lines
- 2AI generates the voice. The American one is OpenAI’s gpt-4o-mini-tts, voice “nova”: 1,272 clips.Spoken lines → OpenAI voice (US)
- 3The British voice is Google Cloud’s en-GB-Neural2-F, with British wording such as Mum and colour: 1,271 clips.Spoken lines → Google voice (UK)
- 4Each clip is saved as an MP3 named after a fingerprint of its text, so a changed line always gets a new file.OpenAI voice (US) → Audio library; Google voice (UK) → Audio library
- 5Two OpenAI speech-to-text models transcribe the clips. A clip both of them mishear is flagged as confirmed.Audio library → Speech-to-text QC
- 6A flagged clip is fixed by changing its wording or its pronunciation spelling, which gives it a new name, and generating it again.Speech-to-text QC → Spoken lines
- 7Before every release, tests check that every prompt has its clip in both voices. A gap stops the release.Audio library → Release gate
09Releasing a changeReleasing a change · 7 steps
- 1Every change is its own pull request, first built onto a locked test site that can’t take payments.GitHub → Test site
- 2The owner tries the change there and says go.Owner & QC testers → Test site
- 3Merging into main starts the release gate: typecheck, lint, a dead-code scan and the unit tests, then robots that play the app and the paywall in a browser.GitHub → Release gate
- 4Only a green gate builds.Release gate → Build & deploy
- 5OpenNext builds the Worker, and wrangler puts it live on plimmery.com.Build & deploy → Site & audio files
- 6The live site is probed at once, then the release is recorded on GitHub with the pull requests it brought.Build & deploy → GitHub Release
- 7From then on, regular uptime checks and a robot account keep watching the live site.Live checks → Site & audio files
10Owner & QC testersPeople · 4 steps
- 1The owner opens the admin dashboard. QC testers only get a button for filing notes.Owner & QC testers → Admin & QC screens
- 2Every admin request is verified on the server.Admin & QC screens → Admin & QC API
- 3The dashboard reads accounts, errors and QC notes with a server-only key.Admin & QC API → Postgres
- 4A QC note can carry a screenshot. It stays private and opens only through a short-lived link.Admin & QC API → Storage
11Deleting an accountCloudflare · 7 steps
- 1The parent chooses to delete the account.Parent → Parent screens
- 2The browser deletes the children and progress it can reach.Cloud sync → Postgres
- 3Then it asks the Worker to finish the job.Parent screens → Account deletion API
- 4Any subscription still renewing is cancelled at Lemon Squeezy and Google Play.Account deletion API → Lemon Squeezy; Account deletion API → Google Play
- 5Profiles, progress and purchases are deleted, and error records lose their link to the account. Only the consent log stays, as the law requires.Account deletion API → Postgres
- 6The sign-in account itself is deleted.Account deletion API → Auth
- 7Only when the Worker confirms does the device wipe its own copy.Parent screens → On-device storage
12Where AI is usedAI · 8 steps
- 1The owner leads development and uses Claude as an AI coding assistant.Owner & QC testers → Claude
- 2Its work arrives as pull requests, like any other change.Claude → GitHub
- 3The owner reviews every change and tries it on the locked test site before it goes live.Owner & QC testers → Test site
- 4Every change, whoever wrote it, passes the same tests, browser robots and document checks.GitHub → Release gate
- 5In the product, AI made the voices: OpenAI’s and Google’s text-to-speech generated all 2,543 clips, the speech sounds included.Spoken lines → OpenAI voice (US); Spoken lines → Google voice (UK)
- 6AI also checks them: two speech-to-text models listen to the clips and flag any they mishear.Audio library → Speech-to-text QC
- 7Then the output is fixed. Clips are files that never change under the same name.Audio player → Service worker; Service worker → Site & audio files
- 8The live app uses no AI at all: no chatbot, nothing generated while a child plays, and no child data sent to an AI service.Child → Lesson player
Want a system like this, documented like this?
Tell us about the process or the product. We'll reply within two business days with a first view of what we'd build.