Skip to content
April 24, 20266 min read

Engineering the OS: What Is Actually Under the Hood

A plain-language walkthrough of the stack: NestJS audit logging, WatermelonDB offline sync, Stripe Connect, and role-based access control.

Written by Bradley Gleave, Founder

I am twenty years old and I am building a SaaS platform alone. That context matters for understanding the technical choices. Everything in this stack has to be something I can maintain, debug, and extend without a team. That rules out complexity for its own sake. But it also means I have had to think carefully about where to spend complexity budget, because some problems in this domain are genuinely hard.

The backend is NestJS. I chose it because it imposes structure without being inflexible — modules, guards, pipes, decorators. When you are the only engineer, guard rails matter. Every endpoint is behind an RBAC guard that resolves the caller's role (OWNER, COACH, CLIENT) against the resource being requested. The role model is not flat: a coach might have access to their own clients' records but not to another coach's, even within the same organisation. That kind of scoped access needs to be right, and it needs to be auditable.

Audit logging is a first-class concern. Every write operation — creating a client record, updating a milestone, processing a payment action — produces an audit log entry with the actor's ID, the resource type, the resource ID, the action, and a timestamp. This is not optional middleware bolted on later; it is a core module that every service calls through. When something goes wrong (and it will), I need to be able to reconstruct exactly what happened and when. This also matters for any future compliance work with data protection regulations.

The mobile client uses WatermelonDB for offline sync. Coaches sometimes run sessions in places without reliable internet. The client-side database keeps a local copy of the data the user has access to, syncs changes when connectivity is available, and handles conflict resolution. WatermelonDB's sync protocol is well-documented and has clear semantics for the cases I care about. The tradeoff is that the sync surface between client and server is more complex than a simple REST layer. I have accepted that tradeoff because the alternative — a coach losing a session note because the connection dropped — is worse.

Payments run through Stripe Connect. Coaches on the platform are connected accounts; TGP acts as the platform account. This means each coach's billing flows through their own Stripe account, with platform fees collected at the application level. It also means I am not holding anyone's money. The refund and dispute handling flows back to Stripe, which is intentional — I do not want to be in the business of adjudicating payment disputes as a young solo operator.

The frontend is Next.js. The design system uses CSS custom properties scoped to tokens: no hardcoded hex values anywhere except the token definitions in globals.css. Typography is handled via Geist, loaded through next/font. The component library is home-built — small primitives (Button, Container, Section, Eyebrow) that compose without fighting each other.

What I am most uncertain about right now is the sync layer performance at scale. WatermelonDB works well in the scenarios I have tested. What happens when a coach has several hundred client records with deep history is a question I will answer with data, not assumptions. The audit logging adds write overhead that I am tracking but have not optimised yet. These are known unknowns, and I would rather be honest about them than paper over them with confident claims.

The honest version of the engineering story is that most of the stack is straightforward, well-documented technology applied carefully to a specific domain. The interesting problems are in the details: the RBAC model, the sync semantics, the audit trail. Those are worth writing about in more depth, and I will.