Skip to content

System Architecture

Overview

Floh is a three-tier application consisting of an Angular frontend, a Fastify REST API backend, and a relational database with Redis for job queuing.

Component Diagram

flowchart LR
    UI["Angular 21 UI<br/>PrimeNG"]
    subgraph api["Fastify REST API (Node.js 24)"]
        Engine["Workflow Engine<br/>BullMQ Scheduler"]
        Notify["Notification service"]
    end
    PG[("PostgreSQL / MySQL")]
    Redis[("Redis")]
    SMTP["SMTP Server"]
    OIDC["OIDC Provider"]

    UI --> api
    Engine --> PG
    Engine --> Redis
    Notify --> SMTP
    api --> OIDC

Backend Modules

Module Responsibility
auth OIDC JWT validation, RBAC guards, client (azp) channel
users User CRUD, role assignment
workflows Definition CRUD, run lifecycle
tasks Step/task management
approvals Approval routing and decisions
notifications Email and in-app notifications
connectors Connector registry and execution
scheduler Cron-based workflow triggers
audit Immutable audit logging
reports Aggregate statistics queries
health System health check

Data Flow: Workflow Execution

  1. User starts a workflow run via POST /workflows/:id/start
  2. The engine creates step records from the definition
  3. Each step is executed based on its type:
  4. action — runs immediately, stores result
  5. approval — creates approval records, pauses workflow
  6. notification — sends email and in-app notification (internal users resolved by ID; external addresses supported)
  7. connector — invokes registered connector with timeout
  8. condition — evaluates expression, sets branch variable
  9. On step completion, the engine checks transitions to determine the next step
  10. On error, the engine follows the onError strategy (stop/skip/retry)
  11. When all steps complete, the run is marked completed

Escalation Flow

  1. An approval step is created with a timeout
  2. BullMQ schedules a delayed job for the deadline
  3. If the deadline passes without a decision, the escalation fires
  4. The step is reassigned to the escalation target
  5. Both parties are notified

Database Schema

Core tables: user, workflow_definition, workflow_run, workflow_step, approval, notification, connector_definition, audit_log, scheduled_trigger. Authorization is not persisted — permissions come from the access token scope claim.

All primary keys are UUIDs. JSON data (steps, variables, config) is stored as TEXT columns and serialized/deserialized in the repository layer.

Public Portal Architecture

The public portal enables external users (invitees, task assignees, approvers) to interact with Floh without direct access to the firewalled admin interface. Both Authifi BFF processes sit outside the firewall; the API and datastores stay inside. The public console hostname proxies /api and /bff to console-bff, not to the API process.

flowchart LR
    PortalSPA["Portal SPA<br/>:7073"]
    PortalBFF["Portal BFF<br/>Authifi 3.3.0 :7071"]
    ConsoleSPA["Admin SPA<br/>:7072"]
    ConsoleBFF["Console BFF<br/>Authifi 3.3.0 :7074"]
    subgraph fw["Firewall"]
        API["Fastify REST API<br/>Node.js 24 :7070"]
        PG[("PostgreSQL / MySQL")]
        Redis[("Redis")]
    end

    PortalSPA --> PortalBFF --> API
    ConsoleSPA --> ConsoleBFF --> API
    API --> PG
    API --> Redis

Portal BFF (Backend-for-Frontend)

The Portal BFF is the Authifi BFF 3.3.0 gateway that sits outside the firewall. It is the portal OIDC relying party, owns /bff/* login/logout/ session routes, and proxies browser /api/* calls to the Floh API while attaching the access token server-side. Browser JavaScript never reads the portal access token or refresh token.

Responsibility Implementation
OIDC browser flow /bff/login, /bff/callback, /bff/logout, /bff/session
API proxying Browser /api/* goes through the BFF, which adds Bearer auth server-side
Token exposure guard bff.exposeTokens=false keeps raw tokens out of browser code
WebSocket policy bffProxy.ws=false
Path allowlisting bffProxy.allowlist.enabled=false in the current shipped config; the cached allowlist follow-up is LSA-9831
CSRF contract floh_portal_csrf cookie and x-csrf-token header

Console BFF

The admin console uses a second Authifi BFF 3.3.0 process (floh-client) on port 7074. Browser cookies stay on the console hostname; the API hop is Bearer-only. Live run execution-state WebSocket upgrades go through this BFF (bffProxy.ws: true). Operators can ship console without portal secrets.

Responsibility Implementation
OIDC browser flow /bff/login, /bff/callback, /bff/logout, /bff/session
API + WS proxying Browser /api/* and execution-state WebSocket go through the BFF
Token exposure guard bff.exposeTokens=false
CSRF contract floh_console_csrf cookie and x-csrf-token on every HTTP method
Cookie names Session floh_console_bff_sid — distinct from portal and leftover API floh_sid

Portal SPA

A minimal Angular application with only the routes external users need:

  • /welcome — landing page
  • /dashboard — pending invitations, tasks, and approvals
  • /tasks — task inbox (tasks and approvals, always scope=mine)
  • /invitations/respond — accept or decline invitations
  • /auth/callback — OIDC callback handler

The portal SPA has no sidebar, admin panel, workflow designer, or any administrative functionality.

Portal-Aware Auth Redirects

The console BFF is the console OIDC relying party (including execution-state WebSocket). The API is a Bearer-only resource server; browser sessions and refresh tokens are owned by the BFFs. Portal browser traffic terminates at portal-bff.

For more details, see the Portal Guide.

Deployment

Docker Compose

The default stack includes:

  • postgres — PostgreSQL 16
  • redis — Redis 7
  • server — Fastify API (port 7070)
  • console-bff — Authifi BFF 3.3.0 gateway (port 7074)
  • web — Angular app via nginx (port 80)
  • portal-bff — Authifi BFF 3.3.0 gateway (port 7071)
  • portal-web — Portal SPA via nginx (port 7073)
  • mailhog — SMTP test server (port 8025)

Use docker-compose.mysql.yml as an override for MySQL deployments.

Portal Compose

The portal extends the default stack with:

  • portal-bff — Authifi BFF 3.3.0 gateway (port 7071)
  • portal-web — Portal SPA via nginx (port 7073)
docker compose -f docker/docker-compose.yml -f docker/docker-compose.portal.yml up

Environment Configuration

All configuration is via environment variables. Portal browser traffic is always HTTPS; Floh's committed defaults are HTTPS-first, and the Palantir deployment keeps HTTPS on Caddy -> BFF and BFF -> API. Other deployments may terminate TLS at a trusted ingress and explicitly choose private-network HTTP for internal hops. See .env.example for the full list.