agentleFS
teispaceteispace/teitunnel/.claude/skills/changing-cloudflare-resources/SKILL.md

changing-cloudflare-resources

Skill2 starsChanged 48 days agoMITView on GitHub

Implements a change Teitunnel makes on a Cloudflare account (tunnels, DNS records, Access, Workers, D1, rulesets, service tokens) through the observe → plan → apply → verify engine in crates/core/src/engine, with rollback, ownership tracking, the cf-api client, fakes and tests. Use for any feature or fix that creates, updates or deletes something on Cloudflare, or adds a Cloudflare API call.

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 3 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    add a cost or plan-limit warning when the change can cost money or needs a paid plan.

    line 85
  2. Distinctive:

    keep the permission optional unless every account needs it, so existing tokens keep working.

    line 103
  3. Distinctive:

    Never test against a real account.

    line 121

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Changing Cloudflare resources

Teitunnel changes real accounts. Every change goes through one pipeline so the person sees it first, it can fail halfway without leaving a mess, and it can be undone. No IPC command, CLI command or MCP tool calls the Cloudflare API to change something directly.

Background: docs/ARCHITECTURE.md §4 (the engine) and §4.7 (ownership). Read them before a non-trivial change.

Intent ─┐
        ├─▶ plan() (pure) ─▶ Plan ─▶ person reviews ─▶ Engine::apply ─▶ verify
Snapshot┘                                                   │
                                                  Activity + undo steps

Checklist

- [ ] 1. API calls in crates/cf-api, with wiremock tests
- [ ] 2. CloudApi trait method, implemented for Client and FakeCloud
- [ ] 3. Observe what the plan depends on (Snapshot)
- [ ] 4. Intent variant (what the person asked for)
- [ ] 5. Step variant(s): describe(), command(), and an Undo
- [ ] 6. Planner: ordering, idempotency, conflicts, ownership
- [ ] 7. Ownership recorded (only what Teitunnel created is ever deleted)
- [ ] 8. Permissions: token template, OAuth scope, capability probe, docs
- [ ] 9. Tests: planner snapshots, executor rollback, fake-cloudflare
- [ ] 10. Callers use preview → apply (IPC, CLI, MCP)
- [ ] 11. pnpm verify

1. The API call (crates/cf-api)

One module per product (dns.rs, tunnels.rs, workers.rs, rulesets.rs, d1.rs…). Requests are typed; responses go through the envelope in envelope.rs; errors are cf_api::Error. Link the endpoint's Cloudflare documentation in the doc comment. Test each endpoint with wiremock: a recorded success response and the error-envelope case.

2. CloudApi (engine/cloud.rs)

The engine depends on the CloudApi trait, not on Client. Add a method, implement it for Client (in cloud.rs) and for FakeCloud (engine/fake.rs), whose in-memory state the engine tests assert against. Keep the fake faithful: the same refusals and shapes as the real API.

3–4. Observe and intend

  • Intent (engine/types.rs) says what the person wants in product terms (AddRoute, ProtectHostname, …), never in API steps.
  • The observed Snapshot holds everything the plan depends on, and its fingerprint changes when any of it changes, so a stale plan is re-planned before it's applied.

5. Steps and undo

A Step (engine/types.rs) is one Cloudflare or local operation. Each step:

  • has describe() returning a translated Text (see writing-user-facing-text) and command() rendering it as a cloudflared or curl command where one exists;
  • is executed in Run::step (engine/executor.rs), which returns an Undo;
  • has a compensating Undo that restores the previous state (delete what was created, put back the previous config, restore a deleted Worker from its recorded config).

On failure, completed steps are undone in reverse order. A step that can't be undone must be last, or be explicitly confirmed by the person.

Consecutive Step::CreateRecord steps aren't run one by one: create_records in engine/executor.rs sends them as one batch per zone (CloudApi::create_records). A test that counts FakeCloud::mutations() sees one mutation per zone for them.

6. The planner (engine/planner.rs, engine/planner/*.rs)

plan(intent, snapshot) is pure: no I/O, no clock, deterministic. It must:

  • order steps safely: create → configure → DNS → start → verify; removal in reverse;
  • be idempotent: planning against the converged state returns an empty plan;
  • never overwrite silently: an existing record, rule, Worker route or Access app that Teitunnel doesn't own sets requires_confirmation and is shown to the person;
  • scope edge rules and Workers to the hostname, never to a whole domain;
  • add a cost or plan-limit warning when the change can cost money or needs a paid plan.

7. Ownership

Teitunnel deletes only what it created. Record everything it creates: DNS records carry the comment teitunnel:route=<id>, and the store keeps ownership indexes (tunnels, records, edge rules, Workers, Access apps, D1). New kinds of resource need their own index: append a migration to crates/core/src/store/migrations.rs (never edit an existing one). Removing a hostname's last route removes everything Teitunnel attached to it.

8. Permissions

A new API needs a permission:

  • the token template (accounts/template.rs) and, for sign-in, the OAuth scope (accounts/oauth.rs): verify the exact key and scope on Cloudflare's dashboard first;
  • a probe in accounts/capabilities.rs so the UI can disable the feature with the reason, and ask for the permission in place, instead of failing mid-plan;
  • keep the permission optional unless every account needs it, so existing tokens keep working; observing must not fail for them either: read the new thing on its own and record whether it was readable (edge::observe reads the Cache Rules phase this way, cache_readable), and refuse only the change that needs it (ObserveError::CacheRulesPermission); a newer API read alongside an older one gets its own Want in ObserveNeed and an Option in the state, like private hostname routes (ObserveNeed::hostname_routes, NetworkState::hostnames), so a failed read hides them instead of failing the overview;
  • regenerate the permissions page: UPDATE_DOCS=1 cargo test -p teitunnel-core --test permissions_doc.

9. Tests

  • Planner: an insta snapshot per scenario in engine/planner_tests.rs (or the area's *_tests.rs): create, change, remove, the already-converged case (empty plan), and a resource someone else owns. Review snapshots with cargo insta review; never accept blindly.
  • Executor: a step failing halfway rolls back the earlier ones (engine/executor_tests.rs).
  • End to end: tools/fake-cloudflare serves the HTTP API for CLI and E2E tests; extend it when the CLI or app exercises the new endpoints.
  • Never test against a real account.

10. Callers

The app (src-tauri/src/ipc/routes.rs and friends), the CLI and the MCP server all use Engine::preview to get a plan and show it, then Engine::apply with the same intent and an Approval carrying the reviewed plan's fingerprint (and confirmed when the person agreed to replace something they own). If reality changed since the preview, the fingerprint no longer matches and the person reviews again. CLI commands show the plan and ask unless --yes; MCP tools return the plan and apply it after the person's approval.

11. Verify

pnpm verify, then check the change in the app against the E2E fake (pnpm --filter @teitunnel/desktop e2e:build && pnpm --filter @teitunnel/desktop e2e).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.