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
Components
Section titled “Components”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 |
Partner checklist
Section titled “Partner checklist”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
emptywhen there’s nothing to show. - Sends
changedonly after its own successful postings. - On
401: keeps the action and key, sendstoken-expired, retries aftertoken. - 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/revokecomparessignedOutAtwithissuedAt(core’s clock with core’s). - Posts by
extIdwith 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).

