User Guide
Version 1 · New page: hands-on run/configure/onboard walkthrough describing each step, derived from README.md — distinct from the conceptual Overview page; runnable command examples deliberately left in README.md rather than duplicated here
GateKeeper — User Guide
Hands-on walkthrough for running GateKeeper locally and doing the actual admin tasks — onboarding a client, issuing/rotating tokens, assigning packages, adding overrides. For the conceptual "what and why," see Overview; for exact endpoint contracts, see API Reference; for config keys, see Quick Reference. Runnable copy-paste commands (curl, SQL) for every step below live in the repo's README.md — this page walks through what each step does and why.
Prerequisites
- .NET 8 SDK
- MySQL 8.0.16+
- Redis (local
redis-server, Docker, or a managed instance)
Build first
This solution wasn't built in the environment it was scaffolded in, so run this before anything else and fix whatever it turns up:
dotnet restore
dotnet build
Set up the database
Load the schema against a fresh database (mysql -u root -p gatekeeper < docs/gatekeeper-schema.sql). This seeds exactly two customers — CWS (id 401) and WSB (id 402) — with no tokens, packages, or overrides attached yet. See Database for the full schema, and Known Issues for a seed-script FK issue you'll hit if you don't touch the script first.
Configure
Edit GateKeeper.Api/appsettings.json (or override via environment variables / user-secrets — never commit real credentials). Set the MySQL and Redis connection strings under ConnectionStrings. Full config key reference, including the optional cache TTL settings, is in Quick Reference.
Run it
dotnet run --project GateKeeper.Api # http://localhost:5080
dotnet run --project GateKeeper.AdminUI # http://localhost:5190
Try it — issue a token and call the API
CWS/WSB have no tokens until you issue one. The fastest way to get a working token for local testing is a direct INSERT into api_tokens, storing SHA2() of any test string you pick as the token_hash (see api_tokens in Database for the column shape) — the README's "Try it" section has the exact statement to copy.
Once you have a token, call GET /v1/meetings supplying CWS's seeded client_guid (a1b2c3d4-0004-4a11-8b11-000000000401) in the X-Client-Guid header and your test value in the X-Api-Secret header (see API Reference for the endpoint's response shape). CWS has no package/subscription assigned yet, so this resolves an empty entitlement — a 200 with no matching data — until you assign a package (below).
Onboarding an external client, end to end
The normal path — not the direct-SQL shortcut above — for onboarding a brand-new client or rotating CWS/WSB's credentials. Every request is authenticated with a client-guid + secret-key pair, never a single bearer token; a valid secret replayed under the wrong client-guid is rejected.
- Create the customer via
POST /admin/customers(skip if it already exists — CWS/WSB are pre-seeded). Requires theX-Admin-Keyheader. Returns the new customer'sidand generatedclientGuid. - Issue a token via
POST /admin/customers/{id}/tokens. The response'ssecretfield is the plaintext key, returned exactly once — only its SHA-256 hash is ever stored, so losing it means issuing a new one. - Hand the client its two credentials — the
clientGuidfrom step 1 and thesecretfrom step 2. Every request it sends must carry both, as theX-Client-GuidandX-Api-Secretheaders respectively. - Assign entitlements once known, via
POST /admin/customers/{id}/subscriptions(see API Reference for the exact body) or the Admin UI's per-customer page — CWS/WSB start with none. - Rotate or revoke with
POST /admin/tokens/{id}/revoke, then repeat step 2 for a fresh secret. No change to the customer record or itsclientGuidis needed.
To remove a client entirely: DELETE /admin/customers/{id} (or the "Delete" button on the Admin UI's customer list) — cascades to its tokens, subscriptions, and overrides, and invalidates any cached entries in Redis.
Using the Admin API
All /admin/* routes require the X-Admin-Key header — see API Reference for every route's exact request/response shape. Every admin write pushes the resulting cache entry into Redis immediately (not just an invalidation) — a downstream API (TD) reads client/entitlement data straight from Redis and never sends a request through GateKeeper.Api that would otherwise repopulate the cache on a miss, so a plain invalidate-and-wait wouldn't be enough for it.
Using the Admin UI
dotnet run --project GateKeeper.AdminUI
Listens on http://localhost:5190 by default — does the same operations as the /admin HTTP endpoints, but through a browser and calling GateKeeperAdminService in-process instead of over HTTP. Log in with the same key configured under Admin:ApiKey (session is circuit-scoped — a hard page reload logs you out; no cookie/JWT session).
From there:
- Dashboard — customer counts, API hits this month, new-customer trend, subscriptions expiring soon, per-package subscriber counts
- Customers — list, create, delete
- Customer detail — issue/revoke tokens, view usage, edit the (currently unenforced) monthly hit limit, assign/change/cancel package subscriptions, add/revoke overrides
- Packages — CRUD with a repeatable scope editor per rule
- Data Types — CRUD for the
data_typepicklist used when building package rules
Every action writes straight to MySQL and pushes the refreshed cache entry into Redis immediately, same as the HTTP endpoints.
Adding a temporary override
Use this for a trial add-on, a promo, or a temporary block (e.g. a data dispute) that shouldn't touch a customer's actual package — via POST /admin/customers/{id}/overrides or the Admin UI's customer detail page. A revoke override always wins over anything a package grants, and it's independently time-bound (startDate/endDate) from the underlying subscription.
What's intentionally not here yet
- EF Core migrations — the
DbContextis mapped to matchdocs/gatekeeper-schema.sqlby hand; run that script directly rather thandotnet ef database update. /v1/racecards,/v1/speedmaps— same shape as/v1/meetings, not yet duplicated.- Unit tests —
EntitlementResolverandEntitlementScope.Matches/EffectiveEntitlement.IsAllowedare pure functions with no I/O, making them the highest-value place to start.
See Known Issues for discrepancies found against the original design doc.