← All privacy impact assessments

PIA: Privacy levels — a four-step ladder per group, and Device-only failing closed everywhere

Amended 4 October 2026 — the ladder is now two states. Every group that is not Device-only runs its AI at Vertex global (cloud-inference §12). The app shows Standard (stored as australia, global_records_here or global_all, all identical now) and Device-only; the Australia/Global choice, its overseas_processing consent and the name replacement this assessment describes no longer apply. Device-only and its guarantees are unchanged.

Amended 1 November 2026 — Device-only is being retired (§3E). No group can choose it any more. Groups that have it keep it, and every protection it gives, until one move date 7 days after the notice goes live, when they move to Standard. Their members are told first, on the Privacy screen and on Home. From now on, our servers are never given a copy of a key a group used while it was Device-only. Later the same day: a count found no group at Device-only, so there is no notice, no move date and no move. Every group is Standard, and the privacy policy published with this release (version 3.16) says so (§3E).

Amended 27 October 2026 — one exception to Device-only, on a member's own tap (HQ PIA-007 F2). A member may report one message to GroupApp. The report carries that one message as the reporter's phone shows it, in plaintext, even from a Device-only or encrypted group, with who wrote it and when (member-safety-reports-and-blocks.md). Nothing on our side decrypts or fetches anything for it, and no image is ever sent. The privacy policy's Device-only text adds "…except a message a member chooses to report to us" before the feature reaches members.

Status: Built. The four-step ladder has been live since 9 September 2026, and this document was revised against the code the same day. §6 says which rows are structural and which are conventional, and §8 is the residual risk we accept. Two further additions, §3B (a member's own request ceiling) and §3C (choosing the level while creating a group), were built and merged on 30 September 2026 and have not yet reached any group; see the note under each. On 2 October 2026 we changed who widens the level: an owner or admin now changes it alone and the group is told (§3D). Written: 9 September 2026 · Last revised: 1 November 2026 (Device-only retired: no way in, the notice, one move date and the key ceiling, §3E; later the same day, no group was Device-only, so no notice or move, and policy 3.16 says every group is Standard; 2 October: an owner or admin changes the level without a second member confirming it, and the group is told, §3D; 30 September: a third way into the same choice, while a group is being created, §3C, built but not released; a member's own request ceiling, §3B, built but not released; 29 September: a second way into the same choice, §3A, since built; later the same day, the redesigned daily brief's routes and a correction about where today's brief runs) Nothing here is legal advice. Children's Code citations are to the exposure draft; section numbers may move on registration (due 10 Dec 2026).

The one claim to be careful with. A privacy level is a statement about what our servers can read and where a model runs, made by a group about itself and rendered back to it as a table. Every sentence in that table has to be true of the rows it describes on the day it is read. So this document does not say "we cannot read" as a flat claim: at australia the assistant reads titles, times and — inside a request — sealed notes; at device_only a title is sealed only once the history job has reached it; and the dashboard brief is an edge function that runs globally at every level until it is routed through our assistant server (§8). What this document says is narrower and dated, and a test suite pins the app's own copy inside those bounds.

Correction, 29 September 2026. The statements in this document that the dashboard brief runs globally at every level (the paragraph above, the §5 row and the first §8 bullet, and the first §9 trigger) date from before the brief moved onto our assistant server on 9 September 2026 (see the cloud inference assessment §8.1). Since then today's brief has run where the platform's default puts every group-data surface, and it reads the level only to refuse a Device-only group. The planned redesign of the brief, which does follow the Global levels, is in §3A and in our home brief assessment.


1. Purpose and scope

A group chooses, once, how much of its information our servers may read and where the models that read it run. Four states, each wider than the last, in one ladder:

level keys server-readable for AI where models run assistant / brief / allocation / voice
device_only chat, photos, captions, album names, comments, event notes and places, and task and event titles, sealed under the group key; no kms wrap exists for the current key version. Task notes, baby records and plans are not sealed (corrected 30 September 2026) nothing nowhere off, failing closed; the on-device brief and the on-device command interface only
australia (default) chat, photos, captions and the assistant transcript sealed on devices; event notes and place readable via the kms wrap inside a request. The same wrapped group key would open chat and photos; only our code keeps it to event notes and places. Baby records, notes included, are not sealed (corrected 30 September 2026) titles, times, structure; notes and place inside a request Sydney, once the Sydney switch is live; a global provider until then, and the screen says which as today, each behind its consent row
global_records_here as australia as australia the organising domains — calendar, chore, meal planner, pantry, admin, games — on the global provider with names replaced; the baby domain, the baby summary, voice extraction and the supervisor's extraction stay in Sydney as today
global_all as australia as australia every domain global, names replaced, except the supervisor's extraction (it sees the raw sentence and is pinned); where a child is in the group, only a guardian of that child can choose it as today

An owner or admin changes the level in one tap, wider or narrower (since 2 October 2026, §3D; until then a widening took two confirmations). Every applied change writes a system row in the group chat, and every change that touches overseas processing writes a consents row (overseas_processing, subject the group — a grant on landing on a Global step, a withdrawal on leaving them; nothing for a move between the two Australian steps, §4). A widening lapses after twelve months and falls back to australia through the existing sweep_expired_consents. Children inherit the group's level, and at every level no under-16 account talks to a model at all — that refusal already exists for child accounts, and this feature adds no second one.

Out of scope, and covered elsewhere: what the assistant discloses inside a turn at australia (see the cloud inference assessment); the Tier 2 wrap mechanism itself (see the Tier 2 encryption assessment); the child-safe variant (see the child accounts assessment); task notes (a decision on whether they should be sealed too is still pending — only the task title is sealed here).

Why four and not two. An earlier Standard/Locked pair of settings answered "may our servers read it" and nothing about "where does the model that reads it run". We asked ourselves whether Australia could be the default with a plain-language opt-in for overseas models, and whether a parent could choose a Global option that includes the baby domain — both answered yes, on conditions tied to the Sydney switch, and the middle step exists so that a group can take the newer model for its calendar without moving a child's feeds anywhere. There is no custom level: a matrix of toggles is a way to be insecure by accident.

2. Data involved

Kind Whose Children's? Sensitive? Free text?
groups.privacy_level, privacy_level_changed_at, privacy_level_changed_by the group's no no — a closed-list word, a date, an id no
group_privacy_level_proposals and group_privacy_level_approvals — dormant since 2 October 2026 (§3D): nothing opens a proposal, the open ones were deleted, and the rows that remain are the record of widenings applied before then. The approvals stay owner-private the group's no no no
consents row, purpose overseas_processing the group's, given_by the owner or admin who made the change no — a household consent, never a child's own no no
group_tasks.sealed_title, group_schedule_events.sealed_title (+ encrypted, key_version, wrapped_data_key on tasks; events already carry them) the member who wrote the title often — "Ada's paediatrician", "Kit's swimming" are ordinary titles a title can name an appointment; at device_only that is the point of sealing it yes
group_schedule_events.guest_title the host's own words, typed for guests can name a child no more than a title yes, plaintext by design — it is what another group is told
What reaches a global model at the Global steps the group's tasks, events, meals, pantry, games, admin (records-here) or everything (all) at global_all, a baby's counted day and notes at global_all, health-adjacent yes
The placeholder map (Member A → user id, Baby 1 → baby id) derived per request no no no — held in memory for one request, never stored, never logged

Not collected, and could have been: who declined a widening (a decline deletes the row); a history of levels (the ledger row and the chat notice are the record); the placeholder map (it exists for one request); a title's plaintext anywhere server-side once the row is sealed; any per-member "consented on" beyond the person recorded as making the change.

Strict necessity (s 38(2)(b)): nothing new is collected about a child. The level is a group setting, made by one adult owner or admin; the sealed title is the same title, stored in a form the server cannot read. guest_title is necessary because a locked host's own title is ciphertext to its guests, and a party with no name is not an invitation.

3. Flows and locations

Data Device Supabase (Sydney) GCP (australia-southeast1) Model provider Region
The level read from the groups row the app already loads; the screen renders the table from PRIVACY_LEVEL_MATRIX the column; written only by propose_privacy_level and the sweep (a guard trigger refuses a direct client write) read PER REQUEST through the caller's RLS-bound client, in our assistant server, never cached — Sydney
A widening an owner or admin chooses it, after a sheet saying it applies to everyone at once; the chat notice that tells the other members opens the Privacy screen when tapped applied at once by propose_privacy_level (since 2 October 2026; until then a second member had to confirm); the consent row and the chat notice, naming who made the change, in the same transaction — — Sydney
A narrowing one tap by an admin, with a confirmation sheet applied at once by propose_privacy_level; a withdrawn consent row where it leaves the Global steps; the chat notice — — Sydney
Locking (moving INTO device_only) the admin device rotates the group key (rotateGroupKey), then runs the title history job for tasks and events; offerServerWrap is not called for the new version the existing kms wrap is not deleted (group_key_wraps is append-only, service role included); it wraps a version that is no longer current the reader finds no wrap for the current version → every sealed column is "not available", and the route is refused before that anyway — —
A task or event TITLE at device_only sealed on the device under a per-row data key, by the code that seals task and event titles; plaintext column holds the fixed placeholder Encrypted; opened once on the way into the store ciphertext; rows the history job has not reached stay plaintext — — Sydney
An assistant turn at device_only the app hides the surface — buildAgentContext throws PrivacyLevelError before any model call; the voice pairing endpoint, every voice kind, the ceremony facilitator and POST /api/keys/kms-wrap refuse likewise nothing —
The dashboard brief and the allocator at device_only the app renders the deterministic fallback brief / keeps the deterministic allocation the two edge functions read the level through the caller's client and answer { status: 'locked' } before any model call — nothing —
A push at device_only — send-message-notification already carries no text; send-action-notification sends "Something changed in " with no title — — —
A turn at global_records_here — — getProvider(node, text, level): calendar/chore/meal_planner/pantry/admin/games → global; supervisor extraction, baby, insight, insight_summary and the voice extraction → Sydney; the health-vocabulary pin still wins; history filtered to non-baby turns; member and baby names replaced with Member A / Baby 1 in the prompt and restored in the reply and in tool arguments the organising domains' prompts, names replaced global
A turn at global_all — — every domain global except the supervisor's extraction; names replaced every domain's prompt, names replaced global
The Mail Sifter assessment at global_all (added 21 September 2026) — — the insight_summary call goes global as above; a SECOND call, a classifier judgment over the same redacted text and the generated options, goes to TypeSafe (Jev), gated on the same isGlobal and the same name map, skipped for health vocabulary, and answered by the Gemini result whenever it does not answer (see the inbound email assessment, § "Jev") two global providers: Gemini's, and TypeSafe in the United States global
The residency sentence on the screen rendered only when /api/usage reports inferenceProvider: 'vertex'; otherwise the interim sentence described in §8 — defaultProvider() — —

What crosses a boundary and why it is allowed to: at the Global steps, a group's organising records (or, at global_all, everything) cross to a global provider under a consent an owner or admin gave on a screen that named the provider's regions in plain words, with names replaced, and the rest of the group is told in its chat (§3D; before 2 October 2026 it took two adults). Nothing crosses at device_only. Since 21 September 2026 "a global provider" is two at global_all for one surface (the Mail Sifter assessment; TypeSafe, United States, no training on Input); the screen copy now says "our providers' regions", and whether groups that consented under the singular sentence are notified or re-confirmed is the open owner decision recorded in the inbound email assessment §8. Since 28 September 2026 the global provider for the assistant is Google Vertex AI at its global location (Gemini 3.8 Flash), not OpenRouter: the same steps, the same name replacement, one fewer party between the group and the model (our cloud inference assessment §9). guest_title crosses to a guest group by the host's own hand, as the existing mirror carries a title today.

3A. Addition, 29 September 2026: a second way into the same choice

We plan to ask, while a new group is being set up, whether the group should use global AI. The reason for asking is model quality: the newest models are not yet available in Australia, and members who would choose them should not have to find a settings screen to do so. The design is assessed in our Home, widgets and setup assessment §8. This section records what it changes here, which is a new entry point and nothing else. Built on 29 September 2026, as described below: the setup step and the Settings screen share one piece of code for the choice, and it calls the same procedure, propose_privacy_level; nothing else in setup writes the level.

Nothing in §§5–8 is changed by this addition, and the risk it adds — a member widening the level from a setup screen they are moving through quickly — is answered by the nothing-pre-selected design, "Decide later", the step being offered only to the group's owner or admin, and the chat notice that tells everyone else. (When this was written the two-confirmation rule answered it too; §3D removed that rule.)

3B. Addition, 30 September 2026: a member's own request ceiling

Built and merged 30 September 2026; not yet released. It takes effect once our next release ships, and until then no member can set a ceiling and every request runs exactly as this document otherwise describes. The rest of this section describes the addition as built.

The group's level is one choice for everybody in it. This addition lets one member keep their own requests narrower than the group allows, without asking anybody and without changing the group. The rule members will be told is: the group sets the ceiling; you choose within it. A member of a Global group who sets their ceiling to Australia has what they ask processed in Australia, while another member's request about the group's shared lists is still processed where the group allows.

What is stored. One new value on the member's own profile row: no ceiling (the default, and what every existing member has), or Australia. Nothing sets it on a member's behalf, and there is no other value: a ceiling can only narrow, never widen, so there is nothing to choose beyond "Australia". The member writes it themselves under the same rule that lets them edit their own profile, and nobody else can set or clear it. Like the rest of that row, the value is readable by the other members of the member's groups; it says where that member's own requests run, which is a preference of the same kind as their privacy stance, not a secret about them.

How it is applied. Our assistant server reads the member's ceiling on every request, through the member's own signed-in access and beside the group's level, never from anything the phone sends and never from a cache. The level the request runs at is the lower of the two. Every route that reads the group's level applies it: the assistant (typed and spoken, including the automatic Mail Sifter assessment, which runs inside a member's own assistant request), voice pairing and each voice request, the baby summary, the daily brief, task allocation, the older home brief, goal breakdown, ceremonies, task search and the event time suggestion. An automated check fails any route that reads the group's level without the member's ceiling.

When something cannot be read.

What it cannot change. A Device-only group stays Device-only: the ceiling only narrows. It also does not change what other members' requests do. In a Global group, content a member wrote can still be read by a global model when another member asks about the group's shared lists; the ceiling is about where your requests go, and members will be told that in those words.

Shared results. Two results are stored once per group and read by every member: the baby summary and the daily brief. Each now records the route that wrote it. A member whose ceiling keeps them in Australia is not shown a result written on the global route: the baby summary is written again in Australia for them, and the daily brief falls back to the app's own line for that edition rather than replacing the group's copy. The baby summary's short list of time-of-day cues (read from a baby's notes) is reused as input to the Australian call and is never shown; reusing it sends nothing of that member's anywhere.

Order. This is enforced on the server first. No setting, onboarding step or promise about it appears in the app until the enforcement is live and has been checked, so nothing on a screen can describe a protection before it exists.

Residual risk.

3C. Addition, 30 September 2026: choosing the level while creating a group

Built and merged 30 September 2026; not yet released. It takes effect once our next release ships and a creator's app has updated; until then every group is still created exactly as §3 describes today. The rest of this section describes the addition as built.

A person creating a group will be asked two things about the group before it exists: what kind of group it is, and how it should use AI. The second question is a third way into the same four-step choice, under plainer names — Phones only (Device-only), Australia and Global (Global, records stay here). The fourth step, Global, including baby records, is not offered while creating a group, nor on the earlier setup step described in §3A; it is chosen only from the Privacy screen. The kind of group is assessed in our member and group types assessment.

Nothing in §§5–8 changes: who may change the level, the guardian rule, the twelve-month expiry and Device-only failing closed are the same whichever screen the choice was made on. (The two-confirmation rule this paragraph once named was replaced on 2 October 2026, §3D.)

3D. Change, 2 October 2026: an owner or admin changes the level, and the group is told

Built and merged 2 October 2026; not yet released. It takes effect with our next release. The database half reaches every group at that moment, whatever version of the app its members have; the new screens arrive with the app update.

Why. Until now a widening opened a request that a second member had to confirm. In practice that cost a group the feature: in one group the person who created it asked several times, and the other member saw a link in the chat with nothing to confirm, because their phone had an older app that could not draw the confirmation card. A level the group wanted never applied. We decided that the person who already holds the authority to change the group's other privacy-relevant settings — an owner or admin — should be able to change this one too, and that the rest of the group should be told rather than asked.

How it works now.

What did not change. No member who is not an owner or admin, and no child account, can change the level; no app can write the level directly; Global is never applied when a group is created or from a link; narrowing is one tap; every change a person makes is announced; a Global level lapses after twelve months and falls back to Australia; and Device-only fails closed everywhere.

Residual risk, accepted.

Structural or conventional. That only an adult owner or admin changes the level, that the child rule names who acts, that the change, the consent row and the chat notice land together, and that a leftover request can no longer apply anything are all enforced by the database (structural). The sheet's wording and the notice opening the Privacy screen are app code (conventional, test-pinned).

3E. Change, 1 November 2026: Device-only retired (tell, then move)

Amended later on 1 November 2026: no group was Device-only. We counted the groups in production and found none at Device-only. So nothing is told and nothing moves: the move date is never set, so the Privacy screen and Home show no notice, and no group's chat gets a message. What stays is everything that keeps a group out of Device-only and the rule that our servers never get a copy of a key a group used while it was Device-only. The privacy policy published with this release (version 3.16) says every group is Standard, keeps one line of Device-only's history, and no longer offers it as a choice; we skip version 3.15, so members acknowledge one edition. We count again on release day, just before the release and just after its database change. If either count finds a group, the plan below runs as written, with a dated notice, and the policy is corrected.

Built; not yet released. This is the first of several changes. This one stops new Device-only groups, tells the members of existing ones, and closes a gap in how a group's key leaves Device-only. No group's level changes in it. A later change moves the remaining groups to Standard on the move date, and posts a notice in each group's chat when it does. Security's assessment of the whole retirement (HQ addendum 2026-11-01-device-only-retired, conditions R1 to R15) is the record of the decision; this section says what the app does.

Why. We decided to remove Device-only from the app. Its users were few, it kept a second set of features (an assistant on the phone, a brief nobody gets any more) that the rest of the app does not need, and no remaining level gives a group "no AI at all": every Standard group's AI runs on Google's service at its global location. Nothing replaces Device-only. The policy will say plainly that every group uses AI.

What changes now.

What does not change yet. Every server, notification, voice and key check still treats a group that has Device-only as Device-only until it moves. No member is asked for consent and no consent row is written: this is the §3D model (an admin decides, the group is told) with notice before the change and an announcement at it, because here nobody in the group chose the change.

A correction to §7. §7 says that at Device-only "a child in the group has the strongest position this app can offer: nothing they or their parents write about them is readable by our servers". That was not true from the app update released after 17 October 2026, when every group's everyday records became ordinary text in our database at every level, Device-only included (content-encryption-v3.md). What Device-only gave a child until now was that nothing about them reached an AI model, and chat and photos were sealed. After the move, records about a child can be read by the assistant and the daily brief an adult uses, as in every Standard group. Under-16s still have no assistant and no notification text, and 16- and 17-year-olds use AI only after their own yes, so nothing a child does starts a model call.

Residual risk, accepted.

Structural or conventional. That no writer can move a group into Device-only, that the move date is one value no app can write, and that a server copy of a Device-only-era key is refused are enforced by the database (structural). That the phone makes a new key before offering a copy, and that no job moves sealed content onto a server-held key, are app code (conventional, test-pinned), backed by the database refusal. The notice's wording and who sees it are app code (conventional, test-pinned).

4. Legal basis and consents

5. Risks and mitigations

Risk Who is harmed Mitigation Structural or conventional? Residual
A member sets the level wider than the group agreed, or a client PATCHes privacy_level straight to global_all every member; every child in the group enforce_privacy_level_admin_only refuses any write to the three columns unless the app.privacy_level_write arm is set, and only the RPC and the sweep set it; every change is applied inside propose_privacy_level, for an adult owner or admin only, never by a client writing the column structural any one owner or admin can widen, and the group is told afterwards (§3D); that is the design
An owner or admin widens the level without the rest of the group agreeing (§3D) every member only an adult owner or admin can; the chat notice naming who made the change is written in the same transaction; any owner or admin narrows in one tap; the Global consent lapses after twelve months; a leftover confirmation request cannot apply anything structural as to who may change it and the notice being written; conventional as to members reading the chat members learn after the fact, and the notice does not notify a phone
A child's feeds, sleeps, medicines and notes go to a global provider on the say-so of somebody who is not the child's parent the child PRV03: at global_all in a group with a live guardian link, the owner or admin making the change must be a guardian; the screen's child notice says in plain words what crosses at each Global step structural as to the guardian rule (the RPC); conventional as to the notice (copy, test-pinned) a guardian of one child makes the change for a group holding two children of different guardians — see §8
The group locks, but the server still holds a kms wrap and can read sealed notes the group locking rotates the group key; the reader looks up the wrap for the row's version and finds none for the new one; offerServerWrap reads the level and does not call the wrap route; the wrap route refuses device_only structural as to "no wrap for the current version" (the rotation is a real key change; group_key_wraps is append-only so nothing can quietly re-add one without the route, and the route refuses); conventional as to offerServerWrap's read (client code) rows written under the OLD version keep a wrap the server could still open if a route ever read them — and every AI route is refused for a locked group before it reads anything (below). Stated in §8
A locked group's titles are read by the assistant, the brief, the allocator or a push the group; a child named in a title buildAgentContext throws PrivacyLevelError before any model call; the voice pairing endpoint, every voice kind and the ceremony facilitator refuse; the two edge functions answer locked; an automated check fails any server route that does not consult the level conventional, test-pinned (source scans and fakes in npm run check) — the database does not refuse a SELECT to the caller's own client, because the caller is a member a title the history job has not reached is plaintext at rest; the generated tools' select('*') would return the placeholder if a route were not refused first
The dashboard brief runs on a global provider at every level, including australia the group none — it is an edge function with no runtime identity (the same residual the cloud inference assessment records at its §8.1) — accepted, §8: the screen's australia sentence is an interim one until the brief is routed through our assistant server
A name reaches a global provider at a Global step a member; a child replaceNames maps every member and baby name (longest first, so a name that is a substring of another is not split) to Member A / Baby 1 in the intent text, the recent turns and the roster in the prompt; restoreNames un-maps the reply and every string argument of every tool call before a write; the check fails a global-eligible node with no replacement in its path conventional, test-pinned (pure functions with a round-trip test; a source scan) a name typed in a form the map does not hold — a nickname, a misspelling, a possessive — is not replaced; a name inside a title read back from the database by a tool result is replaced only if it matches the roster
A baby-domain turn is routed globally at global_records_here a child SYDNEY_PINNED_NODES still wins in getProvider; filterHistoryByOrigin withholds baby turns from a global step; an automated check asserts no baby node can resolve global at that level conventional, test-pinned an untagged history turn (from before the Sydney switch, or after a restart) is admitted — the same residual the cloud inference assessment already records at its §8.5
The consent lapses and the group keeps running on the global provider the group sweep_expired_consents appends expired and sets the level to australia in one savepoint; a test asserts it structural (the sweep is a cron job in Postgres) a night's delay, at most
A guest group's mirror of a locked host's event shows Encrypted as its name the guests guest_title, typed by the host, propagated into the mirror's title by propagate_event_to_mirrors; the app asks for it when a locked group hosts structural as to propagation (trigger); conventional as to the app asking a host that never types one leaves its guests with the placeholder
The "what we can see" table drifts from what the server does the group (misled) the table is one matrix in the app's own code, rendered verbatim; the routing map in our assistant server is keyed by the same level ids; a copy test scans every cell for overclaims conventional, test-pinned the two are in different packages and agree by test rather than by import — see §8
An under-16 account changes the level the group propose_privacy_level refuses an under-16 caller (PRV05), and approve_privacy_level now applies nothing for anybody; the screen is read-only for a child, using the same "decided for you" treatment used on a child's own account screen structural (RPC) and conventional (screen) —

6. Structural versus conventional

Structural

Conventional, test-pinned

Conventional, not pinned

7. Best interests of the child

8. Residual risk we accept

9. Review trigger

10. Checklist against exposure draft s 38(2) (verified 2026-09-08)

s 38(2) Requires Answered in
(a) the nature, scope, context, flow and purposes of the handling of children's personal information §§1–3
(b) why the collection of children's personal information is strictly necessary to provide the service §2
(c) how the collection is done by lawful and fair means §4
(d) whether the handling is consistent with the best interests of the child, with the reasoning §7
(e) specific information about how the entity complies with this Code and the Act §4 and §6
(f) the risk of harm to, and potential impact on, children — physical, emotional, developmental or material §5

s 38(3): conducted before the feature is made available — this assessment was written before the underlying database change went live. s 39: listed on the public register.

11. Sources