Skip to content

Repository files navigation

EngagePulse

Small Go backend for casino / sports brands.

Other systems send player actions (deposit, bet). EngagePulse updates that player’s VIP status, welcome bonus, and warning flags, and may add a reward balance of 100 once.

Live console

Try it: engagepulse-topaz.vercel.app — open Pulse Arena and click Ignite live demo.

Ignite needs the Go API online (Postgres + a Kafka-compatible broker). The public demo API is already hosted: https://api-production-2ef9b.up.railway.app (Postgres + Apache Kafka on Railway; local Compose still uses Redpanda). If the API is down, the console shows a clear offline banner and links to the Arena demo mp4.

Quick start

Use the app on your machine. Start only Postgres + Redpanda in Docker. The app listens on 127.0.0.1 by default (avoids localhost → IPv6 confusion).

cp .env.example .env
docker compose up -d postgres redpanda
make migrate
make run

In another terminal:

make demo

make demo explains each step, sends traffic for two brands, then prints both player results.

If the Compose app image fails to build (module download blocked in Docker), that is fine. Local make run is the normal path.

Demo video

Latest release v0.3.0 — web console (Pulse Arena):

Clip What you see
Arena guided demo (mp4) Ignite → Acme VIP + Nova velocity payoff
Architecture live (mp4) Event-path diagram + live metrics poll
Full tour (mp4) Arena run then Architecture page
Arena preview (gif) Short silent preview

Posters: arena · architecture

CLI demo (still on v0.2.1):

Run the CLI demo yourself:

docker compose up -d postgres redpanda
make migrate
make run
# other terminal:
make demo

What the demo brands show

Brand Meant to show Look for
acme-casino Welcome bonus + VIP going up welcome_bonus, VIP silver or gold, balance: 100
nova-sports Betting too fast welcome_bonus, integrity_flag: velocity, balance: 100

make replay is the contract for those two brands: it applies testdata/replay/ through the worker and fails if the player JSON drifts. make replay-kafka applies the same packs through signed ingest, outbox, Kafka, and the consumer, and still credits once on a second run. make demo is the guided tour of the same outcomes against a running API.

make rulepatch prints an advisory diff for a request like raise velocity threshold and re-runs replay before/after. It does not write internal/rules. Copy the diff yourself if you want it. CI uses the mock proposer; PROPOSER=openai without OPENAI_API_KEY errors instead of falling back to mock.

make shadow scores recorded replay bursts against the velocity rule and prints a disagreement board (pack, rule, model, agree). Mock is the default: flag when the pack has at least VelocityBetLimit bets. The 1-minute rule can disagree (see testdata/replay/sparse-bets/). Shadow does not credit or set VIP. CI uses the mock scorer; SHADOW_SCORER=openai without OPENAI_API_KEY errors instead of falling back to mock.

Plain map of ideas → code

Idea Where
Words like VIP / welcome / velocity docs/concepts.md
How messages move through the app docs/architecture.md
Two brands, separate data migrations/, internal/store
Signed event intake internal/ingest, POST /v1/events
Save first, send to stream later internal/store/outbox.go, internal/outbox
Message stream + failed-message topic internal/kafka
Worker mark + state + credit in one DB tx internal/worker, internal/store, internal/ledger
The three rules internal/rules
Advisory rule patch cmd/rulepatch, internal/rulepatch
Shadow flag vs velocity cmd/shadow, internal/shadow
Bonus credit without double-pay internal/ledger
Read player over HTTP / gRPC internal/api/httpapi, internal/api/grpcapi
HMAC / API-key / tenant isolation tests internal/api/httpapi/security_test.go

HTTP / gRPC surfaces

What Path
Send event POST /v1/events (header X-Signature, optional X-Key-Id; schema_version omitted or 1; response X-Trace-Id)
Rotate HMAC POST /v1/hmac/rotate (header X-API-Key; overlap for that brand only)
Read player GET /v1/players/{id} (header X-API-Key)
List dead letters GET /v1/dlq (header X-API-Key)
Tool call POST /v1/tools/{name} (header X-API-Key; ingest also X-Signature)
Read player (gRPC) GetPlayer (metadata x-api-key)
Live GET /livez (process up; /healthz is the same)
Ready GET /readyz (Postgres ping + Kafka dial; 503 if either is down)
Counters GET /metrics

Make targets

Command What it does
make up Start Postgres + Redpanda
make migrate Create / update tables (+ seed brands)
make loadgen Send signed events
make soak Local only: unique first deposits plus duplicate deliveries; credits Δ equals unique ids, each balance 100. Do not point at Railway.
make demo Guided end-to-end run
make replay Replay pack vs expected player JSON (needs DATABASE_URL)
make replay-kafka Same packs through ingest → outbox → Kafka → consumer (needs Kafka)
make rulepatch Print an advisory rule diff and replay traces (not applied)
make shadow Print shadow vs velocity disagreement board (no credits)
make test Tests (set DATABASE_URL for DB tests)

Web console

Creative dual-tenant arena UI in web/ — Next.js BFF over the live Go API.

Hosted: engagepulse-topaz.vercel.app (points at the Railway API above).

Local UI (API already up via make run):

cd web && npm install && npm run dev

Open http://localhost:3000 and click Ignite live demo.

See web/README.md for pages, env vars, and hosting notes.

CI runs gofmt, go vet, golangci-lint, go test ./... -p 1, make replay, and make replay-kafka on main / develop.

Docs

About

Multi-tenant player engagement service

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages