From e117fa568fda8ec16420fde60245a51f9d5ed458 Mon Sep 17 00:00:00 2001 From: "Sam (automated drift fix)" Date: Wed, 24 Jun 2026 18:34:48 +0300 Subject: [PATCH 1/2] docs(service-role): describe RLS bypass and fix copy script for i18n docs.json `asServiceRole` now bypasses entity RLS and field-level security entirely (backend rolled out via the `service-role-bypass-rls` flag). Reword JSDoc on the client and entities module to describe this directly, drop the obsolete "admin role" framing from agents/integrations/functions where the PR did not change behavior, and replace the User-list example comment with one that reflects the new semantics. Also fix `copy-to-local-docs.js` so it works with mintlify-docs's i18n `navigation.languages[].tabs` layout. The previous version crashed at the docs.json update step because it assumed the legacy top-level `tabs`. The new version walks every locale, locates the SDK Reference group by content (pages referencing `/sdk/docs/`), preserves each locale's translated group and subgroup labels, and points every locale at the same English mdx files (matching what the docs site shows in practice). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../copy-to-local-docs.js | 89 ++++++++++++------- src/client.ts | 12 +-- src/client.types.ts | 4 +- src/modules/agents.types.ts | 2 +- src/modules/entities.types.ts | 4 +- src/modules/functions.types.ts | 2 +- src/modules/integrations.types.ts | 2 +- 7 files changed, 70 insertions(+), 45 deletions(-) diff --git a/scripts/mintlify-post-processing/copy-to-local-docs.js b/scripts/mintlify-post-processing/copy-to-local-docs.js index 6e6b87ff..f6c4a96f 100644 --- a/scripts/mintlify-post-processing/copy-to-local-docs.js +++ b/scripts/mintlify-post-processing/copy-to-local-docs.js @@ -160,47 +160,72 @@ function updateDocsJson(repoDir, sdkFiles) { `SDK Reference pages: ${JSON.stringify(sdkReferencePages, null, 2)}` ); - // Navigate to: Developers tab -> SDK section -> groups -> SDK Reference - // Supports both legacy "anchors" format and current "dropdowns" format. - const developersTab = docs.navigation.tabs.find( - (tab) => tab.tab === "Developers" - ); - - if (!developersTab) { - console.error("Could not find 'Developers' tab in docs.json"); + // docs.json supports three navigation shapes we've seen in the wild: + // 1. top-level tabs (legacy) navigation.tabs + // 2. top-level tabs with dropdowns/anchors navigation.tabs[].dropdowns | .anchors + // 3. i18n layout (current) navigation.languages[].tabs[]... + // The SDK reference mdx files are English-only, so for every locale we point its + // SDK Reference group at the same English paths. This is what the docs site + // effectively shows today anyway. + // + // We locate the target group by content (any group whose pages reference + // `/sdk/docs/`) rather than by tab/group name, because the surrounding labels + // are translated per locale while the "SDK" dropdown id and the page paths + // stay stable. Preserves the existing translated group label. + const tabsContainers = + docs.navigation.languages?.map((l) => l.tabs).filter(Boolean) ?? + [docs.navigation.tabs].filter(Boolean); + + if (tabsContainers.length === 0) { + console.error("Could not find navigation.tabs or navigation.languages in docs.json"); process.exit(1); } - // Find the SDK section (try dropdowns first, then fall back to anchors) - const sdkAnchor = - developersTab.dropdowns?.find((d) => d.dropdown === "SDK") ?? - developersTab.anchors?.find((a) => a.anchor === "SDK"); - - if (!sdkAnchor) { - console.error("Could not find 'SDK' dropdown or anchor in Developers tab"); - process.exit(1); + const groupReferencesSdkDocs = (group) => + JSON.stringify(group).includes(`${basePath}/`); + + let updatedCount = 0; + for (const tabs of tabsContainers) { + for (const tab of tabs) { + const sdkAnchor = + tab.dropdowns?.find((d) => d.dropdown === "SDK") ?? + tab.anchors?.find((a) => a.anchor === "SDK"); + if (!sdkAnchor?.groups) continue; + + const sdkRefIndex = sdkAnchor.groups.findIndex(groupReferencesSdkDocs); + if (sdkRefIndex === -1) continue; + + const existing = sdkAnchor.groups[sdkRefIndex]; + + // Preserve existing subgroup labels (translated per locale) by position. + // Falls back to the English category-map label when no existing subgroup + // sits at that index (e.g. a locale that gains a new subgroup). + const existingSubgroups = Array.isArray(existing.pages) ? existing.pages : []; + const localizedPages = sdkReferencePages.map((g, i) => ({ + ...g, + group: existingSubgroups[i]?.group ?? g.group, + })); + + sdkAnchor.groups[sdkRefIndex] = { + ...existing, + group: existing.group, + icon: existing.icon ?? "brackets-curly", + expanded: true, + pages: localizedPages, + }; + updatedCount++; + } } - // Find SDK Reference within the SDK anchor's groups - const sdkRefIndex = sdkAnchor.groups.findIndex( - (g) => g.group === "SDK Reference" - ); - - if (sdkRefIndex === -1) { - console.error("Could not find 'SDK Reference' group in SDK anchor"); + if (updatedCount === 0) { + console.error( + "Could not find any SDK Reference navigation group to update (looked for groups whose pages reference '/sdk/docs/')" + ); process.exit(1); } - // Update the SDK Reference pages with our generated groups - sdkAnchor.groups[sdkRefIndex] = { - group: "SDK Reference", - icon: "brackets-curly", - expanded: true, - pages: sdkReferencePages, - }; - // Write updated docs.json - console.log(`Writing updated docs.json to ${docsJsonPath}...`); + console.log(`Writing updated docs.json to ${docsJsonPath} (${updatedCount} locale(s))...`); fs.writeFileSync(docsJsonPath, JSON.stringify(docs, null, 2) + "\n", "utf8"); console.log("Successfully updated docs.json"); diff --git a/src/client.ts b/src/client.ts index a028f337..3965f295 100644 --- a/src/client.ts +++ b/src/client.ts @@ -35,12 +35,12 @@ export type { Base44Client, CreateClientConfig, CreateClientOptions }; * The client supports three authentication modes: * - **Anonymous**: Access modules without authentication using `base44.moduleName`. Operations are scoped to public data and permissions. * - **User authentication**: Access modules with user-level permissions using `base44.moduleName`. Operations are scoped to the authenticated user's data and permissions. Use `base44.auth.loginViaEmailPassword()` or other auth methods to get a token. - * - **Service role authentication**: Access modules with elevated permissions using `base44.asServiceRole.moduleName`. Operations can access any data available to the app's admin. Only available in Base44-hosted backend functions. Create a client with service role authentication using {@linkcode createClientFromRequest | createClientFromRequest()}. + * - **Service role authentication**: Access modules with elevated permissions using `base44.asServiceRole.moduleName`. Operations bypass entity access rules and field-level security, giving full read and write access to all of the app's data. Only available in Base44-hosted backend functions. Create a client with service role authentication using {@linkcode createClientFromRequest | createClientFromRequest()}. * * For example, when using the {@linkcode EntitiesModule | entities} module: * - **Anonymous**: Can only read public data. * - **User authentication**: Can access the current user's data. - * - **Service role authentication**: Can access all data that admins can access. + * - **Service role authentication**: Can read and write any record, bypassing access rules. * * Most modules are available in all three modes, but with different permission levels. However, some modules are only available in specific authentication modes. * @@ -302,7 +302,7 @@ export function createClient(config: CreateClientConfig): Base44Client { /** * Provides access to service role modules. * - * Service role authentication provides elevated permissions for backend operations. Unlike user authentication, which is scoped to a specific user's permissions, service role authentication has access to the data and operations available to the app's admin. + * Service role authentication provides elevated permissions for backend operations. Unlike user authentication, which is scoped to a specific user's permissions, service role authentication bypasses entity access rules and field-level security entirely, giving full read and write access to all of the app's data. * * @throws {Error} When accessed without providing a serviceToken during client creation. * @@ -313,7 +313,7 @@ export function createClient(config: CreateClientConfig): Base44Client { * serviceToken: 'service-role-token' * }); * - * // Also access a module with elevated permissions + * // Read every user record, bypassing the User entity's access rules * const allUsers = await base44.asServiceRole.entities.User.list(); * ``` */ @@ -335,7 +335,7 @@ export function createClient(config: CreateClientConfig): Base44Client { * * This function is designed for use in Base44-hosted backend functions. For frontends and external backends, use {@linkcode createClient | createClient()} instead. * - * When used in a Base44-hosted backend function, `createClientFromRequest()` automatically extracts authentication tokens from the request headers that Base44 injects when forwarding requests. The returned client includes service role access using `base44.asServiceRole`, which provides admin-level permissions. + * When used in a Base44-hosted backend function, `createClientFromRequest()` automatically extracts authentication tokens from the request headers that Base44 injects when forwarding requests. The returned client includes service role access using `base44.asServiceRole`, which bypasses entity access rules and field-level security. * * To learn more about the Base44 client, see {@linkcode createClient | createClient()}. * @@ -374,7 +374,7 @@ export function createClient(config: CreateClientConfig): Base44Client { * try { * const base44 = createClientFromRequest(req); * - * // Access admin data with service role permissions + * // Read across all users, bypassing the Orders entity's access rules * const recentOrders = await base44.asServiceRole.entities.Orders.list('-created_at', 50); * * return Response.json({ orders: recentOrders }); diff --git a/src/client.types.ts b/src/client.types.ts index 6b4c9c57..44c2a6c1 100644 --- a/src/client.types.ts +++ b/src/client.types.ts @@ -54,7 +54,7 @@ export interface CreateClientConfig { */ token?: string; /** - * Service role authentication token. Provides elevated permissions to access data available to the app's admin. Only available in Base44-hosted backend functions. Automatically added to client's created using {@linkcode createClientFromRequest | createClientFromRequest()}. + * Service role authentication token. Provides elevated permissions that bypass entity access rules and field-level security. Only available in Base44-hosted backend functions. Automatically added to clients created using {@linkcode createClientFromRequest | createClientFromRequest()}. * @internal */ serviceToken?: string; @@ -122,7 +122,7 @@ export interface Base44Client { /** * Provides access to supported modules with elevated permissions. * - * Service role authentication provides elevated permissions for backend operations. Unlike user authentication, which is scoped to a specific user's permissions, service role authentication has access to the data and operations available to the app's admin. + * Service role authentication provides elevated permissions for backend operations. Unlike user authentication, which is scoped to a specific user's permissions, service role authentication bypasses entity access rules and field-level security entirely, giving full read and write access to all of the app's data. * * @throws {Error} When accessed without providing a serviceToken during client creation */ diff --git a/src/modules/agents.types.ts b/src/modules/agents.types.ts index 49918c0c..689411b0 100644 --- a/src/modules/agents.types.ts +++ b/src/modules/agents.types.ts @@ -207,7 +207,7 @@ export interface AgentsModuleConfig { * This module is available to use with a client in all authentication modes: * * - **Anonymous or User authentication** (`base44.agents`): Access is scoped to the current user's permissions. Users must be authenticated to create and access conversations. - * - **Service role authentication** (`base44.asServiceRole.agents`): Operations have elevated admin-level permissions. Can access all conversations that the app's admin role has access to. + * - **Service role authentication** (`base44.asServiceRole.agents`): Operations run with elevated permissions for backend code that isn't tied to a specific user session. * * ## Generated Types * diff --git a/src/modules/entities.types.ts b/src/modules/entities.types.ts index c5854dd3..f552af63 100644 --- a/src/modules/entities.types.ts +++ b/src/modules/entities.types.ts @@ -724,7 +724,7 @@ type DynamicEntitiesModule = { * This module is available to use with a client in all authentication modes: * * - **Anonymous or User authentication** (`base44.entities`): Access is scoped to the current user's permissions. Anonymous users can only access public entities, while authenticated users can access entities they have permission to view or modify. - * - **Service role authentication** (`base44.asServiceRole.entities`): Operations have elevated admin-level permissions. Can access all entities that the app's admin role has access to. + * - **Service role authentication** (`base44.asServiceRole.entities`): Operations bypass entity access rules and field-level security entirely. Can read and write any record in any entity. * * ## Entity Handlers * @@ -753,7 +753,7 @@ type DynamicEntitiesModule = { * * @example * ```typescript - * // List all users (admin only) + * // List every user, bypassing the User entity's access rules * const allUsers = await base44.asServiceRole.entities.User.list(); * ``` */ diff --git a/src/modules/functions.types.ts b/src/modules/functions.types.ts index b4e8b305..ccc7e025 100644 --- a/src/modules/functions.types.ts +++ b/src/modules/functions.types.ts @@ -45,7 +45,7 @@ export interface FunctionsModuleConfig { * This module is available to use with a client in all authentication modes: * * - **Anonymous or User authentication** (`base44.functions`): Functions are invoked with the current user's permissions. Anonymous users invoke functions without authentication, while authenticated users invoke functions with their authentication context. - * - **Service role authentication** (`base44.asServiceRole.functions`): Functions are invoked with elevated admin-level permissions. The function code receives a request with admin authentication context. + * - **Service role authentication** (`base44.asServiceRole.functions`): Functions are invoked with elevated permissions and no authenticated user. The function code receives a request with no user context. * * ## Generated Types * diff --git a/src/modules/integrations.types.ts b/src/modules/integrations.types.ts index d7e126c2..801019e7 100644 --- a/src/modules/integrations.types.ts +++ b/src/modules/integrations.types.ts @@ -393,7 +393,7 @@ export interface CoreIntegrations { * This module is available to use with a client in all authentication modes: * * - **Anonymous or User authentication** (`base44.integrations`): Integration methods are invoked with the current user's permissions. Anonymous users invoke methods without authentication, while authenticated users invoke methods with their authentication context. - * - **Service role authentication** (`base44.asServiceRole.integrations`): Integration methods are invoked with elevated admin-level permissions. The methods execute with admin authentication context. + * - **Service role authentication** (`base44.asServiceRole.integrations`): Integration methods are invoked with elevated permissions for backend code that isn't tied to a specific user session. */ export type IntegrationsModule = { /** From 9de176d3c412a3b628cc06def988a5aa4418cc6b Mon Sep 17 00:00:00 2001 From: "Sam (automated drift fix)" Date: Sun, 28 Jun 2026 10:29:16 +0300 Subject: [PATCH 2/2] docs(service-role): drop "no user session" claim from agents/integrations/functions A synthetic user is still used to process service-role requests; the PR only adds RLS bypass on top, and only for entity CRUD. Reframe these three module descriptions as "service role for backend code that needs elevated permissions" rather than implying there's no user context. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/modules/agents.types.ts | 2 +- src/modules/functions.types.ts | 2 +- src/modules/integrations.types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/agents.types.ts b/src/modules/agents.types.ts index 689411b0..51e079da 100644 --- a/src/modules/agents.types.ts +++ b/src/modules/agents.types.ts @@ -207,7 +207,7 @@ export interface AgentsModuleConfig { * This module is available to use with a client in all authentication modes: * * - **Anonymous or User authentication** (`base44.agents`): Access is scoped to the current user's permissions. Users must be authenticated to create and access conversations. - * - **Service role authentication** (`base44.asServiceRole.agents`): Operations run with elevated permissions for backend code that isn't tied to a specific user session. + * - **Service role authentication** (`base44.asServiceRole.agents`): Operations are invoked with the service role for backend code that needs elevated permissions. * * ## Generated Types * diff --git a/src/modules/functions.types.ts b/src/modules/functions.types.ts index ccc7e025..65e5f0d4 100644 --- a/src/modules/functions.types.ts +++ b/src/modules/functions.types.ts @@ -45,7 +45,7 @@ export interface FunctionsModuleConfig { * This module is available to use with a client in all authentication modes: * * - **Anonymous or User authentication** (`base44.functions`): Functions are invoked with the current user's permissions. Anonymous users invoke functions without authentication, while authenticated users invoke functions with their authentication context. - * - **Service role authentication** (`base44.asServiceRole.functions`): Functions are invoked with elevated permissions and no authenticated user. The function code receives a request with no user context. + * - **Service role authentication** (`base44.asServiceRole.functions`): Functions are invoked with the service role for backend code that needs elevated permissions. * * ## Generated Types * diff --git a/src/modules/integrations.types.ts b/src/modules/integrations.types.ts index 801019e7..89369e7e 100644 --- a/src/modules/integrations.types.ts +++ b/src/modules/integrations.types.ts @@ -393,7 +393,7 @@ export interface CoreIntegrations { * This module is available to use with a client in all authentication modes: * * - **Anonymous or User authentication** (`base44.integrations`): Integration methods are invoked with the current user's permissions. Anonymous users invoke methods without authentication, while authenticated users invoke methods with their authentication context. - * - **Service role authentication** (`base44.asServiceRole.integrations`): Integration methods are invoked with elevated permissions for backend code that isn't tied to a specific user session. + * - **Service role authentication** (`base44.asServiceRole.integrations`): Integration methods are invoked with the service role for backend code that needs elevated permissions. */ export type IntegrationsModule = { /**