Frizzle is a minimalistic event bus designed to receive telemetry data and publish it to Kafka topics. The current development phase focuses on proving out JWT verification for secure data ingestion.
- Provide a consistent development environment
- Support local Kafka integration
- Enable future WebSocket to Kafka functionality
- Maintain simplicity and scalability
.docker/
├── Dockerfile
├── docker-compose.yaml
└── .env
- Base image: Debian 12
- Includes:
- Zsh with Oh-My-Zsh
- Go 1.21+
- Development tools (golangci-lint, delve)
- Kafka client libraries
- Configured working directory
- Entrypoint setup
Services (profiles):
frizzle(dev): Devcontainer for iterative development.app(ci): Production-like server image for CI/e2e.zookeeper: Zookeeper service.kafka: Kafka service (depends on Zookeeper).keycloak: Keycloak IdP (dev mode; importsfrizzlerealm).postgres: Postgres for Keycloak persistence.zoonavigator(dev): ZooKeeper GUI (dev convenience).
- Zookeeper
- Kafka
- Postgres
- Keycloak
- Frizzle app
- Kafka configuration (broker, topic)
- Application settings
- Development flags
- Pre-installed Kafka client
- Environment variables for Kafka configuration
- Network setup for Kafka communication
- Volume mounts for development and testing
Refactor in progress to internal layering (auth, websocket, kafka, config, server). Basic endpoints implemented: /healthz, /readyz, /ws with bounded backpressure and Kafka publish interface.
- Start stack (dev profile):
make up - Create topic (optional if auto-create):
make setup-topics - Health:
curl http://localhost:8800/healthz - Readiness:
curl http://localhost:8800/readyz - Stop stack:
make down
To run the production-like app container (CI profile):
- Start stack:
PROFILE=ci make up - Stop stack:
PROFILE=ci make down - Build:
make build - Run server locally:
CONFIG_PATH=configs/config.dev.yaml go run ./cmd/server - Setup Kafka topic:
make setup-topics - Health:
GET http://localhost:8800/healthz - Readiness:
GET http://localhost:8800/readyz - WebSocket:
GET ws://localhost:8800/ws(Authorization: Bearer )
When running via Compose (deployments/docker-compose.yml), the app is configured to validate JWTs against Keycloak at http://keycloak:8080/realms/frizzle using JWKS (/protocol/openid-connect/certs).
- Unit:
make test- Runs
go test ./... -race -coverwith unit tests only.
- Runs
- Integration (E2E):
make itest- Brings up the CI profile from
deployments/docker-compose.yml(ZK → Kafka → Postgres → Keycloak → app), ensures the Kafka topic, then runs tests under./itestwith-tags=integration. - The E2E test exercises: Keycloak ROPC token → WS auth → send → Kafka produce → Kafka consume → WS forward.
- Brings up the CI profile from
- Manual E2E (advanced):
PROFILE=ci make upthengo test -race -tags=integration ./itest -v; tear down withPROFILE=ci make down.
- Lint/Vet/Format:
make fmt,make vet,make lint,make tidy.
Policy (summary; see AGENTS.md for full requirements):
- Coverage targets: global ≥ 80%, critical paths ≥ 90% (auth, WS pumps, Kafka I/O).
- Devcontainer is canonical; tests should pass locally and in CI.
- Zookeeper health:
docker logs zookeeper --tail=80and checksrvrfour-letter command is enabled; the compose healthcheck usesecho srvr | nc -w 2 localhost 2181 | grep -q Mode. - Kafka listeners: host uses
localhost:29092, containers usekafka:9092. If Kafka fails to start with dual listeners error, ensure ports and envs match the compose file. - Kafka topic: ensure the topic exists before testing.
go run ./cmd/setup -brokers localhost:29092 -topic frizzle -partitions 1 -replication 1 -timeout 30. - Keycloak availability: root should respond 200/3xx at
http://localhost:8080/. JWKS athttp://localhost:8080/realms/frizzle/protocol/openid-connect/certs. - JWKS warmup failed: confirm
appis on thefrizzle-netnetwork and can reachkeycloak:8080. Reset volumes withdocker compose -f deployments/docker-compose.yml down -vto re-import realm. - Token audience/issuer: tokens from localhost have
iss=http://localhost:8080/realms/frizzleandaud=frizzle-app. App uses JWKS fromkeycloakand checks issuerlocalhost. - WS handshake 401: verify Authorization header; ensure Keycloak client has Direct Access Grants enabled (realm import sets this).
- Architecture mismatch:
exec format errorindicates wrong image arch. Rebuildappwith--no-cache --pulland ensure composeplatformand build args align. - Quick diagnostics:
- App:
docker logs app --tail=80, readiness:curl -v http://localhost:8800/readyz. - Kafka:
docker logs kafka --tail=80, list topics via container:docker exec -it kafka kafka-topics --bootstrap-server localhost:9092 --list. - Keycloak:
docker logs keycloak --tail=80.
- App:
- Static:
gofmt -s,go vet,staticcheck, andgo mod tidymust be clean (no diff). - Unit:
go test ./... -race -coverwith coverage gates (global ≥ 80%, critical paths ≥ 90%). - Integration: Compose boots; WS ↔ Kafka round-trip passes using real Keycloak JWKS.
- Security: No plaintext secrets committed; JWT/JWKS verification enforced; dependency scans if configured.
- Docs: README quickstart and docs/api.md match public endpoints and message schemas.
- Devcontainer: postCreate completes on the CI image.
- Minimal config works with just Kafka and Auth basics in YAML (
configs/config.*.yaml). - JWKS and HTTP tuning have sane defaults; override via env if needed:
AUTH_JWKS_REFRESH_INTERVAL_SECS(default60)AUTH_JWKS_CACHE_TTL_SECS(default300)AUTH_HTTP_TIMEOUT_SECS(default5)AUTH_CLOCK_SKEW_SECS(default0, sample uses5)AUTH_ALLOWED_ALGS(defaultRS256)
Example run with overrides:
AUTH_JWKS_REFRESH_INTERVAL_SECS=30 AUTH_HTTP_TIMEOUT_SECS=3 \
CONFIG_PATH=configs/config.dev.yaml go run ./cmd/server
The following users are generated for the Realm:
- admin-user:
- Username:
admin-user - Email:
admin@frizzle.example - Password:
admin123 - Role:
admin
- Username:
- employee-user:
- Username:
employee-user - Email:
employee@frizzle.example - Password:
employee123 - Role:
employee
- Username:
- customer-user:
- Username:
customer-user - Email:
customer@frizzle.example - Password:
customer123 - Role:
customer
- Username: