MainSDK
Search⌘K
Docs

The check

What mainsdk check verifies, the four layers of code safety, exceptions, exit codes, and how to run it in CI with JSON and SARIF output.

Planned for 0.2

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

mainsdk check is the gate between your agent and main. It runs every registered gate against your code and main.yaml, blocks drift from what main.yaml promises, and blocks anything below the baseline. Every finding carries a stable code, the file and line, and the fix, so your agent can act on it without you.

The check is free, runs on your machine and in your own CI, and works offline.

text
$ npx mainsdk check
mainsdk check · pinecart · env dev · Main 27
 secrets    1 finding
  MAIN-SECRET-0002  critical  app/lib/pay.ts:3
  fix: Move the key to secrets and use secret:STRIPE_KEY
 config   policy   network   data
1 gate failed · exit 1

What it verifies

AreaExamples of what fails
SecretsA key in source, a committed .env, a secret in the browser bundle, a literal in a secret field of main.yaml
ChokepointsA raw database client, raw fetch, axios or node:net outside MainSDK, a storage SDK used directly instead of files
DataA query across regions, an unlabelled personal field, a pan field, SQL built from strings
AccessRelations your access driver can't evaluate, an engine version with a known flaw
NetworkA call to a host not in network.allow or a driver manifest
Sign-inTokens in local storage, an admin role without phishing-resistant sign-in
PrivacyA data label with no retention rule
Production safetyA local driver (SQLite, local secrets, local keys) resolved for prod
Messagingemail.marketing with no unsubscribe link, open or click tracking left on, a real email driver in prod with no bounce webhook, a local SMS or WhatsApp channel in prod, an invalid email or notify section
Escape hatchesEvery raw() use, listed so you know where you bypass guarantees
Main 27Every baseline rule not covered by an open exception
ConfigSchema errors, overlays that weaken a parent, every overlay combination in use

Each interface registers its own gates and check codes; its page lists them, for example secrets. The full list is in check codes.

Layers of code safety

MainSDK checks every change on four layers, and each catches what the one before it cannot:

LayerWhat it checksWhen it runs
RulesDeterministic checks: secrets, chokepoints, types, Main 27 rules, bundled linters and dependency checksAs the agent writes, in milliseconds
IntentAI reviewers ask whether the change does what was asked. Free on your own agent; hosted reviewers are a paid Cloud runAs the agent writes
OutcomesLocked flows in a headless browser at desktop and phone widths, a second user trying to read your data, accessibilityOn every change before main
ProofSigned builds, SBOM, provenance and the Main markOn every change before main

Reviewers for intent use a different model or prompt from the coding agent, so the agent never grades its own work.

Adopting without breaking production

New runtime rules, such as network permissions and policies, can start in observe (log only), move to warn, then enforce, so turning MainSDK on never blocks live traffic by surprise. The modes are explained in Policy as code.

Exceptions

A finding you accept goes in main.yaml with a rule, a reason, an owner and an expiry date. The check passes with the exception recorded, the Main mark shows "Main 27 · 1 exception", and the day after it expires the check fails again. The same list grants a layer the right to weaken an inherited setting; see main.yaml.

main.yaml
exceptions:
  - rule: MAIN-NET-0004
    reason: Partner gateway upgrade booked for November
    owner: platform-team
    expires: 2026-11-30

Exit codes

CodeMeaning
0Passed
1A gate failed
2Usage or configuration error, with a MAIN-CONFIG- code

In CI

The check writes versioned JSON and SARIF 2.1.0, so code scanning shows findings inline on the pull request.

.github/workflows/mainsdk.yml
name: mainsdk
on: [pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    permissions: { contents: read, security-events: write }
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - run: npx mainsdk check --env prod --sarif mainsdk.sarif
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: mainsdk.sarif }
jsonc
// npx mainsdk check --json
{ "version": 1, "project": "prj_01JC8Q4W7ZK3M5N8P2R6T9V0XY", "env": "prod", "passed": false,
  "gates": [{ "id": "secrets", "passed": false, "findings": [
    { "code": "MAIN-SECRET-0002", "severity": "critical", "file": "app/lib/pay.ts", "line": 3,
      "rule": "secrets.no_literal", "fix": "Move the key to secrets and use secret:STRIPE_KEY" } ] }] }

Run with --env prod in CI so local drivers that are fine on your machine fail before production (MAIN-CHECK-0026). --fail-on <severity> sets the lowest severity that fails the run.

Reading a code

Codes look like MAIN-<AREA>-<NNNN>. A code is assigned once and never changes meaning. Explain any code or trace offline (the format of errors your app returns is in Errors):

text
$ npx mainsdk explain MAIN-DATA-0007
MAIN-DATA-0007  Record belongs to another user  (403)
cause  orders.get was called for an order owned by a different user.
rule   data.owner_scope
fix    Scope the query with db.orders.for(session.user).
docs   https://mainsdk.dev/errors/MAIN-DATA-0007

Next

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet