Architecture reference
The Missing Piece in “Backend as a Service”: One User, Not Three Systems
Every modern BaaS platform solves authentication and data storage. What none of them solve is the seam between the two — and that seam is where fragile sync logic, orphaned rows, and drifting security rules usually live.
The problem developers don’t notice until it bites them
Every modern BaaS platform — Supabase, Firebase, Appwrite, PocketBase, Nhost, Convex, AWS Amplify — solves authentication and data storage. What none of them solve is the seam between the two.
In practice, “the user” in these platforms is not one object. It’s at least three:
- Identity — who logged in (managed by the auth service)
- Domain data — the profile, roles, and app-specific data tied to that identity (managed by you, in your database)
- Authorization — the rules that decide what that identity can touch (also built by you, usually in a completely different syntax than your data model)
The platform gives you strong primitives for each piece. It does not give you the glue. That’s still your job — and it’s the part that’s easiest to get subtly wrong (stale profiles, orphaned rows, security rules that drift from your actual schema).
How this plays out platform by platform
| Platform | Identity store | Domain data store | How they’re linked | Who wires it together |
|---|---|---|---|---|
| Supabase | auth.users (internal, not exposed via API) |
public.profiles (a table you create) |
Foreign key on id, populated by a Postgres trigger you write |
You — trigger, RLS policies, and (for custom claims) a Postgres function |
| Firebase | Firebase Auth (UID, email, provider) | Firestore documents (e.g. /users/{uid}) |
Convention only — you create the document after signup | You — sync logic, custom claims, Security Rules |
| Appwrite | Auth “Account” | Database “Collections/Documents” | No native link; community docs and threads consistently point to manually storing the user ID on documents, or using the Relationships feature to connect a separate “users” collection you build yourself | You — relationship attributes, permission strings per collection |
| PocketBase | An “Auth collection” (users by default) |
Other collections (SQLite tables, effectively) | Closer than most: the auth collection is a first-class record type, so a plain relation field can point to it directly |
You still add the relation field yourself, and roles/permissions are DIY via a custom field plus API
access rules — PocketBase’s own docs confirm the default users collection “is not
special” and can be deleted or replaced
|
| Nhost | Hasura-backed auth (auth.users) |
Postgres tables via Hasura | Foreign key + Hasura permission rules, same shape as Supabase | You |
| Convex | Identity via an auth provider (Convex Auth, Clerk, or Auth0) | Convex’s own document database | Convex Auth does store user records in the same database, which narrows the gap somewhat — but roles, permissions, and session-scoped data still need to be modeled and wired by you | You — schema + function-level authorization logic |
| AWS Amplify | Amazon Cognito (User Pools) | AppSync / DynamoDB | Cognito sub referenced from GraphQL types; access control via @auth directives or IAM |
You — schema directives, group-based rules |
The pattern holds across almost the entire category: flexible building blocks, developer does the assembly. That flexibility is genuinely valuable for teams with backend expertise and specific data-modeling needs. It’s also exactly the part that trips up AI coding agents (Cursor, Lovable, Base44, n8n/Make flows) and small teams — they’ll happily generate a Firestore sync function or an RLS policy that looks right and quietly isn’t.
secureFlows: one model, one API, one SDK
secureFlows takes a different default. A user is a single object that already includes:
User
├── Credentials (identity provider, held internally — never exposed to your code)
├── Role (OWNER / ADMIN / USER / ANONYMOUS)
├── Session(s) (per-app, encrypted, TTL-governed)
├── Domain data (the JSON payload inside each session)
└── Session token (short-lived JWT your app actually holds)
There’s no second database to design, no trigger to keep in sync, no separate rules language to maintain
alongside your schema. One API surface and one SDK (secureflows-js) cover identity, session-scoped
data storage, and role-aware access — the identity provider underneath is real (Firebase), but it’s an
internal implementation detail your code never touches directly.
This is the practical difference for the target audience: a vibe coder or automation builder doesn’t need to know what Row-Level Security is, doesn’t write a Postgres trigger, and doesn’t reconcile two schemas. They call one login flow and one session API, and the “user” they get back already has identity, role, and data attached.
Where this doesn’t apply: if you need arbitrary relational queries across your own business tables — joins, complex reporting, ad-hoc SQL — that’s a real database, and Supabase/Nhost/Amplify’s SQL access is a better fit than a per-session JSON payload. secureFlows is purpose-built for session-scoped, per-user application state, not as a general-purpose relational data warehouse.
Additional advantages
- Storage-layer encryption, correctly scoped. Session payloads are stored as ciphertext in both the primary database and backups — a raw DB leak or backup exposure yields no readable data. This is not marketed as a zero-knowledge guarantee: workspace admins/owners retain role-controlled, fully audited API-layer access to payload content, because legitimate support and moderation workflows need it. That distinction is stated plainly rather than implied away. See also Security Infrastructure.
- Tenant isolation by construction. Every session is scoped to a workspace at creation time; cross-workspace access isn’t a query you have to remember to filter — it isn’t reachable.
- Hosted login as the only supported auth UI. Removes an entire class of bug where AI-generated code builds an insecure in-app login form instead of redirecting to a vetted flow.
-
Built for AI-agent-generated integration code.
SKILL.mdand the SDK are written specifically for how coding agents behave — every constraint ships with a working alternative, because prohibition-only instructions get hallucinated around. - Audit trail out of the box. Session creation, logins, key writes, and billing-limit events are logged with resolved identity (email where known, UID as fallback) — no separate audit pipeline to build.
-
Self-service session dashboard for end users,
and a per-app policy model (
acceptAnonymous,enableSelfService) that’s a workspace setting, not custom code.
Positioning summary
The closest comparison isn’t Firebase or Supabase on features — it’s the assembly work those platforms leave on the table. Appwrite, PocketBase, Nhost, Convex, and Amplify each solve pieces of the same problem in their own way, and PocketBase and Convex narrow the identity/data gap more than most — but developer-side wiring for roles, permissions, and the identity-to-data link is still the norm across the category. secureFlows’s bet is that for its target users — people shipping fast with AI coding tools, not hand-rolling backend infrastructure — a single unified model is worth more than maximal flexibility.
Related positioning: Differentiation. For component and deployment detail, see System Components and System Architecture.
Ship fast. And ship safe.