Allin — internal
Enter the password to continue.
Allin — Source of truth

Allin — infrastructure & how it all works

The single reference for every account, service, and piece of logic behind Allin — built for the move onto Allin's own Claude profile. It points to where each credential lives; it never contains the credentials themselves.

Allin Well Ltd · iOS (SwiftUI) + Supabase · v2.3 (build 1) uploaded to App Store Connect, not yet submitted · compiled 2026-08-26

🔒 No secrets live in this document. Passwords, API keys, tokens and the service-role key are deliberately absent — this page can be shared, and secrets must not travel in it. Every row below tells you where the real value is stored (Keychain, Supabase project secrets, GitHub Actions secrets, Xcode signing, or your password manager). Treat the password manager as the master list; this is the map, not the keyring.
Overview

What Allin is & the stack at a glance

Allin is an iOS app for nervous-system / somatic "inner work" — a daily emotional check-in matches you to a guided audio "rewire" session, plus a personalised 10-step beliefs plan, an audio library, and a journal.

Client

iOS, SwiftUI. Repo Maelwi/allin-ios → local ~/Documents/allin-gh/allin-ios, branch main (commit aa3bd52). Bundle com.allinwellltd.allin, App Store ID 6754271583, current v2.3 (build 1) — archived, uploaded to App Store Connect 2026-08-26, not yet attached to a version/submitted (that step is Marie/Isabel's, in the ASC UI).

Backend

Supabase project wsxpmcbtcknewpiwudsc (us-east-1). Repo Maelwi/allin-backend, branch main (commit 094630f). Postgres + Auth (Sign in with Apple) + Storage (audio) + 9 live Edge Functions, versions confirmed live 2026-08-26 via supabase functions list: recommend-plan v39, revenuecat-webhook v7, recommend-audio v6, classify-affect v6, classify-crisis v5, delete-user-data v2, export-user-data v3, get-emotional-sessions v2, save-emotional-session v5. The last four are new this cycle — see the pseudonymization card below. Auto-deploys from main via GitHub Actions, per-function path-filtered (2026-08-14 fix).

Growth & lifecycle

Segment SDK → Amplitude + Customer.io (EU) + Meta SDK. RevenueCat/StoreKit for subscriptions. Deep links on Cloudflare Pages (link.join-allin.com).

Access

Accounts & logins

Every service and the account it lives under. Ownership matters for the migration — note the mix of marie@join-allin.com, info@join-allin.com, and Isabel's Apple ID. Confirm the ⚠ rows during the move.

ServiceAccount / loginKey identifiersWhere the secret lives
Apple Developer / App Store ConnectAccount holder isabel.lewren@gmail.comAllin Well Ltd · Team 2L65759584 · App ID 6754271583ASC API key .p8 + Xcode signing on the build Mac
GitHubMaelwi (Marie); collaborator isabellewrenRepos allin-ios, allin-backend (both private)gh CLI auth / SSH keys on the Mac; Actions secrets in the repo
SupabaseOrg "Allin" (ixbtevgkpksybwxjptrd) — moved 2026-08-14 from Marie's personal account, where it sat alongside an unrelated project. Isabel is a full Owner.Ref wsxpmcbtcknewpiwudsc · us-east-1 · ProPersonal Access Token in macOS Keychain (confirmed it still works post-move); service-role key in Supabase secrets
SegmentWorkspace "Join-Allin-Isabel" · info@join-allin.comSource "Allin iOS"Write key in iOS Info.plist + Supabase secret SEGMENT_WRITE_KEY. no erasure API access — see the GDPR wiki
Amplitudemarie@join-allin.comProject 835236 · org join-allin-253655 · NA · Free planAPI key in Segment destination; AMPLITUDE_API_KEY/_SECRET_KEY added as Supabase secrets 2026-08-15 for account-deletion erasure, live-tested
Customer.ioinfo@join-allin.comWorkspace 216147 · Site ID 6f711…1664 · EU regionTrack API key in Segment destination; APNs key for push; CUSTOMERIO_SITE_ID/_TRACK_API_KEY added as Supabase secrets 2026-08-15 for account-deletion erasure, live-tested
Meta / Facebook⚠ confirm Business Manager ownerFB App 2131466234361765Client token in iOS Info.plist
RevenueCat⚠ confirm loginSubscriptions (StoreKit)Public SDK key in iOS; secret key in RC dashboard
Cloudflaremarie@join-allin.comPages: allin-links, allin-priorities, allin-onboarding · domain link.join-allin.comwrangler auth on the Mac; API token in Cloudflare
Google Workspacemarie@ / info@join-allin.comEmail domain + Drive (audio masters, Docs/Sheets)Google account passwords (password manager)
Website (Squarespace)Isabeljoin-allin.comSquarespace login (Isabel)
Claude (this work)Migrating m@umancstudio.com → marie@join-allin.comArtifacts + connectorsAnthropic account login
The logic

How Allin works today

The core loop and the routing that powers it — verified against the current code.

The daily loop

  1. Check in — emotion, situation, intensity + body, and the belief beneath.
  2. Rewire session — matched guided audio, streamed from Supabase Storage.
  3. Reflect — pre/post mood + a note, saved to the journal.
  4. Return — journal, streak & progress (non-punitive).

Check-in routing (deterministic gates + AI)

Intensity is the gate, emotion is the content, intent is the lane. A crisis scan intercepts early → region-aware support screen (no audio). "Express": intensity ≥ 8 and unpleasant → straight to a soothe audio. fixed Both gates confirmed at >= 8 in the current working tree — EmotionalCheckIn.swift:871 (the slider gate) and CheckinRouter.swift:188's separate mustSootheFirst gate. Neither is live yet: both ship in v2.3 (build 1), currently uploaded to App Store Connect but not yet submitted — the live App Store build still routes at >= 7.

Recommender

Corrected 2026-08-16: the critical path is NOT LLM-free — the live app's version-gate header activates the Anthropic/Claude composition path on every plan generation; Postgres full-text search is the fallback when Claude errors or fails validation, not the primary path. Tier 2 pgvector + Tier 3 shift-rating learning = sketched, not built.

10-step beliefs plan

Server-generated from an intake (areas + ratings + 4 free-text fields). Rating = life-area satisfaction (lower → more steps). Stored verbatim as intake_json; each step captures mood + shift_rating + feedback.

Infrastructure

Architecture

iOS app

Key files

Core/AppView (routing), AuthenticationManager (Apple sign-in), SessionManager + SupabaseService (check-ins), BeliefsPlanManager/Service (plan), Onboarding/OnboardingFlow (8 steps), Core/DeepLink, Core/CrisisResources.

SPM

supabase-swift, analytics-swift (Segment), purchases-ios (RevenueCat), facebook-ios-sdk.

Backend / Supabase

Edge functions

recommend-plan (Deno/TS, v33) — JWT-auth; the live app's version-gate header activates a Claude/Anthropic composition path, with deterministic FTS as the fallback only (corrected 2026-08-16 — this card previously said "deterministic + FTS" as the primary path, which was wrong). Plus recommend-audio, classify-affect, classify-crisis, revenuecat-webhook — all live, all now tracked in the repo (found 2026-08-14: they existed live but weren't in git — see the repo/live drift flag below).

Auto-deploy

GitHub Actions on push to main: migrations → db push, audio metadata → re-seed. still an active landmine, re-confirmed 2026-08-26 The functions job still runs the same single hardcoded supabase functions deploy recommend-plan step it always has — a real per-function path-filtered fix exists (commit 53400bd, branch fix/recommend-plan-repo-drift) but was never merged into main, including in today's consolidation. Practically: pushing a change to any other function (e.g. delete-user-data) still only redeploys recommend-plan — the job goes green, giving false confidence, while the function that actually changed silently doesn't redeploy. All 9 live functions were verified live via direct supabase functions list today, but every one except recommend-plan reached production by manual CLI deploy, not CI. This is the same class of bug the "repo/live drift" box below already describes — it just never got fixed itself.

Storage

Bucket of guided-audio mp3s (public-read); only the onboarding video is bundled in the app.

Deep links

Cloudflare Pages allin-links → link.join-allin.com. Serves the AASA for universal links; non-iOS falls back (302) to join-allin.com. Paths: /home /checkin /plan /library /audio/* /journal /progress /profile /settings. Entitlement shipped in v2.2.

Push

APNs entitlement (aps-environment: production) + device token → Customer.io. verify APNs .p8 uploaded to CIO + production push confirmed on a prod build.

⚠ 2026-08-14 — repo/live drift, was a live landmine. Source drift fixed; the CI fix itself is still NOT live — re-confirmed 2026-08-26. The real, correct recommend-plan was live on Supabase (v31), but the allin-backend git repo's main branch still had an old pre-redesign version — nobody had ever committed the real one. The CI deploy step redeployed recommend-plan on any function change, so the next unrelated push would have silently overwritten the correct live recommender with the stale one. Pulled the real source via supabase functions download (the Management API's function-body endpoint returns a binary bundle, not source — learned that the hard way) and synced it plus the 3 other undocumented live functions into the repo — that part is genuinely done. The per-function CI filter meant to close the actual landmine was written the same day (commit 53400bd) but sits on branch fix/recommend-plan-repo-drift, never merged — including in today's consolidation onto main. main's CI still hardcodes one deploy recommend-plan step regardless of which function actually changed. Standing rule this created: never assume the repo reflects what's actually live — verify against the live project before touching or redeploying anything. That rule is exactly why this was caught again today instead of being taken on faith.
Data

Data model

Public schema. Everything that keys on a user cascades on delete, so account deletion fully erases the database (verified). Free-text fields marked special-category are mental-health data.

TableHoldsSensitivity
emotional_sessionsCheck-ins: emotion, body, trigger, belief, memory, desired/new belief, reframe, pre/post mood, reflection_notes. Pseudonymized + encrypted, live 2026-08-25.special-category
beliefs_plans10-step plan + full intake (intake_json), llm_reasoning. Pseudonymized, live.special-category
beliefs_plan_stepsPer-step mood, shift_rating, feedback_text. Pseudonymized, live.special-category
private.user_pseudonymsReal auth.users.id → random pseudonym UUID. Never exposed via PostgREST — the only table that can re-link the tables above to a real identity.special-category (join key)
user_entitlementssubscription_status, RevenueCat id, consent_granted_at (unused)personal
users / auth.usersAccount id + email (Apple)personal
audio_library · app_config · checkin_configContent + config (no personal data)non-personal

RPCs: delete_user() (cascading erase — fixed 2026-08-25 to rely purely on cascade post-Stage-3, no longer does a doomed explicit emotional_sessions delete by user_id), search_audio_library(), get_or_create_pseudonym(), read-only my_pseudonym() / write-capable my_pseudonym_for_write() (split 2026-08-25 to fix a first-insert RLS bug — see below). Index: emotional_sessions_user_ts_idx added 2026-08-16 — this was the busiest table in the schema with no index on user_id; confirmed live via EXPLAIN that the planner now uses it.

Pseudonymization Stages 1–3 + encryption at rest — shipped this cycle

Both were previously flagged as "engineering mostly done, not yet shipped." As of commit 094630f on backend main, both are live and coordinated with iOS commit aa3bd52 (v2.3 build 1). Encryption alone only protects against a stolen backup; pseudonymization is what removes the identity-join risk from a real breach of the database itself — both were shipped together deliberately, not staged separately.

What changed

All three special-category tables had their direct user_id foreign key dropped and replaced with pseudonym (a random UUID, cascade-linked via private.user_pseudonyms). Five plaintext columns (trigger, belief, memory, desired_belief, new_belief) are now AES-256-GCM encrypted at rest (_shared/session-encryption.ts), key held only in the SESSION_ENCRYPTION_KEY edge-function secret, never in Postgres.

New edge functions

save-emotional-session and get-emotional-sessions replace the iOS app's old direct-table Supabase calls — the client no longer knows the real user_id at all for these tables, only the edge function (via the caller's JWT) does. export-user-data updated to resolve pseudonym once via RPC for its queries.

Bugs found & fixed getting here

A STABLE-marked my_pseudonym() cached a stale NULL within a single INSERT statement, failing RLS for every brand-new user's first check-in — split into a non-STABLE read function and a separate get-or-create write function. Separately, the critical one: Swift's SupabaseEmotionalSession.userId / BeliefsPlan.userId were non-optional — since Stage 3 removed user_id from every API response, every check-in save/read and plan generation would have thrown a decode error on live main. Found by an audit agent, not by earlier curl-only testing; fixed by making both fields UUID? and verified via an isolated Swift decode of a real captured API response before merging.

Growth

Analytics & lifecycle pipe

iOS (Segment SDK) + backend edge functions → Segment source "Allin iOS" → fan-out. Verified live in code.

DestinationPurposeState
Amplitude (835236)Product analytics, "Allin — Core" dashboardlive · free plan
Customer.io (216147, EU)Email + push lifecyclepipe live, campaigns not composed/firing
Meta SDKAd attribution / conversionslive (since v1.8)
RevenueCatSubscription eventslive

GDPR The pipe initialises at launch, before consent — see the GDPR plan (Phase 2) for gating it. anonymisation Today the app sends a persistent id AND real email to Segment on sign-in, forwarded to both Amplitude and Customer.io. Customer.io needs the email (it sends mail); Amplitude doesn't. Fix is a one-step Segment Destination Filter, not yet applied — see the GDPR wiki.

erasure Account deletion propagation to processors: Amplitude + Customer.io done and live-tested; Segment blocked — no accessible Public API token in this workspace, a support request to Segment is drafted. Full status in the GDPR wiki.

Push tokens now bypass Segment entirely — Customer.io's own SDK

Root cause found this cycle: Segment's Customer.io destination only reads a push token off context.device.token on the "Application Installed" event, which fires exactly once per device at the very first launch ever. registerForRemoteNotifications() needs a real network round-trip to Apple, which is structurally slower than that one-shot event — so on a fresh install the token is essentially always still unknown when it fires, and there's no second chance. Fix: CustomerIOPushClient.swift (new file) uses Customer.io's own iOS SDK (customerio-ios via SPM, products DataPipelines + MessagingPushAPN) purely for push-token registration — it registers the token directly against Customer.io's API the moment APNs returns it, no dependency on Segment's install event. Segment still owns every identify/track/screen call, unchanged — this client is deliberately narrow so it can't create a second tracking pipeline. Wired into AllinApp.swift via CioAppDelegateWrapper, with identify() called at every site AnalyticsClient.shared.identify is, and reset() added at sign-out/delete-account (a gap found by audit — without it, a second person signing in on the same device would inherit the previous person's push token attribution). Confirmed working end-to-end via the Customer.io dashboard: source connected, profile exists, clean event stream. The one remaining piece (an actual device token showing up) is blocked on notification permission not yet being granted on the test device — not a code issue.

Tracking audit — funnel gaps closed this cycle

Triggered by a full parallel-agent audit of the tracking/analytics/CRM system end to end. New events, all routed through the existing Segment pipe (no second pipeline): sign_up_completed (fires exactly once per real account, distinct from sign_in_succeeded which fires every sign-in — closes a real gap, there was previously no way to see raw signup volume separate from onboarding completion or repeat sign-ins), reflection_completed (mood_shift, had_note, note_length — never raw note text), session_milestone_reached (fires at 1/2/3/10 completed sessions), crisis_support_shown (tagged by source: severity_gate vs checkin_router), audio_session_exited (audio_slug, audio_title, percent_complete — the "how far did they listen before bailing" gap), plus screen() calls added to Dashboard and Settings. All consent-gated — see AnalyticsClient.identify/track/screen below.

Ship

v2.3 (build 1) — everything else in this release

Branch fix/apple-signin-email-scope merged to iOS main 2026-08-26. Named for its original bug but grew to carry the whole GDPR/consent/crisis-safety/tracking cycle above, plus:

Apple Sign-In email capture

Root fix: request.requestedScopes = [.fullName, .email] now set on every signInWithApple() call, and the raw identity-token JWT is passed to Supabase's signInWithIdToken rather than reading credential.email client-side. Apple quirk: the email/name grant locks in permanently at an Apple ID's first-ever authorization with the app — can't be retroactively granted later, so accounts created before this fix (including test accounts) can't demonstrate it without a full "Stop Using Apple ID" reset.

Consent-gated analytics, actually enforced

AnalyticsClient's identify()/track()/screen() now hard-gate on hasAnalyticsConsent — previously, revoking consent mid-session didn't stop tracking because reset() never disarmed the client. Three independent consent toggles (reminders / analytics / advertising) with a Settings → Privacy screen to revisit them; consent is cleared on account deletion specifically (not on plain sign-out, so a returning same-account user keeps their prior consent).

Account deletion hardened

deleteAccount() no longer swallows a failed delete-user-data call and proceeds as if it worked — a failure now surfaces an error and stops, instead of a user believing they'd exercised erasure while their data was still live.

Two crisis-safety race fixes

Same bug class, two call sites: EmotionalCheckIn.swift's CheckinHeader back button, and AppView.swift's outer check-in back-navigation (reachable via assistive-technology activation bypassing a visual-only overlay), could each discard an in-flight true-positive crisis classification before it was checked. Both fixed by reusing existing state flags — no new state, no visual change.

The paper trail

Docs & wikis we've built

Everything produced for Allin, and where it lives. The four wikis also have durable HTML copies in ~/Documents/allin-wikis/ ready to re-publish from the new profile.

🌿
Priority tracker
Weekly priorities, subcategories, comments, links out to everything else. Public: allin-priorities.pages.dev
🔒
GDPR & onboarding plan
24 measures incl. tonight's anonymisation + privacy-policy fixes, live-user impact, approve/reject. Public: allin-gdpr.pages.dev
🗣️
Tone-of-voice review
20 findings, worst first, approve/hold/reject per finding. Public: allin-tone.pages.dev
🛠️
Engineering deep dive
File/function-level reference, known bugs. Public: allin-deepdive.pages.dev
🧭
Onboarding research
6-app teardown + expert consensus + decision log. Public: allin-onboarding.pages.dev
📓
Knowledge base wiki
What Allin is, tone of voice, brand, team, compliance snapshot. Also exported to ~/Downloads/Allin-Knowledge-Base.pdf.
📢
Marketing Claims Register + Regulatory Positioning Draft
Every public claim mapped to a source; wellness-vs-medical-device positioning. Claims register ↗ · Regulatory draft ↗
📜
Privacy Policy & Terms Draft
Corrects two live false statements on join-allin.com. Read the draft ↗
🤝
Experts & Credibility Candidates
Podcast/academic/journalist candidate lists, nothing contacted yet. Open ↗
🔑
Isabel's Setup brief
How Isabel gets GitHub + Supabase + App Store access from her own terminal. Open ↗
📄
Google Doc & Sheet
Priority narrative + sortable sheet, in the m@umancstudio.com Drive (migrate ownership to join-allin.com).
🧠
Claude memory — allin_* files
infrastructure, backend access, strategy, 10-step plan, recommender, metrics. In ~/.claude/…/memory/ (indexed by allin_index.md).
Claude

Connectors & a caveat

What this Claude environment can reach for Allin — and one important gotcha to fix on the new profile.

ConnectorUse for AllinNote
Supabase (MCP)Read/inspect the prod DB, run SQLprivileged — bypasses the redacted views; the GDPR plan restricts this
Google Drive (MCP)Create/read Docs & SheetsUnder m@umancstudio.com today
Amplitude (MCP)—wrong account — authed to the Insights org, cannot see Allin's project. Re-auth to marie@join-allin.com on the new profile.
Cloudflare / wranglerDeploy the public wikis + deep-link redirectsCLI auth under marie@join-allin.com
The move

Migrating to Allin's Claude profile

You're moving this work from m@umancstudio.com to marie@join-allin.com. Here's exactly how each piece travels — and how to "move" a wiki.

How to move a wiki (artifact)

Artifacts are tied to the Claude account that published them — they don't transfer between accounts, and the link stays owned by the old account. So you re-publish, you don't "move":

  1. The HTML for all four wikis is saved in ~/Documents/allin-wikis/ (done).
  2. Sign in to Claude as marie@join-allin.com and open a session with this same folder.
  3. Ask it to publish each file as an artifact → it mints fresh URLs owned by the new account.
  4. Update any cross-links, then retire the old artifact URLs.

Shortcut that needs no migration: the public Cloudflare copies (allin-priorities.pages.dev, allin-onboarding.pages.dev) live on Cloudflare, not Claude — they already work for anyone, on any account. Keep the Cloudflare login and they carry over untouched.

AssetHow it moves
Wikis (artifacts)Re-publish the HTML in ~/Documents/allin-wikis/ from the new profile (above)
Public wiki URLsNo move needed — they're on Cloudflare (marie@join-allin.com)
Google Doc & SheetTransfer ownership from m@umancstudio.com to a join-allin.com account in Drive
Claude memory (allin_*)Local files — copy the memory folder to the machine/profile you'll run the new Claude on
Amplitude MCPRe-authenticate the connector to marie@join-allin.com so it can finally see Allin's project
Code, Supabase, Cloudflare, App StoreNothing to migrate — they're their own accounts; the new Claude just needs the same CLI auth on the machine
Your notes on this page
Saves as you type. Copy or save, then paste back to Claude.