Repository navigation
chore: production deploy - #7340
supabase-cli-releaser[bot] wants to merge 33 commits into
Conversation
This bumps storage from v1.84.19-r0 to v1.84.20-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.84.20-r0 Planned after supabase/slim-services published v1.84.20-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps storage from v1.84.20-r0 to v1.84.21-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.84.21-r0 Planned after supabase/slim-services published v1.84.21-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Toolchain bumps - Bumps pnpm, node, bun, and mise. `mise.lock` and `pnpm-lock.yaml` are updated to match. - Updates the mise version pinned in each GitHub Actions workflow. - Drops `pnpm exec` from `bun` commands. The toolchain setup already puts bun on `PATH`, so running it through pnpm only added a process hop and a pnpm lookup. Calling bun directly removes that overhead and complexity. ## Workflow changes ### flaky-check installs bun with `oven-sh/setup-bun` Its plan and report jobs only need bun. setup-bun restores a 34 MB cache with just the bun binary. mise-action restored a 68 MB mise data directory and then ran a locked install and a listing on top, which took about twice as long on a cache hit. Every other bun-only step in the repository already uses setup-bun, so this makes them consistent. ### `jdx/mise-action` moves from v4.3.0 to v5.2.2 What we gain: - **The mise binary is verified before it runs.** A downloaded binary is checked against the release's signed manifest, and a binary restored from cache is checked against the signed release checksums. Before, a cached binary could run unchecked. - **The job's GitHub token stays inside the action.** v4 wrote it to the environment of every later step. Now only the action sees it. - **Caches rebuild every 30 days.** GitHub never evicts a cache that keeps getting hits, so a busy repository could keep the same tools for months. The default key now rolls over on a schedule. What does not change for us: every step pins a mise version, so the new release-age default does not apply, and the pinned mise ships the signed manifest the action now requires. One thing to expect: the default cache key format changed, so each mise cache misses once after this lands. The sync check job runs `mise install` in a later step. It no longer gets the token, but it fetches release metadata through mise's own relay and downloads assets directly from GitHub releases, so it never used the token. ### The shared setup action drops its custom `cache_key` The custom key hashed `mise.toml`, `mise.lock`, `.node-version`, `.bun-version`, and `package.json`. mise-action's default key already covers `mise.toml` and `mise.lock`, plus the requested tool subset, the runner image, and the mise version. The lockfile pins the node, bun, and pnpm versions the version files select, and a locked install refuses any version the lockfile does not have. So the extra hashes could not catch anything the lockfile hash misses, and the default key is strictly better. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com>
Stacked on #7285. ## Bump - `jactionlint` moves from 1.8.2 to 2.3.0 (`"2"` in mise.toml, pinned in `mise.lock`). - The config's deprecated `require-commit-hash` key becomes `rules: unpinned-uses: error`, the vendored `.repos/**` submodules are excluded, and the `missing-timeout` and `concurrency-limits` policy rules are turned off. ## Fixes, one commit per rule - **excessive-permissions**: single-job workflows grant their write scopes on the job instead of the workflow. contribution-gate grants both jobs the scopes its script needs. release-smoke-test's caller job keeps the four scopes release-shared's job tree declares. - **github-app**: the three release-notes App tokens request only the permissions their jobs use, confirmed against GitHub's REST permission tables. - **superfluous-actions**: both `softprops/action-gh-release` steps become `gh release create`, skipping when the release already exists and verifying the tag the earlier step pushed. - **pipeline-without-pipefail**, **github-env**, **artipacked**, **unused-job-output**: small mechanical fixes. ## Accepted findings, one commit per rule Each carries an inline `# jactionlint ignore=` comment with a plain-English reason: `dangerous-triggers`, `adhoc-packages`, `unverified-download`, `cache-poisoning` (run-scoped key), `unpinned-images` (smoke-test container), `bot-conditions` (the author check is already present), and `issues: write` on reopen-stale-issue. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com>
This bumps storage from v1.84.21-r0 to v1.84.22-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.84.22-r0 Planned after supabase/slim-services published v1.84.22-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps storage from v1.84.22-r0 to v1.84.23-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.84.23-r0 Planned after supabase/slim-services published v1.84.23-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps realtime from v2.147.1-r0 to v2.147.2-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.147.2-r0 Planned after supabase/slim-services published v2.147.2-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This PR was automatically created to sync API types from the infrastructure repository. Changes were detected after refreshing `apps/cli-go/api/v1-openapi.yaml` from the latest spec from infrastructure, and `apps/cli-go/pkg/api` is regenerated from it. The snapshot diff is the upstream API change and is worth reading: it is marked `linguist-generated`, so GitHub collapses it behind a "Load diff" click rather than hiding it. Co-authored-by: supabase-cli-releaser[bot] <246109035+supabase-cli-releaser[bot]@users.noreply.github.com>
This bumps realtime from v2.147.2-r0 to v2.147.3-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.147.3-r0 Planned after supabase/slim-services published v2.147.3-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps studio from 2026.10.19-sha-b375769-r0 to 2026.10.26-sha-478bd88-r0. https://github.com/supabase/slim-services/releases/tag/studio-2026.10.26-sha-478bd88-r0 Planned after supabase/slim-services published 2026.10.26-sha-478bd88-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Plan-gated Management API denials already return `{ error: { code:
"entitlement_required", feature, upgrade_url } }`. In `--output-format
json` and `stream-json` that signal only survives as escaped text inside
`error.message`, so an agent has to parse prose to learn that an upgrade
is needed and where. I added a structured `entitlement` to the
machine-mode error object for every command whose status errors go
through `mapHttpError`.
```json
{"_tag":"Error","error":{"code":"DomainsUnexpectedStatusError","message":"...","entitlement":{"feature":"custom_domain","upgrade_url":"https://supabase.com/dashboard/org/<slug>/billing"}}}
```
**Changed:**
- **Machine-mode errors carry the plan gate**: on a 4xx, `mapHttpError`
decodes the untruncated body and, when it is the plan-gate envelope,
attaches `entitlement: { feature, upgrade_url }` to the status error.
`normalizeCliError` passes it into the json and stream-json error
object, so commands with no per-command upgrade wiring (`branches
unpause`) get it too.
- **Handlers that rebuild a mapped error keep it**: `branches
create`/`update`, `vanity-subdomains activate`/`check-availability` and
`sso list`/`add`/`update`/`remove` forward the field, and `Output.fail`
declares it in its type.
- **Envelope values are validated in one place**: I moved the parser
into `http-errors.ts` and `suggestUpgrade` reuses it. `feature` must be
a plain key and `upgrade_url` an http(s) URL without credentials,
emitted as the parsed URL so control and bidi characters arrive
percent-encoded; anything else counts as no envelope.
**Note:** text output and `cli_upgrade_suggested` are unchanged for
well-formed envelopes. The per-command text hint stays until I move the
hint and the event onto the same central path in a follow-up.
## Linear
- part of GROWTH-1050
<!-- Before opening this PR, confirm the linked issue is open and carries the `open-for-contribution` label. PRs from external contributors that don't follow the workflow in CONTRIBUTING.md are closed automatically. @supabase members working from Linear tickets are exempt. --> ## Summary Adds a SECURITY.md file so we can get better signal on our security reports. ## Linked issue [Linear Issue](https://linear.app/supabase/issue/SEC-993/securitymd-to-guide-researchers) - [x] The linked issue is **open** and carries the `open-for-contribution` label (or I'm a Supabase maintainer). ## Checklist - [x] The PR title follows [Conventional Commits](https://www.conventionalcommits.org/) (e.g. `fix(cli): …`). - [ ] Tests added or updated for the change. - [ ] From the repository root, `pnpm check:all` passes; relevant package tests pass for every touched workspace, and `pnpm types:check` passes for each touched TypeScript workspace (or workspace declaring it). --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This bumps postgres from 17.11.0.006-r0 to 17.11.0.007-r0. https://github.com/supabase/slim-services/releases/tag/postgres-17.11.0.007-r0 Planned after supabase/slim-services published 17.11.0.007-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps realtime from v2.147.3-r0 to v2.147.4-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.147.4-r0 Planned after supabase/slim-services published v2.147.4-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#7320) ## TL;DR fixes restoring a `supabase db dump --role-only` file as `postgres` failing on a parameter grant to a reserved role ## whats improved/fixed? `supabase db dump --role-only` already comments out create, alter and membership grant lines for reserved roles, but parameter grants like `GRANT SET ON PARAMETER "log_min_messages" TO "supabase_realtime_admin";` passed through linked and local projects whose realtime setup grants this get that line in `roles.sql`, so restoring it as `postgres` aborted with `permission denied for parameter log_min_messages` ## how? a new rule anchored to the start of the line comments out parameter grants to reserved roles, in both the dump script and the in-process filter the local stack uses grants to other roles and grant text inside role setting values stay as they are ## ref: - closes: #7315
…LI-2837) (#7300) ## TL;DR the delegated binary lookup and the project link writes run through effect, ready for the effect lint ## whats improved/fixed? - the binary lookup checks its paths through `FileSystem` and reads its override env var through `Config` - `.link.json` is written through schema json instead of `JSON.stringify` - the lookup and the permissions help tests run as effects with typed errors ## how? - the lookup keeps the same search order and the same not found message - the link file keeps the same pretty printed bytes > [!NOTE] > nothing changes for users, the same binary is found and `.link.json` is exactly the same > these files join the effect lint in the last page of this stack. ## ref: - related: CLI-2837
…7301) ## TL;DR local config values, config validation and path containment run through effect, so the effect lint now covers `apps/cli/src` ## whats improved/fixed? - they read env through `Config` and files through `FileSystem`, and fail with typed errors instead of throwing - the config value tests pin env through a config provider instead of editing `process.env`, so a shell override can no longer skew them - the effect lint allow list swaps its per-file `command-internal` entries for one `!apps/cli/src/**` entry ## how? - `start`, `status`, `stop`, `services`, `notebooks`, `config push` and `stack start` yield the converted helpers and map failures with the same messages they used to catch - the error classes keep their names, so error codes and telemetry fingerprints match develop. - `SIDE_EFFECTS.md` notes now say `fs.realPath` where they said `realpathSync` > [!NOTE] > (same here) nothing changes for users, > every command prints the same output and exits with the same codes and errors. > > `local-db-running` stays excluded, it talks to the docker socket or named pipe over node http and needs a windows check first. ## ref: - closes: CLI-2837
…7309) ## TL;DR brings `packages/api/src` and its tests under the effect lint ## whats introduced? - `packages/api/src/**` joins the allow list, and the generated `contracts.ts` stays out - `SupabaseApiConfigError` and `SupabaseApiInputError` move to `Data.TaggedError` with the same message, name, cause, json and printed output, pinned by a new test, so two `apps/cli` call sites whose failures include them now yield them directly - `createApiClient` in `bun.ts` and `node.ts` chains its promise instead of being `async`, still disposing the runtime before it rejects - the src tests move to effect: `FileSystem` and `Path`, schema json codecs, and `Effect.runPromise` bodies instead of `async` callbacks ## ref - closes: CLI-2841
#7310) ## TL;DR brings the `packages/api` generator scripts and their tests under the effect lint, which completes the package ## whats introduced? - the allow list entry widens to `packages/api/**` - `download-openapi.ts` and `generate.ts` run as effect programs on `FileSystem`, `HttpClient`, `Config` and `Console`, with the same requests, console output, generated files and exit codes - a failed run still prints the original error and exits 1, with effect frames in the stack, and an empty `SUPABASE_API_URL` now reports effect's `UrlError` - the scripts tests move to effect, spawning turbo and oxfmt through `ChildProcess`, and the `openapi.json` sync test now checks the generator's own serializer ## ref - closes: CLI-2842 - closes: CLI-2840
## TL;DR brings `apps/cli/tests` and `apps/cli/vitest.config.ts` under the effect lint ## whats introduced? - `apps/cli/tests/**` and `apps/cli/vitest.config.ts` join the allow list, replacing the seven per-file `tests/helpers` entries - the test helpers, smoke tests and vitest config run on `FileSystem`, `Path`, `Config` and `ChildProcess`, and promise helpers like `runSupabase` keep their call shape and reject with the same values - `spawnSupabase` and `makeTempHome` become scoped effects, and `seedLegacyStateDecoys` and `setLinkedRefFile` become effects, so their e2e and integration call sites yield them - a timed out `until` run now kills its child and removes its temp home instead of leaving both behind ## ref - closes: CLI-2852
## Summary
`secret()` and `env({ secret: true })` mark config fields with
`x-secret`, but the published `config.schema.json` never carried it,
because the JSON Schema generator only emits standard keywords. The only
hint a field held a secret was the "do not commit" note in its
description, which tools can't act on.
The published CLI config schema now has `"x-secret": true` on every
field the schema marks secret. That includes the `remotes.*` copies and
the values of `db.vault` and `edge_runtime.secrets`. Editors ignore
unknown keywords, so validation and completion behave the same, and
nothing changes in how config is loaded.
- `toPublishedJsonSchemaDocument` now generates both published schemas
itself and lets only `x-secret` through, so internal annotations like
`x-env-deferred` stay out
- `project-config.schema.json` doesn't change, since it already leaves
secret fields out altogether
- `apps/docs/public/cli/config.schema.json` is regenerated, and the only
change is the added markers
## Linked issue
Closes CLI-2858
This bumps realtime from v2.147.4-r0 to v2.148.0-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.148.0-r0 Planned after supabase/slim-services published v2.148.0-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps storage from v1.84.23-r0 to v1.84.25-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.84.25-r0 Planned after supabase/slim-services published v1.84.25-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps realtime from v2.148.0-r0 to v2.148.1-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.148.1-r0 Planned after supabase/slim-services published v2.148.1-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps realtime from v2.148.1-r0 to v2.148.2-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.148.2-r0 Planned after supabase/slim-services published v2.148.2-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## TL;DR unblocks go ci `Test`, red since #7287, by listing the nested `pkg` module with `go list -C pkg` so pkg coverage returns and the coveralls total drops ## ref: <details> <summary>as seen in:</summary> <img width="529" height="751" alt="image" src="https://github.com/user-attachments/assets/753837cc-7656-449c-ba53-525dbed7f385" /> </details>
| const char = line[i]; | ||
| const next = line[i + 1]; | ||
| if (quote !== "") { | ||
| if (char === quote) { |
There was a problem hiding this comment.
🟡 Severity: MEDIUM
Multiline, database-controlled E-strings can contain \', which this scanner mistakes for a closing quote. That can leave quote set for a later GRANT SET ON PARAMETER session_replication_role TO authenticated, bypassing the reserved-role filter; restored users could then disable triggers and bypass constraints (PostgreSQL docs).
Helpful? Add 👍 / 👎
💡 Fix Suggestion
Suggestion: Make the quote scanner aware of PostgreSQL E-strings (escape strings). When a single quote is opened with an E or e prefix, for example E'...', treat backslash as an escape character. Skip the character after each \, so \' and \\ never close the string. A doubled '' should still count as an escaped quote. Track this as a separate state, for example quote = "E'" or an isEString flag. Detect it when opening a quote: char === "'" && /[Ee]/.test(line[i - 1] ?? "") && !/[\w$]/.test(line[i - 2] ?? ""). Inside the string, use if (isEString && char === "\\") { i++; continue; } before the closing-quote check. Reset the flag when the string closes. inQuotedSql can stay quote !== "". As defense in depth, make the reserved-role GRANT ... ON PARAMETER filter not depend only on the multiline quote state. For example, also match lines that start with GRANT at column 0, since a pg_dumpall statement always begins a line.
…7336) ## TL;DR brings `apps/cli/scripts` under the effect lint, so the allow list collapses into one `!apps/cli/**` entry ## whats introduced? - `apps/cli/scripts/**` joins the allow list, and the `src`, `tests` and `vitest.config.ts` entries collapse into `!apps/cli/**` - the build, release, publish and docs scripts run as effect programs on `FileSystem`, `HttpClient` and `Config`, with the same arguments, output, error messages, generated files and exit codes - a failed run still crashes with the original error, so malformed json keeps its native `SyntaxError`, and a signal during `refresh-e2e-timings` still waits for its cleanup - the scripts tests move to `it.effect` and `it.live` with the same names and assertions ## ref - closes: CLI-2853 - closes: CLI-2851
This bumps storage from v1.84.25-r0 to v1.84.28-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.84.28-r0 Planned after supabase/slim-services published v1.84.27-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…7347) ## Summary In stack mode, Edge Functions that boot at the same time can fail with `503 BOOT_ERROR`. The logs show `Failed caching npm package … File exists (os error 17)`, or `No such file or directory` when reading a cached package file. This sets `noModuleCache: false` when the stack bootstrap creates user workers, which is what classic `functions serve` already does. ### What's going on `noModuleCache: true` puts the worker's Deno cache into reload mode. Each worker builds its own npm cache, so nothing is shared between workers, and each one downloads every npm package it imports again. When that package is already in the shared `DENO_DIR`, Deno extracts it again in place, over the live copy, deleting and recreating each file. Two workers doing that at the same time collide, and a worker that reads a file in the middle of it finds it missing. ```mermaid flowchart TD boot["Worker boots and imports npm:pkg"] --> flag{"noModuleCache"} flag -- "false" --> cached{"pkg in the cache?"} cached -- "yes" --> reuse["Reuse it"] cached -- "no" --> temp["Extract to a temp dir,<br/>then rename into place"] flag -- "true" --> cached2{"pkg in the cache?"} cached2 -- "no" --> temp cached2 -- "yes" --> inplace["Extract again in place,<br/>over the live copy"] inplace --> fail["Concurrent boots collide<br/>503 BOOT_ERROR"] ``` ```mermaid sequenceDiagram participant A as Worker A participant C as Shared Deno cache participant B as Worker B A->>C: create pkg/README.md (exists, so delete it) B->>C: create pkg/README.md (succeeds) A->>C: create pkg/README.md C-->>A: File exists (os error 17) Note over A: 503 BOOT_ERROR ``` This explains the odd parts of the report: - A package that isn't cached yet goes through the temp dir and rename, which is safe. That's why a cold first round often passes, and why a warm-up request doesn't help. - Native keeps its cache across `stop` and `start`, so it fails much more often than Docker, whose new container starts with an empty cache. - `forceCreate: true` boots a new worker for every request, so every request goes down this path. I've left it as it is, because it's what makes edits show up without a restart. Against Edge Runtime directly, with only this flag changed, 40 of 64 concurrent boots failed with it on and none of 112 failed with it off. ### What changes - npm packages and remote modules are reused from the cache instead of being downloaded and extracted on every boot, so boots are quicker too. - An exact version that isn't cached yet still resolves, because Deno fetches package info again when resolution fails. Floating versions like `npm:x@latest` stay on the cached version until the cache resets (a new container on Docker, `stack destroy` on native). That matches classic `functions serve` and `deno run`. - Local function code is still read from disk on every boot, so edits show up straight away. - A new integration test serves a package from a local npm registry, warms the cache, then boots several workers at once. It checks that they all succeed and that the package is downloaded only once. Earlier bootstraps and classic `functions serve` use `false`. If `true` was there for something I've missed, I'm happy to talk it through. ## Linked issue Closes #7321 (CLI-2850)
…n the config schema (#7333) ## Summary The config loader resolves `env(VAR)` on any field and then coerces it to the field's type, but the published `config.schema.json` typed number, integer, boolean, enum and string-list fields strictly. So `max_rows = "env(MAX_ROWS)"` loads fine but shows as an error in the editor. Those fields now accept either their usual value or a string matching the loader's `env()` syntax. A mistyped literal like `max_rows = "1000"` or `pool_mode = "statement"` still fails. - `toPublishedJsonSchemaDocument` takes an `acceptEnvReferences` option, and the CLI config schema turns it on. Each affected field becomes an `anyOf` of its original type and an `env()` string, with `description`, `default` and `examples` kept on the outer node so hover docs look the same - String lists such as `auth.additional_redirect_urls` accept an `env()` string in place of the whole list, since the CLI splits the resolved value on commas. Lists of enums don't, as the loader doesn't split those - Fields that are already a union (e.g. `storage.file_size_limit`) get one extra `env()` branch rather than a nested one - `project-config.schema.json` doesn't change, since it describes values after `env()` has been resolved - `apps/docs/public/cli/config.schema.json` is regenerated - `ajv` is added as a dev dependency of `@supabase/config` so tests can check the published schema against what the loader accepts ## Schema shape change If you generate types or other tooling from `config.schema.json`, the affected fields now read as a union: their `type` moves from the field itself into the first `anyOf` branch, next to the `env()` branch. Configs that validated before still validate. ## Linked issue Closes CLI-2857
## TL;DR
adds an ECR Public fallback for slim images, so a GHCR rate limit no
longer fails `supabase start`
## whats introduced?
with `SUPABASE_USE_SLIM_IMAGES` on, slim images only came from GHCR, so
a `toomanyrequests` burst failed the command
a failed GHCR pull now falls back to the same tag and digest on
`public.ecr.aws/supabase/cli`,
the mirror the stack already uses
ps: containers started from the mirror keep their slim healthchecks and
pass the `db` stale image guard
<details><summary>visual rep:</summary>
```mermaid
flowchart LR
pull["slim image pull"] --> ghcr["ghcr.io/supabase/cli"]
ghcr -->|ok| run["container starts"]
ghcr -->|"fails 3 times"| ecr["public.ecr.aws/supabase/cli<br/>same tag and digest"]
ecr --> run
```
</details>
## ref
- closes: #7338
…7345) ## TL;DR adds `X-Forwarded-For` and `X-Real-IP` to what the stack gateway sends upstream, so edge functions can see who is calling them ## whats introduced? the gateway now appends the client address to any incoming `X-Forwarded-For` chain and sets `X-Real-IP` to the client address, the same way the legacy backend does both the http path and the websocket upgrade path send them a client sent `X-Real-IP` is always replaced or dropped, so callers cant spoof it through the gateway <details> <summary>visual rep</summary> ```mermaid flowchart LR subgraph before A1[client] --> B1[stack gateway] B1 --> C1[edge function sees no client address] end subgraph now A2[client] --> B2[stack gateway] B2 --> D2{peer address known} D2 -->|yes| E2["append client to X-Forwarded-For<br/>set X-Real-IP to client"] D2 -->|no| F2[drop client sent X-Real-IP] E2 --> G2[http and websocket upstreams] F2 --> G2 G2 --> H2[edge function sees the caller] end ``` </details> ## ref - closes: #7275
…h effect lint (CLI-2881) (#7348) ## TL;DR brings `tools`, `apps/docs` and `packages/cli-test-helpers` under the effect lint ## whats introduced? - `apps/docs/**`, `packages/cli-test-helpers/**` and `tools/**` join the allow list - the release and config tools run as effect programs on `FileSystem` and `HttpClient`, with the same requests, console output, files written and exit codes, so the root gains `effect` and `@effect/platform-bun` dev dependencies - the test harness runs on effect too, so `cli-test-helpers` gains the same two as dependencies, and `makeTempDir` returns a promise of an async disposable dir, so its two `apps/cli-e2e` call sites await it - the docs page swaps its `async` functions for promise chains and renders the same pages - `local-registry.ts` adds one `oxlint-disable` for the `openSync` log descriptor Verdaccio shares across stdout and stderr, which effect's `ChildProcess` cannot hand over - a failed run prints the same error, with effect frames in the stack ## ref - closes: CLI-2881
apps/cli/testswith effect lint (CLI-2852) (test(cli): coverapps/cli/testswith effect lint (CLI-2852) #7327)