Skip to main content

Module catalog

What this covers / who it's for. Every module under app-modules/ — purpose, owned tables (central vs tenant-RLS), public surface, and the seams other modules use — with a link to the README that carries the current specifics. Grouped the way the stables-app-development skill groups them. The enforced dependency graph is tests/Architecture/ModuleBoundaryTest.php; the diagram at the end is a readable sketch of it.

Reading keyowns: tables the module's migrations create (central = no RLS, operator surface; tenant-RLS = tenant_id FK + forced RLS policy). Surface: route groups + artisan commands. Seams: what other modules import (per D52 the sanctioned cross-module surfaces are \Models, Integration\Contracts, Events, Enums/DataObjects, Concerns).

Foundation modules (the original scaffolding)

foundation — the shared kernel; every module depends on it, it depends on no domain module. Owns the CQRS bus, JSON:API contract, eventing base, middleware, error codes, Money/Asset + SupportedAsset/FinanceProvider vocabulary. Owns: command_dedup_records, client_idempotency_keys (tenant-RLS) + the unmanaged pgsql_logs side-effect connection. Surface: no routes; middleware aliases + the ->requires() route macro. Seams: CommandBus/QueryBus, the bus markers (Idempotent, WithoutTransaction, RequiresFreshMfa, RequiresAccountActive, RequiresFeature, RequiresRationale, ChecksPermission), the guard contracts (FeatureGuard, AccountFreezeGuard), audit/DLP markers, DomainException/ErrorCode, ExternalRef.

tenancy — single-DB multi-tenancy via Postgres RLS over stancl/tenancy; owns the Tenant (workspace) model and lifecycle state machine. Owns: tenants (central; carries status, level, jurisdiction, base_currency). Surface: tenants:rls, tenants:verify-isolation; tenant.member middleware. Seams: Models\Tenant, Enums\WorkspaceStatus, Events\WorkspaceStatusChanged, WorkspaceLifecycleService (sole status writer). Consumed by nearly every module.

authorization — RBAC for both guards: the per-module permission registry, roles-as-UX, default-role seeding, super-admin bypass (spatie underneath). Owns: tenant_roles, tenant_role_has_permissions, client_user_has_roles (tenant-RLS); operator roles ride stock central spatie tables. Surface: no routes; permissions:sync, tenant role provisioning on create. Seams: PermissionRegistry, RoleComposer, PermissionGate, Permissions facade, HasTenantRoles, BackofficePermission/TenantPermission enums, per-module PermissionProvider contribution.

identity — the user records only; behaviour lives in authentication. Owns: users (operator) + client_users (tenant users; central tables). Surface: none. Seams: Models\User (web guard), Models\ClientUser (client guard), Enums\UserStatus — the actor models every module type-hints.

authentication — the shared auth surface for both guards: registration, login/sessions, MFA (TOTP/WebAuthn/recovery), step-up, passwords, tenant discovery (Fortify wiring). Owns: auth columns on the user tables + mfa_credentials. Surface: client /api/v1/* + operator /api/v1/backoffice/* auth routes; stables:create-super-admin. Seams: none published — thin controllers over WithoutTransaction bus commands.

team — tenant-side team management: invitations, members, role changes (last-owner guard), tenant role CRUD. Owns: team_invitations (central, nullable tenant, context shared with backoffice). Surface: client /api/v1/team/*

  • the public turnstile-gated accept endpoint. Seams: TeamPermission; declares + meters the team.advanced-roles/team.seats features.

backoffice — the central operator shell (thin leaf): dashboard/Horizon shell, staff management, operator role CRUD, operator invitations. Owns: table-less (reuses team_invitations). Surface: web shell + /api/v1/backoffice/staff/*, /api/v1/backoffice/roles/*. Seams: none — a leaf; nothing may depend on it.

support — operator read-only customer-support lookups: tenant directory/overview, per-tenant accounts+balances, customer and transaction lookup. Owns: table-less (one trigram-index migration on onboarding's customers). Surface: /api/v1/backoffice/support/* (queries only). Seams: none — a leaf; reads accounts/onboarding/funding \Models cross-tenant (BYPASSRLS) and reuses BackofficePermission.

Features / entitlements

jurisdictions — central regulatory reference: the ISO jurisdiction catalog (containment + bloc membership) and the per-jurisdiction feature allow-list — the first gate of the entitlements engine. Owns: jurisdictions, jurisdiction_features, jurisdiction_memberships, jurisdiction_aliases (all central). Surface: none — reference data + gate. Seams: consumed only by features.

features — the custom entitlements engine (jurisdiction gates, tier entitles, usage metering; supersedes Pennant). Owns: levels, level_feature_entitlements (central); feature_usages, tenant_feature_entitlements (tenant-RLS). Surface: @feature directive, feature: middleware, features:sync, features:roll-periods (a task-runner fleet sweep). Seams: implements Foundation's FeatureGuard (the RequiresFeature bus gate); FeatureRegistry, Features façade.

Conduit financial integration (D38 onward — the finance domain)

rails — the finance-provider seam: the umbrella RailsProvider + 13 capability contracts, the Conduit adapter over the first-party SDK (packages/stables/conduit-sdk), neutral error translation, per-attempt vendor-call logging. Owns: vendor_request_logs (central, append-only, written on pgsql_logs). Surface: no routes; rails:prune-vendor-logs. Seams: the capability contracts — the only way any module talks to a finance vendor. See Provider integration.

webhooks — the provider-agnostic inbound pipeline: signature verify → exactly-once dedupe → store+ack → queued job → tenant resolve → RLS-scoped handler dispatch. Owns: webhook_calls (central vendor table). Surface: POST /webhooks/{source}; daily model:prune of processed calls. Seams: WebhookEventRegistry + the TenantResolver chain — domain modules register into it (registry inversion; webhooks imports no domain module).

provider-mirror — the mirror backbone: AbstractMirrorUpsert (race-safe, order-tolerant envelope upserts), reconciler/backfill bases, drift records, the reconciliation registry. Owns: mirror_drift (tenant-RLS). Surface: mirrors:reconcile-stale (hourly, rides task-runner), mirrors:prune-drift. Seams: the AbstractMirror* extension surface + MirrorEnvelope + MirrorReconciliationRegistry — subclassed by every mirror domain.

audit — the durable, tamper-evident, hash-chained audit trail; consumes bus telemetry, nothing imports it. Owns: audit_events, audit_checkpoints (central, append-only, pgsql_logs session). Surface: audit:verify-chain, audit:export, audit:export-worm/audit:verify-worm (config-gated WORM segment offload, D85); operator search/export queries. Seams: listens to CommandExecuted/QueryExecuted + CommandDenied/CommandFailed/CommandRejected; audit queue.

notification — inbox + email + the durable comms record: consent-gated dispatcher, custom RLS inbox channel, responsibility-based operator alerts. Owns: notifications (tenant-RLS inbox), operator_notifications (central), notification_log_items (nullable-tenant RLS), notification_preferences (tenant-RLS). Surface: client inbox /api/v1/*; notifications:prune-log. Seams: NotificationDispatcher — consumed by dlp/compliance/onboarding/funding for alerts.

async-requests — the generic pollable async-request platform (D83). Owns: async_requests (tenant-RLS). Surface: GET /api/v1/async-requests (+ /{uuid} poll); async-requests:prune. Seams: AsyncRequestPlatform (create/markProcessing/complete/fail) + the typed result contract. See Async platforms.

task-runner — the durable async-task execution framework (D93): batches with one job per unit on a central ledger. Owns: task_runs, task_run_units (central; opaque no-FK unit tenant_id); the tasks queue. Surface: tasks:status/retry/cancel/prune/detect-stuck; read-only GET /api/v1/backoffice/task-runs (task-runner.runs.view). Seams: TaskRunner/TaskDefinition/TracksAsTaskRun contracts, TaskTypeRegistry, InteractsWithTaskRun. See Async platforms.

documents — the encrypted document vault: store-before-forward capture, envelope encryption, signed-URL-only downloads with per-serve logging, integrity sweep, RTBF crypto-shred; config-gated (both OFF by default) direct-to-object-storage transfer and malware-scan quarantine. Owns: documents, document_accesses (tenant-RLS); the documents queue. Surface: client document routes; documents:verify-integrity, documents:shred-document, documents:redrive-forward-failed, documents:prune-accesses. Seams: consumed by onboarding (KYB vault path); consumes rails ManagesDocuments.

dlp — data classification + loss prevention: the classification registry, declare-driven access logging, tripwire rules, incidents, egress detection. Owns: data_access_logs (central), tenant_data_access_logs (tenant-RLS), dlp_egress_events, dlp_incidents, rules tables; the dlp queue. Surface: bus-only entry; dlp:evaluate-rules, dlp:evaluate-egress-rules, dlp:validate-classifications. Seams: DataClassificationRegistry (modules push ClassificationProviders in); consumes the ReadsClassifiedData/EgressesData markers; implements task-runner's ScrubsLedgerText.

onboarding — the KYB/KYC vertical, first mirror domain: requirements-driven KYB submit, KYC via the rail, customer/application mirror sync. Owns: customers, applications, onboarding_documents (tenant-RLS mirrors) + provider_customers (central id-routing map). Surface: client KYB/KYC /api/v1/* routes. Seams: registers webhook handlers + the ProviderCustomerTenantResolver; its Customer model is read by accounts/payments/support.

accounts — the accounts mirror domain: wallets, virtual accounts, signers, signing quorum + the wholesale-overwrite balance projection. Owns: wallets, virtual_accounts, wallet_signers, signing_quorums (tenant-RLS mirrors)

  • account_balances (tenant-RLS projection, never incremented — D42). Surface: client account reads. Seams: AccountBalance/Wallet/VirtualAccount models read by treasury/support; registers wallet.*/virtual_account.* webhook handlers.

funding — the deposits/transactions mirror domain + the travel-rule sender-information maker and held-deposit compliance seam. Owns: transactions (tenant-RLS mirror). Surface: client GET /api/v1/transactions* + POST .../sender-information. Seams: emits DepositAwaitingSenderInformation, DepositHeldForSenderInfoTimeout, TransactionAwaitingUserSignature (consumed by compliance + payments); its Transaction model is read by payments/support.

treasury — the table-less FX/master-balance domain: ExchangeRateProvider with a caching decorator (≤20s freshness, D92 named-provider registry) + the multi-asset master-balance rollup converted to the tenant's base currency (D90/D91). Owns: table-less (rates ride the cache; base currency lives on central tenants). Surface: GET /api/v1/treasury/master-balance; treasury:warm-rates (every 15s). Seams: ExchangeRateProvider (compliance uses it for CTR currency conversion); reads accounts' AccountBalance projection.

payments — the largest domain: orders (on/off-ramp), observe-only payout co-sign, registered addresses + whitelist recipients, the transactions filter/export matrix, async exports + monthly statements. Owns: orders, order_transactions, payouts, registered_addresses, whitelist_recipients (tenant-RLS). Surface: client payments routes + export create/download; payments:generate-monthly-statements (monthly). Seams: first consumer of async-requests + a task-runner adopter; reads funding's Transaction + onboarding's Customer models; feature-gated via RequiresFeature (D72).

compliance — the operator compliance backend: cases + the CaseIntake seam, the SLA engine, account freeze/closure (the bus freeze gate's enforcing implementation), SAR/CTR/travel-rule filing. Owns: compliance_cases, account_states, case_notes, held_incoming_items (all central, no-FK tenant ids). Surface: /api/v1/backoffice/compliance/*; compliance:scan-sla-breaches (every minute). Seams: implements Foundation's AccountFreezeGuard; CaseIntake, RegulatoryFiler, ScreeningProvider contracts; reads the domain mirrors cross-tenant.

The dependency direction

Verified against tests/Architecture/ModuleBoundaryTest.php — the enforced, hand-maintained graph (this sketch shows the load-bearing edges, not every allowed one). Arrows point at the dependency; everything ultimately points down to foundation. Leaves (backoffice, support) may not be depended on; audit and task-runner are consumed only via events/registries, never imported (except task-runner's published Integration surface).

Cross-module data reads (the sanctioned \Models edges): accounts → onboarding, treasury → accounts, payments → funding + onboarding, compliance → accounts/onboarding/funding/payments, support → accounts/onboarding/funding. Consumer edges into task-runner exist from features/provider-mirror/documents/dlp/payments (published Integration surface only). When you add a module or an edge, ModuleBoundaryTest is where it gets slotted in — and this page plus architecture-flow.md get updated in the same change.


← Engineering wiki index