maravilla-config
Maravilla Cloud — maravilla.config.ts
maravilla.config.ts lives at your project root and declares everything project-scoped: resources + policies, named groups, relation types, registration fields, OAuth providers, security/branding settings, database indexes, and media transforms.
The build-time adapter reads this file, the runtime ships it as part of the manifest, and the platform reconciles the settings on each deploy. Sections are upsert-only for list-shaped data (resources, groups, relations, oauth, indexes) — declaring them creates/updates entries but never auto-deletes DB-only ones. Singleton sections (registration, security, branding) are replaced wholesale when declared.
Skeleton
import { defineConfig } from '@maravilla-labs/platform/config';
export default defineConfig({
auth: {
resources: [/* ... */],
groups: [/* ... */],
relations: [/* ... */],
registration: { fields: [/* ... */] },
oauth: { /* ... */ },
security: { /* ... */ },
branding: { /* ... */ },
},
database: {
indexes: [/* ... */],
vectorIndexes: [/* ... */],
},
transforms: { /* ... */ },
});
defineConfig is an identity function — it exists purely so TypeScript can infer the shape and give you full IntelliSense.
auth.resources — Layer-2 policy gates
Each resource names a subsystem (a KV namespace, a DB collection, a media bucket) plus the actions it supports and a single policy expression.
resources: [
// No policy → only Layer 1 (tenant isolation) applies. Useful for
// anonymous-friendly demos.
{
name: 'todos',
title: 'Todos',
description: 'Shared todo items (anonymous-friendly)',
actions: ['read', 'write', 'delete'],
},
// Owner-scoped, with a public-share escape hatch via an unguessable id.
{
name: 'invites',
title: 'Birthday Invites',
actions: ['read', 'write', 'delete'],
policy: 'auth.user_id == node.owner || auth.is_admin || node.public == true',
},
// Plain owner-or-admin.
{
name: 'vcards',
title: 'Digital Business Cards',
actions: ['read', 'write', 'delete'],
policy: 'auth.user_id == node.owner || auth.is_admin',
},
],
The policy expression is the raisin-rel language; full details live in maravilla-policies. Key points:
auth.*is the bound caller —auth.user_id,auth.email,auth.is_admin,auth.roles,auth.is_anonymous.node.*is the resource-shaped data for the specific op being checked.node.actionis the action being performed (read / write / delete) — useful for action-specific clauses.- Leaving
policyempty disables Layer-2 for that resource. Layer 1 still applies.
auth.groups — named permission bundles
groups: [
{
name: 'coordinators',
description: 'Can manage gatherings across the whole tenant',
permissions: [
{ resource_name: 'gatherings', actions: ['read', 'write'] },
],
},
],
Group definitions live in config; membership (which users belong to which group) is runtime data managed via the admin UI or platform.auth.updateUser().
auth.relations — typed user-to-user edges
For relationship-aware policies (e.g. "stewards can act on behalf of minors"):
relations: [
{
relation_name: 'STEWARDS', // uppercase identifier used in policies
title: 'Stewards',
category: 'family',
implies_stewardship: true,
bidirectional: false,
},
],
Reference in policies as auth.user RELATES node.owner VIA 'STEWARDS'.
auth.registration.fields — custom signup form
Replaces the registration form's whole field list when declared.
registration: {
fields: [
{ key: 'email', label: 'Email', field_type: 'email', required: true, show_on_register: true },
{ key: 'display_name', label: 'Display name', field_type: 'text', required: true, show_on_register: true },
{ key: 'company', label: 'Company', field_type: 'text', required: false, show_on_register: true },
],
},
field_type is one of: text, email, phone, date, number, select, boolean, url, textarea. Custom fields land on the AuthUser's profile and on event.data.profile in any onAuth({ op: 'registered' }) handler.
auth.oauth — third-party identity providers
oauth: {
google: {
enabled: true,
client_id: process.env.GOOGLE_CLIENT_ID!,
client_secret: { env: 'GOOGLE_CLIENT_SECRET' }, // resolved server-side
scopes: ['openid', 'email', 'profile'],
},
github: {
enabled: true,
client_id: process.env.GITHUB_CLIENT_ID!,
client_secret: { env: 'GITHUB_CLIENT_SECRET' },
scopes: ['read:user', 'user:email'],
},
},
Supported providers: google, github, okta, custom_oidc (set discovery_url on the last). Secrets accept either "literal", "${env.NAME}" template form, or { env: "NAME" } object form — only references are safe to commit.
A common build-time pattern is to omit a provider entirely when its env var is missing, rather than declaring enabled: false:
function buildOAuth() {
const o: Record<string, unknown> = {};
if (process.env.GOOGLE_CLIENT_ID) {
o.google = { enabled: true, client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: { env: 'GOOGLE_CLIENT_SECRET' },
scopes: ['openid', 'email', 'profile'] };
}
return o;
}
auth.security — passwords + sessions
security: {
password_policy: {
min_length: 12,
require_uppercase: true,
require_number: true,
require_special: false,
},
session: {
access_token_ttl_secs: 900, // 15 min
refresh_token_ttl_secs: 2_592_000, // 30 days
max_sessions_per_user: 5,
require_email_verification: true,
},
},
Disable require_email_verification while you're still wiring email delivery — flip it on once SMTP/Mailgun is healthy.
auth.branding — hosted auth pages
branding: {
app_name: 'My App',
logo_url: '/_assets/logo.png',
primary_color: '#3b82f6',
layout: 'centered', // "centered" | "split-left" | "split-right" | "fullscreen"
color_mode: 'auto', // "light" | "dark" | "auto"
welcome_message: 'Welcome back',
terms_url: '/terms',
privacy_url: '/privacy',
custom_css: '.btn-primary { border-radius: 999px; }',
},
Applied to the hosted /_auth/login, /_auth/register, OAuth callback, and password reset pages.
database.indexes — Mongo-style secondary indexes
Reconciled upsert-only. Speeds up find() / findOne() on the indexed fields.
database: {
indexes: [
// Unique single-field
{ collection: 'lessons', keys: [['slug', 1]], unique: true },
// Compound
{ collection: 'modules', keys: [['course_id', 1], ['order', 1]] },
// Sparse — allows multiple docs to omit the field
{ collection: 'enrollments',
keys: [['user_id', 1], ['course_id', 1]],
unique: true, sparse: true },
// TTL — deletes docs older than expireAfterSeconds (single-field index on a unix-seconds field)
{ collection: 'sessions',
keys: [['created_at', 1]],
expireAfterSeconds: 3600 },
],
},
Use tuple form [['field', 1]] for compound indexes — object key order is language-dependent and breaks the ordering invariant.
database.vectorIndexes — semantic search
vectorIndexes: [
{
collection: 'products',
field: 'embedding',
dimensions: 1536,
metric: 'cosine', // 'cosine' | 'l2' | 'hamming'
storage: 'int8', // 'float32' (default) | 'int8' | 'bit'
matryoshka: false, // allow short query vectors
multiVector: false, // ColBERT-style late interaction
},
],
See maravilla-db for the query side (findSimilar, hybrid find with options.vector).
transforms — declarative media pipelines
Each entry compiles into a synthesized storage.put event handler that fans out a Promise.all of the declared platform.media.transforms.* calls every time a matching upload lands. No hand-written event file needed.
transforms: {
// Glob keyed → transform list
'invites/*/*/videos/*': {
transcode: [{ format: 'mp4' }],
thumbnail: { at: '1s', width: 640, format: 'jpg' },
},
'invites/*/*/photo.*': {
variants: [{ width: 400, format: 'webp', quality: 80 }],
},
},
If you need branching logic, fall back to a hand-written events/onSomeUpload.ts with onStorage(...) — see maravilla-events.
Partial adoption
You only declare what you want managed. Empty/omitted sections leave the DB alone — useful for incremental rollout:
// Phase 1 — just policies
export default defineConfig({
auth: { resources: [{ name: 'todos', title: 'Todos', actions: ['read', 'write'] }] },
});
// Phase 2 — add indexes alongside
export default defineConfig({
auth: { resources: [/* ... */] },
database: { indexes: [/* ... */] },
});
Related skills
- maravilla-policies — the policy expression language
- maravilla-auth — the request-scoped binding contract
- maravilla-db — index usage and vector queries
- maravilla-events — hand-written
onStoragefor transforms with branching
Full reference: https://www.maravilla.cloud/llms-full.txt.
More from maravilla-labs/maravilla-cli
maravilla-auth
Maravilla Cloud authentication. Use whenever wiring login/register/session, OAuth callbacks, resource policies, or hitting `platform.auth.*` APIs. Critical: the 3-step request-scoped contract (validate → setCurrentUser → can) — skipping any step silently breaks Layer-2 policies and owner-scoped reads return empty with no error.
12maravilla-events
Maravilla Cloud event handlers — files in `events/*.ts` auto-discovered by the framework adapter. Use to react to data changes (`onKvChange`, `onDb`), auth lifecycle (`onAuth`), schedule (`onSchedule`), queue messages (`onQueue`), realtime publishes (`onChannel`), deploy phases (`onDeploy`), object storage (`onStorage`), or arbitrary REN events (`defineEvent`). Run inside the Maravilla runtime with full platform access via `ctx`.
12maravilla-workflows
Maravilla Cloud durable workflows — replay-based, multi-step processes that survive restarts. Use whenever you need sleeps spanning minutes/hours/days, multi-step pipelines where each step's output feeds the next, waiting for external events, or strict step-history audit. `defineWorkflow` from `@maravilla-labs/functions/workflows/runtime` with `step.run`, `step.sleep`, `step.sleepUntil`, `step.waitForEvent`, `step.invoke`.
12maravilla-media-transforms
Async media + document derivations via `platform.media.transforms` and the declarative `transforms` block in `maravilla.config.ts`. Media: transcode video, thumbnail extraction, image resize/variants, OCR. Documents (.docx/.odt/.pptx/.xlsx/...): convert to PDF, render page thumbnails, generic format conversion, Markdown extraction (RAG-ready), single-file HTML with inlined images, image-replacement templating ({{TAG}} swap + named-object swap), QR-code injection. Use when ingesting user uploads that need normalised renditions, generating contracts/invoices from templates, or extracting structured content for LLMs. Critical: derived keys are content-addressed — `keyFor(srcKey, spec)` is known up front, before the worker starts, so clients can render placeholder UI without round-trips. Declarative config is the default; imperative `transforms.*` calls are for one-offs.
12maravilla-db
Maravilla Cloud document database — MongoDB-style queries, secondary indexes, and vector search. Use for structured app data, multi-field queries, sorting, semantic search via `findSimilar` / hybrid `find` with `options.vector`. Exposed as `platform.env.DB`. Vector indexes support int8/bit quantization, matryoshka, and multi-vector (ColBERT) out of the box.
11maravilla-push
Maravilla Cloud Web Push — server `platform.push.send/schedule/cancelScheduled/listScheduled` with idempotent keys and recurring `everySeconds`, browser `registerPush({ topics, userId, swPath })` from `@maravilla-labs/platform/push`. Use for browser notifications, scheduled reminders, recurring digests, and per-user fan-out by topic.
11