Cycle tracking, symptom logging, appointment preparation, and invitation-only partner cycle sync. The app opens without an account and saves guest data in the browser on the current device. Sign in to open a separate account workspace and use partner cycle sync.
Cycle settings, plans, and symptom logs stay in the active local or account workspace. Ask Bella conversations remain in the current browser session and are sent to the PhaseTwo Supabase Edge Function only when a message is sent. When a question needs personal context, Bella receives only recent structured symptom entries and deterministic pattern counts, never symptom notes or account identifiers.
You'll need Node.js (v18 or newer) installed. Then, in this folder:
npm install
npm run devOpen the URL it prints (usually http://localhost:5173).
No Supabase setup is needed for local guest use. Guest data remains in that browser and is not automatically merged into an account. To enable sign-in, cross-device sync, and partner invitations:
-
In Supabase, enable Google and email/password under Authentication > Providers. Add your Google OAuth client ID and secret to the Google provider.
-
In Authentication > URL Configuration, set your deployed site URL and add the local and production app URLs to the allowed redirect URLs (for example,
http://localhost:5173andhttps://your-app.example). Google should use the Supabase callback URL shown in the Google provider settings. -
Apply the following SQL migrations in numeric order using the Supabase CLI migration workflow or the Supabase SQL Editor. These scripts start at
0002; this workspace does not include a0001migration. The existing core tables referenced by0002(includingcycle_profiles,tasks,chat_messages, andsubscriptions) must already exist.-
0002_symptoms_endo.sql: Adds endometriosis family-history data to cycle profiles; creates or upgrades symptom logs, nudge events, health-consent, and daily AI-usage tables; adds validation constraints and lookup indexes; enables per-user row-level security; adds the symptom-log updated-at trigger; ensures RLS is enabled on core account tables; and creates the authenticateddelete_my_account()function to remove the signed-in user's data and account. -
0003_auth_sync.sql: Creates account profiles for onboarding anduser_app_statefor synchronized cycle settings, tasks, app settings, and chat state. It enables RLS, limits each account to its own rows, and grants authenticated users the required select/insert/update access. -
0004_symptom_impact_awareness.sql: Adds symptom impact areas and the outside-period indicator, and expands the allowed impact values used by symptom logging. -
0005_bella_partner_invites.sql: Creates invitation records with 14-day expiry and uniqueness rules; restricts invitation-table access to the service role; adds the per-user Bella daily-limit RPC; and adds authenticated RPCs for retrieving a redacted shared cycle profile and leaving a cycle share. -
0006_authenticated_health_data_grants.sql: Grants authenticated users the table privileges needed to read, create, update, and delete their own symptom logs, and to read or save their own health-consent record. RLS policies continue to enforce per-user access; anonymous access is revoked. The migration also asks PostgREST to reload its schema cache. -
0007_partner_invite_code_and_acceptance.sql: Adds the normalizedinvite_codecolumn and unique format constraint, creates theaccept_cycle_invitation(p_code text)security-definer RPC, and removes conflicting authenticated table grants while retaining RLS and service-role access for invite management. -
0010_push_reminders.sql: Web Push subscriptions and the reminder log. -
0011_security_hardening.sql: Security fixes. Partner codes now only work for the invited, email-confirmed account and lock out after 10 wrong guesses per hour; invite emails are rate limited; push endpoints must belong to a real browser push service; community posts and profiles can no longer be deleted by clients, and hidden or removed content is no longer editable. Deploy the edge functions in the same release:accept_cycle_invitationnow returns{ ok: false, error }for wrong codes (instead of raising), which the updated client expects, andpartner-invitesneeds the newpartner_invite_sendstable. Tests:supabase/tests/0011_security_hardening.test.sql.
For an existing project, check its migration history first and apply only migrations that have not already been recorded. Review and back up existing data before applying schema changes;
0002makes several existing symptom log fields required.0005must be applied for the shared-cycle RPC, and0006supplies the authenticated table grants required by the client. -
-
Copy
.env.local.exampleto.env.localand setVITE_SUPABASE_URLandVITE_SUPABASE_PUBLISHABLE_KEYfrom your Supabase project API settings. -
Deploy the
bella-chatandpartner-invitesSupabase Edge Functions. -
Restart the dev server. Use Sign in in the app when you want to open your account workspace; signing in is not required for local tracking.
Only the Supabase anon/publishable key belongs in the frontend environment. Never
put a service-role key or Google client secret in .env.local variables prefixed
with VITE_. OpenRouter credentials must only be Supabase Edge Function secrets.
Configure these secrets for the Supabase project (locally with the Supabase CLI, and again in the deployment platform's Supabase function secrets):
OPENROUTER_API_KEY=
OPENROUTER_MODEL=
BELLA_DAILY_MESSAGE_LIMIT=10
For local Edge Function testing, copy supabase/functions/.env.example to
supabase/functions/.env and fill in the OpenRouter values; run
supabase functions serve bella-chat --env-file supabase/functions/.env.
That local secret file is git-ignored. The frontend .env.local should contain
only the VITE_ Supabase public configuration.
Create an API key at openrouter.ai/keys, then set
the secrets without adding them to any VITE_ variable or checked-in file:
supabase secrets set OPENROUTER_API_KEY=your-secret-key OPENROUTER_MODEL=your-openrouter-model
supabase secrets set BELLA_DAILY_MESSAGE_LIMIT=10
supabase functions deploy bella-chat
supabase functions deploy partner-invitesChoose a model supported by OpenRouter's chat completions API; PhaseTwo does not
select or hard-code a model. The daily message limit defaults to 10 if omitted.
The Ari Knows function (ari-chat) builds its own prompt on the server from the cycle dates the
owner shared with the partner, shares the daily counter with Ask Bella, and optionally uses
OPENROUTER_FALLBACK_MODEL and ARI_DAILY_LIMIT. See docs/ARI_KNOWS.md.
Edge functions only answer browser requests from PhaseTwo's own origins
(phasetwo.space, www.phasetwo.space, phasetwo-omega.vercel.app and localhost). To add
another origin, such as a Vercel preview URL, set a comma-separated secret:
supabase secrets set ALLOWED_ORIGINS=https://phasetwo.space,https://my-preview.vercel.app.
Bella is an educational assistant, not a clinician. It cannot diagnose or rule out endometriosis or another condition, prescribe medication, or recommend changes to prescribed treatment. Its deterministic pattern review describes only recorded data; persistent, severe, worsening, or disruptive symptoms may be worth discussing with a qualified healthcare professional.
If Ask Bella is unavailable, its offline reference recognizes topics such as endometriosis, period pain, heavy bleeding, symptoms, cycle tracking, diagnosis, medication, and appointment preparation. Offline responses are general health education, do not use personal tracking data, and link to authoritative sources.
Each person signs in with their own PhaseTwo account. In Settings, the cycle
owner enters the partner's account email and creates an invitation. The edge
function validates the address, rejects self-invites and duplicate links,
generates a single-use 14-day invite code, and sends it from
PhaseTwo <mail@phasetwo.space>. The owner copies the code and shares it
privately with the invited person.
The invited person opens Settings, enters the code, and authenticates with
that account. The accept_cycle_invitation(p_code text) RPC accepts codes with
or without the dash, any case, and marks the invite as single-use. Accepted
accounts receive read-only cycle dates and length only; symptom logs, notes,
tasks, chat, and other account data remain private. The owner may revoke or
resend the invitation, and either party may disconnect at any time.
Never share an invite code publicly. It is not a bearer token and is not returned by an unrestricted table query.
npm run build
npm run previewnpm run build outputs static files to dist/ that you can host anywhere
(PhaseTwo is deployed on Vercel). Configure the Supabase environment values
and production redirect URL in the hosting provider before deploying.