From e68f671bbbfb29171fefffce2a43e359c5c5e356 Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Tue, 11 Aug 2026 22:40:21 +0200 Subject: [PATCH 1/4] docs: say a Cloud environment is protected before asking for a request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Deploy project page asks the reader to check the preview before deploying, then to verify the deployment with `curl -sSf `. Neither step works as written on a default Veryfront Cloud project. Cloud creates `preview`, `staging`, and `production` as protected. A protected environment serves only a browser signed in to Veryfront as a project member; every other request gets a 302 to the sign-in page, on every path including API routes, and `VERYFRONT_API_TOKEN` does not change that because it authenticates the CLI against the Cloud API, not deployment traffic. Against published 0.1.1229: curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' \ https://support-agent.preview.veryfront.com/ 302 https://veryfront.com/sign-in?from=%2F The page never said so, which made the verification step worse than useless: `curl` does not treat a 302 as a failure, so the page's own `curl -sSf ` exits 0 with an empty body whether or not the deployment works. A reader following the page sees a green exit code and no output, and concludes the deploy is fine. Add an Environment access section between Push and Deploy — where the reader is first told to check the preview — naming the default, the redirect, the token's irrelevance to it, and the Studio switch that makes an environment public. Rewrite the verification step to print the status line, so a sign-in redirect is visible instead of passing silently, and say outright why the bare `curl -sSf` form must not be used. The page's other verification claim was already corrected in #3576: this page and the sibling guide state that `veryfront open` opens the Cloud dashboard, not the deployed site, which `veryfront open --json` on 0.1.1229 confirms (`https://veryfront.com/projects/`). That correction has not reached the live site yet, so this change ships alongside it. The regression test pins the three facts a reader needs — protected by default, the sign-in redirect, and the Studio switch — plus the status-line form of the check, and rejects a bare `curl -sSf ` fence. --- docs/getting-started/deploy-project.md | 42 +++++++++++++++++++++++--- tests/docs/guide-content.test.ts | 24 +++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/docs/getting-started/deploy-project.md b/docs/getting-started/deploy-project.md index d1e8569a29..14620977ef 100644 --- a/docs/getting-started/deploy-project.md +++ b/docs/getting-started/deploy-project.md @@ -59,6 +59,31 @@ For a preview deployment per branch: npx veryfront@latest push --branch feature-x ``` +## Environment access + +Veryfront Cloud creates `preview`, `staging`, and `production` as protected by +default, so check the preview URL in a browser signed in to Veryfront as a +member of the project. + +A protected environment serves only that signed-in browser. Every other request +gets a `302` to `https://veryfront.com/sign-in`, on every path including API +routes: + +```bash +curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' \ + https://.production.veryfront.com/api/health +302 https://veryfront.com/sign-in?from=https%3A%2F%2F...%2Fapi%2Fhealth +``` + +`VERYFRONT_API_TOKEN` does not open a protected environment. It authenticates +the CLI against the Cloud API, not deployment traffic, so `curl`, a CI smoke +test, or an uptime monitor sees the sign-in redirect either way. + +To serve an environment to everyone, open **Environments** in Veryfront Studio, +select the environment, turn on **Public Environment**, and confirm +**Make Public**. Keep protection on for environments that serve internal or +unreleased work. + ## Deploy to Veryfront Cloud After checking the preview, deploy the exact pushed source digest: @@ -94,15 +119,22 @@ container example. ## Verify it worked -Deploy prints the environment URL. Request that URL and confirm the deployed -page responds: +Deploy prints the environment URL. Request that URL and print the status line, +so a sign-in redirect is visible instead of passing silently: ```bash -curl -sSf +curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' ``` -Then request an API route the project serves. For the agent route from -[Create API](./create-api.md): +A public environment answers `200`. A protected environment answers `302` to +`https://veryfront.com/sign-in`; open the URL in a signed-in browser, or make +the environment public, as described in +[Environment access](#environment-access). Do not check with a bare +`curl -sSf `: `curl` does not treat a `302` as a failure, so +that command exits `0` with an empty body whether or not the deployment works. + +Once the environment is public, request an API route the project serves. For +the agent route from [Create API](./create-api.md): ```bash curl -sSf -N -X POST /api/ag-ui \ diff --git a/tests/docs/guide-content.test.ts b/tests/docs/guide-content.test.ts index 97d034a48b..6ceb943798 100644 --- a/tests/docs/guide-content.test.ts +++ b/tests/docs/guide-content.test.ts @@ -189,6 +189,30 @@ describe("guide content contracts", () => { ); }); + it("tells the reader a Cloud environment is protected before asking for a request", async () => { + // Veryfront Cloud environments are protected by default: an anonymous + // request to the environment URL gets a 302 to + // https://veryfront.com/sign-in on every path, API routes included, and + // VERYFRONT_API_TOKEN does not change that. Verified against published + // 0.1.1229: + // curl -s -o /dev/null -w '%{http_code} %{redirect_url}' \ + // https://support-agent.production.veryfront.com/api/health + // -> 302 https://veryfront.com/sign-in?from=... + // A verification step written as a bare `curl -sSf ` + // therefore exits 0 with an empty body whether or not the deployment + // works, so the page has to name the redirect and print the status code. + const doc = await Deno.readTextFile( + "docs/getting-started/deploy-project.md", + ); + const prose = doc.replace(/\s+/g, " "); + + assertStringIncludes(prose, "protected by default"); + assertStringIncludes(prose, "https://veryfront.com/sign-in"); + assertStringIncludes(prose, "Public Environment"); + assertStringIncludes(doc, "-w '%{http_code} %{redirect_url}\\n'"); + assertEquals(doc.includes("```bash\ncurl -sSf \n```"), false); + }); + it("uses serve for local production builds", async () => { const docs = [ "docs/getting-started/deploy-project.md", From df2565f5ccdf3464876b509eba310fdf908c3600 Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Tue, 11 Aug 2026 22:51:28 +0200 Subject: [PATCH 2/4] test(docs): resolve the deploy-project read from the module, not the cwd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `lint:cwd-relative-test-reads` failed on the new case: it raised `tests/docs/guide-content.test.ts` from 29 to 30 cwd-relative reads inside test callbacks, and that ratchet may only shrink. The surrounding cases in this file are the grandfathered 29. Rather than join them, resolve the new read from `import.meta.url` — the fix the audit header prescribes, and the one #3592 applied. Test files are separate isolates sharing one process under `--parallel` and `src/testing/cwd.ts` chdirs that process, so a cwd-relative read is correct only until an unrelated file lands beside it in the same shard. Baseline is untouched: 0 at module scope, 95 in callbacks across 21 files, exactly as before. --- tests/docs/guide-content.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/docs/guide-content.test.ts b/tests/docs/guide-content.test.ts index 6ceb943798..19899a3f84 100644 --- a/tests/docs/guide-content.test.ts +++ b/tests/docs/guide-content.test.ts @@ -201,8 +201,10 @@ describe("guide content contracts", () => { // A verification step written as a bare `curl -sSf ` // therefore exits 0 with an empty body whether or not the deployment // works, so the page has to name the redirect and print the status code. + // Resolved from the module, not the process cwd: test files share one + // process under --parallel and src/testing/cwd.ts chdirs it. const doc = await Deno.readTextFile( - "docs/getting-started/deploy-project.md", + new URL("../../docs/getting-started/deploy-project.md", import.meta.url), ); const prose = doc.replace(/\s+/g, " "); From ec5d984eecf3ec506efffb477379fea581b774c5 Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Tue, 11 Aug 2026 23:02:22 +0200 Subject: [PATCH 3/4] docs: correct the protected-environment response and unpaste the output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three review findings, all confirmed against source before applying. The claim that "every other request gets a 302" was wrong for one case. `checkProtectedProxyAccess` in `src/proxy/proxy-access-control.ts` returns 302 only while the caller is unauthenticated or its token yields no user id; a caller signed in as a user who is not a project member gets 403 instead. That is the more confusing failure of the two, because the browser is signed in and the reader would suspect the URL rather than the account, so the page now names it and says what it means. The expected `302 …` line sat inside the `bash` fence with the command. Pasting the rendered block ran `302` as a third command. It moves to its own `text` fence, per the repo rule that code examples must be safe to paste. The regression test's evidence comment recorded a real deployment hostname. Internal hostnames are on the AGENTS.md secret-safety list, and the host was never the point of the evidence, so it is a `` placeholder now. The comment gains the source pointer for the 403 path, and the test pins the 403 sentence so the correction cannot silently regress. --- docs/getting-started/deploy-project.md | 23 +++++++++++++++-------- tests/docs/guide-content.test.ts | 9 ++++++--- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/docs/getting-started/deploy-project.md b/docs/getting-started/deploy-project.md index 14620977ef..86ba8b940f 100644 --- a/docs/getting-started/deploy-project.md +++ b/docs/getting-started/deploy-project.md @@ -65,16 +65,22 @@ Veryfront Cloud creates `preview`, `staging`, and `production` as protected by default, so check the preview URL in a browser signed in to Veryfront as a member of the project. -A protected environment serves only that signed-in browser. Every other request -gets a `302` to `https://veryfront.com/sign-in`, on every path including API -routes: +A protected environment serves only that signed-in member. An unauthenticated +request gets a `302` to `https://veryfront.com/sign-in`, on every path +including API routes: ```bash curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' \ https://.production.veryfront.com/api/health +``` + +```text 302 https://veryfront.com/sign-in?from=https%3A%2F%2F...%2Fapi%2Fhealth ``` +A request signed in as a user who is not a member of the project gets a `403` +instead. A `403` means the account is wrong, not the URL. + `VERYFRONT_API_TOKEN` does not open a protected environment. It authenticates the CLI against the Cloud API, not deployment traffic, so `curl`, a CI smoke test, or an uptime monitor sees the sign-in redirect either way. @@ -127,11 +133,12 @@ curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' ``` A public environment answers `200`. A protected environment answers `302` to -`https://veryfront.com/sign-in`; open the URL in a signed-in browser, or make -the environment public, as described in -[Environment access](#environment-access). Do not check with a bare -`curl -sSf `: `curl` does not treat a `302` as a failure, so -that command exits `0` with an empty body whether or not the deployment works. +`https://veryfront.com/sign-in`, or `403` for a signed-in non-member. In that +case open the URL in a member's browser, or make the environment public, as +described in [Environment access](#environment-access). Do not check with a +bare `curl -sSf `: `curl` does not treat a `302` as a failure, +so that command exits `0` with an empty body whether or not the deployment +works. Once the environment is public, request an API route the project serves. For the agent route from [Create API](./create-api.md): diff --git a/tests/docs/guide-content.test.ts b/tests/docs/guide-content.test.ts index 19899a3f84..ea040cd51d 100644 --- a/tests/docs/guide-content.test.ts +++ b/tests/docs/guide-content.test.ts @@ -193,10 +193,12 @@ describe("guide content contracts", () => { // Veryfront Cloud environments are protected by default: an anonymous // request to the environment URL gets a 302 to // https://veryfront.com/sign-in on every path, API routes included, and - // VERYFRONT_API_TOKEN does not change that. Verified against published - // 0.1.1229: + // VERYFRONT_API_TOKEN does not change that. A signed-in non-member gets a + // 403 instead, per checkProtectedProxyAccess in + // src/proxy/proxy-access-control.ts. Verified against a live protected + // environment on published 0.1.1229: // curl -s -o /dev/null -w '%{http_code} %{redirect_url}' \ - // https://support-agent.production.veryfront.com/api/health + // https://.production.veryfront.com/api/health // -> 302 https://veryfront.com/sign-in?from=... // A verification step written as a bare `curl -sSf ` // therefore exits 0 with an empty body whether or not the deployment @@ -211,6 +213,7 @@ describe("guide content contracts", () => { assertStringIncludes(prose, "protected by default"); assertStringIncludes(prose, "https://veryfront.com/sign-in"); assertStringIncludes(prose, "Public Environment"); + assertStringIncludes(prose, "not a member of the project gets a `403`"); assertStringIncludes(doc, "-w '%{http_code} %{redirect_url}\\n'"); assertEquals(doc.includes("```bash\ncurl -sSf \n```"), false); }); From 2982e1747cec9ef3d5adae20e19456c3bd422a88 Mon Sep 17 00:00:00 2001 From: Koji Wakayama Date: Wed, 12 Aug 2026 08:21:20 +0200 Subject: [PATCH 4/4] docs: stop the protected-environment comment claiming one sign-in apex --- tests/docs/guide-content.test.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/docs/guide-content.test.ts b/tests/docs/guide-content.test.ts index a210091a6f..979ee30c4f 100644 --- a/tests/docs/guide-content.test.ts +++ b/tests/docs/guide-content.test.ts @@ -202,12 +202,13 @@ describe("guide content contracts", () => { it("tells the reader a Cloud environment is protected before asking for a request", async () => { // Veryfront Cloud environments are protected by default: an anonymous - // request to the environment URL gets a 302 to - // https://veryfront.com/sign-in on every path, API routes included, and - // VERYFRONT_API_TOKEN does not change that. A signed-in non-member gets a - // 403 instead, per checkProtectedProxyAccess in - // src/proxy/proxy-access-control.ts. Verified against a live protected - // environment on published 0.1.1229: + // request to the environment URL gets a 302 to a Veryfront sign-in page on + // every path, API routes included, and VERYFRONT_API_TOKEN does not change + // that. A signed-in non-member gets a 403 instead, per + // checkProtectedProxyAccess in src/proxy/proxy-access-control.ts. Which + // apex serves that sign-in page varies by deployment host — see the + // sign-in-apex case below, which owns that half. Verified against a live + // protected environment on published 0.1.1229: // curl -s -o /dev/null -w '%{http_code} %{redirect_url}' \ // https://.production.veryfront.com/api/health // -> 302 https://veryfront.com/sign-in?from=...