Hone
Electrolysis and laser-hair-removal practice software. Booking, intake, treatment plans, session charting, postcare, client portal, consent + e-sign, and card-on-file. Production domain: https://hone.care.
Built by Saltkiln. Pilot studio: Willow Electrolysis (operator: Chloe).
Status
Reconciled 2026-07-27; migration line updated 2026-08-02. Production migration max 0165
(0163 = intake INSERT boundary and 0162 = intake review transition integrity, both applied
and verified 2026-08-02 and now frozen; repository and hosted migration state match; next
number is 0164. Earlier: 0159 =
signed-clinical-record retirement, 0160 = immutable clinical lineage and 0161 = service
order RPC + widened calendar-colour CHECK, all applied and verified 2026-07-30;
0158 deliberately skipped and will never be applied); last runtime-bearing
application HEAD 96b28d62a5f3b9acd67d00b24c80caebd6a66e5d (PR #478 merge, whole-session
copy), serving hone.care. One live studio with real clients: Willow Electrolysis.
This table is a summary. When it and the canonical documents disagree, the canonical
documents plus the live verifier win. No capability is "live" merely because a table,
migration, component, route or flag exists — read the status words precisely.
Read these in order
Also: release changelog ·
migration-first runbook.
Current posture at a glance
- Deployed, enabled, human acceptance PENDING: the Phase A charting correction (unified
Treatment observations & skin response box, galvanic-intensity retirement, exact
0.733 seconds display, Thermolysis pulse count relabel) and whole-session copy.
- Deployed but never production-exercised: whole-session copy — the provenance ledger holds
0 rows; no real copy has ever been performed.
- Dormant: all Google Calendar sync (Willow is not connected; every sync flag off).
- RETIRED by product decision (2026-07-29): signed / finalized clinical records. Hone does
not offer practitioner-signed snapshots, immutable finalized records, cryptographic
clinical-record hashes as a product feature, or a signed-snapshot correction/amendment
workflow. Treatment sessions are ordinary editable operational records; ordinary audit
trails, actor attribution, timestamps and whole-session-copy provenance are all retained.
Enforced by migration 0159. See
docs/decisions/clinical-finalization-retired.md.
- Held: live manual no-show/late-cancel fees · public-booking card collection · public
practitioner selection and assignment.
- Deferred by product decision (2026-07-27): the direct new-client consultation booking route.
- Next: the deep production / security / code audit (not yet performed against this
baseline). Chloe's acceptance testing is also outstanding but is independent — it does
not block the audit.
| Surface | State |
|---|
| Public booking, new/existing client, next-available | Production |
| Authenticated practitioner app, calendar, charting, postcare | Production |
| Clinical memory: structured tolerance/reaction/caution per treatment block, next-session notes, last-session context at the point of care | Production (PR #190) |
| Clinical record: signed finalization boundary (Phase 1, 0119/#399) + signed corrections/amendments backend (Phase 2, 0120/#400) + amendment-path reliability/observability (#402) | RETIRED by product decision (2026-07-29, migration 0159) — not a Hone capability and not coming back. Both flags are now pinned false by CHECK constraint, EXECUTE on the finalize/correct/amend RPCs is revoked from every runtime role, and no session can enter the finalized/void lifecycle. Treatment sessions stay ordinary editable records. 0119/0120 are kept unreverted so history stays replayable and the one legacy finalized artifact (a controlled non-Willow test studio, 2026-07-11) stays readable and immutable. Reintroduction would need a new product decision, not a flag flip |
| Practitioner signup | Invite-only during the pilot (PR #189). Magic-link login creates an account only for emails with a pending team invitation |
| Client portal (magic-link + session cookie, two-zone UX) | Production |
| Portal messages + replies | Production |
| Consent + e-sign (treatment, photo, card authorization, policy ack) | Production, draft template wording, lawyer review required before live use |
| Card-on-file (Stripe SetupIntent on connected account) | Production — live for approved studios + test mode; live/test card isolation is live |
Owner-run session payment charge (PaymentIntent off-session; canonical session-payment-charge.ts executor / payment_charge_attempts ledger, PR #196) | Production — supervised live for approved studios (Willow + Sam's controlled studio; live charges + webhooks proven). A new studio starts test-mode and is enabled per-studio after supervised onboarding; broad self-serve live payments are not ready |
Who Hone is for
Independent electrologists and small permanent-hair-removal studios. The current pilot is Willow Electrolysis. The product framing: a calendar is the surface; the moat is treatment memory; what was done last time, what worked, what the client tolerated, what needs caution, and what the practitioner should remember before the next visit.
Quick start for developers
git clone https://github.com/SaiSamyukthVemuri/Hone.git
cd Hone
npm ci
cp .env.local.example .env.local # fill in the values
npm run dev # http://localhost:3000
Required commands before opening a PR:
npm run typecheck
npm run lint
npm run build
npm test
npm run check:stripe-gates
git diff --check
Or the shortcut that chains the first five:
npm run ci
GitHub Actions runs the same set on every PR and every push to the default branch (.github/workflows/ci.yml, PR #154), plus a separate db-integration job (PR #220/#221) that applies the FULL migration chain from scratch to a local Supabase Postgres, runs the DB/RLS behavior tests (npm run test:db: cross-studio isolation, audit immutability + triggers, clinical delete posture, double-booking constraint, claim RPCs, exposure owner tier), and runs the generated types drift check (npm run check:db-types), and a separate browser-e2e job (PR #227) that runs the Playwright suite — 44 specs under e2e/ — against a local production build and the full local Supabase stack, plus payment-browser-e2e, mobile-completion-e2e and google-browser-e2e lanes (npm run test:e2e locally). CI does not replace the manual smoke catalogue in docs/12_SMOKE_TESTS.md; browser flows, real Resend / Twilio sends, real Stripe Elements, and real webhook delivery still live there.
Required services
| Service | Used for |
|---|
| Vercel | Hosting (Next.js 15 App Router, Node 24 runtime) |
| Supabase | Postgres + Auth (magic link) + Storage |
| Stripe | Connect Express onboarding, card-on-file (SetupIntent), live owner-run session-payment charging for approved studios, test-mode manual fee charging |
| Resend | Transactional email |
| Twilio | SMS confirmations and reminders (off by default; consent-gated) |
| Upstash Redis | Rate-limit token bucket for public surfaces (optional; fails open) |
| External cron | https://cron-job.org/ or Vercel cron hits /api/cron/* with CRON_SECRET |
Environment variables: see .env.local.example for the full list and docs/10_DEPLOYMENT_AND_ENV.md for production setup.
Documentation map
Start with the canonical production set — see "Read these in order" under
Status above:
| File | Audience | Topic |
|---|
| docs/production/current-state.md | Everyone — read first | Canonical snapshot of what Hone is today |
| docs/production/capability-register.md | Engineers, auditors, operators, investors | Per-capability status matrix with evidence |
| docs/production/known-limitations.md | Reviewers, operators | Verified residual limitations, owners, next gates |
| docs/production/migration-ledger.md | Developers, auditors | Migration facts and applied status |
| docs/00_PRODUCT_OVERVIEW.md | Anyone | What Hone is and is not |
| docs/01_ARCHITECTURE.md | Developers | Next.js + Supabase + Stripe shape |
| docs/02_DOMAIN_MODEL.md | Developers | Studios, clients, appointments, the rest |
| docs/03_SECURITY_AND_PRIVACY.md | Reviewers | Tenant isolation, RLS, token routes, portal session |
|
Warnings
- Live payments are enabled ONLY for approved studios, supervised. Supervised live owner-run session payments are live for approved studios (Willow + Sam's controlled studio) — live Connect onboarding, charges and webhooks proven (live refunds have never been exercised in production —
stripe_refunds = 0 rows); live/test isolation live. Do not enable live payments for a new/unapproved studio outside the supervised onboarding + approval process. Still off/held product-wide: public booking card collection (off), deposits / packages / partial payments (not built), live manual no-show / late-cancel fees (hard-held server-side — only session_payment charges live). Broad self-serve live-payment rollout is not complete. See docs/production/current-state.md for the canonical posture.
- Do not enable auto-charge. No automatic, background, batch, or public-triggered charge path exists. Charging is one manual practitioner click on a
ready attempt. Anything different is a new design that needs review.
- Do not bypass RLS or security review. Every public route, token route, RPC grant, and
SECURITY DEFINER function in this repo was chosen carefully. See docs/03 before changing.
- Do not expose tokenized routes to analytics. PR #142 removed Vercel Analytics from
/portal/verify/[token], /cancel/[token], /reschedule/[token], /manage/[token], /intake/[token], /calendar-feed/[token] structurally. Adding analytics to those subtrees re-leaks the token to a third party.
- Do not claim signatures are legally binding. Consent + e-sign produces an evidence-friendly record. Enforceability under Ontario law depends on lawyer-reviewed wording.
- Do not store raw card data, CVC, or Stripe
client_secret. Stripe holds the card; Hone stores brand, last4, expiry, and Stripe ids.
Documentation maintenance rule
Every PR that changes product behavior, data model, security posture, payment behavior, env config, routes, cron, email/SMS, legal copy, or operational behavior must update the relevant docs in the same PR. The .github/pull_request_template.md checklist enforces this at review time. See docs/15_DOCS_MAINTENANCE.md for the map of "which doc to update for which change."
A PR is not complete if it changes behavior but leaves docs stale.