← All privacy impact assessments
PIA: Sealing the rest of a group's free text
Superseded on 17 October 2026 by Everyday records go back to ordinary text, and chat and photos keep their keys, which describes what is encrypted now. From the app update released after 17 October 2026, the everyday records this assessment describes — tasks, the calendar, shopping, a group's own meal plans, the baby tracker's words, ceremonies, games, the daily brief and each member's own plan — are stored as ordinary text again, and only chat, photos, captions, album names, comments, each member's own assistant conversation, forwarded email and the assistant's cards stay encrypted. This document is kept as the record of what was built on 30 September 2026 and why; read every sentence below as a description of that design, not of the app today.
Status: Built on 30 September 2026, the same day this assessment was written, and not yet released. Almost everything it describes was built that day: tasks (every word, at every level, with the completion-history copy and task search), the assistant sealing the event notes and places it writes, baby tracker words, a member's own plan, the words inside cards and proposals, a group's own meal plan, ceremonies, forwarded email through the group inbox keypair, voice (we stopped storing what was said), the home brief, every notification we send, and the reason an owner or admin gives when correcting a points score in game history. One piece described below is not built: the calendar changes needed to seal an event's title beyond a Phones-only group. It is noted where it arises. It depends on the separate key design this document relies on, which was written and built the same day. Written: 30 September 2026 · Last revised: 30 September 2026 Nothing here is legal advice. Children's Code citations are to the exposure draft; section numbers may move on registration (due 10 December 2026).
As built, 30 September 2026. Every surface named above was built and merged the same day this assessment was written, matching the design with the one exception named in the Status line and any others called out where they arise below. None of it has reached a group yet: database changes take effect at our next release, and each change also needs a member's phone to update to the app version built alongside it. Until both of those happen, and for anything written before they do, a field this document describes as sealed stays exactly as it is today — readable, in the form the rest of this document calls "today" or "now" unless a passage says otherwise. Read every present-tense sentence below — "is sealed", "we stopped storing", "our server seals" — as a description of the code as built, not yet of anything that has reached a phone.
The one claim to be careful with. When this work reaches every phone, the words a group types — task and event details, baby notes, meal and game text, ceremony posts, forwarded email — will be stored on our servers as ciphertext. That is not the same as "we cannot read them". For everything in this assessment except personal plans, the group key that opens the words is also held, in a separate wrapped copy, by our assistant server, so that the assistant can use a group's own words inside a request a member made. What we will be able to say, truthfully, is narrower: the words are ciphertext in our database; the key that opens them is held by a different provider; our server opens them only inside a request a member made, only in memory, only after recording that it did; and a group that chooses Phones only gives our server no key for anything written from then on, and none for older content once that content has been re-keyed on the group's phones. Our new key design, built the same day, gives chat and photos a different, stronger arrangement (a key our server is never given), assessed separately, once that too has been released. Names are not sealed, and this assessment says why.
1. Purpose and scope
Why: so that someone who obtains a copy of our database — through a breach, a mistaken query, or a backup — finds ciphertext where a family's words used to be.
Today, the text a group types is stored in plaintext almost everywhere. The exceptions are the group chat, photos and their captions and comments, calendar event notes and places, the assistant conversation (sealed under each member's own key), and task and event titles in a Phones-only group. This assessment covers everything else a person types or a model writes about a group, and the places that text is copied to.
In scope:
- Tasks (built 30 September 2026): title, notes, the reason a member gave ("why"), what a task is blocked on, a pet named as who a task is for, the task's copy in completion history, and custom category names. What stays readable, and why: when a task is due, who it is assigned to, whether it is done, its effort and its category's id and emoji — the state that reminders, sharing out work and the assistant's counts are computed from. A reminder for an encrypted task names nothing the group wrote.
- Calendar: titles at every privacy level (today only in Phones-only groups), and notes and places written by our server or not yet reached by the background job.
- Baby tracker: log notes, the free-text parts of a log (a medicine's name, a food, what an immunisation was), a baby's profile notes, handover notes, book titles, the household's own medicine notes (what, and whose advice), and the stored one-line summary.
- Meals: the name and description of a group's own custom plan. The published catalogue is not group content and stays readable. Built on 30 September 2026. Two things changed for families as a result: the assistant's search of meal plans now searches the published plans only, so it no longer finds a group's own plan by its name; and when the assistant says what is for dinner, it calls a group's own sealed plan "Meal Plan" rather than opening its name for that sentence. The daily brief does open it, inside the request that writes the brief. Plans saved before a member's phone began sealing them stay readable until a member encrypts them from the Devices & keys screen.
- Personal plans: the text, reason, obstacle, timing and "who it's for" of a member's own plan or goal.
- Ceremonies: members' posts, the facilitator's sentences, the outcomes a group agreed, and a renamed ceremony's name. Built on 30 September 2026. The helper's sentence is encrypted by our server inside the request that writes it; posts and outcomes are opened there too, and a post it cannot open is left out of the request rather than sent as a stand-in. A child's page gained the sentence promised in section 7.
- Games: the cards a group wrote for The Deal, what "done" means on each, deal names, prize lists, and the prize text recorded in game history. Built on 30 September 2026. Only new game history is sealed; the history written before keeps its words, as section 8 records. One more piece of free text in game history was found while building this — the reason an owner or admin gives when they correct a Taskrun score — and was sealed on the phone the same day, with the rest of game history's words.
- Cards and proposals: free text carried inside the cards the assistant and Mail Sifter raise (titles, email subjects, summaries, spoken notes).
- Mail Sifter: a forwarded email's subject, body, sender, attachment names and attachment files, the assistant's assessment of it, and the group's standing note to the assistant.
- Voice: what was said to a voice assistant and what it said back.
- Home insights and the home brief: the sentences a model writes about a group, which are made from all of the above.
Out of scope, and covered elsewhere:
- The key design itself — a new group key version whose root never leaves members' phones, with two keys derived from it: one our server is never given (chat, photos, captions, albums and comments) and one it may open inside a request (everything in this assessment except personal plans, which use each member's own key). That design, and how a new phone receives a key, are assessed separately. This assessment is only true once that design is live.
- Search and time suggestions on the phone. Today some searches send titles to our server. Those move to the phone before any title is sealed; that move has its own assessment.
- Notifications that name what they are about. Every push that carries a sealed field becomes generic under this work. Opening a sealed title on the recipient's own phone, so a notification can name it again, is a later native change with its own assessment.
- Names (§2.2) and public reviews (§2.3), which stay readable on purpose.
2. Data involved
2.1 What gets sealed
| Kind | Whose | Children's? | Sensitive? | Free text? | Key |
|---|---|---|---|---|---|
Task title, notes, the "why" line, blocked_reason, a pet's name as beneficiary_label |
the member who wrote it; often about another member or a child | often ("Ada's physio forms") | can be: health, school, money | yes | group, server-openable |
task_completions.title — the frozen copy of a task's title in history |
the same | often | as above | copy | group, server-openable |
group_task_categories.label |
the group | rarely | rarely | yes | group, server-openable |
Event title at every level; notes and location rows our server wrote |
the member who wrote it | often (a clinic, a school, a party) | a place a child will be, at a time | yes | group, server-openable |
baby_logs.notes; the free-text keys in baby_logs.details (name of a medicine, food, an immunisation's what) |
the household, about a baby | always | health information | yes | group, server-openable |
group_babies.notes, baby_shift_handoffs.note, baby_books.title, baby_care_guidance.subject and .source |
the household, about a baby | always | health-adjacent (allergies, medicines, whose advice) | yes | group, server-openable |
baby_recent_summaries.summary, .cues |
written by a model from the notes above | always | as above | model-written | group, server-openable |
A custom plan's meal_plans.name, .title, .introduction |
the group | rarely | can reveal diet or allergy | yes | group, server-openable |
user_intentions.text, .why, .obstacle, .when_text, .beneficiary_label |
one member | sometimes | can be deeply personal | yes | that member's own key; a goal they chose to share with the group uses the group key (§3.5) |
ceremony_messages.body, ceremony_outcomes.text, group_ceremony_settings.display_name |
members, including children; the facilitator's sentences | often a child is an author | family feelings, complaints | yes | group, server-openable |
deal_cards.title, .done_standard; deal names, prize text, card titles and a Taskrun score correction's reason recorded in game_ledger.payload; group_game_settings.settings.rewards; deal_proposals.details.name |
the group | sometimes (a child's chores) | no | yes | group, server-openable |
Free text inside action_stacks.payload — a draft's title, a spoken note, a medicine name, an email's subject, summary and option titles |
the member or a third party | sometimes | can be health (a spoken medicine) | yes | group, server-openable |
| A forwarded email: subject, body, sender name and address, attachment names and files, the assessment's summary and options | a third party, and whoever the email is about | sometimes (school mail) | anything | yes | a per-group inbox keypair (§3.6) |
group_inbound_addresses.assistant_note |
an admin | rarely | rarely | yes | group, server-openable |
group_dashboard_insights.insight_text, .suggestions, .beneficiary_ask; the home brief's sentences |
written by a model from the group's records | often | as their sources | model-written | the home brief: group, server-openable; the older per-member brief: no longer stored (§3.8) |
voice_turns.request, .spoken, and the transcript rows our server writes for a spoken turn |
the member who spoke | sometimes | a spoken medicine or appointment | yes | not stored (§3.7) |
group_photo_reports.reason |
the member reporting | rarely | could name a person | yes | group, server-openable |
Strict necessity (s 38(2)(b)). This work collects nothing new. Every field above already exists and is already stored; the change is the form it is stored in. The two additions are technical: a wrapped data key and a key version on each row, and (for Mail Sifter) a public key per group. Neither describes anybody.
Not collected, and could have been: no searchable index of sealed words on our server; no list of which rows a request opened (the record is a count, as it is today for event notes); no copy of opened text in any log, cache or trace; no spoken sentence kept after its turn.
2.2 What stays readable, and why
- Names:
users.full_name,groups.name, device labels (user_devices.name,voice_grants.label), a baby's name (group_babies.name), and the frozen copy of a responder's name a host sees on an event response (event_rsvps.responder_name). A name is what a notification, a lock-screen tile, an invitation and a cross-group response need, and those are composed where no key exists. Sealing names would make every notification say nothing about who did anything. This is stated in our privacy policy as a deliberate exception. - Structured state: times, dates, durations, volumes, counts, statuses, closed-list values (a nappy's status, a task's category id, a log's type), ids, and the flags a program needs to work. This is the rule we have followed since the first stage of encryption: seal what a person wrote, keep what a program needs. A thief learns that a feed happened at 3am; they do not learn what the parent wrote about it.
- Public content: meal reviews (
meal_reviews.comment,author_group_name) are published to every user on purpose, and the meal catalogue is published content, not a group's. - The note in a "confirm the details" request (
event_confirm_requests.question): stays readable as its original assessment decided — a short coordination question, capped at 200 characters, read by the group. We record it here so that decision is re-read, not forgotten. Open question for review: sealing it costs little, because only phones read it. - A note a responding group leaves for a host (
event_rsvps.note, up to 500 characters) and the responder's name. The note is written by a member of a different group, whose phones hold no key for the host group, for the host group to read; there is no key both sides share. It stays readable, as a short message addressed to another group. Sealing it would need a public key per host group, the pattern we use for Mail Sifter below; we have not chosen that. - The name a Phones-only host gives an event for its guests (
group_schedule_events.guest_title). In a Phones-only group an event's own title is sealed, so a group that responded to the host's invitation would otherwise see "Encrypted" on its own calendar. The host's admin may type a second name that the guest groups see instead. It stays readable because it exists to be read by other groups, whose phones hold no key for the host's group; it is written only when the host chooses to, and it is copied only into the guests' own copy of that one event. - A Mail Sifter item's provider message id stays readable: it is an opaque identifier used to refuse a duplicate delivery.
- The group's sender allowlist (
allowed_senders) stays readable: it is an admin's list of trusted addresses that only the mail webhook reads, to decide whether to accept a message.
3. Flows and locations
3.1 The pattern every sealed field follows
Two words used below. The Tier 2 key is the group key that, outside Phones only, our assistant server may also open inside a member's request. The Tier 1 key is the group key our server is never given; it protects chat and photos and is not used by anything in this assessment.
| Step | Where | What happens |
|---|---|---|
| A member writes | their phone | the phone makes a fresh data key for the row, seals the words under it, wraps the data key under the group's Tier 2 key, and writes ciphertext, the wrapped data key and the key version. The readable column holds a fixed placeholder or nothing |
| Another member reads | their phone | the phone unwraps the row's data key with the group key it already holds and shows the words. Opened words sit on that phone, in memory and in the app's own on-device cache, as they do for chat today |
| The assistant reads | our assistant server, Sydney | only inside one member's request, through the same single reader used for event notes today: rows fetched with that member's own permissions, one audit row per key version written before the key is opened, keys held in memory for that call only |
| The assistant writes | our assistant server, Sydney | inside the same request, the server seals what it writes with the Tier 2 key it holds for that request. It can never write anything under the key our server is not given, so a voice request still cannot post to the chat |
| Database functions and background jobs | Supabase, Sydney | hold no key of any kind. Any database function or edge function that today composes or copies a sealed field must stop: it is given the words by a caller who already has them, stores an id instead of a copy, or the field stays readable as a named exception |
| A model | the provider our privacy level names | receives opened words inside a request, as it does today. Sealing changes what is at rest, not what a request discloses; that is covered in our cloud inference assessment |
When the assistant cannot seal what it writes (built for event notes and places, 30 September 2026). The server can only seal where the group has given it a key: not in a Phones-only group (which has no assistant anyway), not in an event group, and not for a key version an admin's phone has not yet shared with the server. It also cannot while our key service is unreachable, or once the hourly limit on key use is reached. In each of those cases a new note or place is saved as readable text, exactly as the assistant saved every note before this change, and a member's phone seals it later. A note or place on an event that is already sealed is never overwritten with readable text: the assistant says it could not make the change and points the member to the calendar. Clearing a sealed note needs the key too, because the old sealed words would otherwise still open.
3.2 Matching and searching on words
Some of our code finds rows by their words today: a database ilike on an event's title, a
count of events whose title mentions "shop", a lookup of the "Pantry" list by its title, a unique
rule that one medicine has one note per baby. A database cannot compare ciphertext. Every such
place gets one of three answers, listed individually in our reader/writer inventory:
- Moved to the phone, which already holds the rows opened.
- Fetched, then filtered after opening, inside our assistant server's one reader, with a fixed maximum number of rows per request.
- A keyed fingerprint (an HMAC computed on the phone from the group's key), for an equality rule the database must keep. A fingerprint reveals which rows share a value — that two notes are about the same medicine — without revealing the value. We use one only where a database rule needs it; the inventory names one such place (the one-note-per-medicine rule).
- Where the "match" is the app looking for a list it made itself by a fixed name, the name is replaced by a structured marker, and no words are compared at all.
Searches that send titles to our server for similarity matching cannot compare ciphertext. Event time suggestions moved to the phone. Task search at the Australia and Global levels instead opens the candidate titles and notes inside the member's own search request, through the same reader and with the same record of each opening as the assistant, under the caps its own assessment sets, and keeps nothing it opened; the semantic task search assessment in this register was re-read and revised for it. A Device-only group's search stays on the phone.
3.3 Copies
Sealing a field is not finished until every copy of it is sealed or gone. The copies we found:
- Completion history copies a task's title when it is ticked. As built, an encrypted task's copy carries the task's own ciphertext and its wrapped key, copied by the database when the history row is written — copying ciphertext needs no key, and whatever words a caller sent are not stored beside it. A task whose title is still readable keeps a readable copy until a member encrypts older tasks, which also encrypts the history.
- Cards (
action_stacks.payload) carry titles, spoken notes, medicine names and email summaries. The words inside a card are sealed with the group key, or the card stores only the id of the record it is about. Built on 30 September 2026: a card's words are sealed inside the card on the phone that writes it, and by our server for a card a spoken request raises (a spoken note or a medicine's name); a spoken draft's history line now names only the kind of record and its id. A card for an email the group's keypair sealed is refused unless its words are sealed too. A phone that does not yet hold the group's key writes a card as it does today; the words are sealed from the moment the phone is given the key, and every card is deleted 30 days after it was raised. - The Deal copies a card's title into history on every hand-over, and copies cards into the task list when a group asks. Both copies are sealed like their source.
- Mail Sifter copies an email's subject into the notes of a task or event made from it. That copy is sealed as a task or event note.
- The on-device cache holds opened words on each member's phone. That does not change: this work is about what our servers hold.
- Download my data returns sealed fields as ciphertext, with a sentence saying why, and an on-phone export for the words themselves, as it already does for photos.
- Traces and logs. Our improvement traces, where a member has consented, send the words of that member's own request with names replaced. Sealing a field at rest does not remove it from a request a member consented to trace; the trace rules are unchanged and are assessed separately. No log line may carry opened text; two existing log lines that do are being removed.
- Backups are dumps of the database. After sealing, new backups hold ciphertext; backups taken before sealing hold plaintext until they expire, at most 90 days later.
3.4 Old versions of the app
Versions of the app from before this work write plaintext. Until every member runs a version that seals, a group can gain plaintext at any time, and the calendar's existing sealing already accepts plaintext from older versions so that no edit is lost.
- A minimum supported version will be published by our server. The app checks it on launch and shows a blocking "Update GroupApp" screen below it, and the database refuses writes that name an older version.
- Per group and per field, a "done" flag is set when a member's phone finishes sealing that field's older rows. Any later plaintext write clears it.
- Only after the minimum version is past every version that writes plaintext does a database rule refuse plaintext in that field for good.
- Rollback is always possible: the readers of sealed columns are never removed, so lowering the minimum version and removing the refusal lets older versions write again, and nothing already sealed becomes unreadable.
3.5 Personal plans
A member's plan is sealed under that member's own key, which our server is never given. The one place our server reads a plan today is the goal breakdown: it fetches the goal by id. Under this design the member's phone opens the goal and sends its text in the request, as the assistant already receives the member's recent conversation; our server stops fetching it.
A goal the member chooses to share with the group is, by that choice, readable by the group, and so cannot be under the member's key alone. When a member shares a goal, their phone reseals it under the group key; when they stop sharing, it reseals it under their own key. Any copy the group already opened while it was shared cannot be recalled, and the sharing sheet says so.
As built (30 September 2026). Only the goal's own line moves to the group key when it is shared. The member's reasons — why, what might get in the way, when, and a pet's name as who it is for — stay under the member's own key whether the goal is shared or not, because the sharing sheet says they stay private. The phone encrypts a member's existing plan the next time it loads it; the goal breakdown receives the goal's line from the phone. There is no group-wide "finished" flag and no refusal of plaintext for plans: a plan is one member's, only that member's phones can encrypt it, and an older app version on their other phone may still save plaintext, which their current phone encrypts again. The intentions assessment has the detail.
3.6 Mail Sifter: the group inbox keypair
Email arrives in plaintext. That is the nature of email: it passes through our mail provider, Resend, in Japan, and through our webhook's memory, in the form the sender wrote it. We state this as an exception to "encrypted on your phone", and this design does not change it. What it changes is what we keep.
| Step | Where | What happens |
|---|---|---|
| A group turns on Mail Sifter | an admin's phone | the phone creates an inbox keypair for the group's current key version: the public half is stored in our database; the private half is sealed under the group's Tier 2 key |
| An email arrives | Resend (Japan), then our webhook (Supabase, Sydney) | the webhook checks the signature and the sender rule exactly as today, then makes a fresh data key for this item, encrypts the subject, body, sender, attachment names and each attachment file with it (authenticated, with the item's id and the group's id bound in), seals the data key to the group's public key, stores only ciphertext, and discards the plaintext. The webhook holds only the public half, so it can lock but never unlock |
| A member reads it | their phone | the phone opens the private key with the group key, then the item's data key, then the words and files. There is no endpoint that takes an item and returns plaintext |
| The assessment runs | our assistant server, inside the claiming member's request | the server fetches the sealed item with that member's permissions and opens it through the same single reader, after recording the open; it reads attached PDFs the same way. It no longer trusts plaintext sent by the phone |
| The assessment is saved | the member's phone | free-text parts of the assessment (summary, instructions followed, option titles, times in words, suggested people) are sealed; the structured parts — relevance, each option's kind, date, time and source — stay readable so the database can keep refusing a malformed assessment |
| A member shares something into Mail Sifter from another app | their phone | the phone seals it itself before sending, with the same public key |
| The item is resolved, or 30 days pass | Supabase | as today, the body is removed on resolution and the item after 30 days. Removing the item's sealed data key is enough on its own to make the rest unreadable |
| The group's key rotates | — | new items use the new version's keypair; old items keep theirs |
| The group moves to Phones only | — | no new mail is accepted, so no new keypair is needed; items already received keep their keypair, whose private half stays under the older key version our server was given a copy of, until they are removed (30 days after arriving, or 30 days after being resolved) |
The plaintext boundary, stated: Resend holds a copy on its own systems for its stated retention period (30 days on the tier we use, which we have not verified against a signed agreement); our webhook holds the plaintext in memory for the length of one delivery; after that, our database holds only ciphertext. The sender policy is checked against the incoming message, not against anything stored, so it keeps working.
3.7 Voice
We built the change that stops storing what was said on 30 September 2026; it takes effect
at our next release. Until then, and until it does, every spoken turn stores the transcribed
request and the spoken reply (voice_turns.request, .spoken), and copies both into the
member's assistant conversation in plaintext until their phone next seals it. Once released:
- the audit record keeps what happened, not what was said: the kind of request, the capability it used, the outcome, a short reason code when it did not go ahead, ids of any records it created or proposed, the baby a feed request was about, and how long it took. The database refuses text in the two old columns, and refuses a reason that is not a short code;
- the conversation shows a structured line ("By voice: a feed for Ada is waiting for your OK"), composed by the phone from that record and the names the phone already holds. The row our server writes there has no words in it, so there is nothing for the phone to seal and nothing sent back to a model as an earlier turn;
- a spoken request that could not be sent because the member's weekly allowance was used up is no longer kept as an unsent draft. It is dropped, and the spoken reply says so;
- the words kept from earlier voice requests are deleted from the audit record by the update that turns the refusal on. Earlier voice turns in a member's conversation leave with the rest of it after 30 days, as every conversation does, and are sealed by the member's phone before then if they run the job;
- a card a spoken request raises (a baby log, a draft) still carries its own words, like any card; sealing them with the group key is part of the proposals step of this work (§3.4), not of this one. What a spoken request creates — a task's title, a card's note — is the record the member asked for, and holds its words the way anything else in the app does.
Apple's or Google's speech service on the phone transcribes the words before they reach us, as our voice assessment already states; this design changes only what we keep.
3.8 Insights and the home brief
Both are sentences a model writes from the group's records, so they carry the same words as their sources.
- The home brief (built 30 September 2026). Our server seals its sentences with the group's Tier 2 key when it saves them; the brief is saved through a narrow function that holds no key and receives ciphertext. A member's phone opens it with its own key; our server opens it only for an older app version that cannot, inside that one request. When the group has not shared a key with our server the brief is stored as ordinary text, as before; when the key exists but cannot be used, nothing is published. The home brief's own assessment has the detail.
- The older per-member brief is not sealed; it is no longer stored. It serves only app versions from before the shared brief, and goes when they are no longer supported. From 30 September 2026 our server keeps each one in its own memory while it is valid and writes none to our database; rows already there are deleted within three days of expiring. App versions from before 9 September 2026 are answered by an older function that cannot seal and still stores them as ordinary text until it is retired.
3.9 Notifications (built 30 September 2026)
Amended 29 October 2026 (message text and titles in notifications). From the release that carries privacy policy v3.15, a chat notification in a group whose chat isn't encrypted can show the start of the message, and a new-task notification, a task or event reminder and the "added a task for you" notice can show the title, read from the row when the notification goes, for a recipient 16 or over with "Show message text in notifications" on, on a phone that reported it can show it, while our switch is on. Encrypted and Device-only groups, baby handoffs and notes, Mail Sifter and the assistant stay generic as below. The paragraphs below describe every notification from 30 September 2026 until that release.
From the release that follows 30 September 2026, no notification our servers send carries words a group wrote, at any privacy level. A reminder says which group it is in and, for an event, how soon it starts; a new-task notification says who added it (their own profile name, never text the sender's app supplied) and in which group; a duty handoff says who handed over which baby, and never the note written with it. Group and member names are the disclosed exception, and at the Device-only level a notification names nobody. Three notifications carried content before: task and event reminders (the title), the new-task notification (the title as the sender's app sent it) and the duty handoff (its note). A check in our build lists every function that sends a notification and fails when a new one appears until it is decided what it may say.
A notification a member's own phone sets for itself — the duty handoff alarm — is written on that phone from what it has already opened and does not pass through the notification services, so it may still show the handoff's note on that phone's lock screen. Naming a task on the lock screen again, opened on the recipient's own phone, is the later native change §1 describes.
4. Legal basis and consents
- No new consent purpose. Sealing narrows what is readable at rest; it does not start any new use or disclosure. Every existing consent keeps its meaning: the assistant's, the baby summary's, Mail Sifter's and voice's.
- The privacy level is the member-facing choice. At Phones only, no copy of the Tier 2 key for the current key version reaches our server, so nothing written from then on can be opened by it (older content, once re-keyed on the group's phones, neither). At every other level our server may open these fields inside a request; the level's description will say so.
- No preset or group type turns anything on. This work adds no switch.
- Children. A child account cannot trigger an assistant request, so cannot cause our server to open anything. A child's own posts in a ceremony, and a guardian's notes about a child, are among the fields this work protects best (§7).
5. Risks and mitigations
| Risk | Who is harmed | Mitigation | Structural or conventional? | Residual |
|---|---|---|---|---|
| Somebody with a copy of our database reads a family's task notes, baby notes and email | every member; every child named | the words are ciphertext; the keys are on members' phones and, wrapped under a key at a different provider, for our assistant server | structural (cryptography and the provider split) | metadata — who, when, how much — stays readable; names stay readable |
| Somebody who controls our database also impersonates a member to our assistant server and asks it to read | the group | our server opens only what that member's own request fetches, one request at a time, audited before the key is opened, with an hourly ceiling; there is no endpoint that takes a record id; a Phones-only group gives the server no key | structural as to no bulk path; conventional as to the ceiling (code) | a request-sized read per impersonated request is possible; detection is assessed with the key design |
| A database function or edge function keeps writing plaintext copies (history, cards, notices) after the source is sealed | the group | every such writer is listed in our inventory and changed in the same step as its source; a query for residual plaintext runs per surface; the database refuses plaintext once the minimum version allows | conventional until the refusal, then structural | the window between sealing a field and its refusal |
| A database function copies the placeholder "Encrypted" into history, so a completed task reads "Encrypted" for ever | the group | the completion-history copy moves to the caller that already has the words; our inventory lists every place that copies a sealed field | conventional (tested) | none once changed; the change must land before titles are sealed |
| Our assistant hands a model the placeholder as though it were the words, or reports "3 events called Encrypted" | a member (a wrong answer) | every server reader of a sealed field goes through the one reader, which substitutes a sentence, never the placeholder; our checks fail any tool that reads a sealed column without it | conventional, test-pinned | — |
| A search that used to work on our server stops working, or silently matches nothing | a member | server searches on words move to the phone first; server lookups are rewritten to fetch then filter; the database rule for one medicine note per baby uses a fingerprint | conventional | a fingerprint reveals which notes share a medicine |
| An older app version writes plaintext into a sealed field | the group | minimum supported version; "done" flag cleared by any plaintext write; refusal after the minimum passes every plaintext writer | structural after refusal | members who never update are blocked, by design |
| A member is locked out of their own group's words (no key on a new phone) | a member | assisted admission from another member's phone; the key design's recovery options | assessed with the key design | a group whose only phone is lost cannot recover sealed words without a recovery phrase |
| A notification shows a sealed title on a lock screen | anyone near the phone | every push is generic about content at every level (§3.9): the push functions never read a title or note and never print text a caller sends; a build check lists every push sender. Amended 29 October 2026: four named senders may show unsealed words read from the row, through one gate that refuses a sealed row, an encrypted or Device-only group and anyone under 16 (notification previews); the build check lists them apart | conventional, test-pinned | less useful notifications in encrypted groups until the on-phone notification change; the phone's own handoff alarm still shows its note; words on lock screens in groups whose chat isn't encrypted, accepted in that assessment (R1–R6) |
| A Mail Sifter email is read on the way in | the sender; the family | the boundary is stated; the webhook discards plaintext; storage is ciphertext from the first write | structural at rest; not at all in transit through Resend | Resend's copy for its retention period |
| A forwarder's embedded instruction ("remove Sarah") shapes an assessment | the group | the structured parts of the assessment stay readable, so the database keeps refusing any option kind other than an event or a task | structural | — |
| A shared goal's text stays readable to the group after the member stops sharing | the member | reseal under the member's key on unsharing; the sheet says an already-opened copy cannot be recalled | conventional | copies already on other phones |
| Opened text sits in each phone's cache | anyone with an unlocked phone | unchanged; the phone's own protections apply | out of scope | as today |
| Backups taken before sealing hold plaintext | every member | backups are encrypted and expire after 90 days; a restore re-runs the sealing jobs | structural as to expiry | up to 90 days |
6. Structural versus conventional
Structural
- Sealed words are ciphertext at rest; the database holds no key of any kind.
- Database functions and edge functions cannot open sealed words, by construction: they are never given a key. The mail webhook holds only a public key, which can lock but not unlock.
- A personal plan is under the member's own key, which our server is never given.
- At Phones only, our server holds no key for the group's current key version, and none for older versions once the group's content has been re-keyed — except for mail received before the lock, and a former member's private assistant cards, neither of which is moved (§8).
- The audit table for server opens is append-only by privilege.
- Once a field's refusal rule is live, the database rejects plaintext in it.
- The shape of a Mail Sifter assessment (option kinds, dates, times) stays validated by the database.
- The voice record holds no words: the database refuses text in the request and reply columns for every writer, and refuses a reason that is not a short code.
Conventional, and pinned by automated checks where we can
- Our server opens sealed words only through one reader that cannot be given a record id; checks fail any other file that tries, and any tool that reads a sealed column without it.
- Every place that copied a sealed field has been changed, as listed in our inventory; each surface ships with a query for residual plaintext and tests on its write paths. A schema check alone cannot prove sealing.
- Pushes for sealed fields are generic.
- Download my data explains sealed fields.
- The hourly ceiling on server opens.
7. Best interests of the child
- What it gives a child. The records a family keeps about a baby or child — medicines, what was eaten, notes about sleep and illness, a child's own ceremony posts, school email — stop being readable to anyone holding a copy of our database. These are the records most likely to follow a child into adulthood, and a database copy is the likeliest way they would leak.
- What it costs a child. Nothing new is collected. The assistant can still read a baby's notes inside a parent's request, at every level except Phones only, because a parent asked for an assistant that can answer about their baby. A child who later wants their infancy records gone still depends on their family deleting them, as today.
- The safer design, and why it was not chosen. Sealing baby notes under the key our server is never given would mean the assistant could not read them at all. We decided the baby tracker's notes stay readable by the assistant inside a request, because families use it to ask about their baby; a family that disagrees can choose Phones only, and the level's description says what that switches off.
- What a child is told. Our child-facing privacy page describes what the group decides and what the child decides. When this work ships, it gains one sentence, in plain words, that what they post in a ceremony is stored so that our servers cannot read it except when a grown-up in their group asks the assistant something.
8. Residual risk we accept
- Our server can open these fields inside a member's request, at every level except Phones only. That is the price of an assistant that can use a group's own words. The protection is that it happens only inside a request, is recorded first, is capped, and cannot be pointed at a record by id.
- Names stay readable, including a baby's name and a responder's name on an event response.
- Metadata stays readable: who did what, when, how much, and the ids that link records.
- Email passes through Resend and our webhook in plaintext.
- A spoken request still goes to the model, exactly as a typed one does, and a card it raises carries its words until the proposals step seals them. Not keeping the words is about what we store, not about what is sent to answer.
- A fingerprint on a medicine note reveals which notes share a medicine, not which medicine.
- Plaintext written before sealing survives in backups for up to 90 days.
- Game history written before sealing may stay readable. Game history is append-only, so older deal names and prize text recorded before this work cannot be rewritten without a narrow exception to that rule. Until we decide on that exception, those older entries stay readable.
- Locking to Phones only does not move everything. Forwarded email received before the lock stays openable by the older key version our server was given a copy of until it is removed (at most 30 days after it arrived, or 30 days after it was resolved), and a former member's private assistant cards stay under that older version because nobody can move them.
- Until every member updates, a group can gain plaintext, and the "done" flag and refusal are what bound that.
- Losing every phone in a group without a recovery phrase loses the sealed words.
- Opened words on a phone are as safe as that phone.
9. Review trigger
- A new field that a person types, or a model writes, is added to any table.
- A database function or edge function starts composing or copying a sealed field.
- A server search on words is proposed.
- The key design's assessment changes how a key reaches our server or a new phone.
- Mail Sifter changes provider, retention or the parts of an assessment that stay readable.
- The voice feature starts keeping spoken words again for any reason.
- A surface ships: its section here is re-read against what was built.
10. Checklist against exposure draft s 38(2)
| s 38(2) | Requires | Answered in |
|---|---|---|
| (a) | the nature, scope, context, flow and purposes of the handling of children's personal information | §§1–3 |
| (b) | why the collection is strictly necessary | §2.1 — nothing new is collected |
| (c) | how the collection is lawful and fair | §4 |
| (d) | whether the handling is consistent with the best interests of the child, with reasoning | §7 |
| (e) | how the entity complies with this Code and the Act | §§4 and 6 |
| (f) | the risk of harm to children | §5 |
11. Sources
- The reader and writer inventory this assessment relies on:
docs/content_encryption_inventory.md. - The plan:
docs/privacy_personas_plan.md§8.1–§8.3, §8.5, Appendix B and the decision log in §14. - The existing design:
docs/encryption_plan.md;docs/pia/tier2-kms.md;docs/pia/privacy-levels.md;docs/pia/inbound-email.md;docs/pia/intentions.md;docs/pia/ceremonies.md;docs/pia/baby-tracker.md;docs/voice_and_system_assistants_plan.md. - The single server reader:
agent-server/src/tier2.ts; its checks:agent-server/checks/tier2.check.ts,agent-server/checks/toolContracts.check.ts. - Existing sealing on the phone:
lib/scheduleSealing.ts,lib/taskSealing.ts,lib/chatSealing.ts, and the background jobslib/scheduleHistoryJob.ts,lib/titleHistoryJob.ts,lib/chatHistoryJob.ts. - The webhook:
supabase/functions/receive-inbound-email/. - Voice:
agent-server/src/voice.ts(the turn record). - Resend's retention: the provider's published pricing page, as cited in our inbound email assessment; not verified against a signed agreement.