From f58fb3d81b6914e668b5c52680c695ad3e6c8467 Mon Sep 17 00:00:00 2001 From: Peng Date: Mon, 3 Aug 2026 14:26:05 +0800 Subject: [PATCH 1/6] docs(raft-apps): add deployment tutorial Signed-off-by: Peng --- .vitepress/config.mts | 1 + content/developers/raft-apps/build/index.md | 2 + content/developers/raft-apps/deploy/index.md | 211 +++++++++++++++++++ content/developers/raft-apps/index.md | 14 +- 4 files changed, 222 insertions(+), 6 deletions(-) create mode 100644 content/developers/raft-apps/deploy/index.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 8b1df8d..522d867 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -231,6 +231,7 @@ export default defineConfig({ items: [ { text: 'Overview', link: '/developers/raft-apps/' }, { text: 'Build a Raft App', link: '/developers/raft-apps/build/' }, + { text: 'Deploy a Raft App', link: '/developers/raft-apps/deploy/' }, { text: 'Login with Raft', link: '/developers/login-with-raft/' }, ], }, diff --git a/content/developers/raft-apps/build/index.md b/content/developers/raft-apps/build/index.md index 7a79a04..2a1c2f8 100644 --- a/content/developers/raft-apps/build/index.md +++ b/content/developers/raft-apps/build/index.md @@ -46,6 +46,8 @@ npm run dev Each template ships its own `README.md` and `AGENTS.md`. Treat those files as the source of truth for that template's exact environment variables, callback URLs, and local commands. +Once the app works locally, use [Deploy a Raft App](/developers/raft-apps/deploy/) to choose a runtime, create a stable HTTPS origin, register exact callback URLs, store secrets, verify the deployed flow, and prepare rollback. + ## Register it in Raft Open **Settings → Connected Apps → My Apps** in the Raft server that should own the app. diff --git a/content/developers/raft-apps/deploy/index.md b/content/developers/raft-apps/deploy/index.md new file mode 100644 index 0000000..0bc37ec --- /dev/null +++ b/content/developers/raft-apps/deploy/index.md @@ -0,0 +1,211 @@ +--- +llms_section: "Developers" +llms_order: 895 +llms_summary: "Read when your Raft App works locally and you need to deploy it, register exact production URLs, verify login or actions, and prepare rollback." +--- + +# Deploy a Raft App + +A Raft App is ready to register only after it has a stable HTTPS origin. This tutorial takes a locally working app through deployment, registration, verification, and rollback without putting a client secret in the browser or repository. + +The safe sequence is: + +1. Deploy a fail-closed app and obtain its public origin. +2. Register that exact origin, callback, and optional manifest URL in Raft. +3. Add the issued credentials to the serving environment. +4. Redeploy or restart, then verify the complete flow. + +If you have not scaffolded an app yet, start with [Build a Raft App](/developers/raft-apps/build/). + +## Choose a deployment shape + +| Shape | Good fit | Important boundary | +| --- | --- | --- | +| Static frontend + server-side API | A mostly static UI hosted on Pages, Vercel, or a CDN | The callback, token exchange, sessions, and authenticated actions still need a Worker, serverless function, or web server. Static browser code must never receive the client secret. | +| Long-running Node web service | The Express-based sign-in or action-service templates | Run the generated build and start commands on a host with HTTPS, managed secrets, logs, and durable session storage. | +| Worker/serverless app | The `hono-react-cfworker` template or an app already designed for that runtime | Use platform bindings for state and managed secrets. Do not assume an in-memory session survives another request or deployment. | + +Deployment does not grant Raft access. Registration, marketplace review, installation, and each user's or agent's grant remain separate Raft authority gates. + +## 1. Finish the server-side paths + +Generated templates are intentionally fail closed. Before you deploy, complete the paths your app actually needs: + +- the browser login start route +- the exact human callback route +- the server-side authorization-code exchange +- userinfo lookup and a local HttpOnly session +- the agent callback and service-local agent session, if the app supports Agent Login +- manifest-declared action routes, input validation, authorization, and idempotency +- a non-sensitive health endpoint or equivalent public readiness check + +Do not treat a successful scaffold build as proof that OAuth is implemented. The template `README.md` and `AGENTS.md` are the source of truth for its routes, environment variables, and remaining fail-closed markers. + +Run the generated checks before deploying: + +```bash +npm install +npm run build +``` + +If the template exposes a different check command, use the command in its README as well. + +## 2. Separate preview and production + +Use different app registrations and credentials for preview/staging and production. Do not point a preview deployment at a production client secret. + +| Value | Preview example | Production example | +| --- | --- | --- | +| App origin | `https://preview.example.dev` | `https://app.example.com` | +| Human callback | `https://preview.example.dev/auth/callback` | `https://app.example.com/auth/callback` | +| Agent callback | `https://preview.example.dev/agent/callback` | `https://app.example.com/agent/callback` | +| Client ID and secret | Preview registration | Production registration | +| Session/action secrets | Preview-only values | Production-only values | +| State and storage | Preview namespace | Production namespace | + +For generated Node templates, the main variables are typically: + +- `APP_ORIGIN` — your deployed app's public origin +- `RAFT_APP_ORIGIN` — the Raft browser origin that serves the setup page +- `RAFT_API_ORIGIN` — the Raft API used for token exchange and userinfo +- `RAFT_CLIENT_ID` — the registered app's client ID +- `RAFT_CLIENT_SECRET` — the registered app's server-only secret +- `SESSION_SECRET` or an action/session credential required by that template + +`APP_ORIGIN` is your app. `RAFT_APP_ORIGIN` and `RAFT_API_ORIGIN` are Raft. Mixing these values is a common cause of redirects that loop or return to the wrong environment. + +## 3. Deploy without the Raft secret + +First deploy the app with its public configuration and no usable Raft client secret. Login and protected actions should fail closed, while the homepage, health check, and public manifest remain reachable. + +### Node web service + +Use a Node host that supports a stable HTTPS hostname and managed environment variables. Configure the generated commands: + +```text +Build command: npm install && npm run build +Start command: npm start +``` + +Set `APP_ORIGIN` to the final HTTPS origin. Add durable storage before relying on any template that currently keeps sessions, OAuth state, idempotency records, or agent grants in memory. + +### Cloudflare Worker template + +The `hono-react-cfworker` template includes a Worker, React assets, D1, R2, and Queue bindings. Follow its generated README to create the resources and replace every placeholder ID before deployment: + +```bash +cd worker +npx wrangler d1 create YOUR_APP +npx wrangler r2 bucket create YOUR_APP-files +npx wrangler queues create YOUR_APP-events +npx wrangler d1 migrations apply YOUR_APP --remote +``` + +Set the final `APP_ORIGIN` and public Raft origins in `worker/wrangler.toml`, then deploy from the project root: + +```bash +npm install +npm run deploy +``` + +Do not add `RAFT_CLIENT_SECRET` to `wrangler.toml`. Add it as a managed Worker secret only after registration. + +## 4. Register the exact deployed URLs + +In the Raft server that should own the app, open **Settings → Connected Apps → My Apps** and register: + +- the deployed homepage or app origin +- every exact callback URL the app supports +- the primary category and description +- the public agent manifest URL, if the app exposes agent actions + +The scheme, hostname, port, and path must match the deployed route exactly. Production callbacks must use HTTPS. Do not register a wildcard callback or a preview hostname as the production callback. + +An agent can prepare the same registration with `raft integration app prepare register`; a server owner or admin still commits it through the approval card. Raft then issues a client ID and shows the client secret once to the app owner. + +## 5. Add credentials and redeploy + +Put credentials directly into the deployment platform's managed secret store. Never place them in source control, a public manifest, browser JavaScript, build output, chat, screenshots, or command-line arguments that will be retained in shell history or process listings. + +For the Cloudflare Worker template: + +```bash +cd worker +npx wrangler secret put RAFT_CLIENT_SECRET +``` + +Set `RAFT_CLIENT_ID` as the public deployment variable, then redeploy. For a Node host, add the client ID and secret in the provider's environment/secret settings and restart or roll out a new revision. + +If the app has its own session or action credential, manage and rotate that value independently from the Raft client secret. + +## 6. Verify the deployed app + +Verify the deployed origin, not localhost and not only the provider preview. + +### Public surface + +- The origin serves valid HTTPS with no human-only perimeter in front of agent routes. +- The health endpoint returns success without credentials or sensitive details. +- The exact manifest URL returns the generated public manifest, if applicable. +- The response and deployment artifact do not contain the client secret, session secret, access token, or private environment file. + +### Human login + +1. Start Login with Raft from the deployed app. +2. Confirm the setup page names the expected app and Raft server. +3. Authorize and return to the exact deployed callback. +4. Confirm the app creates its own HttpOnly session and does not expose the raw Raft access token to browser JavaScript. +5. Sign out, revoke, or uninstall as appropriate and confirm access fails closed. + +### Agent login and actions + +For an app with a published agent manifest: + +```bash +raft integration list +raft integration login --service YOUR_SERVICE_SLUG +raft integration invoke --service YOUR_SERVICE_SLUG --list-actions +``` + +Invoke one read-only or disposable smoke action using the parameters declared by the manifest. Confirm invalid credentials, undeclared actions, malformed input, revoked access, and duplicate write retries fail safely. + +If the Raft integration inventory does not list the service, check app availability, marketplace installation, manifest discovery, and the current agent's grant before debugging the action handler. + +## 7. Prepare rollback before wider use + +Keep the prior known-good artifact or deployment revision until the new revision passes the full smoke test. + +If the new revision fails: + +1. Stop routing traffic to it or redeploy the previous exact artifact. +2. Keep the Raft registration in place unless the registration itself is unsafe. +3. Verify the old homepage, callback, session, and manifest paths. +4. If a secret was exposed, rotate it in Raft and the serving secret store, deploy the new value, verify it, then revoke the old value. + +Do not delete the app registration as the first rollback step. Deletion changes identity and grants, and can make recovery harder than restoring a previous serving revision. + +## Troubleshooting + +| Symptom | Check first | +| --- | --- | +| Raft rejects the callback | Compare the deployed URL with the registered redirect URI character for character. Check HTTPS, hostname, port, path, and environment. | +| Login returns to setup or a not-implemented page | The scaffold is still fail closed because the callback exchange or local session has not been implemented. | +| Human login works but Agent Login does not | Check the agent callback, app availability/installation, manifest, agent grant, and any human-only access perimeter. | +| Manifest returns the frontend HTML or 404 | Fix SPA rewrites or route precedence so the Worker/server handles the manifest path before static fallback. | +| Actions return 401 or 403 | Check the service-local credential/session, install and grant state, declared auth type, and whether preview credentials were mixed with production. | +| Sessions disappear after a restart | Replace in-memory fixture state with durable, expiring, revocable session storage. | +| Preview works but production fails | Compare the two registrations, callback URLs, origins, secret revisions, storage namespaces, and deployed commit. | + +## Production checklist + +- [ ] One stable HTTPS production origin +- [ ] Exact production callback and manifest URLs registered +- [ ] Preview and production registrations and secrets separated +- [ ] Client secret present only in the server-side managed secret store +- [ ] OAuth state, sessions, grants, and idempotency records are durable and expiring where required +- [ ] Human and agent flows tested on the deployed origin +- [ ] Revocation, uninstall, invalid input, and replay paths fail closed +- [ ] Logs and receipts contain no secrets or raw access tokens +- [ ] Previous exact artifact and rollback steps recorded + +Continue with [Login with Raft](/developers/login-with-raft/) for protocol details and [Connected Apps](/features/apps/) for availability, installation, and marketplace behavior. diff --git a/content/developers/raft-apps/index.md b/content/developers/raft-apps/index.md index c5c6bd8..6fb8c78 100644 --- a/content/developers/raft-apps/index.md +++ b/content/developers/raft-apps/index.md @@ -8,7 +8,7 @@ llms_summary: "Read when you need a high-level overview of what Raft Apps are an Raft Apps are external tools that plug into a Raft server. They can let humans and agents sign in with their Raft identity, expose agent actions through a manifest, and send structured app notifications to agents when the server has installed or registered the app. -Use this page when you are deciding what kind of app to build. Use [Build a Raft App](/developers/raft-apps/build/) when you are ready to scaffold and register one. Use [Login with Raft](/developers/login-with-raft/) when you need the OAuth protocol details. +Use this page when you are deciding what kind of app to build. Use [Build a Raft App](/developers/raft-apps/build/) when you are ready to scaffold one, [Deploy a Raft App](/developers/raft-apps/deploy/) when it works locally, and [Login with Raft](/developers/login-with-raft/) when you need the OAuth protocol details. ## What a Raft App can do @@ -39,11 +39,12 @@ Most apps follow this path: 1. Decide which surfaces you need: login, agent actions, notifications, or a combination. 2. Scaffold or implement the app using [Build a Raft App](/developers/raft-apps/build/). -3. Register the app in Raft with its name, homepage, callback URL, primary category, and optional manifest URL. -4. Generate a client secret and keep it server-only. -5. Test login, userinfo, serverinfo, and any manifest actions or notifications in a development server. -6. If the app should be public, request marketplace review. -7. After approval, server owners or admins install it from **Settings → Connected Apps → Marketplace**. +3. Deploy it to a stable HTTPS origin, then register the app in Raft with its name, homepage, exact callback URL, primary category, and optional manifest URL. +4. Generate a client secret, store it only in the serving environment, and redeploy. +5. Test login, userinfo, serverinfo, and any manifest actions or notifications on the deployed origin. +6. Record a rollback to the prior serving revision. +7. If the app should be public, request marketplace review. +8. After approval, server owners or admins install it from **Settings → Connected Apps → Marketplace**. ## Identity and permissions @@ -71,5 +72,6 @@ Use the examples as implementation references, then verify the exact contract yo ## Next steps - Start with [Build a Raft App](/developers/raft-apps/build/) for scaffolding, local development, registration, and testing. +- Continue with [Deploy a Raft App](/developers/raft-apps/deploy/) for HTTPS hosting, environment separation, exact URL registration, deployed smoke tests, and rollback. - Read [Login with Raft](/developers/login-with-raft/) for setup URLs, callback handling, token exchange, userinfo, serverinfo, agent access, and app notifications. - Read [Connected Apps](/features/apps/) for the user-facing marketplace, install, uninstall, and server-admin model. From 514ca10af570b74f8497d95961001747a725903e Mon Sep 17 00:00:00 2001 From: Peng Date: Mon, 3 Aug 2026 14:36:52 +0800 Subject: [PATCH 2/6] docs(raft-apps): fold deployment guidance into build guide Signed-off-by: Peng --- .vitepress/config.mts | 1 - content/developers/raft-apps/build/index.md | 114 +++++++++- content/developers/raft-apps/deploy/index.md | 211 ------------------- content/developers/raft-apps/index.md | 14 +- 4 files changed, 117 insertions(+), 223 deletions(-) delete mode 100644 content/developers/raft-apps/deploy/index.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 522d867..8b1df8d 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -231,7 +231,6 @@ export default defineConfig({ items: [ { text: 'Overview', link: '/developers/raft-apps/' }, { text: 'Build a Raft App', link: '/developers/raft-apps/build/' }, - { text: 'Deploy a Raft App', link: '/developers/raft-apps/deploy/' }, { text: 'Login with Raft', link: '/developers/login-with-raft/' }, ], }, diff --git a/content/developers/raft-apps/build/index.md b/content/developers/raft-apps/build/index.md index 2a1c2f8..e088447 100644 --- a/content/developers/raft-apps/build/index.md +++ b/content/developers/raft-apps/build/index.md @@ -1,7 +1,7 @@ --- llms_section: "Developers" llms_order: 890 -llms_summary: "Read when you are ready to scaffold, register, and locally test a Raft App with create-raft-app." +llms_summary: "Read when you are ready to choose a Raft App architecture, scaffold it with create-raft-app, deploy it, register it, and verify it." --- # Build a Raft App @@ -12,7 +12,7 @@ The fastest way to start is `create-raft-app`. It gives you a contract-shaped pr The fastest way to build a Raft App is to hand this page — and [Login with Raft](/developers/login-with-raft/) — to your agent. -Your input is the product decision set, in one message: app name, homepage, callback URL, category, description, which app surfaces you need, and whether the app stays server-local or requests marketplace publication. +Your input is the product decision set, in one message: app name, category, description, which app surfaces you need, whether it stores records or files, whether it runs background work, any provider or budget constraint, and whether the app stays server-local or requests marketplace publication. From there, the agent scaffolds with `create-raft-app`, follows the generated `README.md` and `AGENTS.md`, prepares the registration, and pauses exactly once — for the owner or admin approval card. The client secret is shown once to the app owner and belongs in the serving environment — never in chat, browser JavaScript, or the repo. @@ -46,7 +46,115 @@ npm run dev Each template ships its own `README.md` and `AGENTS.md`. Treat those files as the source of truth for that template's exact environment variables, callback URLs, and local commands. -Once the app works locally, use [Deploy a Raft App](/developers/raft-apps/deploy/) to choose a runtime, create a stable HTTPS origin, register exact callback URLs, store secrets, verify the deployed flow, and prepare rollback. +## Choose what to deploy + +You do not need to know the provider vocabulary before you start. Tell your agent what the app must do. The agent should ask these questions before recommending a stack: + +1. Is this only a public page, or must humans or agents sign in with Raft? +2. Does the app need server-side API routes or agent actions? +3. Does it store structured records? +4. Does it accept file uploads or produce downloadable files? +5. Does work continue after the request finishes? +6. Do you already have a provider or domain, and do budget, region, or data residency constrain the choice? + +The answer should be the smallest component set that satisfies those requirements. If you do not have a provider account, the agent can guide account creation and deployment, but a human may still need to accept provider terms, choose billing, verify a domain, or complete a Human verification challenge. + +### Match the app to its components + +| App shape | Components you need | Concrete combinations | +| --- | --- | --- | +| Public information or demo page, with no private credential | Static assets only | [Cloudflare Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/) or a [Vercel deployment](https://vercel.com/docs/deployments/overview) | +| Login with Raft, private API, or agent actions | Static assets plus a server, Worker, or serverless function with managed secrets and session storage | `hono-react-cfworker` on Cloudflare Workers, or a frontend plus [Vercel Functions](https://vercel.com/docs/functions) | +| App with structured records | The previous row plus a database | Cloudflare Worker + [D1](https://developers.cloudflare.com/d1/), or Vercel Functions + a Postgres provider from the [Vercel Marketplace](https://vercel.com/marketplace?category=storage) | +| App with uploads or generated files | The previous row plus object storage | Cloudflare Worker + [R2](https://developers.cloudflare.com/r2/), or Vercel Functions + [Vercel Blob](https://vercel.com/docs/vercel-blob) | +| App with imports, webhooks, retries, or other background work | The previous row plus a queue or long-running worker | Cloudflare Worker + [Queues](https://developers.cloudflare.com/queues/), [Vercel Queues](https://vercel.com/docs/queues) where its current Beta status is acceptable, or a separately hosted worker process | + +A pure frontend can only display public information or call APIs that require no private credential. Login with Raft uses an authorization-code exchange, so it requires a server-side callback and a client secret. Never put that secret in browser JavaScript. + +The current `create-raft-app` templates are code and contract presets, not one-click deployment plans. Their generated `README.md` and `AGENTS.md` are the source of truth for exact routes, bindings, environment variables, and commands. The `hono-react-cfworker` template is the closest current beginner path when an app needs a frontend, server logic, database, files, and background work in one Cloudflare project. A Vercel composition is possible, but no current template should be described as a Vercel deployment preset unless its generated files say so. + +### Deploy in a safe order + +Local development ends at `localhost`. Registration needs the stable HTTPS origin that will serve your app, callbacks, and optional agent manifest. + +1. Build the production artifact and run the generated checks. +2. Create only the runtime, database, object storage, and queue selected above. +3. Deploy a fail-closed revision. Login and protected actions should remain unavailable until credentials are installed. +4. Register the exact public origin, callback URLs, and optional manifest URL in Raft. +5. Put the one-time client secret directly into the provider's managed secret store. +6. Deploy or restart again, then read back and smoke-test the running service. + +Preview and production should use different registrations, client secrets, state stores, and stable hostnames. Do not use a changing preview URL or temporary tunnel as a production callback. Deployment does not grant Raft access. Registration, marketplace review, installation, and the human or agent grant remain separate authority gates. + +Follow the existing [Login with Raft secret-placement rule](/developers/login-with-raft/#two-rules-that-prevent-the-two-most-common-failures): the secret must exist where the app runs. A secret configured in a repo host is not automatically present in the serving environment, and a green deploy does not prove auth works. + +### Cloudflare Worker example + +The current `hono-react-cfworker` template includes a Worker, static React assets, D1, R2, and a Queue. Follow its generated README, create the bindings, and replace every placeholder ID before deployment: + +```bash +cd worker +npx wrangler d1 create YOUR_APP +npx wrangler r2 bucket create YOUR_APP-files +npx wrangler queues create YOUR_APP-events +npx wrangler d1 migrations apply YOUR_APP --remote +``` + +Bind the stable custom domain and set the public configuration required by the generated README. Store each secret with `wrangler secret put`, which reads the value interactively instead of placing it in `wrangler.toml`, source control, or the command line: + +```bash +npx wrangler secret put YOUR_SECRET_NAME +cd .. +npm run deploy +``` + +These commands match the generated Worker template. @曼波 field-verified this Cloudflare sequence against a real Worker deployment completed on 2026-08-02. A different template or provider may need different resources and commands. + +### Verify the running service + +Verify the deployed origin itself: + +- The provider reports the expected revision serving traffic. +- The public origin and non-sensitive health route return success over HTTPS. +- The generated manifest URL returns the manifest, not frontend HTML or a 404. +- An unauthenticated action returns the expected typed authorization failure, not a 500 or HTML error page. +- Human Login with Raft returns through the exact registered callback and creates an app-local HttpOnly session. +- Agent Login can discover the service and invoke one read-only or disposable smoke action. +- A real browser loads the most complex public page with no Content Security Policy violation in the console. +- The deployed artifact, public responses, logs, and receipts contain no client secret, session secret, access token, or private environment file. + +Use the exact paths from the generated README. For a manifest-backed action service, the agent-side smoke is: + +```bash +raft integration list +raft integration login --service YOUR_SERVICE_SLUG +raft integration invoke --service YOUR_SERVICE_SLUG --list-actions +``` + +If the service is absent from `integration list`, check registration or installation, manifest discovery, and the current agent's grant before debugging the action handler. + +### Recognize common failures + +| What you see | Likely cause and next check | +| --- | --- | +| Raft rejects the callback | The deployed URL and registered redirect URI differ. Compare scheme, hostname, port, path, and environment character for character. | +| Login returns to setup or a not-implemented page | The scaffold is still fail closed. Complete the real callback exchange and app-local session. | +| Human login works but Agent Login does not | Check the agent callback, app availability, manifest, agent grant, and any human-only access perimeter. | +| Manifest returns frontend HTML or 404 | SPA fallback or route precedence is shadowing the manifest route. The Worker or server must handle API and manifest paths first. | +| Sessions disappear after a restart | Fixture state is still in memory. Move sessions and revocation state to durable, expiring storage. | +| A page is blank while `curl` is green | `curl` does not execute Content Security Policy. Open the page in a real browser and inspect console violations. | +| A custom D1 migration runner reports `incomplete input` | Use the generated Wrangler migration command, then read back the remote schema instead of using an unverified SQL splitter. | +| The one-time client secret is no longer available | The app owner can generate a replacement with `raft integration app rotate-secret --client YOUR_CLIENT_KEY`. Store the new value immediately. Rotation invalidates the previous secret. | + +The rotation behavior above is enforced by the Raft server, and @曼波 also verified the recovery path after missing a show-once secret on 2026-08-01. + +### Keep rollback boring + +Keep the prior known-good artifact or deployment revision until the new revision passes the full smoke test. If the new revision fails, route traffic back or redeploy the prior exact artifact, then verify the old callback, session, and manifest paths. + +Do not delete the Raft app registration as the first rollback step. Deletion changes app identity and grants. If a secret was exposed, rotate it, deploy the new value, verify it, and only then retire the old revision. + +Bring-up is complete when the deployed origin, callback, manifest or actions, secret-absence checks, and rollback path have all been verified against the running service. ## Register it in Raft diff --git a/content/developers/raft-apps/deploy/index.md b/content/developers/raft-apps/deploy/index.md deleted file mode 100644 index 0bc37ec..0000000 --- a/content/developers/raft-apps/deploy/index.md +++ /dev/null @@ -1,211 +0,0 @@ ---- -llms_section: "Developers" -llms_order: 895 -llms_summary: "Read when your Raft App works locally and you need to deploy it, register exact production URLs, verify login or actions, and prepare rollback." ---- - -# Deploy a Raft App - -A Raft App is ready to register only after it has a stable HTTPS origin. This tutorial takes a locally working app through deployment, registration, verification, and rollback without putting a client secret in the browser or repository. - -The safe sequence is: - -1. Deploy a fail-closed app and obtain its public origin. -2. Register that exact origin, callback, and optional manifest URL in Raft. -3. Add the issued credentials to the serving environment. -4. Redeploy or restart, then verify the complete flow. - -If you have not scaffolded an app yet, start with [Build a Raft App](/developers/raft-apps/build/). - -## Choose a deployment shape - -| Shape | Good fit | Important boundary | -| --- | --- | --- | -| Static frontend + server-side API | A mostly static UI hosted on Pages, Vercel, or a CDN | The callback, token exchange, sessions, and authenticated actions still need a Worker, serverless function, or web server. Static browser code must never receive the client secret. | -| Long-running Node web service | The Express-based sign-in or action-service templates | Run the generated build and start commands on a host with HTTPS, managed secrets, logs, and durable session storage. | -| Worker/serverless app | The `hono-react-cfworker` template or an app already designed for that runtime | Use platform bindings for state and managed secrets. Do not assume an in-memory session survives another request or deployment. | - -Deployment does not grant Raft access. Registration, marketplace review, installation, and each user's or agent's grant remain separate Raft authority gates. - -## 1. Finish the server-side paths - -Generated templates are intentionally fail closed. Before you deploy, complete the paths your app actually needs: - -- the browser login start route -- the exact human callback route -- the server-side authorization-code exchange -- userinfo lookup and a local HttpOnly session -- the agent callback and service-local agent session, if the app supports Agent Login -- manifest-declared action routes, input validation, authorization, and idempotency -- a non-sensitive health endpoint or equivalent public readiness check - -Do not treat a successful scaffold build as proof that OAuth is implemented. The template `README.md` and `AGENTS.md` are the source of truth for its routes, environment variables, and remaining fail-closed markers. - -Run the generated checks before deploying: - -```bash -npm install -npm run build -``` - -If the template exposes a different check command, use the command in its README as well. - -## 2. Separate preview and production - -Use different app registrations and credentials for preview/staging and production. Do not point a preview deployment at a production client secret. - -| Value | Preview example | Production example | -| --- | --- | --- | -| App origin | `https://preview.example.dev` | `https://app.example.com` | -| Human callback | `https://preview.example.dev/auth/callback` | `https://app.example.com/auth/callback` | -| Agent callback | `https://preview.example.dev/agent/callback` | `https://app.example.com/agent/callback` | -| Client ID and secret | Preview registration | Production registration | -| Session/action secrets | Preview-only values | Production-only values | -| State and storage | Preview namespace | Production namespace | - -For generated Node templates, the main variables are typically: - -- `APP_ORIGIN` — your deployed app's public origin -- `RAFT_APP_ORIGIN` — the Raft browser origin that serves the setup page -- `RAFT_API_ORIGIN` — the Raft API used for token exchange and userinfo -- `RAFT_CLIENT_ID` — the registered app's client ID -- `RAFT_CLIENT_SECRET` — the registered app's server-only secret -- `SESSION_SECRET` or an action/session credential required by that template - -`APP_ORIGIN` is your app. `RAFT_APP_ORIGIN` and `RAFT_API_ORIGIN` are Raft. Mixing these values is a common cause of redirects that loop or return to the wrong environment. - -## 3. Deploy without the Raft secret - -First deploy the app with its public configuration and no usable Raft client secret. Login and protected actions should fail closed, while the homepage, health check, and public manifest remain reachable. - -### Node web service - -Use a Node host that supports a stable HTTPS hostname and managed environment variables. Configure the generated commands: - -```text -Build command: npm install && npm run build -Start command: npm start -``` - -Set `APP_ORIGIN` to the final HTTPS origin. Add durable storage before relying on any template that currently keeps sessions, OAuth state, idempotency records, or agent grants in memory. - -### Cloudflare Worker template - -The `hono-react-cfworker` template includes a Worker, React assets, D1, R2, and Queue bindings. Follow its generated README to create the resources and replace every placeholder ID before deployment: - -```bash -cd worker -npx wrangler d1 create YOUR_APP -npx wrangler r2 bucket create YOUR_APP-files -npx wrangler queues create YOUR_APP-events -npx wrangler d1 migrations apply YOUR_APP --remote -``` - -Set the final `APP_ORIGIN` and public Raft origins in `worker/wrangler.toml`, then deploy from the project root: - -```bash -npm install -npm run deploy -``` - -Do not add `RAFT_CLIENT_SECRET` to `wrangler.toml`. Add it as a managed Worker secret only after registration. - -## 4. Register the exact deployed URLs - -In the Raft server that should own the app, open **Settings → Connected Apps → My Apps** and register: - -- the deployed homepage or app origin -- every exact callback URL the app supports -- the primary category and description -- the public agent manifest URL, if the app exposes agent actions - -The scheme, hostname, port, and path must match the deployed route exactly. Production callbacks must use HTTPS. Do not register a wildcard callback or a preview hostname as the production callback. - -An agent can prepare the same registration with `raft integration app prepare register`; a server owner or admin still commits it through the approval card. Raft then issues a client ID and shows the client secret once to the app owner. - -## 5. Add credentials and redeploy - -Put credentials directly into the deployment platform's managed secret store. Never place them in source control, a public manifest, browser JavaScript, build output, chat, screenshots, or command-line arguments that will be retained in shell history or process listings. - -For the Cloudflare Worker template: - -```bash -cd worker -npx wrangler secret put RAFT_CLIENT_SECRET -``` - -Set `RAFT_CLIENT_ID` as the public deployment variable, then redeploy. For a Node host, add the client ID and secret in the provider's environment/secret settings and restart or roll out a new revision. - -If the app has its own session or action credential, manage and rotate that value independently from the Raft client secret. - -## 6. Verify the deployed app - -Verify the deployed origin, not localhost and not only the provider preview. - -### Public surface - -- The origin serves valid HTTPS with no human-only perimeter in front of agent routes. -- The health endpoint returns success without credentials or sensitive details. -- The exact manifest URL returns the generated public manifest, if applicable. -- The response and deployment artifact do not contain the client secret, session secret, access token, or private environment file. - -### Human login - -1. Start Login with Raft from the deployed app. -2. Confirm the setup page names the expected app and Raft server. -3. Authorize and return to the exact deployed callback. -4. Confirm the app creates its own HttpOnly session and does not expose the raw Raft access token to browser JavaScript. -5. Sign out, revoke, or uninstall as appropriate and confirm access fails closed. - -### Agent login and actions - -For an app with a published agent manifest: - -```bash -raft integration list -raft integration login --service YOUR_SERVICE_SLUG -raft integration invoke --service YOUR_SERVICE_SLUG --list-actions -``` - -Invoke one read-only or disposable smoke action using the parameters declared by the manifest. Confirm invalid credentials, undeclared actions, malformed input, revoked access, and duplicate write retries fail safely. - -If the Raft integration inventory does not list the service, check app availability, marketplace installation, manifest discovery, and the current agent's grant before debugging the action handler. - -## 7. Prepare rollback before wider use - -Keep the prior known-good artifact or deployment revision until the new revision passes the full smoke test. - -If the new revision fails: - -1. Stop routing traffic to it or redeploy the previous exact artifact. -2. Keep the Raft registration in place unless the registration itself is unsafe. -3. Verify the old homepage, callback, session, and manifest paths. -4. If a secret was exposed, rotate it in Raft and the serving secret store, deploy the new value, verify it, then revoke the old value. - -Do not delete the app registration as the first rollback step. Deletion changes identity and grants, and can make recovery harder than restoring a previous serving revision. - -## Troubleshooting - -| Symptom | Check first | -| --- | --- | -| Raft rejects the callback | Compare the deployed URL with the registered redirect URI character for character. Check HTTPS, hostname, port, path, and environment. | -| Login returns to setup or a not-implemented page | The scaffold is still fail closed because the callback exchange or local session has not been implemented. | -| Human login works but Agent Login does not | Check the agent callback, app availability/installation, manifest, agent grant, and any human-only access perimeter. | -| Manifest returns the frontend HTML or 404 | Fix SPA rewrites or route precedence so the Worker/server handles the manifest path before static fallback. | -| Actions return 401 or 403 | Check the service-local credential/session, install and grant state, declared auth type, and whether preview credentials were mixed with production. | -| Sessions disappear after a restart | Replace in-memory fixture state with durable, expiring, revocable session storage. | -| Preview works but production fails | Compare the two registrations, callback URLs, origins, secret revisions, storage namespaces, and deployed commit. | - -## Production checklist - -- [ ] One stable HTTPS production origin -- [ ] Exact production callback and manifest URLs registered -- [ ] Preview and production registrations and secrets separated -- [ ] Client secret present only in the server-side managed secret store -- [ ] OAuth state, sessions, grants, and idempotency records are durable and expiring where required -- [ ] Human and agent flows tested on the deployed origin -- [ ] Revocation, uninstall, invalid input, and replay paths fail closed -- [ ] Logs and receipts contain no secrets or raw access tokens -- [ ] Previous exact artifact and rollback steps recorded - -Continue with [Login with Raft](/developers/login-with-raft/) for protocol details and [Connected Apps](/features/apps/) for availability, installation, and marketplace behavior. diff --git a/content/developers/raft-apps/index.md b/content/developers/raft-apps/index.md index 6fb8c78..c5c6bd8 100644 --- a/content/developers/raft-apps/index.md +++ b/content/developers/raft-apps/index.md @@ -8,7 +8,7 @@ llms_summary: "Read when you need a high-level overview of what Raft Apps are an Raft Apps are external tools that plug into a Raft server. They can let humans and agents sign in with their Raft identity, expose agent actions through a manifest, and send structured app notifications to agents when the server has installed or registered the app. -Use this page when you are deciding what kind of app to build. Use [Build a Raft App](/developers/raft-apps/build/) when you are ready to scaffold one, [Deploy a Raft App](/developers/raft-apps/deploy/) when it works locally, and [Login with Raft](/developers/login-with-raft/) when you need the OAuth protocol details. +Use this page when you are deciding what kind of app to build. Use [Build a Raft App](/developers/raft-apps/build/) when you are ready to scaffold and register one. Use [Login with Raft](/developers/login-with-raft/) when you need the OAuth protocol details. ## What a Raft App can do @@ -39,12 +39,11 @@ Most apps follow this path: 1. Decide which surfaces you need: login, agent actions, notifications, or a combination. 2. Scaffold or implement the app using [Build a Raft App](/developers/raft-apps/build/). -3. Deploy it to a stable HTTPS origin, then register the app in Raft with its name, homepage, exact callback URL, primary category, and optional manifest URL. -4. Generate a client secret, store it only in the serving environment, and redeploy. -5. Test login, userinfo, serverinfo, and any manifest actions or notifications on the deployed origin. -6. Record a rollback to the prior serving revision. -7. If the app should be public, request marketplace review. -8. After approval, server owners or admins install it from **Settings → Connected Apps → Marketplace**. +3. Register the app in Raft with its name, homepage, callback URL, primary category, and optional manifest URL. +4. Generate a client secret and keep it server-only. +5. Test login, userinfo, serverinfo, and any manifest actions or notifications in a development server. +6. If the app should be public, request marketplace review. +7. After approval, server owners or admins install it from **Settings → Connected Apps → Marketplace**. ## Identity and permissions @@ -72,6 +71,5 @@ Use the examples as implementation references, then verify the exact contract yo ## Next steps - Start with [Build a Raft App](/developers/raft-apps/build/) for scaffolding, local development, registration, and testing. -- Continue with [Deploy a Raft App](/developers/raft-apps/deploy/) for HTTPS hosting, environment separation, exact URL registration, deployed smoke tests, and rollback. - Read [Login with Raft](/developers/login-with-raft/) for setup URLs, callback handling, token exchange, userinfo, serverinfo, agent access, and app notifications. - Read [Connected Apps](/features/apps/) for the user-facing marketplace, install, uninstall, and server-admin model. From fc1a144b85ed3263bec234cdd8a86d1896c9011c Mon Sep 17 00:00:00 2001 From: Peng Date: Mon, 3 Aug 2026 14:40:32 +0800 Subject: [PATCH 3/6] docs(raft-apps): separate hosting from registration Signed-off-by: Peng --- content/developers/raft-apps/build/index.md | 100 +++++++++++--------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/content/developers/raft-apps/build/index.md b/content/developers/raft-apps/build/index.md index e088447..b75fb26 100644 --- a/content/developers/raft-apps/build/index.md +++ b/content/developers/raft-apps/build/index.md @@ -12,9 +12,49 @@ The fastest way to start is `create-raft-app`. It gives you a contract-shaped pr The fastest way to build a Raft App is to hand this page — and [Login with Raft](/developers/login-with-raft/) — to your agent. -Your input is the product decision set, in one message: app name, category, description, which app surfaces you need, whether it stores records or files, whether it runs background work, any provider or budget constraint, and whether the app stays server-local or requests marketplace publication. +Your input is the product decision set, in one message: app name, category, description, which app surfaces you need, whether it stores records or files, whether it runs background work, the deployment owner, any provider or budget constraint, and whether the app stays server-local or requests marketplace publication. -From there, the agent scaffolds with `create-raft-app`, follows the generated `README.md` and `AGENTS.md`, prepares the registration, and pauses exactly once — for the owner or admin approval card. The client secret is shown once to the app owner and belongs in the serving environment — never in chat, browser JavaScript, or the repo. +From there, the agent scaffolds with `create-raft-app`, follows the generated `README.md` and `AGENTS.md`, prepares the registration, and pauses only for the human steps identified below. The client secret is shown once to the app owner and belongs in the serving environment, never in chat, browser JavaScript, or the repo. + +## Choose what to deploy + +You do not need to know the provider vocabulary before you start. Tell your agent what the app must do. The agent should ask these questions before recommending a stack: + +1. Is this only a public page, or must humans or agents sign in with Raft? +2. Does the app need server-side API routes or agent actions? +3. Does it store structured records? +4. Does it accept file uploads or produce downloadable files? +5. Does work continue after the request finishes? +6. Do you already have a provider or domain, and do budget, region, or data residency constrain the choice? + +First separate registration from hosting: + +- **Raft app registration** gives the app identity, callback URLs, ownership, and permission boundaries. +- **Hosting** is the provider account, runtime, storage, domain, billing, monitoring, and rollback that keep the service online. + +Registration does not give you a machine or permission to deploy into someone else's app. Name one deployment owner who is accountable for the provider account, billing, domain, and rollback. An agent can perform the technical work under that owner's authority. If nobody owns hosting yet, freeze the product specification and stop there instead of turning the conversation into a multi-cloud shopping lesson. + +For a nontechnical app owner, lead with their three actions: + +1. Name the deployment owner and accept any provider terms, billing choice, domain verification, or Human verification challenge that only a person can complete. +2. Approve the Raft registration card after the deployment owner provides the stable HTTPS URLs. +3. Open the deployed link, complete the acceptance flow, and know who can disable or roll back the app. + +The technical answer should be the smallest component set that satisfies the six requirements above. + +### Match the app to its components + +| App shape | Components you need | Concrete combinations | +| --- | --- | --- | +| Public information or demo page, with no private credential | Static assets only | [Cloudflare Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/) or a [Vercel deployment](https://vercel.com/docs/deployments/overview) | +| Login with Raft, private API, or agent actions | Static assets plus a server, Worker, or serverless function with managed secrets and session storage | `hono-react-cfworker` on Cloudflare Workers, or a frontend plus [Vercel Functions](https://vercel.com/docs/functions) | +| App with structured records | The previous row plus a database | Cloudflare Worker + [D1](https://developers.cloudflare.com/d1/), or Vercel Functions + a Postgres provider from the [Vercel Marketplace](https://vercel.com/marketplace?category=storage) | +| App with uploads or generated files | The previous row plus object storage | Cloudflare Worker + [R2](https://developers.cloudflare.com/r2/), or Vercel Functions + [Vercel Blob](https://vercel.com/docs/vercel-blob) | +| App with imports, webhooks, retries, or other background work | The previous row plus a queue or long-running worker | Cloudflare Worker + [Queues](https://developers.cloudflare.com/queues/), [Vercel Queues](https://vercel.com/docs/queues) where its current Beta status is acceptable, or a separately hosted worker process | + +A pure frontend can only display public information or call APIs that require no private credential. Login with Raft uses an authorization-code exchange, so it requires a server-side callback and a client secret. Never put that secret in browser JavaScript. + +The current `create-raft-app` templates are code and contract presets, not one-click deployment plans. Their generated `README.md` and `AGENTS.md` are the source of truth for exact routes, bindings, environment variables, and commands. The `hono-react-cfworker` template is the closest current beginner path when an app needs a frontend, server logic, database, files, and background work in one Cloudflare project. A Vercel composition is possible, but no current template should be described as a Vercel deployment preset unless its generated files say so. ## Scaffold the app @@ -46,32 +86,7 @@ npm run dev Each template ships its own `README.md` and `AGENTS.md`. Treat those files as the source of truth for that template's exact environment variables, callback URLs, and local commands. -## Choose what to deploy - -You do not need to know the provider vocabulary before you start. Tell your agent what the app must do. The agent should ask these questions before recommending a stack: - -1. Is this only a public page, or must humans or agents sign in with Raft? -2. Does the app need server-side API routes or agent actions? -3. Does it store structured records? -4. Does it accept file uploads or produce downloadable files? -5. Does work continue after the request finishes? -6. Do you already have a provider or domain, and do budget, region, or data residency constrain the choice? - -The answer should be the smallest component set that satisfies those requirements. If you do not have a provider account, the agent can guide account creation and deployment, but a human may still need to accept provider terms, choose billing, verify a domain, or complete a Human verification challenge. - -### Match the app to its components - -| App shape | Components you need | Concrete combinations | -| --- | --- | --- | -| Public information or demo page, with no private credential | Static assets only | [Cloudflare Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/) or a [Vercel deployment](https://vercel.com/docs/deployments/overview) | -| Login with Raft, private API, or agent actions | Static assets plus a server, Worker, or serverless function with managed secrets and session storage | `hono-react-cfworker` on Cloudflare Workers, or a frontend plus [Vercel Functions](https://vercel.com/docs/functions) | -| App with structured records | The previous row plus a database | Cloudflare Worker + [D1](https://developers.cloudflare.com/d1/), or Vercel Functions + a Postgres provider from the [Vercel Marketplace](https://vercel.com/marketplace?category=storage) | -| App with uploads or generated files | The previous row plus object storage | Cloudflare Worker + [R2](https://developers.cloudflare.com/r2/), or Vercel Functions + [Vercel Blob](https://vercel.com/docs/vercel-blob) | -| App with imports, webhooks, retries, or other background work | The previous row plus a queue or long-running worker | Cloudflare Worker + [Queues](https://developers.cloudflare.com/queues/), [Vercel Queues](https://vercel.com/docs/queues) where its current Beta status is acceptable, or a separately hosted worker process | - -A pure frontend can only display public information or call APIs that require no private credential. Login with Raft uses an authorization-code exchange, so it requires a server-side callback and a client secret. Never put that secret in browser JavaScript. - -The current `create-raft-app` templates are code and contract presets, not one-click deployment plans. Their generated `README.md` and `AGENTS.md` are the source of truth for exact routes, bindings, environment variables, and commands. The `hono-react-cfworker` template is the closest current beginner path when an app needs a frontend, server logic, database, files, and background work in one Cloudflare project. A Vercel composition is possible, but no current template should be described as a Vercel deployment preset unless its generated files say so. +## Deploy and bring it up ### Deploy in a safe order @@ -88,27 +103,18 @@ Preview and production should use different registrations, client secrets, state Follow the existing [Login with Raft secret-placement rule](/developers/login-with-raft/#two-rules-that-prevent-the-two-most-common-failures): the secret must exist where the app runs. A secret configured in a repo host is not automatically present in the serving environment, and a green deploy does not prove auth works. -### Cloudflare Worker example +### Cloudflare Worker beginner path -The current `hono-react-cfworker` template includes a Worker, static React assets, D1, R2, and a Queue. Follow its generated README, create the bindings, and replace every placeholder ID before deployment: +The current `hono-react-cfworker` template includes a Worker, static React assets, D1, R2, a Queue, `wrangler.toml`, an initial migration, and `npm run deploy`. Its generated README already owns the exact commands for creating resources, replacing binding IDs, applying the remote migration, writing secrets, and deploying. Follow that README instead of copying commands from this guide. -```bash -cd worker -npx wrangler d1 create YOUR_APP -npx wrangler r2 bucket create YOUR_APP-files -npx wrangler queues create YOUR_APP-events -npx wrangler d1 migrations apply YOUR_APP --remote -``` +The deployment owner still needs to complete what the template cannot automate: -Bind the stable custom domain and set the public configuration required by the generated README. Store each secret with `wrangler secret put`, which reads the value interactively instead of placing it in `wrangler.toml`, source control, or the command line: - -```bash -npx wrangler secret put YOUR_SECRET_NAME -cd .. -npm run deploy -``` +- Create or choose the Cloudflare account and confirm its billing and region constraints. +- Bind a stable custom domain instead of registering a temporary preview hostname. +- Verify the remote D1 schema, Worker revision, bindings, health route, callback, and manifest after deployment. +- Keep the prior deployment available until the new revision passes acceptance. -These commands match the generated Worker template. @曼波 field-verified this Cloudflare sequence against a real Worker deployment completed on 2026-08-02. A different template or provider may need different resources and commands. +@曼波 field-verified this full Cloudflare sequence against a real Worker deployment completed on 2026-08-02. A different template or provider may need different resources and commands. ### Verify the running service @@ -144,9 +150,9 @@ If the service is absent from `integration list`, check registration or installa | Sessions disappear after a restart | Fixture state is still in memory. Move sessions and revocation state to durable, expiring storage. | | A page is blank while `curl` is green | `curl` does not execute Content Security Policy. Open the page in a real browser and inspect console violations. | | A custom D1 migration runner reports `incomplete input` | Use the generated Wrangler migration command, then read back the remote schema instead of using an unverified SQL splitter. | -| The one-time client secret is no longer available | The app owner can generate a replacement with `raft integration app rotate-secret --client YOUR_CLIENT_KEY`. Store the new value immediately. Rotation invalidates the previous secret. | +| The one-time client secret is no longer available | The app owner can rotate it. Run `raft integration app rotate-secret --help` first because secret delivery differs by CLI generation. Rotation invalidates the previous secret. | -The rotation behavior above is enforced by the Raft server, and @曼波 also verified the recovery path after missing a show-once secret on 2026-08-01. +If `--help` lists `--output`, pass `--output ` on a supported POSIX host. The CLI creates that new path as a mode-0600 file, rejects an existing path, and emits only a sanitized receipt. On Windows this file sink fails closed, so use an authorized secret-store carrier. Older CLI releases without `--output` return the replacement secret once in stdout or JSON. Prefer upgrading; if you must use the older flow, capture that value only through a private secret handoff and store it immediately. Do not run the rotation command itself to detect the behavior because it changes the live secret. @曼波 also verified the older recovery path after missing a private transient initial-secret notice on 2026-08-01. ### Keep rollback boring From bbb0baad6430ee37cfc797897a3f6523420a0e27 Mon Sep 17 00:00:00 2001 From: Peng Date: Mon, 3 Aug 2026 14:46:30 +0800 Subject: [PATCH 4/6] docs(raft-apps): keep field evidence public-safe Signed-off-by: Peng --- content/developers/raft-apps/build/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/developers/raft-apps/build/index.md b/content/developers/raft-apps/build/index.md index b75fb26..7346efa 100644 --- a/content/developers/raft-apps/build/index.md +++ b/content/developers/raft-apps/build/index.md @@ -114,7 +114,7 @@ The deployment owner still needs to complete what the template cannot automate: - Verify the remote D1 schema, Worker revision, bindings, health route, callback, and manifest after deployment. - Keep the prior deployment available until the new revision passes acceptance. -@曼波 field-verified this full Cloudflare sequence against a real Worker deployment completed on 2026-08-02. A different template or provider may need different resources and commands. +This sequence was field-verified against a real Cloudflare Worker deployment on 2026-08-02. A different template or provider may need different resources and commands. ### Verify the running service @@ -152,7 +152,7 @@ If the service is absent from `integration list`, check registration or installa | A custom D1 migration runner reports `incomplete input` | Use the generated Wrangler migration command, then read back the remote schema instead of using an unverified SQL splitter. | | The one-time client secret is no longer available | The app owner can rotate it. Run `raft integration app rotate-secret --help` first because secret delivery differs by CLI generation. Rotation invalidates the previous secret. | -If `--help` lists `--output`, pass `--output ` on a supported POSIX host. The CLI creates that new path as a mode-0600 file, rejects an existing path, and emits only a sanitized receipt. On Windows this file sink fails closed, so use an authorized secret-store carrier. Older CLI releases without `--output` return the replacement secret once in stdout or JSON. Prefer upgrading; if you must use the older flow, capture that value only through a private secret handoff and store it immediately. Do not run the rotation command itself to detect the behavior because it changes the live secret. @曼波 also verified the older recovery path after missing a private transient initial-secret notice on 2026-08-01. +If `--help` lists `--output`, pass `--output ` on a supported POSIX host. The CLI creates that new path as a mode-0600 file, rejects an existing path, and emits only a sanitized receipt. On Windows this file sink fails closed, so use an authorized secret-store carrier. On older CLI releases without `--output`, the `--json` response includes the replacement secret once. Prefer upgrading; if you must use the older flow, capture that value only through a private secret handoff and store it immediately. Do not run the rotation command itself to detect the behavior because it changes the live secret. The older recovery path was field-verified on 2026-08-01 after a private transient initial-secret notice was missed. ### Keep rollback boring From 02f33d41365202a2cbc18bbe1dda5e5347b4271e Mon Sep 17 00:00:00 2001 From: Peng Date: Mon, 3 Aug 2026 14:50:33 +0800 Subject: [PATCH 5/6] docs(raft-apps): keep cloud choice with deploy owner Signed-off-by: Peng --- content/developers/raft-apps/build/index.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/content/developers/raft-apps/build/index.md b/content/developers/raft-apps/build/index.md index 7346efa..5d9505d 100644 --- a/content/developers/raft-apps/build/index.md +++ b/content/developers/raft-apps/build/index.md @@ -38,11 +38,11 @@ For a nontechnical app owner, lead with their three actions: 1. Name the deployment owner and accept any provider terms, billing choice, domain verification, or Human verification challenge that only a person can complete. 2. Approve the Raft registration card after the deployment owner provides the stable HTTPS URLs. -3. Open the deployed link, complete the acceptance flow, and know who can disable or roll back the app. +3. Open the deployed link, complete the acceptance flow, and verify that an admin can disable or unpublish the app. -The technical answer should be the smallest component set that satisfies the six requirements above. +The beginner path stops there. The deployment owner gets the technical comparison and should choose the smallest component set that satisfies the six requirements above, with one default recommendation and one-line rationale instead of a cloud catalog. -### Match the app to its components +### For the deployment owner: match the app to its components | App shape | Components you need | Concrete combinations | | --- | --- | --- | @@ -54,6 +54,8 @@ The technical answer should be the smallest component set that satisfies the six A pure frontend can only display public information or call APIs that require no private credential. Login with Raft uses an authorization-code exchange, so it requires a server-side callback and a client secret. Never put that secret in browser JavaScript. +Provider accounts, dashboards, and access gates control the hosting platform. They do not replace Login with Raft as the human or agent identity inside the app. + The current `create-raft-app` templates are code and contract presets, not one-click deployment plans. Their generated `README.md` and `AGENTS.md` are the source of truth for exact routes, bindings, environment variables, and commands. The `hono-react-cfworker` template is the closest current beginner path when an app needs a frontend, server logic, database, files, and background work in one Cloudflare project. A Vercel composition is possible, but no current template should be described as a Vercel deployment preset unless its generated files say so. ## Scaffold the app From 1206960e3e91d7f93d3510429d3a688e89294c56 Mon Sep 17 00:00:00 2001 From: Peng Date: Mon, 3 Aug 2026 16:20:14 +0800 Subject: [PATCH 6/6] docs(raft-apps): verify current storage offerings Signed-off-by: Peng --- content/developers/raft-apps/build/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/content/developers/raft-apps/build/index.md b/content/developers/raft-apps/build/index.md index 5d9505d..65d767e 100644 --- a/content/developers/raft-apps/build/index.md +++ b/content/developers/raft-apps/build/index.md @@ -52,6 +52,8 @@ The beginner path stops there. The deployment owner gets the technical compariso | App with uploads or generated files | The previous row plus object storage | Cloudflare Worker + [R2](https://developers.cloudflare.com/r2/), or Vercel Functions + [Vercel Blob](https://vercel.com/docs/vercel-blob) | | App with imports, webhooks, retries, or other background work | The previous row plus a queue or long-running worker | Cloudflare Worker + [Queues](https://developers.cloudflare.com/queues/), [Vercel Queues](https://vercel.com/docs/queues) where its current Beta status is acceptable, or a separately hosted worker process | +Provider products change. Before recommending one, the deployment owner should fresh-check its official documentation for runtime, database, object storage, background work, region, billing, limits, and rollback, then record the check date and source. Do not infer a missing capability from an older comparison. For example, as checked on 2026-08-03, Railway offers private [S3-compatible Storage Buckets](https://docs.railway.com/storage-buckets) inside a project, while Fly.io provisions [Tigris object storage](https://fly.io/docs/flyctl/storage/) as a managed third-party extension through `fly storage`. Neither is a current `create-raft-app` deployment preset, so choosing one still requires the deployment owner to adapt and verify the generated app. + A pure frontend can only display public information or call APIs that require no private credential. Login with Raft uses an authorization-code exchange, so it requires a server-side callback and a client secret. Never put that secret in browser JavaScript. Provider accounts, dashboards, and access gates control the hosting platform. They do not replace Login with Raft as the human or agent identity inside the app.