Appearance
Firewall - Data Isolation
The firewall generates WHERE clauses automatically to isolate data by user, organization, or team.
Configuration Options
typescript
firewall: {
// User-level ownership (personal data)
owner?: {
column?: string; // Default from auth provider
source?: string; // e.g., 'ctx.userId'
mode?: 'required' | 'optional'; // Default: 'required'
};
// Organization-level ownership
organization?: {
column?: string;
source?: string;
};
// Team-level ownership
team?: {
column?: string;
source?: string;
};
// Soft delete filtering
softDelete?: {
column?: string; // Default: 'deletedAt'
};
// Opt-out for public tables (cannot combine with ownership)
exception?: boolean;
}Common Patterns
typescript
// Public/system table - no filtering
firewall: { exception: true }
// Organization-scoped data
firewall: { organization: {} }
// Generated: WHERE organizationId = ctx.activeOrgId
// Personal user data
firewall: { owner: {} }
// Generated: WHERE ownerId = ctx.userId
// Org data with optional owner filtering
firewall: {
organization: {},
owner: { mode: 'optional' }
}
// With soft delete
firewall: {
organization: {},
softDelete: {}
}Rules
- Every resource MUST have at least one ownership scope OR
exception: true - Cannot mix
exception: truewith ownership scopes
Why Can't You Mix exception with Ownership?
They represent opposite intentions:
exception: true= "Generate NO WHERE clauses, data is public/global"- Ownership scopes = "Generate WHERE clauses to filter data"
Combining them would be contradictory - you can't both filter and not filter.
Handling "Some Public, Some Private" Data
If you need records that are sometimes public and sometimes scoped, you have two options:
Option 1: Two Separate Resources
Split into two tables - one public, one scoped:
typescript
// Public templates anyone can see
defineResource(templateLibrary, {
firewall: { exception: true }
});
// User's custom templates
defineResource(userTemplates, {
firewall: { owner: {} }
});Option 2: Use Access Control Instead
Keep ownership scope but make access permissive, then control visibility via access:
typescript
defineResource(documents, {
firewall: {
organization: {}, // Still scoped to org
},
crud: {
list: {
// Anyone in the org can list, but they see different things
// based on a "visibility" field you check in your app logic
access: { roles: ["member", "admin"] },
},
get: {
// Use record conditions to allow public docs OR owned docs
access: {
or: [
{ record: { visibility: { equals: "public" } } },
{ record: { ownerId: { equals: "$ctx.userId" } } },
{ roles: ["admin"] }
]
}
},
}
});Which to Choose?
| Scenario | Recommendation |
|---|---|
| Truly global data (app config, public templates) | exception: true in separate resource |
| "Public within org" but still org-isolated | Ownership scope + permissive access rules |
| User can toggle their own data public/private | Ownership scope + visibility field + access conditions |