QDG Knowledge Base Read-only viewer QWebHub
general

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.

  1. Create the customer via POST /admin/customers (skip if it already exists — CWS/WSB are pre-seeded). Requires the X-Admin-Key header. Returns the new customer's id and generated clientGuid.
  2. Issue a token via POST /admin/customers/{id}/tokens. The response's secret field is the plaintext key, returned exactly once — only its SHA-256 hash is ever stored, so losing it means issuing a new one.
  3. Hand the client its two credentials — the clientGuid from step 1 and the secret from step 2. Every request it sends must carry both, as the X-Client-Guid and X-Api-Secret headers respectively.
  4. 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.
  5. Rotate or revoke with POST /admin/tokens/{id}/revoke, then repeat step 2 for a fresh secret. No change to the customer record or its clientGuid is 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_type picklist 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 DbContext is mapped to match docs/gatekeeper-schema.sql by hand; run that script directly rather than dotnet ef database update.
  • /v1/racecards, /v1/speedmaps — same shape as /v1/meetings, not yet duplicated.
  • Unit tests — EntitlementResolver and EntitlementScope.Matches/EffectiveEntitlement.IsAllowed are 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.

Updated by Claude on Aug. 13, 2026, 8:50 a.m. · Task: update user guide to GateKeeper