@rebasepro/agent-skills 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rebase
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@rebasepro/agent-skills",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "description": "Bundled AI agent skills for Rebase",
6
6
  "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/rebasepro/rebase.git",
10
+ "directory": "rebase-agent-skills"
11
+ },
7
12
  "files": [
8
13
  "skills/"
9
14
  ],
@@ -11,5 +16,6 @@
11
16
  ".": "./package.json",
12
17
  "./package.json": "./package.json",
13
18
  "./skills": "./skills/"
14
- }
15
- }
19
+ },
20
+ "scripts": {}
21
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rebase-admin
3
- description: Guide for navigating the Rebase admin CMS, opening entities in side drawers, building URLs, embedding collection panels, using the collection registry, and programmatic navigation. Use this skill when an agent or user needs to navigate to a collection view, open an entity in the side panel/drawer, build admin URLs, embed a collection inside a custom page, use the entity selection dialog, or access CMS-specific controllers.
3
+ description: Guide for navigating the Rebase admin CMS, opening entities in side drawers, building URLs, embedding collection panels, using the collection registry, and programmatic navigation. Use this skill when an agent or user needs to navigate to a collection view, open a entity in the side panel/drawer, build admin URLs, embed a collection inside a custom page, use the entity selection dialog, or access CMS-specific controllers.
4
4
  ---
5
5
 
6
6
  # Rebase Admin (`@rebasepro/admin`)
@@ -13,12 +13,12 @@ The `@rebasepro/admin` package provides the CMS layer for Rebase. It handles col
13
13
 
14
14
  | Task | Hook / Component | Package |
15
15
  |------|-----------------|---------|
16
- | Open entity in side drawer | `useSideEntityController()` | `@rebasepro/admin` |
16
+ | Open entity in side drawer | `useSidePanel()` | `@rebasepro/admin` |
17
17
  | Navigate to a collection view | `useUrlController()` | `@rebasepro/admin` |
18
18
  | Look up a collection by slug | `useCollectionRegistryController()` | `@rebasepro/admin` |
19
19
  | Embed a collection in a custom page | `<CollectionPanel>` | `@rebasepro/admin` |
20
20
  | Add custom top-level views | `<RebaseCMS views={[...]}>` | `@rebasepro/admin` |
21
- | Open an entity selection dialog | `useEntitySelectionDialog()` | `@rebasepro/admin` |
21
+ | Open a entity selection dialog | `useSelectionDialog()` | `@rebasepro/admin` |
22
22
  | Open a custom side dialog | `useSideDialogsController()` | `@rebasepro/admin` |
23
23
  | Set breadcrumbs | `useBreadcrumbsController()` | `@rebasepro/admin` |
24
24
  | Access full CMS context | `useCMSContext()` | `@rebasepro/admin` |
@@ -28,16 +28,16 @@ The `@rebasepro/admin` package provides the CMS layer for Rebase. It handles col
28
28
 
29
29
  ## 1. Opening Entities in the Side Drawer
30
30
 
31
- Use `useSideEntityController()` to open, replace, or close entity side panels (the sliding drawer that shows entity forms).
31
+ Use `useSidePanel()` to open, replace, or close entity side panels (the sliding drawer that shows entity forms).
32
32
 
33
33
  ```typescript
34
- import { useSideEntityController } from "@rebasepro/admin";
34
+ import { useSidePanel } from "@rebasepro/admin";
35
35
  ```
36
36
 
37
- ### SideEntityController Interface
37
+ ### SidePanelController Interface
38
38
 
39
39
  ```typescript
40
- interface SideEntityController {
40
+ interface SidePanelController {
41
41
  /** Close the last (topmost) panel */
42
42
  close: () => void;
43
43
 
@@ -69,7 +69,7 @@ interface EntitySidePanelProps<M extends Record<string, unknown> = Record<string
69
69
  width?: number | string;
70
70
 
71
71
  /** Explicit collection config (auto-resolved from navigation if omitted) */
72
- collection?: EntityCollection<M>;
72
+ collection?: CollectionConfig<M>;
73
73
 
74
74
  /** Whether to update the browser URL when opening (default: true) */
75
75
  updateUrl?: boolean;
@@ -98,7 +98,7 @@ interface EntitySidePanelProps<M extends Record<string, unknown> = Record<string
98
98
 
99
99
  **Open an existing entity for editing:**
100
100
  ```tsx
101
- const sideEntityController = useSideEntityController();
101
+ const sideEntityController = useSidePanel();
102
102
 
103
103
  // Open the product with ID "abc123" in the side drawer
104
104
  sideEntityController.open({
@@ -236,10 +236,10 @@ import { useCollectionRegistryController } from "@rebasepro/admin";
236
236
  ```typescript
237
237
  type CollectionRegistryController<
238
238
  DB = Record<string, unknown>,
239
- EC extends EntityCollection = EntityCollection
239
+ EC extends CollectionConfig = CollectionConfig
240
240
  > = {
241
241
  /** All registered collections */
242
- collections?: EntityCollection[];
242
+ collections?: CollectionConfig[];
243
243
 
244
244
  /** Whether the registry is ready */
245
245
  initialised: boolean;
@@ -321,7 +321,7 @@ type CollectionPanelProps = {
321
321
  className?: string;
322
322
 
323
323
  /** Additional collection-level overrides */
324
- collectionOverrides?: Partial<EntityCollection>;
324
+ collectionOverrides?: Partial<CollectionConfig>;
325
325
  };
326
326
  ```
327
327
 
@@ -356,17 +356,17 @@ type CollectionPanelProps = {
356
356
 
357
357
  ## 5. Entity Selection Dialog
358
358
 
359
- Use `useEntitySelectionDialog()` to open a side dialog for selecting entities (same mechanism used by reference fields).
359
+ Use `useSelectionDialog()` to open a side dialog for selecting entities (same mechanism used by reference fields).
360
360
 
361
361
  ```typescript
362
- import { useEntitySelectionDialog } from "@rebasepro/admin";
362
+ import { useSelectionDialog } from "@rebasepro/admin";
363
363
  ```
364
364
 
365
365
  ### Usage
366
366
 
367
367
  ```tsx
368
368
  function MyComponent() {
369
- const { open, close } = useEntitySelectionDialog<Product>({
369
+ const { open, close } = useSelectionDialog<Product>({
370
370
  path: "products",
371
371
  onSingleEntitySelected: (entity) => {
372
372
  console.log("Selected:", entity.id);
@@ -378,7 +378,7 @@ function MyComponent() {
378
378
  }
379
379
  ```
380
380
 
381
- The hook accepts all `EntitySelectionProps` except `path` (which you pass separately), plus an `onClose` callback.
381
+ The hook accepts all `SelectionProps` except `path` (which you pass separately), plus an `onClose` callback.
382
382
 
383
383
  ---
384
384
 
@@ -545,7 +545,7 @@ import { useCMSContext } from "@rebasepro/admin";
545
545
 
546
546
  ```typescript
547
547
  type CMSContext = RebaseContext & {
548
- sideEntityController: SideEntityController;
548
+ sideEntityController: SidePanelController;
549
549
  sideDialogsController: SideDialogsController;
550
550
  urlController: UrlController;
551
551
  navigationStateController: NavigationStateController;
@@ -572,14 +572,14 @@ context.data; // DataSource from RebaseContext
572
572
 
573
573
  ## 10. Common Patterns
574
574
 
575
- ### Navigate to a collection and open an entity
575
+ ### Navigate to a collection and open a entity
576
576
 
577
577
  ```tsx
578
- import { useUrlController, useSideEntityController } from "@rebasepro/admin";
578
+ import { useUrlController, useSidePanel } from "@rebasepro/admin";
579
579
 
580
580
  function navigateAndOpen() {
581
581
  const urlController = useUrlController();
582
- const sideEntityController = useSideEntityController();
582
+ const sideEntityController = useSidePanel();
583
583
 
584
584
  // First navigate to the collection
585
585
  urlController.navigate(urlController.buildUrlCollectionPath("products"));
@@ -595,10 +595,10 @@ function navigateAndOpen() {
595
595
  ### Open entity from a custom view without navigating
596
596
 
597
597
  ```tsx
598
- import { useSideEntityController } from "@rebasepro/admin";
598
+ import { useSidePanel } from "@rebasepro/admin";
599
599
 
600
600
  function MyCustomView() {
601
- const sideEntityController = useSideEntityController();
601
+ const sideEntityController = useSidePanel();
602
602
 
603
603
  return (
604
604
  <button onClick={() => sideEntityController.open({
@@ -615,10 +615,10 @@ function MyCustomView() {
615
615
  ### Programmatic entity creation from a custom view
616
616
 
617
617
  ```tsx
618
- import { useSideEntityController } from "@rebasepro/admin";
618
+ import { useSidePanel } from "@rebasepro/admin";
619
619
 
620
620
  function CreateButton() {
621
- const sideEntity = useSideEntityController();
621
+ const sideEntity = useSidePanel();
622
622
 
623
623
  return (
624
624
  <button onClick={() => sideEntity.open({
@@ -754,10 +754,10 @@ The admin package exports the following components (from `@rebasepro/admin`):
754
754
  | `RebaseCMS` | Declarative CMS config (collections, views, editor) — renders nothing |
755
755
  | `RebaseShell` | App shell (drawer, nav, routes, layout) — renders the actual UI |
756
756
  | `CollectionPanel` | Embed a collection view inside custom pages |
757
- | `EntityCollectionView` | The collection view component |
758
- | `EntityView` | Entity detail/edit view |
757
+ | `DataCollectionView` | The collection view component |
758
+ | `EntityCustomView` | Entity detail/edit view |
759
759
  | `EntityPreview` | Reference/relation preview chip |
760
- | `EntityCard` | Card representation of an entity |
760
+ | `EntityCard` | Card representation of a entity |
761
761
  | `SideDialogs` | Side dialog container |
762
762
  | `SideEntityProvider` | Context provider for side entity controller |
763
763
  | `EntitySelectionTable` | Table for selecting entities |
@@ -773,16 +773,16 @@ The admin package exports the following components (from `@rebasepro/admin`):
773
773
 
774
774
  | Hook | Description |
775
775
  |------|-------------|
776
- | `useSideEntityController()` | Open/close entity side panels |
776
+ | `useSidePanel()` | Open/close entity side panels |
777
777
  | `useSideDialogsController()` | Open/close generic side dialogs |
778
778
  | `useUrlController()` | Build URLs and navigate |
779
779
  | `useNavigationStateController()` | Access navigation state |
780
780
  | `useCollectionRegistryController()` | Look up collections by slug |
781
781
  | `useBreadcrumbsController()` | Read/set breadcrumbs |
782
782
  | `useCMSContext()` | Full CMS context (core + CMS controllers) |
783
- | `useEntitySelectionDialog()` | Open entity selection dialog |
783
+ | `useSelectionDialog()` | Open entity selection dialog |
784
784
  | `useSelectionController()` | Multi-select controller |
785
- | `useEntityHistory()` | Entity version history |
785
+ | `useHistory()` | Entity version history |
786
786
  | `useApp()` | App-level utilities |
787
787
 
788
788
  ## Exported Utilities
@@ -796,8 +796,8 @@ The admin package exports the following components (from `@rebasepro/admin`):
796
796
  | `getLastSegment(path)` | Get last path segment |
797
797
  | `getCollectionBySlugWithin(collections, slug)` | Find collection in array |
798
798
  | `mergeEntityActions(...)` | Merge entity action arrays |
799
- | `resolveEntityAction(...)` | Resolve an entity action |
800
- | `resolveEntityView(...)` | Resolve an entity view |
799
+ | `resolveEntityAction(...)` | Resolve a entity action |
800
+ | `resolveEntityView(...)` | Resolve a entity view |
801
801
  | `isReferenceProperty(prop)` | Check if property is a reference |
802
802
  | `isRelationProperty(prop)` | Check if property is a relation |
803
803
  | `getIconForProperty(prop)` | Get icon for a property type |
@@ -43,8 +43,8 @@ All data routes are mounted under `/api/data/`. Other route categories:
43
43
  | `GET` | `/api/data/{slug}/count` | Count matching entities (with optional filters) | `200` |
44
44
  | `GET` | `/api/data/{slug}/:id` | Get a single entity by ID | `200` |
45
45
  | `POST` | `/api/data/{slug}` | Create a new entity | `201` |
46
- | `PUT` | `/api/data/{slug}/:id` | Update an entity | `200` |
47
- | `DELETE` | `/api/data/{slug}/:id` | Delete an entity | `204` |
46
+ | `PUT` | `/api/data/{slug}/:id` | Update a entity | `200` |
47
+ | `DELETE` | `/api/data/{slug}/:id` | Delete a entity | `204` |
48
48
 
49
49
  ### Subcollection Routes
50
50
 
@@ -405,7 +405,7 @@ curl -H "Authorization: Bearer $TOKEN" \
405
405
  "https://example.com/api/data/products?status=eq.active&price=gte.50&orderBy=created_at:desc&limit=10&offset=0&include=category"
406
406
  ```
407
407
 
408
- ### Create an entity
408
+ ### Create a entity
409
409
 
410
410
  ```bash
411
411
  curl -X POST \
@@ -415,7 +415,7 @@ curl -X POST \
415
415
  "https://example.com/api/data/products"
416
416
  ```
417
417
 
418
- ### Update an entity
418
+ ### Update a entity
419
419
 
420
420
  ```bash
421
421
  curl -X PUT \
@@ -425,7 +425,7 @@ curl -X PUT \
425
425
  "https://example.com/api/data/products/uuid-123"
426
426
  ```
427
427
 
428
- ### Delete an entity
428
+ ### Delete a entity
429
429
 
430
430
  ```bash
431
431
  curl -X DELETE \
@@ -655,7 +655,7 @@ GET /api/collections
655
655
  | `pagination.maxLimit` | `number` | `100` | Maximum allowed page size |
656
656
  | `cors.origin` | `string \| string[] \| boolean` | — | CORS origin configuration |
657
657
  | `cors.credentials` | `boolean` | `false` | Allow credentials in CORS |
658
- | `collections` | `EntityCollection[]` | `[]` | Collections to generate APIs for |
658
+ | `collections` | `CollectionConfig[]` | `[]` | Collections to generate APIs for |
659
659
  | `collectionsDir` | `string` | — | Directory to auto-discover collections |
660
660
 
661
661
  ## References
@@ -5,7 +5,7 @@ description: Guide for setting up and using Rebase Authentication, roles, Row-Le
5
5
 
6
6
  # Rebase Authentication
7
7
 
8
- Rebase ships a complete, built-in authentication system with JWT sessions, OAuth, MFA/TOTP, API keys, Row-Level Security, and lifecycle hooks — or you can plug in an external provider (Clerk, Auth0, Firebase Auth) via the `AuthAdapter` interface.
8
+ Rebase ships a complete, built-in authentication system with JWT sessions, OAuth, MFA/TOTP, API keys, Row-Level Security, and lifecycle hooks — or you can plug in an external auth system (e.g., Clerk, Auth0, or custom identity providers) via the `AuthAdapter` interface.
9
9
 
10
10
  > **IMPORTANT FOR AGENTS:** Always read the `rebase-basics` skill first. The auth system is configured inside `initializeRebaseBackend()` which is covered there.
11
11
 
@@ -33,21 +33,22 @@ Rebase ships a complete, built-in authentication system with JWT sessions, OAuth
33
33
 
34
34
  Authentication is configured via the `auth` property of `initializeRebaseBackend()`. It accepts **either** a `RebaseAuthConfig` object (built-in auth) or an `AuthAdapter` (external auth).
35
35
 
36
- > **Auth & multiple data sources.** The built-in auth system (users, sessions, API keys) is bootstrapped on the **default** data source — the auth collection must live there (the backend warns at boot otherwise). **RLS only protects Postgres**: server collections on engines without row-level security (e.g. MongoDB) still require authentication but enforce authorization at the app layer (the backend warns for these). **Direct data sources (e.g. Firestore) bypass Rebase auth entirely** — they're governed by the external backend's own rules/token; use an `AuthAdapter` (e.g. Firebase Auth) to unify identity. See the **rebase-collections** skill for the data-source model.
36
+ > **Auth & multiple data sources.** The built-in auth system (users, sessions, API keys) is bootstrapped on the **default** data source — the auth collection must live there (the backend warns at boot otherwise). **RLS only protects Postgres**: server collections on engines without row-level security (e.g. MongoDB) still require authentication but enforce authorization at the app layer (the backend warns for these). **Direct data sources (e.g. Firestore) bypass Rebase auth entirely** — they're governed by the external backend's own rules/token; use an `AuthAdapter` to unify identity. See the **rebase-collections** skill for the data-source model.
37
37
 
38
38
  ### RebaseAuthConfig
39
39
 
40
40
  | Property | Type | Default | Description |
41
41
  |---|---|---|---|
42
- | `collection` | `EntityCollection` | Built-in users collection | The collection used for auth user storage. Import `defaultUsersCollection` from `@rebasepro/common` or pass a custom collection with required auth fields. |
42
+ | `collection` | `CollectionConfig` | Built-in users collection | The collection used for auth user storage. Import `defaultUsersCollection` from `@rebasepro/common` or pass a custom collection with required auth fields. |
43
43
  | `jwtSecret` | `string` | — | **Required.** Secret for signing JWT access tokens. |
44
44
  | `accessExpiresIn` | `string` | `"1h"` | Access token lifetime (e.g. `"15m"`, `"2h"`). |
45
45
  | `refreshExpiresIn` | `string` | `"30d"` | Refresh token lifetime. |
46
46
  | `requireAuth` | `boolean` | `true` | When `true`, data routes return 401 for unauthenticated requests. Set to `false` to rely entirely on Postgres RLS. |
47
47
  | `allowRegistration` | `boolean` | `false` | Enable self-service registration via `POST /auth/register`. |
48
+ | `allowUserLookup` | `boolean` | `false` | Expose `POST /auth/find-user` — an authenticated email→minimal-profile lookup (`uid`/`displayName`/`photoURL` only) for invite flows. Enables user enumeration by signed-in users, so it's off by default. See [Inviting by email](#inviting-teammates-by-email). |
48
49
  | `serviceKey` | `string` | — | Static secret for server-to-server auth. Must be ≥ 32 characters. Requests with `Authorization: Bearer <serviceKey>` get admin access. |
49
50
  | `defaultRole` | `string` | — | Role ID assigned to new users (except the first user, who always gets `"admin"`). **Must NOT be `"admin"`** — throws a security error at startup. |
50
- | `providers` | `OAuthProvider<any>[]` | `[]` | Custom OAuth providers (use alongside the shorthand configs below). |
51
+ | `providers` | `OAuthProvider<unknown>[]` | `[]` | **Canonical** OAuth provider array. Use `create*Provider` factories or pass custom providers. Named shorthand fields below are merged into this array at startup. |
51
52
  | `hooks` | `AuthHooks` | — | [Lifecycle hooks](#auth-lifecycle-hooks) to customize passwords, credentials, and auth events. |
52
53
  | `email` | `EmailConfig` | — | [Email configuration](#email-configuration) for password resets, verification, and welcome emails. |
53
54
  | `google` | `{ clientId, clientSecret? }` | — | Google OAuth shorthand. |
@@ -107,9 +108,9 @@ await initializeRebaseBackend({
107
108
  Instead of relying solely on the default database auth rules, you can mark any Postgres collection (such as `users.ts` or a custom `members.ts` collection) as the authentication collection. This is configured via the `auth` property on the collection itself:
108
109
 
109
110
  ```typescript
110
- import { PostgresCollection } from "@rebasepro/types";
111
+ import { PostgresCollectionConfig } from "@rebasepro/types";
111
112
 
112
- const membersCollection: PostgresCollection = {
113
+ const membersCollection: PostgresCollectionConfig = {
113
114
  name: "Members",
114
115
  slug: "members",
115
116
  table: "members",
@@ -154,6 +155,27 @@ When custom hooks (`onCreateUser`, `onResetPassword`) are called, they receive a
154
155
 
155
156
  > **IMPORTANT FOR AGENTS:** The very first user registered (via `POST /auth/register` or OAuth) is automatically promoted to `"admin"`. This prevents the chicken-and-egg problem. All subsequent users receive the `defaultRole`.
156
157
 
158
+ ### Inviting teammates by email
159
+
160
+ Invite flows must turn an email into a user id, but the `users` collection is
161
+ RLS-protected from the client. **Do not** hand-roll an admin server function for
162
+ this — enable `allowUserLookup` and use the built-in primitive:
163
+
164
+ ```typescript
165
+ // backend: initializeRebaseBackend({ auth: { allowUserLookup: true } })
166
+
167
+ // client:
168
+ const profile = await rebase.auth.findUserByEmail("teammate@example.com");
169
+ // → { uid, displayName, photoURL } | null (never email/roles/metadata)
170
+ if (profile) {
171
+ await rebase.data.team_members.create({ team_id, user_id: profile.uid });
172
+ }
173
+ ```
174
+
175
+ The `find-user` endpoint is authenticated-only and returns just the minimal
176
+ public profile. It is off by default because it enables user enumeration by any
177
+ signed-in user.
178
+
157
179
  ---
158
180
 
159
181
  ## OAuth Providers
@@ -351,14 +373,14 @@ hooks: {
351
373
  }
352
374
  ```
353
375
 
354
- ### Example: Firebase Custom Token Bridge
376
+ ### Example: External Token Bridge (e.g. custom auth system)
355
377
 
356
378
  ```typescript
357
379
  import admin from "firebase-admin";
358
380
 
359
381
  hooks: {
360
382
  transformAuthResponse: async (response, context) => {
361
- // Generate a Firebase Custom Token for the authenticated user
383
+ // Generate a custom provider token for the authenticated user
362
384
  const firebaseToken = await admin.auth().createCustomToken(context.userId);
363
385
  return {
364
386
  ...response,
@@ -371,7 +393,7 @@ hooks: {
371
393
  }
372
394
  ```
373
395
 
374
- The frontend can then call `signInWithCustomToken(firebaseToken)` immediately after login.
396
+ The frontend can then call `signInWithCustomToken(providerToken)` immediately after login.
375
397
 
376
398
  ---
377
399
 
@@ -664,7 +686,7 @@ All login/register/OAuth endpoints return:
664
686
  }
665
687
  ```
666
688
 
667
- > **TIP:** Use the `transformAuthResponse` hook to inject additional tokens (e.g., Firebase Custom Tokens) or metadata into this response. See [Auth Lifecycle Hooks](#auth-lifecycle-hooks).
689
+ > **TIP:** Use the `transformAuthResponse` hook to inject additional tokens (e.g., external system tokens) or metadata into this response. See [Auth Lifecycle Hooks](#auth-lifecycle-hooks).
668
690
 
669
691
  ### Error Response Format
670
692
 
@@ -857,6 +879,21 @@ Requests with the `serviceKey` bypass RLS — they receive admin-level access wi
857
879
 
858
880
  API keys use a service identity for RLS scoping: `uid: "api-key:{id}"`, `roles: ["service"]` (or `["admin", "service"]` when `admin: true`). They do not inherit the `created_by` user's identity.
859
881
 
882
+ ### Reserved System Identities
883
+
884
+ The auth middleware assigns these reserved identities automatically. They are visible in `context.user` (Collection Callbacks / DataHooks) and `c.get("user")` (custom functions):
885
+
886
+ | Auth Method | `userId` | `roles` | When It Occurs |
887
+ |---|---|---|---|
888
+ | JWT (end-user) | Real user ID (e.g. `"abc123"`) | User's assigned roles (e.g. `["viewer"]`) | Normal authenticated requests |
889
+ | Service Key | `"service"` | `["admin"]` | Server-side `rebase.data` calls, cron jobs, or any request with `Authorization: Bearer <serviceKey>` |
890
+ | API Key (default) | `"api-key:{id}"` | `["service"]` | Machine-to-machine API key requests |
891
+ | API Key (admin) | `"api-key:{id}"` | `["admin", "service"]` | Admin API key requests |
892
+ | Anonymous | `"anon"` | `["anon"]` | Unauthenticated when `requireAuth: false` |
893
+ | No token + `requireAuth: true` | — | — | **Rejected (401)** |
894
+
895
+ > **IMPORTANT FOR AGENTS:** Server-side `rebase.data` (the singleton used in cron jobs, custom functions, and webhooks) is built with `createRebaseClient({ token: serviceKey })`. It round-trips through the REST API, so all middleware, DataHooks, and Collection Callbacks fire with `userId: "service"`, `roles: ["admin"]`. This lets developers distinguish server-internal reads from end-user reads in callbacks.
896
+
860
897
  ---
861
898
 
862
899
  ## Rate Limiting
@@ -917,7 +954,7 @@ const myLimiter = createRateLimiter({
917
954
 
918
955
  ## Custom Auth Adapters
919
956
 
920
- For external auth systems (Clerk, Auth0, Firebase Auth, or custom JWT), use the `AuthAdapter` interface or the `createCustomAuthAdapter()` helper.
957
+ For external auth systems (Clerk, Auth0, custom providers, or custom JWT), use the `AuthAdapter` interface or the `createCustomAuthAdapter()` helper.
921
958
 
922
959
  ### AuthAdapter Interface
923
960
 
@@ -1077,7 +1114,7 @@ Admin user and role management is handled via dedicated admin routes (mounted un
1077
1114
 
1078
1115
  ## Backend Hooks
1079
1116
 
1080
- Backend hooks intercept data at the **API boundary** (after DB operations, before API responses). They are separate from auth hooks and collection-level `EntityCallbacks`.
1117
+ Backend hooks intercept data at the **API boundary** (after DB operations, before API responses). They are separate from auth hooks and collection-level `CollectionCallbacks`.
1081
1118
 
1082
1119
  ### BackendHooks Interface
1083
1120
 
@@ -1241,6 +1278,8 @@ When a request includes `Authorization: Bearer <serviceKey>`:
1241
1278
  - Comparison is done with constant-time comparison to prevent timing attacks.
1242
1279
  - Must be ≥ 32 characters (validated at startup).
1243
1280
 
1281
+ > **TIP:** In Collection Callbacks and DataHooks, server-side `rebase.data` calls appear as `userId: "service"`, `roles: ["admin"]`. Use this to skip masking, bypass rate limits, or grant elevated access in your callback logic.
1282
+
1244
1283
  ### Token Rotation
1245
1284
 
1246
1285
  Refresh tokens are rotated on every use:
@@ -1281,3 +1320,4 @@ All auth endpoints validate input with Zod schemas:
1281
1320
  - Source: `packages/types/src/types/auth_adapter.ts` — `AuthAdapter` interface
1282
1321
  - Source: `packages/server-core/src/auth/rls-scope.ts` — RLS scoping
1283
1322
  - Source: `packages/server-core/src/email/types.ts` — Email configuration
1323
+ - **Reserved Identities**: `"service"` / `"anon"` / `"api-key:{id}"` — see [Row-Level Security > Reserved System Identities](#reserved-system-identities)
@@ -69,7 +69,7 @@ rebase db migrate
69
69
  |---------|---------|
70
70
  | `@rebasepro/server-core` | Hono server coordinator, API generation, auth, storage, env validation |
71
71
  | `@rebasepro/server-postgresql` | PostgreSQL bootstrapper, data driver, connection helpers, realtime (LISTEN/NOTIFY) |
72
- | `@rebasepro/types` | Shared TypeScript type definitions (`PostgresCollection`, etc.) |
72
+ | `@rebasepro/types` | Shared TypeScript type definitions (`PostgresCollectionConfig`, etc.) |
73
73
 
74
74
  ## Connection Functions
75
75
 
@@ -339,7 +339,7 @@ The `initializeRebaseBackend()` coordinator calls bootstrapper methods in this o
339
339
 
340
340
  | Property | Type | Default | Description |
341
341
  |----------|------|---------|-------------|
342
- | `collection` | `EntityCollection` | Built-in `rebase.users` | Auth users collection |
342
+ | `collection` | `CollectionConfig` | Built-in `rebase.users` | Auth users collection |
343
343
  | `jwtSecret` | `string` | Auto-generated in dev | JWT signing secret (≥32 chars) |
344
344
  | `accessExpiresIn` | `string` | `"1h"` | Access token TTL |
345
345
  | `refreshExpiresIn` | `string` | `"30d"` | Refresh token TTL |
@@ -514,7 +514,7 @@ const backend = await initializeRebaseBackend({
514
514
 
515
515
  When enabled:
516
516
  - The bootstrapper auto-creates a `rebase.entity_history` table
517
- - Every `INSERT`, `UPDATE`, `DELETE` is recorded with before/after snapshots
517
+ - Every `INSERT`, `UPDATE`, `DELETE` is recorded with before/after entities
518
518
  - History is queryable via the `/:slug/:entityId/history` REST endpoint
519
519
 
520
520
  ## Health Check
@@ -125,7 +125,7 @@ rebase/
125
125
  | `@rebasepro/server-postgresql` | PostgreSQL bootstrapper and Drizzle ORM data driver | Backend setup when using PostgreSQL |
126
126
  | `@rebasepro/server-mongodb` | MongoDB bootstrapper and data driver | Backend setup when using MongoDB |
127
127
  | `@rebasepro/core` | Core framework, types, hooks, and React components | Frontend — React integration, hooks, providers |
128
- | `@rebasepro/types` | Shared TypeScript type definitions (`PostgresCollection`, `EntityCollection`, `RebaseClient`, etc.) | Type imports across all packages |
128
+ | `@rebasepro/types` | Shared TypeScript type definitions (`PostgresCollectionConfig`, `CollectionConfig`, `RebaseClient`, etc.) | Type imports across all packages |
129
129
  | `@rebasepro/ui` | Standalone component library (Tailwind CSS v4 + Radix) | Building custom views in Studio or standalone UI |
130
130
  | `@rebasepro/admin` | CMS frontend application | The Studio admin panel frontend |
131
131
  | `@rebasepro/studio` | Admin panel, collection editor, visual schema editor | Studio-specific features and customization |
@@ -388,7 +388,7 @@ When `extend` is provided, the base `rebaseEnvSchema` is merged (`.merge()`) wit
388
388
  | `DB_POOL_MAX` | `string` → `number` | `"20"` | No | Max database pool connections |
389
389
  | `DB_POOL_IDLE_TIMEOUT` | `string` → `number` | `"30000"` | No | Pool idle timeout (ms) |
390
390
  | `DB_POOL_CONNECT_TIMEOUT` | `string` → `number` | `"10000"` | No | Pool connect timeout (ms) |
391
- | `STORAGE_TYPE` | `"local" \| "s3"` | `"local"` | No | File storage backend type |
391
+ | `STORAGE_TYPE` | `"local" \| "s3" \| "gcs"` | `"local"` | No | File storage backend type |
392
392
  | `STORAGE_PATH` | `string` | — | No | Local storage directory path |
393
393
  | `FORCE_LOCAL_STORAGE` | `"true" \| "false"` | — | No | Force local storage even in production |
394
394
  | `S3_BUCKET` | `string` | — | When S3 | S3 bucket name |
@@ -397,6 +397,9 @@ When `extend` is provided, the base `rebaseEnvSchema` is merged (`.merge()`) wit
397
397
  | `S3_SECRET_ACCESS_KEY` | `string` | — | When S3 | S3 secret key |
398
398
  | `S3_ENDPOINT` | `string` (URL) | — | No | Custom S3 endpoint (MinIO, R2, etc.) |
399
399
  | `S3_FORCE_PATH_STYLE` | `"true" \| "false"` | — | No | Use path-style S3 URLs |
400
+ | `GCS_BUCKET` | `string` | — | When GCS | GCS/Firebase Storage bucket name |
401
+ | `GCS_PROJECT_ID` | `string` | — | When GCS | GCP project ID |
402
+ | `GOOGLE_APPLICATION_CREDENTIALS` | `string` (path) | — | When GCS | Path to GCP service account JSON |
400
403
 
401
404
  ### Production Validations
402
405
 
@@ -431,14 +434,14 @@ import { initializeRebaseBackend, RebaseBackendConfig } from "@rebasepro/server-
431
434
  |----------|------|---------|-------------|
432
435
  | `server` | `Server` (Node `http.Server`) | — | **Required.** The HTTP server instance |
433
436
  | `app` | `Hono<HonoEnv>` | — | **Required.** The Hono application instance |
434
- | `collections` | `EntityCollection[]` | `[]` | Inline collection definitions |
437
+ | `collections` | `CollectionConfig[]` | `[]` | Inline collection definitions |
435
438
  | `collectionsDir` | `string` | — | Directory to auto-discover collection files (used if `collections` is empty) |
436
439
  | `basePath` | `string` | `"/api"` | Base path for all API routes |
437
440
  | `database` | `DatabaseAdapter` | — | Database adapter (takes precedence over `bootstrappers`) |
438
441
  | `bootstrappers` | `BackendBootstrapper[]` | `[]` | Database bootstrappers. Use one per engine for multiple engines in a single instance (e.g. Postgres + MongoDB); mark one `isDefault` |
439
442
  | `dataSources` | `DataSourceDefinition[]` | `[]` | Declared data sources (`key`, `engine`, `transport`). Drives capabilities and the server-vs-direct distinction. Collections on a `direct`/`custom` transport are client-only — the backend skips data routes for them. Server engines need no entry |
440
443
  | `auth` | `RebaseAuthConfig \| AuthAdapter` | — | Authentication config or pluggable adapter |
441
- | `storage` | `BackendStorageConfig \| StorageController \| Record<string, ...>` | — | File storage configuration |
444
+ | `storage` | `BackendStorageConfig \| StorageController \| Record<string, ...>` | — | File storage configuration. Supports `"local"`, `"s3"`, and `"gcs"` (GCS/Firebase Storage) backends. Use `Record<string, StorageController>` for multi-backend setups with named sources |
442
445
  | `history` | `unknown` | — | Entity history/audit-log configuration |
443
446
  | `defaultSecurityRules` | `SecurityRule[]` | — | Default RLS rules for collections without their own |
444
447
  | `enableSwagger` | `boolean` | `true` | Enable OpenAPI spec at `/api/docs` and Swagger UI at `/api/swagger` (dev only) |
@@ -456,7 +459,7 @@ import { initializeRebaseBackend, RebaseBackendConfig } from "@rebasepro/server-
456
459
 
457
460
  | Property | Type | Default | Description |
458
461
  |----------|------|---------|-------------|
459
- | `collection` | `EntityCollection` | `defaultUsersCollection` | The collection used for auth users |
462
+ | `collection` | `CollectionConfig` | `defaultUsersCollection` | The collection used for auth users |
460
463
  | `jwtSecret` | `string` | — | JWT signing secret (≥32 chars) |
461
464
  | `accessExpiresIn` | `string` | `"1h"` | Access token TTL |
462
465
  | `refreshExpiresIn` | `string` | `"30d"` | Refresh token TTL |
@@ -546,6 +549,8 @@ await initializeRebaseBackend({
546
549
  ? { clientId: env.GOOGLE_CLIENT_ID }
547
550
  : undefined,
548
551
  },
552
+ // Single backend: { type: "local" | "s3" | "gcs" }
553
+ // Multi-backend: Record<string, StorageController> with named sources
549
554
  storage: { type: env.STORAGE_TYPE },
550
555
  defaultSecurityRules: [
551
556
  { operation: "select", access: "public" },
@@ -649,7 +654,7 @@ The Rebase MCP server provides these tools for AI agents. Use the MCP tool calli
649
654
  | `rebase_schema_generate` | Generate Drizzle schema from collection definitions. Run after adding or modifying collection files | — |
650
655
  | `rebase_schema_introspect` | Introspect the live database and generate Rebase collection definitions from existing tables | — |
651
656
  | `rebase_db_push` | Apply the current Drizzle schema directly to the database (development shortcut, skips migration files) | — |
652
- | `rebase_db_generate` | Generate SQL migration files from schema changes (compares current Drizzle schema against the last snapshot) | — |
657
+ | `rebase_db_generate` | Generate SQL migration files from schema changes (compares current Drizzle schema against the last entity) | — |
653
658
  | `rebase_db_migrate` | Run all pending SQL migrations against the database | — |
654
659
 
655
660
  ### SDK