Fork Horse repository, listed by its owner. Publisher snapshot.
.agents/skills/cloudflare-os-operator/SKILL.md
Snapshot at 34e3175
1---
2name: cloudflare-os-operator
3description: Guides cloudflare/cloudflare-os-starter setup, Cloudflare Access, deployment.jsonc, storage, AI, observability, custom Gatekeepers, deployment, verification, troubleshooting, rollback, and pinned-submodule upgrades. Use only for deployments created from the Cloudflare OS Deployment Starter, not standalone Cloudflare OS checkouts.
4compatibility: Requires Git, the checkout-declared Node.js and pnpm versions, project-pinned Wrangler, and a Cloudflare account with the products used by the deployment.
5---
6
7# Cloudflare OS Operator
8
9Own the outcome, not just the next command. Guide the operator from an unprepared checkout to a verified deployment, or from a failure to a diagnosed and safe recovery. A provider outage, unavailable product, or incorrect external policy can never be ruled out in advance, so do not promise an issue-free deployment. Prevent avoidable failures and do not call the work complete without evidence.
10
11This skill operates the deployment wrapper, not Cloudflare OS in isolation. The wrapper pins Cloudflare OS as a submodule, derives temporary Wrangler configs, and coordinates several Workers.
12
13## Locate The Starter
14
15Find the repository root containing all of these markers:
16
17- `deployment.jsonc`
18- `scripts/deploy.ts`
19- `cloudflare-os/`
20- `packages/custom-gatekeeper/`
21
22Do not assume the current working directory or this skill's directory is the starter root. If the markers are absent, stop and ask for the starter checkout. Do not apply this workflow directly to a standalone Cloudflare OS checkout.
23
24Once the submodule is initialized, confirm `cloudflare-os/packages/router/` exists as well. The deploy binds and deploys it, so a pinned commit without it cannot be deployed by this wrapper; treat its absence as a provenance problem, not a missing build step.
25
26## Read Current Sources First
27
28Before any mutation, always read the current checkout's:
29
301. `README.md`
312. `deployment.jsonc`
323. `scripts/deploy.ts`
334. `package.json`
345. `.gitmodules`
356. Available relevant upstream files under `cloudflare-os/`
36
37Load `docs/customization.md`, `docs/observability.md`, package guides, and reference files from this skill only when their subject is relevant. If the submodule is unavailable, inspect all available wrapper sources and defer upstream-dependent advice until provenance is resolved.
38
39Inspect the deploy script rather than assuming its validation, generated paths, build steps, or deployment order. Consult current Cloudflare documentation before relying on product behavior, permissions, CLI syntax, limits, or rollback support. Repository code is authoritative for what this checkout does; current Cloudflare documentation is authoritative for the platform it calls. Surface conflicts instead of silently choosing one.
40
41Expected baseline commands are:
42
43```sh
44git submodule update --init
45pnpm install
46pnpm --dir cloudflare-os install
47pnpm exec wrangler login
48pnpm check
49pnpm deploy
50```
51
52Confirm them against the checkout before running them. Always use `pnpm`, never `npm` or `yarn`.
53
54## Operating Rules
55
56- Start read-only. Inventory before mutation.
57- Ask for all missing operator decisions in one concise batch.
58- Explain why each requested value is needed and where to find it.
59- Never guess an account ID, resource ID, hostname, Access issuer, audience, administrator, Worker name, provider, or existing-resource ownership.
60- Never expose a token, cookie, JWT, secret, prompt, request body, response body, or private log content in chat, tracked files, command arguments, or reports. For local development only, use Wrangler's supported local-secret mechanism after proving the file is ignored and the credential is non-production.
61- Use the project-pinned Wrangler through `pnpm exec wrangler`.
62- Run only one `pnpm check` or `pnpm deploy` per checkout at a time. Use separate worktrees for concurrency.
63- Change one root cause at a time. Re-run the narrow check, then the complete check.
64- Do not broaden permissions, disable security controls, recreate resources, or retry deployments blindly to make an error disappear.
65- Keep a sanitized operation record: root commit, submodule commit, target account, intended route, Worker names, resource identities, deployment/version IDs, decisions, verification, and rollback limits.
66
67## Classify The Operation
68
69Choose one mode before acting:
70
71- **Research:** read-only explanation or design.
72- **Evaluation:** a non-production deployment, usually on an explicitly protected `workers.dev` hostname.
73- **Production:** a custom domain and production data or users.
74- **Incident:** restore safety and service while preserving evidence.
75- **Upgrade:** move the pinned `cloudflare-os` gitlink to a reviewed commit.
76
77Also classify the deployment as a first deploy, continuation of an existing deployment, or replacement. If this is unclear, remain read-only.
78
79## Hard Stops
80
81### Secrets
82
83Never ask the operator to paste a secret. Never read or print a secret to confirm it. Never put production secrets in `deployment.jsonc`, Wrangler vars, repository-local environment files, tracked files, shell history, or chat.
84
85When AI is enabled, a human enters `CF_AI_GATEWAY_API_TOKEN` interactively for the exact Workshop Worker:
86
87```sh
88CLOUDFLARE_ACCOUNT_ID=<account-id> pnpm exec wrangler secret put CF_AI_GATEWAY_API_TOKEN --name <workshop-worker>
89```
90
91Confirm this account selector against current Wrangler documentation. Confirm the account, existing Worker ownership, and Worker name immediately before presenting or running the interactive command. `secret put` immediately deploys a new Worker version; require production approval, record its version ID, and verify it like any other deployment. Verify only that the secret name exists, never its value. If exposure is suspected, stop deployment and rotate or revoke the credential first.
92
93### Trust Boundary
94
95Require explicit operator approval before changing any of these:
96
97- Cloudflare account, Worker identity, route, DNS, or Access application.
98- Access issuer, audience, sign-in method, policy, administrator list, or allowed population.
99- Existing KV/R2 bindings, `publicBaseUrl`, `context.sharingDomain`, data ownership, or service bindings. The public origin is a data boundary as well as an address: `context.sharingDomain: null` derives from it, so moving the hostname moves the Context isolation boundary with it.
100- AI provider, billing, token scope, model logging, retention, or external observability export.
101- Gatekeeper authority, OAuth scopes, writes, observer policy, auto-provisioning, or ambience.
102- Reporter visibility, browser reporting, retention, or destination.
103- Durable Object migrations or storage schema.
104
105Show the authority before and after, affected users/data, cost and privacy implications, verification plan, and rollback limitations.
106
107### Production Mutation
108
109Do not run `pnpm deploy`, install or delete secrets, modify Access/DNS, adopt resources, or roll back production until the operator approves a mutation summary containing:
110
111- Exact account ID and hostname.
112- Root and submodule commits.
113- Every Worker to create or update.
114- Every resource to provision or adopt.
115- Access application and administrator policy.
116- AI, observability, Reporter, and Gatekeeper modes.
117- Last-known-good deployment IDs and rollback limitations.
118- Evidence that `pnpm check` passed.
119
120Never delete Workers, routes, Access applications, KV namespaces, R2 buckets, secrets, or user data as automatic cleanup or troubleshooting.
121
122## End-To-End Workflow
123
124### 1. Establish Provenance And State
125
126Record, without leaking credentials:
127
128```text
129operation mode and first/continuation/replacement:
130root commit and branch:
131root worktree changes:
132configured submodule URL:
133expected submodule gitlink:
134checked-out submodule commit and status:
135Node, pnpm, and Wrangler versions:
136Wrangler authentication source and target account:
137route, hostname, and resolved public origin:
138Worker names and whether each is new, owned, or conflicting:
139storage choice and ownership for each binding:
140AI Gateway name, providers, and whether a token is required:
141error-reporting mode:
142custom Gatekeeper provenance and intended policy:
143last-known-good deployment/version IDs:
144```
145
146Inspect `.gitmodules` before initializing. If its URL is private or inaccessible, do not request SSH credentials, accept an unknown host key, rewrite the URL, or substitute a branch head. Offer these choices:
147
1481. Use an already-authorized source.
1492. Use a public mirror only after proving the exact pinned commit exists there and receiving approval for the provenance change.
1503. Stop.
151
152The checked-out submodule commit must equal the parent gitlink. Never use `git submodule update --remote` or an unreviewed "latest" commit.
153
154Check whether a `CLOUDFLARE_API_TOKEN` environment variable is present without printing it; it may override an expected Wrangler profile. Use sanitized `pnpm exec wrangler whoami` output to prove the account matches `deployment.jsonc.accountId` before mutation. Never run a command that prints the active authentication token.
155
156Inventory remote Worker-name and route collisions. A syntactically valid Worker name can overwrite an unrelated Worker in the same account. Stop on unknown ownership.
157
158### 2. Collect Deployment Decisions
159
160Ask once for any decisions not established by existing approved configuration:
161
1621. Evaluation or production, target account, exact hostname, and zone for a custom domain. On a `workers.dev` route, also the exact `publicBaseUrl` origin, which the operator has to supply because nothing in the checkout can derive it.
1632. Access application, intended users, identity provider requirements, administrators, and denied test identity.
1643. Stable names for the Router, Workshop, Context, Scheduler, Custom Gatekeeper, and Error Reporter. Only the Router's name appears in the public URL; the Workshop's is the identity every Durable Object belongs to, and Context and Scheduler own Durable Objects of their own — collections and schedules — so renaming either strands its data.
1654. New auto-provisioned or existing resource for each of Context KV, Blueprints KV, Avatars KV, and Blueprint Content R2.
1665. Which AI Gateway and which providers; billing/BYOK on the gateway, budget, prompt/response logging, and retention. Disabling `aiGateway` at all is the exception, not the default.
1676. Error Reporter enabled state, environment/release metadata, telemetry sampling, and retention/export policy.
1687. Custom Gatekeeper disabled, optional, or enabled, and whether its code still matches the low-stakes example.
1698. Rollback tolerance and the person authorized to approve production mutation.
170
171Default recommendations for a first evaluation are the default AI configuration (enabled, the account's own gateway, `cloudflare` only, no token), Context Artifacts disabled, new storage, Error Reporter enabled and private, Custom Gatekeeper disabled until reviewed, narrow Access policy, and operators-only access until verification completes. Do not ask first-time operators about Context Artifacts; discuss it only when the existing configuration enables it or the operator explicitly requests Git-backed Context collections.
172
173### 3. Prepare The Workspace
174
175Require Node.js major 24 and pnpm major 11 unless the current repository says otherwise. Confirm account access to every enabled product, including Workers, KV, R2, Browser Rendering, and Dynamic Worker Loaders. Workers AI and AI Gateway belong on that list unless the operator has deliberately disabled the model catalog, since it is enabled by default. Artifacts is optional.
176
177Run the repository's documented setup commands. Stop if installation unexpectedly changes lockfiles, the submodule gitlink, or tracked files. Resolve provenance or version drift; do not normalize it away.
178
179Temporary `wrangler.prod.jsonc` files are generated implementation details. Never edit, commit, or deploy them directly. If one remains after interruption, first prove that no check/deploy process is active, preserve the original failure, and then remove only the stale generated file.
180
181### 4. Configure Cloudflare Access And Routing
182
183Treat routing, authentication, and `/admin` authorization as separate controls.
184
185The route belongs to the Router alone. The other five Workers are generated with `workers_dev: false`, no routes, and Preview URLs off, so the Router's hostname is the single Access-protected entrance. Verify that in the generated configs rather than assuming it, and treat any additional public route on a backend Worker as an Access bypass.
186
187For production:
188
1891. Confirm the hostname belongs to an active zone in the target account.
1902. Stop on an existing DNS record, Worker route, custom domain, or Access application collision. Never delete a conflicting record automatically.
1913. Create or identify a self-hosted Access application covering the exact hostname.
1924. Use a narrow policy for intended identities. Broad `Everyone`, `Bypass`, or weak bootstrap policies require an explicit risk acceptance.
1935. Copy the exact HTTPS team-origin issuer and exact application audience tag.
1946. Make administrators an explicit subset of users allowed by Access.
195
196For evaluation, using `workersDev: true` does not itself add authentication. Protect the exact evaluation hostname with Access and use that application's audience. `publicBaseUrl` must name that same hostname: it is what `PUBLIC_BASE_URL` and the Context sharing boundary derive from, and the deploy refuses a `workers.dev` route without it. Confirm Preview URLs are off in every generated config, and never patch a generated file to change one.
197
198For a first `workers.dev` deployment, determine whether the Access application can be created for the predictable hostname before the Worker exists. If it requires the Worker first, present a controlled two-stage bootstrap for approval: pre-create only a reserved Router Worker identity with no sensitive application or data, enable Access and obtain its audience, then configure and run the wrapper deployment. Do not use `pnpm deploy` for the identity bootstrap because it deploys the whole stack. Record and minimize any temporary public route, and do not proceed if the bootstrap cannot be contained.
199
200Initially allow only deployment operators. Widen access only after `/admin` policy and all post-deploy checks pass.
201
202Verification must include an unauthenticated/incognito request, one intended administrator, one authenticated non-administrator when applicable, and one denied identity. Verify there is no unintended alternate route that bypasses Access.
203
204### 5. Configure `deployment.jsonc`
205
206Edit only the annotated, non-secret control surface unless the requested feature cannot be expressed there.
207
208- Account IDs are exact 32-character hexadecimal IDs.
209- Active Worker names are unique, stable, lowercase account-level service identities.
210- Set exactly one Router route: `customDomain` or `workersDev: true`. No other Worker takes a route.
211- `publicBaseUrl` is an HTTPS origin with no path and no trailing slash, `null` only on a custom domain, and must agree with `customDomain` when both are set.
212- The Access issuer is an HTTPS origin without a path; the audience is exact and unpadded.
213- Administrator emails must match the verified identity representation expected by the current backend.
214- Keep the Context data-isolation boundary stable unless intentionally creating a new one. `context.sharingDomain: null` follows the public origin, so a hostname change moves the boundary; pin the old origin as a literal string to hold it in place.
215- Context Artifacts defaults to disabled when `enabled` is omitted; when enabled, keep its namespace stable. Omitting the namespace selects `gatekeeper-context-collections`, while `null` is invalid.
216- `customGatekeeper.message` is tracked and agent-readable. It is not a secret store.
217- Set release metadata only to a real deployment identifier; use `null` otherwise.
218
219Account IDs, administrator emails, hostnames, resource identifiers, and organization guidance are non-secret but potentially sensitive repository metadata. Confirm the repository audience before committing them.
220
221Choose storage separately for all four bindings. `null` requests Wrangler automatic provisioning. An explicit value adopts an existing resource and therefore requires proof of account ownership, data classification, compatibility, and backup status. Similar names are not proof.
222
223Context Artifacts is separate from those KV/R2 resources. Enabling it creates the namespace implicitly with the first repository. Disabling it later stops repository refresh and token management but does not delete repositories or cached Context content. Treat repository write tokens as credentials and record the namespace in the deployment inventory.
224
225After first production provisioning, inventory the actual resource identities. Consider pinning explicit IDs/names when reproducible binding is required. Treat Worker renames as migration work because they can create new service identities, provision empty resources, or orphan old mappings.
226
227### 6. Configure AI
228
229`aiGateway.enabled` defaults to true, and that is normally the right setting. Every provider, Workers AI included, is reached through AI Gateway over the Workshop's `WORKERS_AI` binding, which is pre-authenticated inside the deployment's own account. The common configuration needs **no `CF_AI_GATEWAY_API_TOKEN`**, so there is no token bootstrap to sequence.
230
231Disabling it is a deliberate choice with a consequence: every user must then supply their own model API keys, and a deployment migrated from the hosted flow shows an empty model picker because `wrangler deploy` replaces `vars` wholesale and drops the hosted `CF_AI_GATEWAY*` values. Confirm the operator wants that before setting it false.
232
233Decisions to confirm:
234
2351. Which gateway. `default` is created on first use; a migration from the hosted deploy must reuse `<instance>-ai` (see `docs/migrate-from-hosted.md`).
2362. Which providers, and for `anthropic`/`openai` the billing/BYOK setup and budget on the gateway itself.
2373. Whether prompts/responses are logged, where, for how long, and who can access them.
2384. Whether `aiGateway.accountId` should stay `null`. A different account is the deploy's way of saying the gateway is out of the binding's reach, and it turns the token back on.
239
240Only two configurations still require the token, and `pnpm check` names whichever applies before deploying: the `google` provider, whose adapter refuses the binding's fetch, and a gateway in another account. In exactly those cases the generated config declares the secret as required, so wrangler refuses the deploy without it — install it interactively on the exact Workshop identity, after confirming account and Worker ownership, and record the version it deploys.
241
242Never remove the `WORKERS_AI` binding to opt out of gateway transport. It is also what the agent's `webFetch` document-to-Markdown conversion runs on. The supported opt-out is `aiGateway.accountId`, which the deploy translates into `CF_AI_GATEWAY_USE_BINDING: "false"`.
243
244A secret existing does not prove its scope, billing, or account is correct. One approved, non-sensitive, low-cost request proves only the runtime inference path. Verify token configuration, billing ownership, Gateway/provider logs, prompt collection, retention, and access policy through their authoritative control surfaces. Disabling AI does not delete an existing secret.
245
246### 7. Review The Custom Gatekeeper
247
248The starter example is credential-free, read-only, auto-provisioned, and permissive to observers because it returns identical low-stakes text to every authenticated user. Those choices are unsafe defaults for account-specific, tenant-specific, or confidential data.
249
250Keep the Custom Gatekeeper disabled in `/admin` until its provenance and authority are reviewed. If it has been customized, load the upstream `write-gatekeeper` skill and stop for operator approval of the agent-facing API before implementation or enablement. Review:
251
252- Capability and resource granularity.
253- Every observation and side effect.
254- Approval, application, rejection, and simulation behavior.
255- Observer verification and sharing boundaries.
256- OAuth scopes, token storage, revocation, and account isolation.
257- SSRF and untrusted external responses.
258- Auto-provisioning, optional connection, and ambient authority.
259
260Never enable it for everyone merely to complete a smoke test. If an approved smoke test is needed, use the least-authoritative policy and disable it again unless continued access was approved.
261
262### 8. Configure Observability And Error Reporting
263
264Keep the Error Reporter private with no route. It receives only explicit upstream `reportIssue()` events; it does not catch every exception or `console.error`, cover every Worker automatically, or provide alerting.
265
266Structured logs, invocation logs, traces, browser reports, sampling, source maps, export, retention, and alerts are separate decisions. Sampling can hide individual events. Never put secrets, prompts, headers, bodies, or raw user documents in errors or report attributes.
267
268Browser reporting stays disabled unless rate limiting, origin checks, private source-map handling, data retention, and privacy review are all approved. Do not manufacture a production exception containing user data to test reporting. If no deterministic non-sensitive capture exists, record end-to-end Reporter dispatch as unverified rather than claiming success.
269
270### 9. Validate Locally
271
272Run:
273
274```sh
275pnpm check
276```
277
278Expect wrapper tests, selected package builds, generated Wrangler configs, and Wrangler dry-runs as defined by the current script. Confirm generated files are removed afterward. An upgrade also requires the separate relevant upstream checks described in the upgrade reference.
279
280Passing `pnpm check` does not prove remote resource ownership, secret scope, Access policy, custom-domain availability, billing, live service compatibility, or data preservation. Fix failures one cause at a time, run the narrow package/test where useful, then rerun the complete check.
281
282### 10. Approve And Deploy
283
284Before requesting approval, inventory current deployed version IDs for all affected Workers and verify downstream RPC changes remain compatible with both the old and new Workshop. The deployment is not atomic. The expected baseline order is:
285
2861. Error Reporter, when enabled.
2872. Context.
2883. Scheduler.
2894. Custom Gatekeeper.
2905. Workshop.
2916. Router, which binds every one of the above.
292
293If current `scripts/deploy.ts` differs, use its order. The Router going last means a failure before it leaves the previous Router version still serving the public URL over partly-updated backends, so a healthy-looking site is not evidence the deploy completed. Breaking cross-Worker contracts need parallel identities and a controlled binding switch, not an in-place sequential deploy.
294
295Present the production mutation summary from the hard stop. After explicit approval, run:
296
297```sh
298pnpm deploy
299```
300
301Record each successful stage and resulting deployment/version ID. On the first failure, stop and inventory actual remote state before deciding to resume or roll back. Never assume the script is transactional or retry-safe.
302
303### 11. Verify The Live Deployment
304
305Success requires evidence for every applicable item:
306
307- The intended hostname serves valid TLS on the Router and is the only intended public route.
308- The Router proxies correctly: the frontend loads, `/api` reaches the Workshop, and a Gatekeeper that serves HTTP answers under `/gatekeeper/<name>`.
309- Access redirects/denies unauthenticated users, allows the intended identity, and denies the negative test identity.
310- `/admin` allows an administrator and denies an authenticated non-administrator.
311- Signup, connector, Context, and Gatekeeper policies match the approved decisions.
312- Workshop, Context, Scheduler, Custom Gatekeeper, and Error Reporter have no public routes and no Preview URLs. Each would be an unauthenticated path around the Router's Access application.
313- Existing data remains visible; newly created data persists across a safe reload or redeploy test.
314- Context Artifacts is absent when disabled, or an approved Git-backed collection can be populated and refreshed in the intended stable namespace when enabled.
315- Each Workshop service binding targets the intended service, entrypoint, and props, and each Router service binding targets the intended service with no entrypoint. The Context binding's `sharingDomain` prop matches the boundary the existing collections live under.
316- The Custom Gatekeeper is disabled or behaves according to its reviewed policy; approved reads appear as observations.
317- The model picker lists the configured providers' models, and one approved low-cost request proves the runtime path, with separate evidence for billing ownership, provider/Gateway selection, prompt collection, retention, and log access, plus token scope where a token is required. An empty picker is a failure, not an absence of evidence: see `references/troubleshooting.md`.
318- Existing schedules are still listed and a newly created one fires. Both come from the Scheduler's own Durable Objects, so an empty list on an upgraded or migrated deployment points at its Worker name, not at its storage.
319- Router, Workshop, Context, Scheduler, Custom Gatekeeper, and Reporter logs are available with the expected sampling.
320- The Reporter query surface exists; absence of events is not a failure without an explicit capture.
321- No secrets or generated Wrangler files are tracked.
322- Last-known-good deployment IDs and recovery instructions are recorded.
323
324Do not declare success from a passing check, an HTTP 200, Worker existence, or absence of visible errors alone.
325
326### 12. Close Out
327
328Give the operator a concise report:
329
330```text
331Outcome: verified / partially verified / failed safely
332Account and route:
333Root/submodule commits:
334Workers changed and deployment IDs:
335Resources provisioned or adopted:
336Access positive/negative evidence:
337AI, Gatekeeper, and Reporter state:
338Checks performed:
339Unverified items and why:
340Rollback path and limits:
341Follow-up owners:
342```
343
344Redact identifiers when the audience does not need them. Do not include authentication artifacts, private log bodies, or sensitive configuration.
345
346## Failure Recovery
347
348Use [references/troubleshooting.md](references/troubleshooting.md) for symptom-specific checks and recovery. For every failure:
349
3501. Stop further mutation.
3512. Preserve the first exact error and identify which deployment stages succeeded.
3523. Classify it as provenance, local setup, validation, authentication, authorization, routing, resource, partial deployment, runtime, data, or compatibility.
3534. Form one falsifiable root-cause hypothesis.
3545. Gather the minimum non-sensitive evidence needed to test it.
3556. Correct one cause and run the narrowest relevant check.
3567. Rerun `pnpm check`.
3578. Re-enter the approval gate if production or a trust boundary changed.
3589. Deploy and repeat the full live verification.
359
360After two identical failed attempts, stop and escalate with sanitized evidence. Do not widen permissions or recreate infrastructure as an experiment.
361
362## Rollback
363
364Rollback is a coordinated system operation, not one command. Read [references/upgrade-and-rollback.md](references/upgrade-and-rollback.md), produce the per-Worker rollback matrix, require approval, and repeat the full live verification. Never delete or downgrade storage as an automatic rollback step.
365
366## Pinned Submodule Upgrade
367
368Never advance the submodule blindly. Read and follow [references/upgrade-and-rollback.md](references/upgrade-and-rollback.md). An evaluation worktree prevents local file races but is not staging isolation; use separate Worker identities, route, storage, bindings, and data. Stop for specialist review before any migration, deleted/renamed Durable Object class, irreversible data change, auth boundary change, or incompatible RPC change.
369