Quickstart
Five minutes from a clean checkout to a Tiyi instance blocking real OWASP CRS attacks against a real upstream. We'll build the binary, start it in standalone mode, create one site, and watch the WAF do its job.
Prerequisites
- Linux (amd64 or arm64) or macOS for development. Windows is not a target host.
- Go 1.25+ if you're building from source.
- Node 20+ and pnpm 9+ if you're rebuilding the embedded admin UI.
- A spare backend you can put behind Tiyi — anything that speaks HTTP. The examples below use a Python
http.server.
1. Build the binary
From a fresh checkout:
$ make proto # regenerate Connect Go + Protobuf Go from proto/
$ make web # build the Vben Admin frontend into web/dist/
$ make build # compile bin/tiyi with the frontend embedded
Or run all three in one shot:
$ make build-all
The output binary is written to ./bin/tiyi. It's a single statically-linked Go executable; copy it anywhere.
No Go toolchain? Skip to Installation → Prebuilt binaries for the signed-release path.
2. Run in standalone mode
For unprivileged local testing, override the data-plane ports:
$ ./bin/tiyi standalone \
--state-db /tmp/tiyi.db \
--caddy-admin-socket /tmp/tiyi-caddy.sock \
--proxy-http-addr 127.0.0.1:18080 \
--proxy-https-addr 127.0.0.1:18443
Tiyi prints a banner with the API URL, the bootstrap admin password (printed once on first run), and the Caddy admin socket path:
tiyi v3.0.0-rc.1 standalone
api http://127.0.0.1:8080
proxy http://127.0.0.1:18080 https://127.0.0.1:18443
state /tmp/tiyi.db
bootstrap admin password: <random-shown-once>
Open http://127.0.0.1:8080/ in a browser. Log in as admin with the password above. You're now in the Vben Admin dashboard.
The bootstrap password is shown exactly once. Copy it from the terminal before reloading. Lost it? Stop the binary, remove state.db, and start again — Tiyi will rebootstrap.
3. Stand up a backend
In a second terminal, start something Tiyi can proxy to. Anything HTTP works — for the demo we'll use Python:
$ python3 -m http.server 9000
4. Create your first site
You can do this in the UI or via the CLI. The CLI uses the local admin socket and needs no token:
# 1. Create an upstream pool pointing at the demo server
$ ./bin/tiyi upstream create \
--name demo-backend \
--target http://127.0.0.1:9000
# 2. Attach the built-in Standard policy and create the site
$ ./bin/tiyi site create \
--name demo \
--hostname demo.local \
--upstream demo-backend \
--policy "Built-in Standard" \
--tls none
# 3. Enable it — this triggers the live Caddy apply
$ ./bin/tiyi site enable demo
The applier translates the site row into Caddy JSON, hot-reloads Caddy through its admin socket, and emits a signed config-bundle row. If anything fails, the previous config stays active and the site row is restored.
5. Verify the WAF blocks attacks
From a third terminal, send a baseline request and three classic attacks against the proxy port:
# Baseline — should pass
$ curl -sS -o /dev/null -w "%{http_code}\n" -H "Host: demo.local" http://127.0.0.1:18080/
200
# SQL injection
$ curl -sS -o /dev/null -w "%{http_code}\n" -H "Host: demo.local" \
"http://127.0.0.1:18080/?id=1'%20OR%20'1'='1"
403
# XSS
$ curl -sS -o /dev/null -w "%{http_code}\n" -H "Host: demo.local" \
"http://127.0.0.1:18080/?q=<script>alert(1)</script>"
403
# Path traversal
$ curl -sS -o /dev/null -w "%{http_code}\n" -H "Host: demo.local" \
"http://127.0.0.1:18080/../../../../etc/passwd"
403
In the dashboard at http://127.0.0.1:8080/#/logs/security, the three blocked requests appear immediately with the Coraza rule id, severity, attack tag, and the matching CRS message. The dashboard's traffic chart shows the 1:3 baseline-to-block ratio in real time.
What to read next
- Installation — config file, prebuilt binaries, the four operational modes, and production-hardening fields you should set before going live.
- Domain model — what sites, upstreams, certificates, policies, agents, telemetry, and the audit chain actually are.
- CLI reference — every
tiyisubcommand grouped by resource, with flag defaults.