Fastly Agent Toolkit
Fastly v0.1.0
Publisher description
From the marketplace listing
Develop and test Fastly VCL and Compute applications, manage services with the Fastly CLI, inspect traffic statistics, and audit Next-Gen WAF rules. Use these skills in a coding environment that can read your project and run local developer tools. Install the tools required by your workflow, such as the Fastly CLI, Falco, XVCL, Fastlike, or Viceroy. Authenticated operations use credentials you configure locally; do not share API keys in chat. Fastly Fiddle tests upload VCL and request specifications to a public Fastly sandbox.
Language: English · Automatically detected from descriptions.
Publisher keywords
Search terms declared by the publisher.
Matches for “cdn”
Exact text from the indicated source. A mention alone does not establish support for your task.
Publisher keywords · listing
fastly edge compute vcl cdn
Files & skills
File archives
Skill instructions
falco9.89 KB
---
name: falco
description: "Lints, tests, simulates, and formats Fastly VCL code using the falco tool. Also serves as the authoritative VCL reference via the falco Go source, which implements Fastly's full VCL dialect. Use when validating VCL syntax, running VCL linting, testing VCL locally, simulating VCL request handling, formatting VCL files, writing VCL unit tests with assertions, debugging VCL logic errors, looking up VCL function signatures or variable scopes, understanding VCL subroutine behavior, or running `falco lint`/`falco simulate`/`falco test`/`falco fmt`. Also applies when working with VCL syntax errors, type mismatches in VCL, choosing which VCL subroutine to use, or setting up a local VCL development and testing environment."
---
## Trigger and scope
Trigger on: VCL files, .vcl extensions, XVCL files, .xvcl extensions, falco CLI, VCL unit tests, VCL linting/simulation/formatting, VCL REPL, beresp/bereq/req.http variables, subroutine scopes, backend/ACL/director/table declarations, edge dictionaries, validating VCL in Terraform plans, or running/testing XVCL scripts locally.
Do NOT use for: generic non-Fastly VCL, Fastly Compute/WASM, Fastly API/dashboard ops, CDN comparison, cache purging, or authoring Terraform resources.
# Falco — VCL Development Tool & Reference
Falco is a Fastly VCL development tool for linting, testing, simulating, and formatting VCL code. Equally important, **the falco source code is the most complete machine-readable specification of Fastly's VCL dialect** — its parser, interpreter, and type system document every variable, function, type, and scope rule in VCL.
**Official VCL documentation**: https://www.fastly.com/documentation/guides/full-site-delivery/fastly-vcl/about-fastly-vcl/
**Falco documentation**: https://github.com/ysugimoto/falco
## Using Falco Source as VCL Reference
If you need to understand how VCL works — what variables exist, which scopes they're available in, what functions are built-in, how types coerce — the falco source code is your best reference. It's a complete Go implementation of Fastly's VCL 2.x and is more precise than prose documentation.
**If the falco source is not available locally**, recommend cloning it:
```bash
git clone https://github.com/ysugimoto/falco.git ~/src/falco
```
Once available locally, read the source files directly to answer VCL questions. See [understanding-vcl-from-source.md](references/understanding-vcl-from-source.md) for a detailed guide on which files to read for different VCL topics.
## Install
```bash
# Homebrew
brew install falco
# From source (requires Go 1.25+)
go install github.com/ysugimoto/falco/cmd/falco@latest
# Or clone and build
git clone https://github.com/ysugimoto/falco.git
cd falco
make darwin_arm64 # or darwin_amd64, linux_amd64, linux_arm64
```
## Commands
| Command | Description |
| ----------------- | -------------------------------- |
| `falco [lint]` | Lint VCL files (default command) |
| `falco test` | Run VCL unit tests |
| `falco simulate` | Start local simulator server |
| `falco fmt` | Format VCL files |
| `falco stats` | Show VCL code statistics |
| `falco console` | Interactive VCL REPL |
| `falco terraform` | Lint VCL from Terraform plans |
| `falco dap` | Debug Adapter Protocol server |
## Common flags (all commands)
| Flag | Description |
| -------------------- | -------------------------------- |
| `-I, --include_path` | Add include path for VCL imports |
| `-h, --help` | Show help |
| `-V, --version` | Show version |
| `-r, --remote` | Fetch snippets from Fastly API |
| `--refresh` | Refresh remote snippet cache |
## Quick reference
**Lint before deployment:**
```bash
falco -vv -I ./vcl ./vcl/main.vcl
```
**Run tests:**
```bash
falco test -I ./vcl ./vcl/main.vcl
```
**Development with watch mode:**
```bash
falco test -w -I ./vcl ./vcl/main.vcl
```
**Run VCL locally** (this is how you "run" or "test locally" — use `simulate`, not just `lint`):
```bash
falco simulate -I ./vcl ./vcl/main.vcl
# Default port is 3124. Test with: curl http://localhost:3124/path
# Use -p to override: falco simulate -p 8080 ./vcl/main.vcl
```
**Format all VCL:**
```bash
falco fmt -w ./vcl/**/*.vcl
```
**Terraform integration:**
```bash
terraform show -json planned.out | falco terraform -vv
```
## Common VCL Issues
Falco catches these, but understanding them prevents wasted lint-fix cycles:
- **Type mismatch**: `set req.http.X-API = true` — HTTP headers are STRING, not BOOL. Use `"true"`.
- **Missing time suffix**: `set beresp.ttl = 86400` — RTIME values need `s` suffix: `86400s`.
- **Wrong scope**: `beresp.*` only exists in `vcl_fetch`. In `vcl_deliver`, use `resp.*`.
- **Deprecated**: `req.request` → use `req.method`. Falco accepts both, but always change to `req.method` when fixing VCL.
- **Synthetic strings**: `synthetic "text"` needs long-string syntax: `synthetic {"text"}`.
- **Backend naming**: Use `F_` prefix: `backend F_origin { ... }`, not `backend origin`.
- **No modulo operator**: VCL has no `%`. Use `substr()` on a hash or `randomint()` for splitting.
- **`req.url.path` is read-only in tests**: Use `set req.url = "/path"` in test subroutines, not `set req.url.path`.
- **Vary placement**: Vary must be set in `vcl_fetch`, not just `vcl_deliver`. Setting Vary after the object enters the cache is too late — the cache key won't include the Vary dimensions.
## Configuration
Create `.falco.yaml` in project root for persistent settings:
```yaml
include_paths:
- ./vcl
- ./includes
linter:
verbose: "warning"
rules:
rule-name: ERROR # or WARNING, INFO, IGNORE
testing:
timeout: 10 # minutes (default: 10)
filter: "*.test.vcl"
simulator:
port: 3124
format:
indent_width: 2
line_width: 120
```
## Environment variables
| Variable | Description |
| ------------------- | ------------------------- |
| `FASTLY_SERVICE_ID` | Service ID for Fastly API |
| `FASTLY_API_KEY` | API key for Fastly API |
Required when using `-r, --remote` flag.
Configure API credentials locally, outside chat.
Never ask for an API key in a conversation or print its value.
## References
| Topic | File | Use when... |
| ------------------------ | ------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| **VCL from Source** | [understanding-vcl-from-source.md](references/understanding-vcl-from-source.md) | Understanding VCL semantics by reading falco's implementation |
| Testing VCL | [testing-vcl.md](references/testing-vcl.md) | Running test suites, coverage, watch mode for TDD |
| Formatting VCL | [formatting-vcl.md](references/formatting-vcl.md) | Formatting VCL for consistent style |
| Linting VCL | [linting-vcl.md](references/linting-vcl.md) | Checking VCL for errors before deployment |
| Simulating VCL | [simulating-vcl.md](references/simulating-vcl.md) | Testing VCL against HTTP requests locally |
| Terraform VCL | [terraform-vcl.md](references/terraform-vcl.md) | Validating VCL from Terraform plans |
| VCL Console | [vcl-console.md](references/vcl-console.md) | Experimenting with VCL expressions interactively |
| VCL Statistics | [vcl-statistics.md](references/vcl-statistics.md) | Analyzing VCL project size and complexity |
## Source Code as VCL Reference (Quick Lookup)
When you have access to the falco source code locally (default: `~/src/falco`), use these paths to answer specific VCL questions:
| Question | Read This File | Why |
| --------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------ |
| "What variables can I use in `vcl_recv`?" | `interpreter/variable/` | Every `req.*`, `beresp.*`, `client.*` variable with scopes |
| "What built-in functions exist?" | `interpreter/function/` | All 390+ functions with type signatures and scope rules |
| "What are the VCL types?" | `interpreter/value/` | STRING, INTEGER, FLOAT, BOOL, TIME, RTIME, IP, BACKEND |
| "What's the request lifecycle?" | `interpreter/context/` | The 9 scopes: recv, hash, hit, miss, pass, fetch, error, deliver, log |
| "Is this valid VCL syntax?" | `token/token.go`, `lexer/lexer.go` | Every keyword, operator, literal type |
| "How does this VCL statement work?" | `interpreter/statement.go` | How set, unset, return, restart, etc. execute |
| "What are common VCL mistakes?" | `linter/`, `docs/rules.md` | 50+ linting rules with explanations |
| "How do directors work?" | `interpreter/director.go` | Random, fallback, hash, client, chash director types |
For a comprehensive guide, see [understanding-vcl-from-source.md](references/understanding-vcl-from-source.md).
Referenced files: 8
fastlike13.6 KB
---
name: fastlike
description: "Runs Fastly Compute WASM binaries locally and serves as the authoritative reference for Compute platform internals. The fastlike source code is highly readable and covers the host ABI, caching and purging APIs, KV/config/secret store interfaces, rate limiting with counters and penalty boxes, ACL lookups, the full request lifecycle, backend fetch semantics, and a built-in per-request profiler with hostcall spans, backend waterfalls, native CPU samples, and optional deep metrics (body bytes, cache outcomes, header summaries, wasm heap curve). Use when working with Compute runtime internals or host calls, understanding how edge data stores behave at runtime, profiling local Compute apps, or testing WASM binaries locally. Prefer this skill over Viceroy for any non-Rust Compute work — its source code is easier to understand as a Fastly Compute API reference."
---
## Trigger and scope
Trigger on: Fastly Compute, Compute@Edge, WASM on Fastly, fastlike, XQD ABI, Compute request lifecycle, 508 loop detection, backend subrequests, body streaming, profiling a local Compute app, embedding a Fastly Compute runtime in Go code, or any question about how Compute platform primitives work internally (caching, KV stores, edge rate limiting, ACLs, geolocation, secret stores, config stores, dictionaries, logging, dynamic backends, request collapsing, async I/O).
Do NOT use for: Fastly VCL (use falco), Fastly CLI/API (use fastly-cli or fastly), Viceroy, CDN comparison, WAF, Terraform, cache purging via API, or Fastly logging/stats configuration.
# Fastlike — Local Compute Runtime & Reference
Fastlike is a Go implementation of the Fastly Compute ABI. It runs compiled WebAssembly programs locally, implementing the same 249+ host functions that Fastly's production Compute platform provides: backends, dictionaries, KV stores, caching, geolocation, rate limiting, ACLs, secret stores, and more.
Equally important, **the fastlike source code is the most complete programmatic specification of how Fastly Compute works** — its ABI implementations document every platform primitive, request lifecycle detail, and data structure as executable code.
**Fastlike documentation**: https://github.com/avidal/fastlike
## Source Code as Compute Reference
When you have access to the fastlike source code locally (default: `~/src/fastlike`), use these paths to answer specific Compute questions:
| Question | Read This File | Why |
| ----------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------- |
| "How does the request lifecycle work?" | `instance.go`, `xqd_http_downstream.go` | Per-request setup, execution, downstream handling |
| "What ABI functions exist for X?" | `xqd_*.go` files | Each file implements a group of related ABI functions |
| "How do backend subrequests work?" | `xqd_backend.go`, `backend.go` | Request sending, dynamic backends, timeouts |
| "How does caching work?" | `xqd_cache.go`, `xqd_http_cache.go`, `cache.go` | Cache operations, Vary, surrogate keys, request collapsing |
| "How does KV store work?" | `xqd_kv_store.go`, `kv_store.go` | CRUD operations, pagination, generation-based concurrency |
| "How does rate limiting work?" | `xqd_erl.go`, `erl.go` | Rate counters, penalty boxes, threshold checks |
| "How do ACLs work?" | `xqd_acl.go`, `acl.go` | CIDR-based IP filtering, most-specific match |
| "What configuration options exist?" | `options.go` | Every `With*` functional option for the runtime |
| "What error codes can operations return?" | `constants.go` | All XQD status codes and error types |
| "How does the profiler work / what does a trace look like?" | `profile.go`, `profile_json.go`, `docs/profiling.md` | Trace data model, JSON wire format, deep-mode metrics, encoders |
For a comprehensive guide, see [understanding-compute-from-source.md](references/understanding-compute-from-source.md).
## Install from Source
Requires Go 1.24+.
```bash
# Clone and build
git clone https://github.com/avidal/fastlike.git ~/src/fastlike
cd ~/src/fastlike
make build # Creates bin/fastlike
# Or install to GOPATH/bin
make install
# Or install directly
go install fastlike.dev/cmd/fastlike@latest
```
## Quick Start
The `bin/fastlike` examples use a source build.
Use its absolute path from your project directory, or `fastlike` for an installed binary.
```bash
# Minimal: WASM + single backend. The wasm path is positional.
bin/fastlike -backend localhost:8000 app.wasm
```
Flags can appear on either side of the wasm path.
On macOS, avoid binding to port 5000 — the AirTunes/AirPlay Receiver listens there by default and will steal the connection. Pick another port (e.g. `-bind localhost:8000`) or disable the AirPlay Receiver in System Settings.
## Fastlike vs Viceroy
| Feature | Fastlike | Viceroy |
| -------------- | ---------------------------- | ----------------------------------------- |
| Language | Go | Rust |
| Geolocation | Custom JSON file (`-geo`) | Built-in defaults |
| Hot reload | SIGHUP (`-reload`) | Restart required |
| Install | `go install` or `make build` | `cargo install` or `fastly compute serve` |
| Local backends | `-backend name=host:port` | `[local_server.backends]` in fastly.toml |
**When to use Fastlike**: Non-Rust Compute apps, want custom geo data, need hot reload, debugging.
**When to use Viceroy**: Rust Compute apps with cargo-nextest, Component Model projects, using `fastly compute serve`.
## Common Configurations
**With named backends:**
```bash
bin/fastlike \
-backend api=api.example.com:8080 \
-backend cache=redis:6379 \
-backend localhost:8000 \
app.wasm
```
**Development mode with hot-reload:**
```bash
bin/fastlike -backend localhost:8000 -reload -v 2 app.wasm
```
Send `SIGHUP` to reload the WASM without restarting.
**With the built-in profiler:**
```bash
bin/fastlike -backend localhost:8000 -profile-ui localhost:6060 app.wasm
```
Open `http://localhost:6060/` for the trace index. Each request lands as `/r/{id}` (HTML) or `/r/{id}.json` (canonical native JSON; also `.chrome.json`, `.firefox.json`, `.pprof`). Add `-profile deep` for body byte / cache outcome / header / wasm heap metrics. Non-loopback `-profile-ui` requires `-profile-auth TOKEN` (or explicit `-profile-insecure-ui`). See [profiling.md](references/profiling.md).
**Full configuration:**
```bash
bin/fastlike \
-bind 0.0.0.0:5000 \
-backend localhost:8000 \
-dictionary config=./config.json \
-kv store=./data.json \
-config-store settings=./settings.json \
-secret-store secrets=./secrets.json \
-acl blocklist=./acl.json \
-logger output=./logs.txt \
-geo ./geodata.json \
-compliance-region us-eu \
-v 2 \
-reload \
app.wasm
```
## Required Arguments
| Argument | Description |
| ------------------------------ | ---------------------------------------------------------- |
| `<wasm-file>` | Positional path to the WebAssembly program (required) |
| `-backend VALUE` or `-b VALUE` | Backend server (required, repeatable) |
## Optional Flags
| Flag | Default | Description |
| ------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `-bind ADDR` | `localhost:8000` | Server bind address |
| `-reload` | false | Enable SIGHUP hot-reload |
| `-v INT` | 0 | Verbosity (0-2) |
| `-dictionary NAME=FILE` or `-d` | - | Load dictionary from JSON |
| `-kv NAME[=FILE]` | - | KV store (empty or from JSON) |
| `-config-store NAME=FILE` | - | Config store from JSON |
| `-secret-store NAME=FILE` | - | Secret store from JSON |
| `-acl NAME=FILE` | - | ACL from JSON |
| `-logger NAME[=FILE]` | - | Log endpoint (file or stdout) |
| `-geo FILE` | - | Geolocation JSON file |
| `-compliance-region REGION` | - | Compliance region (none, us-eu, us) |
| `-profile MODE` | `trace` | `off`, `trace`, `native`, `combined`, `deep`. See [profiling.md](references/profiling.md). |
| `-profile-ui ADDR` | - | Bind the profile UI listener on ADDR (separate socket from `-bind`). |
| `-profile-auth TOKEN` | - | Bearer token required on UI requests. Mandatory for non-loopback `-profile-ui` unless `-profile-insecure-ui` is set. |
| `-profile-insecure-ui` | false | Permit a non-loopback `-profile-ui` without `-profile-auth` (use only behind external auth). |
| `-profile-retain N` | 256 | LRU size for completed traces. |
| `-profile-backend-cap N` | 512 | Per-request cap on recorded backend calls. |
| `-profile-async-grace DUR` | 100ms | How long finalize waits for in-flight async backends. Pass `0` to disable. |
| `-profile-dir PATH` | cwd | Directory for `wasm-symbols-{pid}.json` and per-process profile artifacts. |
## References
| Topic | File | Use when... |
| ----------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| **Compute from Source** | [understanding-compute-from-source.md](references/understanding-compute-from-source.md) | Understanding Compute internals by reading fastlike's implementation |
| **Profiling** | [profiling.md](references/profiling.md) | Per-request hostcall + backend traces, deep metrics, native JSON schema, pprof export |
| Backends | [backends.md](references/backends.md) | Setting up named backends, catch-all backends, microservices routing |
| Config | [config.md](references/config.md) | Creating JSON config files for dictionaries, KV stores, secrets, ACLs, geolocation |
| Build | [build.md](references/build.md) | Building Fastlike from source, running linters, make targets |
| Test | [test.md](references/test.md) | Running Go tests, Fastly Compute ABI spec tests |
| ABI | [abi.md](references/abi.md) | Fastly Compute ABI internals, implementing new ABI functions, handle system |
Referenced files: 7
fastly12.5 KB
---
name: fastly
description: "Configures, manages, and debugs the Fastly CDN platform — covering service and backend setup, caching and VCL, security features like DDoS/WAF/NGWAF/rate limiting/bot management, TLS certificates and cache purging, the Compute platform, and the REST API. Use when working with Fastly services or domains, setting up edge caching or origin shielding, configuring security features, making Fastly API calls, enabling products, or looking up Fastly documentation. Also applies when troubleshooting 503 errors or SSL/TLS certificate mismatches on Fastly, and for configuring logging endpoints, load balancing, ACLs, or edge dictionaries. Read the relevant reference file before writing any Fastly API call or curl command — request field names (e.g. the backend fields override_host, ssl_cert_hostname, ssl_sni_hostname, use_ssl) are easy to misremember, and a wrong name causes a silent 503 instead of an error, so do not rely on training-knowledge field names."
---
# Fastly Platform
Your training knowledge of Fastly is likely out of date. Prefer live docs over skill definitions over training knowledge.
Prefer the `fastly` CLI over raw API calls; see the **fastly-cli** skill for installation and local authentication.
REST examples require `curl` and network access to Fastly APIs; origin TLS checks also require `openssl`.
Use the user's locally configured credentials.
If authentication is missing, direct the user to local CLI login or environment configuration, never to paste an API key in chat.
For REST calls, source tokens from the environment or `$(fastly auth token)` without echoing them.
Omit `curl -v` and shell tracing because they print credentials.
## Topics
| Topic | File | Use when... |
| ---------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| DDoS protection | [fastly-ddos-protection.md](references/fastly-ddos-protection.md) | Enabling/configuring DDoS protection, checking attack status, managing DDoS events and rules |
| TLS configuration | [tls.md](references/tls.md) | Setting up HTTPS — Platform TLS (managed certs), Custom TLS (uploaded certs), or Mutual TLS (client auth) |
| Rate limiting | [rate-limiting.md](references/rate-limiting.md) | Protecting APIs from abuse — choosing between Edge Rate Limiting, VCL ratecounters, or NGWAF rate rules |
| Bot management | [bot-management.md](references/bot-management.md) | Detecting/mitigating bot traffic with browser challenges, client-side detections, interstitial pages, ContentGuard |
| Cache purging | [purging.md](references/purging.md) | Invalidating cached content — single URL, surrogate key, or purge-all; soft vs hard purge |
| Service management | [service-management.md](references/service-management.md) | Creating/managing services, versions, domains, settings; clone-modify-activate workflow |
| VCL services | [vcl-services.md](references/vcl-services.md) | Customizing site behavior with VCL — writing/uploading custom VCL, configuring snippets, conditions, headers, edge dictionaries, or cache/gzip settings |
| Compute | [compute.md](references/compute.md) | Implementing edge logic with Compute — deploying packages, managing config/KV/secret stores, using cache APIs |
| Observability | [observability.md](references/observability.md) | TTFB percentiles from the metrics platform, alert definitions and history, log explorer queries |
| Load balancing | [load-balancing.md](references/load-balancing.md) | Distributing traffic across origins — configuring backends, directors, pools, or health checks; choosing between backends and pools |
| ACLs | [acls.md](references/acls.md) | Restricting access by IP — managing VCL ACLs, Compute ACLs, or IP block lists; adding/removing access control entries |
| NGWAF | [ngwaf.md](references/ngwaf.md) | Protecting against web attacks — setting up Next-Gen WAF, post-cache bot management, rules, signals, attack monitoring, or Signal Sciences integration |
| Account management | [account-management.md](references/account-management.md) | Managing users, IAM roles, API tokens, automation tokens, billing, or invitations |
| Domains & networking | [domains-and-networking.md](references/domains-and-networking.md) | Routing traffic to Fastly — managing domains, DNS zones, domain verification, or other service platform networking |
| Logging | [logging.md](references/logging.md) | Shipping logs to external systems — configuring logging endpoints for 25+ providers (S3, Splunk, Datadog, BigQuery, etc.) |
| Products | [products.md](references/products.md) | Enabling/disabling Fastly products via API — universal pattern and product slug catalog |
| API security | [api-security.md](references/api-security.md) | Discovering APIs from web traffic, managing API operations and tags |
| Client-Side Protection | [client-side-protection.md](references/client-side-protection.md) | Protecting against rogue third-party scripts (Magecart, formjacking, skimmers) — monitoring scripts on web pages, managing script authorization, configuring CSP policies |
| Other features | [other-features.md](references/other-features.md) | Pubsub, fanout/real-time messaging, IP lists, POPs, HTTP/3, Image Optimizer, events, notifications |
| Edge phase ordering | [edge-phases.md](references/edge-phases.md) | Understanding edge request/response ordering, debugging feature interactions |
Traffic numbers are not in this table. Cache hit ratio, bandwidth, request and status-code counts, error rates, real-time request rate, origin latency, per-domain traffic, account usage and billing totals belong to the **fastly-stats** skill.
## Quick Start: Simple Caching Proxy
The most common task is setting up a VCL service to cache an origin. Before touching any Fastly config, always run the pre-flight checks from the **fastly-cli** skill's services.md reference under "Pre-flight checklist". The two checks that prevent the most common errors:
1. **Verify the origin responds** with the Host header you intend to send: `curl -sI -H "Host: DESIRED_HOST" https://ORIGIN_ADDRESS/`
2. **Check TLS certificate SANs** to determine the correct `ssl-cert-hostname`/`ssl-sni-hostname`: `echo | openssl s_client -connect ORIGIN:443 -servername ORIGIN 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"`
If HTTPS cert validation cannot be made correct but HTTP with the intended Host works, use an HTTP backend or fix the origin cert; never disable backend cert verification as the workaround.
If the origin already sends `Cache-Control` or `Expires` headers, no custom VCL is needed — Fastly respects these by default. Only add VCL snippets to override or extend caching behavior.
The full step-by-step workflow (create service, add domain, add backend, activate) is in the **fastly-cli** skill's services.md reference under "Create a Caching Proxy".
## Common VCL Recipes
Copy-pasteable patterns that are easy to get wrong without guidance.
### Grace Detection
`obj.ttl` is only meaningful in `vcl_hit`. Pass a flag to `vcl_deliver` via a request header.
```vcl
sub vcl_hit {
if (obj.ttl <= 0s) {
set req.http.X-Grace = "true";
}
}
sub vcl_deliver {
if (req.http.X-Grace) {
set resp.http.X-Grace = "true";
}
}
```
### Vary Header Append
**Warning: Set Vary in `vcl_fetch`, not `vcl_deliver`.** The Vary header must be present when the object enters the cache so the cache key includes the Vary dimensions. Setting Vary only in `vcl_deliver` means the cache won't differentiate responses — every user gets the same cached variant regardless of the Vary field.
Never `set beresp.http.Vary = "Accept-Encoding"` — that overwrites any existing Vary values from the origin, breaking other downstream caches.
```vcl
sub vcl_fetch {
if (!beresp.http.Vary) {
set beresp.http.Vary = "Accept-Encoding";
} else if (beresp.http.Vary !~ "Accept-Encoding") {
set beresp.http.Vary = beresp.http.Vary ", Accept-Encoding";
}
}
```
### Redirect via Error
VCL has no `return(redirect)`. Use the synthetic error mechanism instead.
```vcl
sub vcl_recv {
if (req.url ~ "^/old-path") {
error 801 "https://example.com/new-path";
}
}
sub vcl_error {
if (obj.status == 801) {
set obj.status = 301;
set obj.http.Location = obj.response;
synthetic {""};
return(deliver);
}
}
```
### Cache Status Headers
Use `obj.hits > 0` in `vcl_deliver` — this is the only reliable way to detect cache hits. Do not rely on auto-generated `resp.http.X-Cache` or any other header inspection. Pass PASS state from `vcl_recv` via a request header.
```vcl
sub vcl_recv {
if (req.url ~ "^/api/") {
set req.http.X-Pass = "true";
return(pass);
}
}
sub vcl_deliver {
if (req.http.X-Pass) {
set resp.http.X-Cache = "PASS";
} else if (obj.hits > 0) {
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
}
```
### Cookie Parsing with subfield()
Regex like `Cookie ~ "name=(\w+)"` is unreliable — it false-matches cookies with similar prefixes. For example, if the cookie header is `name_v2=X`, the regex `"name=(\w+)"` still matches because `name` appears as a substring of `name_v2`. Use `subfield()` instead — it performs exact key matching with proper delimiter handling.
```vcl
set req.http.X-My-Cookie = subfield(req.http.Cookie, "name", ";");
```
### VCL Table for Lookups
Use `table` + `table.contains()` + `table.lookup()` for O(1) lookups instead of long if/else chains.
```vcl
table redirects {
"/old": "/new",
"/blog": "/articles",
}
sub vcl_recv {
if (table.contains(redirects, req.url)) {
error 801 table.lookup(redirects, req.url);
}
}
```
### Common Mistakes
- `beresp.*` is only available in `vcl_fetch`, not `vcl_deliver`.
- `req.request` is deprecated — use `req.method`.
- `return(purge)` does not exist in Fastly VCL. Use `return(pass)` and check in `vcl_miss`/`vcl_hit`.
- `set beresp.ttl = 86400` is a type error — needs the `s` suffix: `86400s`.
- `synthetic "text"` needs long-string syntax: `synthetic {"text"}`.
- `beresp.ttl = 0s` still caches the object (for zero seconds) — use `set beresp.cacheable = false;` to truly prevent caching.
## Fetching Documentation
Prefer the local reference files. To fill gaps, fetch live docs with `Accept: text/markdown` — works for all `www.fastly.com/documentation/` and `docs.fastly.com` URLs. Discover pages via `https://www.fastly.com/documentation/llms.txt`. For URL patterns and doc categories, see [docs-navigation.md](references/docs-navigation.md).
Referenced files: 21
fastly-cli14.1 KB
---
name: fastly-cli
description: "Executes Fastly CLI commands for managing CDN services, Compute deploys, and edge infrastructure. Use when running `fastly` CLI commands, creating or managing Fastly services from the terminal, deploying Fastly Compute applications, managing backends/domains/VCL snippets via command line, purging cache, configuring log streaming, setting up TLS certificates, managing KV/config/secret stores, checking service stats, authenticating with Fastly SSO, or working with fastly.toml. Also applies when working with Fastly service IDs in CLI context, or with `fastly service`, `fastly compute`, `fastly auth`, or any Fastly CLI subcommand. Covers service CRUD, version management, autocloning, and troubleshooting common CLI errors."
---
## Trigger and scope
CRITICAL: many subcommands have unintuitive paths (e.g. `fastly domain create` fails with 403, correct is `fastly service domain create`; logging is under `fastly service logging`; alerts under `fastly service alert`; rate limits under `fastly service rate-limit`).
Covers: services, backends, domains, VCL snippets, cache purging, Compute/WASM deploys, log streaming (S3/Datadog/Splunk/Kafka/25+ providers), NGWAF/WAF, TLS/mTLS, KV/config/secret stores, stats, alerts, rate limiting, ACLs, and auth tokens.
# Fastly CLI Overview
## Prerequisites
Requires the `fastly` CLI; API operations also need network access to Fastly.
For installation, see <https://www.fastly.com/documentation/reference/cli/>.
JSON examples also need `jq`; origin checks use `curl` and `openssl`.
Compute builds need the project's language toolchain and dependencies.
Use credentials already configured in the user's local CLI or environment.
If authentication is missing, have the user complete `fastly auth login --sso` locally or configure `FASTLY_API_TOKEN` outside chat.
Never ask for an API key in chat, print a token, or enable shell tracing on authenticated commands.
## References
| Topic | File | Use when... |
| -------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Authentication | [auth.md](references/auth.md) | Login, stored tokens, service auth, CI/CD auth setup |
| Compute | [compute.md](references/compute.md) | Building/deploying edge applications, local dev server |
| Services | [services.md](references/services.md) | Service CRUD, backends, domains, ACLs, dictionaries, VCL, purging, rate limiting |
| Logging | [logging.md](references/logging.md) | Log streaming to S3, GCS, Datadog, Splunk, Kafka, 25+ providers |
| NGWAF | [ngwaf.md](references/ngwaf.md) | Next-Gen WAF workspaces, IP/country lists, rules, signals, thresholds, alerts |
| Notifications | [notifications.md](references/notifications.md) | Slack/PagerDuty/webhook integrations, audit log event mappings |
| Stats | [stats.md](references/stats.md) | Which `fastly stats` subcommand takes which flag, and where the two flag sets diverge |
| Stores | [stores.md](references/stores.md) | KV Stores, Config Stores, Secret Stores, resource links |
| TLS | [tls.md](references/tls.md) | Platform TLS, Let's Encrypt subscriptions, custom certs, mutual TLS |
For endpoint choice, unit and window conventions and worked queries, use the **fastly-stats** skill.
## Command Structure
```
fastly <command> <subcommand> [flags]
```
### Top-Level Commands
| Category | Commands |
| ------------ | --------------------------------------------------------------------------------------- |
| **Compute** | `compute` - Build and deploy edge applications |
| **Services** | `service` - Manage CDN services, logging, backends, VCL, ACLs, purging |
| **Security** | `ngwaf` - Web application firewall |
| **TLS** | `tls-subscription`, `tls-custom`, `tls-platform`, `tls-config` - Certificate management |
| **Storage** | `kv-store`, `config-store`, `secret-store` - Edge data stores |
| **Auth** | `auth` - Login, stored tokens, active token output, revocation; `auth-token` (deprecated) |
| **Info** | `stats`, `ip-list`, `pops`, `whoami` - Information queries |
| **Notify** | `integration` - Notification destinations; `audit-log event-mapping` - Event triggers |
| **Other** | `dashboard`, `domain`, `dns`, `apisecurity`, `products`, `object-storage`, `tools` |
## Global Flags
Available on most commands:
```bash
# Service targeting
--service-id SERVICE_ID # Target service by ID
--service-name NAME # Target service by name
-s SERVICE_ID # Short form
# Version targeting (version-scoped commands like `fastly service domain/backend/...`)
# NOTE: `fastly domain create` does NOT accept --version (it uses a different API)
--version VERSION # Specific version number
--version active # Currently active version
--version latest # Most recent version
--version staged # Currently staged version
# Authentication
--token TOKEN # API token or stored token name (use 'default' for default)
# Output (--json is per-command, not global)
--verbose # Detailed output
--quiet # Minimal output
# Automation
--accept-defaults # Accept default values
--auto-yes # Skip confirmations
--non-interactive # No prompts
```
## Key Patterns
- Target by ID (`-s SERVICE_ID`) or name (`--service-name NAME`)
- Version targeting: `--version active`, `--version latest`, `--version staged`, or `--version N`
- Use `--autoclone` to auto-clone locked versions
- Use `--json` for scripted output, `--non-interactive --accept-defaults` for CI/CD
- JSON field names vary by command; inspect output before writing `jq` selectors.
- `ActiveVersion` shape varies; prefer `--version active`, or parse with `jq -r '.ActiveVersion.Number // .ActiveVersion'`
- CLI version is `fastly version` (not `fastly --version`)
- POP/shield lookup is `fastly pops`; it has no `list` subcommand and no `--json`; use the `SHIELD` column value (not POP `CODE`) for `--shield`
- Auth: `fastly auth login --sso` to login, or set `FASTLY_API_TOKEN` env var
- For commands that need the active API token, use `$(fastly auth token)`; select a specific stored token with `$(fastly auth token --token TOKEN_NAME)`
- `auth token` refuses terminal output, but agent-captured stdout may be non-terminal; never run it standalone or echo its result
- Logging is under `service logging` (e.g. `fastly service logging s3 create`)
- Config: use `fastly config --location` to find the platform-specific CLI config file; `fastly.toml` is the project manifest
## Common Flag Examples
These are the flags that cause the most confusion. Copy-paste these patterns directly.
### Autocloning (existing service versions)
```bash
# --autoclone automatically clones a locked version before making changes.
# Without it, you get "version is locked" errors and waste time cloning manually.
fastly service backend create --service-id $SID --version active --autoclone \
--name my-origin --address origin.example.com --port 443 --use-ssl
fastly service domain create --service-id $SID --version active --autoclone \
--name cdn.example.com
```
Pass `--autoclone` when creating, updating, or deleting backends, domains, snippets, VCL, conditions, headers, or other version-scoped resources on an existing service.
For a brand new service, configure the unlocked `--version 1` without `--autoclone`, then validate and activate once, as shown in the new-service workflow below.
### Boolean flags (--use-ssl, --use-ssl is NOT --use-ssl true)
```bash
# CORRECT - boolean flags are bare, no value
fastly service backend create --name origin --address example.com --port 443 --use-ssl
# WRONG - do not pass a value to boolean flags
fastly service backend create --name origin --address example.com --port 443 --use-ssl true
```
Other boolean flags that work the same way: `--auto-yes`, `--non-interactive`, `--verbose`, `--quiet`, `--autoclone`.
### Domain creation (requires --name flag)
```bash
# CORRECT
fastly service domain create --service-id $SID --version active --autoclone --name cdn.example.com
# WRONG - domain is not a positional argument
fastly service domain create --service-id $SID --version active cdn.example.com
# WRONG - there is no -d flag
fastly service domain create --service-id $SID --version active -d cdn.example.com
```
### Stats (historical and real-time)
```bash
# Historical stats by day for a date range (JSON output)
fastly stats historical --service-id $SID --by day \
--from "2026-02-01" --to "2026-03-01" --json
# Real-time stats (last second)
fastly stats realtime --service-id $SID --json
```
The `--by` flag accepts: `day`, `hour`, `minute`. The `--from` and `--to` flags use quoted date strings. Use `--json` for JSON output on stats commands.
## Propagation Delays
Changes propagate across Fastly's network in seconds to minutes (up to 10 min for version activations, up to 5 min for TLS). Cache purges are 1-2 seconds. Retry with backoff when verifying changes.
**New service activation sequence**: After activating a brand new service, expect 500 "Domain Not Found" for 10-60 seconds while the domain propagates to edge POPs. This is normal — do not change configuration. Wait and retry. After version updates (e.g., fixing backend settings), allow 15-30 seconds for the new version to propagate.
## KV Store Gotchas
- **Link before use**: A KV store must be linked to a service version before Compute code can access it. Use `fastly kv-store create` then `fastly service resource-link create --resource-id STORE_ID --service-id $SID --version active --autoclone`.
- **Eventual consistency**: Read-after-write is eventually consistent. A key you just wrote may not be readable for a few seconds. Do not rely on immediate read-back in scripts; add a short delay or retry loop.
- **Entry size limit**: Individual KV store entries are limited to 25 MB. Plan accordingly for large values.
- **Listing stores**: `fastly kv-store list` lists all stores on the account, not per-service. Use `fastly service resource-link list` to see which stores are linked to a given service.
## Host Header Override Pattern
When the origin hostname differs from the desired Host header (e.g., origin is `example.com` but you want to send `Host: download.example.com`), use `--override-host` on the backend:
```bash
fastly service backend create --service-id $SID --version 1 \
--name my-origin --address example.com --port 443 --use-ssl \
--override-host download.example.com \
--ssl-cert-hostname example.com --ssl-sni-hostname example.com
```
The `--override-host` value is the Host header sent to the origin. The `--ssl-cert-hostname` and `--ssl-sni-hostname` must match the origin's TLS certificate (usually the `--address` value). Getting these backwards causes 503 errors.
## Service List Completeness
When enumerating services (e.g., for bandwidth stats), use `fastly service list --json`.
The command follows API pagination internally and returns all pages unless you explicitly start from a later `--page`.
Services with zero traffic still appear in the list, so loop over every returned service ID instead of relying on stats APIs that omit zero-traffic services.
## New VCL Service Setup Workflow
Use this sequence to stand up a new VCL caching service end-to-end. Each step includes a validation checkpoint.
1. **Pre-flight** — verify the origin responds and check its TLS certificate SANs:
```bash
curl -sI -H "Host: DESIRED_HOST" https://ORIGIN_ADDRESS/
echo | openssl s_client -connect ORIGIN_ADDRESS:443 -servername ORIGIN_ADDRESS 2>/dev/null | \
openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
```
_Checkpoint: origin returns 200 and the backend `ssl-cert-hostname` matches the served cert. If no HTTPS SNI/cert combination validates but HTTP with that Host works, use `--port 80` or fix the origin cert; do not disable verification._
2. **Create service** — note the service ID from the output:
```bash
fastly service create --name "my-service" --non-interactive
```
3. **Add domain + backend on version 1** (do NOT use `--autoclone` or `--version latest` on a new service):
```bash
fastly service domain create --service-id $SID --version 1 \
--name my-service.global.ssl.fastly.net
fastly service backend create --service-id $SID --version 1 \
--name origin --address ORIGIN_ADDRESS --port 443 --use-ssl \
--override-host ORIGIN_ADDRESS \
--ssl-cert-hostname ORIGIN_ADDRESS --ssl-sni-hostname ORIGIN_ADDRESS
```
4. **Validate version** before activating:
```bash
fastly service version validate --service-id $SID --version 1
```
_Checkpoint: validation returns success (no missing domain/backend errors)._
5. **Activate**:
```bash
fastly service version activate --service-id $SID --version 1
```
6. **Verify propagation** — wait 15-30s, then test with GET (not HEAD):
```bash
curl -sS -D - -o /dev/null https://my-service.global.ssl.fastly.net/ | head -1
```
_Checkpoint: 200 OK. If 500 "Domain Not Found", wait and retry (normal for 10-60s). If 503, check backend SSL settings._
See [services.md](references/services.md) for advanced workflows (custom domains with TLS, host header overrides, live service updates).
## Troubleshooting
See [troubleshooting.md](references/troubleshooting.md) for the full list. Key pitfalls are covered inline above: SSL hostname flags (see Host Header Override Pattern), boolean flags and domain `--name` (see Common Flag Examples), `--autoclone` (see Key Patterns), and token safety (see Key Patterns).
Referenced files: 10
fastly-fiddle16.7 KB
---
name: fastly-fiddle
description: "Use when testing VCL against real Fastly edge infrastructure, writing assertion-based Fiddle tests, producing shareable fiddle URLs for bug reproductions, running VCL integration tests in CI, linting VCL remotely via the Fiddle API, or working with clientFetch/events/originFetches test expressions."
---
# Fastly Fiddle — Real-Edge VCL Testing
## Trigger and scope
Trigger on: Fastly Fiddle, fiddle.fastly.dev URLs, the Fiddle HTTP API, CI testing of VCL services, real-edge VCL tests, shareable VCL reproductions, `clientFetch.*`/`events.*`/`originFetches.*` test expressions, `.test.js` / Mocha specs that target fiddles, SSE `updateResult` / `waitingForSync` events, remote VCL linting via Fiddle, or validating real Fastly behavior (geo, WAF, ESI, clustering, shielding) that local tools cannot simulate.
Do NOT use for: local VCL unit testing (use `falco`), fast TDD loops (Fiddle has a 10-20s edge-sync floor per publish), Fastly Compute/Wasm testing (use `viceroy` or `fastlike`), production service deployment (use `fastly-cli`), or anything requiring authenticated Fastly API access — Fiddle does not use your Fastly API key.
Fastly Fiddle is a web-based sandbox at <https://fiddle.fastly.dev> that compiles and runs VCL on real Fastly edge nodes. Because it uses the production VCL compiler and real POPs, it's the only way outside a real service to test VCL features that depend on edge infrastructure — geolocation data, WAF, ESI, clustering, shielding, rate limiting, real TLS, and real cache behavior.
**Official UI**: <https://fiddle.fastly.dev>
**Demo CI runner**: <https://github.com/fastly/demo-fiddle-ci>
**API base**: `https://fiddle.fastly.dev` (undocumented but stable; no auth required for public fiddles)
## Prerequisites and public uploads
The helper requires Bash, `curl` 7.76 or newer, `jq`, and network access to `fiddle.fastly.dev`.
If a tool is missing, name it and provide installation guidance before running the helper.
Creating or updating a fiddle uploads its VCL, origins, request headers, request bodies, and tests to a public service, including with `--lint-only`.
Only publish when the user has authorized a public Fiddle upload or shareable reproduction.
For a request limited to local testing, use Falco and keep the spec local.
Never include credentials or private project data in a fiddle.
Set `FIDDLE_SKILL_DIR` to the absolute directory containing this `SKILL.md`, using the installed skill location supplied by the client:
```bash
FIDDLE_SKILL_DIR=/absolute/path/to/fastly-fiddle
```
This is a variable you assign, not a client-provided environment variable.
Keep the user's project as the working directory so inputs such as `fiddle.json` and `spec.json` remain project-relative.
Bundled scripts and examples use `FIDDLE_SKILL_DIR` instead.
## When Fiddle, when Falco
| Need | Use |
| ----------------------------------------------------- | ---------------------------------- |
| Fast local iteration (< 1s), watch mode, offline | `falco test` |
| Real Fastly VCL compiler and semantics | Fiddle |
| Real `client.geo.*`, WAF, ESI, rate limiting, shield | Fiddle |
| Shareable URL for bug repros and support tickets | Fiddle |
| CI against real edge nodes | Fiddle |
| Structured lint with line/col (no execution required) | Either |
| Fastly Compute (WASM) | Neither — use `viceroy`/`fastlike` |
Common workflow: iterate locally with `falco test` for speed, then push edge cases to Fiddle when you need real Fastly behavior or a shareable link. See [falco-vs-fiddle.md](references/falco-vs-fiddle.md) for full trade-offs.
## Workflow: deliverable first, then the cheapest check that answers your question
Two rules keep you out of the slow path, which is what wastes time and gets
agents killed by a wall-clock limit:
1. **If your job is to produce a spec file, write it to disk first**, before
any network call. The file is the deliverable — it must exist even if a
later publish stalls on a cold edge-sync. Don't build the spec only inside
a `curl --data` argument and lose it when the call blocks.
2. **Match the check to the question.** "Does this VCL compile / is the spec
shape accepted?" is answered by a _single_ `POST /fiddle` reading `valid` —
~1-3s, no execution, no edge-sync wait (see gotcha #5). You only need the
full publish → execute → SSE round trip when you must observe _runtime_
assertion results (actual `clientFetch`/`originFetches`/`events` values),
and that pays the 10-120s edge-sync floor per publish. **Executing is the
exception, not the default** — reach for it deliberately, and always bound
your wait; never block indefinitely on the stream.
### Lint-only (the common case): compile check, no execution
```bash
bash "$FIDDLE_SKILL_DIR/scripts/run-fiddle.sh" --lint-only fiddle.json
# {fiddle_id, url, valid, lintStatus}. Exit 0 = compiles, 2 = lint error
# (details on stderr). No /execute, no SSE, no edge-sync wait.
```
The raw equivalent is one call — POST and read `valid`:
```bash
UA='fiddle-skill-example/1.0'
curl -sS --max-time 30 -X POST https://fiddle.fastly.dev/fiddle \
-H 'Content-Type: application/json' -H "User-Agent: $UA" \
--data @fiddle.json | jq '{valid, lintStatus}' # valid==true ⇒ it compiles
```
### Full round trip (only when you need runtime results)
When you genuinely need to see assertions pass/fail on the edge, the bundled
helper handles publish → execute → SSE → completion-detection in one call,
with a bounded `--max-wait` (default 180s per attempt) so it can't hang:
```bash
bash "$FIDDLE_SKILL_DIR/scripts/run-fiddle.sh" "$FIDDLE_SKILL_DIR/examples/robots.json"
# Prints fiddle URL, then pass/fail JSON per assertion (including body_preview
# and status by default). Exits non-zero on failure. Pass --no-bodies for
# compact output.
# Iterate against an already-published fiddle without paying edge-sync again:
bash "$FIDDLE_SKILL_DIR/scripts/run-fiddle.sh" --id <fiddle-id> # re-execute (warmest, ~2s)
bash "$FIDDLE_SKILL_DIR/scripts/run-fiddle.sh" --id <fiddle-id> spec.json # PUT then execute
```
The equivalent raw-curl flow, for reference or when the helper isn't available:
```bash
UA='fiddle-skill-example/1.0'
# 1. Create. Capture the ID and the validity flag.
RESP=$(curl -sS -X POST https://fiddle.fastly.dev/fiddle \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
-H "User-Agent: $UA" \
--data '{
"origins": ["https://http-me.fastly.dev"],
"vcl": {
"init": "# Synthetic robots.txt response\n# via error restart pattern",
"recv": "if (req.url.path == \"/robots.txt\") {\n error 601;\n}",
"error": "if (obj.status == 601) {\n set obj.status = 200;\n synthetic {\"User-agent: BadBot\"};\n return(deliver);\n}"
},
"requests": [
{ "path": "/robots.txt",
"headers": "X-Custom: value\nX-Other: second",
"tests": ["clientFetch.status is 200", "clientFetch.bodyPreview includes \"BadBot\""] }
]
}')
FID=$(echo "$RESP" | jq -r '.fiddle.id')
echo "$RESP" | jq '{valid, lintStatus}' # bail here if valid is false
# 2. Execute. Subscribe to the SSE stream IMMEDIATELY — session IDs expire fast.
SID=$(curl -sS -X POST "https://fiddle.fastly.dev/fiddle/$FID/execute?cacheID=1" \
-H 'Accept: application/json' -H "User-Agent: $UA" | jq -r '.sessionID')
# 3. Stream results. Emits repeated `event: waitingForSync` (~10-20s on first
# publish) then `event: updateResult` with full pass/fail data. ALWAYS cap
# the stream with --max-time so a slow/cold sync can't block you forever;
# on timeout, re-execute the same $FID (warm, ~2s) rather than re-publishing.
curl -sS -N --max-time 120 -H "User-Agent: $UA" "https://fiddle.fastly.dev/results/$SID/stream"
```
Full protocol details in [api.md](references/api.md).
## Wire-format gotchas
Non-obvious behavior that will break tools round-tripping fiddles programmatically:
1. **`vcl` on input, `src` on output.** You `POST {"vcl": {"recv": "..."}}` but `GET` returns `{"src": {"recv": "..."}}`. The server renames the key on normalization. Any tool that fetches a fiddle and re-publishes it must map `src` → `vcl` (or send `src` — both work on input). **Update existing fiddles with `PUT /fiddle/:id`** — same body shape as `POST`, but partial updates are not supported; omitted subroutines are cleared.
2. **`tests` is a string on the wire.** You can send `tests: ["a", "b"]` but a subsequent `GET` returns `tests: "a\nb"`. One assertion per line. Split on `\n` when reading.
3. **`headers` is a newline-joined string, not an array.** Unlike `tests` (which accepts both), `headers` must be a string: `"headers": "User-Agent: BadBot/1.0\nX-Custom: value"`. An array will be rejected with a validation error.
4. **Request fields are auto-defaulted by the server.** A `GET` of a fiddle you just created will include fields you didn't send: `method: "GET"`, `connType: "h2"` (HTTP/2 by default — matters for tests that depend on protocol), `enableCluster: true`, `enableShield: false`, `useFreshCache: false`, `sourceIP: "client"`, `followRedirects: false`, `delay: 0`. Set them explicitly if you care.
5. **Invalid VCL still gets a fiddle ID.** `POST` returns `{valid: false, lintStatus: {...}, fiddle: {id, ...}}` for broken VCL. On a **create/update** (`POST`/`PUT`) response, `valid` is the lint result — `true` if the VCL compiles, `false` if not, with details in `lintStatus`. That's the number to trust for "does this compile?", and you get it without executing anything. Don't rely on HTTP status.
**But `valid` means something different on a `GET`.** There it tracks execution, not compilation: it stays `false` until the fiddle has been executed at least once, then flips to `true`. A fiddle that lints perfectly cleanly still reads back as `valid: false` right after you create it. So judge compilation from the create/update response (or by re-submitting the spec) — never from a `GET`. A `GET` can't distinguish a valid-but-not-yet-run fiddle from a genuinely broken one: both come back `valid: false` with an empty `lintStatus`.
6. **VCL string concat with `+` rejects parenthesized operands.** `set X = "used=" + (a - b);` fails with a _misleading_ "Remove the trailing `+` operator" suggestion — the `+` is fine, the `(` is what the parser rejects. Compute the sub-expression into a local variable first. See [spec-shape.md](references/spec-shape.md#vcl-string-concatenation).
7. **`error 8NN;` / `error 9NN;` is rejected by Fiddle lint — use 6xx.** Any 800–999 code fails with "8xx and 9xx error codes are used internally by Fastly. Use 6xx instead.", in any subroutine and any context (bare or inside `if`/`else`/`switch`). So the classic `error 801 <url>;` redirect idiom trips Fiddle lint even inside an `if` — use `error 602 "<url>";` and build the 301 in `vcl_error`. Codes 400–799 are accepted. See [spec-shape.md](references/spec-shape.md#error-codes-use-6xx-for-synthetics).
8. **Some test expressions have built-in delays.** `originFetches.count() is 0` returns `asyncDelay: 2500` — the server waits 2.5s before evaluating "did nothing happen?". Client wait time must accommodate this; 45-60s is a safe ceiling.
9. **SSE session IDs are short-lived.** Subscribe to `/results/<sessionID>/stream` within seconds of receiving the ID from `/execute`. Delayed connections get a 404. Always have the stream open before you start waiting on results.
10. **Test DSL has unusual syntax.** No `.first()`, no `reqHeaderValue()`, no `isnt`/`empty` operators. Event objects expose only `url`/`method`/`return`/`status`/`ttl` — not arbitrary `req.http.*` values. **Read [test-dsl.md](references/test-dsl.md) before writing assertions.**
11. **The server assigns a fresh fiddle ID on every `POST /fiddle`** — even for byte-identical input. IDs are not a content hash; back-to-back POSTs of the same body return different IDs, and each new ID needs its own edge-sync pass before `/execute` can produce results. **`PUT /fiddle/<id>`** keeps the URL stable (good for shared bug-repro links) but the new VCL still recompiles and propagates, so PUTs pay the same edge-sync cost as POSTs. The genuinely warm path is **re-executing an unchanged fiddle ID** — same content, repeat `/execute` calls finish in ~2s. Capture the ID from the first POST and reuse it; vary `cacheID` to force cold caches without re-publishing. (PUT is not partial — omitted subroutines are cleared, see #1.)
12. **`originFetches.count() is N` is fragile under retries and shared `cacheID`.** A retry — automatic in `run-fiddle.sh`, or manual via `--id <fid>` — re-executes against the same `cacheID`, so any origin response cached on the previous attempt is now a HIT and `originFetches.count()` drops to 0. Two reliable fixes: set **`useFreshCache: true`** on the request (forces a fresh cache, ignoring the session `cacheID` — see [spec-shape.md `Request objects`](references/spec-shape.md#request-objects)), or assert via **`events.where(fnName=fetch).count()`** (counts subroutine entries, not network calls). The same goes for `originFetches[0].*` assertions whenever the test runs after a possible warmup.
## Authoring conventions
Fiddles are read by humans in a browser. These aren't surprises, but they make shared fiddles useful:
- **Set a `title`.** Makes fiddles findable in browser tabs, bookmarks, and shared links. Example: `"title": "fastly_info.state: compound values deep dive"`.
- **Use `init` as a header comment.** The `init` subroutine renders first in the UI. Put a short summary there (~55 chars/line): `"init": "# fastly_info.state: compound values\n# Demonstrates MISS-CLUSTER, HIT-CLUSTER, HIT-SYNTH"`.
- **Format VCL with `\n` and indentation** rather than cramming everything onto one line.
- **Send `User-Agent: <tool>/<version>` on every API call.** Fiddle is unauthenticated shared infra; default `curl/x.y` or library UAs are bad citizenship. This is the API call's UA, not the simulated request's `headers`.
## Testing in CI
Reference implementation: [fastly/demo-fiddle-ci](https://github.com/fastly/demo-fiddle-ci) — a Node + Mocha harness. Clone it and write your `{spec, scenarios[]}`.
The one non-obvious thing: it publishes the fiddle once, then re-executes the same fiddle ID per scenario with different `requests[]`. Only re-execution is warm (~2s); a fresh publish pays the 10-20s edge-sync floor (see "Limits" below and gotcha #11). Keep scenarios sequential.
## References
| Topic | File | Use when... |
| ------------------- | --------------------------------------------------- | ------------------------------------------------------------------ |
| **Helper script** | [scripts/run-fiddle.sh](scripts/run-fiddle.sh) | Publishing + executing + streaming a fiddle in one shell command |
| **Example payload** | [examples/robots.json](examples/robots.json) | Starting from a known-good minimal fiddle spec |
| **HTTP API** | [api.md](references/api.md) | Calling Fiddle endpoints directly, driving it from any language |
| Fiddle spec shape | [spec-shape.md](references/spec-shape.md) | Building the JSON payload: origins, src, requests, defaults |
| Test DSL | [test-dsl.md](references/test-dsl.md) | Writing `clientFetch.*`, `events.where(...)`, `originFetches.*` |
| Falco vs Fiddle | [falco-vs-fiddle.md](references/falco-vs-fiddle.md) | Choosing the right tool, or combining them in one workflow |
## Limits and cautions
- **No auth required, no quota documented.** Be a good citizen: don't hammer the API in tight loops, and always send a descriptive `User-Agent` on API calls (see Authoring conventions). Use `cacheID` consistently across requests that need to share cache, and vary it to force cold caches.
- **Edge-sync floor is ~10-20s per publish, sometimes much longer.** Applies per fiddle ID, not per unique content — and every `POST /fiddle` mints a new ID (see Wire-format gotchas #11), so re-publishing identical input still pays the full sync cost. Cold publishes regularly take 60-120s in practice. Unusable for TDD. Batch changes; execute once per meaningful delta; reuse IDs via `PUT` when iterating.
- **Execution hops through a real POP** (tests observed running from IAD on node `kiad7000140`). Geographic assertions reflect wherever the fiddle executor landed.
- **Fiddles are public by default.** Don't put secrets in VCL you publish.
- **The API is undocumented.** Field names and behavior can change.
Referenced files: 6
fastly-ngwaf8.39 KB
---
name: fastly-ngwaf
description: "Performs an internal audit of Fastly Next-Gen WAF (NGWAF) workspaces to audit that critical templated protection rules are configured and enabled. Use when auditing NGWAF workspace security posture, checking for missing or disabled login protection rules (LOGINDISCOVERY, LOGINATTEMPT, LOGINSUCCESS, LOGINFAILURE), auditing credit card validation rules (CC-VAL-ATTEMPT, CC-VAL-FAILURE, CC-VAL-SUCCESS), auditing gift card protection rules (GC-VAL-ATTEMPT, GC-VAL-FAILURE, GC-VAL-SUCCESS), identifying potential login endpoints not covered by NGWAF rules, or comparing attack traffic against blocked traffic to confirm enabled rules are actually blocking."
---
# Fastly NGWAF Workspace Audit
Audits NGWAF workspaces to verify critical templated rules are configured and enabled. Use the **fastly-cli** skill to configure rules; this skill identifies gaps.
## Quick Start
The bundled assessment requires Bash, `curl`, `jq`, network access to `api.fastly.com`, and a locally configured `FASTLY_API_KEY` with NGWAF read access.
The manual workflow also requires the `fastly` CLI.
Configure credentials in the user's local environment or CLI configuration.
Never ask the user to paste an API key into chat or print it in command output.
Set `NGWAF_SKILL_DIR` to the absolute directory containing this `SKILL.md`, using the installed skill location supplied by the client.
This is a variable you assign, not a client-provided environment variable.
Run the helper from the user's working directory:
```bash
NGWAF_SKILL_DIR=/absolute/path/to/fastly-ngwaf
bash "$NGWAF_SKILL_DIR/scripts/assess_ngwaf_rules.sh"
```
For manual inspection or partial audits, work through the steps below with the `fastly` CLI.
The CLI ignores `FASTLY_API_KEY`. Its precedence is `--token` > `FASTLY_API_TOKEN` > `fastly.toml` profile > default
stored token. To reuse the script's credential:
```bash
export FASTLY_API_TOKEN="$FASTLY_API_KEY"
```
## Audit Workflow
1. **List workspaces** — verify the account has NGWAF workspaces
2. **Fetch rules per workspace** — retrieve each workspace's rule set
3. **Validate critical signals** — confirm required rules exist and are enabled
4. **Flag gaps and search for uncovered endpoints** — report missing/disabled rules
5. **Check attack traffic against blocked traffic** — confirm enabled rules are actually blocking
### Step 1: List Workspaces
```bash
fastly ngwaf workspace list --json | jq -r '.data[].id'
```
If empty, NGWAF is not configured for this account.
### Step 2: Fetch Rules for a Workspace
```bash
fastly ngwaf workspace rule list --workspace-id "$WORKSPACE_ID" --json
```
Both list commands return `{"data": [...], "meta": {...}}` and cap at 100 items with no flag to raise it. Check
`.meta.total`; above 100, fall back to `GET /ngwaf/v1/workspaces?limit=200` or `.../rules?limit=200`.
`rule list` also takes `--enabled` and `--action` to filter server-side.
### Step 3: Validate Critical Signals
For each workspace, verify these templated rules exist and `enabled` is `true`:
| Category | Required Signals |
| ---------------------- | ---------------------------------------------------------------- |
| Login Protection | `LOGINDISCOVERY`, `LOGINATTEMPT`, `LOGINSUCCESS`, `LOGINFAILURE` |
| Credit Card Validation | `CC-VAL-ATTEMPT`, `CC-VAL-FAILURE`, `CC-VAL-SUCCESS` |
| Gift Card Validation | `GC-VAL-ATTEMPT`, `GC-VAL-FAILURE`, `GC-VAL-SUCCESS` |
Check a specific signal:
```bash
fastly ngwaf workspace rule list --workspace-id "$WORKSPACE_ID" --json \
| jq '[.data[] | select(.actions[].signal == "LOGINDISCOVERY") | {enabled, id}]'
```
### Step 4: Search for Uncovered Login Endpoints
When `LOGINATTEMPT` is missing or disabled, search recent request logs for login-like traffic the WAF isn't protecting.
No CLI equivalent exists (there is no `fastly ngwaf workspace requests`), so use the API:
```bash
curl -s -H "Fastly-Key: $FASTLY_API_KEY" \
"https://api.fastly.com/ngwaf/v1/workspaces/$WORKSPACE_ID/requests?limit=100&page=1&q=from%3A-30min%20method%3APOST%20path%3A~%22%2Alogin%2A%22" \
| jq -r '.data[].path' | sort | uniq -c
```
### Step 5: Check Attack Traffic Against Blocked Traffic
A rule can be present and enabled and still block nothing when the workspace mode overrides it. `requests_attack` is
what NGWAF flagged; `requests_total_blocked` is what it stopped. Attacks above zero with nothing blocked means the
workspace is in `log` or `off` mode. Report it even when every rule checks out.
```bash
fastly ngwaf workspace time-series get --workspace-id "$WORKSPACE_ID" \
--from=2026-08-01T00:00:00Z --to=2026-08-08T00:00:00Z \
--metrics=requests_total,requests_attack,requests_total_blocked \
--granularity=86400 --json \
| jq -r '.data[] | "\(.timestamp) total=\(.requests_total) attack=\(.requests_attack) blocked=\(.requests_total_blocked)"'
```
Across every workspace at once, grouped by workspace:
```bash
fastly ngwaf time-series list \
--from=2026-08-01T00:00:00Z --to=2026-08-08T00:00:00Z \
--metrics=requests_total,requests_attack \
--granularity=86400 --dimensions=workspaces --json \
| jq -r '.data[] | "\(.dimensions.workspace) \(.dimensions.time) \(.values | add)"'
```
The workspace-level subcommand is `get`, the account-level one is `list`. They differ in three ways that break audit
scripts:
- Output shape. `get` returns flat objects keyed by metric with a `timestamp`. `list` nests them under `dimensions`
and a `values` array, hence `.values | add`.
- Bucket size. The CLI only sends `--granularity` when passed; `get` then buckets hourly and `list` daily. Always
pass it.
- Zeroes. `get` reports a quiet metric as `0`. `list` drops it from `values`, and returns
`{"data":[],"meta":{"total":0}}` when nothing recorded. A missing key means zero, not an error.
Read `requests_total_blocked` through `get`. A workspace that blocked nothing is the case this audit is looking for,
and `list` reports it as an absence.
`--from` and `--metrics` are required on both. Timestamps are RFC 3339, not the `YYYY-MM-DD` that `fastly stats` takes.
`--metrics` also accepts `XSS`, `SQLI`, `HTTP404` and any custom signal name on the workspace, so a rule verified in
step 3 can be checked for real traffic by signal name. Query those through `get`.
## Expected Output
**Healthy workspace** — all signals present and enabled:
```text
### Workspace: abc123
[LOGIN Rules]
- LOGINDISCOVERY: ENABLED
- LOGINATTEMPT: ENABLED
- LOGINSUCCESS: ENABLED
- LOGINFAILURE: ENABLED
[CC Rules]
- CC-VAL-ATTEMPT: ENABLED
- CC-VAL-FAILURE: ENABLED
- CC-VAL-SUCCESS: ENABLED
[GC Rules]
- GC-VAL-ATTEMPT: ENABLED
- GC-VAL-FAILURE: ENABLED
- GC-VAL-SUCCESS: ENABLED
```
**Unhealthy workspace** — missing or disabled rules require remediation:
```text
### Workspace: def456
[LOGIN Rules]
- LOGINDISCOVERY: NOT CONFIGURED (Recommended: CRITICAL: Configure and enable this rule to discover unknown login endpoints)
- LOGINATTEMPT: IS DISABLED (Recommended: Enable this rule)
- LOGINSUCCESS: ENABLED
- LOGINFAILURE: ENABLED
-> LOGINATTEMPT is not enabled. Searching recent request logs for potential login paths...
-> Found potential login paths in last 30 minutes:
3 /api/v1/login
1 /auth/signin
```
## Error Handling
| Error | Cause | Fix |
| --------------------------------- | ---------------------------- | ---------------------------------------------- |
| `FASTLY_API_KEY not set` | Environment variable missing | Configure the key locally, outside chat |
| `API call failed with status 403` | Token lacks NGWAF scope | Verify token has `global:read` permission |
| `No workspaces found` | NGWAF not provisioned | Enable NGWAF on the account first |
| `jq is not installed` | Missing dependency | `brew install jq` or `apt-get install -y jq` |
| `curl is not installed` | Missing dependency | Install `curl` with the system package manager |
## API References
- [List Workspaces](https://www.fastly.com/documentation/reference/api/ngwaf/workspaces/#ngwafListWorkspaces)
- [List Workspace Rules](https://www.fastly.com/documentation/reference/api/ngwaf/rules/#ngwafListWorkspaceRules)
- [Time Series Metrics](https://www.fastly.com/documentation/reference/api/ngwaf/timeseries/)
Referenced files: 1
fastly-reference-architectures688 Bytes
--- name: fastly-reference-architectures description: "Curated GitHub repositories demonstrating working reference architectures on the Fastly platform, from single Compute applications to systems combining multiple Fastly products. Use when designing a new solution on Fastly and a worked example of the target pattern would help, or when implementing and a concrete assembled reference would clarify the shape of the code. Not for documentation of how a specific Fastly tool or API works on its own — see the tool-specific skills for that." --- # Fastly reference architectures Example projects running on Fastly (git repositories): [references/examples.md](references/examples.md)
Referenced files: 1
fastly-stats12.6 KB
---
name: fastly-stats
description: "Fastly traffic numbers: cache hit ratio, bandwidth, request counts, status-code and error rates, edge vs origin traffic, real-time requests-per-second, origin latency, per-domain traffic, account usage and billing totals. Owns the `fastly stats` CLI commands and the Historical Stats, Real-Time and Origin/Domain Inspector HTTP APIs. Use for any question that needs a number about how a Fastly service is performing or how much it is being used."
---
# Fastly stats
Prefer the `fastly` CLI. Drop to `curl` only for the seven things the CLI cannot do, listed under
Raw API below.
Requires the `fastly` CLI, `jq`, and network access to Fastly APIs; raw API examples also require `curl`.
Use the user's locally configured authentication.
If authentication is missing, direct the user to a local CLI login or local `FASTLY_API_TOKEN` configuration, never to paste a key in chat.
Keep tokens out of output and avoid shell tracing for authenticated commands.
## Rules that decide whether the answer is right
1. Bytes to GB is decimal SI: `bytes / 1e9`. TB is `/ 1e12`. Never `2^30`. Fastly bills in
decimal units, so a GiB figure is wrong by 7.4% and still reads as a plausible number.
2. For a calendar window, pass explicit UTC boundaries:
`--from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z` returns every day bucket in July. A
bucket is emitted only when the whole period falls inside the window, and a relative window
opens and closes mid-bucket, so `--from "N days ago" --by day` returns N-1 buckets, never N,
and `"1 day ago"` returns none at all. Relative strings are safe at `--by hour`, not at
`--by day`.
3. On the raw API, `from=yesterday` means 12:00:00 UTC, not midnight, and `from=today` means now.
`N days ago` / `N hours ago` are exact offsets. Read back `meta.from` / `meta.to`.
4. `hit_ratio`, `edge_hit_ratio` and `origin_offload` are gauges. Never sum or average them
across buckets. Recompute from the summed counters: `hits / (hits + miss)`.
5. `ts/h` on `rt.fastly.com` covers the last 120 seconds, not an hour, and returns only the seconds
that carried traffic. Divide a rate by 120 there, or by the window you bounded when sampling
with the CLI; never by the sample count or the `recorded` span. Print the window beside the rate.
6. `fastly stats ... --json` emits NDJSON, one object per line, no array. Slurp with `jq -s`
before aggregating. The raw HTTP API returns a normal array in `data`.
7. Stats responses omit services with zero traffic in the window. Enumerate from
`fastly service list --json` and default sums with `add // 0`.
8. Do not read the newest bucket. Historical aggregation keeps growing for a few minutes after
a period closes.
9. On a Compute service the traffic lands in `compute_requests` and `requests` stays 0. Summing
`requests` alone reports zero traffic for a service that is serving fine. Check both.
10. Status codes split the same way. Use `all_status_*`, never bare `status_*` or
`compute_resp_status_*`: `status_5xx` is 0 on Compute, `compute_resp_status_5xx` is absent on
VCL, `all_status_5xx` is right on both. No `all_requests` exists, so denominators still need
`requests + compute_requests`.
## Pick the command
| You need | Command |
| ------------------------------------ | -------------------------------------------------------------------------- |
| One service over a past window | `fastly stats historical -s ID --from T --to T --by day` |
| One field only | `fastly stats historical -s ID --field bandwidth` |
| All services, one row of totals | `fastly stats aggregate --from T --to T --by day` |
| Account usage totals, by region | `fastly stats usage --from T --to T --json` |
| Account usage split per service | `fastly stats usage --by-service --json` |
| Valid region codes | `fastly stats regions` |
| POP codes and shield names | `fastly pops` |
| Is Inspector enabled on this service | `fastly products -s ID` |
| Per-origin metrics, origin latency | `fastly stats origin-inspector -s ID --downsample hour --metric responses` |
| Per-domain metrics | `fastly stats domain-inspector -s ID --downsample hour --group-by domain` |
| Live per-second data | `fastly stats realtime -s ID --json` |
`historical`, `aggregate` and `usage` take `--by minute|hour|day` and `--field`. The two
inspectors take `--downsample` and `--metric` (repeatable) instead, plus `--group-by`,
`--datacenter`, `--limit`, `--cursor`, and `--domain` or `--host`. Mixing the two vocabularies
fails with a usage error. `historical` has no `--datacenter`; `realtime` takes no filters at all,
and `regions` takes no flags whatsoever, not even `--json`. The documented `--metric` cap of 10 is
not enforced; 20 names in one call are accepted and echoed in `meta.metric`. Full flag matrix: the
`fastly-cli` skill's stats reference.
Service-scoped subcommands take `-s` / `--service-id` or `--service-name`, falling back to
`FASTLY_SERVICE_ID` then `fastly.toml`.
## Worked answers
Cache hit ratio over a whole month, recomputed from counters rather than averaged:
```bash
fastly stats historical -s "$SID" --by day \
--from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z --json \
| jq -s '(map(.hits)|add // 0) as $h | (map(.miss)|add // 0) as $m
| {hits:$h, miss:$m, hit_ratio: (if $h+$m > 0 then $h/($h+$m) else null end)}'
```
5xx count and share over a month, correct on both service types:
```bash
fastly stats historical -s "$SID" --by day \
--from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z --json \
| jq -s '{requests: (map((.requests // 0) + (.compute_requests // 0))|add // 0),
status_5xx: (map(.all_status_5xx // 0)|add // 0)}
| . + {pct: (if .requests > 0 then .status_5xx/.requests*100 else null end)}'
```
Bandwidth in GB per service, ranked. Drive the loop from the service list, not from a stats
response, so zero-traffic services are still counted:
```bash
fastly service list --json | jq -r '.[] | "\(.ServiceID)|\(.Name)"' | while IFS='|' read -r id name; do
gb=$(fastly stats historical -s "$id" --by day \
--from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z --json \
| jq -s '([.[].bandwidth] | add // 0) / 1e9')
printf '%.3f\t%s\n' "$gb" "$name"
done | sort -rn
```
Account totals for a month. `fastly stats usage --json` returns one object keyed by region, so sum
the leaves. Dropping `compute_requests` here omits every Compute service from the total:
```bash
fastly stats usage --from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z --json \
| jq '{bandwidth_gb: (([.[].bandwidth]|add)/1e9),
requests: ([.[] | .requests + .compute_requests]|add)}'
```
`billable_units=true` on `GET /stats/usage_by_month` rescales, it does not switch quantity:
`bandwidth` / 1e9, `requests` and `compute_requests` / 10,000, so a `requests` of `1.4452` means
14,452. For one month `/stats/usage`, the per-service `/stats` sum and `/stats/usage_by_month` all
report the same byte total, so a mismatch is an arithmetic bug, not a billing subtlety.
Live request rate. `fastly stats realtime --json` streams one flat object per second,
`{recorded, aggregated, datacenter}`, no `Data` wrapper and no `Timestamp`; those exist only on the
raw `rt.fastly.com` payload. It prints nothing on a quiet service and never exits, so `head -n`
deadlocks. Bound it by wall clock and divide by that bound:
```bash
SECS=20
OUT=$(mktemp)
fastly stats realtime -s "$SID" --json > "$OUT" & P=$!
sleep "$SECS"; kill "$P" 2>/dev/null; wait "$P" 2>/dev/null
jq -s --argjson w "$SECS" \
'{samples: length, window_s: $w,
requests: (map((.aggregated.requests // 0) + (.aggregated.compute_requests // 0))|add // 0)}
| . + {rps: (.requests / $w)}' "$OUT"
rm -f "$OUT"
```
Report the window beside the rate. Do not derive it from `recorded` min/max: only seconds with
traffic are emitted, so on bursty traffic that span is a fraction of what you watched and the rate
comes out several times too high. Ratios and same-window comparisons survive a misjudged window;
extrapolated rates do not.
One-shot alternative, returns immediately even with no data:
`GET rt.fastly.com/v1/channel/{id}/ts/h`, the traffic-bearing seconds of the last 120.
## Raw API
Seven things the CLI cannot do. Everything else has a CLI command above.
| Need | Request |
| --------------------------------------- | -------------------------------------------------------------------------------- |
| Per-POP history on classic stats | `GET api.fastly.com/stats/service/{id}?datacenter=SJC,LHR&by=day` |
| Every service broken out in one call | `GET api.fastly.com/stats?from=T&to=T&by=day` |
| One field across every service | `GET api.fastly.com/stats/field/{field}?from=T&to=T&by=day` |
| Month-to-date billable usage | `GET api.fastly.com/stats/usage_by_month?year=2026&month=07&billable_units=true` |
| POP `region` / `stats_region` fields | `GET api.fastly.com/datacenters` |
| Live per-origin or per-domain data | `GET rt.fastly.com/v1/{origins,domains}/{id}/ts/0` |
| 120 s per-POP snapshot in one call | `GET rt.fastly.com/v1/channel/{id}/ts/h` |
`datacenter=` is absent from the CLI's SDK input type, not just its flags, so no flag combination
reaches per-POP history. The two account-wide rows need `curl` because `stats historical` always
resolves a service ID and errors without one; `fastly stats aggregate` is not a substitute, it sums
every service into one series instead of breaking them out.
Auth is the header `Fastly-Key: <token>`.
Use `fastly auth token` in a shell substitution to supply it directly from the CLI.
```bash
curl -sS -H "Fastly-Key: $(fastly auth token)" \
"https://api.fastly.com/stats/service/$SID?from=2026-07-01T00:00:00Z&to=2026-08-01T00:00:00Z&by=day&datacenter=SJC"
```
Never run `fastly auth token` standalone in an agent session: captured stdout can bypass its terminal-output guard.
Do not echo the token or pass `-v` on an authenticated curl call; both expose it in the transcript.
Endpoint paths, parameters and response shapes: [references/api.md](references/api.md).
Field names and aggregation shape: [references/fields.md](references/fields.md).
Errors, empty data and wrong-scope symptoms: [references/debugging.md](references/debugging.md).
## Scope traps
- `region=` takes `stats_region` values (`usa`, `europe`), not the `region` values from
`/datacenters` (`US-East`, `North-America`). Get the live list from `fastly stats regions`.
- `region=` is ignored on `/stats/usage` and `/stats/usage_by_service`: `meta` echoes it and all
eleven regions come back, byte-identical to the unfiltered response. `fastly stats usage --region`
filters client-side, so the CLI and the raw URL disagree. Filter usage responses yourself.
- Sending `region` and `datacenter` together returns HTTP 200 with the POP filter dropped
silently: `meta` echoes `region` and omits `datacenter` entirely, and the numbers are
whole-region. Never send both, and assert `meta` carries the filter you sent.
- POP codes are uppercase. A lowercase or unknown code fails loudly with `invalid datacenter`.
- Origin and Domain Inspector are paid add-ons. When not enabled the endpoints return HTTP 200,
`"status":"success"` and an empty `data` array, which reads exactly like a service with no
traffic. Check `fastly products -s ID` before concluding there is nothing to see.
- A shield POP's `datacenter` entry carries edge-to-shield traffic, not client traffic. Identify
shields from the `SHIELD` column of `fastly pops` and label them separately.
- When diagnosing rather than reporting, pull the per-POP breakdown. A healthy service-wide
number routinely hides one POP erroring: `datacenter=` on classic stats, `--group-by datacenter`
on the inspectors, the `datacenter` map in real-time.
## Not this skill
Creating or configuring services, backends, VCL or WAF: `fastly-cli` and `fastly`. Raw request
logs: stats are pre-aggregated counters, not log lines. NGWAF security events: `fastly-ngwaf`.
Referenced files: 3
viceroy2.92 KB
--- name: viceroy description: "Runs Fastly Compute WASM applications locally with Viceroy, specifically for Rust and Component Model projects. Use when starting a local Fastly Compute dev server with Viceroy, configuring fastly.toml for local backend overrides and store definitions, running Rust unit tests with cargo-nextest against the Compute runtime, debugging Compute apps locally, adapting core WASM modules to the Component Model, or troubleshooting local Compute testing issues (connection refused, missing backends, store config). For non-Rust Compute work or understanding the Compute API, prefer the fastlike skill instead — its source code is easier to understand as a Fastly Compute API reference." --- # Viceroy — Local Fastly Compute Runtime Viceroy is Fastly's official local testing environment for Compute applications. It emulates the Fastly Compute platform, allowing you to develop and test WASM services locally. **Viceroy documentation**: https://github.com/fastly/Viceroy ## Common Gotchas - **Dictionaries and ConfigStores are both supported** but configured differently in `fastly.toml`. Dictionaries go under `[local_server.dictionaries]` as inline key-value maps or JSON files. ConfigStores go under `[local_server.config_stores]`. - **`fastly.toml` must have a `[local_server]` section.** Without it, Viceroy won't know about your backends, stores, or other local overrides. Every backend your app calls must be listed under `[local_server.backends]`. - **Default port is 7676**, not 5000 (which is Fastlike's default). Access your app at `http://127.0.0.1:7676`. - **Backends should point to local servers** when developing locally. Avoid proxying to remote production origins to prevent accidentally leaking request data. ## Quick Start ```bash # Install Viceroy cargo install --locked viceroy # Build your Compute app fastly compute build # Start local server (default: 127.0.0.1:7676) viceroy -C fastly.toml bin/main.wasm # Or use the Fastly CLI wrapper fastly compute serve ``` ## References | Topic | File | Use when... | | ------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------ | | Serve | [fastly-compute-serve.md](references/fastly-compute-serve.md) | Starting local dev server, profiling, advanced server options | | Config | [fastly-compute-config.md](references/fastly-compute-config.md) | Configuring fastly.toml backends, stores, geolocation, device detection, ACLs | | Test | [fastly-compute-test.md](references/fastly-compute-test.md) | Running Rust unit tests with cargo-nextest, writing tests for Compute services | | Adapt | [fastly-compute-adapt.md](references/fastly-compute-adapt.md) | Converting core WASM modules to Component Model, custom build pipelines |
Referenced files: 4
xvcl11.4 KB
---
name: xvcl
description: "Extends Fastly VCL with loops, functions, constants, macros, conditionals, and includes via XVCL — a VCL transpiler that compiles .xvcl files into standard VCL. Use when writing VCL for Fastly, working with .xvcl files, generating repetitive VCL (multiple backends, routing rules, headers) with loops, defining reusable VCL functions with return values, using compile-time constants instead of magic numbers, or writing any Fastly VCL configuration. XVCL syntax is not in training data so this skill is required. Also applies when writing and testing VCL locally (compile with `uvx xvcl`, test with falco), reducing VCL code duplication, splitting large VCL into modular includes, or doing any VCL development task for Fastly — even without explicitly mentioning XVCL."
---
## Trigger and scope
Trigger on: XVCL, .xvcl files, VCL transpiler, VCL metaprogramming, #const/#for/#def/#inline in VCL context, writing a VCL script, writing VCL and running it locally, or any Fastly VCL writing task.
Do NOT trigger for: debugging existing .vcl files without XVCL, Fastly API/CLI ops, Fastly Compute, or Terraform — even if they mention VCL.
If the user explicitly requests plain VCL, keep `.vcl` files and do not introduce XVCL.
Follow explicitly requested tools and test modes instead of the default examples.
# Writing VCL with XVCL
XVCL is a VCL transpiler that adds metaprogramming to Fastly VCL. Write `.xvcl` files, compile to `.vcl`, then test with Falco or deploy to Fastly. All XVCL constructs are resolved at compile time — zero runtime overhead.
## Quick Start
Compilation requires `uvx`, provided by uv; the local checks use Falco.
```bash
# Compile with uvx
uvx xvcl main.xvcl -o main.vcl
# Lint the output
falco lint main.vcl
# Run locally — this is how you "run" VCL on your machine
falco simulate main.vcl
# Listens on localhost:3124 by default
# Then test with: curl http://localhost:3124/
```
Unless the user explicitly requests another test mode, compile and run `falco simulate` for local execution.
Linting alone does not execute the VCL.
## Minimal Working Example
**Backend naming**: Fastly VCL requires backends to use `F_` prefixed names (e.g., `F_origin`, `F_api`). Never use `backend default` — falco will reject it. Always set `req.backend` explicitly in `vcl_recv`.
```xvcl
#const ORIGIN_HOST = "api.example.com"
#const REGIONS = [("us", "us.example.com"), ("eu", "eu.example.com")]
#for name, host in REGIONS
backend F_{{name}} {
.host = "{{host}}";
.port = "443";
.ssl = true;
}
#endfor
sub vcl_recv {
#FASTLY recv
set req.backend = F_us;
return (lookup);
}
sub vcl_deliver {
#FASTLY deliver
set resp.http.X-Served-By = "edge";
return (deliver);
}
```
## XVCL Directives Summary
Read [xvcl-directives.md](references/xvcl-directives.md) for complete syntax and examples of every directive.
### Constants — `#const`
```xvcl
#const NAME = value // type auto-inferred
#const NAME TYPE = value // explicit type
#const TTL INTEGER = 3600
#const ORIGIN = "origin.example.com"
#const ENABLED BOOL = true
#const DOUBLE_TTL = TTL * 2 // expressions supported
#const BACKENDS = ["web1", "web2"] // lists
#const PAIRS = [("api", 8080), ("web", 80)] // tuples
```
Constants are **compile-time only** — they do NOT become VCL variables. Always use `{{NAME}}` to emit their value. A bare constant name in VCL (e.g., `error 200 GREETING;`) passes through as a literal string, producing invalid VCL. Use `error 200 "{{GREETING}}";` instead.
Use in templates: `"{{TTL}}"`, `{{ORIGIN}}`, `backend F_{{name}} { ... }`
### Template Expressions — `{{ }}`
```xvcl
{{CONST_NAME}} // constant substitution
{{PORT * 2}} // arithmetic
{{hex(255)}} // → "0xff"
{{format(42, '05d')}} // → "00042"
{{len(BACKENDS)}} // list length
{{value if condition else other}} // ternary
```
Built-in functions: `range()`, `len()`, `str()`, `int()`, `hex()`, `format()`, `enumerate()`, `min()`, `max()`, `abs()`
### For Loops — `#for` / `#endfor`
```xvcl
#for i in range(5) // 0..4
#for i in range(2, 8) // 2..7
#for item in LIST_CONST // iterate list
#for name, host in TUPLES // tuple unpacking
#for idx, item in enumerate(LIST) // index + value
```
### Tables with Loops
Use `#for` loops to populate VCL `table` declarations for O(1) lookups, instead of generating inline if-chains.
```xvcl
#const REDIRECTS = [
("/blog", "/articles"),
("/about-us", "/about"),
("/products/old-widget", "/products/widget-v2")
]
// O(1) hash-table lookup — the right pattern for data-driven VCL
table redirects STRING {
#for old_path, new_path in REDIRECTS
"{{old_path}}": "{{new_path}}",
#endfor
}
sub vcl_recv {
if (table.contains(redirects, req.url.path)) {
error 801 table.lookup(redirects, req.url.path);
}
}
```
Prefer populating VCL `table` declarations with `#for` loops over generating inline if-chains. Tables give O(1) hash lookups and are the idiomatic Fastly pattern for any data-driven routing, redirects, or configuration.
### Conditionals — `#if` / `#elif` / `#else` / `#endif`
```xvcl
#if PRODUCTION
set req.http.X-Env = "prod";
#elif STAGING
set req.http.X-Env = "staging";
#else
set req.http.X-Env = "dev";
#endif
```
Supports: boolean constants, comparisons (`==`, `!=`, `<`, `>`), operators (`and`, `or`, `not`).
### Variable Shorthand — `#let`
```xvcl
#let cache_key STRING = req.url.path;
// expands to:
// declare local var.cache_key STRING;
// set var.cache_key = req.url.path;
```
### Functions — `#def` / `#enddef`
```xvcl
// Single return value
#def normalize_path(path STRING) -> STRING
declare local var.result STRING;
set var.result = std.tolower(path);
return var.result;
#enddef
// Tuple return (multiple values)
#def parse_pair(s STRING) -> (STRING, STRING)
declare local var.key STRING;
declare local var.value STRING;
set var.key = regsub(s, ":.*", "");
set var.value = regsub(s, "^[^:]*:", "");
return var.key, var.value;
#enddef
// Call sites
set var.clean = normalize_path(req.url.path);
set var.k, var.v = parse_pair("host:example.com");
```
Functions compile to VCL subroutines with parameters passed via `req.http.X-Func-*` headers.
### Inline Macros — `#inline` / `#endinline`
```xvcl
#inline cache_key(url, host)
digest.hash_md5(url + "|" + host)
#endinline
// Zero-overhead text substitution. Auto-parenthesizes arguments
// containing operators to prevent precedence bugs.
set req.hash += cache_key(req.url, req.http.Host);
```
### Includes — `#include`
```xvcl
#include "includes/backends.xvcl" // relative path
#include <stdlib/security.xvcl> // include path (-I)
```
Include-once semantics. Circular includes are detected and reported.
## Compilation
```bash
# Basic
uvx xvcl input.xvcl -o output.vcl
# With include paths
uvx xvcl main.xvcl -o main.vcl -I ./includes -I ./shared
# Debug mode (shows expansion traces)
uvx xvcl main.xvcl -o main.vcl --debug
# Source maps (adds BEGIN/END INCLUDE markers)
uvx xvcl main.xvcl -o main.vcl --source-maps
```
| Option | Description |
| ---------------- | -------------------------------------------------- |
| `-o, --output` | Output file (default: replace `.xvcl` with `.vcl`) |
| `-I, --include` | Add include search path (repeatable) |
| `--debug` / `-v` | Show expansion traces |
| `--source-maps` | Add source location comments |
| `--error-format` | Error output format: `text` (default) or `json` |
## Common Mistakes
- **Bare constant names in VCL**: `error 200 GREETING;` passes through as a literal string. Use `error 200 "{{GREETING}}";` with template syntax.
- **Generating if-chains instead of tables**: When you have data-driven routing or redirects, always populate a VCL `table` with `#for` — not an inline if-chain. If-chains are O(n); tables are O(1).
- **Forgetting `#FASTLY` macros**: Every VCL subroutine (`vcl_recv`, `vcl_fetch`, `vcl_deliver`, `vcl_error`, `vcl_hit`, `vcl_miss`, `vcl_pass`) needs `#FASTLY recv` (or the appropriate name) at the top.
- **Using `backend default`**: Fastly VCL requires `F_` prefixed backend names. Use `backend F_origin { ... }` and `set req.backend = F_origin;`.
## VCL Gotchas
VCL runtime pitfalls that are easy to get wrong:
- **No modulo operator**: VCL has no `%` operator. For traffic splitting, use `substr()` on a hash digest: `if (substr(digest.hash_sha256(client.ip), 0, 1) ~ "^[0-7]$")` gives ~50%. Or use `randomint(0, 99) < 50`.
- **Vary MUST be set in `vcl_fetch`**: The Vary header controls the cache key. Setting it only in `vcl_deliver` is too late — the object is already cached without Vary dimensions. Always append Vary in `vcl_fetch` (and optionally mirror in `vcl_deliver` for client-visible headers). Never overwrite existing Vary: check and append.
- **`req.url.path` is read-only in falco tests**: In test subroutines, use `set req.url = "/path"` instead of `set req.url.path = "/path"`. The `.path` property is computed from `req.url` and cannot be set directly.
- **`req.request` is deprecated**: Use `req.method` instead. Falco accepts both but `req.method` is the modern form.
- **Cookie parsing**: Use `subfield(req.http.Cookie, "name", ";")` instead of regex. Regex like `Cookie ~ "name=(\w+)"` false-matches cookies with similar prefixes (e.g., `name_v2=X`).
## References
For VCL basics (request lifecycle, return actions, variable types), see the VCL syntax and subroutines references below.
Read the relevant reference file completely before implementing specific features.
| Topic | File | Use when... |
| ------------------- | --------------------------------------------------- | ---------------------------------------------------------- |
| **XVCL Directives** | [xvcl-directives.md](references/xvcl-directives.md) | Writing any XVCL code — complete syntax for all directives |
| VCL Syntax | [vcl-syntax.md](references/vcl-syntax.md) | Working with data types, operators, control flow |
| Subroutines | [subroutines.md](references/subroutines.md) | Understanding request lifecycle, custom subs |
| Headers | [headers.md](references/headers.md) | Manipulating HTTP headers |
| Backends | [backends.md](references/backends.md) | Configuring origins, directors, health checks |
| Caching | [caching.md](references/caching.md) | Setting TTL, grace periods, cache keys |
| Strings | [strings.md](references/strings.md) | String manipulation functions |
| Crypto | [crypto.md](references/crypto.md) | Hashing, HMAC, base64 encoding |
| Tables/ACLs | [tables-acls.md](references/tables-acls.md) | Lookup tables, access control lists |
| Testing VCL | [testing-vcl.md](references/testing-vcl.md) | Writing unit tests, assertions, test helpers |
## Project Structure
```text
vcl/
├── main.xvcl
├── config.xvcl # shared constants
└── includes/
├── backends.xvcl
├── security.xvcl
├── routing.xvcl
└── caching.xvcl
```
Referenced files: 10
Package details
Publisher declarations from the archived package. These are separate from our research and the live service's terms.
- Package license
- MIT
- Package author
- Fastly
- Keywords
- See publisher keywords
Package observed Oct 10, 2026.
Technical details
- First seen
- Oct 9, 2026 · 18:00 UTC
- Last seen
- Oct 10, 2026 · 18:00 UTC
- Collection status
- Collected
plugins_6abe9a4d0ac0819197f4ad9a145abc45
Download plugin data (JSON)Before you connect Fastly Agent Toolkit
How do I connect it?
Open the publisher's marketplace listing to check current availability and follow its connection instructions. This directory does not install plugins. Check the requested access and any account requirements before connecting.
Check marketplace availability ↗
Does it require paid access?
We have not established the pricing or subscription requirements for this plugin. An absent price does not mean free access.
Compare researched pricing and access models →
How can I evaluate it?
Check the declared skills and available files, then try a small task whose result you can verify. Our archived descriptions and instructions establish publisher claims, not tested runtime quality. Review sources and coverage limits.