← All privacy impact assessments
PIA: Separating the key our server may use from the key it must never hold
Amended 27 October 2026: removing a lost phone from one screen. A member can remove their own lost phone in one step. That ends its sessions (and refuses them again for two hours), cancels its pending access and deliveries, deletes the earlier-message copies held for it, and starts a key change in each of their groups. The screen shows each step as done, pending or failed, and never says "complete" while one is unknown. It says plainly that messages and photos already on the phone stay readable on it while it's offline, and that changing the Group Password afterwards stops it opening new ones. Where the phone can't be told apart from the member's others, it says that their other phones will be signed out too.
Amended 22 October 2026: automatic key delivery is retired (HQ PIA-001 round-2 addendum §5, conditions 12-1 to 12-6). Our server no longer gives a waiting phone the group's key for tasks and calendar. A new phone gets the key from another of the member's phones, from an owner or admin after they compare safety words, or from the Group Password if the group has one and it holds the current key. The switch is off for every group and can't be turned on again; groups that had it on were told once in their chat ("Automatic key delivery has been retired. A new phone is now connected by an owner or admin."); open waits ended as retired; the agent-server routes and the push kind refuse, and the code that boxed a key to a phone is removed. What stays: the hold, delivery and block rows (history), a phone's own read of a key it was given before, and revoking a past delivery, now from that delivery's own line in the chat. Everything §3.5 and §5.3 say about delivery is history from this date; the residual risk is that a new phone with no owner or admin online waits, with tasks and calendar unreadable on it until someone connects it.
Amended 18 October 2026: a group chooses whether chat and photos are encrypted, and a Group Password holds the key. This design gave every group a root key per version that only its phones hold, with a twelve-word recovery phrase as the safety net if every keyed phone was lost. From the app update released after 18 October 2026 a new group has no such key at all: its chat and photos are stored as ordinary text under the per-group rules in our database, and an owner or admin turns encryption on by setting a Group Password, typed with restrictions and never received by us, which wraps every version of the group's key archive. A member's new phone then opens the group's whole history with the password alone; every current phone is wrapped for automatically, with no approval ceremony. Only members known to be 18 or over are shown the password. Everything below about how a root key is made, derived, boxed to a phone, checked and announced is unchanged for a group that has turned encryption on; the recovery phrase stays only for groups that saved one. See the assessment of that change.
Amended 10 October 2026. Every key notice is still written and every member can still read all of them, but most no longer appear in the group chat. The chat keeps the ones someone may need to act on: anything done with no member behind it, and every step of automatic key delivery. The rest are listed under Key history on the Devices screen. See §3.6.
Amended 14 October 2026. Reviewing this design found that "a phone that held the last key" meant only that our database had a record saying so, and a member's own account could write that record for a new phone without holding the key. So someone who had taken over a member's account — their password and second sign-in step, but none of their phones — could be given the group's next key, or change the key to one they chose, without any member approving. From the app update released after 14 October 2026, a phone counts as holding a key only once a phone that holds it has shown so, and a new key can only be made by a phone that shows it holds the current one. Each group changes its key once after that update to move onto the new rule. Phones also check that whoever made a new key held the one before it, and won't use a key that fails; a group stuck behind such a key is reset by us once the account is secured, and the group is told. See §5.2, §5.3, §6 and §8.
Amended 14 October 2026, again: a group's earlier keys reach every member's phone. Until now a phone given the group's key got the current key only, so a member who joined later, or replaced their phone, could not read anything encrypted before. Written before the code, this amendment assesses sending a group's earlier keys automatically to every person's phone that holds the current key, once the group has turned it on with one tap by an owner or admin. Keys made since the change above carry a check value a phone can test a copy against; earlier keys carry nothing, so that admin's tap is what vouches for them. Children receive them once an adult in the group has acknowledged it for them; shared displays never do. A key change that skipped a version, a reset by us, or a refused key change stops sharing until an admin taps again, and in one case until we have checked the group's phones. In groups not yet on the new arrangement, a breach of our assistant server could use the whole-key copy kept there to receive a group's earlier keys; that is a stated limit until the separation is finished. See §3.7, §4, §5.4, §6, §7, §8 and §9.
Amended 17 October 2026. A person's own key, which protects their assistant conversation and the private parts of their plans, passes only from one of their phones to another. When every phone that held it has been removed, no phone can ever pass it on, yet a new phone kept asking them to confirm it from a phone they already use. From the next app version, that phone starts a new key of its own, made on the phone and never sent to us, and says that what was sealed under the old key can no longer be opened. Our database lets only one of a person's phones start a version, so two phones doing this at once no longer end up with a key each, and a person whose key was already split that way gets a shared one back. See §3.5, "A person's own key".
Amended 16 October 2026, again. Asking for older content now posts a request card in the group chat. On the phone of the owner or admin who can share it, the card carries the tap itself, and everyone reading the chat can see whether it is done. Phones can now ask our database which earlier keys a member's phone holds, to show that. See §3.7, "Asking in the chat".
Amended 16 October 2026. The chat line posted when a group turns on sharing its earlier messages read as a promise that everyone could now see them, while a phone could still be missing an earlier key. It now says who turned sharing on, and the notice on a phone still missing older content says what would get it there. See §3.7.
Amended 15 October 2026, again. When a phone that holds the current key still can't open older content, it now says who has it and what would get it there, and the owner or admin whose tap is needed is told above the chat. Either notice can be hidden for up to a week, on that phone only. See §3.7, "When older content still doesn't open".
Amended 15 October 2026. A request for the key from a phone that doesn't have it is shown to the group's owners and admins as a line in the chat while it is open. It is an exception to the 10 October rule, made because the person waiting needs someone to act, and it is shown only to the people who can share the key. It is not a stored notice and writes nothing new. See §3.6.
Amended 15 October 2026, again. §5.3 compared automatic delivery with "today", where someone who can sign in as a member can already have our assistant read a group's tasks and calendar. We found that baseline is wider than this document assumed: our server opens that content for any active member's account whatever phone asks, so it also reaches a phone still waiting for the key, and a phone whose delivery a member stopped or revoked. A change assessed in the Tier 2 assessment, not yet built, makes our server open content only for a request that proves it comes from a phone holding the key. Until it is switched on, the comparisons in §5.3 stand as written; after, they change as noted there.
Status: Built on 30 September 2026, against this assessment, which was written before the code the same day; not yet released. All four pieces of work it gates were built: separating the group's keys, moving existing encrypted content onto the new keys, recording a notice for every way a key is given out, and letting our server give a waiting phone the narrower key for tasks and calendar when no member has shared the group's key after 24 hours. That last piece is a switch an admin turns on per group and is off everywhere until then. See the note below for what this means in practice. Written: 30 September 2026 · Last revised: 27 October 2026 (removing a lost phone from one screen; history verification, §10) 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 mechanism this assessment describes was built the same day it was written, matching the design below with no material deviation we found. None of it has reached a group yet: the database changes take effect at our next release, and a phone only reads or writes the new keys once it has updated to the app version built alongside them. Three things to hold in mind while reading the rest of this document as a plan:
- Separating the keys, moving existing content, and the admission notices (§§3.2–3.4, 3.6) are built and will apply to every group once released — nothing here needs a further decision.
- The automatic key-delivery path (§3.5) is built but switched off everywhere. It only ever runs for a group whose admin has turned on "Automatic key delivery" for that group, which is its own explicit, announced choice — never a default. Until an admin does that, a waiting phone is only ever given the key by another member, as it is today.
- The off-database ledger that bounds automatic delivery (§3.5, §5.3) is written but not switched on. Its storage location is prepared, but nobody has turned it on yet; until that happens, the delivery routes themselves refuse every request and hand out nothing — so the weaker case this document's threat model describes cannot occur before that separate step is taken, and it is taken by us, not by an app release.
The one claim to be careful with. Today a single group key protects both kinds of encrypted content: the kind our server must never read (chat, photos) and the kind our assistant server may read while answering a member (event notes and places). At the Australia and Global levels a copy of that one key is wrapped for our server. Nothing on our server reads chat or photos with it — but that is a rule in our code, not a property of the keys. This assessment describes the change that makes it a property of the keys, what it can and cannot fix for content that already exists, and a new, bounded way for our server to hand out the smaller key. That last part is a new capability for our server, and §5.3 says plainly where it is weaker than what exists today.
1. Purpose and scope
We want to be able to say, and have it be true of the keys themselves: "Chat, photos and your personal plans are encrypted with keys our servers never receive." Today that sentence would be false at the Australia and Global levels, because the key that protects chat and photos is the same key our server holds a copy of for the assistant.
In scope:
- Key separation. From a new key version onwards, the key a group's phones share (the root key) never leaves the phones. Two keys are derived from it: a Tier 1 key for content our servers must never read, and a Tier 2 key for content our assistant server may open inside a member's request. Only the Tier 2 key is ever sent to our server.
- Moving existing content. A job on members' phones moves each existing encrypted item from the old key to the right new key, so that our server's older copies open nothing that is still stored. It does this without re-encrypting the content itself.
- Locking to Device-only (the level the app will call "Phones only") uses the same move, so that nothing written before the lock stays openable by our server's older copies.
- Notices. Every way a person, a phone or our server comes to hold one of a group's keys leaves a notice for the group, attributed to whoever did it, or recorded as having no member behind it. Since 10 October 2026 the routine ones are listed on the Devices screen rather than in the group's chat (§3.6).
- Key delivery to a waiting phone. A member helping first, as today. If nobody helps within 24 hours, our server may give that phone the Tier 2 key only, with a notice any member can use to stop it from happening again.
Out of scope, and covered elsewhere:
- Which fields become encrypted and which key each uses (task titles, baby notes, meal text and so on). That is a separate assessment of content encryption. This one only decides that such fields will use the Tier 2 key, and chat, photos and plans the Tier 1 key or the member's own key.
- Personal plans moving to the member's own key. The member's key already never leaves their phones (it protects the assistant conversation today); moving plans onto it is decided here and assessed with the content encryption work.
- Google's independent record of key use, and checking it against ours. That is a separate piece of work on detection; this assessment relies on it in §5.3 and says where.
- What reaches the AI model provider. Unchanged by anything here.
2. Data involved
| Kind | Whose | Children's? | Sensitive? | Free text? |
|---|---|---|---|---|
Root key per group and version: 32 random bytes, stored only as group_key_wraps rows boxed to one phone's public key (wrap_kind = 'device'), or sealed under a recovery phrase ('recovery') |
the group's | no — it is a key | yes: whoever holds it opens every item the group has encrypted at that version | no |
Tier 1 and Tier 2 keys, derived on the phone from the root key; never stored on a server, except the Tier 2 key wrapped under our key-management key (wrap_kind = 'kms') |
the group's | no | yes, as above for their share of content | no |
New: group_key_versions — per group and version, which key arrangement the version uses and two 32-byte key checks that let anyone holding a key confirm which key it is, without revealing it |
the group's | no | no — a check value is a one-way function of a key | no |
Per-item wrapped data key (wrapped_data_key) and key_version on each encrypted row — the only two columns the move rewrites |
the group's | the content they protect often is (photos of children, messages about them) | the columns are not; the content is | no |
| New: delivery hold and delivery rows — which phone is waiting, since when (set by the database clock, not the phone), whether a member stopped it, and the Tier 2 key boxed to that phone | the waiting member's | may be a child's phone | the boxed key, as above | no |
| New: an off-database ledger entry per hold and per delivery (group and phone identifiers, a hash of the phone's public key, the server's own time) | the waiting member's | as above | no | no |
| Notices about each key event, listed on the Devices screen and, for some, shown in the group chat (§3.6) | the actor's and the recipient's names, a platform word, a version number | may name a child member | no | no — fixed sentences, never a phone's free-text name |
New (14 October 2026): copies of a group's earlier keys, each boxed to one receiving phone's public key (group_history_wraps), with the sending phone and member, the version and the time |
the group's | the content they open often is | yes: an earlier key opens what the group encrypted under it | no |
New (14 October 2026): an admin's fingerprints of earlier keys that have no check value (group_history_legacy): a hash of each key, the key version the admin vouched from, and a code only that key makes |
the group's | no | no — a one-way hash; the key itself is never sent | no |
New (14 October 2026): receipts — which phone has which earlier key (group_history_receipts); the group's sharing state (when it was told, whether it is on, held, or waiting for our support); and, per member, when they or an adult for them acknowledged that members can see the group's earlier messages |
the member's; a child's acknowledgement is an adult's act | may concern a child member | no | no |
Not collected, and could have been: no copy of the root key or the Tier 1 key anywhere off the phones; no list of which items a phone opened; no record of why a member stopped a delivery; nothing that ranks or counts members.
Strict necessity (s 38(2)(b)). Nothing here collects new personal information about a child. The key checks, hold rows and ledger entries are what make the protections checkable and the new delivery path bounded; each is identifiers and times only.
3. Flows and locations
Every way a group's key reaches a phone or our server, with what starts it, who may and what the server checks, is listed in the key paths inventory (added 21 October 2026). A test scans the database and the code for ways of writing a key copy, and a new one it finds fails until it is listed; it is a scan, so a writer it can't read could get past it. Each listed way is tied to a test that tries it with a removed phone's sign-in and sees it refused. The inventory says exactly what is checked.
3.1 Today
- A phone mints each group key version and boxes it for every member phone it can see. At every level except Device-only, an admin's phone also sends that same key to our assistant server, which wraps it under a key in Google Cloud KMS (Australia) and stores the result.
- Every encrypted item has its own data key, wrapped under the group key. Whoever holds the group key for a version can open every item at that version: chat, photos, captions, album names, comments, event notes and places, and — at Device-only — task and event titles.
- Our assistant server opens only event notes and places, only inside a member's request, after writing an audit row. That limit is our code, checked by an automated test. The key it unwraps would open chat and photos too.
3.2 The new arrangement
| Data | Phones | Supabase (Sydney) | Google Cloud (Australia) |
|---|---|---|---|
| Root key | minted, boxed to each member phone, derived from | boxed copies (unreadable without a phone's private key); a recovery-phrase copy | never |
| Tier 1 key | derived when needed; wraps item keys for chat, photos, captions, album names, comments | never, in any form | never |
| Tier 2 key | derived; wraps item keys for fields the assistant may read | never in plain form | received once per version from an admin's phone, wrapped under KMS, stored as the kms wrap. Opened only inside a member's request, as today |
| Key checks | computed at minting; every phone re-computes and compares them whenever it loads a version | stored in group_key_versions |
our server computes the Tier 2 check of what it is sent and refuses anything that does not match |
What makes this hold:
- The two keys are different keys, derived with HKDF-SHA256 under distinct labels bound to the group and version. Knowing the Tier 2 key tells you nothing about the Tier 1 key or the root.
- Each wrapped item key says which key wrapped it in its first byte, so a reader never guesses. Our server refuses a Tier 1-marked item before trying any key.
- The KMS wrap is labelled. A Tier 2 wrap is encrypted with the purpose named in the data KMS authenticates alongside it. An older wrap, which holds a whole group key, cannot be opened as though it were a Tier 2 wrap: KMS refuses the mismatch. So even if our database were altered to describe an old version as new, our server could not be tricked into treating an old whole-key copy as the smaller key.
- Our server checks what it is sent. A phone at the new arrangement publishes the Tier 2 key's check value when it creates the version. Our server refuses to store anything whose check does not match, so a buggy or out-of-date phone cannot accidentally send the root or Tier 1 key. Every other member phone re-computes the checks from the root it holds; a mismatch stops that phone encrypting anything under the version and tells the member something is wrong.
- Older versions of the app cannot quietly undo the separation. Once a group is on the new arrangement, the database refuses a new key version created the old way, and our server refuses an old-style copy for a new-style version.
3.3 Moving existing content
A job on a member's phone takes each encrypted row whose version still has an old whole-key copy
on our server, opens its item key with the old version's root, and rewraps that item key under
the current version's Tier 1 or Tier 2 key. The content's own ciphertext does not change; nor
does the item key. Only wrapped_data_key and key_version are rewritten, through a narrow
function per table that checks the caller is an active member, the row is in their group, the
target is the group's current version, the row has not changed since the phone read it, and the
new wrap is marked for the right tier.
When no encrypted row in a group remains at a version with a whole-key server copy, the group is complete, and a notice says so. From then on the database refuses a new row, or a new item key, at such a version. A nightly check re-verifies each complete group.
Limits, stated plainly:
- Old server copies are never deleted. The key table is append-only, deliberately, so that nobody can swap a key for one they can open. After completion those copies open nothing that is still stored — but they would open older database backups.
- Backups keep the old wrapped item keys for up to 90 days (our own encrypted backup copies; our database provider's managed backups have their own retention, which we have not verified). Combined with an old server copy, a backup made before completion can still open content. A restore from such a backup must re-run the move.
- A row no member's phone can open cannot be moved. If every phone that held a group's first key version is gone, rows written under it stay where they are. Those rows are already unreadable to members; they stay readable to our server's old copy until they are deleted. The group is shown as not complete, with the count, rather than being described as complete.
- Item keys do not change. Anyone who ever opened an item key — a member removed later, or our server for the event notes it was entitled to read — could still open that item from a backup.
- Phones running an older version of the app cannot read content written under the new arrangement until they update. The new arrangement is switched on for a group only when every active member has at least one phone that can read it; an older second phone of theirs shows "encrypted" until it is updated.
3.4 Locking to Device-only
In order: the level changes first (so nothing offers our server a copy of what comes next); a new key version is made with no server copy (the database refuses one); the move runs for every encrypted row, Tier 1 and Tier 2, at any version our server holds a copy for; then titles are encrypted as today. Only when the move is complete does the group's "our servers hold no key for anything you write from [date]" statement become true. Refusing server requests at Device-only is our code; the move is what makes it a property of the keys.
"Every encrypted row" means every place a group's words are sealed under the group key: chat and photos, events, tasks and their history copies and categories, a baby's records, meal plans, ceremonies, games, the standing note to the assistant in Mail Sifter, and the words inside the assistant's cards. Until 30 September 2026 the move and the completion count left out a baby's records, that note and the cards, so a group could be counted as complete while those stayed under a version our server was given a copy of. Two things are deliberately not moved, and the statement must not be read as covering them:
- Mail received before the lock. Each forwarded email is sealed to the group's inbox keypair, not to the group key, and the private half of that keypair is kept under the key version current when it was made. A Device-only group receives no mail, so the lock needs no new keypair, but email already received stays openable by that older version until it is deleted: 30 days after it arrived, or 30 days after a member resolves it (its body is removed at resolution).
- A former member's private assistant cards. Cards in a member's own assistant conversation can be moved only by that member's phone. Once a member has left, nobody can open or move theirs, so they are not counted; they stay under the older version.
A care note's medicine fingerprint (which lets the app keep one note per medicine) is made from the group key; the move makes it again under the new key, so the old copy cannot be used to test guessed medicine names against it.
3.5 Giving a waiting phone a key
First, a member helps — exactly as today. A phone registered for a member, with no key for the group's current version, asks from the chat; members whose phones hold the key get a card and a push, and one tap (or a QR scan, or a setup link) boxes the root key for that phone. Improvements: the request is made automatically when a registered phone has no key, and helpers see new requests without reopening the screen.
Then, if nobody helps within 24 hours, our server may deliver the Tier 2 key. In order:
- When the phone starts waiting, it asks our assistant server to open a hold. The server records the hold in an append-only ledger in Google Cloud Storage, outside our database, with the server's own time and a hash of the phone's public key. The database records the hold too, using the database's clock, never the phone's. A notice goes to the group chat and a push to the group's members: "Sam's new iPhone is waiting for this group's key. If nobody shares it by [time], our server will give it the key for tasks, calendar and similar content — not chat or photos. You can stop this."
- Any active member can stop the hold. That is recorded with their name, and our server will not deliver automatically to that phone in that group again.
- After 24 hours, the waiting phone asks for delivery. Our server checks, through the member's own session: the member is active; the group is at Australia or Global and has not switched automatic delivery off; the current version uses the new arrangement and has a Tier 2 copy; the phone belongs to the member, is not removed, and holds no key for the current version; none of the member's other phones holds the key (a member who has a keyed phone admits a new one themselves); the hold was not stopped. It then checks, against its own ledger and its own clock, that the hold is at least 24 hours old and the phone's public key has not changed, and that the group has had no more than two automatic deliveries in 30 days and the whole service no more than ten today.
- It writes a ledger entry and an audit row, then opens the Tier 2 copy (KMS refuses anything but a Tier 2-labelled wrap), boxes the Tier 2 key to the phone's public key, and stores the box through a database function that re-checks every condition in the same transaction. It never returns a key in its response; the phone fetches the box, which only it can read.
- A notice and a push tell the group: "Our server gave Sam's iPhone the key for tasks, calendar and similar content because nobody shared the group's key within 24 hours. Chat and photos stay locked on it. Any member can revoke this."
Revoking blocks further automatic delivery to that phone in that group and asks the next member phone that holds the root key to rotate to a new version, so that new content is under a key the phone does not hold; existing Tier 2 content is then moved to the new version as well. It cannot take back anything the phone has already opened or copied.
Never, under any path: our server delivers the Tier 1 key or a root key. It does not hold either at the new arrangement. For versions made the old way, whose server copy is a whole key, automatic delivery is refused — and KMS itself would refuse to open that copy under the Tier 2 label.
A person's own key (added 17 October 2026). A person's own key protects their assistant conversation and the private parts of their plans. It is made on their first phone, passes only from one of their phones to another, and is never given to us, so our server cannot deliver it. In use, a member whose earlier phones had all been removed got a new phone that waited for the key forever, telling them to confirm it from a phone they already use, which they no longer had. From the first app version released after 17 October 2026, written before the code:
- A phone that has no copy of the person's own key checks whether any of that person's phones that have not been removed holds it. If one does, the phone waits, as before, for that phone to pass it on.
- If none does, the phone starts a new version of the person's own key, on the phone, wrapped for itself only, exactly as a first phone does. Nothing is sent to us that was not sent before.
- What was sealed under the old key stays sealed. The assistant says once that the earlier conversation was on phones that have since been removed, so it can't be opened here, and an edit to a private plan item from before the change is refused with the same reason. Both are said only on a phone that started a new version, and only about rows it cannot open: an ordinary second phone, whose words are readable on the first one, is told where to go instead. That conversation was already unreadable on every phone the person still has; this only stops the false promise and lets the assistant and private plans work again. Our server never had the old key, so we cannot open what was sealed under it. Removing a phone does not reach into that phone: like revoking in a group, it stops further delivery and cannot take back a key a phone already holds, so a removed phone that still exists could open what it already had.
- If the check can't be read (for example, offline), the phone waits rather than start a key.
- Only one phone starts a version, and our database is what decides it. It records the first copy of a new version only when that version follows the newest one the person has and no phone of theirs that has not been removed still holds the key, and it answers one phone at a time. So two of the person's phones trying at the same moment do not both start: the second is refused and goes back to waiting to be given the key, as above. It holds whether the two phones are a second or a week apart.
Where a person's key was already split. A person whose phones each started their own key before this change — which was possible until the app version released after 17 October 2026 — has two different keys under one version number, and neither phone can give its key to the other. When one of those phones next confirms the other, it starts the next version and gives it to both, so everything written from then on can be read on both. What each phone sealed under the split version was readable only on the phone that sealed it, and after that moment is not openable on either; our server never held any of those keys, so nothing we hold can open it. Carrying what was sealed under an earlier version of a person's own key across to a new one is not built, for their own key or for a group's.
Residual. A person's own key is still one version at a time on a phone, so the repair above and the fresh start above it both leave the person's earlier assistant conversation and private plan wording unopenable. The assistant says so once, in the session it becomes true.
3.6 Notices
Every event below writes a notice for the group. The database writes it, so no path through the app can skip it, and every member can read every notice. Notices deliberately raise no unread badge and no push; the two delivery notices above are the exception and are sent as a separate push. A notice records; it does not guarantee anyone reads it.
| Event | Notice | Shown in the chat |
|---|---|---|
| Group key first created | created this group's key | no |
| A member joins, is approved, accepts a direct invitation, joins an event group, or a child is linked and joins | X joined (approved by Y, or by invitation from Y) | yes |
| A phone is given the root key by a member (approval, QR, setup link, a member's own second phone) — for every version, not only the first | Y shared this group's key with X's iPhone | no |
| A member restores with the recovery phrase | X used the group's recovery phrase on an iPhone | no |
| A recovery phrase is saved | X saved a recovery phrase for this group | no |
| Our server is given a copy (Tier 2 at the new arrangement; the whole key for older versions) | X gave our assistant server the key for tasks and calendar | no |
| A phone is removed; key rotation, including the switch to the new arrangement | X removed an iPhone; X rotated this group's key | no |
| Moving existing content completes | Existing messages and photos now use keys our servers never receive | no |
| A phone registered and waiting; a hold opened, stopped, delivered, revoked | as in §3.5 | yes |
| Any of the above with no member behind it (a change made with a service credential) | recorded as made without a member, never silently skipped | yes |
Every key notice in the table, whether or not the chat shows it, is listed under Key history on the Devices screen, newest first. A member joining is not a key event and is not listed there.
Where a notice is shown (amended 10 October 2026). As built on 30 September, every one of these notices was a line in the group's chat. In use, that put two to four lines about keys, key numbers and recovery phrases into a family's conversation each time they set up the group, added a member, changed a phone or set up a shared display, and a test group whose phones were reset often saw dozens. Almost all of them record something a member chose to do, which nobody else needs to act on, written in words most members should never have to think about. So from the first app version released after 10 October 2026:
- What is written does not change. Every notice above is still written by the database, in the same words, and every member can read all of them under Key history.
- The chat shows a key notice only when someone may need to act on it: anything done with no member behind it, and every step of automatic delivery — a phone waiting for our server, a delivery, a stop, a revocation. These lines end in an arrow, and on a phone tapping one opens the Devices screen at the part it is about: a waiting phone and its Stop or Revoke button, which sit at the top of that screen while a wait or a delivery is open, or Key history.
- Everything else moves to Key history only: a member sharing the key with a phone, adding, removing or recovering their own phone, saving a recovery phrase, giving our assistant server its copy, and rotating the key.
- A phone on an older app version still shows every notice in the chat until it updates.
An open request for the key (added 15 October 2026). A phone without the group's current key can ask for it, by itself once per key or when its person taps Ask to connect on the notice shown above content it can't open (see Connecting a phone to encrypted group content). A request is not a key notice and writes nothing to the chat. From the first app version released after 15 October 2026, each owner and admin sees one line at the top of the group chat for each open request, on a phone that can connect the other, drawn by their own phone from the request. It names who is waiting and opens the card that connects their phone. This is a recorded exception to the 10 October rule: a person waiting for the key is someone who needs a member to act, which is the test that rule applies. It differs from the notices above in two ways. Only the owners and admins asked to connect the phone see it, not every member, so children who are not admins never do. And it goes the moment the request ends, so the chat keeps no record of it; the request itself is deleted on its own schedule.
Today several of these are silent: joining, saving a recovery phrase, giving our server a copy, and any wrap after a phone's first one. Today's convention of saying nothing about a change with no member behind it is reversed for key events.
3.7 A group's earlier keys reach every member's phone (added 14 October 2026)
What changes. A phone given a group's key has always received the current key only. Messages and photos encrypted under an earlier key stayed locked on that phone unless the member's own older phone, or the recovery phrase, brought them back. So a member who joined later, or who replaced their phone after a key change, saw a locked bubble for everything before. We decided that every member of a group should see its history, children included, and that the group and each person joining must be told. From the app update released after 14 October 2026, once a group has turned it on, a member's phone that holds the current key sends the group's earlier keys to every person's phone that holds it too.
Who receives them. Our database refuses a copy of an earlier key unless all of these hold, every time one is sent:
- sharing is switched on for the whole app — it is off at release, and we switch it on once most groups have made the one key change described in §5.2;
- the group's current key has a check value, and our support has not reset it;
- the group has turned sharing on, and no hold is in place (both below);
- the receiving phone holds the current key and has shown it with that key's token (§5.2), has not been removed, and runs an app version that checks what it is given;
- the phone belongs to a person who is an active member — never a shared display or a profile;
- that person has been told (below).
Being told. An adult has been told if they were already in the group when the group was told, or if they agreed to a sentence saying so when they joined (on the join screen, or when accepting a direct invitation), or later on a one-time sheet. A child is told through an adult, and a child's own agreement never counts, though the child sees the same sentence. The adult can be: the admin who turns sharing on (for the children already in the group), the adult who approves a child's request to join, the adult admin who sends a child a direct invitation, a guardian of the child who is in the group, or an admin of the group. Our database refuses each of these from someone who is a child. A child here means someone under 18 by the birth month their guardian gave, or someone linked to a guardian with no birth month showing they are an adult; a person with neither is treated as an adult, as everywhere else in the app.
Turning it on: one tap by an owner or admin. Keys made since the change in §5.2 carry a check value, so a phone can confirm that a copy of one is genuine before keeping it. Earlier keys carry nothing to check against, and no automatic rule can tell a genuine one from one an attacker planted. So an owner or admin of the group vouches for them once, from a phone that holds the current key and trusts it (it made the key, or checked the key change into it). The card says "Share this group's earlier messages with everyone". The tap:
- lists the group's phones by member, platform and date added, and is refused if that list has changed since the card was drawn; "Something's wrong" leads to the existing ways to remove a phone;
- records, for each earlier key the admin's phone holds and has actually opened a stored message or photo with, a fingerprint of that key, locked with a code only the current key can make, so every other phone can check a copy against it;
- posts in the group's chat that sharing is on. As built on 14 October the line read "Everyone in this group can now see its earlier messages and photos, including people who joined later", which read as a promise: a phone can still be missing an earlier key, for instance one only another admin's tap can share. From the first app version released after 16 October 2026 the chat shows it as what happened, "Sarah turned on sharing earlier messages and photos with everyone in this group, including people who join later", for lines already written too, and new lines are written as "Sharing earlier messages and photos is now on for everyone in this group, including people who join later";
- acknowledges for the group's existing child members;
- turns sharing on for the group.
The tap is refused while sharing is switched off for the whole app, so the chat never announces something that is not yet happening. A later tap writes no second notice; it re-checks the phones, may add fingerprints for earlier keys not yet listed, and acknowledges for any child still waiting.
New groups. A group whose first key is made after the change in §5.2, while it is one person, has no earlier key without a check value, so sharing is on from its first key, with no notice: there is nobody else in the group to tell. Each person joining is told when they join. A group that already has other members when its first key is made (an older group keyed for the first time) is not turned on that way: its owner or admin turns it on, and the group is told, as above.
Holds. Sharing stops for a group, until an owner or admin taps again, whenever something suggests its phones need checking: a key change that skipped a version, a key reset by our support, or a phone refusing a key change (once per key). When the version a key change skipped from has no check value — the window before the group's first checked key change, where nothing can rule out a phone planted there — the group waits for our support, not only an admin: we secure the account involved, check the group's phones (identifiers only, recorded in our access register), remove anything unexpected, and lift that hold. An admin still taps. If a key change skips past the key an admin vouched from, the fingerprints recorded under it are deleted and the card returns. Holds that started before this update are worked out from the group's existing records when the update is applied. Stopping sharing does not take back what phones already received.
How a copy travels. A member's phone that holds the current key, and trusts it, sends copies at most every ten minutes while the app is open, to at most five phones at a time. Each copy is boxed to the receiving phone's own public key, so only that phone can open it; our database stores the box and checks its size and shape, nothing more. The receiving phone opens it and checks it against the key's check value or the admin's fingerprint before keeping it. A copy that fails is thrown away, and a copy never replaces a key the phone already has that works. The first time a phone receives a group's earlier keys, a notice is recorded under Key history: "Alex shared this group's earlier keys with Sam's iPhone or iPad." A phone on an older app version shows that notice as a line in the chat until it updates.
What survives what. Copies and fingerprints stay when the member who sent them, or the admin who tapped, deletes their account or removes that phone: they belong to the group, and deleting them would lock other members out. Removing a phone stops it receiving new copies, and it can no longer read the ones it was sent. Those copies and its receipts stay on our database until the phone's record itself is deleted, which happens when its owner deletes their account. Everything goes when the group is deleted.
When older content still doesn't open (added 15 October 2026). In use, a member's phone that held the current key could still be missing an earlier key that only one other phone held, and that key needed an admin's vouch, from a phone holding it, before any phone could pass it on. Nothing on either phone said so. From the first app version released after 15 October 2026, written before the code:
- The phone missing older content names who has it and what would get it there, worked out on the phone from records it can already read (which members' phones hold each earlier key, and whether each has a check value or a vouch): it will arrive when that person next opens the app; or that person, an owner or admin, needs to tap Add earlier messages from this phone; or open the app on the member's own other phone; or, when no phone that could pass it on still has it, that it can't be shown on this phone. It offers to start a message to that person in the group chat, which the member sends or not.
- An owner or admin whose phone holds an earlier key that only their tap can make shareable, and that another member's phone is missing, sees one line above the group chat naming that member and opening the tap. Like the request line, it is drawn by their own phone and never stored, and only they see it; it goes once they tap, because their tap is what it asks for.
- Either notice can be hidden for now. Hiding folds it to one line on that screen rather than removing it, lasts up to seven days, and ends early as soon as anything it describes changes. It is kept only on that phone; nobody else, including us, can tell.
Naming who holds an earlier key tells a member something new about another member's phone, inside their own group: that it holds a key to the group's older content. We accept that: it is the fact the member needs to ask the right person, and members already see each other's names and roles.
Asking in the chat (added 16 October 2026). In use, the message the member started was read as ordinary chat and nothing happened: the person asked had to find a setting among many, and nobody could tell whether it had worked. From the first app version released after 16 October 2026, written before the code:
- "Ask Sarah" on the phone missing older content posts a request in the group chat when tapped; the tap is the send. It is an ordinary chat message, encrypted like any other, that carries the numbers of the earlier keys this phone is missing and this phone's identifier, so every phone in the group can draw it as a card. While one is waiting, the button opens the chat instead of asking again.
- On the phone of an owner or admin who can make those keys shareable, the card carries the tap itself: what it does, that everyone in the group sees the earlier messages, including people who join later, and that it can't be undone, with one button. It is the same tap as Add earlier messages from this phone, with the same checks. The line above the chat (above) does the same tap in place, after a confirmation saying the same thing. Turning sharing on for a group the first time stays in Manage group, because that tap lists the group's phones to check.
- Everyone who reads the chat sees where the request is: waiting for that person, shared and on its way, or done. To tell, a phone asks our database which of those earlier keys the asking phone now holds. It answers with key numbers only, from records the phones already write (the keys each phone was given and the receipts it records for copies), and only to a person who is an active member, about a person's phone in the same group: never to a shared display, and never about a shared display's or a profile's phone (corrected 27 October 2026; before then a shared display could ask, see §10).
That answer tells every member of the group, children included since they read the chat, whether a named member's phone has the group's older content. It is the same kind of fact as naming who holds an earlier key, above, and we accept it for the same reason: without it the person asked can't tell whether their tap worked, and the person asking can't tell whether to ask again.
Shared displays, Device-only groups and event groups. A shared display stays current-key only: it never receives a group's earlier keys and never sends them. Device-only groups follow the same rules, since nothing here passes through a server copy of any key. Event groups follow them too; their first keys are made after the change in §5.2, so sharing is on from the start.
4. Legal basis and consents
- No new consent purpose. Separating keys and moving content narrow what our server can open; they change no one's settings and give no one's data to anybody new.
- Giving our server the Tier 2 key remains an admin's act on the group's behalf, as today (only an admin of a standing group may write a server copy; a Device-only group cannot).
- Automatic delivery is a new way for a group's content to reach a phone. It is not a new disclosure outside the group — the phone belongs to a member who is entitled to that content — but it is a new way for our server to release a key. It is therefore switched on per group only with a notice to that group when it first becomes available, and an admin can switch it off for the group in one tap (a narrowing, so one tap is enough). We are not treating it as a consent purpose, because nothing leaves the group. We record here that this is a judgement, and that §8 accepts its cost.
- Children. A child's phone is treated like any member's: a child member is already entitled to the group's content. A guardian, like any member, can stop a hold or revoke a delivery.
- A group's earlier keys (§3.7) are not a new consent purpose either: nothing leaves the group, and every receiving phone belongs to a member. What changes is that a person who joins later sees what was written before they joined. We treat that as a fact each person must be told before it applies to them, not a consent the group's existing members give: the group is told once in its chat, and each person joining is told before they join, which is recorded. For a child, an adult in the group acknowledges on their behalf, and our database will not send a child's phone an earlier key until one has. A member who does not want their earlier messages seen by people who join later can delete them, as today. We record that this is a judgement, and §8 accepts its cost.
5. Risks and mitigations
5.1 Separation and moving content
| Risk | Who is harmed | Mitigation | Structural or conventional? | Residual |
|---|---|---|---|---|
| Our server, or anyone who compromises it, opens chat and photos with the key it holds for the assistant | every member; every child photographed | at the new arrangement our server receives only the Tier 2 key, which opens no Tier 1 item | structural (a key the server never receives), from the date a group completes the move | older backups (§3.3) |
| An out-of-date or buggy phone sends the root or Tier 1 key to our server | the group | our server refuses anything whose check does not match the published Tier 2 check; it refuses an old-style request for a new-style version | conventional, test-pinned (server code) | a phone that lies about both the check and the key is a member's phone that already holds the root |
| A malicious member publishes a false check so that the "Tier 2" copy is really the root | the group | every other member phone re-computes the checks and refuses to encrypt under a version whose checks are wrong, and says so | structural on honest phones (cryptographic comparison) | the member already held the root; they could leak it by other means |
| An older version of the app rotates a group's key the old way and sends the whole new key to our server | the group | once a group is on the new arrangement, the database refuses old-style key versions and our server refuses old-style copies for new-style versions | structural (database policy and trigger) plus conventional (server) | an older phone cannot rotate that group's key; it reports the failure and a newer phone does it |
| A phone with an out-of-date key cache writes a message under an old version our server still holds a copy of | the group | after completion the database refuses a new item key at any such version | structural (trigger) | the send fails and is retried after the phone reloads its keys |
| The move corrupts a wrap and makes content unreadable | the group | each rewrite is compare-and-swap on the exact old value; the phone opens the item key with the old key and re-opens it with the new key before writing; nothing is re-encrypted | structural as to atomicity; conventional as to verification | a phone bug that writes a key that opens but is wrong is caught by the re-open check |
| A group is told it is protected before it is | the group | the promise is shown only for groups the database has stamped complete; the stamp is recounted in the same transaction and re-checked nightly; a restore re-runs the move | conventional (copy rendered from the stamp), stamp structural | a backup restore between stamp and nightly check |
| Our server's old whole-key copies plus a backup reopen historical chat | the group | backups age out at 90 days; restores re-run the move | conventional (retention setting) | up to 90 days, and the provider's own backups |
5.2 Existing weaknesses this work also closes
Reading the current code for this assessment found three things that matter as much as the separation itself:
- Today a key rotation hands the new key to every registered phone of every active member, without checking whether that phone ever held the key before or whether its public key changed. So a phone that exists only as a database row receives the whole group key at the next rotation. The new arrangement changes rotation so that a new version is boxed only for phones that already held the previous version (plus the rotating phone); every other phone waits for a member to admit it, as a new phone does. The database enforces this.
- That fix counted a record, not a key (found 14 October 2026). "Already held the previous version" meant the database had a record of a key copy for that phone, and any member may write one, with any contents, for a phone of their own. So an account could make a phone it had just set up count as holding the key, be given the next one, or change the group's key itself; and until a group moved to the new arrangement, an app could still make the next key the old way. From the app update released after 14 October 2026, each new key comes with a check value: the hash of a token that only that key produces. A phone counts as holding the key only after a phone that holds it — the phone itself once it has opened the key, or the phone that admitted it — has shown the database that token, and making the next key needs the same token. The database compares the token with the stored hash; it never sees the key. No app can make a new key except through the one function, in any group. The same update closes what remains for the phones themselves: each key change carries a code that only a holder of the key before it can make, and every phone checks it, and checks the key it was given matches what the key change committed to, before encrypting anything under it. A key that fails is still readable but never used for new content, never passed on, and Key history records that a phone refused it.
- Any active member can change a group's current key version number, including moving it backwards. Our phones encrypt with the newest version they hold, so this does not leak content today, but nothing refuses it. The version will only ever move forward, one step at a time, and at the new arrangement only through the one function that also writes the new version's copies.
- Rotation after removing a member is best effort on one phone. If that phone cannot do it, nothing retries it. The database will record that a rotation is owed when a member is removed or a phone is revoked, and the next phone that holds the key will perform it.
5.3 The threat model for automatic delivery
The comparison that matters is with what exists today. Today our server opens encrypted event notes only inside a member's request, a day's events at a time, through the assistant, at most 60 times an hour per member, with an audit row first. (Amended 15 October 2026: once the proof check described in the Tier 2 assessment is switched on, that path opens nothing for a request that doesn't come from a phone holding the key, so the sentence that follows stops being true.) Someone who can impersonate a member can already use that path; what they get is bounded, slow, and passes through the AI model. Automatic delivery gives a phone the whole Tier 2 key for the current version, which — once existing content has been moved — opens all of the group's Tier 2 content, history included. We looked for a way to withhold history and did not find one that keeps the feature useful: the point of delivery is that a new phone can see the group's existing tasks and calendar.
| Attacker | Can they obtain a delivery today's design would not give them? | What limits it |
|---|---|---|
| Someone who holds our database's service key (a breach of our database provider, or of our credentials there). With it they can create any account's session, create member and phone rows, flip any group's automatic-delivery switch, and read all encrypted content. | Yes, for any group — not only ones an admin actually chose to turn delivery on for. The switch (groups.automatic_key_delivery) and the admin identity that is allowed to change it are both facts our database holds, so this attacker sets both themselves: forge a session as the group's own admin (or as anyone; the admin-only trigger checks auth.uid(), which this attacker controls), flip the switch for a group whose real admin never asked for it, fabricate a member and a phone in that group, open a hold, wait 24 hours, and receive that group's Tier 2 key — then read all of its Tier 2 content, not a day at a time. They can also delete the recorded notice and the push row that would otherwise have told the group's owners and admins a hold was open. |
The per-group switch and the notice are not a boundary against this attacker — they are rows in the same database this attacker controls, so we say so plainly rather than let the table imply otherwise. What IS outside their reach is the Google Cloud ledger our server writes: the hold's start and the delivery counts are checked against it and our server's clock, neither of which this attacker can edit. That gives a hard ceiling whatever they switch on: no delivery before 24 hours, at most five per group per 30 days, at most twenty-five across the whole service per day. Every delivery is also a KMS operation in Google's own audit log, checked nightly against our records, and each one raises an alert to us that this attacker cannot suppress. So a database-only attacker can reach at most twenty-five groups a day — of their own choosing, whichever they switch on, whether or not a real admin ever opted in — not before 24 hours, and we find out; for the groups they reach, they get everything Tier 2. |
| Someone who holds only a member's password (account takeover) | Only if that member has no phone holding the key. If they do, automatic delivery is refused and the attacker must be admitted by a phone — the member's own, or another member's by tapping "approve". | The group sees the waiting phone and the hold; any member can stop it. Compared with today this is not worse: the same account can already read task and event titles, which are not yet encrypted, and use the assistant. Amended 15 October 2026: once the proof check in the Tier 2 assessment is switched on, that account's assistant reads none of the group's encrypted content without a phone holding the key, so this comparison narrows to what the account can read unencrypted. Corrected 14 October 2026: until the app update released after that date, this account could also be given the whole next key, or change the key, without being admitted at all (§5.2). After it, being admitted by a phone is the only way in, as this row says. |
| A fabricated membership or phone row made through ordinary app use | No. Rows can only be created for your own account, and membership needs an admin's approval or invitation. | The database policies. A phone's own recorded registration time can be set by the phone, which is why the hold never relies on it. |
| A member who was removed | No. They are not an active member. They keep whatever keys they already held, and can read content under those keys only if they also get the ciphertext, which the database no longer gives them. | Rotation on removal (now recorded as owed, §5.2). |
| A removed phone of a member who is still active (added 20 October 2026: a lost or stolen phone the member removed in Devices) | Before B1, yes: its sign-in kept working until its token expired, so it could read every table its member can, fetch the next key version's wraps, open new versions of the same Group Password generation with the cached password key, register itself again as a new phone, and revoke or attest its member's other phones. | B1 (20 October 2026): each phone is bound to the sign-in that added it; removing it (remove_device) ends that sign-in, records it as removed for two hours, and every table, server function, photo and live update then refuses its leftover token, as does adding a phone again. A phone added before binding is removed by signing out all the member's other sign-ins, whose leftover tokens are refused the same way. The rotation is still owed on removal (§5.2). What stays: anything already on the phone, including keys and content it had opened, and a fresh sign-in with the member's password, which is a new sign-in. An app version from before this change can't remove a phone (it fails closed). |
| Our assistant server compromised while running | Not beyond today. Code running inside our server during a member's request could already unwrap that group's server copy. After separation that copy is the Tier 2 key only, so a compromised server can no longer reach chat or photos at all. It could also write false ledger entries. | Google's KMS audit log, which our server cannot alter; per-request audit rows. |
| Us, or our infrastructure controls | We control deployment, the database and the ledger. | Detection only: Google's audit log and alerts. Nothing here makes it impossible for us. |
What the 24-hour hold buys, precisely. Against someone who does not control the database, it gives members a day to notice a phone they do not recognise and stop it. Against someone who does control the database, a hold kept only in the database would buy nothing — they would simply backdate it and delete the notice; the same is true of the per-group opt-in switch, which they can flip themselves, and of the admin check that guards it, which they can forge. That is why the hold, the per-group and per-day limits, and the delivery record are kept in a Google Cloud ledger our server writes and our database cannot touch. Against such an attacker the hold then buys time and a ceiling: nothing before 24 hours, no more than twenty-five groups a day — of the attacker's own choosing, since the switch is theirs to flip — and an alert to us each time. It does not buy prevention, and it does not depend on any admin having actually chosen to turn delivery on.
What revoking achieves, precisely. It stops further automatic deliveries to that phone in that group, and rotates the key so that new content is under a key the phone does not hold. It cannot recall what the phone has already read or copied, and it cannot remove the phone from the member's account; only the phone's owner can do that. Amended 15 October 2026: until the proof check in the Tier 2 assessment is switched on, neither stopping a hold nor revoking a delivery stops the account behind that phone having our assistant read the group's tasks and calendar from it. Once it is on, a stopped phone gets nothing from the assistant, and a revoked one nothing under the group's next key.
Mitigations considered and not adopted:
- Deliver only key versions newer than the phone's registration, so history is not handed over. After the move, the current version holds the group's whole Tier 2 history, so this would mean either never delivering (the current version usually predates the phone) or not moving Tier 2 history forward, which leaves it under whole-key server copies and defeats the rest of this design. Rejected; the history exposure is stated instead.
- Require the member to have belonged to the group for some days first. A database attacker can backdate membership, and it would lock out exactly the person automatic delivery is for: a member who joined by direct invitation, which has no admin phone in the loop. Rejected.
- Make delivery a consent purpose. Nothing leaves the group, and a consent every member must give before a lost-phone recovery works would not be given in time. Replaced by the group-level switch and the per-hold stop.
Our honest assessment. Against an attacker who controls our database, automatic delivery is materially worse than today's bounded tool reads — and that attacker can reach any group, not only ones whose admin actually turned delivery on, because the switch and the admin check that guards it are both facts our database holds and this attacker already controls. The per-group off switch is not a boundary against a database-compromise attacker, and we no longer describe it as one. What bounds that attacker is the off-database ledger and its limits — the 24-hour hold, the per-group and service-wide caps, the KMS-level label that stops a whole-key copy being released, the rule that a member with a keyed phone is never served, and the alert to us that a delivery happened — which together cap the damage at twenty-five groups a day, none before 24 hours, with us finding out. Against an attacker who does not control the database — the ordinary threat this feature is built for — the per-group switch, defaulting off, is a real boundary: such an attacker (an account takeover, see the row above) cannot turn it on at all. We judge the feature acceptable for invited testing on that basis, not on the false claim that the switch also bounds a database compromise. Without the off-database ledger and the limits, we would not switch it on, and the design keeps it off whenever they are not configured.
The named upgrade path, if this residual is ever judged too wide: anchor the opt-in decision — and the admin identity that is allowed to make it — outside the database, the same way the hold and the caps already are (an entry in the same Google Cloud ledger, or an equally independent record). Until that exists, a database compromise can reach every group's Tier 2 content that could be switched on, not only the ones an admin has switched on today; that gap is accepted as residual risk for invited testing and is not yet closed.
5.4 A group's earlier keys reaching every member (added 14 October 2026)
| Risk | Who is harmed | Mitigation | Structural or conventional? | Residual |
|---|---|---|---|---|
| Someone who joins later reads what was written before they joined, including messages and photos about a child | members who posted, children photographed or discussed | decided as the intended behaviour; the group is told once in its chat, and every person joining is told before joining; the database sends nothing to a person until then | structural that nobody untold receives a key; the words themselves are conventional (app copy) | accepted: anyone admitted to the group sees its history |
| Someone who has taken over a member's account adds a phone and receives the group's history | every member | the phone must hold the current key and have shown it with the key's token, which an account alone cannot produce; a copy of a key made since §5.2's change is checked against its check value before a phone keeps it; an earlier key flows only after an admin's phone vouched for it; a refused or skipped key change stops sharing | structural (database refusals) and cryptographic on honest phones | §8 items 16 and 17 |
| A key an attacker planted before §5.2's change is vouched for and spread to every phone | every member | the admin's phone lists only earlier keys it holds as its own copy and has opened a stored message or photo with; the tap needs the current key trusted on that phone, and the group's phone list confirmed | conventional (the admin's phone decides which keys to list), pinned by automated tests | the tap is a trust decision (§8 item 18) |
| A junk copy replaces a key a member's phone already uses, locking them out | that member | a phone keeps a copy only after checking it, and a checked copy replaces its own key only when its own key fails the same check | conventional, test-pinned (app code) | a defect in that check |
| A child's phone receives history with no adult aware of it | the child, others in the group | an adult's acknowledgement is required, by one of five routes, none of which records a child's (the tap and the acknowledgement for a child refuse a child caller; approval and a direct invitation ignore the flag from one); the database refuses a copy to a child's phone without it | structural | the child is told but not asked (§7) |
| A shared display receives the group's history | everyone in the house | the database refuses a copy for any phone that is not a person's, and displays never send | structural | none known |
| A group's sharing stops and nobody notices | members missing history | the card returns for owners and admins; a metadata-only check for held, support-held and never-turned-on groups is part of our release checks | conventional | a delay, not a disclosure |
| An account fills the receipts or copies with junk to block or slow sharing | members waiting | receipts can be written only for the caller's own phone; copies only by a phone holding the current key, at most 50 per call, each a correctly shaped box; a junk box fails the receiving phone's check and is thrown away | structural as to who may write; the check is cryptographic | a junk copy from a helper delays that one version for that phone until another helper sends a good one |
6. Structural versus conventional
Structural
- The root and Tier 1 keys are never sent to our server at the new arrangement; there is no code path that has them to send, and our server's copy is labelled Tier 2 in data KMS authenticates.
- KMS refuses to open an old whole-key copy under the Tier 2 label, so automatic delivery cannot release a whole key.
- Honest phones refuse to encrypt under a version whose published checks do not match its key, or whose key change can't be shown, with a code only the previous key produces, to come from someone who held the key before it.
- The database refuses: an old-style key version in a group on the new arrangement; a new key version made any way but the one function, in any group; a key version moving backwards or skipping; a new version boxed for a phone that has not shown it holds the previous one, or made by a phone that has not shown it holds the current one (shown with a token only the key produces, checked against a stored hash); after completion, a new item key at a version our server holds a whole-key copy for; a Tier 2-marked item key in a chat or photo table; a server copy for a Device-only group or an event group; any change to a key row after it is written.
- Each rewrite changes only the two key columns, only to the current version, only if the row is unchanged since it was read.
- The delivered Tier 2 box is readable only by the phone it was made for.
- The hold's age and the delivery limits are checked against a ledger the database cannot alter.
- A copy of an earlier key (§3.7) is accepted only when every condition listed there holds, checked in the same transaction that stores it; it is readable only by the phone it is boxed to. Copies, receipts and an admin's fingerprints can't be changed or deleted by any member, and our support is the only one who can delete a fingerprint. The group's sharing state and each member's acknowledgement change only through the database's own functions; a member, or an admin of this or another group, writing them directly is refused. Skipped versions, resets and refused key changes raise a hold through database triggers, and a key change skipping past an admin's vouching key deletes the fingerprints under it.
Conventional, and what pins each
- Our server checks the Tier 2 check before storing a copy, and refuses old-style requests for new-style versions — automated route tests.
- Our server's reader never tries a key on a Tier 1-marked item — automated check.
- Automatic delivery's conditions are checked by our server as well as by the database function; the order (ledger, audit row, key release, store) is asserted by an automated check, as the existing reader's order is.
- The "complete" promise is shown only from the database's stamp — copy tests.
- Notices are written by database triggers (so no app path can skip them), but a notice written by the database is not independent evidence against someone who controls the database.
- That a phone shows it holds a key once it has opened it, and that a phone admitting another shows it on that phone's behalf, is our app code; a phone whose app never does is left out of the next key and asks again. Automated tests pin both, and the database's refusals above.
- Which key notices the chat shows, and the Key history list on the Devices screen, are our app code. Automated tests pin which event goes where, that every notice the chat leaves out is listed in Key history, and that a key event with no member behind it, or one this version of the app does not recognise, is always shown in the chat.
- A system notice raising no badge, and the delivery pushes being the one exception, is our code.
- For a group's earlier keys (§3.7): that a phone checks a copy before keeping it, never lets a copy replace a key that works, and lists for the admin's tap only keys it has opened a stored message or photo with; that copies are sent only from a phone that made or checked the current key, at most every ten minutes and five phones at a time, and never from a shared display or under a refused key; that the card and the join screens say what they say. Our app code, pinned by automated tests.
7. Best interests of the child
- What it gives a child: photos of them and messages about them stop being openable with a key our server holds, and a phone that exists only as a database row stops being handed the group key at the next rotation (fully so from the app update released after 14 October 2026, §5.2).
- What it costs them: automatic delivery could let someone who has broken into our database read a family's calendar and tasks — where a child will be and when — in bulk rather than a day at a time, for any group they choose, not only ones an admin actually turned delivery on for (the switch is a database row too, and that attacker sets it). That is the real cost, and what actually bounds it is the off-database ledger, its recorded caps, and Google's own audit log and alerts — not the per-group switch. Against ordinary misuse, where nobody has broken into our database, the switch defaulting off, the visible hold, and any adult being able to stop it are real protections.
- The safer design was assisted admission only. It was not chosen alone because a family that loses its only keyed phone, or whose new member joined by invitation with no admin phone in the loop, would otherwise lose tasks and calendar entirely once those are encrypted. For a child's household that lockout is itself a harm: the calendar is how the adults coordinate the child's week.
- Where interests diverge: a parent wants their new phone to work tomorrow; a child's interest is that the record of their week is not readable by anyone outside the family. The design gives the parent tasks and calendar after a day, never chat or photos, and lets the other parent stop it.
- What the child is told: nothing on a child's page changes. A child member already sees the group's content.
- A group's earlier keys (§3.7, added 14 October 2026). A child sees the group's history like any member: in a family group, the chat before they joined is mostly their own family's, and shutting a child out of it would make them the one member who cannot follow what the group is talking about. The cost runs the other way too: a child's own earlier messages and photos, and photos of them, become readable by anyone who joins later. An adult acknowledges for the child — never the child alone — and the adult adding a child sees the sentence when they do. A child's page already says everyone in the group can see the chat and photos, which stays true.
8. Residual risk we accept
- Against someone who controls our database, automatic delivery exposes up to twenty-five groups' Tier 2 content per day, of the attacker's own choosing — not limited to groups an admin actually opted in, since the switch and the admin check are both database rows this attacker controls — including history, after 24 hours, and they can hide the notices. We find out through Google's audit log and our alerts; we do not prevent it. Anchoring the opt-in decision outside the database (§5.3's named upgrade path) would narrow this to groups an admin actually chose; until then we accept the wider exposure for invited testing and will re-assess before the app is offered more widely.
- Backups taken before a group's move completes can still be opened with our server's older copies for up to 90 days, and longer in our database provider's managed backups, whose retention we have not verified.
- Rows no member's phone can open cannot be moved, so a group whose original phones are all gone may never complete. It is shown as not complete.
- Phones running an older app version lose the ability to read new content in a group on the new arrangement until they update.
- Revoking a delivery cannot recall what was read. It only stops further deliveries and future content.
- Notices are written by the database, so they are a record for members, not evidence against us or anyone controlling the database.
- A malicious member can always leak a key they hold. Nothing here addresses that.
- Legacy groups that have never created a key still create their first key for every member phone they can see, as today. Few groups remain in that state.
- A routine key event is now easy to miss (amended 10 October 2026). A member who never opens the Devices screen will not see that another member shared the key with a new phone, used or saved the recovery phrase, or gave our assistant server its copy. That includes someone who has taken over a member's account and also has the group's recovery phrase: using it is recorded under that member's name in Key history, not shown in the chat. We accept this because those are acts that need a member's account, the record is complete and open to every member, and the cases with no member behind them, and our server handing out a key, are still shown in the chat.
- Until a group's first key change on the updated app, its current key keeps the old rule (added 14 October 2026): a record of a key copy is enough to count as holding it. Every group is asked to change its key once, by the first phone that holds the key and has the update; a group none of whose phones has updated waits. A phone that an account added that way before the change is carried by it. An account can also make that change itself, with a key it chose: every phone that held the key before refuses the result and makes a new key past it. If the account sent those phones keys they can't use, no phone can move the group on; members' phones can't send in that group until we reset its key, which we do only after the account is secured, and the group is told.
- A phone given a key, but not yet opened when the key next changes, is left out of the next one and asks again. A phone admitted by another phone in person is vouched for at once; one given the key through an access request or a setup link vouches for itself as soon as it opens the key, which it normally does while its owner is waiting for it.
- A taken-over account can still get in the way. It can write an unopenable key copy for another member's waiting phone, which blocks that phone from being given the current key until the key next changes; the phone then asks again. It can also write one for a phone of its own, which gives it nothing but is listed in Key history under the member's name as adding a phone, or as using the recovery phrase if one was saved for that key. This denies access or misleads the record; it gives the account no key, and it ends when the account is recovered or removed.
- Our database sees a key's token whenever a phone presents one. The token opens nothing. What it allows — vouching for a phone, making the next key — is no more than someone who controls our database can already do (§5.3).
- A phone trusts the first key it's given on first use. A phone admitted through an approval, a setup link or the recovery phrase has no earlier key to check against. If a taken-over account approves another member's new phone with a key the account chose, that phone trusts it until the group's key next changes, as with any approval, which every member's phone records in Key history. The same holds for a phone that already has an older key but is given a new one by approval, when the key change into it was checked against a key that phone never held.
- A new key made past a refused one goes only to the phones that held the key it is checked against (added 14 October 2026), the last key the refusing phone trusts. Whoever made the refused key knows it, so they could make a phone of their own count as holding it; that phone is not given the new key. The cost is that a phone given the key only while the refused key was current, even by a member in good faith, drops out of the new one and asks again. While that earlier key is still on the old rule (item 10), a phone counted as holding it by a record alone is still carried. A phone checks a key before it encrypts under it, passes it to another phone, or chooses it as the one a new key is checked against, including a key that reached it while it was reading its keys or making a key change (amended 14 October 2026). Before that, a key that arrived after the phone's last check could be used unchecked: encrypted under until the next check, passed on, or chosen, so that the new key went to that key's holders.
Added 14 October 2026, for a group's earlier keys reaching every member (§3.7):
- The stated limit for groups not yet on the new arrangement. Until a group's chat and photos have moved onto a key our servers never receive (§3.3), the copy of its key kept for our assistant server is the whole key. Someone who breached our assistant server, or its access to Google's key service, could compute a key's token from that copy. With an owner's or admin's account as well, they could make the tap; with any member's account, they could make a phone they control count as holding the current key and receive the group's earlier keys. This lasts until the separation is finished, and our privacy policy says so.
- Our servers, acting maliciously, could fake the records phones rely on and receive a group's history, as §5.3's last row already accepts for keys generally.
- The admin's tap is a trust decision, made with the same trust as their admin role. An admin whose phone holds a key someone planted before §5.2's change, and who has opened a stored row with it, would vouch for it.
- An earlier key that no tapping admin's phone holds stays unreadable for members who don't already have it. A later tap, by an admin whose phone does hold it, can still add it.
- Everyone admitted to a group sees its history, including people who join long after a message was written, and children through an adult's acknowledgement. That is the intended behaviour; the group is told once and every joiner before joining. A member who does not want an earlier message seen can delete it.
- Older app versions show the "shared this group's earlier keys" notice as a line in the chat until they update.
- A hold stops sharing until someone acts, and a hold that waits for our support needs us. New members in that group see only what was written since they joined until then.
9. Review trigger
- Any change to what automatic delivery gives out, its conditions, its limits, or where the hold and counts are recorded.
- Our server gains the ability to encrypt content itself (for rows it writes), or any new reader of encrypted columns.
- A new table stores encrypted content: it must be classed Tier 1 or Tier 2 and added to the move.
- Delivery is switched on without the off-database ledger or limits being configured — which the design refuses, so this would mean the design has changed.
- Any change to which key notices the group chat shows.
- The app is offered beyond invited testers.
- A confidential computing environment becomes affordable, which would let key release depend on attested code rather than on our controls.
- Any change to who receives a group's earlier keys, the conditions in §3.7, what releases a hold, or a shared display or profile receiving them.
- Switching sharing on for the whole app before most groups have made the key change in §5.2.
- The separation in §3.3 finishing for every group, which retires §8 item 16.
10. Checklist against exposure draft s 38(2)
| s 38(2) | Requires | Answered in |
|---|---|---|
| (a) | nature, scope, context, flow and purposes | §§1–3 |
| (b) | why collection is strictly necessary | §2 (no new personal information about a child; new rows are identifiers and times that make the protections checkable) |
| (c) | lawful and fair means | §4 |
| (d) | best interests of the child, with reasoning | §7 |
| (e) | how the entity complies with the Code and the Act | §4, §6 |
| (f) | risk of harm to children | §5 |
s 38(3): written before the design is built. s 39: listed on the published register.
History verification (S11), 27 October 2026
HQ PIA-001 Stage 2 addendum §7 asked us to show, on our own test database and with no member's
content, that a member whose phone can't open a group's older content gets it once an eligible
helper taps "Add earlier messages from this phone", a child included once an adult has
acknowledged for them; and to check the earlier-keys question the request card asks
(group_history_held) against who may ask it. The run uses the app's own key code against a local
copy of the database (the test is listed in §11). The group: sharing on, current key version 2 with a holder check, version 1
from before holder checks, held only by the group owner's phone and not yet on the admin's list. An adult
with two phones and a 14-year-old, each given version 2 only.
- The notice (S11-1). Both phones missing version 1 work out "Some older content is on Olive's
phone…" (
needs_tap), naming the one admin whose phone holds it. Passed. - The helper's route (S11-1). The group owner's phone finds version 1 as one it can vouch for, its line names the two members missing it, and the request card offers the group owner the share. Manage group's card lists version 1; the tap adds it and the sending loop boxes a copy to each eligible phone. Passed.
- A child (S11-1). Before any adult acknowledges for the child, the child's phone can't receive
a copy (the database refuses one sent directly), and the child's own acknowledgement is recorded
but doesn't count. The group owner's tap is an adult's acknowledgement for each child in the group (as designed in
§3.7); after it the child's phone receives the copy. The other adult route, an admin or guardian
choosing "Let
see earlier messages", is checked by an existing test (§11). Passed. - The key in hand (S11-1). The adult's remaining phone and the child's phone each verify the copy, unwrap the real version 1 key (compared with the key it was made from), open a message sealed before they had it, and record a receipt, after which every member's card reads done. Passed.
- Who
group_history_heldanswers (S11-2). It should answer only a person who is an active member, about a person's phone. Failed, and fixed. A shared display with a live grant counts as an active member, so it was answered; and a shared display's or a profile's phone row was answered about. Migration20261027194341_group_history_held_persons_onlyadds both conditions. The test failed before the migration and passes after. A profile asking was already refused. {}to anyone else (S11-2). Someone outside the group, a phone outside it, a removed phone and a phone that doesn't exist all get the same empty answer, never an error. Passed.- Removing a phone with a copy waiting (S11-2, the removal change of 20 October). With copies waiting for both of the adult's phones, the member removes one. Its copy is deleted and the other phone's is not; after the removal's key change, the remaining phone still verifies and opens its copy. The removed phone's copy stays gone: it is no longer a recipient, the database refuses a copy sent to it, the owner's next two sending runs pass it by, the request card says it holds nothing, and the phone itself, its sign-in refused, reads no copy and loads no key. Passed.
- What the answer reveals (S11-3).
group_history_heldreturns a list of key numbers, only ones that were asked about: no rows, counts or times. Passed.
One thing we noticed and left: the sending loop decides what a phone is missing from the receipts it records for copies, not from the keys it was given. After a key change, a phone that hasn't opened the group since can be sent a copy of a key it already holds. It's the group's own key going to a phone that has it, so nothing is disclosed.
Still owed on real phones. None of this has been run on a phone yet. Owed:
- iPhone: a member's phone missing an earlier key shows "Some older content is on
's phone…" with the Ask button; the admin's phone shows the line above the chat and Manage group's card; after the tap, the member's older messages open while the app is open (within one sending interval, ten minutes) and Key history changes to "Earlier messages are on this phone." - Android: the same run, with the copy kept in the phone's own key storage across a restart.
- A child's account on a phone: nothing arrives before an adult's tap or acknowledgement, and the older messages open after it.
- Two phones on one account: remove one from Devices while a copy is waiting for both; the phone kept opens the older messages, and the removed one shows nothing new once it reconnects.
Follow-up, outside this check: the request's chat message starts with a raw link in its visible text (HQ settings report, issue 12).
11. Sources
- The implementation design for this assessment:
docs/key_architecture_v2_design.md. - A group's earlier keys reaching every member (§3.7):
docs/group_history_sharing_plan.md, and the decisions it carries,docs/decisions/2026-10-13-group-history-and-key-recovery.md(a), (h)–(j). The database side:supabase/migrations/20261014192505_group_history_for_every_member.sql, checked by__tests__/group_history_rls.test.ts. - The history verification (§10, 27 October 2026):
__tests__/group_history_rls.test.ts, "S11: earlier-message recovery on the local stack", and for an adult's acknowledgement for a child, "a child is told only by an adult"; the fix,supabase/migrations/20261027194341_group_history_held_persons_only.sql. - The plan that decided this work:
docs/privacy_personas_plan.md§8.1, §9.2 (items 1.1–1.5) and §14.1 (decisions D-K1 to D-K4). - The encryption design as built:
docs/encryption_plan.md§§3–5; the assessments it amends: Tier 2 through KMS, Connecting a phone to encrypted group content, Privacy levels. - Current key handling:
lib/crypto/keyring.ts(mintGroupKey,rotateGroupKey,offerServerWrap,admitDevice,recoverWithPhrase),lib/crypto/groupKeys.ts,lib/crypto/envelope.ts,lib/crypto/kmsWrap.ts,lib/privacyLevelActions.ts. - Current sealing:
lib/chatSealing.ts,lib/photoSealing.ts,lib/photoCommentSealing.ts,lib/scheduleSealing.ts,lib/taskSealing.ts,lib/assistantTranscript.ts, and the history jobslib/chatHistoryJob.ts,lib/photoHistoryJob.ts,lib/scheduleHistoryJob.ts,lib/titleHistoryJob.ts. - Our server's side:
agent-server/src/tier2.ts,agent-server/src/kmsWrap.ts,agent-server/src/crypto/envelope.ts,agent-server/checks/tier2.check.ts. - Schema:
supabase/migrations/20260908022653_ws11_key_distribution.sql(key tables, policies, the admission, removal and rotation notices),20260908074957_ws12_photos_sealed.sql,20260908085513_ws13_chat_sealed.sql,20260908220811_ws15_tier2_kms.sql,20260908231344_ws16_privacy_levels.sql,20260918215648_close_default_acl_grant_gap.sql(current table privileges),20260923171931_chat_access_requests.sql,20260924180000_photo_comments_and_chat_photos.sql. - Backup retention:
terraform/app/main.tf(secondary backups). - Google Cloud, Cloud KMS: additional authenticated data — https://cloud.google.com/kms/docs/additional-authenticated-data
- RFC 5869, HMAC-based Extract-and-Expand Key Derivation Function (HKDF) — https://www.rfc-editor.org/rfc/rfc5869
- Children's Online Privacy Code, exposure draft (our copy in the sources directory), ss 10, 38, 39.