Skip to content
All articles
4 min read

Your workspace is a security boundary.

Why ownership checks belong in every read and write, and why a task URL should never grant access.

supastack team

Derive ownership on the server

A record ID identifies something; it does not prove who may read it. supastack scopes user-owned records by workspaceId. The server resolves the authenticated user’s workspace and includes that scope when reading or changing a record.

Do not treat a workspace ID sent by the browser as evidence of ownership. The browser can request an action, but the trusted server context must establish whose action it is. This rule needs to hold for task status, saved provider configuration, usage records, and every product-owned resource. Layouts and components can filter what renders, but rendering is not authorization — the check belongs where the record is actually read or written.

Everything else in this post follows from that rule: ownership is derived, never requested. The moment a client-sent workspaceId is trusted anywhere, every other check becomes decoration. The authentication post covers how the session itself is established upstream of this boundary.

Membership is part of the boundary

Scoping answers which workspace a record belongs to; membership answers whether this user belongs to that workspace. Resolve both on the server. supastack models personal and organization workspaces with owner, admin, and member roles — the workspaces guide documents the model.

Roles add a second axis: a member may read what only an admin may change. Keep the two checks distinct — “belongs to this workspace” and “may perform this action” — so a demotion or removal takes effect at the same boundary as everything else. Invitations and removals are part of the same lifecycle, not an admin afterthought.

Membership changes also have to take effect immediately. A removed member’s next request should fail at the boundary, not whenever a cached token happens to expire — which is another reason the check lives where records are read, rather than inside a credential issued earlier.

Test the requests that must fail

A test that lets an owner retrieve a record proves the happy path. It does not prove isolation. Pair it with a second workspace requesting the same record, an anonymous request, and a write that attempts to substitute another owner.

Assert the external behavior as well as the persisted result. A rejected request must not mutate another workspace’s data, and an error response must not leak secrets or raw database details. These checks make the boundary reviewable when implementation changes.

  • A second workspace requests the same record ID — rejected.
  • An anonymous request hits the same endpoint — rejected.
  • A member removed from the workspace retries — rejected.
  • A write names a different owner or workspace — rejected, with nothing mutated.
  • Error responses stay generic — no secrets, no schema details.

Carry the rule into your product

When you add a new product table, decide its ownership before building the screen. Include workspaceId in its persistence model, resolve the workspace through the existing server boundary, and use that context in both list and detail operations.

Background workers need the same care. A queued task must carry enough trusted context to load the correct record, while provider credentials stay on the server. A scheduled job loading rows by bare ID is still inside the boundary. A consistent ownership rule is easier to maintain than a collection of route-specific exceptions.

The API surface belongs to the same boundary. Workspace API keys and signed outbound webhooks carry the workspace’s scope with them — a key must never become a way to read across tenants, and a webhook payload must never carry a neighbor’s records. The API module guide covers the shape of that surface.

Common questions about workspace isolation

Multi-tenant isolation raises the same handful of questions on every project. These are the ones worth settling early.

  • Is application-level scoping enough, or do I need row-level security? — Server-enforced scoping on every read and write covers the threat model for most SaaS. Database RLS adds defense in depth but does not replace boundary checks — workers and API keys still need the same rule.
  • How should sharing across workspaces work? — Model it explicitly — an invitation, a membership row — rather than weakening the scope. “Anyone with the link” should be a designed feature with its own checks, not an accident.
  • What breaks first in multi-tenant apps? — Convenience paths: recovery links, admin tooling, background workers loading records by bare ID. Audit every code path that touches a record without going through the normal scope.
  • How do I add scoping to an existing table? — Add workspaceId, backfill it, enforce it in reads and writes, then touch the UI. Ship the negative tests first so the boundary is proven rather than assumed.
  • Do server actions need checks if the page is protected? — Yes. Page guards hide screens; they do not authorize operations. Every action resolves the session and workspace independently.

Frequently asked questions

14 questions

What is supastack?

supastack is a modular foundation for building AI-powered SaaS products. It brings together common infrastructure so you can focus on product-specific features.

Does it support web and mobile?

Yes. supastack includes Next.js and Expo apps as starting points for web and mobile experiences.

What authentication is included?

Better Auth covers email OTP, magic-link, and social sign-in; email/password stays available at the API layer and through the Auth.js credentials engine for products that need it. Personal workspaces are provisioned with the account so product records stay scoped from day one.

How does it support AI?

supastack includes configurable multi-provider AI, usage tracking, budgets, and encrypted bring-your-own-key support.

How are subscriptions handled?

For products built on the kit, Stripe billing, subscriptions, and entitlements form the paid-access foundation. Provider secrets stay server-only.

Where does my product-specific code live?

Product-specific features remain in a separate codebase while the product configures and integrates supastack’s foundation modules.

How does supastack pricing work?

The kit is a one-time purchase with lifetime updates: One Platform at $249 (temporary launch price, $100 off the regular $349) — your pick of Next.js (Web), Expo (iOS & Android), or Tauri (macOS & Windows Desktop) — or the All Platforms Bundle at $499 (temporary launch price, $100 off the regular $599) with every platform and the full monorepo, plus $75 per extra developer seat. No kit subscription. Checkout on this site delivers your license key by email, with repository access via GitHub invite.

How do workspaces scope my data?

User-owned records are scoped by workspaceId. Identity, entitlements, AI usage, billing events, and audit entries stay inside the personal workspace boundary.

How do background jobs work?

Trigger.dev is the explicit job runtime. The JobRunner contract only promises enqueue, status, and cancel—workflow semantics stay Trigger.dev-specific and explicit.

How is email different from outreach?

Transactional email covers product workflows such as completion notices. Cold outreach and campaigns are product concerns outside this kit’s email module.

How do mobile purchases work?

Mobile includes native purchases and restore through RevenueCat, with access verified by your server. Configure the store accounts and complete device testing before release. Web billing remains separate.

Where do AI provider keys live?

Bring-your-own-key credentials are encrypted and stay server-only. Keys are redacted from logs; usage and budgets remain visible at the workspace level.

Which payment methods does checkout accept?

The Stripe-hosted checkout accepts cards and can also offer Alipay and WeChat Pay once those wallets are enabled on the Stripe account — handy for buyers in China. Wallet authorizations confirm asynchronously, so the license page updates as soon as the payment clears.

Can I get a refund?

After you've got access to the repository, supastack is yours forever, so it cannot be refunded. But rest assured, you get the complete source code with lifetime updates to build and ship unlimited products for yourself or clients. You just cannot resell the code or redistribute the repository as a starter kit or template.

EN