Skip to content

Reference implementation

FINNS built a small stand-in for the partner’s service so the app, core and the contract could be built and tested before the partner’s version exists. It shows a points widget with Collect 10 and Redeem 10 buttons and a signed-in banner. It is the reference for behaviour, not for product.

Test URL: https://trident-poc-gamification.juna.workers.dev

Runtime
Cloudflare WorkersTypeScript 6no runtime dependencies
Storage
Cloudflare D1Session token hashes, and recent sign-outs.
Widget page
vanilla JS + CSSself-hosted fontsstrict CSP + Trusted Types
Protection
Workers rate limiting: launch per IP, adjust per member
Tests
Vitest@cloudflare/vitest-plugin
flowchart TB
  classDef ext fill:#F3ECDF,stroke:#B9A88B,color:#12191B
  classDef ours fill:#13686C,stroke:#0B4A4D,color:#ffffff
  classDef store fill:#ffffff,stroke:#13686C,color:#12191B
  classDef dev fill:#FFF4E8,stroke:#E9724C,color:#12191B,stroke-dasharray:5 4

  subgraph worker["Worker: trident-poc-gamification"]
    direction TB
    launch["launch.ts<br/>POST /api/v1/launch"]:::ext
    points["points.ts<br/>GET /points · POST /points/adjust"]:::ext
    revoke["revoke.ts<br/>POST /auth/revoke"]:::ext
    shell["shell.ts<br/>GET /w/home + CSP"]:::ext
    registry["widgets.ts<br/>which widgets for whom"]:::ext
    harness["dev harness + fake core<br/>/dev (local only)"]:::dev
  end
  d1[("D1<br/>session · signed_out_customer")]:::store
  core["Core<br/>Points API"]:::ours

  launch --> registry
  launch & points & revoke --> d1
  launch & points --> core
Widget Slot Height hint Shown to
points home 146 Everyone (guest view invites sign-in)
signed-in-banner home 64 Members only

The page

  • Speaks protocol v1 over both transports; handles guests.
  • Exact-origin messaging; never '*'.
  • No cookies or web storage; token in memory only; no innerHTML; CSP like the reference’s.
  • Never reports a height below 1; sends empty when there’s nothing to show.
  • Sends changed only after its own successful postings.
  • On 401: keeps the action and key, sends token-expired, retries after token.
  • Reports unused swipes and wheels as scroll / scroll-end.

The backend

  • Serves member and guest launches; decides widgets per member.
  • Redeems each code once, never retries; never downgrades a failed launch to guest.
  • Bounds sessions by notAfter; stores only token hashes.
  • /auth/revoke compares signedOutAt with issuedAt (core’s clock with core’s).
  • Posts by extId with member-scoped idempotency keys; retries with the same key.
  • Never lets the page choose an amount.
  • Never logs codes, tokens or external ids.

Internal and confidential. Share partner sections only as partner builds (pnpm build:partner).