Invite a person,
not a contact.

The creator names the circle, chooses real people, and records one answer. That voice becomes the reason to open the invite.

Creator01

Start inside Threads

The feature begins where conversations already live.

No onboarding detour.

The existing plus action opens a two-choice sheet. Group thread gets the visual priority; solo capture remains available without competing for attention.

Product note: the user sees the value phrase before any contact permission or setup form.

Make the invite worth opening.

Setup stays utilitarian until the kickoff answer. The designed moment is the preview of what family will actually receive.

Creator02

One field, not a wizard

Name the circle; defer everything else.

Creator03

Contacts are a tool

Search, select, continue. No referral theater.

Creator04

Seed the conversation

The creator commits before asking others to.

Creator05

Preview the payload

Show exactly what the recipient will receive.

Context before commitment.

The recipient sees who invited them, who else is there, and a real voice note before any account friction.

Recipient06

Land in the moment

People, prompt, and voice precede sign-up.

Recipient07

No dead chat

Accept the invite and reply. No role setup in between.

Progress should cause a reply.

Do not expose a mastery dashboard. Surface a timely reason to celebrate someone or practise together, then make the next social action one tap away.

Engagement08

Celebrate, then act

A positive milestone becomes a reaction or a voice challenge—not a passive statistic.

Engagement09

Win together

A short cooperative quest gives everyone one small, voice-first reason to return.

Borrow mechanics, not styling.

The useful references are specific interaction contracts. Generic incentivized referral screens are deliberately excluded.

01

GroupMe · Telegram · WhatsApp

Contact selection is a searchable list with obvious selected state and progress. It does not need to sell the feature.

Open Mobbin results ↗
02

Partiful

The sender previews a designed artifact before sharing. The payload carries context instead of relying on accompanying copy.

Open Partiful ↗
03

Discord invites

The recipient sees the specific group identity before accepting. Group threads makes that context smaller, private, and family-based.

Open join flows ↗
04

Apple Invites

The invite is a destination with recognizable people and activity—not an anonymous utility link.

Open Apple Invites ↗

The flow stays honest.

The onboarding earns the social ask, preserves privacy, and reaches a real conversation as quickly as possible.

Keep

  • Value firstExplain Group thread before asking for contacts.
  • Real payloadThe creator's voice note travels with the invite.
  • Two-sidedDesign the recipient funnel as carefully as creation.
  • Shared participationEveryone can answer, react, and offer support without choosing a role.
  • Actionable progressEvery shared milestone offers a next action: celebrate, challenge, or practise together.

Avoid

  • Referral UINo codes, rewards, or generic “invite friends” pitch.
  • Empty groupNever land the first invitee in a blank thread.
  • Auth wallShow family context before account friction.
  • Public socialNo discovery, follower counts, or stranger graph.
  • Passive mastery dashboardsNo rankings or exposed weakness; progress is shared only when it helps someone act.

Keep the magic. Change the ownership model.

Today, a Lexifari Thread belongs to one user_id, and its participants are private Person records inside that learner’s world. A real family thread needs shared ownership, authenticated membership, and message-level authorization. Build that multiplayer domain deliberately; do not stretch the capture model until its tenancy stops being legible.

A small library beats a loading spinner.

The name should make the group feel personal immediately. Curated topic marks give us consistency and zero-latency creation; bespoke AI artwork stays an optional later action.

Suggest three. Let people choose.

Match the title and selected topic against a versioned taxonomy, then offer three coherent marks. Unknown groups receive a monogram and a deterministic colour. Store the chosen avatar_asset_key; never block creation on generation.

Swahili Study Groupstudy · class · language
Nairobi Explorerstravel · city · adventure
Photo Walkscamera · art · neighbourhood
Family Circlefamily · cousins · home
Chakula Clubfood · recipes · dinner
Football Crewfootball · match · teammates
AI later, never inline. “Create a custom group picture” can run asynchronously after the group exists, with a curated mark visible while generation, moderation, caching, and user approval happen.

Reuse the rails. Add the multiplayer core.

Lexifari already has most of the expensive primitives. The new work is authorization, invitations, shared state, and a thin synchronization contract.

01 · iOS

SwiftUI client

Local contact selection, the existing M4A recorder, thread UI, and system share sheet.

02 · Contract

OpenAPI

Firebase bearer identity and additive generated Swift operations. No hand-rolled HTTP.

03 · Commands

FastAPI

Create draft, post message, publish invite, accept role, react, and correct.

04 · Truth

Postgres

Groups, memberships, opaque invites, prompts, messages, reactions, and participation.

05 · Voice

Cloudflare R2

S3-compatible storage with group-scoped keys and short-lived, membership-checked playback URLs.

06 · Ritual

DBOS + APNs

Daily prompts, respectful nudges, recap generation, and push-driven refresh.

V1 transport: REST writes + cursor reads while the thread is open; APNs when it is closed. No typing indicators, presence service, or WebSocket fleet until the daily ritual proves it needs them.

Online truth. Instant local memory.

The API owns multiplayer truth; SwiftData owns the last useful view of it. A social product may need connectivity to send, but it should not forget the conversation every time it launches.

01 · Canonical

API + Postgres

Membership, ordering, reactions, corrections, edits, and deletions remain authoritative on the server.

02 · Projection

SwiftData cache

Groups and recent message metadata render immediately across launches, then reconcile in the background.

03 · Media

File cache

Voice files and unfinished recordings live on disk, not as database blobs. Signed URLs refresh when needed.

04 · Sync

Cursor + client ID

Monotonic group sequence numbers and optimistic client IDs make refreshes incremental and retries safe.

Offline: read cached history, write drafts, keep voice takes. Online: join, invite, send, resolve fresh media access, and reconcile.

The invite is a state machine.

The backend enforces the product promise: nothing becomes shareable until the creator’s kickoff answer exists.

Draft

Create the private group shell. Selected contact intent remains local.

POST /groups
Idempotency-Key

Seed

Upload the creator’s text or short voice answer with a client message ID.

POST /messages
client_message_id

Publish

Only a seeded group can mint an expiring, revocable opaque invite.

POST /invites
token shown once

Preview

The web landing reveals minimal context: inviter, group, prompt, and seeded note.

GET /invites/{token}
no transcript access

Accept

After Firebase sign-in, the recipient joins once and lands directly in the conversation.

POST /accept
invite token

Converse

Members fetch after a cursor; retries never duplicate a voice note or reaction.

GET ?after_seq=
membership required

Small schema. Strong invariants.

Model the social facts directly. Avoid burying membership, corrections, or invite security inside an untyped metadata blob.

groups

Private group identity, language, lifecycle state, creator, chosen topic mark, and last activity.

group_members

One row per authenticated person with membership status and join time. No participation role required.

group_invites

Hashed token, expiry, revocation, use limit, creator, and accepted membership. Never store the raw token.

group_prompts

The shared daily Swahili question and English gloss, keyed by local group day.

group_messages

Text or audio key, author, prompt, reply target, correction target, sequence, and client idempotency key.

group_reactions

One lightweight emoji reaction per member/message/emoji tuple.

group_milestones

A positive, consented projection such as “getting comfortable.” Raw review evidence and weak-word history remain learner-private.

group_challenges

A short cooperative objective, local deadline, and qualifying contribution rule. Completion is derived per member, never ranked.

daily_participation

First qualifying contribution per member/day. The streak is derived from this evidence, not mutated as a counter.

The bucket changes. The access model changes more.

R2 speaks the same S3 protocol the Railway-hosted API already uses. The real migration is replacing permanent public URLs with durable object keys and explicit authorization.

R2 stores bytes. Lexifari grants access.

Keep the provider S3-compatible, configure the R2 endpoint and auto region, and persist a storage reference instead of a public URL. The API verifies group membership before producing a short-lived playback URL.

Key classespublic/…, users/{id}/…, and groups/{id}/… make the policy legible.
UploadsShort-lived presigned PUT, restricted content type and size, followed by an authenticated finalize command.
PlaybackMember-checked signed GET or authenticated range proxy. No permanent group-media URL enters the database.
MigrationInventory, copy, validate count/size/samples, switch new writes, retain a rollback window, then close legacy public access.
BoundaryRailway continues hosting FastAPI and DBOS. Cloudflare R2 becomes the object store; this is not an API-host migration.

One picker, not two.

The contact screen earns its place only if those selections become meaningful pending invites. Opening a second recipient picker inside WhatsApp would repeat the job.

Local contact intent. Explicit sharing.

Use iOS limited contact access to choose individuals. Keep the address book on-device, create a unique invite packet only for each selected person, and show the exact outgoing message before the user initiates sharing.

Identity realityWhatsApp is phone-number keyed. Lexifari currently uses Firebase Apple/Google identities, where email may be missing or private relay.
PermissionAdd NSContactsUsageDescription only when this path ships; support limited access, denial, and manual sharing.
Data boundaryNever upload the full address book. No “Select all,” silent matching, growth analytics, or reusable contact database.
Channel truthIf a channel cannot address the chosen person directly, use one system share sheet and do not claim Lexifari knows delivery.
Later discoveryServer-side contact matching requires verified phone/email identity, deletion semantics, consent review, and abuse controls. It is not an onboarding shortcut.

Make product thinking phone-native.

These one-off boards are faster and more reviewable than folding every exploration into Studio. Product artifacts deserve a tiny publishing path of their own.

01 · Build

Any static folder

HTML, CSS, images, and lightweight interaction stay portable and framework-free.

/tmp/group-threads/
02 · Publish

One CLI command

A reusable command validates the entry point, uploads the directory, and records a manifest.

stu artifact publish
03 · Serve

Worker + R2

A small Worker resolves clean slugs, content types, cache headers, and optional Access policy.

artifacts.lexifari.com/{slug}
04 · Review

Open anywhere

Phone-ready URLs can be shared in Linear, WhatsApp, Telegram, or a founder review without a local server.

versioned · reversible · indexable
Start smaller than a platform. V1 publishes one immutable directory to a generated slug and returns a URL. Add galleries, comments, expiry, private sharing, and Figma-style review only after the publishing loop proves useful. Open LEX-101 ↗

Prove the two-person loop first.

The valuable proof is not a complete family-plan platform. It is one seeded invite turning into one real reply between two people who know each other.

Reuse now

IdentityFirebase bearer auth and first-party Lexifari users.
VoiceThreadsVoiceRecorder M4A capture and existing playback components.
MediaStorage provider abstraction and upload patterns, with group authorization added.
DeliveryAPNs device registration, notification provider, and thread-routing seam.
Async workDBOS worker for prompts, nudges, and weekly recap generation.

Build deliberately

TenancyGroup membership authorization on every read, write, and media URL.
InvitesOpaque token lifecycle, public preview, universal link, revocation, and abuse limits.
MessagesCursor reads, idempotent writes, replies, corrections, and reactions.
ContactsLimited-access picker, denied state, pending-invite state, and honest channel handoff.
EntitlementsA family seat model later; today’s billing entitlement is per user.
Slice 01 · Activation proof

Seed → invite → first reply

Two authenticated people complete the entire loop with real text and voice.

  • Draft group and kickoff message
  • Expiring invite + web preview
  • One-tap membership acceptance
  • Member-only thread and playback
Slice 02 · Family loop

Invite roster + support

Expand from one invitee to a small known circle without adding public-social machinery.

  • Limited contact selection
  • Pending/joined invite states
  • Voice/text corrections and reactions
  • Positive celebration events
  • Cursor refresh + targeted APNs
Slice 03 · Ritual + growth

Daily reason to return

Only after conversation works, add the compounding ritual and external artifact.

  • Daily shared prompt
  • Derived group streak
  • Cooperative word quests
  • Supportive opt-in nudges
  • Weekly WhatsApp recap
Defer family-plan billing until activation is real. The current entitlement belongs to one user. Seat ownership, sponsor transfer, grace periods, and join-without-payment are a separate model—not a wrapper around the onboarding experiment.

Engineer the k-factor.

Internal codename for the virality workstream (a contagion homage). The Bier review verdict is accepted: family circles retain, they do not spread. Spread needs interlocking graphs and a cheaper first taste. Every idea here carries one number it must move.

01 · Family

Closed graph

5-8 people who mostly share the same "other family." k < 1 by design. Measure D30 retention and family-plan conversion, never virality.

02 · Campus

Interlocking graphs

A university Swahili section is 15-40 students who meet weekly and share other classes, clubs, and dorms. The only segment where k > 1 is plausible.

03 · Orgs

Bought distribution

Universities and East-Africa NGOs pay for cohorts outright. No k-factor required; measure seats, activation, and renewals instead.

04 · The wall

Moves later

Broaden the app's existing anonymous usage: real inference before any sign-in. Every segment converts better when the first taste is free.

Take the loop where graphs interlock.

University Swahili courses are the closest thing this product has to Bier's school graph: ages near the invite sweet spot, physical contact several times a week, and sections that overlap through majors, clubs, and housing.

01

The cohort thread

"Swahili 201 crew." One student seeds with a real voice answer; classmates and the instructor join the same conversation without special roles.

02

The graph math

Invite decay favors 18-22. Each member sits in four other classes and two clubs, so invites can jump graphs. A family invite dies at the family boundary; a campus invite does not.

03

The wedge

Every course already has a WhatsApp group. The invite payload (prompt + a classmate's voice) drops into it, and the weekly recap keeps pulling from it all semester.

04

The renewable node

One instructor or student organizer re-seeds a fresh 30-student cohort every semester. Churn at semester's end is matched by re-acquisition at the next enrollment.

Pilot before product. No campus features yet. The existing group loop is the pilot; the only new work is a pitch to one instructor and a roster join link that fits a syllabus. Metrics: invites per creator, acceptance rate, cross-section jumps, D30. If k stays below 1 on a campus, the consumer virality thesis is dead and B2B becomes the play.

Inference before identity.

The app already supports some anonymous usage. Broaden it so the invite page lets a recipient speak before the sign-in wall, and let creators seed without performing.

Reply from the browser. Claim it after.

On the web invite, the recipient records an answer and hears one round of feedback (transcription plus a gentle correction) with no account. Sign-in exists to post the reply into the thread, not to try the product.

Claim on sign-upThe anonymous reply is held against the invite token and attached to the new membership after Firebase auth. Nothing is lost at the wall.
Cost + abuseAnonymous inference is metered per invite token: small rate limit, capped audio length, no retry farming. The token already scopes everything.
Privacy heldAnonymous means unauthenticated, not public. Still no transcript access and no member list beyond the preview.
One-tap seedCreators promote an existing daily answer as the kickoff instead of recording a fresh performance. Accepted fix from the Bier review.
Funnel metricinvite → reply-started (before the wall) and reply → claimed (after it). The gap between the two is the true price of the wall.

Sell cohorts. Keep the app clean.

Universities and development organizations working in East Africa already pay for Swahili training. That is bought distribution and real revenue, and none of its sales machinery may leak into consumer onboarding.

Who buys

UniversitiesDepartment or course licenses. Instructors and students use the same thread; roster join links replace contact picking.
NGOs / dev orgsStaff language training for East-Africa postings. Cohorts formed by the org; the daily ritual fits the workweek.
Language schoolsExisting Swahili programs get a between-lessons practice layer instead of a competitor.
The bridgeAn org-cohort member later creates a family circle; the family circle later converts to a family plan. Segments feed each other.

What it needs

Not product yetA landing page and founder-led sales. Cohort creation is manual behind the scenes on the existing group domain.
LaterSeat billing, org admin, roster import, and reporting. Only after two paying pilots prove the shape.
Bier lensB2B buys the distribution the family graph cannot generate. Campus proves k, orgs pay margin, family retains.
Experiment order. E1 anonymous web reply · E2 one-tap seed · E3 WhatsApp recap promoted to Slice 1 · E4 one campus course pilot · E5 first org pilot. Each experiment reports one k or conversion number before the next one starts.
Expanded Group threads onboarding concept