The Laravel Foundation

January 6, 2026 4 min read

The Starting Point

When we started building TimOS, we reached for what we knew: Laravel.
It’s a mature, well-documented framework with excellent authentication support, ORM, and a huge ecosystem.
For most projects, it would be the right choice.

Session 1 focused on scaffolding the foundation:

  • GitHub repository setup with proper branching
  • Laravel 11 application with SQLite for development
  • Database schema with 6 migrations (users, pillars, entries, etc.)
  • 7 Eloquent models with relationships
  • 4 API controllers for core CRUD operations
  • Next.js 15 frontend scaffold

Session 1 Stats:
Time spent: 2.0 hours
Manual estimate: 8.0 hours
ROI: 4.0x

Session 2: Authentication Deep Dive

Session 2 was all about authentication – arguably the most critical part of any SaaS application.
We implemented:

Microsoft OAuth 2.0 Integration

Since TimOS targets executives and founders, Microsoft OAuth was essential. Many use corporate Microsoft accounts,
and we needed to handle:

  • Account picker for users with multiple accounts
  • Work/school account detection and warnings
  • Token refresh handling
  • Profile sync (name, email, avatar)

Multi-Factor Authentication (TOTP)

Security was non-negotiable. We implemented mandatory TOTP-based MFA:

// Laravel TOTP setup (deprecated code, shown for reference)
public function enableMfa(Request $request)
{
    $user = $request->user();
    $google2fa = new Google2FA();

    $secret = $google2fa->generateSecretKey();
    $qrCode = $google2fa->getQRCodeUrl(
        config('app.name'),
        $user->email,
        $secret
    );

    // Store encrypted secret...
}

Foreshadowing:
This TOTP implementation had a critical flaw – the secret was stored in plaintext.
We’d fix this later, but not in Laravel.

Device Trust Management

To reduce MFA friction, we implemented device trust:

  • Device fingerprinting (user agent, IP, etc.)
  • 90-day trust tokens
  • Remote device revocation
  • Audit logging for all auth events

Session 2 Stats:
Time spent: 2.0 hours
Manual estimate: 10.0 hours
ROI: 5.0x

The Growing Concern

By the end of Session 2, we had a working auth system. But a problem was emerging.

TimOS has a core requirement: all user content must be encrypted at rest.
Not just “database-level encryption” – we needed per-user encryption keys so that:

  1. Even with database access, content is unreadable without user keys
  2. Deleting a user’s encryption key destroys all their data (crypto-shredding)
  3. We can honestly tell users “we cannot read your data”

Laravel’s encryption is good, but it uses a single application key. We needed envelope encryption
with Master Encryption Keys (MEK) and per-user Data Encryption Keys (DEK).

The Realization:
Implementing proper envelope encryption in Laravel was possible, but it would fight the framework
at every turn. Eloquent’s attribute casting, query scopes, and relationship loading all assume
unencrypted data. We’d be writing workarounds constantly.

The Decision Point

We had 4 hours invested in Laravel code. The authentication worked. The models were defined.
The migrations were written. Throwing it away felt wasteful.

But we asked ourselves: “Will we regret this choice in 6 months?”

The answer was yes. Every feature we built would require encryption workarounds.
The codebase would become a mess of special cases. And worst of all, we might compromise
on security to make things “easier.”

The Lesson:
Sunk cost fallacy is real. 4 hours of work is not worth 6 months of pain.
When the foundation is wrong, rebuild it. The earlier you pivot, the cheaper it is.

What We Kept

Not everything was lost. From Sessions 1-2, we kept:

  • The data model: Users, Pillars, Entries, Feels, Habits, Recaps – these didn’t change
  • The auth flow: OAuth + TOTP + device trust – we reimplemented this in Node.js
  • The frontend scaffold: Next.js stayed, just pointed at a new API
  • The lessons: Security requirements, UX patterns, and edge cases we’d discovered

The Laravel code was moved to tim_wip/old_php_code/ as reference. It served its purpose –
it taught us what we actually needed.

Next Up: The Big Pivot

In the next post, we’ll dive into Session 3: rebuilding the entire backend in Node.js + TypeScript + Prisma.
5.5 hours of intense work that created the foundation TimOS actually needed.

Key Takeaways

  • Start with what you know, but be ready to change
  • Security requirements should drive architecture decisions
  • 4 hours of throwaway work is better than 6 months of regret
  • The lessons from “failed” code still have value
Secret Link