Topology
Dependencies point inward
The kernel depends on interfaces. Concrete providers are injected exactly once, at the composition root.
apps/web (TanStack Start)
|
@marvia/core (ports + registries + loader)
|
+---------+-------+--------+-----------+-----------+
| | | | |
database auth storage queue events
(Drizzle) (Better Auth) (R2 + KV) (CF Queues) (bus)
| |
Postgres workers/* consumers| Layer | May import | Must never import |
|---|---|---|
| apps/* | @marvia/core, @marvia/ui, @marvia/sdk | vendor SDKs |
| packages/core | adapter contracts | any vendor SDK |
| adapter packages | @marvia/shared | @marvia/core |
| extensions/* | @marvia/sdk | anything else in the kernel |
Boot sequence
How a Marvia runtime comes up
Five deterministic steps. Nothing self-registers by import side effect except the component registry.
- 1Parse environment through @marvia/config.
- 2Construct adapters: database driver, auth, storage, queue, cache.
- 3createKernel(ports) builds registries, the connector registry and the loader.
- 4Installed extensions load per workspace with their granted permissions.
- 5Routes, navigation, cards, reports and actions are read from the registries.
Snapshot engine
JSON is the record, never HTML
Architecture only — the kernel defines the pipeline stages, the fetcher and the extractor contract. It does not crawl.
The snapshot body is written to object storage; only metadata reaches Postgres. Completion publishes snapshot.completed, which the report engine consumes to produce a document in the universal report schema.
Workers
Cloudflare Worker consumers
Each worker is a thin consumer. Adapters and pipelines are supplied by the host deployment, never hardcoded.
snapshot-worker
Drives fetch → extract → normalize → persist → emit.
connector-worker
Runs ConnectorAdapter.sync for queued integrations.
queue-worker
Single binding dispatching to registered consumers.
ai-worker
Executes queued AI tasks through the router.
cron-worker
Declarative scheduled task registry.
webhook-worker
Verifies signatures before any side effect.
Tenancy
Isolation is structural
Every repository method takes a WorkspaceId. Storage and cache keys are workspace-prefixed. Roles live on membership rows, never on the user record — privilege escalation is impossible by shape.