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 thestables-app-developmentskill groups them. The enforced dependency graph istests/Architecture/ModuleBoundaryTest.php; the diagram at the end is a readable sketch of it.
Reading key — owns: 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 theteam.advanced-roles/team.seatsfeatures.
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/VirtualAccountmodels read by treasury/support; registerswallet.*/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.