Adapters
One seam per external concern
A vendor SDK may only be imported inside its own adapter package. Everywhere else, the interface is the dependency.
| Contract | Package | Reference implementation |
|---|---|---|
| Repositories | @marvia/database | Drizzle + PostgreSQL |
| AuthAdapter | @marvia/auth | Better Auth (Google, GitHub, email-invite) |
| ObjectStorageAdapter | @marvia/storage | Cloudflare R2 |
| KVAdapter | @marvia/storage | Cloudflare KV |
| QueueAdapter | @marvia/queue | Cloudflare Queues |
| EventBus | @marvia/events | in-memory (dev), queue-backed (prod) |
| BillingAdapter | @marvia/billing | none — interfaces only |
| AiProviderAdapter | @marvia/ai-router | none — interfaces only |
| ConnectorAdapter | @marvia/connector-engine | supplied by extensions |
Data access
Ten repositories, zero raw queries outside them
Every method takes an explicit WorkspaceId, so tenant isolation cannot be forgotten at a call site.
WorkspaceRepositoryProjectRepositoryUserRepositoryCreditRepositorySnapshotRepositoryContentRepositorySettingsRepositoryActivityRepositoryConnectorRepositoryExtensionRepositoryReportRepositoryBillingRepositoryKnowledgeRepositoryHistoryRepositoryNotificationRepositoryMarketplaceRepositoryAiRepositoryConnectorHistoryRepository
Extension SDK
defineExtension is the only entry point
Permissions are declared in the manifest and enforced at load time — a misconfigured extension can never partially install.
import { defineExtension } from "@marvia/sdk";
export default defineExtension({
manifest: {
id: "example",
name: "Example",
version: "0.1.0",
permissions: ["workspace:read", "events:subscribe"],
},
setup(registrar) {
registrar.registerNavigation({
id: "example",
label: "Example",
to: "/example",
});
registrar.on({
event: "snapshot.completed",
handle: async () => {},
});
},
});Registration surface
registerCardregisterReportregisterActionregisterWidgetregisterRouteregisterNavigationregisterSettingsregisterConnectoron
Extension permissions
workspace:readproject:readproject:writecredits:spendcontent:readcontent:writeconnector:readevents:subscribestorage:readstorage:writeai:invoke
Reports
Every producer emits the same schema
Core, extensions and AI agents all return this document. One renderer, one export path, one permission model for actions.
overview
metrics
cards
issues
recommendations
actions
history
Access control
Roles and permissions
Roles live on the workspace membership row. Permission checks are uniform: role + permission + workspace.
Roles
owneradmineditormemberviewerguest
Permissions
workspace.readworkspace.updateworkspace.archiveworkspace.restoreworkspace.deleteworkspace.transfermember.readmember.invitemember.updatemember.removerole.assignproject.readproject.writeproject.deletereport.readreport.generatereport.exportcredits.readcredits.consumecredits.purchaseextension.readextension.installextension.configureextension.approveconnector.readconnector.managecontent.readcontent.writecontent.publishsettings.readsettings.updatehistory.readaudit.read
Billing
Adapter interfaces only
The kernel ships no payment implementation. Providers plug in behind BillingAdapter.
telegram-manualpaddleduitkustripe (planned)xendit (planned)polar (planned)lemonsqueezy (planned)