Security and Organization Scoping
Audience: Developer, System Admin, Support Related: Organizations · Users Permissions And Authentication · Database Service · Environment And Secrets
Security is enforced through authentication, account status, role permissions, Active Organization context, resource ownership, CSRF protection, rate limiting, safe external fetch rules, secret handling, provider request validation, and audit logging.
Organization-scoped queries must include organization identity explicitly. Platform-scoped settings and System Admin operations are exceptions that require deliberate authorization. User-global resources such as personal authentication state or personal AI Seeds must not be mistaken for organization-owned data.
Blackcap Pi treats operational/business content as organization-owned. This document summarizes the current scoping rules for Database Admin and the major data domains.
🔐 Core rule
Organization-scoped resources are visible and editable only in the active organization context.
- Org admins see permitted resources for their own organization.
- Lower roles see only the resources/actions allowed by their permissions.
- System admins in the Default organization context see Default organization-scoped business data only.
- A system admin must intentionally switch organization context to inspect or affect another organization.
- Direct URLs, form values, and API/POST calls must not override the active context.
Platform/global resources remain system-admin-only where appropriate.
🗄️ Database Admin scope behavior
The Database Admin resource registry defines each resource's scope. Organization filtering is enforced by the provider/service layer for:
- resource counts
- resource lists
- filter dropdowns
- search results
- detail drawer reads
- safe edits
- exports/direct route calls that use resource pages
Resources can be scoped by:
- a direct
organization_id-style column - a parent ownership join, such as shopping-list items through shopping lists
- scoped settings columns, such as
scope_type='organization'andscope_id=<active org> - organization key suffixes, such as
recipe_cart:org:<organization_id>in app state
Organization-scoped provider queries fail closed if no active organization context is available.
✅ Resources explicitly treated as organization-scoped
The Database Admin registry treats these as organization-scoped when shown through Resource Explorer:
- Recipes
- Recipe Edit Content
- Recipe Ingredients
- Recipe Tags
- Meal Planner Settings
- Meal Slots
- Meal Plan Entries
- Meal Plan Shopping List Links
- Shopping Lists
- Shopping List Items
- Shopping List Item Sources
- Recipe Shares
- Recipe Share Recipient Targets
- Recipe Share Notifications
- Recipe Share Consumptions
- Recipe Share Audit
- Background Jobs
- Cache Status
- Devices
- Displays
- Connections
- Organization Memberships
- Organization Backup Runs
- Cloud Backup Provider Tokens
- Cloud Backup State
- Organization Noun Project Rules/cache resources
- Settings where
scope_type='organization' - Setting Audit Log rows where
scope_type='organization' - App State rows whose keys belong to the active organization, such as
recipe_cart:org:<organization_id> - Audit Log rows where
organization_idis present
Platform/global resources include organizations, organization lifecycle events, setting definitions, connection types, provider configuration metadata, schema migrations, and platform-level emoji registry/cache data.
🍲 Recipes and recipe content
Recipes are scoped by organization_id + recipe_id. A recipe lookup by recipe_id alone is unsafe unless organization context is applied by the service.
Expected behavior:
- same
recipe_idcan exist in multiple organizations - create/update/delete/cache-status lookup is organization-scoped
- recipe image/PDF endpoints must resolve through the active organization or an authorized share token
- recipe search and ingredient search are organization-scoped
- recipe sharing/import writes new data into the recipient organization
- recipe edit content and recipe ingredients are scoped directly or through recipe ownership
📅 Meal Planner
Meal Planner data is organization-owned. Settings, slots, entries, review/shopping-list generation, and move operations all resolve through the active organization context.
Expected behavior:
- week start day and meal slot configuration belong to the active organization;
- meal slots are not shared across organizations;
- meal plan entries reference recipes using the current organization plus recipe id;
- Recipe Discovery return context creates meal plan entries only in the active organization;
- shopping lists generated from Meal Planner are created inside the active organization;
- item/slot/day move endpoints cannot move records across organizations;
- recipe archive/delete dependency checks consider only the current organization.
A system admin must switch organization context before viewing or managing another organization's meal plans. Forged organization_id, slot_id, entry_id, or recipe_id values should not override the active context.
🧂 Ingredients and canonical cleanup
Recipe ingredients and shopping-list items are organization-owned. Ingredient search/typeahead and canonical cleanup should stay inside the active organization.
Canonical maintenance now uses strict active-org filters. Missing organization context means no rows, not all rows.
🛒 Shopping lists and shopping-list items
Shopping lists are organization-owned by direct organization_id.
Shopping-list items are also organization-owned and are enforced in Database Admin with both:
- the item row's
organization_id, and - the parent shopping list's
organization_id.
This defense-in-depth check prevents an orphaned or mismatched child row from leaking through Resource Explorer.
⚙️ Settings, Setting Audit Log, and App State
Settings and Setting Audit Log are not treated as platform-wide in Database Admin when they represent organization settings. Resource Explorer shows organization settings where:
scope_type = organization
scope_id = active organization id
System Admin users also see platform-scoped settings in the Settings resource so platform and active-organization settings can be reviewed together. That mixed view still does not include settings from other organizations unless the system admin intentionally switches organization context.
App State is organization-scoped when it stores operational state for an organization. The current recipe-cart app-state pattern uses keys like:
recipe_cart:org:<organization_id>
Platform/global app state should be represented separately and should stay system-admin-only. Platform settings are allowed in the System Admin Settings view, but only alongside the active organization's settings.
☁️ Backup and Noun Project data
Backup provider connections, encrypted provider token payloads, backup status/state, and backup run records are organization-scoped. The Default organization additionally shows full/platform backup runs in organization_backup_runs with backup_scope='platform'.
Noun Project API credentials are platform/provider configuration, but Noun Project keyword rules are organization-scoped rows in noun_project_rules. Downloaded icon files remain filesystem cache artifacts under noun_cache/ and org-specific subfolders.
👥 Users, security, and activity pages
The Admin Users, Security, and Activity pages use the same active-organization rule as Database Admin organization resources. A System Admin in the Default organization context sees Default users, tokens, invites, security activity, and audit events. To inspect another organization, the System Admin must intentionally switch organization context first. Org admins can view their organization's Security dashboard, including org user counts, Google-linked users, API-token status, and scoped security activity; platform security configuration links remain hidden from org admins.
🧪 Isolation test checklist
- Org admin can see Settings, Setting Audit Log, Shopping List Items, and App State for their org.
- Org admin cannot see another org's records through lists, details, search, filters, exports, or crafted URLs.
- System admin in the Default organization context sees only Default organization-scoped business data, users, tokens, security activity, and audit events.
- System admin sees platform settings together with active-org settings, without seeing settings from other organizations.
- System admin switches context intentionally and then sees that selected org's business data and org-owned admin data.
- Direct detail/edit calls with a forged
organization_iddo not override active context. - The same
recipe_idcan exist in two organizations and resolves only in active context. - Meal Planner settings, slots, entries, moves, review, and shopping-list generation stay in the active organization.
- Recipe Discovery return context cannot create a meal plan entry in another organization.
- Shopping-list items require both child and parent org ownership.
- SQL Console is System Admin only. It allows read-only inspection of organization-scoped tables, but it does not automatically inject the active organization context; production queries should add explicit
organization_idfilters and exclude regression/diagnostic clone organizations when appropriate. - SQL Console can query tables that contain hidden/masked fields, but protected fields cannot be selected. Use explicit safe columns instead of
SELECT */alias.*for those tables. - Secret settings, token hashes/encrypted tokens, password hashes, provider config JSON, cloud-backup token payloads/state JSON, and token references are hidden or masked in Database Admin.
- Recipe Library, recipe add/edit/delete, recipe sharing, shopping lists, completed shopping lists, cache queue, backup, mobile UI, and Chrome Extension still work.
🧹 Purge and organization isolation
Manual purge is scoped to the active organization context. A system admin in the default organization purges only default-organization records unless they intentionally switch organization context first. Org admins can purge eligible content in their own organization only.
Scheduled purge is controlled by platform-level configuration and iterates across all non-deleted organizations using the same retention rules. Purge ordering is domain-aware: shopping lists are evaluated before recipes so shopping-list references are cleaned before recipe parent records are removed.