Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# AGENTS.md

Short guide for coding agents working in `conduit-ui/cloudflare`.

## What this repo is

Laravel Zero CLI (`./cf`) for Cloudflare zones, DNS, and tunnels. Part of the Conduit agent tooling set — see [docs/conduit.md](docs/conduit.md). Demo session: [docs/demo.md](docs/demo.md).

## Local loop

```bash
composer install
cp .env.example .env # set CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID for live API calls
./bin/ci
```

`./bin/ci` is Pint `--test`, PHPStan, Rector dry-run, and Pest. That matches [quality-runners](https://github.com/synapse-sentinel/quality-runners). GitHub Actions still runs Pint `--test` and Pest as a residual.

## Layout

- `app/Commands/` — Artisan commands (`zones`, `dns:*`, `tunnel:*` including `tunnel:expose`)
- `app/Integrations/Cloudflare/` — Saloon connector, resources, requests
- `tests/` — Pest feature/unit tests
- `docs/conduit.md` — agent recipes
- `docs/demo.md` / `docs/demo.cast.md` — walkthrough and sample session

## Conventions

- Prefer `--json` in examples and agent-facing docs. That flag prints pretty-printed JSON of the API `result` on stdout. Lists are arrays; creates are objects. Failures are a human error line and a non-zero exit.
- `tunnel:create` / `tunnel:expose --json` include create-time `token` and `credentials_file`. Treat that stdout as secret.
- Keep API credentials in env; never hardcode tokens.
- New Cloudflare endpoints: Saloon request + resource method + command; mirror existing `--json` / table patterns.
- Avoid breaking command signatures; additive flags are fine.
- Docs-only / packaging PRs should not change runtime behavior.

## Tests

Add feature tests for new commands when practical; keep unit tests free of live Cloudflare calls unless mocked.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Get credentials from:

## Agent JSON output

Pass `--json` for a stable envelope on stdout: `{"ok":true,"data":...}` on success, `{"ok":false,"error":"..."}` on failure (exit `0` / `1`). See [docs/agent-output.md](docs/agent-output.md).
Pass `--json` for a stable envelope on stdout: `{"ok":true,"data":...}` on success, `{"ok":false,"error":"..."}` on failure (exit `0` / `1`). See [docs/agent-output.md](docs/agent-output.md). Agent recipes: [docs/conduit.md](docs/conduit.md). Walkthrough: [docs/demo.md](docs/demo.md).

## Commands

Expand Down Expand Up @@ -144,6 +144,20 @@ app/
│ └── Requests/ # Individual API requests
```

## Conduit ecosystem

`cf` is agent tooling for the [conduit-ui](https://github.com/conduit-ui) ecosystem. Prefer `--json` output when scripting or driving the CLI from an agent.

Sibling tools (conceptual neighbors):

- [issue-cli](https://github.com/conduit-ui/issue-cli) — GitHub issues
- [qdrant-tools](https://github.com/conduit-ui/qdrant-tools) — Qdrant vector DB
- [pr-cli](https://github.com/conduit-ui/pr-cli) — pull requests
- [commit-cli](https://github.com/conduit-ui/commit-cli) — commit messages
- [knowledge](https://github.com/conduit-ui/knowledge) — semantic knowledge base

Agent-oriented setup and recipes (list zones, create DNS, create tunnel, expose a local app): **[docs/conduit.md](docs/conduit.md)**. Coding agents: **[AGENTS.md](AGENTS.md)**. Sample session: **[docs/demo.md](docs/demo.md)** / **[docs/demo.cast.md](docs/demo.cast.md)**.

## Development

PR quality is [quality-runners](https://github.com/synapse-sentinel/quality-runners) (Pint, Pest, PHPStan, Rector on the clone host). Until that runner is subscribed to this repo, GitHub Actions still runs Pint `--test` and Pest as a residual. Local `./bin/ci` is the full set.
Expand Down
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
{
"name": "conduit-ui/cloudflare",
"description": "Cloudflare management CLI for the Conduit agent ecosystem",
"description": "Cloudflare zones, DNS, and tunnel CLI for Conduit agents (Laravel Zero)",
"keywords": [
"agents",
"cli",
"cloudflare",
"conduit-ui"
"conduit",
"conduit-ui",
"dns",
"laravel-zero",
"tunnel"
],
"homepage": "https://github.com/conduit-ui/cloudflare",
"type": "project",
Expand Down
130 changes: 130 additions & 0 deletions docs/conduit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Using `cf` in the Conduit ecosystem

`cf` is the Cloudflare management CLI for [conduit-ui](https://github.com/conduit-ui) agent tooling. Agents should prefer **JSON output**, treat exit codes as success/failure, and keep secrets in `.env` (never in prompts or commits).

Walkthrough with sample output: [demo.md](demo.md).

## Setup

```bash
git clone https://github.com/conduit-ui/cloudflare.git
cd cloudflare
composer install
cp .env.example .env
```

Set:

| Variable | Required for | Notes |
|----------|--------------|--------|
| `CLOUDFLARE_API_TOKEN` | all commands | API token from the Cloudflare dashboard |
| `CLOUDFLARE_ACCOUNT_ID` | `tunnel:*` | Account ID (sidebar on any zone page) |

Minimum token scopes:

| Command group | Permission |
|---------------|------------|
| `zones` | Zone:Read |
| `dns:*` | DNS:Edit |
| `tunnel:*` | Cloudflare Tunnel:Edit |

## Agent conventions

1. **Always pass `--json`** when parsing results.
2. **Check exit code** — `0` success, non-zero failure (errors go to stderr / human error lines).
3. **`--json` prints the API `result`** as pretty-printed JSON on stdout. Lists are arrays. Creates are objects. Failures are a human error line and a non-zero exit.
4. **Zone args** accept a zone ID (`^[a-f0-9]{32}$`) or a domain name; domain names are resolved via the Zones API.
5. **Do not print tokens.** Read credentials from the environment only. `tunnel:create` / `tunnel:expose --json` include create-time `token` and `credentials_file` — store them, do not log them.
6. Prefer composing with sibling tools (`issue-cli`, `qdrant-tools`, `pr-cli`, …) rather than re-implementing Cloudflare calls.

```bash
./cf zones --json | jq '.[].name'
./cf tunnel:create my-app --json | jq -r '.id'
./cf tunnel:expose my-app app.example.com --json | jq -r '.tunnel.id'
```

## Typical recipes

### List zones

```bash
./cf zones --json
./cf zones --name=example.com --json
```

JSON is an array of zone objects (`id`, `name`, `status`, `plan`, …).

### Create DNS for an app

Resolve the zone, then create records. Example: proxied A record and a CNAME for `www`.

```bash
# Inspect existing records
./cf dns:list example.com --json
./cf dns:list example.com --type=A --json

# Point apex at an origin IP (proxied)
./cf dns:create example.com A @ 203.0.113.10 --proxied --json

# www → apex (proxied)
./cf dns:create example.com CNAME www example.com --proxied --json
```

`--ttl` defaults to `1` (Cloudflare auto). Omit `--proxied` for DNS-only (grey cloud).

### Create a tunnel

```bash
./cf tunnel:list --json
./cf tunnel:create my-app --json
```

Tunnel create returns a tunnel object (`id`, `name`, `status`, `token`, `credentials_file`, …). Runtime still uses `cloudflared` locally:

```bash
cloudflared tunnel route dns my-app app.example.com
cloudflared tunnel run --token <token-from-create>
```

### Expose a local app

One-shot: create the tunnel, print the connector token, and (when a hostname is given) attempt remote ingress + DNS CNAME via the API. Falls back to exact `cloudflared` commands when the API cannot route.

```bash
./cf tunnel:expose my-app
./cf tunnel:expose my-app app.example.com http://localhost:3000 --json
```

Default local URL is `http://localhost:8000`. Then:

```bash
cloudflared tunnel run --token <token-from-output>
```

Delete when done:

```bash
./cf tunnel:delete <tunnel-id> --force
```

## Command map

| Intent | Command |
|--------|---------|
| List zones | `./cf zones [--name=…] [--json]` |
| List DNS | `./cf dns:list <zone> [--type=…] [--name=…] [--json]` |
| Create DNS | `./cf dns:create <zone> <type> <name> <content> [--proxied] [--ttl=1] [--json]` |
| List tunnels | `./cf tunnel:list [--json]` |
| Create tunnel | `./cf tunnel:create <name> [--json]` |
| Expose local app | `./cf tunnel:expose <name> [hostname] [url] [--json]` |
| Delete tunnel | `./cf tunnel:delete <id> [--force]` |

## Related Conduit tools

- [issue-cli](https://github.com/conduit-ui/issue-cli) — GitHub issues for agents
- [qdrant-tools](https://github.com/conduit-ui/qdrant-tools) — Qdrant vector DB CLI
- [pr-cli](https://github.com/conduit-ui/pr-cli) — pull request workflows
- [commit-cli](https://github.com/conduit-ui/commit-cli) — conventional / AI-assisted commits
- [knowledge](https://github.com/conduit-ui/knowledge) — semantic knowledge base

See also [AGENTS.md](../AGENTS.md) for coding-agent guidance in this repo.
147 changes: 147 additions & 0 deletions docs/demo.cast.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Sample terminal session (`demo.cast`)

Fake/sample output for docs and recordings. No live Cloudflare API calls required.

To turn this into a real [asciinema](https://asciinema.org/) cast later:

```bash
# asciinema rec docs/demo.cast
# …run the commands from demo.md…
# asciinema upload docs/demo.cast
```

Until a binary `.cast` is checked in, use the transcript below as the expected session.

`--json` prints pretty-printed JSON of the API `result`. Lists are arrays. Creates are objects. `tunnel:expose --json` is an object with `tunnel`, `hostname`, `url`, `ingress_configured`, `dns_created`, `dns_record`, `configuration`, and `dns_route_command`.

---

```text
$ ./cf zones
+----------------------------------+-------------------+--------+----------+
| ID | Name | Status | Plan |
+----------------------------------+-------------------+--------+----------+
| a1b2c3d4e5f6789012345678abcdef01 | example.com | active | Free |
| 9f8e7d6c5b4a3210987654321fedcba0 | staging.example | active | Pro |
+----------------------------------+-------------------+--------+----------+

$ ./cf zones --json
[
{
"id": "a1b2c3d4e5f6789012345678abcdef01",
"name": "example.com",
"status": "active",
"plan": {
"name": "Free Website"
}
}
]

$ ./cf dns:list example.com --type=A
+------+------------------+-------------+---------+------+
| Type | Name | Content | Proxied | TTL |
+------+------------------+-------------+---------+------+
| A | example.com | 203.0.113.1 | Yes | Auto |
| A | api.example.com | 203.0.113.2 | No | Auto |
+------+------------------+-------------+---------+------+

$ ./cf dns:create example.com A demo-cf 203.0.113.10
DNS record created successfully!
+---------+----------------------------------+
| Field | Value |
+---------+----------------------------------+
| ID | rec01abcdefghijklmnopqrstuvwxyz |
| Type | A |
| Name | demo-cf.example.com |
| Content | 203.0.113.10 |
| Proxied | No |
+---------+----------------------------------+

$ ./cf tunnel:expose demo-conduit
Creating tunnel "demo-conduit" to expose http://localhost:8000...

Tunnel ready to expose your app!
+---------+--------------------------------------+
| Field | Value |
+---------+--------------------------------------+
| ID | 11111111-2222-3333-4444-555555555555 |
| Name | demo-conduit |
| Status | inactive |
| Created | 2026-03-26T12:00:00Z |
| Token | eyJhbG****************demo |
+---------+--------------------------------------+

Expose your local app:
1. Install cloudflared: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/
2. Run: cloudflared tunnel run --token <token>
3. Local service for ingress: http://localhost:8000
4. Optional DNS: cloudflared tunnel route dns demo-conduit <hostname>

$ ./cf tunnel:expose demo-conduit demo-tunnel.example.com http://localhost:3000 --json
{
"tunnel": {
"id": "11111111-2222-3333-4444-555555555555",
"name": "demo-conduit",
"status": "inactive",
"created_at": "2026-03-26T12:00:00Z",
"token": "<redacted>",
"credentials_file": {
"AccountTag": "account-tag",
"TunnelID": "11111111-2222-3333-4444-555555555555",
"TunnelName": "demo-conduit",
"TunnelSecret": "<redacted>"
}
},
"hostname": "demo-tunnel.example.com",
"url": "http://localhost:3000",
"ingress_configured": true,
"dns_created": true,
"dns_record": {
"id": "rec02abcdefghijklmnopqrstuvwxyz",
"type": "CNAME",
"name": "demo-tunnel.example.com",
"content": "11111111-2222-3333-4444-555555555555.cfargotunnel.com"
},
"configuration": {},
"dns_route_command": "cloudflared tunnel route dns demo-conduit demo-tunnel.example.com"
}

$ ./cf tunnel:create demo-conduit --json
{
"id": "11111111-2222-3333-4444-555555555555",
"name": "demo-conduit",
"status": "inactive",
"created_at": "2026-03-26T12:00:00Z",
"token": "<redacted>",
"credentials_file": {
"AccountTag": "account-tag",
"TunnelID": "11111111-2222-3333-4444-555555555555",
"TunnelName": "demo-conduit",
"TunnelSecret": "<redacted>"
}
}

$ ./cf tunnel:list --json
[
{
"id": "11111111-2222-3333-4444-555555555555",
"name": "demo-conduit",
"status": "inactive",
"created_at": "2026-03-26T12:00:00Z",
"connections": []
}
]

$ ./cf tunnel:delete 11111111-2222-3333-4444-555555555555 --force
Tunnel deleted successfully.
```

---

**Agent tip:** pipe any of these with `--json` into `jq` for Conduit workflows:

```bash
./cf zones --json | jq '.[].name'
./cf tunnel:create demo-conduit --json | jq -r '.id'
./cf tunnel:expose demo-conduit demo-tunnel.example.com --json | jq -r '.tunnel.id'
```
Loading
Loading