Security & Row-Level Security
basefyio uses a layered security model: API keys authenticate requests, JWT tokens identify users, and database Row-Level Security (RLS) policies control data access at the database level.
Authentication Flow
Client → API Key (apikey header) → Platform API
│
├── Anonymous access (anon key, no JWT)
│ └── database role: anon
│
└── Authenticated access (anon key + JWT Bearer)
└── database role: authenticated
└── RLS policies filter rows based on auth.uid()
Server → Service Key (apikey header)
└── database role: service_role (bypasses RLS)API Keys
| Key | Use In | RLS | Access Level |
|---|---|---|---|
| Anon Key | Client-side (browser, mobile) | Enforced | Only rows allowed by RLS policies |
| Service Key | Server-side only | Bypassed | Full access to all data |
Never expose your service key in client-side code, public repositories, or browser DevTools.
JWT Tokens
When a user signs in, basefyio issues a JWT containing their user ID, email, and roles. The JWT is verified against the project's auth realm JWKS endpoint. Claims are exposed to RLS policies via helper functions.
| Function | Returns | Example Usage |
|---|---|---|
auth.uid() | Current user ID (from JWT sub) | WHERE user_id = auth.uid() |
auth.jwt() | Full JWT claims as JSON | WHERE auth.jwt()->>'email' = email |
auth.role() | Current role name | WHERE auth.role() = 'authenticated' |
auth.email() | Current user email | WHERE owner_email = auth.email() |
Row-Level Security (RLS)
Every project database has RLS enabled with three pre-installed roles:
anon— Unauthenticated users (public access)authenticated— Signed-in users (JWT verified)service_role— Server-side admin (bypasses all RLS)
The API automatically switches to the correct role based on the request's authentication context using SET LOCAL ROLE inside a transaction.
Writing RLS Policies
Use the SQL Editor in the dashboard to create policies:
-- Users can only read their own data
CREATE POLICY "Users read own data" ON profiles
FOR SELECT
TO authenticated
USING (user_id = auth.uid());
-- Users can insert their own profile
CREATE POLICY "Users insert own profile" ON profiles
FOR INSERT
TO authenticated
WITH CHECK (user_id = auth.uid());
-- Users can update their own profile
CREATE POLICY "Users update own data" ON profiles
FOR UPDATE
TO authenticated
USING (user_id = auth.uid())
WITH CHECK (user_id = auth.uid());
-- Public data readable by everyone
CREATE POLICY "Public posts readable" ON posts
FOR SELECT
TO anon, authenticated
USING (published = true);
-- Only authors can edit their posts
CREATE POLICY "Authors edit own posts" ON posts
FOR UPDATE
TO authenticated
USING (author_id = auth.uid());Enable RLS on a Table
-- Enable RLS (required before policies take effect)
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
-- Force RLS for table owner too (recommended)
ALTER TABLE profiles FORCE ROW LEVEL SECURITY;Best Practices
- Always enable RLS on tables that store user data. Without RLS, the
anonrole can read everything. - Use the anon key for clients. The service key should only be used in server-side code (backend APIs, cron jobs, admin scripts).
- Test policies thoroughly. Use the SQL Editor with different roles to verify access.
- Avoid storing secrets in the database. Use environment variables for API keys, tokens, and passwords.
- Rotate compromised keys from the project settings. Regenerate the anon or service key if you suspect a leak.
- Use HTTPS. basefyio enforces HTTPS in production via Traefik with automatic Let's Encrypt certificates.
Data Engine Security
The Data Engine enforces tenant isolation differently from RLS:
- Every query has
_projectIdinjected server-side as a mandatory filter - This filter cannot be omitted or overridden by callers
- Documents from Project A are invisible to Project B, even in shared storage
- The Data Engine uses the same JWT/API key authentication as the REST API
Audit Logging
basefyio logs security-relevant events:
- SQL Audit Log — Every SQL query executed, with user, duration, and result count
- Project Activity Log — Table/column/row changes, auth config changes, imports, exports
- Data Engine Outbox — Every document create/update/delete event
- Root Audit Log — Platform-level actions (user management, team changes, billing)