AG
TR

// work / fivucsas

FIVUCSAS

A multi-tenant biometric authentication platform covering face, voice, MRZ, and active liveness, packaged as an embeddable widget. Self-hosted, KVKK-compliant, with WebAuthn as one of several supported auth methods.

Role
Lead engineer (full-stack, ML, and infrastructure) · RollingCat Software
Date
Mar 2026 – Jun 2026

Stack

  • Java 21
  • Spring Boot 3
  • Python
  • FastAPI
  • PostgreSQL
  • pgvector
  • React 18
  • Kotlin Multiplatform
  • WebAuthn
  • OAuth 2.0
  • OIDC
  • Flyway
  • Docker
  • Traefik
  • Loki + Promtail + Grafana

The problem

Biometric authentication is hard to adopt because integrating it usually means handling raw face and voice data, defeating spoofing attacks, and meeting strict privacy law. This is work most teams cannot take on. The goal was to make biometric login as easy to drop into a site as reCAPTCHA, without the integrator ever touching the biometric data.

Constraints

  • Biometric data must never sit unencrypted at rest, and embedding extraction must never be reachable from the public internet.
  • KVKK (Turkish data-protection law) compliance is a hard requirement, not a later add-on.
  • Each tenant organization must be isolated from every other tenant at the database level.

Approach

The platform splits into three runtime axes: a Spring Boot identity core as the source of truth, a private FastAPI ML sidecar that owns all biometric processing, and thin clients (React web, Kotlin Multiplatform mobile and desktop, and an embeddable widget). The widget exposes the whole challenge flow the way reCAPTCHA exposes a challenge.

Key decisions

  • Split the ML sidecar from the identity core as a separate deployable

    The biometric processor talks to the API only over a private Docker network and is never exposed publicly. The ML stack can be upgraded without touching the API, and embedding extraction is structurally unreachable from the internet.

  • Enforce tenant isolation at the database level via RLS and application-layer filtering

    Multi-tenancy uses a shared schema with PostgreSQL Row-Level Security (RLS) plus a Hibernate @Filter, backed by tenant_id columns on every tenant-scoped table. Both layers must agree for a query to reach data it should not see.

  • Fail fast when the embedding encryption key is missing

    Embeddings are encrypted with Fernet at rest and decrypted only in-process at the moment cosine similarity runs. The app refuses to boot without the key, so it can never silently fall back to a default that would invalidate every stored embedding.

Architecture

Browsers and mobile clients reach the Spring Boot identity core through a Traefik reverse proxy. The identity core owns PostgreSQL with pgvector and talks to the FastAPI biometric processor over a private Docker network that the public internet cannot reach. Loki, Promtail, and Grafana observe the whole stack.

flowchart TB
  client["Clients<br/>React web · KMP mobile/desktop · embeddable widget"]
  traefik["Traefik reverse proxy"]
  api["Identity Core API<br/>Spring Boot 3 · Java 21"]
  db[("PostgreSQL<br/>+ pgvector")]
  ml["Biometric Processor<br/>FastAPI · Python"]
  obs["Loki · Promtail · Grafana"]

  client --> traefik --> api
  api --> db
  api -. private docker network .-> ml
  api --> obs
  ml --> obs
Three runtime axes behind a Traefik proxy; the ML sidecar is private-network-only.

Outcome

FIVUCSAS runs in production as a self-hosted multi-tenant platform with an embeddable widget, WebAuthn/FIDO2 passkeys, and a randomized active-liveness challenge that defeats screen-replay and pre-recorded attacks. The repository is private pending a third-party security review; source access is available on request.

By the numbers

  • 85 Flyway migrations (V1 to V86)
  • 13 FK-cascaded tables behind the users row
  • 3 Runtime axes (API · ML · clients)
  • Fernet Embedding encryption at rest
  • SHA-256 Model delivery integrity
  • WebAuthn, TOTP, NFC, OTP Supported auth methods include

Deep dive

FIVUCSAS (Face and Identity Verification Using Cloud-based SaaS) started as a three-person Marmara University senior project (CSE4297/CSE4197) and now ships under RollingCat Software. I led it and built the Spring Boot 3 / Java 21 identity core, the React 18 web app, the ML integration, and the infrastructure. Ayşenur Arıcı built the anti-spoofing and liveness ML algorithms. Ayşe Gülsüm Eren worked across the Kotlin Multiplatform mobile and desktop apps, liveness tuning, the gesture-analysis module, NFC passport reading, and UX. For a deeper technical walkthrough, see the companion write-up.

The shape of the system

Two constraints drove most design decisions: biometric data must never sit unencrypted at rest, and embedding extraction must never be reachable from the public internet.

Those constraints are why the ML stack lives in a separate FastAPI sidecar on a private Docker network rather than inside the Spring Boot API. The identity core is the authoritative source of truth for tenants, users, sessions, audit logs, and MFA factors (TOTP, WebAuthn, NFC, biometric). The biometric processor owns the face mesh, embedding extraction, and active-liveness puzzle scoring, and it answers only to the API, never to a browser.

Active liveness

The “Biometric Puzzle” prompts a randomized sequence of facial actions (smile, blink, look left, look right) rather than accepting a single still frame that a photo or screen replay could defeat. The randomization makes a pre-recorded attack impractical because the attacker cannot know the sequence in advance. The widget exposes this flow the way reCAPTCHA exposes a challenge, so a tenant integrates against the smallest possible surface. Active liveness is flag-gated.

Tenancy and privacy

Multi-tenancy uses a shared schema with PostgreSQL Row-Level Security (RLS) and a Hibernate @Filter, backed by tenant_id columns on every tenant-scoped table. Both layers must agree before a query can reach another tenant’s data.

Face embeddings are encrypted with Fernet at rest in pgvector and decrypted in-process only at the moment cosine similarity runs. The application refuses to start without the embedding encryption key, so it can never silently fall back to an invalid state.

Operational posture

The platform runs on a Hetzner CX43 box behind Traefik, observed with Loki, Promtail, and Grafana, with nightly encrypted database backups. Security hygiene is part of the workflow: GitHub secret-scanning with push-protection is on, and every new OAuth endpoint gets a permitAll-chain review as part of PR review.

The source is private until a third-party security review completes. Source access is available on request.

All case studies