MainSDK
Search⌘K
Integrations

Built-in and local drivers

The drivers that ship inside MainSDK - local drivers for offline development, built-in production drivers, action surfaces and testing fakes.

80 interfaces92 driversWritten guide
Updated 2026-10-28

Some drivers need no third-party account because they ship inside MainSDK. They fall into two groups. Local drivers let mainsdk dev run your app offline, with no provider keys and no network calls to providers. Built-in drivers are production drivers that run in your app: Cedar for permissions and policy, the network allowlist, rate limits, privacy, webhooks, PDFs and more. This page covers both, plus the action surfaces, MainSDK Cloud as a destination, a few generic slots and the testing fakes. SQLite for local data has its own page: SQLite.

Interfaces it serves

Pick Built-in and local drivers in main.yaml for any of these. Your calls stay the same, and you can swap later.

TenantstenantBuilding for 0.3

Driver builtin Built in (coreDefault

main.yaml
tenant:
  driver: builtin
Context and shared contextcontextBuilding for 0.4

Driver builtin Built in (the app link)Default

main.yaml
context:
  driver: builtin
Actions and agent toolsactionPreview

Driver mcp MCP

main.yaml
action:
  driver: mcp

Swap to another provider

HTTP APIServer actionsCLI
Actions and agent toolsactionPreview

Driver http HTTP API

main.yaml
action:
  driver: http

Swap to another provider

MCPServer actionsCLI
Actions and agent toolsactionPreview

Driver server-action Server actions

main.yaml
action:
  driver: server-action

Swap to another provider

MCPHTTP APICLI
Actions and agent toolsactionPreview

Driver cli CLI

main.yaml
action:
  driver: cli

Swap to another provider

MCPHTTP APIServer actions
App agentsagentsBuilding for 0.3

Driver builtin Built inDefault

main.yaml
agents:
  driver: builtin
Playbook advisoradvisorBuilding for 0.6

Driver builtin Built in (calls the ai interface)Default

main.yaml
advisor:
  driver: builtin
PaymentspayBuilding for 0.3

Driver local Local test cards

main.yaml
payments:
  driver: local

Swap to another provider

Engagement journeysengagementBuilding for 0.4

Driver builtin Built in on notify and workflowsDefault

main.yaml
engagement:
  driver: builtin
WebhookswebhooksPreview

Driver builtin Built inDefault

main.yaml
webhooks:
  driver: builtin

Swap to another provider

CryptographycryptoBuilding for 0.3

Driver cloud-kms Cloud key services

planned; use the keys interface today

main.yaml
crypto:
  driver: cloud-kms

Swap to another provider

Network permissionsnetworkBuilding for 0.3

Driver builtin Built inDefault

main.yaml
network:
  driver: builtin
Local filesdiskBuilding for 0.3

Driver builtin Built inDefault

main.yaml
disk:
  driver: builtin
Code sandboxessandboxBuilding for 0.4

Driver local Local subprocess (development)

main.yaml
sandbox:
  driver: local

Swap to another provider

Privacy and data rightsprivacyBuilding for 0.3

Driver builtin Built inDefault

main.yaml
privacy:
  driver: builtin
Health, resources and status pagestatusBuilding for 0.4

Driver builtin Built inDefault

main.yaml
status:
  driver: builtin

Swap to another provider

Your status page provider
Health, resources and status pagestatusBuilding for 0.4

Driver provider Your status page provider

main.yaml
status:
  driver: provider

Swap to another provider

Built in
Security eventssecurityBuilding for 0.3

Driver builtin Built in (log, OTLP and events sinks)Default

main.yaml
security:
  driver: builtin
Incident controlsincidentBuilding for 0.4

Driver builtin Built in (pushes through the config driver)Default

main.yaml
incident:
  driver: builtin
Exfiltration guard and canariescanaryBuilding for 0.4

Driver builtin Built in (an Aho-Corasick matcher on the standard library)Default

main.yaml
canary:
  driver: builtin
AlertsalertsBuilding for 0.4

Driver builtin Built in (sends through notify and email drivers)Default

main.yaml
alerts:
  driver: builtin
SignalssignalsBuilding for 0.4

Driver builtin Built in (diagnostics channels, events.broadcast and the host bus)Default

main.yaml
signals:
  driver: builtin
PlaybooksplaybooksBuilding for 0.4

Driver builtin Built inDefault

main.yaml
playbooks:
  driver: builtin
Observe the SDKobserveBuilding for 0.3

Driver builtin Built in (coreDefault

main.yaml
observe:
  driver: builtin
Behaviour normsnormsBuilding for 0.4

Driver builtin Built inDefault

main.yaml
norms:
  driver: builtin
LocationgeoBuilding for 0.7

Driver platform-headers Platform headersDefault

a hint from the declared platform; no geocoder

main.yaml
geo:
  driver: platform-headers

Swap to another provider

MapboxGoogle Maps PlatformLocal table in the testing kit
LocationgeoBuilding for 0.7

Driver local Local table in the testing kit

geocodes from a table with no network, for tests

main.yaml
geo:
  driver: local

Swap to another provider

Neighbour appsneighboursBuilding for 0.4

Driver builtin Built in (user-only sockets and the app link)Default

main.yaml
neighbours:
  driver: builtin
App registry and linkappsBuilding for 0.4

Driver builtin Built in (a local folder and a user-only socket)Default

main.yaml
apps:
  driver: builtin
Ops serveropsBuilding for 0.4

Driver builtin Built in (a user-only socket and a token file)Default

main.yaml
ops:
  driver: builtin
Schema and parsersschemaBuilding for 0.2

Driver builtin Built inDefault

main.yaml
schema:
  driver: builtin
Testing kittestingBuilding for 0.4

Driver local The local drivers with recording and chaosDefault

main.yaml
testing:
  driver: local
Adopt an existing appadoptBuilding for 0.5

Driver builtin Built in (reads files, runs git)Default

main.yaml
adopt:
  driver: builtin

Before you start

There is nothing to create: no account, no keys, no dashboard. Two things to know:

  1. Local drivers are for development and tests. mainsdk check --env prod, or any run whose resolved environment is prod, fails with MAIN-CHECK-0026 if any interface resolves to a local driver. The keys local driver also fails with MAIN-KEYS-0001 in production.
  2. Built-in drivers are production-ready. Cedar, network, limits, privacy, webhooks, pdf, status, analytics, feedback, i18n files, config files and geo platform headers are the default or only driver for their interface and are meant to run in production.

Keep local drivers in a development overlay (main.env.dev.yaml) and real providers in main.env.prod.yaml, so the check keeps them apart.

Configure

Local development drivers

mainsdk dev runs a local driver for every interface in use, so the app works end to end with no cloud account.

  • auth: local - sign-in for development only, with no identity provider.
  • ai: local - a local fake model, with redaction, caps and audit enforced as in production.
  • pay: local - local test cards. With no provider key, a checkout completes and fires payment.succeeded.
  • notify: local - the terminal channel. WhatsApp and SMS messages print in the terminal. The key that selects it is not decided yet.
  • email: local - a local inbox. Emails land there so you can see the rendered message and its headers.
  • files: local - files on disk. In production, use getqr, s3, uploadthing or vercel-blob (ADR-0013); check refuses the local driver in production.
  • jobs: local - an in-process queue for events and background jobs.
  • secrets: local - reads .mainsdk/secrets.json, which is git-ignored.
  • secrets: env - reads the process environment. Whether check treats it as a local driver in production is not specified; prefer a secrets manager with workload identity there.
  • keys: local - local envelope encryption, development only.
main.env.dev.yaml
ai:
  driver: local
email:
  driver: local
files:
  driver: local
jobs:
  driver: local      # [to be decided] driver id for the in-process local queue
secrets:
  driver: local
keys:
  driver: local

Built-in production drivers

  • access: cedar - permissions decided in process with Cedar, RBAC and ABAC, deny by default.
  • policy: cedar - policies from your policies/ folder, decided in process, in observe, warn or enforce mode.
  • network: builtin - the outbound allowlist; undeclared hosts are blocked and reported.
  • limits: builtin - rate limits, locks and quotas, with sign-in and AI calls limited by default.
  • privacy: builtin - export, erasure and consent across every other driver, with retention per data label.
  • webhooks: builtin - signed outbound delivery with retries, and verified inbound webhooks.
  • pdf: builtin - renders PDFs from templates, localised through i18n, and signs them.
  • status: builtin - health checks per dependency and a status page.
  • analytics: builtin - the built-in store; active users by country, region and version are queries, and IP addresses are never stored.
  • feedback: builtin - report capture with the trace, version and steps attached.
  • i18n: files - messages in local YAML files; i18n: ai-draft adds an AI draft for new strings, marked as needing review until a person approves it.
  • config: files - typed settings from files; where it reads its values from is not decided yet.
  • geo: platform-headers - a coarse location hint from the headers your hosting platform adds, never used for access.
main.yaml
access:
  driver: cedar
  models: [rbac, abac]
policy:
  engine: cedar
  dir: policies/
  mode: observe
network:
  allow: [api.partner.com]
  default: deny
webhooks:
  driver: builtin
  secret: secret:WEBHOOK_SIGNING_KEY   # suggested name
i18n:
  driver: files
  locales: [en-IN, hi-IN]
config:
  driver: files
  schema: ./config.schema.json

limits, pdf, status, feedback and geo take driver: builtin (or platform-headers) and nothing else so far. network and privacy have one driver, so there is nothing to set.

Action surfaces

mcp, http, server-action and cli are not drivers you choose. One action() definition gives you an HTTP endpoint, a server action, a CLI command (mainsdk run <name>) and an audit event, plus an MCP tool when you opt in with mcp: true or mcp.expose.

main.yaml
mcp:
  expose: [orders.refund, orders.list]
  confirm: { write: true, destructive: true }

MainSDK Cloud as a destination

log: mainsdk and analytics: mainsdk send telemetry and product events to MainSDK Cloud. Nothing leaves your app before mainsdk login, and then only when you opt in. log: terminal is the default and keeps everything local.

main.yaml
telemetry:
  to: [terminal, mainsdk]   # default [terminal]

Generic slots

  • status: provider - your own status page provider. Which providers are supported is not decided yet.
  • crypto: cloud-kms - cloud key services behind crypto. Key names and references are not decided yet.
  • backup: object-lock - object storage with object lock, for immutable backups. How you select it is not decided yet.

Testing fakes

testing: fakes is the only driver of the testing kit: an in-memory fake for every interface, generated from its contract. It has no main.yaml block; createTestApp() reads your file and swaps each interface for its fake.

Use it

Your code is the same on a local driver and a real provider. With email: local, this lands in the local inbox:

app/signup/actions.ts
"use server";
import { email } from "mainsdk";

export async function welcome(address: string) {
  await email.send({ to: address, template: "welcome", data: {} });
}

A built-in production driver, Cedar, deciding in process:

app/api/orders/[id]/route.ts
import { access, auth } from "mainsdk";

export async function PATCH(req: Request, { params }: { params: { id: string } }) {
  const session = await auth.require(req);
  const order = { type: "order", id: params.id };
  const decision = await access.check({ subject: session.user, action: "orders.update", resource: order });
  if (!decision.allow) return new Response("Forbidden", { status: 403 });
  return Response.json({ ok: true });
}

Local development and testing

npx mainsdk dev

mainsdk dev uses local drivers with no network calls to providers. In tests, createTestApp() from @mainsdk/testing puts every interface on its fake, clock.advance() controls time, and chaos.on("<interface>", { outage: true }) injects outages.

tests/signup.test.ts
import { it } from "node:test";
import { createTestApp, expect, mailbox } from "@mainsdk/testing";

it("sends a welcome email", async () => {
  const app = createTestApp({ main: "./main.yaml" });
  await welcome("a@pinecart.dev");
  mailbox(app).toHaveSent({ to: "a@pinecart.dev", template: "welcome" });
});

What MainSDK adds on top

  • Fakes enforce the same guarantees as production: a marketing email without an unsubscribe fails in a test too.
  • Local drivers never reach production: MAIN-CHECK-0026 stops the build.
  • Built-in drivers run in your app, with no third-party account; Cedar decides in process with no network round trip.
  • Nothing goes to MainSDK Cloud before login and opt-in.

See Testing kit and Test your app.

Limits and notes

  • Local drivers arrive with their interfaces: the 0.2 milestone ships local mode for config files, secrets local, policy Cedar and log terminal; the rest come with 0.3.
  • files: local and the getqr.id driver pass the same conformance suite; whether the local driver counts as the second driver for files to be stable is still open.
  • pdf, limits, status, feedback, i18n and geo have no spec yet, so their options are provisional.
  • ai: local is a fake; it does not run a model on your machine.

Swap to or from it

Move from a local driver to a provider with an environment overlay:

main.env.prod.yaml
email:
  driver: resend   # main.env.dev.yaml keeps driver: local

FAQ

Can I run the whole app offline? Yes, for interfaces whose local drivers have shipped. mainsdk dev makes no calls to providers and needs no cloud account.

Is Cedar only for development? No. Cedar is the default production engine for access and policy, running in process.

Why does mainsdk check --env prod fail with MAIN-CHECK-0026? An interface still resolves to a local driver in production. Set a real driver in main.env.prod.yaml.

Next

Open questions

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet