This document focuses on architecture-level security patterns. For implementation details, see:
- Authentication - User authentication flows
- Authorization - Permission system
- RLS Policies - Row-level security implementation
Plan-aware egress and abuse allowances
WhenSECURITY_EGRESS_ENFORCEMENT_ENABLED=true, live Next.js uses Postgres-backed
request and private-download budgets independently of Redis. Free/anonymous
traffic has separate shared ceilings; active paid personal subscriptions and
current paid workspace memberships provide bounded account uplifts. Team CMS
delivery uses the target workspace’s subscription, while personal workspace
media inherits the owner’s paid account memberships. Free/Plus/Pro/Enterprise
multipliers are 1/4/10/20, with a capped bonus for multiple paid workspaces.
Trials, unpaid/expired subscriptions and zero-cost products do not increase limits.
Paid plans do not bypass authentication, permissions, abuse challenges, or global
byte containment. Anonymous public consumers keep strict client limits. Existing
public Storage assets remain outside private relay budgets.
See Storage egress protection for exact
base allowances, scope, cache/revocation timing, regression tests, and migration-first
activation. This behavior is implemented in live Next.js; Rust parity is pending
and protected paths must remain on Next.js before any future traffic cutover.
1. Consistent Security Enforcement at the API Boundary
Architectural Choice
Rather than a literal standalone API gateway, Tuturuuu enforces security consistently at the API route boundary through a shared higher-order wrapper. Every public SDK route underapps/web/src/app/api/v1/* is wrapped with withApiAuth, which centralizes the security logic (API-key authentication, rate limiting, permission checks) in one tested implementation instead of re-deriving it per route.
Conceptual vs. real. “API gateway” describes the pattern — a single, uniform enforcement point — not a literal gateway process in front of microservices. In
apps/web there is no separate gateway tier; security is enforced per route. The uniformity comes from sharing the withApiAuth wrapper (apps/web/src/lib/api-middleware.ts), Next.js middleware.ts, and Vercel’s edge. In the TanStack + Rust migration, the Rust backend (apps/backend) becomes the primary boundary that validates Supabase sessions.Impact and Justification
By funneling authentication and authorization through one shared wrapper, we create a single security control plane in code: policies are defined once and applied consistently before any business logic runs. Routes do not re-implement token parsing, key validation, or rate-limiting individually, which removes the drift and duplication that cause inconsistent security postures. Conceptual enforcement flow (illustrative, not a literal gateway process):apps/web/src/app/api/v1/storage/list/route.ts):
rateLimit is optional: by default withApiAuth leaves GET/HEAD reads open and applies a per-key mutation limit (about 100 requests/minute), with workspace-specific overrides read from workspace_secrets. Pass a rateLimit object only to tighten or relax those defaults.
Clarifying Additions
Shared enforcement ensures uniform security behavior across routes. Instead of each route implementing authentication differently,withApiAuth provides a single, tested implementation every endpoint benefits from.
Policies update consistently without touching each route. When a new requirement emerges (e.g., a stricter default rate limit), it is implemented once in the wrapper and immediately applies everywhere it is used.
This strengthens protection by eliminating inconsistent security implementations. Human error is reduced because developers do not re-implement the same security logic across many routes.
2. Zero Trust Principles Applied at the Architectural Level
Architectural Choice
The architecture implements Zero Trust security principles, where no service or request is inherently trusted based on its network location or source. Every interaction, whether from external clients or between internal services, must be explicitly authenticated and authorized.Impact and Justification
Traditional “castle-and-moat” security assumes that anything inside the network perimeter is trusted. This is a dangerous assumption because once an attacker breaches the perimeter, they have unrestricted access to internal systems. Zero Trust architecture eliminates implicit trust. Every request is re-validated for identity and permissions at the boundary it crosses, and the database independently enforces access rules regardless of which app issued the query. How identity is established in practice. Every request that reaches data must carry a verifiable identity, and that identity is re-checked at the boundary:- User requests carry a Supabase session (cookie or
Authorization: Beareraccess token). The Rust backend validates these inapps/backend/src/supabase_auth.rs, which extracts the access token from thesb-*-auth-tokencookie (including base64-prefixed, chunked cookies) or a non-app Bearer token, then calls the Supabase Auth/userendpoint with the service-role key to resolve the user. - SDK / machine requests carry a workspace-scoped API key, authenticated and rate-limited by
withApiAuthinapps/webbefore any handler runs.
Clarifying Additions
Identity is established from verifiable tokens, not network location. A request is trusted because it carries a valid Supabase session or workspace API key that is re-validated at the boundary — not because it originated inside the deployment. The database has the final say. Even if an API layer were bypassed or compromised, RLS policies independently enforce that a user can only read or write rows for workspaces they belong to. Application bugs cannot silently defeat data-access rules. This reduces risks associated with implicit trust. Because authorization is enforced both at the boundary and in the database, a single compromised layer does not grant unrestricted data access.3. Security Boundaries Through RLS and Permission Scoping
Architectural Choice
Isolation between domains (finance, AI chat, drive, tasks) is enforced primarily by per-table Row-Level Security policies and scoped permissions, not by giving each app its own Supabase project or key. The apps share one Supabase database; the boundary lives in the policies and in the permission set each API key or user role is granted.Impact and Justification
In a single shared database, a vulnerability that bypasses one route must still defeat the per-table RLS policy to reach data. By scoping each domain’s tables with dedicated policies and permission requirements, we contain the blast radius: a bug in a drive route cannot read finance rows, because the finance policy requires finance-specific roles and the request’s identity does not satisfy them. Concretely, SDK routes declare the permissions they need (permissions: ['manage_drive']), and the database enforces an orthogonal check at query time. An attacker who somehow reaches a query path is still bounded by both the granted permission scope and the row-level policy.
Data isolation through RLS policy design:
Clarifying Additions
A bug in one domain’s routes does not expose another domain’s data. RLS policies and permission scoping bound each domain independently, so reaching a finance table requires finance-grade identity even from an unrelated route. Policies act as an independent barrier alongside application checks. Defense in depth is achieved because the database re-validates access regardless of what the application layer believed. This scoping limits the damage in security incidents. Incident response is more manageable because the data an attacker could reach is bounded by the policies and permissions that gate each table.4. Defense-in-Depth via Layered Architecture
Architectural Choice
The architecture implements multiple layers of security controls, from the network edge through the API Gateway, into the application services, down to the database layer. Each layer provides independent security validation.Impact and Justification
Relying on a single security layer is fragile. If that layer is bypassed or fails, the entire system is exposed. The defense-in-depth approach ensures that even if an attacker successfully bypasses one security control, they encounter additional independent security barriers at deeper layers. For example, even if an attacker bypassed the API boundary’s authentication (highly unlikely but theoretically possible), they would still encounter:- Route-level authorization checks (declared permissions) in the application code
- Database Row-Level Security policies that enforce data access rules
- Workspace scoping that bounds every query to the caller’s workspace
createTuturuuuNextConfig receives Content-Security-Policy: frame-ancestors 'none' and X-Frame-Options: DENY on its responses. This prevents an
attacker-controlled page from rendering Tuturuuu interfaces inside an iframe
for UI redressing. App-specific headers() entries are composed after the
shared security rule, so route-specific cache and indexing headers remain
intact.
The only shared exception is the CMS WebGL asset route
/api/v1/workspaces/:wsId/external-projects/assets/:assetId/webgl/*. The CMS
player intentionally renders that document in a sandboxed iframe without
allow-same-origin; the asset route keeps its own restrictive CSP sandbox
policy. New framing exceptions must be path-specific, justified by a documented
product requirement, and preserve an equivalent sandbox boundary.
Layer 2: Application-Level Security
Clarifying Additions
Multiple layers ensure no single point of failure exposes sensitive logic. An attacker must defeat multiple independent security controls to compromise the system, making successful attacks exponentially harder. Higher layers validate and protect the domain before requests reach it. The most critical business logic (the domain core) is protected by multiple security barriers, ensuring it only processes validated, authorized requests. The system becomes safer by distributing security responsibility across layers. Security is not the responsibility of a single component but is woven throughout the architecture, creating a more resilient security posture.5. Auditability and Background Security Workflows
Architectural Choice
Security-relevant actions should be traceable, and heavier security workflows (anomaly scans, notifications, periodic reviews) should run as durable background jobs. Tuturuuu uses Trigger.dev v4task() for background work and native server console logs plus retained platform observability data for server-side diagnostics.
Impact and Justification
Security is not just about prevention; it’s also about detection and accountability. A durable, append-only audit trail would provide:- Forensic Analysis: After an incident, history lets investigators reconstruct what happened, when, and by whom
- Compliance: Regulations (GDPR, SOX, HIPAA) require audit trails of data access and modifications
- Anomaly Detection: Patterns can be analyzed to detect suspicious behavior (e.g., unusual access patterns, privilege escalation)
- Non-Repudiation: Append-only, tamper-evident records provide strong proof of actions
task() API):
Trigger.dev v4 uses the task() builder (@trigger.dev/sdk ^4.4.5). The v2 API (client.defineJob, eventTrigger, io.*) does not exist in this repo. A security-scan task looks like the calendar/schedule tasks under packages/trigger/src:
securityScanTask.trigger({ userId }) (or batchTrigger for many). See the Event-Driven Architecture page for how Tuturuuu actually uses Trigger.dev v4 and native server console logs.
Clarifying Additions
Durable background tasks keep heavy security work off the request path. Anomaly scans and notifications run as Trigger.dev v4 tasks rather than blocking the user’s request. An append-only audit trail would support accountability and forensics. This is the target; today, rely on retained platform observability data, native server console logs, and table-level history for traceability. Compliance benefits from consistent logging. Use the native console method matching severity and retain structured metadata where platform observability wrappers are wired.Security Architecture Summary
Security Best Practices in Tuturuuu
1. Never Trust, Always Verify
- Every request validated at multiple layers
- No assumptions about request origin or authenticity
- All user input sanitized and validated
2. Principle of Least Privilege
- Services granted minimum permissions needed
- Users granted minimum roles required
- API keys scoped to specific operations
3. Fail Securely
- Default deny for authorization decisions
- Explicit permission grants required
- Errors don’t leak sensitive information
4. Defense in Depth
- Multiple layers of security controls
- Redundant security checks
- No single point of failure
5. Audit Everything
- All security-relevant events logged
- Immutable audit trail
- Regular security audits and reviews
Security Under the TanStack + Rust Migration
The maintained runtime is Next.js Web and its satellite apps. Rust (apps/backend) and TanStack Start (apps/tanstack-web) are paused and not in
use. Docker setup is inactive and the Docker cron runner is retired. Retained
migration designs describe a possible future, not live traffic or a current
setup requirement. See the active runtime map.
- Boundary authentication shifts into the Rust backend (
apps/backend/src/supabase_auth.rs), which validates Supabase access tokens from cookies or Bearer headers against Supabase Auth using the service-role key. - TanStack web must not call
/trpcor raw app APIs directly (a guard,check-tanstack-api-access, enforces this); product data flows throughpackages/internal-apihelpers and REST/api/v1routes. - RLS remains the durable backstop across both stacks because both share the same Supabase project.
Related Documentation
- Authentication - User authentication implementation
- Authorization - Permission system and role-based access
- RLS Policies - Database-level security
- TanStack Start And Rust Migration - How the boundary moves into the Rust backend
- Encapsulation Patterns - Service boundary enforcement
- Event-Driven Architecture - Trigger.dev v4 tasks and background workflows
Authenticator and meeting coordination
Authenticator registry mutations use theapps/coordination Cloudflare Worker
and SQLite Durable Objects. One object per hashed user serializes changes to
server-owned Supabase app metadata. Meeting delivery uses a separate namespace
with payload fingerprints and completion records, so retries preserve their
original identity. Login approval challenges remain in Supabase; this service
never stores authenticator secrets, session tokens, email addresses, or meeting
contents. Upstash is not required for these operations.
Provision COORDINATION_TOKEN as a Worker secret. Store its value in the
Infrastructure root-workspace secret cloudflare_coordination:v1 as an
envelope (version, encryptedDataKey, encryptedSecret) using the existing
mobile deployment encryption helpers, then provision the same value as the server-only
CLOUDFLARE_COORDINATION_TOKEN in the web, Calendar, and Infrastructure deployment environments.
Set CLOUDFLARE_COORDINATION_URL to the deployed Worker’s HTTPS origin. Never
expose these credentials in Flutter defines, public environment variables, URLs,
or logs. Missing credentials and network failures fail closed. Deploy the Worker
and verify its authenticated endpoint before enabling the server callers.
Registry leases expire after five minutes. Meeting leases expire after ten
minutes, while request fingerprints and completion records remain for at least two hours
and never expire before an active lease.
An expired owner cannot complete or release a newer owner’s operation. The
calling service must still enforce request age and provider idempotency: a lease
alone cannot prevent an external provider request from finishing after a timeout.
Alarms clean up expired state. Arbitrary payloads and unbounded TTLs are rejected.
Validate changes with:
COORDINATION_TOKEN on the Worker and configure
COORDINATION_CLOUDFLARE_API_TOKEN in the GitHub coordination-production
environment (the existing account deployment credential is a bootstrap fallback).
Inspect secret names only during deployment checks. A successful /health
response proves routing; also verify an authenticated acquire/check/release cycle
and rejection of unauthenticated requests before declaring coordination ready.
Internal account recovery
Infrastructure’s internal account directory and mobile Settings → Internal accounts share the same authorized API. Native requests use their Supabase Bearer session; an explicit invalid credential never falls back to another browser session. Operators need a currently confirmed@tuturuuu.com account, enabled
access, and manage_internal_accounts in the root workspace.
Profile, access, password, and authenticator actions apply only to exact internal
email accounts. Sensitive actions require typing the target email and cannot
reset the operator’s own access or credentials. The mobile directory remains in
memory only; password input is never cached or logged.
Authenticator reset clears server-owned trusted-device proofs before deleting
Supabase MFA factors. It shares the Cloudflare authenticator lease with device
registration and fails closed if coordination is unavailable. A partial provider
failure returns an error; retry the reset to finish removing remaining factors.
The action preserves an existing account ban. Factor enrollment still requires
the account holder to prove possession; administrators cannot silently enroll an
authenticator for someone else. Resetting factors is a recovery operation, not a
policy that requires MFA for subsequent sign-ins.
Before releasing these controls, verify Infrastructure has the same server-only
coordination credentials as web and test both authorized and denied native
requests. Never distribute those credentials in mobile configuration.