System landscape
This page shows the whole universe: FINNS’s own services, integration partners and the third-party services we rely on. See External services and the Roadmap for payment, POS, CDP and hotel systems.
System context
Section titled “System context”flowchart TB
classDef ours fill:#13686C,stroke:#0B4A4D,color:#ffffff
classDef ext fill:#F3ECDF,stroke:#B9A88B,color:#12191B
classDef person fill:#E9724C,stroke:#C55A37,color:#ffffff
classDef staff fill:#FDE9E2,stroke:#E9724C,color:#12191B
guest(["Guest"]):::person
staff(["Staff"]):::person
subgraph touch["Touchpoints"]
direction TB
pass["<b>FINNS Pass</b><br/>Apple/Google Wallet"]:::ours
app["<b>Guest app</b><br/>Flutter (mobile app + web)"]:::ours
booking["<b>Booking website</b><br/>CTG"]:::ext
gate["<b>Gate scanner</b><br/>Vendor TBD"]:::ext
admin["<b>Admin console</b><br/>SvelteKit"]:::staff
end
subgraph platform["Trident platform"]
direction TB
api["<b>API</b>"]:::ours
loyalty[("<b>Loyalty</b><br/>Points ledger and membership")]:::ours
assets[("<b>Assets</b><br/>Wallet balance, wristbands")]:::ours
accesses[("<b>Accesses</b><br/>Gym")]:::ours
rel[("<b>Relationships</b>")]:::ours
resv[("<b>Reservations</b><br/>Events")]:::ours
txn[("<b>Transactions</b>")]:::ours
pii[("<b>Customer PII</b>")]:::ours
end
subgraph services["Services"]
direction TB
kc["<b>Guest identity</b><br/>Keycloak"]:::ext
jc["<b>Staff identity</b><br/>JumpCloud"]:::ext
ses["<b>Transactional email</b><br/>Amazon SES"]:::ext
pg["<b>Payment gateway</b><br/>Airwallex"]:::ext
pos["<b>POS</b><br/>Shiji"]:::ext
pms["<b>PMS</b><br/>Daylight"]:::ext
sota["<b>Customer engagement</b><br/>Sota Platforms"]:::ext
cdp["<b>CDP</b><br/>Insider"]:::ext
end
guest --> touch
staff --> touch
touch -- "REST · handoff and launch codes ·<br/>check-ins · sign-out revoke" --> platform
touch -- "OIDC sign-in" --> services
platform -- "user changes · Points API ·<br/>integrations" --> services
- FINNS platform built and run by FINNS
- Vendor or third party built or bought outside FINNS, to our contract
- Staff tool used by FINNS staff, not guests
- Person guest or staff
What runs where
Section titled “What runs where”| Container | Owner | Built with | Runs on | Talks to |
|---|---|---|---|---|
| Core | FINNS | Java 25, Quarkus 3.39 (native image) | DigitalOcean App Platform | PostgreSQL, Keycloak, partners’ revoke endpoints |
| PostgreSQL | FINNS | PostgreSQL 18, Flyway migrations | DigitalOcean Managed Postgres | Core (and Keycloak’s own database, same cluster) |
| Keycloak | FINNS (operated) | Keycloak 26.8 + FINNS event-listener extension | DigitalOcean droplet, Docker Compose, Caddy | Core (webhook), browsers (sign-in) |
| Guest app | FINNS | Flutter 3.47 / Dart 3.13 | iOS, Android, web on Cloudflare Workers | Keycloak, core, engagement partner, booking website |
| Admin console | FINNS | SvelteKit 2, Svelte 5, Tailwind 4, shadcn-svelte | Cloudflare Workers | JumpCloud, core |
| Booking website | CTG | Vendor’s choice | Vendor’s choice | Keycloak, core |
| Customer engagement | Sota Platforms | Vendor’s choice | Vendor’s choice | Core |
| Gate scanner | Vendor | Vendor’s choice | Vendor’s hardware | Core |
| JumpCloud | SaaS | n/a | JumpCloud | Admin console |
The booking website, the customer engagement partner and the gates each have a reference implementation that FINNS built to prove the contracts. Those are examples only. Vendors build their own on whatever stack they choose, and the only requirement is that they follow the API contracts: booking, engagement, gate.
How the pieces talk
Section titled “How the pieces talk”| From → To | Protocol | Authenticated by | Direction of trust |
|---|---|---|---|
| App → Core | HTTPS JSON | Keycloak access token (azp=trident-app) |
Core validates JWT against Keycloak’s keys |
| App → Partner | HTTPS + one-time code | Code issued by core | Partner redeems the code with core |
| Admin console → Core | HTTPS JSON, server to server | Static service token + acting staff member’s id | Core re-checks the staff member on each call |
| Booking / Engagement partner / Gate → Core | HTTPS JSON, server to server | One static bearer token per integration | Core checks before routing |
| Core → Partners | HTTPS POST /auth/revoke |
A separate revoke token per partner | Partner checks |
| Keycloak → Core | HTTPS POST (async, retried) | Webhook token | Core checks, then reads the user itself |
| Core → Keycloak | Admin REST API | Client credentials (finns-core-sync, view-users only) |
Keycloak checks |
There is no message broker. Changes flow as direct HTTPS calls, plus one pull-based feed (the points events feed) that the engagement partner polls. See Points & membership.
Internal and confidential. Share partner sections only as partner builds (pnpm build:partner).

