caputchin
All docs
View raw .md

Dashboard

The customer's account UI — one of four management modalities, alongside OpenAPI, MCP, and Terraform. All four read and write the same canonical management API; the dashboard is the human-facing surface.

Signing in

Three paths into the dashboard — magic link, Continue with GitHub, Continue with Google. All three resolve to the same account row keyed by email; the platform stores your email and nothing else. Mechanism in account-login; step-by-step in guides/sign-up-and-login. The session itself is D1-backed and revocable — sign out anywhere kills the session immediately.

Per site key, the dashboard exposes:

Surface Purpose
Public site key Used in the widget's sitekey attribute
Secret Used by the customer backend when calling /siteverify. Rotatable.
Allowed domains Origin allowlist for the widget
Aggregate stats sessions started, sessions client-completed, sessions server-verified — counts only
Integration health diagnostics Derived from gaps between the three counts above

What is intentionally absent

  • No game pool config. Pool selection is a widget-side games= attribute — see widget. There is no server-side allowlist or pinned set. See principles.
  • No game allowlist. Customers choose their own games per page; the platform doesn't gate which ones they're allowed to use.
  • No user data anywhere. Per-site-key aggregate counters only — no IPs, no UAs, no geo, no per-user history. See privacy.
  • No risk thresholds, score histograms, or behavioral charts. The score is game metadata, not a security signal.

Integration health diagnostics

The three counters are deliberately structured so their gaps tell you something useful:

  • sessions startedsessions client-completed ≈ user drop-off (game too hard, slow load, accessibility gap)
  • sessions client-completedsessions server-verified ≈ customer integration problem (forgot to call /siteverify, bad secret, network issue on backend)

That's the entire analytics layer. Anything more would require collecting data we structurally refuse to collect.

Secret rotation

Customers can rotate the secret at any time. The dashboard issues a new secret; the old secret continues to verify tokens for a short overlap window so the customer can deploy the new one without downtime. (Specific window length will be documented when the rotation flow ships in phase 3.)

Site key shape

  • Public key: cpt_pub_...
  • Secret: cpt_sec_...

Prefixes make leaked-secret detection (e.g. GitHub secret scanning) trivial to wire up later.

Management API tokens

Account-level credentials for the non-UI management modalities (OpenAPI, MCP, Terraform). Distinct from the per-site-key cpt_sec_... (which is for runtime /siteverify only).

Surface Purpose
Token issue Mint a new management token (cpt_pat_...). Shown once at issue time; not retrievable afterwards.
Token list Names, prefixes, last-used timestamp, scope (when scopes ship). Never the full token.
Token revoke Immediate; revoked tokens stop authenticating on the next request.

A leaked management token compromises account configuration; a leaked cpt_sec_... only compromises one site key's verification. Different blast radii — different rotation cadences are appropriate.

Paid-tier surfaces

Visible only on paid site keys. Free-tier accounts do not see these sections.

Surface Purpose
Hosted verification toggle Enables the hosted-verification forwarder for the site key
Destination configuration Webhook URL and / or email address. Both can be enabled simultaneously.
Webhook signing key Issued when a webhook destination is added; used by the customer's handler to verify incoming requests
Forwarder URL The Caputchin URL the customer points their form's action at

Hosted-verification configuration follows the same posture as the rest of the dashboard: aggregate counters on delivery success / failure, no per-submission history. See ADR-0007 for the rationale.

Scoreboards (Post-MVP)

Per-site-key, per-game leaderboard view. Surfaces top-N entries plus an aggregate score distribution. Entries display the session-scoped 3-letter handle collected by the widget; rows whose nickname was never submitted render as ---. No sort, filter, or query by nickname is exposed — the column is display-only. Same privacy posture as the rest of the dashboard: no per-user dimension, no IPs / UAs / geo, no cross-session linkage. See ADR-0014 for the design.