Getting Started
Project Structure
How the Quayel Gateway repository is organized and where the gateway keeps its files at runtime.
Project Structure
Repository layout
quayel-gateway/
├── setup.sh # bootstrap: datasets + cargo packages
├── Dockerfile # multi-stage image build (rust:1-bookworm → debian-slim)
├── docker-compose.yml # SSL-ready compose file (ports 80/443, cert volume)
├── Cargo.toml # crate dependencies
├── config.json # live config (hot-reloaded on change)
├── config.demo.json # full-featured demo config
├── config.all-rules.json # every feature enabled — used by the test suite
├── src/
│ ├── main.rs # entry point
│ ├── gateway/ # Pingora proxy pipeline
│ ├── plugins/ # ip_intel, firewall, rate_limit, cache, load_balancer
│ ├── auto_ssl/ # ACME / Let's Encrypt automation
│ ├── condition/ # request matching engine
│ ├── config/ # JSON config loader + hot reload
│ ├── data/ # embedded MMDB + CIDR datasets (setup.sh)
│ └── utils/ # embedded_data, CIDR sets, helpers
├── docs/ # full documentation (+ ai-guide.md, config.schema.json)
├── skills/ # AI skill package (skills/quayel-gateway/SKILL.md)
└── scripts/ # benchmarks, live test suite, nginx/APISIX mirrors
| Path | What it is for |
|---|---|
setup.sh | One-time bootstrap: downloads embedded datasets and installs the Rust toolchain + crates |
src/gateway/ | The Pingora proxy pipeline: request context, upstream selection, response handling |
src/plugins/ | Plugin implementations — IP intel, firewall, rate limiting, cache, load balancer |
src/auto_ssl/ | On-demand ACME / Let's Encrypt certificate automation |
src/condition/ | The matching engine shared by firewall, rate limit, cache, and LB rules |
src/config/ | Config schema (schema.rs — authoritative) and loader (loader.rs — startup validation) |
src/data/ | MMDB databases + CIDR lists, compiled into the binary via include_str! |
docs/ | Markdown docs, ai-guide.md, and config.schema.json |
skills/ | The portable AI skill package (install it) |
scripts/ | Benchmarks and test-all-rules.sh (the 57-assertion live suite) |
What setup.sh does
- Downloads the embedded datasets into
src/data/(latest versions):File Content dbip-city-lite.mmdbDB-IP City Lite — country, region, city, lat/lon dbip-asn-lite.mmdbDB-IP ASN Lite — ASN + organization dbip-country.mmdbDB-IP Country Lite — country fallback cloudflare.txtCloudflare edge ranges (IPv4 + IPv6) hosting-asns.txtASNs known for hosting / abuse proxy.txtFireHOL anonymous proxy ranges tor.txtTor Project exit nodes vpn.txtX4BNet VPN provider ranges provider-asns.txt(versioned in repo, not downloaded) — provider ASN registry: ASN|Label(e.g.47583|Hostinger) - Installs cargo packages — Rust toolchain (
rustup) and system build tools if missing, then fetches every crate dependency fromCargo.toml/Cargo.lock.
Flags: ./setup.sh --data-only · ./setup.sh --asns-only · ./setup.sh --cargo-only
Datacenter/hosting detection is ASN-based: the ASN MMDB resolves any client IP to its ASN, and provider-asns.txt + hosting-asns.txt classify the network. Adding a provider is one line in src/data/provider-asns.txt, then rebuild.
Runtime layout (Docker image)
| Path | Purpose |
|---|---|
/usr/local/bin/quayel-gateway | The gateway binary (entrypoint) |
/etc/quayel-gateway/config.json | The config file (image WORKDIR is /etc/quayel-gateway) |
/var/lib/quayel/certs | Auto-SSL certificate storage — persist this as a volume |
/var/log/quayel/access.jsonl | Structured JSONL access log |
| Port | Purpose |
|---|---|
80 | HTTP — ACME HTTP-01 challenges + plain HTTP (map to container 8080) |
443 | HTTPS — SNI cert resolver / Auto-SSL (map to container 8443) |
8080 / 8443 | The demo config's listeners inside the container |
Environment variables
| Variable | Default | Description |
|---|---|---|
RUST_LOG | info | Log level (debug, info, warn, error) |
QUAYEL_IP_INTEL_CACHE_SIZE | 65536 | LRU cache size for IP lookups |
Config and hot reload
The gateway watches its config file and reloads it on change:
- A valid save is applied without a restart (log line:
Config hot-reloaded successfully). - An invalid save is rejected and the previous config keeps serving.
- Edit atomically (write to a temp file, then rename) so the watcher never sees a half-written file.
Bump
config_version on every config change — it is stamped into every log line so you can tell exactly which config handled a request. See Versioning.