FixPortalDocsSimulator
Browse documentation

Architecture

FixPortal separates the public and operator UI, the simulator backend, and content authoring so each can ship on its own cadence without sharing a database or source tree.

Runtime path

The React application is a Vite-built SPA with prerendered public routes. Authenticated commands and queries use REST; live session, order, and traffic updates use SignalR. React Query owns server state, Zustand owns small client preferences, and MSAL supplies Entra authentication in cloud builds.

The .NET 10 backend is an ASP.NET Core minimal API and SignalR host. It owns tenant authorization, SQL Server persistence, simulation policy, and operational endpoints. Its engine and simulation projects wrap QuickFIX/n sessions and turn policy into FIX messages.

The SQL-free Acceptor.Lite project is the demo venue. The browser never calls it: the public demo talks to a separate demo host service (the FixPortal.Demo.Host sample in the engine repository) over its own demo contract, recorded in contracts/demo/demo-host.contract.ts, and that host logs a pool of initiator sessions on to Acceptor.Lite and drives its venue controls over HTTP. That demo contract is separate from the tenant REST contract.

Keystatic CMScontent/ MDXSPAVite buildRESTSignalRdemo contractBackend API.NET minimal APISQL Servertenant stateTenant FIX engineQuickFIX/nDemo hostsession pool, engine repoAcceptor.LiteSQL-free demo venue
Keystatic content builds into the SPA. On the tenant path the SPA calls the backend over REST for commands and queries and SignalR for live updates, and the backend drives SQL Server and the tenant FIX engine. On the demo path the SPA calls a separate demo host over its own demo contract, and that host logs pooled initiator sessions on to the SQL-free Acceptor.Lite venue; the browser never talks to Acceptor.Lite directly.

Repository boundaries

  • fixportal-simulator-frontend owns the SPA, public pages, docs/blog MDX, generated REST type declarations, and committed consumer contract snapshots.
  • fixportal-simulator-backend owns REST, SignalR, DTO, ATDL, persistence, and FIX-runtime contracts.
  • fixportal-cms is a Keystatic editor over this repository's content/ directory; it owns no content database.
  • fixportal-engine remains a separate reusable FIX-engine product; the simulator backend does not depend on it. It hosts the FixPortal.Demo.Host sample that the public demo talks to. Do not assume every simulator runtime concern belongs there.
FrontendSPA, content, contracts/contract seamBackendREST, SignalR, DTOsCMSedits content/demo venueEnginehosts Demo.Host
Four repositories, each owning a distinct slice: the frontend and backend meet at the committed contract snapshots; the CMS edits this repository's content directory over the network; the engine is a separate product whose demo host drives the backend's Acceptor.Lite demo venue, not a runtime the backend wraps.

Contracts

The backend publishes OpenAPI, SignalR, TypeScript-shape, and ATDL state-rule snapshots. The frontend commits consumer snapshots under contracts/. npm run generate:rest-types writes REST type declarations to src/api/generated/; it does not generate an HTTP client. Hand-maintained application types in src/api/types.ts form the stable facade consumed by UI code.

The backend's parity tests compare its DTOs with the frontend snapshot, preventing one repository from silently renaming or removing fields.

Content and deployment

Docs and blog entries are MDX compiled into the released frontend. A content change therefore goes through frontend build and prerender checks. The content workflow composes current content with an already released application version, then publishes the resulting static site. Merging an edit does not mutate a live database.

Theme persistence

The published site theme lives in one PlatformSettings row keyed theme_overrides, holding a JSON blob. Saved theme presets live in their own ThemePresets table. Only an unpublished draft touches the browser: it sits in localStorage and is merged over the fetched published theme on boot. White-label snapshot builds skip the runtime fetch and instead bake a committed theme snapshot, exported from ThemeLab into brands/<site>/theme.json, into the static build.

White-label buildThemeLab export, no fetchBrowserlocalStorage draftThemeStoreProvidermerge on boot/api/theme/globalGET / PUTPlatformSettingsrow: theme_overridesThemePresetstable: saved presetsexported snapshot
A published theme lives in one PlatformSettings row; saved presets live in their own ThemePresets table. Only an unpublished draft touches the browser, merged over the fetched published theme on boot. A white-label snapshot build skips that runtime fetch and bakes a committed snapshot exported from ThemeLab into the static build instead.

Security boundaries

Tenant identity is enforced by the backend, not trusted from browser state. Customer builds are generated from the same source with site-specific assets and exclusions. Brand-leak tests, auth gates, and server authorization are separate controls; none substitutes for another.

Local devAuthentication:Mode DisabledSeeded dev tenantfixed tenant and userCloud buildMSAL sign-inEntra tenantissues the identityBackend resolvesaccount, isPlatformAdmin
Local development skips real authentication and reads a seeded development tenant and user; cloud builds sign in through MSAL against Entra, and the backend resolves the account and its PlatformAdmin role independently of what the client claims.

Updated 2026-09-25 · Edit on GitHub