Get started

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

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