Skip to content

Mobayilo - Project Status

Last Updated: 2026-02-21 Current Phase: Social OAuth Rollout Phase 2 (X implementation + tests) Branch Policy: Work on development, merge to main when approved.


Current Phase: Social OAuth Rollout Phase 2 (X implementation + tests)
State: 🟡 Google complete; X implementation started on development; Facebook queued after X
Branch: development (merge to main when approved)

  • Social login scope finalized: Google, Facebook, and X (x.com) included in roadmap.
  • Rollout strategy locked: Provider-by-provider delivery; no one-shot implementation.
  • Execution order updated: Google first (done), X second (in progress), Facebook third.
  • Safety policy: Existing passwordless flow remains active; social login will not bypass current account/role boundaries.
  • New implementation plan added: See docs/src/content/docs/project-history/SOCIAL_OAUTH_IMPLEMENTATION_PLAN.md.
  • Google OAuth implemented (Phase 1): Added Devise OmniAuth Google provider, callback controller, and oauth_identities model/table.
  • Boundary enforcement added: Company-seat/claimed-domain and company-account users are routed to magic-link flow in this rollout cycle.
  • Google test pass complete (local): test/integration/google_oauth_sign_in_test.rb and test/integration/beta_access_test.rb green.
  • Provider priority change approved: X.com is being implemented before Facebook.
  • Font GDPR + render path cleanup: Removed Google Fonts CDN import and switched to self-hosted WOFF2 fonts (Cormorant Garamond, Inconsolata, Geist Mono) with font-display: swap.
  • Route-level font preload fix: Added targeted font preloads on Home only to remove unused preload warnings on non-home pages.
  • Third-party script deferral: Moved Simple Analytics and Tally loading out of critical path; now loaded on first interaction, with a 10s fallback for analytics.
  • Critical-chain reduction: Replaced Cloudflare email-obfuscation dependency in hero copy (mail_to -> internal contact link) and removed external transparent texture dependency (local CSS background utility).
  • Image payload reduction: Converted key desktop marketing images to responsive srcset + sizes with explicit dimensions to reduce oversized image transfers.
  • JS bundle optimization: Enabled production minification in pnpm build, added build:dev for watch mode, and split Twilio SDK into a separate entry (twilio_sdk.js) loaded only where dialer is mounted.
  • Main-thread task reduction: Updated country selector controller to lazy-build dropdown items on first open/search instead of building all entries at connect.
  • Animation hygiene: Removed the flagged non-composited transition from the homepage rate calculator call-type toggle.
  • Layout consistency: Switched desktop /rates to shared marketing_desktop_shell layout to match /features and avoid route-specific maintenance drift.
  • How-it-works CTA logic: Removed extra Sign In button and made Start calling now route by auth state (/dial if signed in, otherwise /users/sign_in).
  • Validation: Local CI-equivalent checks passed (bundler-audit, brakeman, rubocop, JS/CSS build, Rails test, Rails system test) before push to origin/development (0441a59).
  • Twilio cancel/hangup stability: Hardened dialer call-state handling to prevent frozen UI when canceling/ending calls on mobile Safari.
  • Dialer controller safety: Added hidden-instance guard + disconnect cleanup so only visible dialer instances initialize and stale device state is torn down cleanly.
  • Hangup fail-safe UX: Added client-side hangup safety timeout to force UI recovery when Twilio disconnect events are delayed.
  • Voice webhook resilience: Added fail-safe rescue in Api::Webhooks::TwilioController#voice to always return valid TwiML instead of surfacing 5xx on unexpected exceptions.
  • Callback URL hardening: Status callback URL now supports APP_BASE_URL and enforces HTTPS in production fallback.
  • Verification: Added/ran controller coverage for fallback TwiML path (test/controllers/api/webhooks/twilio_controller_test.rb).
  • Mobile shell harmonization: Completed light-theme app-like shell across Dialer, Contacts, History, Rates, Settings, and auth touchpoints.
  • Strict mobile frame behavior: Preserved shared/mobile_phone_shell app-like experience for signed-in mobile users while showing full marketing /home for signed-out mobile users.
  • Auth UX in frame: Sign-in + “magic link sent” views now render cleanly in iPhone frame without duplicate headings.
  • Rates UX fixes: Fixed “Back to Dialer” placement/visibility, corrected landline/mobile toggle highlight state, and improved country dropdown contrast/readability.
  • Navigation/copy polish: Header state labels standardized to SIGN IN (logged out) and ONLINE (logged in); fixed mobile sign-out button contrast.
  • Dialer fit tuning: Reduced keypad and display spacing/font footprint so the blue call button remains fully visible in mobile Safari viewport.
  • Routing bug fix: Resolved missing contact_path route key issue in mobile shell/help center flow.
  • Docs deploy pipeline: Fixed Cloudflare Pages deploy workflow by aligning pnpm setup (pnpm/action-setup@v4, pinned pnpm, lockfile install, cache dependency path).
  • Contacts Management: Added full CRUD for Contacts (Add, Edit, Delete) with normalized phone number storage.
  • UI Harmonization (Desktop): Refined Desktop Dialer to match Light Theme (Blue/White), fixed contrast issues, and unified button styles.
  • Mobile Light Theme:
    • Converted Mobile App Experience to a consistent Light Theme (slate-50 background, white cards).
    • Updated Dialer, Contacts, History, Settings, Rates pages on mobile.
    • Replaced dark mode components with clean, modern light alternatives.
  • UX Improvements:
    • Added “Back” links to Contacts/History pages.
    • Improved touch targets and spacing for mobile.
    • Dynamic “Call” button shadow (Blue for light theme).
  • Added Twilio call linkage fields to calls (parent_call_sid, twilio_price, twilio_price_unit) and indexed parent linkage.
  • Persisted ParentCallSid from status callbacks and fetch Twilio Call Resource on terminal status.
  • Added child leg status callbacks on <Dial>/<Number> so PSTN leg events are captured.
  • Fixed statusCallbackEvent format to Twilio-compliant space-separated string.
  • Validated in production: parent/child linkage appears, parent price captured.
  • 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: Optional Caller ID verification for individuals; server-enforced usage only.

  • Account fields for caller ID status + E.164 storage.
  • API endpoints: preview, request, confirm.
  • Country-required national number input with E.164 normalization.
  • Twilio Outgoing Caller ID validation integration.
  • Verification audit events logged.
  • Cooldown (60s) and daily cap (5 attempts) per account.
  • Already-verified Twilio numbers treated as verified.
  • Mobile UI: dedicated Caller ID page + menu link for individual accounts.

Exit criteria: Individual users can verify a personal number and the server selects it as caller ID; unverified numbers are never used.


Phase 8: Admin Call Investigation (Twilio Data + Avo) (Target: Day 8–10)

Section titled “Phase 8: Admin Call Investigation (Twilio Data + Avo) (Target: Day 8–10)”

Objective: Capture full Twilio call-leg data (no paid SDK/Insights) and provide a clean Avo workflow for support investigations.

Phase 8.1 — Schema (Twilio linkage + pricing fields)

  • Add calls.parent_call_sid for leg linking (parent ↔ child).
  • Add calls.twilio_price and calls.twilio_price_unit for Twilio cost.
  • Add index on calls.parent_call_sid.
  • Document why we are not storing paid SDK/Insights data yet.

Phase 8.1 Tests

  • Migration runs and schema loads locally.
  • Index on parent_call_sid exists.

Phase 8.1 Exit criteria: Schema supports parent/child linkage and Twilio price fields. ✅

Phase 8.2 — Data Collection (Webhook + Call Resource fetch)

  • Persist ParentCallSid from Twilio status callbacks into calls.parent_call_sid.
  • On terminal status, enqueue a job to fetch the Twilio Call Resource.
  • Persist price, price_unit, start_time, end_time, duration from Call Resource.
  • Ensure parent/child linkage is preserved when call legs arrive out of order.
  • Child leg callbacks enabled via <Dial>/<Number> status callbacks.

Phase 8.2 Tests

  • Webhook status updates persist parent_call_sid for child legs.
  • Twilio Call Resource fetch populates twilio_price and twilio_price_unit.
  • Completed calls show twilio_price even if the webhook arrives before the fetch job.

Phase 8.2 Exit criteria: Parent/child linkage is captured in production and parent price is stored. Child price may lag and can be refreshed by re-running the fetch job.

Phase 8.3 — Avo UX (Investigation surfaces)

  • Add Call resource with parent/child links and Twilio pricing fields.
  • Add CallerIdEvent resource for audit history.
  • Add CompanyPhoneNumber resource for company caller ID status.
  • Enrich Account with caller ID status, company number, and recent calls.
  • Enrich Transaction with related_call_id and Twilio Call SID from metadata.

Phase 8.3 Tests

  • Avo Call show page links parent and child call legs.
  • Avo Transaction show page links to the related call when present.
  • Avo Account show page surfaces caller ID status and company number.

Phase 8.3 Exit criteria: Support can trace call legs and billing directly in Avo without manual SQL.

Phase 8.4 — Backfill + QA

  • Backfill Twilio pricing for recent calls (batch job or rake task).
  • Validate parent/child linking for recent call pairs.
  • Reconcile Twilio price per leg vs internal ledger charge.

Phase 8.4 Tests

  • Backfill populates twilio_price for at least the last 7 days.
  • Spot-check parent/child link integrity for real customer calls.
  • Verify call leg counts and pricing totals match Twilio console.

Phase 8.4 Exit criteria: Recent call history is populated and Avo investigation workflow is trusted.

Phase 8 Exit criteria: Admin can trace a customer issue from Account → Call (parent/child) → Transaction, with Twilio cost per leg captured and visible in Avo.

Handover Notes (2026-02-08)

  • Production TwiML now returns <Number statusCallback="https://mobayilo.com/api/webhooks/twilio/status" statusCallbackEvent="initiated ringing answered completed" statusCallbackMethod="POST">.
  • Parent and child legs are now visible in production DB with parent_call_sid populated.
  • Parent leg price is captured; child leg price may lag and can be refreshed by rerunning Twilio::FetchCallDetailsJob.
  • If Twilio REST fetch returns 401, check TWILIO_ACCOUNT_SID in runtime env matches the AccountSid in webhook payloads.
  • Phase 8.3/8.4 (Avo UI + backfill) intentionally deferred; next focus is user feedback on UI.

Post-Phase: Beta Audit Remediation (2026-02-03)

Section titled “Post-Phase: Beta Audit Remediation (2026-02-03)”

✅ Senior Engineer Checklist — Beta Audit Remediation

Rule of engagement

No production changes without local validation + CI

No “quick fixes” — every item requires explicit reasoning

If a risk is accepted, it must be documented

🔴 PRIORITY 1 — Must Be Addressed Before Feature Work ☐ 1. Call Balance Guardrail (1-Minute Threshold)

Verification

☐ Trace call initiation flow from request → Twilio call creation.

☐ Identify exact point where balance is checked.

☐ Confirm current logic only blocks when cached_balance_cents <= 0.

☐ Confirm rate resolution happens after call completion.

Risk Analysis

☐ Calculate minimum charge for 1 minute using RateResolver.

☐ Validate scenario: balance > 0 but < 1-minute rate → call allowed.

☐ Quantify worst-case negative balance exposure.

Decision

☐ Define minimum required balance at call start.

☐ Decide behavior when balance is insufficient:

☐ Block call

☐ Allow but cap duration

☐ Allow with overdraft limit

☐ Confirm behavior is race-condition safe.

Pre-merge Validation

☐ Unit tests cover insufficient balance cases.

☐ CI passes.

☐ Manual reasoning documented in PR description.

☐ 2. Webhook Endpoint Throttling (Stripe & Twilio)

Verification

☐ List all webhook routes:

☐ /api/webhooks/stripe

☐ /api/webhooks/twilio/*

☐ Confirm no Rack::Attack throttles apply.

Risk Analysis

☐ Evaluate request cost (DB writes, jobs enqueued).

☐ Consider provider retry behavior on 429s.

☐ Identify safe throttle limits.

Decision

☐ Decide throttle strategy:

☐ IP-based

☐ Endpoint-specific

☐ Provider-aware (Stripe vs Twilio)

☐ Ensure signature verification still runs.

Pre-merge Validation

☐ Throttle rules documented.

☐ CI passes.

☐ No webhook retries unintentionally broken.

🟠 PRIORITY 2 — Security & Financial Integrity ☐ 3. Passwordless Session Expiration & Token Safety

Verification

☐ Identify passwordless gem version.

☐ Confirm defaults for:

☐ Token expiration

☐ Single-use enforcement

☐ Session timeout

☐ Validate against schema (expires_at, timeout_at).

Risk Decision

☐ Are defaults acceptable for beta?

☐ Yes → document acceptance

☐ No → explicit config required

Pre-merge Validation

☐ If changed, tests cover token reuse & expiry.

☐ Security decision recorded in PR.

☐ 4. Stripe Idempotency & DB Constraints

Verification

☐ Confirm current uniqueness:

☐ idempotency_key unique

☐ stripe_payment_intent_id non-unique

☐ Trace all credit creation paths.

Risk Analysis

☐ Identify scenario where a different idempotency key is used.

☐ Assess likelihood of double-credit.

Decision

☐ Decide whether to enforce DB-level uniqueness.

☐ Evaluate migration safety with existing data.

Pre-merge Validation

☐ Migration reviewed for locking risk.

☐ CI passes.

☐ 5. Negative Balance via update_all

Verification

☐ Identify all update_all balance mutations.

☐ Confirm no validation or guardrails are applied.

Risk Analysis

☐ Determine whether negative balances are:

☐ Allowed

☐ Temporarily tolerated

☐ Forbidden

Decision

☐ Define invariant for cached_balance_cents. ☐ Invariant: no negative ledger entries may be persisted.

☐ Ensure solution does not introduce race conditions.

Pre-merge Validation

☐ Tests cover overdraft edge cases.

☐ Decision documented.

🟡 PRIORITY 3 — Operational & Observability ☐ 6. Environment Variable & Log Safety

Verification

☐ Review production logging config.

☐ Confirm no logs include:

☐ Webhook signatures

☐ API keys

☐ Authorization headers

☐ Validate filter_parameter_logging.rb coverage.

Decision

☐ Determine if additional filtering is required.

☐ Or formally accept current risk.

☐ 7. Solid Queue Failure Visibility

Verification

☐ Confirm retry limits (5 attempts).

☐ Identify where discarded jobs surface.

☐ Confirm alerts or dashboards exist.

Decision

☐ Decide if beta needs:

☐ Alerting

☐ Manual review process

☐ Dead-letter handling

🧾 Final Sign-Off (Required)

☐ All Priority 1 items resolved or explicitly accepted

☐ Medium risks documented with rationale

☐ No changes merged without CI green

☐ PR descriptions include risk reasoning, not just code

Priority 3 Verification Notes (2026-02-03)

Section titled “Priority 3 Verification Notes (2026-02-03)”

Scope: Verification + documentation only. No behavior changes unless evidence of leak/blind spot is confirmed.

  • Code review: config/initializers/filter_parameter_logging.rb filters common secrets (:secret, :token, :_key, :otp, etc.). config/environments/production.rb logs to STDOUT at info, with attributes_for_inspect = [:id].
  • Potential gaps: Header-level secrets (Authorization, Stripe-Signature, X-Twilio-Signature) are not filtered by param filters.
  • Verification required in prod: Confirm runtime logs do not include webhook headers/payloads or authorization headers (app + platform logs).
  • Decision: No code changes unless production log inspection confirms leakage.
  • Code review: config/initializers/active_job_logging.rb logs discard.active_job and retry_stopped.active_job to STDOUT.
  • Verification required in prod: Confirm these warnings are retained and monitored; verify alerting or review process exists.
  • Decision: No code changes unless monitoring is absent or discards are not visible.
  • Confirm STRIPE_WEBHOOK_SECRET is present and correct in production.
  • Confirm webhook payloads/headers are not logged in production (app + platform logs).
  • Confirm Solid Queue discarded/retry-stopped logs are visible/retained and tied to alerting or manual review.

Follow-Up Recommendations (If Gaps Confirmed)

Section titled “Follow-Up Recommendations (If Gaps Confirmed)”
  • Add structured logging filter/middleware to redact sensitive headers.
  • Add log-based alert for ActiveJob discarded and retry_stopped messages.
  • Confirm STRIPE_WEBHOOK_SECRET is set in production environment and matches Stripe dashboard.
  • Confirm production logs do not include Stripe-Signature, X-Twilio-Signature, or Authorization headers.
  • Confirm production logs do not include full webhook payloads for Stripe/Twilio.
  • Confirm ActiveJob discarded and retry_stopped messages are visible in log aggregation.
  • Confirm a human review or alerting process exists for discarded/retry-stopped jobs. 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.


Phase 7.5: Docs + CDN + DB Console Hardening (Target: Day 8+)

Section titled “Phase 7.5: Docs + CDN + DB Console Hardening (Target: Day 8+)”

Objective: Ship internal docs safely, serve public assets via CDN, and enable local production console access.

  • Active Storage reads now use R2 public domain (R2_PUBLIC_DOMAIN)
  • R2 cloudflare storage marked public: true for direct CDN URLs
  • Cloudflare Pages docs project created (mobayilo-docs) + custom domain docs.mobayilo.com
  • Docs locked behind Basic Auth (Cloudflare Pages)
  • Docs deploy workflow targets mobayilo-docs project
  • Docs build pinned to Node + pnpm only (docs/.tool-versions) to avoid Ruby install failures
  • Production DB config flattened with cache/queue/cable URL fallbacks for local CLI

Exit criteria: Docs deploy green, docs domain serves correct content, and local rails console can connect via public DB URL.


Phase 7.6: Social OAuth Rollout (Google → X → Facebook) (Target: Day 9+)

Section titled “Phase 7.6: Social OAuth Rollout (Google → X → Facebook) (Target: Day 9+)”

Objective: Add social sign-in safely without breaking existing magic-link authentication.

  • Finalize shared OAuth data model (oauth_identities) and security guardrails.
  • Implement Google OAuth end-to-end for User auth.
  • Complete Google test checklist (callbacks, linking, conflicts, regressions).
  • Validate Google rollout on development with no passwordless regressions.
  • Implement X OAuth after Google checklist (safe fallback first for missing email).
  • Complete X test checklist and regression checks.
  • Implement Facebook OAuth only after X checklist is fully green.
  • Complete Facebook test checklist, including provider conflict behavior.
  • Keep CompanySeat auth on magic-link flow for this rollout cycle.
  • Update docs/support flows after each provider phase completion.

Test checklist (Phase 7.6)

  • Google: linked identity sign-in, verified email linking, conflict handling.
  • Google: existing passwordless flows remain unchanged.
  • X: safe fallback when email is unavailable or unverified.
  • Facebook: parity with Google linking/conflict rules.
  • Full regression pass before enabling each next provider.

Exit criteria: Google, Facebook, and X are delivered in sequence with full test coverage and no regressions to existing auth/session/account behavior.

Reference plan: docs/src/content/docs/project-history/SOCIAL_OAUTH_IMPLEMENTATION_PLAN.md


  • Balance Guardrails: Calls blocked UI-side when balance is empty; redirects to /credits with specific warning.
  • Desktop Content Pages: Added /how-it-works, /features, /pricing, /testimonials pages with shared design system.
  • Dynamic Pricing: /pricing page now fetches real rates from the CallRate database (US, CA, UK, etc.).
  • Smart Navigation: Desktop navbar intelligently switches between anchor links (on Home) and full path links (elsewhere).
  • Code Quality: Fixed Rubocop offenses in controllers; CI passing.
  • 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
  • Infrastructure: R2 public CDN domain for Active Storage reads
  • Docs: new mobayilo-docs Cloudflare Pages project + Basic Auth
  • Ops: production DB config flattened with cache/queue/cable fallbacks for local console

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)