Marvia

Sign inSign up

Marvia-05

Persistence is a contract, not a database

PostgreSQL first. The host is a hosting provider — nothing is built around it. Applications call services, services call repositories, repositories call Drizzle. Nothing else touches the database.

Domains

17

Tables

32

Repositories

17

Schema version

1.1.0

Applications → Services → Repositories → Drizzle ORM → PostgreSQL

Base columns

Ten columns on every table, no exceptions

Soft delete, audit and versioning are structural. A table without these columns is a defect, not a shortcut.

idworkspace_idcreated_atupdated_atdeleted_atcreated_byupdated_bydeleted_byversionmetadata

Domains

One domain, one repository

Every resource belongs to exactly one workspace. workspaceId is an explicit argument on every repository method, so cross-workspace leakage is impossible by construction.

DomainTablesRepositoryIsolation
Authenticationusers, sessionsUserRepositoryagnostic
Workspaceworkspaces, workspace_membersWorkspaceRepositoryworkspace
ProjectprojectsProjectRepositoryworkspace
SnapshotsnapshotsSnapshotRepositoryworkspace
Reportreports, report_historyReportRepositoryworkspace
Creditscredit_wallets, credit_transactionsCreditRepositoryworkspace
Billingpayments, subscriptionsBillingRepositoryworkspace
Contentcontent_entriesContentRepositoryworkspace
Extensionextensions, extension_versions, installed_extensions, extension_configsExtensionRepositoryworkspace
Marketplacemarketplace_extensions, marketplace_reviews, marketplace_purchasesMarketplaceRepositoryglobal
Connectorconnector_accounts, connector_sync_historyConnectorRepositoryworkspace
Knowledgeknowledge_objects, knowledge_categories, knowledge_tags, knowledge_relationsKnowledgeRepositoryworkspace
SettingssettingsSettingsRepositoryworkspace
AI Providerai_providers, ai_settingsAiRepositoryworkspace
Activityactivity_timelineActivityRepositoryworkspace
Auditaudit_logsHistoryRepositoryworkspace
NotificationnotificationsNotificationRepositoryworkspace

Strategies

How the data layer defends itself

Soft delete first

deleted_at plus deleted_by. Features call softDelete and restore; only the retention job purges.

Audit first

Every write carries an AuditContext and appends a hash-chained audit_logs row. The chain is verifiable.

Versioning

A version column on every row, plus a history table where rollback matters.

JSONB discipline

Metadata, AI outputs and extension configs only. Structured data stays relational.

Workspace isolation

workspace_id is an explicit argument on every repository method — never inferred from ambient state.

Provider agnostic

Billing and connector providers are data, not branches. PostgreSQL is the contract; the host is replaceable.

Migrations

Append only, forever

Every migration is committed. A shipped migration is never edited — schema change means a new file and a semantic version bump.

00000000_marvia_foundation.sqlv1.0.0

Foundation schema: auth, workspace, project, snapshot, report, credits, billing, content, extension, marketplace, connector, knowledge, settings, AI, activity, audit and notification tables with base columns and isolation indexes.

00010001_marvia_production_data_layer.sqlv1.1.0

Production data layer: schema_migrations audit, auth sessions, workspace members, extension installations and generic settings, credit wallets and ledger, plans and subscriptions, integration connections with vault references, admin audit logs, plus workspace-isolation RLS policies and indexes.

Retention (days)

  • auditLogs30
  • softDeleted7
  • syncHistory30
  • notifications90

Query budget (ms)

  • workspaceOverview100
  • usageSeries150
  • latestReportPerProject100

Reads

Materialised views and seeds

Reads that cannot meet their latency budget move to a view refreshed by the cron worker, never to ad-hoc caching in the request path.

mv_workspace_usage_daily

Daily credit and snapshot usage per workspace.

refresh hourly

mv_marketplace_rankings

Downloads and rating rollup for marketplace listings.

refresh daily

mv_report_freshness

Latest report per project with age in hours.

refresh hourly

development seed

Minimum viable data to boot the app locally.

  • users: one owner account
  • workspaces: one workspace owned by that user
  • workspace_members: owner membership row
  • credit_wallets: wallet with a 1 000 credit grant
  • settings: workspace defaults namespace

demo seed

A populated workspace for demos and screenshots.

  • everything in the development seed
  • workspace_members: admin, member and viewer accounts
  • projects: three projects with realistic URLs
  • snapshots: five completed snapshots per project
  • reports: one seo-audit report per project with metrics and issues
  • content_entries: blog, docs and faq entries
  • extensions + installed_extensions: two installed extensions
  • credit_transactions: grants, usage and one refund
  • notifications: a mixed unread inbox
  • activity_timeline: thirty events across domains

Security

Secrets never live in tables

  • API keys and tokens are stored as references to a secrets manager, never as plaintext columns.
  • Sensitive fields are encrypted at rest by the driver, not by feature code.
  • RBAC is enforced through workspace_members.role — never on the user or profile record.
  • audit_logs is append only and hash chained, so tampering is detectable.