HALO Knowledge Docs
Generated knowledge spine · research/lanes/01-domains.md

03 · Domain inventory

Per-domain maturity grades and the build-vs-buy line.

Generated from research/lanes/01-domains.md · regenerate with /opt/homebrew/opt/node@24/bin/node site/scripts/generate-docs.mjs

The work was completed. Here is the full deliverable, resent as a single message.

HALO CRM — Domain Map & Capability Inventory

Verified totals (re-run against /Users/shaansisodia/SISO_Workspace/SISO_Agency/clients/halocrm/repo): 79,980 LOC (src + convex, excl. _generated) · 38 pages · 36 convex modules (35 + tsconfig) · 56 schema tables · 43 Supabase importers · 511 any usages · 105 (api as any) casts · 0 test files · 159 console.log.

CLAUDE.md accuracy: unusually high — the Drive taxonomy, markUsed ordering, provisioning idempotency, and auth-naming sections all check out against source. Three drifts: it claims ~70 Supabase importers (actual 43), ~55 tables (actual 56), and lists payroll as Supabase-owned when convex/payroll.ts (330 LOC) exists — but see §2, it has zero frontend consumers.

1 & 2. Domain inventory + maturity

DomainPagesComponentsConvexTables~LOCMaturityEvidence
Shared Drive / content pipelineSharedDrive, CreatorUploadshared-drive/driveClient, googleDrive, driveScan, driveIndex, driveContent, driveStructure, cronsdriveCreatorFolders, driveFiles, driveCreatorStats~2.5kreal-and-workingLive Drive v3 REST + OAuth refresh token; cron reconcile convex/crons.ts:15; idempotent provisioning scheduled not awaited
Gamification / questsTasksRewardsgamification/ (5,886)gamification, gamificationSeed13 gamification* tables~7.6kreal-and-workingLargest single domain; XP/banana ledgers, quest re-roll, shop
Referrals + referral invoicingReferrals, ReferralInvoicingreferrals/, referral-invoicing/referrals, referralInvoicing, referralDemoreferralPartners, referralPayments, referralLinks~3.7kreal-and-workingsrc/pages/Referrals.tsx:61 live Convex; separate referralDemo seeder
Creator invoicingCreatorInvoicing, CreatorInvoicecreator-invoicing/ (4,581)invoicing, currencycreatorInvoicing, currencyRates~5.6kreal-and-workingMulti-currency w/ cached daily rates, server-side key
Payment logPaymentLog (871)payment-log/paymentLogpaymentLogEntries, paymentLogConfirmations, recurringExpenses~2.5kreal-and-workingLargest page component
AI voice cloningAIVoice (788)voice/voice (628)voiceSources, generatedVoiceClones, voiceJobs~1.5kpartial — splitConvex path calls ElevenLabs live; but src/services/voice-clone/synthesize.ts:71 still invokes Supabase edge fn voice-generate
Creator onboardingCreatorOnboarding, CreatorOnboardForm, CreatorOnboardQueueonboarding/ (4,052)onboardingSubmissions, creatorInvitationsonboardingSubmissions, creatorInvitations~4.4kreal-and-workingaccept triggers Drive provisioning
Contracts / e-signContracts, ContractSigningcontracts/contractscontracts~1.3kreal, weak cryptoCanvas src/components/contracts/SignaturePad.tsx, token link flow; token gen is broken (§4)
Customs trackerCustomsTrackercustoms/customscustoms, customStatusHistory~2.3kreal-and-workingsrc/pages/CustomsTracker.tsx:21 live Convex
Payroll / attendancePayrollpayroll/ (4,263)payroll (330)attendance, salesTracker~4.7kpartial — orphaned backendconvex/payroll.ts has 0 api.payroll.* consumers; 15 of 18 payroll components still import Supabase
Team / employeesTeam, TeamMemberProfile/Edit/Onboardingteam/, employees/teamMembersteams, teamAssignments, teamMemberRoles~4.3kreal, partial"Activity tracking coming soon" src/pages/TeamMemberProfile.tsx:274
Secure logins (credential vault)SecureLoginssecure-logins/socialMediaLogins, secureAreasocialMediaLogins, secureAreaPasswords~1.5kreal-and-working, insecurePasswords stored plaintext convex/socialMediaLogins.ts:12
Access control / RBACAccessControlPanel, UserManagementadmin/rolePermissions, accessUsersrolePermissions, profiles~2.8kpartial — two systems§4
AuthLogin, Register, Forgot/ResetPassword, Invitationauth/ (129)haloAuth (49)authUsers, profiles~900partial, insecureupdateCredentials/createTeamMember stubbed to toast src/context/SupabaseAuthContext.tsx:229,254
Creator analyticsCreatorAnalytics (62)analytics/~1.4kdemo-seeded-onlysrc/pages/CreatorAnalytics.tsx:21mockEngagementStats["1"], hardcoded to first mock record, no backend at all
MessagingMessages (66)messages/~1.1kstub-or-vestigialsrc/pages/Messages.tsx:22 reads process.env.N8N_WEBHOOK_URLundefined in Vite (needs import.meta.env), so webhookUrl is always '' and every send POSTs to the empty string
DashboardDashboard (55)dashboard/dashboard (42)~400partialThin

Orphaned Convex modules (0 frontend references, verified by loop): payroll, driveScan, googleDrive, driveStructure, driveClient, demoSeed, demoFixes, resetHalo, seed, gamificationSeed, referralDemo, crons. Most are legitimately server-only (crons/seeders/internal actions) — payroll is the one genuine orphan: a complete, well-documented 330-LOC rewrite that nothing calls.

3. Build-vs-buy line — the 10x surface

(b) Commodity — replaceable by OSS, ~40k LOC of the 80k:

CapabilityCurrent costNotes
Auth + session~900 LOC bespokeconvex/haloAuth.ts is 49 lines of homemade crypto. Convex Auth / Better Auth / Auth.js is a drop-in. Highest-value swap.
RBAC / permissions~2.8k, two systemsCASL or Convex-native ctx.auth collapses both
E-sign~1.3kDocumenso / OpenSign are complete OSS e-sign with audit trails, tamper-evidence, PDF certs
Invoicing + multi-currency~5.6kInvoice Ninja / Crater; currency caching is a solved problem
Payroll / attendance~4.7kFrappe HR / OrangeHRM cover attendance, payslips, commission
Credential vault~1.5kVaultwarden / Passbolt. Current impl stores plaintext with a hardcoded fallback — the single strongest buy case
File explorer / Drive UI~830 + Supabase stackFilestash, Nextcloud
PDF generation13 files, jspdf+html2canvasreact-pdf / Gotenberg; html2canvas rasterizes text (unsearchable, poor print)
Spreadsheet import/exportxlsx across ~6 filesFine as a library; consolidate to one adapter
Notifications54 LOC + toastsNovu
Audit log~noneNo audit trail anywhere despite money + credentials. Greenfield: adopt, don't build
Cron/schedulingConvex nativeAlready correct — keep
Analytics dashboard~1.4k mockNothing real exists; adopt Tremor/Metabase rather than build
Messaging~1.1k brokenNovu or plain n8n wiring

(a) Genuinely bespoke — keep and protect (~15k LOC):

  • Drive taxonomy + reconcile loop (convex/driveStructure.ts, convex/driveScan.ts, convex/driveContent.ts) — the creator content pipeline. No OSS equivalent; this is the actual product.
  • Gamification/quest engine (~7.6k) — chatter motivation mechanics specific to the vertical.
  • Referral partner ledger + remittance — agency-specific commission model.
  • Customs tracker — creator-agency-specific order workflow.
  • Creator onboarding → Drive provisioning chain — the integration is the value.

4. Architectural debt / risk register

S1 — The entire Convex backend is unauthenticated. grep -rn "ctx.auth\|getUserIdentity" convex/ returns 0 across all 35 modules; no convex/auth.config.* exists. Every query and mutation is a public internet endpoint. Anyone with the deployment URL can call socialMediaLogins.listByCreatorEmail and receive OnlyFans/Instagram credentials in plaintext, or call payroll/paymentLog mutations. All permission checks are client-side React (src/pages/Payroll.tsx:24, src/utils/permissionUtils.ts:11) and therefore advisory only. This is the top finding — it subsumes every other auth issue.

S2 — Credential vault is plaintext + hardcoded master password. convex/socialMediaLogins.ts:12 returns password: login.password — stored and returned unencrypted. src/hooks/useSecurePasswordManager.ts:43-45: when no hash row exists, verifySecurePassword falls back to password === "HaloVault2026!" — a hardcoded literal in the public bundle. Worse, secureArea.activePasswordHash (convex/secureArea.ts:6) is a public query returning the hash itself, and verification happens client-side (src/hooks/useSecurePasswordManager.ts:47), so the gate is bypassable from devtools regardless.

S3 — Password hashing is unsalted/single-round SHA-256. src/utils/passwordUtils.ts:13 — plain SHA-256, no salt, no iterations, for the vault. convex/haloAuth.ts:28 is marginally better (per-user salt) but still 1 round of SHA-256, GPU-crackable at ~10^10 guesses/sec. No bcrypt/scrypt/argon2. No rate limiting anywhere in haloAuth.login — unlimited credential stuffing. Comparison at convex/haloAuth.ts:29 is non-constant-time.

S4 — Contract signing tokens have far less entropy than they appear. convex/contracts.ts:6-10: 20 random bytes, but each is rendered byte.toString(36).padStart(2,"0"). Values 0–35 produce a 1-char string padded to 2; values ≥36 produce 2 chars unpadded. The encoding is ambiguous and lossy-looking — but the real problem is it's used as the sole bearer credential for /contracts/sign/:token with no expiry check visible. Should be crypto.randomUUID() or hex.

S5 — Session in localStorage, no expiry. src/context/SupabaseAuthContext.tsx:97 writes haloLocalAuthSession plus userRole/userRoles as separate localStorage keys. XSS-readable; role is client-writable, and since the backend never checks (S1), editing localStorage.userRole to "Admin" is a complete privilege escalation.

S6 — Vestigial Auth0 with hardcoded credentials. src/main.tsx:13-14 — real tenant domain and client ID as literal fallbacks. Nothing calls useAuth0. Dead code shipping live credentials.

S7 — Dual-backend split-brain. 43 files import the Supabase client. Concentrated in src/components/payroll (15 files) — where a finished Convex replacement sits unused. src/services/creator (5), src/pages (5). With VITE_DISABLE_SUPABASE=true these fail per-call silently rather than at boot, so the payroll page degrades invisibly.

S8 — Two permission systems. convex/rolePermissions.ts:4 (canonical ladder, 10 roles) vs src/utils/permissionUtils.ts:11. Note CLAUDE.md is now stale heresrc/utils/permissionUtils.ts:46 reads api.rolePermissions.list, i.e. it has already migrated off Supabase. The duplication is now logic-level, not backend-level.

S9 — Type safety defeated at the boundary. 105 (api as any) casts erase Convex's end-to-end typing — its main selling point. 511 total any.

S10 — Zero tests. No framework, no test files, 80k LOC handling payroll, invoicing, and credentials.

S11 — Broken messaging. src/pages/Messages.tsx:22 uses process.env in a Vite client bundle — always undefined.

S12 — 159 console.log in production, including a role-dump at src/pages/Payroll.tsx:33-41.

5. What is genuinely good

  • CLAUDE.md itself. Rare quality: it documents rationale and failure modes, not structure — "provisioning creates duplicates alongside the originals", "which has already happened once, breaking Model Profile's announcements". Preserve verbatim.
  • convex/driveStructure.ts source-of-truth discipline. Deliberately Convex-free (:9-12) so backend and UI import one definition. USED_FOLDER = "USED : POSTED" carries the exact spelling gotcha inline (:16-21). Textbook.
  • markUsed ordering rationale (convex/driveContent.ts) — Drive move before index delete, reasoned from data-loss consequence.
  • Index-holds-unused-only + lastFileAddedAt as a monotonic high-water mark — a genuinely subtle correctness insight (deriving it would make a fully-posted creator look inactive).
  • Alert thresholds computed, never stored (convex/driveIndex.ts) — turns a migration into a constant edit.
  • Scheduled, never-awaited provisioning — a Drive outage can't fail an onboarding. Correct decoupling.
  • convex/currency.ts:5-15 — server-side key with the reason stated, plus per-day caching against quota exhaustion.
  • convex/payroll.ts docblock — explains the week-scoped query design vs the Supabase version's fetch-everything-and-filter. The best-engineered module in the repo; it just needs wiring.
  • RouteErrorBoundary × useRouteMemory interaction — the team noticed that route memory turns a mount-crash into a permanent trap, and made "Back to dashboard" clear the key. Systems-level thinking.
  • vite.config.ts iCloud watcher tuning — pragmatic fix for a real environment problem.

Recommended sequence: (1) Convex ctx.auth on every function — nothing else matters until this lands; (2) replace the vault with Vaultwarden; (3) swap haloAuth for Convex Auth; (4) wire convex/payroll.ts and delete 15 Supabase payroll files; (5) then the commodity swaps in §3.

Canonical source remains research/lanes/01-domains.md. This HTML is a generated projection; edit the source, then run generate-docs.mjs.