Skip to content

Mobayilo - Project Status

Last Updated: 2026-01-29 Current Phase: Phase 5.5 — Caller ID & Company Number Enforcement Branch Policy: Work on development, merge to main when approved.


Current Phase: Phase 5.5 — Caller ID & Company Number Enforcement
State: ✅ Engineering complete, ⚠️ awaiting final verification
Branch: development (no merge to main yet)

  • PRD defined (docs/PRD.md)
  • Setup guidance drafted (docs/SETUP.md)
  • Architecture notes drafted (docs/ARCHITECTURE.md)
  • Market overview drafted (docs/MARKET_OVERVIEW.md)
  • MVP analysis captured (docs/MVP_ANALYSIS_AND_PROPOSAL.md)

MVP = individual + company wallets with:

  • Browser dialer (WebRTC) → PSTN via Twilio
  • Pay-as-you-go credits + ledger
  • Stripe top-ups (min $5 individual, min $100 company)
  • Company seats (magic link), shared numbers allowed, usage tracked per seat
  • Billing: per-started-minute (ceil(duration_seconds / 60))
  • Jobs: Solid Queue

Phase 0: Bootstrap Rails monolith (Target: Day 1)

Section titled “Phase 0: Bootstrap Rails monolith (Target: Day 1)”

Objective: Generate the Rails 8.1 app at repo root and run it locally.

  • Generate Rails app in repo root (--database=postgresql --css=tailwind --javascript=esbuild)
  • Ensure docs/ remains intact and readable
  • Configure database and run migrations
  • Confirm local boot: Rails server starts and serves a home page
  • Document required env keys in docs/SETUP.md

Test checklist (Phase 0)

  • bundle exec rails server boots without errors
  • Home page renders at /

Exit criteria: Rails boots locally and serves pages.


Phase 1: Auth + Accounts (Target: Day 1–2)

Section titled “Phase 1: Auth + Accounts (Target: Day 1–2)”

Objective: Support individuals and company admins (password) + company seats (magic link).

  • Add User authentication for individuals + company admins (Devise)
  • Password reset via email (Devise recoverable)
  • Add Company / Account model (wallet owner)
  • Add company seat login (magic link)
  • Add company allowlist policy (domain-based)
  • Add basic navigation (mobile-first): Dial / History / Credits / Settings

Test checklist (Phase 1)

  • Individual sign up → land on home → bottom nav visible
  • Individual sign out → sign in again successfully
  • Company admin sign up → account created → allowlist set to admin email domain
  • Create seat with allowed domain succeeds; disallowed domain fails
  • Seat magic-link sign-in works (email delivered)
  • Seat auto-provision works for allowed-domain email (no pre-created seat)

Exit criteria: Individual can sign up/login; company admin can create seats; seat can sign in via magic link.


Phase 1.5: Pre-Flight Fixes (Target: Day 2)

Section titled “Phase 1.5: Pre-Flight Fixes (Target: Day 2)”

Objective: Resolve critical architectural gaps before building the ledger and VoIP layers.

  • Add immutable Transaction model with idempotency keys
  • Add cached balance column on accounts (computed from ledger)
  • Add idempotent ledger service for credits/debits
  • Validate accounts always have at least one owner user
  • Prevent orphaned individual/company accounts (app-level guard)
  • Add phony_rails (or equivalent) to normalize E.164 numbers
  • Add PhoneNormalizable concern for future Call model
  • Add rack-attack
  • Throttle: login, magic link, token, call initiation
  • Remove dev-only SMTP CRL bypass (SMTP_DISABLE_CRL_CHECK, VERIFY_NONE) before production

Exit criteria: Ledger is safe for concurrent updates, ownership is enforced, phone normalization is ready, and rate limits are in place.


Phase 2: Wallet + Ledger (Target: Day 2–3)

Section titled “Phase 2: Wallet + Ledger (Target: Day 2–3)”

Objective: Store balances and an auditable transaction log for both individual and company wallets.

  • Balance stored as *_cents integer on wallet owner (Account)
  • Transaction ledger with idempotency keys (avoid double credits/charges)
  • Basic credits page showing current balance + recent transactions
  • Admin-safe “manual adjustment” transaction type

Test checklist (Phase 2)

  • Credits page shows current balance + ledger-backed balance
  • Create admin adjustment (Avo): positive amount increases balance
  • Create admin adjustment (Avo): negative amount decreases balance
  • Transaction appears in credits list with type, status, amount
  • Idempotency check via console does not double-credit

Exit criteria: You can credit/debit balances in dev with a consistent ledger.


Phase 3: “First Call” thin slice (Target: Day 3–4)

Section titled “Phase 3: “First Call” thin slice (Target: Day 3–4)”

Objective: Successfully place an outbound call from the browser to PSTN.

  • Twilio token endpoint: GET /api/calls/token (rate-limited)
  • TwiML endpoint: POST /api/webhooks/twilio/voice
  • Status endpoint: POST /api/webhooks/twilio/status (persist call lifecycle)
  • Twilio webhook signatures validated (RequestValidator)
  • Webhook processing offloaded to background job
  • Dialer page (mobile-first)
  • Stimulus controller integrating Twilio Voice JS (connect/disconnect/status)
  • Server-side number normalization/validation to E.164 (e.g., phony_rails)
  • Persist Call records from Twilio webhooks
  • Attribute calls to account/user/seat from client identity
  • Show recent numbers on the dialer (server-side)

Exit criteria: You can dial a real number, it rings, answers, and hangs up cleanly.


Phase 3.1: Security & Quality Hardening (Target: Day 4)

Section titled “Phase 3.1: Security & Quality Hardening (Target: Day 4)”

Objective: Address code review findings and improve system robustness.

  • Security: Enforce strong parameter permitting in Twilio webhooks
  • Observability: Enhanced error logging for Twilio API calls
  • Quality: Refactored authentication and async logic
  • Testing: Verified webhook security and error handling with integration tests

Phase 3.5: Unified Magic-Link Entry (Target: Day 4)

Section titled “Phase 3.5: Unified Magic-Link Entry (Target: Day 4)”

Objective: Single email-first sign-in flow that routes users/seats automatically.

  • Single sign-in screen with email-only input
  • Existing user → magic link
  • Allowed company domain → auto-provision seat + magic link
  • Unknown domain → create individual account + magic link
  • Navbar/CTA “Get Started” routes to the same entry point
  • Async magic-link delivery (deliver_later) + resend link
  • Trackable sign-in counters for cleanup
  • Daily cleanup job for unconfirmed users (Solid Queue recurring)
  • Mismatch handling for /magic vs /seats links with expired link fallback
  • Add guardrails/observability for domain auto-provisioning (logging, rate limits)

Exit criteria: One entry point handles all sign-ins without account-type selection.


Objective: Refresh the dialer UI with a glass + iPhone-style keypad layout.

  • Glass/glow background shell for /dial
  • iPhone-like dialpad layout with keypad interactions
  • Phonebook and balance panels added (design scaffolding)
  • Mobile app-like shell for Contacts/History/Settings and compact Credits summary
  • Logged-out and logged-in hamburger menus aligned to mobile design
  • Desktop pricing calculator now uses live rates + searchable country picker
  • Public 404 page styled to match Mobayilo brand

Phase 4: Stripe top-ups + auto top-up (Target: Day 4–6)

Section titled “Phase 4: Stripe top-ups + auto top-up (Target: Day 4–6)”

Objective: Users can top up credits and enable auto top-up; balances update reliably.

  • Stripe Checkout session for one-time top-ups
  • Stripe Checkout (setup mode) for saving a card for auto top-up
  • Stripe webhook handler (idempotent) credits wallet balance + stores payment method
  • Enforce minimums:
    • Individual ≥ $5
    • Company ≥ $100
  • Auto top-up settings: enabled, threshold, amount, daily cap
  • Auto top-up triggers off-session charge when balance < threshold
  • Receipt/transaction record created for each top-up

Notes:

  • Credits flow now redirects back to /dial after successful checkout or settings save.
  • Auto top-up values are rounded to whole dollars for entry.

Exit criteria: A real Stripe test payment increases wallet balance and auto top-up can charge and record a top-up.


Phase 5: Bill calls and update balances (Target: Day 5–7)

Section titled “Phase 5: Bill calls and update balances (Target: Day 5–7)”

Objective: Calls produce costs and deduct credits correctly.

  • Persist Call records with Twilio SID and final duration
  • Billing calculation: per-started-minute
  • Deduct credits exactly once on completion (idempotent)
  • Call history page with cost and duration
  • Seat attribution for company calls (who initiated the call)

Notes:

  • Call charge rate uses CALL_RATE_CENTS_PER_MINUTE (default 5) until rate tables land.
  • Static rate table now lives in config/call_rates.yml and is served via /api/rates/:iso
  • Admin sync actions: “Sync Twilio Voice Rates” (Twilio) and “Sync YAML Call Rates” (static table)

Exit criteria: Balance decreases correctly after completed calls and ledger reconciles.


Phase 5.5: Caller ID & Company Number Enforcement (MVP-CRITICAL) (Target: Day 6–7)

Section titled “Phase 5.5: Caller ID & Company Number Enforcement (MVP-CRITICAL) (Target: Day 6–7)”

Objective: Enable compliant outbound identity for individuals and companies, close audit blockers, and prepare for public beta.

  • OTP verification via Twilio Verify (anti-abuse gate, balance ≥ $5)
  • Optional verified Caller ID using Twilio Outgoing Caller ID validation
  • Correct flow implemented:
    • Twilio returns validation_code
    • User enters code on phone keypad (not website)
    • No web-based confirmation endpoint
  • Persist pending verification state
  • Server-side enforcement at call time:
    • Verified Caller ID used when available
    • Fallback to Mobayilo default number otherwise
  • Mandatory Twilio phone number for outbound calls
  • Balance pre-check before provisioning (prevents real-money risk)
  • Monthly number fee charged (idempotent, once per month)
  • Insufficient funds:
    • Number suspended
    • Outbound calls hard-fail
  • Inbound calls explicitly rejected with TwiML message
  • Caller ID always chosen server-side
  • Client cannot spoof callerId
  • Twilio API behavior aligned with SDK constraints (validation_requests.create)
  • Audit blocking issues fully resolved

Exit criteria: Caller ID and company number enforcement works end-to-end; no Twilio resources are purchased without funds; audit verdict cleared.


Phase 6 (MVP+): Marketing & Growth (Target: Day 7+)

Section titled “Phase 6 (MVP+): Marketing & Growth (Target: Day 7+)”

Objective: Capture leads and enable external subscriptions.

  • subscribers table for capturing email interest
  • JSON API POST /api/subscribe for public form submissions
  • Email uniqueness and format validation
  • CORS configuration allowing mobayilo.com requests
  • Admin dashboard (Avo) resource for managing subscribers

Exit criteria: Public marketing site can successfully POST emails to the backend.


Phase 7 (Beta): Access Control (Target: Day 7+)

Section titled “Phase 7 (Beta): Access Control (Target: Day 7+)”

Objective: Gate sign-ups behind a beta waitlist managed by admins.

  • Add status (waiting/invited) to subscribers table
  • Update Subscriber model logic
  • Protect Users::SessionsController with beta gatekeeper
  • “You’re on the list” message for blocked users
  • Admin action (Avo) to “Invite to Beta”
  • Add case-insensitive unique index on subscribers.email

Exit criteria: Only invited emails can complete the sign-up flow.


  • Caller ID verification rewritten to match Twilio’s keypad-entry flow
  • Company number provisioning now gated by available balance (pre-purchase)
  • Server-side caller ID enforcement finalized for individuals and companies
  • Audit blocking issues resolved; ready for public beta verification
  • Mobile: in-phone shell for Contacts/History/Settings + Credits summary view
  • Mobile: hamburger menu polish + sign-in overlay improvements
  • Rates: YAML-backed rates table + API integration (individual vs company seat rates)
  • Admin: Call Rates visible in Avo; added “Sync YAML Call Rates” action
  • Desktop: pricing calculator uses searchable country picker + live rates
  • Auth: /dial now requires login; “Open Dialer” routes to sign-in when logged out
  • Misc: styled 404 page; fixed sign-in page background; CSRF hardening for magic link form

What’s missing before opening Public Beta

Section titled “What’s missing before opening Public Beta”
  1. Payments hardening: verify Stripe webhooks in production, SCA fallback, retry UX.
  2. Rate integrity: finalize rate source of truth (Twilio vs YAML), document update cadence.
  3. Monitoring/alerts: basic error tracking (Sentry) + uptime checks for Twilio webhooks.
  4. Abuse controls: finalize rate limits and add logging for domain auto-provisioning.
  5. Legal/Compliance: finalize Terms/Privacy and add KYC/telephony compliance checks if required.
  6. Support flow: user-facing help/support channel and basic FAQ.

Phase 8 (MVP+): Auto top-up hardening (optional)

Section titled “Phase 8 (MVP+): Auto top-up hardening (optional)”

Objective: Improve reliability, visibility, and safeguards for auto top-up.

  • Notify user on auto top-up failures (email + in-app banner)
  • Add SCA recovery flow for off-session failures
  • Admin dashboard for auto top-up retries and caps

Exit criteria: Auto top-ups are observable with retry + recovery path.


  • Company seat allowlist: domain-based vs explicit email allowlist
  • Number provisioning: company admin self-serve vs manual/admin-only (MVP)
  • Should individual account creation ever be blocked for company-like domains?
  • Do we need email-domain verification before auto-provisioning seats?
  • Stripe Checkout + setup mode for payments and saved cards
  • Subscriber API requires public CORS access? (Yes, implemented)