Earlier today, we turned sh0 from a deployment platform into a Backend-as-a-Service by adding PostgREST and Logto auth (post #50). But two items still had "coming soon" badges in the services hub: Realtime and Functions. Thales looked at the sidebar and said the obvious thing: "Go ahead and implement the remaining features."
This is the story of how we completed the full BaaS stack -- managed WebSocket messaging, serverless functions, and a pricing restructure -- in the same session, using the same parallel agent pattern that built PostgREST and Auth.
The Architecture Decision: Centrifugo + Deno
Why Centrifugo for Realtime
We needed a real-time messaging server that: - Runs as a single Docker container (no Erlang/Elixir runtime like Supabase Realtime) - Supports WebSocket + SSE out of the box - Has channels, presence, history, and a publish API - Includes a built-in admin dashboard - Uses minimal resources (this sits alongside database servers, storage, mail, auth)
Centrifugo checks every box. Written in Go, 128 MB RAM footprint, battle-tested at scale. One container, four environment variables, done.
Why Deno for Functions
For serverless functions, we needed: - TypeScript/JavaScript native (the language most sh0 users write) - HTTP-native (functions are HTTP handlers, not Lambda-style event processors) - Sandboxed by default (Deno's permission model) - Lightweight container image
The architecture: a Deno container with a mounted volume. A bootstrap _server.ts routes incoming HTTP requests to user function files. Upload hello.ts, invoke it at https://my-fn.sh0.app/hello. No build step, no deployment pipeline -- just files in a volume.
Parallel Agents, Again
The same pattern from PostgREST + Auth: two independent agents, each in an isolated git worktree.
realtime-agent: Migration 047, RealtimeServer model, Docker module (centrifugal/centrifugo:v5), 6 API endpoints, dashboard list + detail pages, i18n in 5 languages.
functions-agent: Migration 048, FunctionServer model, Docker module (denoland/deno:alpine), bootstrap script writer, 6 API endpoints, dashboard list + detail pages, i18n in 5 languages.
Both agents followed the Auth server implementation as their template -- same file structure, same error handling, same naming conventions. The pattern was so well-established by this point that both agents completed in under 20 minutes.
After merge: cargo check pass, cargo clippy -- -D warnings pass, npm run build pass. Four clippy fixes needed (two format! -> .to_string(), two redundant closures, one let_underscore_future).
The Pricing Problem
With the BaaS stack complete, we had a pricing problem. The current tiers:
| Free ($0) | Pro ($19/mo) | Business ($97/mo) |
|---|---|---|
| 1 stack | Unlimited stacks | Unlimited stacks |
| Deploy only | All managed services | + Multi-server, Team, RBAC |
sh0 Pro at $19/mo now included: deployment platform + 5 database engines + S3 storage + email hosting + PostgREST API + authentication + real-time messaging + serverless functions + cloud backups (13 backends) + full monitoring + uptime checks + status pages.
That is Supabase ($25/mo per project) + Vercel + Uptime Kuma + Postal combined. For $19. With unlimited projects.
The Fix: 4 Tiers
Thales had the insight: "Keep everything like that, but remove unlimited stacks from Pro. Create a Scale tier at $49 with unlimited stacks."
| Free ($0) | Pro ($19) | Scale ($49) | Business ($99) |
|---|---|---|---|
| 1 stack | 1 stack | Unlimited | Unlimited |
| Deploy only | Full BaaS | Full BaaS | Full BaaS |
| Local backups | Cloud backups | Cloud backups | Cloud backups |
| Basic monitoring | Full monitoring | Full monitoring | Full monitoring |
| + Multi-server | |||
| + Team + RBAC | |||
| + Auto-scaling |
Pitch: "Deploy free. Build for $19. Scale for $49. Run your team for $99."
The key insight: most indie developers start with one project. $19 gives them the full BaaS stack for that one project. When they grow to multiple projects, $49 is the natural upgrade. Teams and agencies that need multi-server and RBAC pay $99.
What Changed in Code
The pricing update touched more files than expected:
Rust backend:
- LicensePlan enum: added Scale variant
- max_stacks(): Free = 1, Pro = 1, Scale/Business = unlimited
- require_plan(): hierarchy rewritten as a level function (Free=0, Pro=1, Scale=2, Business=3)
Dashboard:
- License page: 4-plan feature matrix with 21 features including the 4 new BaaS rows
- Platform store: added isScaleOrAbove() helper
Website:
- Pricing page: 4 columns, Scale card with $49
- Billing page: 3 purchase cards (Pro/Scale/Business)
- License page: Scale icon (TrendingUp, amber color)
- Both checkout APIs (Stripe + ZeroFee): scale plan validation + pricing
- License key generation: sh0-scl- prefix for Scale keys
- Key detection in activation handler: sh0-scl- -> Scale plan
The Full sh0 BaaS Stack
After this session, sh0's managed services:
| Service | Container | Resources | Auto-Domain |
|---|---|---|---|
| Database Servers (5 engines) | postgres/mysql/mariadb/mongo/redis | 1 GB RAM | {name}.sh0.app |
| File Storage (S3) | minio/minio | 512 MB | {name}-s3.sh0.app |
| Email Hosting | stalwartlabs/stalwart | 1 GB | mail.{domain} |
| PostgREST API | postgrest/postgrest:v12.2.3 | 128 MB | {name}-api.sh0.app |
| Auth (Logto) | logto/logto | 512 MB | {name}-auth.sh0.app |
| Realtime (Centrifugo) | centrifugal/centrifugo:v5 | 128 MB | {name}-realtime.sh0.app |
| Functions (Deno) | denoland/deno:alpine | 256 MB | {name}-fn.sh0.app |
Total memory for a full BaaS stack: ~2.5 GB. Runs comfortably on a $10/mo VPS.
What This Means
sh0 started as "deploy apps from Git." Today it is a self-hosted cloud platform. A single binary that turns any Linux server into a complete Backend-as-a-Service.
The remarkable part is not the feature count. It is the methodology. Build with one AI session, audit with a second, verify with a third. Use parallel agents for independent work. Follow existing patterns religiously so new features are consistent with old ones.
Every BaaS service in sh0 follows the same architecture: SQLite migration, Rust model, Docker module, Axum handlers, Svelte 5 dashboard, i18n in 5 languages. The pattern is so consistent that adding a new managed service is now a 20-minute job for an AI agent.
That is not a product. That is a platform.