Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Architecture

ScaiControl is the operator-grade control plane that ties the ScaiLabs service ecosystem together. It owns the billing and subscription relationships, maintains a registry of installed services, and exposes a single admin surface across them. This page describes the moving parts.

Top-level components#

  • Backend — FastAPI + SQLAlchemy 2.0 async + MariaDB Galera. Single Python service exposing /api/v1/* REST endpoints and an MCP server with 16 tools.
  • Frontend — SolidJS 1.9 + Tailwind 4 + DaisyUI 5. The customer-facing portal (catalog, billing, usage) and the admin UI both live in one bundle, gated by RBAC.
  • Background workerarq over Redis. Owns provisioning loops, heartbeat monitoring, identity reconciliation, the event dispatcher, the subscription reaper, the trial-ending notifier.
  • Storage — MariaDB Galera for relational state, S3-compatible object store for invoice PDFs and uploaded assets, Redis for queues and short-lived state.

Tenancy hierarchy#

flowchart TB P["<b>Platform</b><br/><i>operator</i>"] PA["<b>Partner</b><br/><i>resells to its own customers, or is the operator itself</i>"] T["<b>Tenant</b><br/><i>the billable entity, has users</i>"] U["<b>User</b><br/><i>authenticates via ScaiKey, scoped by role</i>"] P --> PA --> T --> U

Every API call below /api/v1/* (except a handful of public ones) carries a JWT whose claims pin a partner_id and a tenant_id. Queries on tenant-scoped tables are auto-filtered server-side via a SQLAlchemy do_orm_execute event hook (db/tenant_filter.py), so leaks across tenants aren't possible at the ORM layer.

What ScaiControl owns#

  • Service registry — every running service (ScaiKey, ScaiVault, ScaiFlow, …) registers here. Registration carries app_id, base_url, health_check_url, capabilities. Heartbeats keep the row marked active.
  • Plans + service packs — pricing units. Plans belong to a service; service packs bundle multiple (service, plan) pairs under one billable line.
  • Subscriptions — the active link between a tenant and a (service, plan). Lifecycle is a state machine: pending → trialing → active → past_due/cancelling → cancelled/expired/suspended.
  • Billing profiles — buyer-side identity for invoices (tenant_billing_profiles) and the seller-side for partners (partner_configuration).
  • Invoices and credit notes — EU-compliant, with VAT determination, finalisation immutability, multi-format e-invoicing (UBL, XRechnung, ZUGFeRD, Factur-X), and a GrapesJS-based template designer with language variants.
  • Outbound events + webhook subscribers — every billing or subscription transition emits a signed webhook; subscribers (CRM, accounting bridges, dashboards) are managed in the admin UI.
  • Payment providers — Stripe, Mollie, and Coinbase Commerce (crypto) via a plugin architecture.

What ScaiControl does NOT own#

  • Identity. ScaiKey owns users, groups, OIDC. ScaiControl consumes the JWT and reflects a denormalised copy of identity into its own users table (synced via services/identity_sync.py).
  • Secrets. ScaiVault owns API keys and webhook signing secrets. ScaiControl resolves them by vault path at use time.
  • Email transport. ScaiSend ships the outgoing mails; ScaiControl renders the template + envelope and hands off to ScaiSend.
  • The actual services being subscribed to — those are independent applications that integrate via the registry and event protocols.

Data flow at a glance#

flowchart LR SK["ScaiKey"] -->|login| P["Portal"] P -->|REST| BE["ScaiControl backend"] BE --- DB[("MariaDB<br/>state")] BE --- S3[("S3<br/>PDFs, assets")] BE --- R[("Redis<br/>queues")] BE --> W["arq worker"] W --> ED["event dispatcher"] W --> RP["subscription reaper"] W --> HM["heartbeat monitor"] W --> TN["trial-ending notifier"] W --> PR["provisioning loops"] W --> IR["identity reconciliation"] ED -->|HMAC POST| SUBS["external subscribers"] HM -->|GET /health| SVCS["registered services"]

Surfaces#

Surface Path / mechanism
Tenant portal https://<host>/ — catalog, services, billing, invoices, usage
Admin portal https://<host>/admin/... — billing, partners, tenants, subscriptions, packs, templates, webhook subscribers
REST API https://<host>/api/v1/... — same endpoints both surfaces use
MCP server manage_subscriptions and 15 sibling tools for AI agents
CLI scaicontrol admin ... — operator commands, runs in-process against the DB
Background jobs arq scaicontrol.workers.main.WorkerSettings — cron + queue

Where to look next#

Updated 2026-05-18 01:48:39 View source (.md) rev 2