A self-hosted dashboard for searching, filtering, and visualizing request-level Fastly logs streamed to Fastly Object Storage.
Fastly's historical stats give you aggregates. When you need to drill into individual requests — by IP, URL, status, WAF signal, or any field you log — Fastly's real-time log streaming makes the raw data available, but you still need somewhere to put it and something to query it with. This project fills that gap using only Fastly products. Costs are limited to Fastly Object Storage class operations and storage — no third-party logging vendor required.
You'll need:
- A Fastly account with permission to create services and Object Storage buckets
- Object Storage enabled on the account — it's a separately activated product, not on by default
- At least one VCL service to stream logs from
- Docker (recommended) — or Python 3.10+ and Node.js 24+ for a manual install
- Optional: a Fastly API token with the Billing permission to power the Usage & Cost page
- Optional:
falcoto validate VCL during provisioning (highly recommended; the app degrades gracefully without it) - Optional: Rust 1.90+ with the
wasm32-wasip1target (rustup target add wasm32-wasip1) — only needed if you plan to rebuild the Session Scoring Compute Wasm scorer from source
docker compose up --buildRequires Docker Compose v2 (the
docker composesubcommand). The standalonedocker-composev1 binary reached end-of-life in 2023 and isn't shipped with current Docker installs.
Open http://localhost and follow the provisioning wizard. The wizard creates your Object Storage bucket, access keys, a CDN-fronting service, and the logging endpoint on the VCL service you select.
The stack is fronted by Caddy on port 80 (a single ingress, mirroring production). A friendlier alias http://fastly.localhost works with no setup — browsers resolve
*.localhostto127.0.0.1. For a custom name like http://fastly.analytics, add127.0.0.1 fastly.analyticsto your hosts file.
For manual install (no Docker), see Manual Installation. The non-Docker run.sh path still serves the app directly on http://localhost:3000.
The admin runs the provisioning wizard to set up a Fastly Object Storage bucket, deploy a CDN-fronting service for cheap log reads, and attach a structured JSON logging endpoint to a chosen VCL service. Once that's in place, the app continuously ingests new .gz log files from the bucket into an Apache Iceberg table.
Once the data lake is healthy, you can collaborate with teammates using two different approaches depending on your hosting setup and security needs:
| Model | Path A: Independent Copy | Path B: Live Shared Server |
|---|---|---|
| Analyst Setup | Runs their own local copy of the app | Standard web browser only |
| Admin Setup | Offline-friendly. Your laptop/server can go offline. | Always-on. Your machine hosts the active web server. |
| Data Source | Analyst queries FOS bucket directly | Analyst queries the Admin's database over HTTP |
| Credential Sharing | Shares read-only FOS bucket keys | Zero keys shared. Admin handles all credentials. |
| Best For | Long-term analysts, laptop-only admins. | Quick screen-shares & non-technical associates. |
The analyst runs their own independent copy of the app on their laptop or server. They use a read-only credential package to sync and query the Fastly Object Storage bucket directly.
- Admin: Click Invite Analyst in your dashboard. The app packages your FOS bucket name, region, and a set of read-only access keys into a secure JSON string. Send this JSON securely to your teammate.
- Analyst: Start your own copy of the app (e.g., using
docker compose), select Join Service on the setup screen, and paste the JSON config. - Your teammate's app automatically configures itself in
read_onlymode and syncs directly from the bucket. Note: Because only the Admin's machine runs the active raw log ingestion pipeline, if the admin is offline, no new logs will be written to the database (though the analyst can still query all historical data). Once the admin is back online, the analyst's dashboard will automatically sync the newly committed logs.
You run the application as a central web-accessible server on a dedicated VM (or a laptop reachable at its own hostname / IP). Your associates connect using a standard web browser and enter a passcode.
- Admin: Click Share Dashboard in your dashboard. The sharing manager prompts for your server's public URL — a custom domain or IP that the analyst can reach over HTTPS. (The previous SSH-reverse-tunnel mode via
localhost.runwas removed in v2.0; production deployments use direct-mode against a real public endpoint.) - Admin: Mint an analyst invitation in the sharing manager by specifying their name, an optional IP allowlist, and a passcode. Give them the public URL and passcode.
- Analyst: Open the shared link in a standard browser, accept the Terms of Service, enter the passcode, and view the live read-only dashboard. All database queries are executed securely on your host server. You can revoke access or Sever All Access instantly.
- Apache Iceberg data lake — ACID-compliant log storage in FOS, safe for concurrent readers and writers
- Automated provisioning — wizard creates the bucket, access keys, CDN-fronting service, and logging endpoint
- CDN-accelerated reads — every FOS read goes through a Fastly service to minimize egress and maximize caching
- Crash-safe ingestion — buffered locally, atomically committed; interrupted imports never corrupt the table
- Schema evolution — new and missing JSON fields handled gracefully; corrupt lines isolated and surfaced
- Log sampling — optionally log a random percentage of requests to manage cost on high-traffic services
- Multi-source support — analyze logs from multiple services side by side
- Interactive dashboards — traffic over time, global request map, top-N aggregations, raw log viewer with click-to-filter
- Insights — automated anomaly detection (error spikes, regional surges, new-region traffic, botnet IP-spread, WAF signal changes, cache regressions, latency)
- Usage & Cost — live storage breakdown, FOS operation counts, period totals, interactive cost estimator
- Log field configuration — built-in field groups (HTTP, network, geo, TLS, NGWAF) plus custom VCL expressions
- Alerts — threshold-based, webhook-delivered
- Live dashboard sharing — direct-mode via your own hostname or IP, with per-analyst passcode invites, IP allowlisting, and instant revoke
- Session scoring — edge-computed 0-100 risk score per request combining cookie/timing signals with a PageRank transition matrix, with live threshold enforcement, audit logging, key rotation, and matrix version history. See the runbook and feature reference
See docs/features.md for the full feature reference.
If you'd rather not use Docker:
# Recommended: install uv if you don't have it
curl -LsSf https://astral.sh/uv/install.sh | sh
# Backend dependencies
uv sync
# Frontend dependencies
cd frontend && npm ci && cd ..
# Start the app (production mode)
./run.sh
# ...or development mode with hot reload
./run.sh --devThen open http://localhost:3000.
If you have a Fastly API token with Engineer or Superuser permissions, you can provision from the command line:
# Guided
uv run python -m backend.provision.cli provision
# Non-interactive
uv run python -m backend.provision.cli provision --token <YOUR_TOKEN> --service-id <ID> --yes
# Teardown
uv run python -m backend.provision.cli teardown --service-id <ID> --yesCommon flags: --region us-east-1, --bucket <name>, --prefix <path>, --sample-rate 100, --period "1 minute", --cdn-prefix <subdomain>, --remove-data (on teardown), -y / --yes (accept defaults). Provisioning auto-rolls back on failure to leave your Fastly account clean.
If you already have a bucket, drop a JSON config file in configs/ instead (create the directory if your checkout doesn't have one yet). See config.example.json at the repo root for the schema (fos_endpoint, fos_bucket, fos_access_key_id, fos_secret_access_key, fos_region, optional cdn_url + cdn_secret, optional fastly_api_key).
All app-level configuration is via environment variables. Copy .env.example to .env and uncomment any value you want to override. The app starts with sensible defaults if you skip this entirely.
Per-service configuration (credentials, log field selection, custom fields, sync intervals) lives in configs/{service_id}.json and is managed via the UI or the provisioning CLI.
The Fields button on each service card opens the log field configurator — pick which JSON fields to log and the app generates the matching VCL log format (and any required Edge Data Capture snippets). See docs/features.md for the field-group reference.
To route FOS reads through a Fastly CDN service (for free egress and edge caching) the wizard creates this for you. If you're configuring manually:
- Create a Fastly Delivery service with your FOS bucket as the backend origin
- Configure the VCL to handle AWS4 signing on the request to FOS and shared-secret query-param authentication (
?key=…) from the backend. The provisioning wizard generates this VCL automatically — if you've already enabled a service through the wizard, you can copy the active VCL from the Fastly UI or API as a starting point for a hand-managed service. - Set
cdn_urlandcdn_secretin your service config
make install # uv sync + frontend npm ci
make ci # full gate: lint + format + typecheck + tests (back + front) + security scans
make dev # backend + frontend with hot reload (./run.sh --dev)
make test # backend pytest only
make test-frontend # frontend vitest only
make typecheck # mypy backend/
make lint-fix # ruff check --fix
make format # ruff formatPre-commit hooks:
make install-hooks # runs uv run pre-commit install onceAfter this, every git commit runs ruff (lint + format), mypy, and standard file checks.
The Next.js frontend uses a typed API client generated from the FastAPI OpenAPI schema. run.sh and production builds regenerate types on startup; after manual backend model changes, regenerate manually:
cd frontend && npm run gen:typesSee docs/ARCHITECTURE.md for the system design, and AGENTS.md for the deeper contributor/agent notes — canonical patterns and the (extensive) list of known traps.
The frontend listens on 3000 and the backend on 8000 by default. If another process holds one, find it with lsof -i :3000 or lsof -i :8000, then either stop it or run on different ports: set FRONTEND_PORT / BACKEND_PORT (and API_PROXY_URL to match the backend) in .env, or pass --frontend-port / --backend-port to run.sh.
Usually a protocol mismatch from a security setting forcing HTTPS on a port that only speaks HTTP. Hit http://localhost:3000 (not 127.0.0.1), and make sure the frontend is going through the Next.js proxy rather than calling the backend port directly.
The dev script in frontend/package.json binds with -H 127.0.0.1 to bypass network interface enumeration. Don't drop that flag in any custom dev setup.
- Check the time range overlaps with your log data.
- Check the browser console for failed
POSTs (often the ALPN issue above). - A newly provisioned service can take a few minutes for the first ingestion + commit. Check the Log Management page for sync status.
See CONTRIBUTING.md.
See SECURITY.md. Vulnerability reports should go through Fastly's security issue reporting process — please don't file public GitHub issues for security problems.
Apache License 2.0. Copyright 2026 Fastly, Inc.


