documentation
getting started
Drop GroundShade in behind Caddy, nginx, or HAProxy. Five minutes to a working request path.
updated
GroundShade sits between your TLS terminator and your app. It does
not terminate TLS itself; your fronting proxy keeps doing that and
forwards the JA4 fingerprint as X-JA4.
client → TLS terminator (Caddy / nginx / HAProxy) → groundshade → your origin
Run with Docker Compose
services:
groundshade:
image: codeberg.org/groundshade/groundshade:latest
restart: unless-stopped
environment:
GROUNDSHADE_UPSTREAM_URL: http://your-app:3000
GROUNDSHADE_ADMIN_TOKEN: ${GROUNDSHADE_ADMIN_TOKEN:?set an admin token}
GROUNDSHADE_TRUSTED_PROXIES: 172.18.0.2/32
ports:
- "127.0.0.1:9090:9090"
volumes:
- groundshade-state:/var/lib/groundshade
volumes:
groundshade-state:
Generate the admin token once:
openssl rand -hex 32
Put it in your .env. The token gates /admin/* and /metrics.
Startup refuses to bind admin on a non-loopback address without a
token.
Then point your fronting proxy at groundshade:8080 instead of your
app. Note that, if you are using docker, groundshade and your app need to share the same docker network.
Check the dashboard
The container’s admin port is published to host loopback on
127.0.0.1:9090. Open it in a browser:
http://127.0.0.1:9090/
The login page accepts your admin token and sets a cookie. After login you land on the dashboard.
If you’re not on the host, tunnel over SSH:
ssh -L 9090:127.0.0.1:9090 your-host
Forward JA4 (optional, recommended)
The JA4 arm of the rate signal needs your fronting proxy to forward
the TLS fingerprint as X-JA4. Without it you keep the per-IP/24 arm
and trustless persistence; you lose detection of botnets that share a
TLS library across many IPs.
Stock Caddy has no JA4 placeholder. The build recipe lives at
examples/deploy/caddy-ja4.Dockerfile
and the matching Caddyfile at
examples/deploy/caddy-ja4.Caddyfile.
For nginx and HAProxy see
nginx-ja4.conf
and
haproxy-ja4.cfg.
GroundShade auto-detects JA4. After 100 requests (or 60 seconds), if
no JA4 has arrived, it logs a single WARN and the dashboard’s signals
row reads JA4: not detected. Everything else keeps working.
Trusted proxies
GROUNDSHADE_TRUSTED_PROXIES should list only your fronting proxy’s
pinned IP. Trusted peers get X-Forwarded-For and X-Real-IP honored
and bypass the per-IP connection cap. Pin tight: a /16 would grant
cap bypass to every container on the bridge.
Defaults trust only loopback (127.0.0.1/32, ::1/128).
Where to go next
- Operating GroundShade covers daily workflows, the dashboard, the metrics reference, and the tuning table.
- Architecture explains the request lifecycle and the three defense levels.
- Deployment hardening is the pre-deploy security checklist. Read it before publishing.
- Behind Cloudflare covers the orange-cloud recipe and what gets silenced.
- Building from source builds local Docker images and multi-arch releases.
- FAQ lists the questions operators ask first.