Two memories. One slow and forever, one fast and forgetful.

Houston Cloud has two databases that work as a team. Postgres is the boring permanent record: workspaces, users, agents, who can talk to whom. Redis is the fast scratchpad: who's online right now, which session is open, where the WebSocket is. Both matter. They do completely different jobs.

Postgres, the long term memory

Postgres is the most boring, most reliable database in existence. Tables, rows, SQL. It's what your bank's bank uses. When data has to be there next Tuesday, it goes in Postgres.

In Houston Cloud, Postgres holds:

None of this changes fast. Permissions get set once and read a thousand times. Postgres is perfect.

Why Supabase

Supabase is "Postgres in a box, plus auth, plus realtime, plus file storage, plus a Node.js library." You get a managed Postgres database and a bunch of helpful extras around it. We already chose Supabase for auth, so we get the database for free (well, for cheap).

The alternative is hosting Postgres ourselves on AWS RDS or Google Cloud SQL. We could. Supabase is faster because we don't have to wire up auth separately. Honest tradeoff: Supabase has ops headroom limits if we grow huge. We can migrate to dedicated Postgres when it actually hurts.

Redis, the short term memory

Redis is a different shape of database. Postgres stores everything on disk. Redis stores everything in RAM. That makes it stupid fast (microseconds per query) and stupid forgetful (if it restarts, things you didn't explicitly save can vanish).

Trade-off: speed for permanence. For some data, that's the right trade.

In Houston Cloud, Redis holds:

Notice the pattern: anything we'd be sad about losing goes in Postgres. Anything we can rebuild from Postgres in a second goes in Redis.

The four tables you actually need to remember

The first version of the schema is roughly four tables in Postgres.

TableWhat's in itHow often it changes
workspacesTenant ID, name, billing plan, K8s namespaceRarely (new customer signs up)
usersUser ID, Supabase ID, email, workspace, roleSometimes (invites, removals)
agentsAgent ID, workspace, name, Knative URL, settingsSometimes (new agent published)
permissionsUser ID + Agent ID + can_use boolSometimes (admin grants/revokes)

Plus an audit log table and a billing-events table. That's it for v1. Grow from there.

How they work together

01
Juan opens Houston
Frontend calls control plane: "give me my agents."
02
Control plane Postgres
Look up Juan's permissions. Returns: [HR, Recruiter]. Caches in Redis for 5 minutes.
03
Juan clicks HR
Frontend opens WebSocket to control plane.
04
Control plane Redis
Lookup HR's Knative URL. Cached. Microseconds.
05
Control plane HR pod
Open WebSocket to HR. Stream the conversation.
06
Postgres Control plane
After the turn ends, write an audit entry: "Juan asked HR a question at 10:42."
Postgres is the source of truth. Redis is the speed layer in front of it.

Why we don't just use Postgres for everything

We could. Postgres can do rate limiting (with row locks), can do pub/sub (with LISTEN/NOTIFY). It would just be slower and more expensive.

Adding Redis is a 30-minute decision that buys us 100x throughput on the hot path. Cheap insurance against scale problems we don't want to debug at 3am.

Where the agent's own memory lives

Important distinction. The agent's own memory (its conversations with the user, its CLAUDE.md, its skills) is not in Postgres. That lives in the agent's pod, on its persistent volume, in the .houston/ directory. Postgres only holds the platform metadata around the agent.

The pod is the agent. Postgres is the registry of which pods exist and who's allowed to talk to them.

Two databases, one rule

Lose Redis: site is slow for a few minutes, no data lost. Lose Postgres: catastrophe, everyone's locked out, payments break, this is what backups are for. Treat them accordingly: Postgres gets daily backups + read replicas + point-in-time recovery. Redis gets a restart script and a shrug.

Concrete services

Postgres via Supabase (managed). Redis via Upstash (managed, serverless, cheap) or a small self-hosted instance on the cluster for v1. Connection pooling via PgBouncer in front of Postgres to keep connection counts sane as the control plane scales.