How MainSDK works
The four layers under every MainSDK call, and what the SDK in your app, the CLI and MainSDK Cloud each run and where.
MainSDK is a library you import, a CLI you run, and an optional Cloud. Every call your app makes to an interface, for example email.send(), passes through the same four layers inside your process. Nothing runs beside your app: there is no sidecar, daemon or extra service, and no native binary to install (ADR-0001, ADR-0066).
Four layers under every call
flowchart TB
app["Your code<br/>email.send(), db.orders.for(user), pay.checkout()"]
subgraph sdk["Inside your app process"]
iface["1. Interface<br/>one versioned contract, e.g. email@1.4"]
guar["2. Guarantees<br/>unsubscribe link, owner scope, redaction, idempotency"]
pol["3. Policy engine<br/>policy.decide(), Cedar by default, in process"]
drv["4. Driver<br/>resend, ses, postgres, getqr.id …"]
end
prov["Provider API"]
app --> iface --> guar --> pol --> drv --> prov
pol -. decision .-> audit[("Audit log<br/>OCSF, hash-chained")]| Layer | What it does | Example |
|---|---|---|
| Interface | The contract you call. Written once in TypeSpec, versioned like email@1.4 | email.marketing({ to, template }) |
| Guarantees | Rules built into the contract, so the unsafe version is hard to write | email.marketing() won't compile without an unsubscribe link |
| Policy engine | The single decision point. Every interface asks it before acting | Can this user run orders.update? May this call reach api.resend.com? |
| Driver | Talks to the provider. Swapping one is a line in main.yaml | providers: { email: resend } |
Changing the provider changes only the bottom layer:
providers:
email: resend # was ses: change this line, no code change
fallback:
email: [resend, ses] # fail over if the first is downEvery interface also has an escape hatch, raw(), that returns the provider's own client. Each use is logged, listed by mainsdk check and marked as bypassing that interface's guarantees.
App, CLI and Cloud
flowchart LR
subgraph you["Your machine and CI (free)"]
cli["mainsdk CLI<br/>init · sync · check · dev · info · logs"]
repo["Repo<br/>main.yaml · policies/ · main.lock"]
end
subgraph run["Where your app runs (free)"]
sdk["mainsdk SDK<br/>interfaces, guarantees, policy engine, drivers"]
end
cloud["MainSDK Cloud (paid plans, phase 2)<br/>history, team views, debug loop"]
tuf["TUF delivery<br/>baseline and market packs, driver metadata"]
cli --> repo
repo --> sdk
tuf -. signed packs, background .-> sdk
sdk -. buffered telemetry, after login and opt-in .-> cloud| Part | Where it runs | What it does |
|---|---|---|
SDK (mainsdk) | Inside your app | Runs every call through the four layers. Never waits on our Cloud |
CLI (mainsdk) | Your machine and your CI | init writes main.yaml, sync plans changes as a pull request, check runs every gate, dev runs offline on local drivers, info shows what an app is and does |
| Cloud | Our regions: US, EU, India (phase 2) | Hosted history, team views, live tail, debug sessions, replay, hosted triage |
The dotted lines are never in the request path. Packs refresh in the background, and telemetry is buffered and dropped when the Cloud is unreachable. See request lifecycle.
What is free and what is paid
What runs inside your app and your CI is free and open source under Apache-2.0: every interface, guarantee, driver, check and the policy engine. Only what we run in our Cloud is paid, and security is never metered (ADR-0007). Plans and limits are on free and Cloud.
Next
- Request lifecycle: what happens on one call.
- Core and language SDKs: the pure TypeScript core, and the native Rust, Python and Go SDKs that share its contracts, not its code.
- The check pipeline: what runs before a change reaches main.
Was this page helpful?