Skip to content
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
e579add
fix(api): harden RBAC auth compatibility
riderx Jun 3, 2026
d3e8ae7
test(api): advance apikey seed sequence
riderx Jun 3, 2026
cf309e1
fix(api): harden apikey management auth guard
riderx Jun 4, 2026
83a257e
Merge remote-tracking branch 'origin/main' into codex/rbac-apikey-man…
riderx Jun 4, 2026
75f2d72
fix(security): remove old API key rights paths
riderx Jun 4, 2026
479c258
fix(test): remove old RBAC helper from e2e setup
riderx Jun 4, 2026
8378313
fix(security): remove stale old helper paths
riderx Jun 4, 2026
9847747
fix(security): remove stale role fallback lint failures
riderx Jun 4, 2026
6b6a069
fix(security): require JWT for API key management
riderx Jun 4, 2026
915ee39
fix(security): keep pending invites visible in RBAC org list
riderx Jun 4, 2026
e1a60ef
merge: resolve main into rbac apikey hardening
riderx Jun 5, 2026
811b32e
fix(db): reorder RBAC cleanup migration
riderx Jun 5, 2026
c7c16a1
fix(db): honor channel-scoped RBAC updates
riderx Jun 5, 2026
55e833d
fix(db): block direct org role escalation
riderx Jun 5, 2026
367790f
fix(api): normalize organization invite roles
riderx Jun 5, 2026
4ac35f5
fix(db): block developer channel setting mutations
riderx Jun 5, 2026
07db9e7
merge: resolve main into rbac apikey hardening
riderx Jun 5, 2026
7078697
merge: resolve main into rbac apikey hardening
riderx Jun 5, 2026
1b95eda
merge: resolve latest main schema sync
riderx Jun 5, 2026
e6f835d
fix(db): move rbac cleanup migrations after latest main
riderx Jun 5, 2026
7eea9aa
fix(api): preserve hashed org update keys
riderx Jun 5, 2026
dfa2970
Merge remote-tracking branch 'origin/main' into codex/rbac-apikey-man…
riderx Jun 5, 2026
67196a9
test: retry app transfer auth sign-in
riderx Jun 5, 2026
c0cac04
fix(cli): use rbac app permission check directly
riderx Jun 5, 2026
3e5b0ac
fix(cli): pass channel scope for delete checks
riderx Jun 5, 2026
7f50de7
fix(api): honor channel scope in backend channel writes
riderx Jun 5, 2026
94a56fc
fix(api): preserve bundle promotion error guards
riderx Jun 5, 2026
552ebb1
fix(api): authorize apikey management through rbac
riderx Jun 5, 2026
539dedc
fix(security): resolve rbac merge and preserve old cli upload
riderx Jun 8, 2026
85c1a93
fix(db): retimestamp rbac cleanup migrations
riderx Jun 8, 2026
45e3f74
test(db): allow rbac-backed old cli compat rpc
riderx Jun 8, 2026
7ebf397
fix(security): harden bundle promotion rbac
riderx Jun 8, 2026
2914aaf
chore(db): align app versions policy dump
riderx Jun 8, 2026
42bd7b9
fix(security): keep org updates on rls client
riderx Jun 8, 2026
59570c3
chore: merge main into rbac hardening
riderx Jun 10, 2026
18cad6d
fix(db): explicitly deny direct apikey rls access
riderx Jun 10, 2026
c5121b6
fix(cli): honor channel scoped set permissions
riderx Jun 10, 2026
6932d77
Merge branch 'main' into codex/rbac-apikey-management-hardening
riderx Jun 11, 2026
77480e5
chore: merge main into rbac hardening
riderx Jun 11, 2026
d648ab8
fix(db): move rbac migrations after main
riderx Jun 11, 2026
16798ae
fix(db): preserve cli compatibility grants
riderx Jun 11, 2026
9cf0be6
fix(test): skip cli update prompt in tui suite
riderx Jun 11, 2026
4febb0f
fix(frontend): detect org invites from rbac flag
riderx Jun 11, 2026
7420610
test(frontend): expose invite helper in auth mock
riderx Jun 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 35 additions & 53 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,8 @@ $$;
Every PostgreSQL function call added to a policy, view, trigger, RPC,
PostgREST-exposed query path, or hot backend endpoint must be proven to scale
before it ships. Treat this as mandatory for RLS helpers such as
`check_min_rights`, `get_identity_org_appid`, `get_identity_org_allowed`, and
any new wrapper around them.
`rbac_check_permission_request`, `rbac_check_permission`, and any new wrapper
around them.

Before adding or changing a function call, document the execution model:

Expand All @@ -409,9 +409,8 @@ RLS function calls are dangerous by default:
another large table. Allowed-list helpers are only acceptable when they start
from caller-scoped, indexed identity data and stay bounded before touching the
protected resource.
- Never create a helper that scans a broad production table and calls
`check_min_rights`, `get_identity*`, RBAC checks, API-key checks, logging, or
other SQL functions once per scanned row.
- Never create a helper that scans a broad production table and calls RBAC
checks, API-key checks, logging, or other SQL functions once per scanned row.
- If a table has `app_id`, prefer a policy shape that constrains by that
row's indexed `app_id`/`owner_org` values. Do not precompute visibility by
scanning all apps, all versions, all channels, or all org resources.
Expand Down Expand Up @@ -586,48 +585,37 @@ FOR SELECT USING (

## Database RLS Policies

### Identity Functions for RLS - CRITICAL RULES
### RBAC Functions for RLS - CRITICAL RULES

**NEVER use `get_identity()` directly in RLS policies.**
**NEVER use deleted identity/min-right helpers in RLS policies.**

**ALWAYS use `get_identity_org_appid()` when app_id exists on the table.**
Authorization must use RBAC permission keys through the current helpers:

```sql
public.get_identity_org_appid(
'{read,upload,write,all}'::public.key_mode[],
owner_org, -- or org_id
app_id
)
```

**`get_identity_org_allowed()` is an ABSOLUTE LAST RESORT.** Only use it when:
- `public.rbac_check_permission_request(permission_key, org_id, app_id, channel_id)`
- `public.rbac_check_permission(permission_key, org_id, app_id, channel_id)`
- `public.rbac_check_permission_direct(permission_key, user_id, org_id, app_id, channel_id, apikey)`

- The table genuinely has NO app_id column
- There is NO way to join to get an app_id
- You have exhausted all other options
`org_users` is metadata only and must not grant rights. API keys must authorize
through `role_bindings` with `principal_type = public.rbac_principal_apikey()`.

If you find yourself reaching for `get_identity_org_allowed()`, STOP and ask:
"Is there ANY way to get an app_id here?" If yes, use `get_identity_org_appid()`.
When a table has `app_id`, pass the row's indexed `owner_org`/`org_id` and
`app_id` to the RBAC check. If a table has no `app_id`, join through the closest
indexed parent that provides the app or org scope.

### RLS Pattern Examples

```sql
-- CORRECT: Table has app_id - use get_identity_org_appid
-- CORRECT: Table has app_id - use row scope with RBAC permission key
CREATE POLICY "Allow org members to select build_requests"
ON public.build_requests
FOR SELECT
TO authenticated, anon
USING (
public.check_min_rights(
'read'::public.user_min_right,
public.get_identity_org_appid(
'{read,upload,write,all}'::public.key_mode[],
owner_org,
app_id
),
public.rbac_check_permission_request(
public.rbac_perm_app_read(),
owner_org,
app_id,
NULL::BIGINT
NULL::bigint
)
);

Expand All @@ -638,46 +626,40 @@ FOR SELECT
TO authenticated, anon
USING (
EXISTS (
SELECT 1 FROM public.apps
WHERE apps.app_id = daily_build_time.app_id
AND public.check_min_rights(
'read'::public.user_min_right,
public.get_identity_org_appid(
'{read,upload,write,all}'::public.key_mode[],
apps.owner_org,
apps.app_id
),
SELECT 1 FROM public.apps
WHERE apps.app_id = daily_build_time.app_id
AND public.rbac_check_permission_request(
public.rbac_perm_app_read(),
apps.owner_org,
apps.app_id,
NULL::BIGINT
NULL::bigint
)
)
);

-- LAST RESORT: Table has NO app_id and NO way to get one (e.g., build_logs)
-- Table has no app scope, so use the row's org scope directly.
CREATE POLICY "Allow org members to select build_logs"
ON public.build_logs
FOR SELECT
TO authenticated, anon
USING (
public.check_min_rights(
'read'::public.user_min_right,
public.get_identity_org_allowed(
'{read,upload,write,all}'::public.key_mode[],
org_id
),
public.rbac_check_permission_request(
public.rbac_perm_org_read(),
org_id,
NULL::CHARACTER VARYING,
NULL::BIGINT
NULL::character varying,
NULL::bigint
)
);
```

Key points:

- Use both `authenticated` and `anon` roles (anon enables API key auth)
- Pass app_id to BOTH `get_identity_org_appid()` AND `check_min_rights()`
- Reference apps, channels, app_versions tables for more examples
- Use both `authenticated` and `anon` roles where API-key traffic must reach the
policy.
- API-key access must still resolve through RBAC `role_bindings`; do not inspect
key modes.
- Reference apps, channels, app_versions, and manifest tables for current
policy examples.

## Frontend Style

Expand Down
Loading
Loading