Security model
How MainSDK keeps keys out of code, limits who your app talks to, redacts what it writes and signs what it ships and delivers.
MainSDK's security rests on one rule: if it touches data, the network, money, messages, AI or identity, it goes through MainSDK. Every sensitive call then has one place where it is checked, decided and recorded. This page covers the parts that protect your app as a whole.
Secrets are references
Keys never appear in code or in main.yaml. You refer to them as secret:NAME, and the secrets driver resolves them on the server at startup, from a cloud key service, Vault, Doppler or Infisical, preferring workload identity over static tokens.
secrets:
driver: vault
vault: { address: https://vault.pinecart.internal, auth: workload-identity }
db:
driver: postgres
regions: { us: secret:DATABASE_URL_US, eu: secret:DATABASE_URL_EU }
files:
driver: getqr
key: secret:GETQR_API_KEYimport { secrets } from "mainsdk"; // server only: a client import fails the build
const token = await secrets.get("PARTNER_API_TOKEN");The check fails on a key pattern in source (MAIN-SECRET-0002), a committed .env other than .env.example (MAIN-SECRET-0003), a secret in the browser bundle (MAIN-SECRET-0004), a client import of secrets (MAIN-SECRET-0001) and a literal in a secret field of main.yaml (MAIN-CONFIG-0004). Resolved values are marked for redaction everywhere. See secrets.
Network permissions
Like a phone app's declared permissions, your app lists the destinations it may reach. Anything else is blocked and reported.
network:
allow:
- 10.20.0.0/16 # a range
- google.com # exactly this domain
- "*.pinecart.dev" # its subdomains only
default: deny
mode: enforce # observe | warn | enforce- Drivers declare their own hosts, so the list holds only your destinations.
- Enforced in the SDK's network layer for every outbound call. DNS answers are checked (no rebinding to private ranges), and redirects to undeclared hosts are refused (
MAIN-NET-0003). mainsdk checkblocks raw sockets, other HTTP clients and directfetchoutside the SDK at build (MAIN-NET-0001).- A poisoned dependency that sends secrets to a new domain fails in CI and is blocked and reported in production (
MAIN-NET-0002). mainsdk network export --to vercel|k8s|aws-sg|cloudflarewrites the same list as platform rules for a second, independent wall.
Network permissions run inside your app and are free on every plan. See network.
Redaction before anything is written
Redaction runs in process before any log, error or export:
- values of fields labelled
personal,sensitive,childorphi(see data labels); - anything resolved from a
secret:reference; - known key formats, such as
msk_live_andmsk_test_.
By default MainSDK doesn't collect request or response bodies, cookies, or authorization, cookie and set-cookie headers. User IDs are pseudonymised with a keyed hash per project before export, and IP addresses are trimmed (IPv4 /24, IPv6 /48). The telemetry contract telemetry@1 lists every field collected and its purpose; a field not in it is never exported.
2026-10-25T10:14:03Z ERROR MAIN-DATA-0007 orders.get for another user's order
user.id=h_5c1a… email=[redacted:personal] key=[redacted:secret]Problem details returned to end users never include file, line or stack in production; those stay in your logs. See Errors.
The audit log
Every policy decision, sign-in, key use and erasure is written to an append-only, tamper-evident audit log in OCSF. Each record carries the hash of the previous one, so log.audit.verify() detects any edit, deletion or reorder. The audit log is never sampled.
Signed releases and safe delivery
| What | How it's protected |
|---|---|
| MainSDK's own packages | Sigstore signatures, SLSA level 3 provenance, reproducible builds, an SBOM, no install scripts, few dependencies pinned by hash |
| Your releases | Signed builds and SBOM per release; the Main mark records provenance at /.well-known/mainsdk.json |
| Driver versions | Resolved into main.lock with SHA-256 hashes |
| Baseline packs, market packs, driver metadata | Delivered through The Update Framework (TUF): an offline threshold root, snapshot and timestamp roles, so a replayed old pack (MAIN-TUF-0001 rollback) or a hidden newer one is refused |
When delivery metadata expires and the mirror is unreachable, your app keeps its last good pack and logs MAIN-TUF-0004 stale. It never blocks a request; see Never in the request path. Delivery internals are in Supply chain.
Sign-in and admin access
Passkeys are the default for your users; passwords are a fallback you can turn off. Admin roles must use passkeys or SSO with phishing-resistant MFA, or the check fails. See auth.
Access to production through MainSDK Cloud (live tail, debug sessions, Fleet critical actions) needs SSO and MFA, plus step-up authentication, and every use is audited. MainSDK never offers a remote shell or debugger into production.
Disclosure and support
Critical vulnerabilities are fixed within 7 days and high within 30, with advisories and CVEs. Each baseline has a long-term support line of at least 24 months. Every interface and driver class has a published STRIDE threat model, versioned with its contract. How these pieces fit together inside MainSDK is in Security architecture.
Next
Was this page helpful?