From f9cbf661ca6e47b62669ab77742cdf758b590d44 Mon Sep 17 00:00:00 2001
From: Ivan Vasilov
Date: Mon, 5 Jan 2026 07:00:49 +0200
Subject: [PATCH 1/5] fix: Fix a bug in Edit FDW sheet (#41685)
* Add types to the getDecryptedValues function.
* Refactor the edit-wrapper-sheet to fetch the secrets by id, instead of name.
* Add try/catch for the secret values fetching.
* Minor CodeRabbit nitpicks.
* Small improvement for types in EditWrapperSheet
* Small fix for WrapperRow icon space issue hiding ChevronRight
* Update comment
* Only fetch encrypted IDs if value is a valid UUID
* Nit
---------
Co-authored-by: Joshen Lim
---
.../Wrappers/EditWrapperSheet.tsx | 110 ++++++++++--------
.../Integrations/Wrappers/WrapperRow.tsx | 25 ++--
.../Integrations/Wrappers/WrapperTable.tsx | 3 +-
apps/studio/data/auth/user-query.ts | 3 +-
.../vault-secret-decrypted-value-query.ts | 14 ++-
apps/studio/lib/constants/index.ts | 2 +
6 files changed, 88 insertions(+), 69 deletions(-)
diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/EditWrapperSheet.tsx b/apps/studio/components/interfaces/Integrations/Wrappers/EditWrapperSheet.tsx
index b159095c48aee..c659e6d4e3935 100644
--- a/apps/studio/components/interfaces/Integrations/Wrappers/EditWrapperSheet.tsx
+++ b/apps/studio/components/interfaces/Integrations/Wrappers/EditWrapperSheet.tsx
@@ -1,15 +1,15 @@
import { useQueryClient } from '@tanstack/react-query'
-import { isEmpty } from 'lodash'
+import { compact, isEmpty, mapValues } from 'lodash'
import { Edit, Trash } from 'lucide-react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { toast } from 'sonner'
+import { UUID_REGEX } from '@/lib/constants'
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection'
import { invalidateSchemasQuery } from 'data/database/schemas-query'
import { useFDWUpdateMutation } from 'data/fdw/fdw-update-mutation'
import { FDW } from 'data/fdw/fdws-query'
-import { getDecryptedValue } from 'data/vault/vault-secret-decrypted-value-query'
-import { useVaultSecretsQuery } from 'data/vault/vault-secrets-query'
+import { getDecryptedValues } from 'data/vault/vault-secret-decrypted-value-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useConfirmOnClose, type ConfirmOnCloseModalProps } from 'hooks/ui/useConfirmOnClose'
import { Button, Form, Input, SheetFooter, SheetHeader, SheetTitle } from 'ui'
@@ -44,11 +44,6 @@ export const EditWrapperSheet = ({
const queryClient = useQueryClient()
const { data: project } = useSelectedProjectQuery()
- const { data: secrets, isPending: isSecretsLoading } = useVaultSecretsQuery({
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- })
-
const { mutate: updateFDW, isPending: isSaving } = useFDWUpdateMutation({
onSuccess: () => {
toast.success(`Successfully updated ${wrapperMeta?.label} foreign data wrapper`)
@@ -75,7 +70,7 @@ export const EditWrapperSheet = ({
...convertKVStringArrayToJson(wrapper?.server_options ?? []),
}
- const onUpdateTable = (values: any) => {
+ const onUpdateTable = (values: FormattedWrapperTable) => {
setWrapperTables((prev) => {
// if the new values have tableIndex, we are editing an existing table
if (values.tableIndex !== undefined) {
@@ -91,9 +86,9 @@ export const EditWrapperSheet = ({
setSelectedTableToEdit(undefined)
}
- const onSubmit = async (values: any) => {
+ const onSubmit = async (values: Record) => {
const validate = makeValidateRequired(wrapperMeta.server.options)
- const errors: any = validate(values)
+ const errors = validate(values)
const { wrapper_name } = values
if (wrapper_name.length === 0) errors.name = 'Please provide a name for your wrapper'
@@ -137,7 +132,15 @@ export const EditWrapperSheet = ({
onSubmit={onSubmit}
className="h-full flex flex-col"
>
- {({ values, initialValues, resetForm }: any) => {
+ {({
+ values,
+ initialValues,
+ resetForm,
+ }: {
+ values: Record
+ initialValues: Record
+ resetForm: (value: Record>) => void
+ }) => {
// [Alaister] although this "technically" is breaking the rules of React hooks
// it won't error because the hooks are always rendered in the same order
// eslint-disable-next-line react-hooks/rules-of-hooks
@@ -152,50 +155,60 @@ export const EditWrapperSheet = ({
const hasChanges = hasFormChanges || hasTableChanges
hasChangesRef.current = hasChanges
- const encryptedOptions = wrapperMeta.server.options.filter((option) => option.encrypted)
-
// [Alaister] although this "technically" is breaking the rules of React hooks
// it won't error because the hooks are always rendered in the same order
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
- const fetchEncryptedValues = async () => {
- setLoadingSecrets(true)
- // If the secrets haven't loaded, escape and run the effect again when they're loaded
- if (isSecretsLoading) {
- return
- }
+ const fetchEncryptedValues = async (ids: string[]) => {
+ try {
+ setLoadingSecrets(true)
+ // If the secrets haven't loaded, escape and run the effect again when they're loaded
+ const decryptedValues = await getDecryptedValues({
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ ids: ids,
+ })
- const res = await Promise.all(
- encryptedOptions.map(async (option) => {
- const secret = secrets?.find(
- (secret) => secret.name === `${wrapper.name}_${option.name}`
- )
- if (secret !== undefined) {
- const value = await getDecryptedValue({
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- id: secret.id,
- })
- return { [option.name]: value[0]?.decrypted_secret ?? '' }
- } else {
- return { [option.name]: '' }
- }
+ // replace all values which are in the decryptedValues object with the decrypted value
+ const transformValues = (values: Record) => {
+ return mapValues(values, (value) => {
+ return decryptedValues[value] ?? value
+ })
+ }
+
+ resetForm({
+ values: transformValues(values),
+ initialValues: transformValues(initialValues),
})
- )
- const secretValues = res.reduce((a: any, b: any) => {
- const [key] = Object.keys(b)
- return { ...a, [key]: b[key] }
- }, {})
+ } catch (error) {
+ toast.error('Failed to fetch encrypted values')
+ } finally {
+ setLoadingSecrets(false)
+ }
+ }
- resetForm({
- values: { ...values, ...secretValues },
- initialValues: { ...initialValues, ...secretValues },
+ const encryptedOptions = wrapperMeta.server.options.filter(
+ (option) => option.encrypted
+ )
+
+ const encryptedIdsToFetch = compact(
+ encryptedOptions.map((option) => {
+ const value = initialValues[option.name]
+ return value ?? null
})
- setLoadingSecrets(false)
- }
+ ).filter((x) => UUID_REGEX.test(x))
+ // [Joshen] ^ Validate UUID to filter out already decrypted values
- if (encryptedOptions.length > 0) fetchEncryptedValues()
- }, [isSecretsLoading])
+ if (encryptedIdsToFetch.length > 0) {
+ fetchEncryptedValues(encryptedIdsToFetch)
+ }
+ /**
+ * [Joshen] We're deliberately not adding values and initialValues to the dependency array here
+ * as we only want to fetch the encrypted values once on load + values and initialValues will be updated
+ * as a result of that
+ */
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [project?.ref, project?.connectionString])
return (
<>
@@ -279,8 +292,7 @@ export const EditWrapperSheet = ({
Target: {target}
- Columns:{' '}
- {table.columns.map((column: any) => column.name).join(', ')}
+ Columns: {table.columns.map((column) => column.name).join(', ')}
diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx
index ef5f4772d275e..43d6b8d200c0d 100644
--- a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx
+++ b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx
@@ -25,7 +25,6 @@ import { formatWrapperTables } from './Wrappers.utils'
interface WrapperRowProps {
wrapper: FDW
- wrappers: FDW[]
selectedWrapperToEdit?: FDW
selectedWrapperToDelete?: FDW
setSelectedWrapperToEdit: (value: string | null) => void
@@ -33,9 +32,8 @@ interface WrapperRowProps {
deletingWrapperIdRef: MutableRefObject
}
-const WrapperRow = ({
+export const WrapperRow = ({
wrapper,
- wrappers,
selectedWrapperToEdit,
selectedWrapperToDelete,
setSelectedWrapperToEdit,
@@ -88,17 +86,20 @@ const WrapperRow = ({
{_tables?.map((table) => {
- const target = table.table ?? table.object
+ const target = table.table ?? table.object ?? table.src_key
return (
-
-
+
+
{integration.icon({ className: 'p-0' })}
{target}
-
+
{target}
@@ -110,13 +111,16 @@ const WrapperRow = ({
-
+
{table.schema}.{table.table_name}
-
+
{table.schema}.{table.table_name}
@@ -129,7 +133,6 @@ const WrapperRow = ({
{encryptedMetadata.map((metadata) => (
- {/*
{metadata.label}:
*/}
)
}
-
-export default WrapperRow
diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperTable.tsx b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperTable.tsx
index 46af1b1529a19..431fdf15aa67d 100644
--- a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperTable.tsx
+++ b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperTable.tsx
@@ -17,7 +17,7 @@ import {
TableRow,
} from 'ui'
import { INTEGRATIONS } from '../Landing/Integrations.constants'
-import WrapperRow from './WrapperRow'
+import { WrapperRow } from './WrapperRow'
import { wrapperMetaComparator } from './Wrappers.utils'
interface WrapperTableProps {
@@ -91,7 +91,6 @@ export const WrapperTable = ({ isLatest = false }: WrapperTableProps) => {
{
const sql = vaultSecretDecryptedValuesQuery(ids)
- const { result } = await executeSql({ projectRef, connectionString, sql }, signal)
- return result.reduce((a: any, b: any) => {
- return { ...a, [b.id]: b.decrypted_secret }
- }, {})
+ const { result } = await executeSql<{ id: string; decrypted_secret: string }[]>(
+ { projectRef, connectionString, sql },
+ signal
+ )
+ return result.reduce(
+ (a, b) => {
+ return { ...a, [b.id]: b.decrypted_secret }
+ },
+ {} as Record
+ )
}
diff --git a/apps/studio/lib/constants/index.ts b/apps/studio/lib/constants/index.ts
index 4a95864039e97..3bf961dec49d9 100644
--- a/apps/studio/lib/constants/index.ts
+++ b/apps/studio/lib/constants/index.ts
@@ -56,3 +56,5 @@ export const OPT_IN_TAGS = {
export const GB = 1024 * 1024 * 1024
export const MB = 1024 * 1024
export const KB = 1024
+
+export const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
From a88c88fa3641de17c3826b58faa5c30455a6f21a Mon Sep 17 00:00:00 2001
From: Joshen Lim
Date: Mon, 5 Jan 2026 12:14:19 +0700
Subject: [PATCH 2/5] Hide RLS disabled CTA in table editor header if no lints
(#41517)
* Hide RLS disabled CTA in table editor header if no lints
* Clean
* Update e2e tests
* INvlidate lints when creating tbale
* Revert
* Invalidate lints when duplicating table
---
.../interfaces/Settings/API/PostgrestConfig.tsx | 2 --
.../TableGridEditor/GridHeaderActions.tsx | 12 ++++++++++--
.../SidePanelEditor/SidePanelEditor.tsx | 3 +++
.../project-postgrest-config-update-mutation.ts | 6 +++++-
e2e/studio/features/table-editor.spec.ts | 13 +++++++++----
5 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx
index 1d88dcdf0d554..454d3fcf9075e 100644
--- a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx
+++ b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx
@@ -35,11 +35,9 @@ import {
Form_Shadcn_,
Input_Shadcn_,
PrePostTab,
- Separator,
Skeleton,
Switch,
WarningIcon,
- cn,
} from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns'
import { Admonition } from 'ui-patterns/admonition'
diff --git a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx
index fc77f12c9c4f7..0c91f6e92a066 100644
--- a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx
@@ -161,6 +161,14 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
// This will change when we allow autogenerated API docs for schemas other than `public`
const doesHaveAutoGeneratedAPIDocs = table.schema === 'public'
+ const { hasLint: tableHasLints } = getEntityLintDetails(
+ table.name,
+ 'rls_disabled_in_public',
+ ['ERROR'],
+ lints,
+ table.schema
+ )
+
const { hasLint: viewHasLints, matchingLint: matchingViewLint } = getEntityLintDetails(
table.name,
'security_definer_view',
@@ -301,7 +309,7 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
)}
>
- ) : (
+ ) : tableHasLints ? (
}>
@@ -338,7 +346,7 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
- )
+ ) : null
) : null}
{isTable && isIndexAdvisorAvailable && !isIndexAdvisorEnabled && (
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx
index f56ec5c682059..70daf4bde1dd6 100644
--- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx
@@ -15,6 +15,7 @@ import type { ForeignKeyConstraint } from 'data/database/foreign-key-constraints
import { databaseKeys } from 'data/database/keys'
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
import { entityTypeKeys } from 'data/entity-types/keys'
+import { lintKeys } from 'data/lint/keys'
import { privilegeKeys } from 'data/privileges/keys'
import { tableEditorKeys } from 'data/table-editor/keys'
import { isTableLike } from 'data/table-editor/table-editor-types'
@@ -530,6 +531,7 @@ export const SidePanelEditor = ({
queryClient.invalidateQueries({
queryKey: privilegeKeys.tablePrivilegesList(project?.ref),
}),
+ queryClient.invalidateQueries({ queryKey: lintKeys.lint(project?.ref) }),
])
// Show success toast after everything is complete
@@ -583,6 +585,7 @@ export const SidePanelEditor = ({
queryClient.invalidateQueries({
queryKey: privilegeKeys.tablePrivilegesList(project?.ref),
}),
+ queryClient.invalidateQueries({ queryKey: lintKeys.lint(project?.ref) }),
])
toast.success(
diff --git a/apps/studio/data/config/project-postgrest-config-update-mutation.ts b/apps/studio/data/config/project-postgrest-config-update-mutation.ts
index 238a7cc0a30a7..44aa1f70dfca3 100644
--- a/apps/studio/data/config/project-postgrest-config-update-mutation.ts
+++ b/apps/studio/data/config/project-postgrest-config-update-mutation.ts
@@ -3,6 +3,7 @@ import { toast } from 'sonner'
import { components } from 'api-types'
import { handleError, patch } from 'data/fetchers'
+import { lintKeys } from 'data/lint/keys'
import type { ResponseError, UseCustomMutationOptions } from 'types'
import { configKeys } from './keys'
@@ -63,7 +64,10 @@ export const useProjectPostgrestConfigUpdateMutation = ({
mutationFn: (vars) => updateProjectPostgrestConfig(vars),
async onSuccess(data, variables, context) {
const { projectRef } = variables
- queryClient.invalidateQueries({ queryKey: configKeys.postgrest(projectRef) })
+ await Promise.all([
+ queryClient.invalidateQueries({ queryKey: configKeys.postgrest(projectRef) }),
+ queryClient.invalidateQueries({ queryKey: lintKeys.lint(projectRef) }),
+ ])
await onSuccess?.(data, variables, context)
},
async onError(data, variables, context) {
diff --git a/e2e/studio/features/table-editor.spec.ts b/e2e/studio/features/table-editor.spec.ts
index d4e3445ee92fd..ec66e4b8a8e12 100644
--- a/e2e/studio/features/table-editor.spec.ts
+++ b/e2e/studio/features/table-editor.spec.ts
@@ -1,7 +1,7 @@
-import { expect, Locator, Page } from '@playwright/test'
+import { expect, Page } from '@playwright/test'
import fs from 'fs'
import path from 'path'
-import { isCLI } from '../utils/is-cli.js'
+import { env } from '../env.config.js'
import { releaseFileOnceCleanup, withFileOnceSetup } from '../utils/once-per-file.js'
import { resetLocalStorage } from '../utils/reset-local-storage.js'
import { test } from '../utils/test.js'
@@ -13,7 +13,6 @@ import {
waitForGridDataToLoad,
waitForTableToLoad,
} from '../utils/wait-for-response.js'
-import { env } from '../env.config.js'
const tableNamePrefix = 'pw_table'
const columnName = 'pw_column'
@@ -244,14 +243,20 @@ testRunner('table editor', () => {
await page.getByTestId('table-name-input').fill(tableNameRlsDisabled)
await page.getByLabel('Enable Row Level Security (').click()
await page.getByRole('button', { name: 'Confirm' }).click()
+
+ // Wait for table creation
const apiPromise = waitForApiResponse(
page,
'pg-meta',
ref,
'tables?include_columns=false&included_schemas=public'
- ) // wait for table creation
+ )
+ // Wait for lints refresh
+ const lintsPromise = waitForApiResponse(page, 'projects', ref, 'run-lints')
+
await page.getByRole('button', { name: 'Save' }).click()
await apiPromise
+ await lintsPromise
await page.getByRole('button', { name: `View ${tableNameRlsDisabled}` }).click()
await expect(page.getByRole('button', { name: 'RLS disabled' })).toBeVisible()
})
From 90b77c3b23f7d8a858bac276ea378d9d8795b978 Mon Sep 17 00:00:00 2001
From: Joshen Lim
Date: Mon, 5 Jan 2026 12:17:10 +0700
Subject: [PATCH 3/5] Mark useQueryStateWithSelect as deprecated (#41707)
* Mark useQueryStateWithSelect as deprecated
* update comment
---
apps/studio/hooks/misc/useQueryStateWithSelect.ts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/apps/studio/hooks/misc/useQueryStateWithSelect.ts b/apps/studio/hooks/misc/useQueryStateWithSelect.ts
index 46f7a0624ce3c..31cbe6fa6bd8c 100644
--- a/apps/studio/hooks/misc/useQueryStateWithSelect.ts
+++ b/apps/studio/hooks/misc/useQueryStateWithSelect.ts
@@ -13,6 +13,10 @@ import { toast } from 'sonner'
* @returns Object with:
* - value: The result of select(selectedId) or undefined
* - setValue: Function to set/clear the selected ID in the URL
+ *
+ * @deprecated Avoid using this hook, and use nuqs directly
+ * Refer to this PR for more information: https://github.com/supabase/supabase/pull/41380
+ * as well as context on how to refactor to remove usage of this hook
*/
export function useQueryStateWithSelect({
enabled,
From 3336b2245175a6a68abf4f625567649cfae6039b Mon Sep 17 00:00:00 2001
From: Pamela Chia
Date: Mon, 5 Jan 2026 15:22:06 +0700
Subject: [PATCH 4/5] chore(flags): migrate homeNew flag from configcat to
posthog (#41101)
Switch homeNew flag to usePHFlag.
---
apps/studio/components/layouts/ProjectLayout/index.tsx | 5 +++--
apps/studio/pages/new/[slug].tsx | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/apps/studio/components/layouts/ProjectLayout/index.tsx b/apps/studio/components/layouts/ProjectLayout/index.tsx
index 3ffa96b4852bc..e2f9b39341fc6 100644
--- a/apps/studio/components/layouts/ProjectLayout/index.tsx
+++ b/apps/studio/components/layouts/ProjectLayout/index.tsx
@@ -2,7 +2,7 @@ import Head from 'next/head'
import { useRouter } from 'next/router'
import { forwardRef, Fragment, PropsWithChildren, ReactNode, useEffect } from 'react'
-import { mergeRefs, useFlag, useParams } from 'common'
+import { mergeRefs, useParams } from 'common'
import { CreateBranchModal } from 'components/interfaces/BranchManagement/CreateBranchModal'
import { ProjectAPIDocs } from 'components/interfaces/ProjectAPIDocs/ProjectAPIDocs'
import { ResourceExhaustionWarningBanner } from 'components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner'
@@ -11,6 +11,7 @@ import { useCustomContent } from 'hooks/custom-content/useCustomContent'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { withAuth } from 'hooks/misc/withAuth'
+import { usePHFlag } from 'hooks/ui/useFlag'
import { PROJECT_STATUS } from 'lib/constants'
import { useAppStateSnapshot } from 'state/app-state'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
@@ -275,7 +276,7 @@ const ContentWrapper = ({ isLoading, isBlocking = true, children }: ContentWrapp
const { ref } = useParams()
const state = useDatabaseSelectorStateSnapshot()
const { data: selectedProject } = useSelectedProjectQuery()
- const isHomeNewFlag = useFlag('homeNew')
+ const isHomeNewFlag = usePHFlag('homeNew')
const isBranchesPage = router.pathname.includes('/project/[ref]/branches')
const isSettingsPages = router.pathname.includes('/project/[ref]/settings')
diff --git a/apps/studio/pages/new/[slug].tsx b/apps/studio/pages/new/[slug].tsx
index 29494cd39f19a..c93e0dc64aad1 100644
--- a/apps/studio/pages/new/[slug].tsx
+++ b/apps/studio/pages/new/[slug].tsx
@@ -51,6 +51,7 @@ import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { withAuth } from 'hooks/misc/withAuth'
+import { usePHFlag } from 'hooks/ui/useFlag'
import { DOCS_URL, PROJECT_STATUS, PROVIDERS, useDefaultProvider } from 'lib/constants'
import { useTrack } from 'lib/telemetry/track'
import { AWS_REGIONS, type CloudProvider } from 'shared-data'
@@ -82,7 +83,7 @@ const Wizard: NextPageWithLayout = () => {
const projectCreationDisabled = useFlag('disableProjectCreationAndUpdate')
const showPostgresVersionSelector = useFlag('showPostgresVersionSelector')
const cloudProviderEnabled = useFlag('enableFlyCloudProvider')
- const isHomeNew = useFlag('homeNew')
+ const isHomeNew = usePHFlag('homeNew')
const showNonProdFields = process.env.NEXT_PUBLIC_ENVIRONMENT !== 'prod'
const isNotOnHigherPlan = !['team', 'enterprise', 'platform'].includes(currentOrg?.plan.id ?? '')
From 351ba14d3c5a34275c18a1a296cc2e681d13ca34 Mon Sep 17 00:00:00 2001
From: Andrew Valleteau
Date: Mon, 5 Jan 2026 09:28:16 +0100
Subject: [PATCH 5/5] docs(branching): add the list of all valid secrets fields
(#41640)
* docs(branching): add the list of all valid secrets fields
* chore(docs): allow Captcha to the rules spelling lints
* docs(branching): make external secret a star field
---
.../deployment/branching/configuration.mdx | 54 ++++++++++++++++++-
supa-mdx-lint/Rule003Spelling.toml | 1 +
2 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/apps/docs/content/guides/deployment/branching/configuration.mdx b/apps/docs/content/guides/deployment/branching/configuration.mdx
index 9e5559a506b86..937edbec6fe6d 100644
--- a/apps/docs/content/guides/deployment/branching/configuration.mdx
+++ b/apps/docs/content/guides/deployment/branching/configuration.mdx
@@ -154,7 +154,59 @@ secret = "env(SUPABASE_AUTH_EXTERNAL_GITHUB_SECRET)"
-The `encrypted:` syntax only works for designated "secret" fields in the configuration (like `secret` in auth providers). Using encrypted values in other fields will not be automatically decrypted and may cause issues. For non-secret fields, use environment variables with the `env()` syntax instead.
+The `encrypted:` syntax only works for designated "secret" fields in the configuration. Using encrypted values in other fields will not be automatically decrypted and may cause issues. For non-secret fields, use environment variables with the `env()` syntax instead.
+
+The following fields support the `encrypted:` syntax:
+
+**Studio**
+
+- `studio.openai_api_key`
+
+**Database**
+
+- `db.root_key`
+- `db.vault.*` (any key in the vault map)
+
+**Auth - Core Keys**
+
+- `auth.publishable_key`
+- `auth.secret_key`
+- `auth.jwt_secret`
+- `auth.anon_key`
+- `auth.service_role_key`
+
+**Auth - Email (SMTP)**
+
+- `auth.email.smtp.pass`
+
+**Auth - Captcha**
+
+- `auth.captcha.secret`
+
+**Auth - Hooks**
+
+- `auth.hook.mfa_verification_attempt.secrets`
+- `auth.hook.password_verification_attempt.secrets`
+- `auth.hook.custom_access_token.secrets`
+- `auth.hook.send_sms.secrets`
+- `auth.hook.send_email.secrets`
+- `auth.hook.before_user_created.secrets`
+
+**Auth - SMS Providers**
+
+- `auth.sms.twilio.auth_token`
+- `auth.sms.twilio_verify.auth_token`
+- `auth.sms.messagebird.access_key`
+- `auth.sms.textlocal.api_key`
+- `auth.sms.vonage.api_secret`
+
+**Auth - External OAuth Providers**
+
+- `auth.external.*.secret`
+
+**Edge Runtime**
+
+- `edge_runtime.secrets.*` (any key in the secrets map)
diff --git a/supa-mdx-lint/Rule003Spelling.toml b/supa-mdx-lint/Rule003Spelling.toml
index 5da86acc7a327..912a50d853111 100644
--- a/supa-mdx-lint/Rule003Spelling.toml
+++ b/supa-mdx-lint/Rule003Spelling.toml
@@ -358,6 +358,7 @@ allow_list = [
"gte-small",
"halfvec",
"hCaptcha",
+ "Captcha",
"https?:\\/\\/\\S+",
"i.e.",
"imgproxy",