SHELL := /usr/bin/env bash
ENGINE ?= $(shell command -v docker >/dev/null 2>&1 && echo docker || echo podman)
COMPOSE := $(ENGINE) compose
BIN := edgeguard
CONFIG ?= edgeguard.toml
HOST ?= localhost
PORT ?= 8080
ADMIN_PORT ?= 9090
DOCKER_IMAGE ?= eggrd
DOCKER_TAG ?= dev
UPSTREAM ?= http://host.docker.internal:3000
DAYS ?= 90
CERT_OUT ?= ./tls/cert.pem
KEY_OUT ?= ./tls/key.pem
GEN_TARGET ?= _headers
SCENARIO ?= baseline
SCRIPT ?= baseline
COMPARE_TARGET ?= edgeguard
STAGE_DIR ?= /tmp/eggrd-ossync-stage
.DEFAULT_GOAL := help
.PHONY: help \
build build-release build-ner install \
run init init-force doctor hash cert generate version \
fmt fmt-fix lint test test-verbose cli-test integration-test deps test-all bench whitepaper-check \
docker-build docker-build-multiarch docker-run docker-inspect \
monitor-up monitor-down monitor-logs \
example-up example-down example-logs \
loadtest-up loadtest-down loadtest-run loadtest-compare loadtest-observability \
api-health api-ready api-metrics api-csp-report \
docs docs-open config-reference \
ossync-check ossync-scan \
worker-build worker-dev worker-deploy \
ee-run ee-poc-up ee-poc-down ee-demo-up ee-demo-down ee-demo-llm-up ee-demo-llm-down ee-demo-local-up ee-demo-local-down \
llmbench-preflight-up llmbench-preflight-down \
clean clean-compose
help:
@echo "eggrd (EdgeGuard) — developer Makefile. Run targets from this directory (the crate root)."
@echo "Container engine: $(ENGINE) (override with ENGINE=podman or ENGINE=docker)."
@echo "[PRIVATE] targets touch ee/ and llm-bench/ — internal-only workspaces, never part of the published eggrd crate."
@awk 'BEGIN{FS = ":.*?## "} \
/^## === .* ===$$/ {gsub(/^## === | ===$$/,""); printf "\n\033[1m%s\033[0m\n", $$0; next} \
/^[a-zA-Z0-9_.-]+:.*?## / {printf " \033[36m%-24s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
build:
cargo build --bin $(BIN)
build-release:
cargo build --release --bin $(BIN)
build-ner:
cargo build --release --bin $(BIN) --features ner
install: ## Install the edgeguard binary via `cargo install --path .` (FORCE=1 to overwrite)
cargo install --path . --bin $(BIN) $(if $(FORCE),--force)
run: ## Run edgeguard against CONFIG (serve mode); WRAP="cmd" to co-process an app
cargo run --bin $(BIN) -- --config $(CONFIG) $(if $(WRAP),--wrap "$(WRAP)")
init:
cargo run --bin $(BIN) -- init
init-force:
cargo run --bin $(BIN) -- init --force
doctor:
cargo run --bin $(BIN) -- doctor --config $(CONFIG)
hash:
cargo run --bin $(BIN) -- --hash
cert: ## Write a self-signed TLS cert+key (HOSTS="a,b" DAYS=90 CERT_OUT=.. KEY_OUT=.. FORCE=1)
cargo run --bin $(BIN) -- cert $(if $(HOSTS),--host "$(HOSTS)") --days $(DAYS) --cert-out $(CERT_OUT) --key-out $(KEY_OUT) $(if $(FORCE),--force)
generate: ## Emit static-host/edge header config (GEN_TARGET=_headers|vercel|vercel-middleware|netlify-edge, OUT=path)
cargo run --bin $(BIN) -- generate --config $(CONFIG) --target $(GEN_TARGET) $(if $(OUT),--out $(OUT))
version:
cargo run --bin $(BIN) -- --version
fmt:
cargo fmt --all -- --check
fmt-fix:
cargo fmt --all
lint:
cargo clippy --all-targets -- -D warnings
test:
cargo test --all-targets
test-verbose:
cargo test --all-targets -- --nocapture
cli-test:
cargo test --test cli
integration-test:
cargo test --test integration
deps:
bash scripts/check-crypto-provider.sh
test-all: fmt lint test deps
bench:
cargo bench
whitepaper-check:
@missing=0; \
for f in docs/TESTPLAN.md docs/WHITEPAPER.md loadtest/README.md; do \
if test -f $$f; then echo "ok $$f"; else echo "MISSING $$f"; missing=1; fi; \
done; \
test $$missing -eq 0
docker-build:
$(ENGINE) build -t $(DOCKER_IMAGE):$(DOCKER_TAG) .
docker-build-multiarch: ## Buildx multi-arch build (amd64+arm64), Docker only; PUSH=1 to push, else --load
docker buildx build --platform linux/amd64,linux/arm64 -t $(DOCKER_IMAGE):$(DOCKER_TAG) $(if $(PUSH),--push,--load) .
docker-run:
$(ENGINE) run --rm -p $(PORT):8080 --add-host host.docker.internal:host-gateway -e UPSTREAM=$(UPSTREAM) $(DOCKER_IMAGE):$(DOCKER_TAG)
docker-inspect:
$(ENGINE) create --name eggrd-inspect-tmp $(DOCKER_IMAGE):$(DOCKER_TAG)
$(ENGINE) cp eggrd-inspect-tmp:/usr/local/bin/edgeguard ./edgeguard.extracted
$(ENGINE) rm eggrd-inspect-tmp
@echo "extracted to ./edgeguard.extracted"
monitor-up:
$(COMPOSE) -f monitoring/compose.yaml up -d
@echo "Grafana: http://localhost:3000 (anonymous admin)"
@echo "Prometheus: http://localhost:9091"
@echo "EdgeGuard: http://localhost:8080"
monitor-down:
$(COMPOSE) -f monitoring/compose.yaml down -v
monitor-logs:
$(COMPOSE) -f monitoring/compose.yaml logs -f
example-up:
@test -f examples/edgeguard.toml || { echo "missing examples/edgeguard.toml — start from the crate's annotated config: cp edgeguard.toml examples/edgeguard.toml (then set a real auth.users hash)"; exit 1; }
@test -f examples/secrets/jwt_secret.txt || { echo "missing examples/secrets/jwt_secret.txt — create one: mkdir -p examples/secrets && openssl rand -hex 32 > examples/secrets/jwt_secret.txt"; exit 1; }
$(COMPOSE) -f examples/docker-compose.yml up -d
example-down:
$(COMPOSE) -f examples/docker-compose.yml down -v
example-logs:
$(COMPOSE) -f examples/docker-compose.yml logs -f
loadtest-up: ## Bring up the load-test stack (SCENARIO=baseline|auth-apikey|ratelimit-local|ratelimit-redis|waf-block|full)
cd loadtest && EG_SCENARIO=$(SCENARIO) $(COMPOSE) up -d --build edgeguard upstream redis prometheus
loadtest-down:
cd loadtest && $(COMPOSE) down -v
loadtest-run: ## Run one k6 scenario end to end (SCENARIO=baseline SCRIPT=baseline DIRECT=1); writes loadtest/results/
cd loadtest && bash run.sh $(SCENARIO) $(SCRIPT) $(if $(DIRECT),--direct)
loadtest-compare: ## Head-to-head vs a competitor proxy (COMPARE_TARGET=edgeguard|nginx|haproxy|caddy|traefik|envoy SCRIPT=baseline)
cd loadtest && COMPOSE_BIN=$(ENGINE) bash run-compare.sh $(COMPARE_TARGET) $(SCRIPT)
loadtest-observability:
cd loadtest && $(COMPOSE) --profile observability up -d grafana
api-health:
curl -fsS -i http://$(HOST):$(PORT)/__edgeguard/health
api-ready: ## GET /__edgeguard/ready — 200 only once the upstream is reachable (ADMIN=1 for ADMIN_PORT)
curl -fsS -i http://$(HOST):$(if $(ADMIN),$(ADMIN_PORT),$(PORT))/__edgeguard/ready
api-metrics: ## GET /__edgeguard/metrics — Prometheus exposition (ADMIN=1 for ADMIN_PORT)
curl -fsS http://$(HOST):$(if $(ADMIN),$(ADMIN_PORT),$(PORT))/__edgeguard/metrics
api-csp-report:
curl -fsS -i -X POST http://$(HOST):$(PORT)/__edgeguard/csp-report \
-H 'Content-Type: application/csp-report' \
-d '{"csp-report":{"document-uri":"https://example.com","violated-directive":"script-src"}}'
docs:
cargo doc --no-deps
docs-open:
cargo doc --no-deps --open
config-reference:
python3 scripts/build-config-reference.py
ossync-check:
bash scripts/ossync-check.sh $(STAGE_DIR)
ossync-scan:
bash scripts/ossync-scan.sh
worker-build:
cd worker && worker-build --release
worker-dev:
cd worker && wrangler dev
worker-deploy:
cd worker && wrangler deploy
ee-run:
$(MAKE) -C ee run
ee-poc-up:
$(MAKE) -C ee poc-up ENGINE=$(ENGINE)
ee-poc-down:
$(MAKE) -C ee poc-down ENGINE=$(ENGINE)
ee-demo-up:
$(MAKE) -C ee demo-up ENGINE=$(ENGINE)
ee-demo-down:
$(MAKE) -C ee demo-down ENGINE=$(ENGINE)
ee-demo-llm-up:
$(MAKE) -C ee demo-llm-up ENGINE=$(ENGINE)
ee-demo-llm-down:
$(MAKE) -C ee demo-llm-down ENGINE=$(ENGINE)
ee-demo-local-up:
$(MAKE) -C ee demo-local-up ENGINE=$(ENGINE)
ee-demo-local-down:
$(MAKE) -C ee demo-local-down ENGINE=$(ENGINE)
llmbench-preflight-up:
$(COMPOSE) -f llm-bench/compose.preflight.yaml up --build -d
llmbench-preflight-down:
$(COMPOSE) -f llm-bench/compose.preflight.yaml down -v
clean:
cargo clean
clean-compose:
-$(COMPOSE) -f monitoring/compose.yaml down -v
-$(COMPOSE) -f examples/docker-compose.yml down -v
-cd loadtest && $(COMPOSE) down -v
-$(COMPOSE) -f llm-bench/compose.preflight.yaml down -v
-$(MAKE) -C ee poc-down ENGINE=$(ENGINE)
-$(MAKE) -C ee demo-down ENGINE=$(ENGINE)
-$(MAKE) -C ee demo-llm-down ENGINE=$(ENGINE)
-$(MAKE) -C ee demo-local-down ENGINE=$(ENGINE)