Skip to content

[pull] master from supabase:master - #1207

Merged
pull[bot] merged 8 commits into
code:masterfrom
supabase:master
Aug 31, 2026
Merged

[pull] master from supabase:master#1207
pull[bot] merged 8 commits into
code:masterfrom
supabase:master

Conversation

@pull

@pull pull Bot commented Aug 31, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

claude Bot and others added 8 commits August 31, 2026 13:43
…tester (#49650)

<!-- ccr-slack-attribution -->
_Requested by **Kalleby Santos** · [Slack
thread](https://supabase.slack.com/archives/C0AQ3UHCCKW/p1787840441551609?thread_ts=1787840441.551609&cid=C0AQ3UHCCKW)_

**Before:** you deploy the editor's default template ("Deploy a new
function" → "Via Editor"), which wraps its handler in `withSupabase({
auth: ["publishable", "secret"] })`. You click **Test** and get `401
{"message":"Invalid credentials","code":"INVALID_CREDENTIALS"}` — from
the function's own middleware, with an empty Headers section. Studio was
quietly setting `Authorization` to a legacy `service_role` JWT (and,
before that, to your dashboard session token), routed through a private
`x-test-authorization` header that the proxy route renamed to
`Authorization`. A legacy JWT is neither a publishable nor a secret key,
so the middleware rejected it. Pasting your own `Authorization` row did
not help: the route overwrote it unconditionally. On a project with
legacy keys disabled there was no `service_role` key at all and the
literal string `Bearer undefined` went out.

**After:** the tester sends your publishable key on the `apikey` header,
where new-format keys belong, and never generates an `Authorization`
header. `Authorization` only ever comes from your own header rows —
typed by hand, or prefilled for you by the role selector. The editor's
default template works on the first click, a header you paste is
actually sent, and an **Add secret key** action in the "Add header"
dropdown gives you one-click access to a secret key, the same affordance
the database webhooks and cron job screens already have.

**How:** header construction moves into `buildEdgeFunctionTestHeaders`
(`EdgeFunctionTesterSheet.utils.ts`), which sets `Content-Type` and
`apikey` and then applies the user's rows last. The
`x-test-authorization` hop is gone from both the component and
`pages/api/edge-functions/test.ts`; the route now forwards the supplied
headers as given. Both sides merge on the lowercased header name, so a
row typed `authorization` or `apikey` replaces the generated one instead
of sitting beside it and being comma-joined by `fetch`. The Headers and
Query Parameters sections now use the shared `KeyValueFieldArray`, which
is what makes `buildEdgeFunctionHeaderAddActions` reusable here.

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Bug fix.

## What is the current behavior?

Fixes #42755.

- `EdgeFunctionTesterSheet.tsx` sent the legacy `service_role` JWT (or a
role-impersonation JWT) as the value of `x-test-authorization` on every
request, plus the dashboard session access token as `Authorization`.
- `pages/api/edge-functions/test.ts` then overwrote `Authorization` with
`x-test-authorization` whenever that header was present, discarding any
`Authorization` the user had entered.
- No `apikey` header was ever sent, so `withSupabase` in `publishable`
or `secret` auth mode — the modes used by the editor's own templates —
could never succeed.
- Header merging was case-sensitive on both sides of the proxy, so a row
typed in the conventional lowercase form produced two entries that
`fetch` comma-joined into one malformed value.
- The API keys query did not pass `reveal: true`, unlike the webhooks
and cron job UIs.

## What is the new behavior?

- `apikey` carries the publishable key, falling back to the legacy
`anon` key. This mirrors the example snippets on the function details
page, which already prefer `publishableKey ?? anonKey`. Defaulting to
the least-privileged key means a secret key is only ever sent when the
user explicitly adds it.
- `Authorization` is never generated. The `useSessionAccessTokenQuery`
call is removed from this component entirely — the dashboard user's own
session token has no business being forwarded to a project's function.
- `x-test-authorization` is removed from both files. The proxy route
stays, because it is what reads the raw upstream response for the
response panel (`redirect: 'manual'`, full status/header/body capture),
keeps the request off the browser's CORS path, and holds the
`isValidEdgeFunctionURL` guard and the local-dev URL rewrite. Only the
header rewriting is gone.
- Role impersonation keeps working, but as a visible, editable
`Authorization` row rather than a hidden injected header, so what is
sent is always what is displayed. Two details worth reviewing: the
selector tracks the value it last wrote, so clearing the role removes
only that row and leaves an `Authorization` row you typed by hand alone;
and an incrementing request id discards a JWT that resolves after a
newer role has already been picked.
- Headers merge case-insensitively, user rows winning.
- `reveal: true` is passed on the API keys query, matching
`Database/Hooks/HTTPHeaders.tsx`.

## Additional context

**Relationship to #47159.** #47159 identified the same root cause
independently and got the important part right: the key belongs on
`apikey`, and neither the legacy service-role JWT nor the dashboard
session token should be forwarded. Its extraction of a testable header
builder is a good shape, and this PR keeps it — including the spirit of
its test suite. The differences are in scope rather than direction. This
PR also removes the `x-test-authorization` hop and the route's
unconditional `Authorization` overwrite (#47159 leaves the route
untouched); drops the remaining legacy service-role fallback rather than
keeping it for projects without a publishable key; adds `reveal: true`,
secret-key support and the shared "Add secret key" affordance; and
normalizes header casing for every header rather than only
`x-test-authorization`. Whether to land that PR first and layer this on
top, or take this one, is the maintainers' call — either way the credit
for spotting it belongs there too.

**Overlap with #48143.** That open PR fixes the same case-sensitivity
defect for `Content-Type` in these two files. It is not addressed
separately here, but the case-insensitive merge in this PR covers
`Content-Type` as a side effect, so the two will conflict textually.
Happy to rebase on whichever lands first.

**A note on `verify_jwt`.** The gateway creates a temporary token when
`apikey` is present, so `verify_jwt` does not affect this path and a
request with `apikey` and no `Authorization` reaches the function
normally. No deploy defaults are changed here.

**Compatibility.** One behaviour gets worse and is worth an explicit
decision: a function that expects a legacy JWT on `Authorization` used
to "just work" in the tester because Studio injected the service-role
key. It now needs an `Authorization` row, which the **Add secret key**
action produces in one click — the shared helper already emits an
`Authorization: Bearer` row for legacy-format keys. Projects with legacy
keys disabled strictly improve: they used to receive `Bearer undefined`.
Functions using `auth: "user"` are unchanged — the tester never had a
real end-user JWT, only the impersonation token.

## Testing

`apps/studio` dependencies could not be installed in the environment
this was written in (`pnpm install` fails on a 403 from `npm.jsr.io`),
so `vitest`, `tsc --noEmit` and `eslint` were not run. What was run
instead:

- Prettier with the repo's config, including
`@ianvs/prettier-plugin-sort-imports`: clean on all five files.
- `tsc` parse of the changed files: no syntax or type errors beyond
pre-existing unresolved-module noise.
- Both new test suites transpiled and executed as plain Node assertions:
7/7 for `buildEdgeFunctionTestHeaders`, 4/4 driving the API route
handler with a stubbed `fetch`.

Please run the real suites in CI. `pnpm --filter studio exec vitest
--run tests/components/Functions/EdgeFunctionTesterSheet.utils.test.ts
tests/pages/api/edge-functions/test.test.ts` covers the added tests. A
component-level test of the impersonation prefill is not included and
would be a reasonable follow-up.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Kalleby Santos <105971119+kallebysantos@users.noreply.github.com>
…49781)

Hides the "Export Metrics to your dashboards. Get started for free!"
banner (`ObservabilityLink`) for High Availability (Multigres) projects
— the Metrics API it links to is not available for them. The check lives
inside the shared component, so it applies to every observability
sub-page that renders the banner; non-HA projects are unchanged.

Addresses
[MUL-1346](https://linear.app/supabase/issue/MUL-1346/database-observability-dashboard-remove-text-for-unsupported-feature).

## To test

- On an HA project: open Observability → Database (and any other
observability sub-page, e.g. Auth) — the "Export Metrics to your
dashboards" banner at the bottom of the page should be gone
- On a non-HA project: same pages — the banner still shows, with "Get
started for free!" linking to the metrics docs

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Metrics export links are now hidden for High Availability projects,
where the Metrics API isn’t available.
* Existing metrics export functionality remains unchanged for supported
projects.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Adds "Multigres" to the "Which services are affected?" multi-select on
the Contact Support form, so Alpha customers can tag tickets for the
Multigres Front inbox. Placed alphabetically between Edge Functions and
Realtime; the later option ids are renumbered, which is safe — they're
only used as React list keys, and the submit payload sends the lowercase
value tokens (`affectedServices: "multigres;..."`, verified with a live
submission).

Addresses
[FE-4273](https://linear.app/supabase/issue/FE-4273/add-multigres-to-support-form-services).
Front-inbox routing itself is Platform-side (SUPPORT-421) and should
match on the token `multigres`.

## To test

- Open /support/new → "Which services are affected?" → Multigres appears
between Edge Functions and Realtime, selectable alongside other
services, and the combobox search finds it
- Submit a ticket with it selected → the POST to
`/platform/feedback/send` carries `affectedServices` containing
`multigres`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added Multigres as a selectable service option in the support
interface.
  * Updated service ordering to accommodate the new option.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
…g boot (#49786)

While an HA (Multigres) project is provisioning, the `/ha-admin`
topology endpoints fail or return an empty topology as a matter of
course — the cluster topology diagram rendered that as a hard "Failed to
retrieve cluster topology" error (or the "Cluster topology unavailable"
contact-support state). The diagram now checks the project status and,
while the project is building (`COMING_UP`/`UNKNOWN`, same pair
`ProjectLayout` treats as booting), shows a "Setting up project" empty
state instead. Both the project-detail query (self-polls while booting)
and the ha-admin queries (30s interval) keep refetching, so the diagram
appears on its own once boot completes.

Once the project is past provisioning, genuine errors and the
empty-topology state surface exactly as before.

<img width="1491" height="769" alt="mul1475-setting-up-state-wide"
src="https://github.com/user-attachments/assets/3f095634-9939-41cd-88c3-0849cbad7374"
/>

**Added:**
- Component tests for the four states: booting + error, booting + empty
(→ setup state), running + error (→ error alert), running + empty (→
unavailable state)

Addresses
[MUL-1475](https://linear.app/supabase/issue/MUL-1475/polish-infra-diagram).

## To test

- On an HA project mid-provisioning (or simulate: dev toolbar
project-status override → `COMING_UP`, with `/ha-admin` requests
failing), open Settings → Infrastructure — the topology panel shows
"Setting up project" with a spinner, not the error alert
- On a healthy HA project, the topology diagram renders as before; if
`/ha-admin` genuinely fails there, the error alert still shows
- `pnpm --filter studio exec vitest run
components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/HaInstanceConfiguration.test.tsx`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a “Setting up project” state while projects are provisioning or
their status is unavailable.
* Prevents premature topology errors or unavailable messages during
project setup.
* Added accessible status announcements for loading and setup-state
transitions.

* **Bug Fixes**
* Active projects now correctly display topology errors when cluster
health data cannot be retrieved.
* Healthy responses with no topology data display an appropriate
unavailable state.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
…rod (#49787)

`getHighAvailabilityRegionCode` had `staging` and `local` cases but no
`prod` case, so it returned `undefined` in production and the High
Availability region filtering never applied — the creation flow offered
every AWS region while HA Alpha is only live in `us-east-1`. Adds the
`prod` case returning `us-east-1`, matching staging. The region filter
and the "High Availability projects are currently limited to…" banner
both key off this value, so no other changes are needed.

This keeps the accepted hardcoded-per-environment pattern for Alpha; the
API/entitlement-driven enabled-regions design stays open on the ticket
for when the rollout expands.

**Changed:**
- `ProjectCreation.utils.ts` — `prod` → `us-east-1`
- `ProjectCreation.utils.test.ts` — the two env tables previously pinned
prod as unrestricted; now expect `us-east-1`

Addresses
[FE-3716](https://linear.app/supabase/issue/FE-3716/show-enabled-regions)
(and the folded-in MUL-1337).

## To test

- `pnpm --filter studio exec vitest run
components/interfaces/ProjectCreation/ProjectCreation.utils.test.ts`
- In prod after deploy: new project → toggle High Availability → region
select offers only East US (North Virginia) and shows the "limited to"
notice; staging/local behavior unchanged (only the `prod` env branch
changed)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- High-availability project creation now correctly uses the `us-east-1`
region for production environments.
- Region selection is now properly restricted to `us-east-1` when
creating production projects.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
…rives (#49792)

In the Realtime Inspector, the messages view — which holds every
"Broadcast a message" entry point — only rendered once at least one
message had been received. With only Broadcast enabled and no inbound
traffic, the page stayed on the "Create realtime experiences" onboarding
forever, so there was no way to send a broadcast at all.

The render gate now keys off a channel being set rather than
`logData.length`: once you join a channel, `MessagesTable` renders and
its existing empty states provide the send entry points ("Listening • No
message found yet…" toolbar + the "No Realtime messages found" panel).
The onboarding remains the pre-channel state.

Addresses
[FE-4278](https://linear.app/supabase/issue/FE-4278/realtime-inspector-cant-send-broadcast-without-incoming-messages).

## To test

- Realtime → Inspector, before joining a channel: the "Create realtime
experiences" onboarding still shows
- Join a channel (with Presence off / Broadcast only so nothing
arrives): the listening view renders immediately with "No message found
yet…" and "Broadcast a message" in both the toolbar and the empty-state
panel — previously this was stuck on the onboarding
- Click "Broadcast a message" and send with defaults: the broadcast
appears in the grid
- Stop listening: no crash, messages retained

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* The Realtime Inspector now displays the messages view as soon as a
channel is selected.
* Empty-state guidance, including the option to broadcast a message, is
now available before any messages arrive.
* Pre-channel onboarding remains visible until a channel is configured.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
## Problem

Workers Secrets was merged in #49589 into the stacked
jordi/workers-detail branch. The parent Workers PR reached master
without that child merge, leaving the page absent from staging.

## Fix

Cherry-pick the missing Workers Secrets route, menu item, shared-secret
copy, and generated route tree onto current master. The page uses the
existing workers flag and permission gates.

## How to test

- Enable the workers flag for a project with Workers access.
- Open Workers, then select Secrets.
- Expected result: the shared project secrets page renders at
/project/:ref/workers/secrets and is not treated as a worker named
secrets.
- Add, edit, or delete a secret, then confirm the same value appears
under Edge Functions, Secrets.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added a **Secrets** page to the Workers section.
  * Added navigation to Worker secrets from the Workers menu.
* Displayed default secrets and deployment-specific guidance where
applicable.
* Clarified that platform secrets are shared between Edge Functions and
Workers.
  * Updated deletion warnings to reflect shared secret usage.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…#48044)

## Summary
Studio's Advisor UI reads lint metadata from a fixed `lintInfoMap`, not
from the API response. A lint name missing from that map shows a blank
title, no icon, no filter checkbox, and no remediation link. This PR
adds a `pitr_archiving_stale` entry to `lintInfoMap`, copied from the
existing `pitr_not_enabled` entry, so the new lint renders correctly in
the Advisor UI.

## Dependencies

> [!WARNING]
>
[supabase/platform#35862](supabase/platform#35862)
defines the `pitr_archiving_stale` lint. Until it merges, the API never
sends this lint name, so the Advisor grid and the public
`/v1/projects/{ref}/advisors/security` response never show the new row
-- but the Security Rules page
(`/project/<ref>/advisors/rules/security`) renders one row per
`lintInfoMap` entry regardless of the API, so this PR's new row appears
there immediately, before the backend lint exists. See Details for what
that means in the gap between merges.

---

<details>
<summary>Details</summary>

- A lint name missing from `lintInfoMap` has these effects:
- The grid row shows a blank title and no icon. There is no fallback to
the API's own `title`.
- The row has no filter checkbox. Filter options come from
`lintInfoMap`, not from the API.
- The row has no lint-specific remediation link. The "Learn more" link
falls back to the generic database-linter page.
  - The row does not appear in the Advisor Rules enable/disable list.
- The new `pitr_archiving_stale` entry copies the existing
`pitr_not_enabled` entry's `link`, `docsLink`, and `category`, and uses
a new `title` matching
[supabase/platform#35862](supabase/platform#35862
lint definition verbatim. Its `name` also matches that lint definition
exactly.
- **Known gap, until the backend PR merges:** `AdvisorRules`
(`components/interfaces/Advisors/AdvisorRules.tsx`) filters
`lintInfoMap` by `category` alone, with no dependency on the API
returning the lint -- so this entry makes a "PITR archiving may be
broken" row appear on the Security Rules page for every project right
away, ahead of the backend lint actually existing. From that row, a user
can open `CreateRuleSheet` and submit a disable rule, which `POST`s
`lint_name: 'pitr_archiving_stale'` to the notification-exceptions
endpoint. That name is not yet in the generated
`CreateNotificationExceptionsBody` enum
(`packages/api-types/types/platform.d.ts`), so the request either errors
or stores an exception keyed to a lint name nothing will ever match,
until api-types regenerates after the backend PR ships. This window
closes on its own once
[supabase/platform#35862](supabase/platform#35862)
merges; accepted as a short-lived tradeoff rather than gating this PR on
merge order or adding code to hide the row until then.
- The docs anchor (`#point-in-time-recovery`) explains what PITR and
WAL-G archiving are. It does not explain how to fix a stale or broken
archive. That content does not exist yet in either pull request.
INDATA-1149 tracks this as a follow-up.
- `packages/api-types/types/platform.d.ts` is a generated file. This
repo's own CLAUDE.md says never to hand-edit it. The file does not list
`pitr_archiving_stale` yet, because it regenerates only after the
backend lint ships and `pnpm api:codegen` runs. Until then,
`LintInfo['name']` stays a plain `string`. If someone misspells the new
entry's `name`, the code still compiles and the tests still pass. At
runtime, the icon and docs link fall back silently instead of failing a
build. Once
[supabase/platform#35862](supabase/platform#35862)
merges and api-types regenerates, `LintInfo['name']` must tighten to the
generated `LINT_TYPES` union. This closes the gap for every lint entry,
not only this one.

</details>

---

<details>
<summary>Testing</summary>

- `pnpm --filter=studio test Linter.utils.test.tsx` (17 passed,
including a test that asserts the `pitr_archiving_stale` entry's shape)
- `pnpm typecheck --filter=studio` (clean)
- `pnpm exec eslint` on the touched files (clean; the `pnpm lint
--filter=studio` turbo wrapper itself errors on this machine with an
unrelated JSON-parse failure -- a tool-invocation issue, not a lint
finding)
- `prettier --check` on the touched files
- The `docsLink` assertion
(`toContain('/guides/platform/backups#point-in-time-recovery')`) is
domain-agnostic by construction, so it holds regardless of which
`NEXT_PUBLIC_DOCS_URL` value is set -- no test in this file overrides
that variable, this is a property of the assertion's own shape, not a
scenario the suite exercises

</details>

---

<details>
<summary>Misc</summary>

- Part of INDATA-979
- Changelog:
[supabase/changelog#192](supabase/changelog#192)

</details>
@pull pull Bot locked and limited conversation to collaborators Aug 31, 2026
@pull pull Bot added the ⤵️ pull label Aug 31, 2026
@pull
pull Bot merged commit eea39cc into code:master Aug 31, 2026
2 of 19 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants