Your first check
Read mainsdk check output, fix a finding with a secret reference, record an expiring exception, check prod settings and run the check in CI with SARIF.
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.
At the end of the quickstart, mainsdk check passed. This page reads that output, breaks it on purpose so you see a finding, then fixes the finding properly and covers the cases you'll meet next: a gap you can't fix yet, production settings, and CI.
The check runs on your machine, free, with no account. MainSDK is pre-release; the commands here are the planned design and ship from 0.2 (see the quickstart).
Run it on the clean app
$ npx mainsdk check
mainsdk check · pinecart · env dev · Main 27
secrets config policy network data
all gates passed · exit 0Every gate that ran has one line, and exit code 0 means a pass. Run it before every pull request.
Break it on purpose
Paste a live-looking key into a helper:
export const stripeKey = "sk_live_51H…";$ npx mainsdk check
mainsdk check · pinecart · env dev · Main 27
secrets 1 finding
MAIN-SECRET-0002 critical app/lib/pay.ts:1
fix: Move the key to secrets and use secret:STRIPE_SECRET_KEY
config policy network data
1 gate failed · exit 1| Part | What it tells you |
|---|---|
| Header | The project, the environment it resolved (dev unless you pass --env) and the baseline |
| Failed gates | Listed first. Each finding has a code, a severity, the file and line, and a fix |
MAIN-SECRET-0002 | A stable code. npx mainsdk explain MAIN-SECRET-0002 prints the cause and fix offline; every code is listed in check codes |
| Passed gates | One line, so you can see what ran |
| Exit code | 0 passed, 1 a gate failed, 2 a usage or configuration error (with a MAIN-CONFIG- code) |
check resolves and tests every overlay combination you use, so a problem that exists only in main.in.yaml or main.env.prod.yaml still shows up.
Fix the finding
Pinecart takes payments with Stripe. Put the key behind a secret: reference in main.yaml and let the pay interface use it on the server:
payments: { driver: stripe }
pay:
stripe: { key: secret:STRIPE_SECRET_KEY, webhook: secret:STRIPE_WEBHOOK_SECRET }import { pay } from "mainsdk";
export function startCheckout(orderId: string, amountInCents: number) {
return pay.checkout({ order: orderId, amount: amountInCents, currency: "USD",
successUrl: "/orders/done", cancelUrl: "/cart" });
}For local runs, the local secrets driver reads values from .mainsdk/secrets.json, which init git-ignored. In production, your secrets driver (Vault, a cloud secret manager, Doppler or Infisical) supplies them. Run npx mainsdk check again; it exits 0, the same as before you broke it.
If the key was ever committed, rotate it with your provider: it is in your git history. See Payments and Secrets.
When you can't fix it yet
Some gaps take time, such as a partner API that still offers only TLS 1.1. Record an exception in main.yaml with the rule, a reason, an owner and an expiry date:
exceptions:
- rule: MAIN-NET-0004
reason: Partner gateway upgrade booked for November
owner: "@pinecart/platform"
expires: 2026-11-30While the exception is open, check passes and your mark reads "Main 27 · 1 exception". The day after it expires, check fails again. See Main 27 and the Main mark.
Check the production settings
npx mainsdk check --env prodThis resolves main.env.prod.yaml and fails with MAIN-CHECK-0026 local driver in production: secrets if, say, secrets.driver is still local. Set a real driver for every interface in main.env.prod.yaml, and the same command passes. Run it before you ship and in CI.
Machine-readable output
npx mainsdk check --json --sarif mainsdk.sarif{ "version": 1, "project": "prj_01JC8Q4W7ZK3M5N8P2R6T9V0XY", "env": "dev", "passed": false,
"gates": [{ "id": "secrets", "passed": false, "findings": [
{ "code": "MAIN-SECRET-0002", "severity": "critical", "file": "app/lib/pay.ts", "line": 1,
"rule": "secrets.no_literal", "fix": "Move the key to secrets and use secret:STRIPE_SECRET_KEY" } ] }] }The JSON is versioned and has a published schema. The SARIF file is SARIF 2.1.0, so GitHub code scanning shows each finding inline on the pull request.
Run it in CI
Gates in your own CI are free on every plan. Check the production settings on every pull request:
name: mainsdk
on: [pull_request]
permissions:
contents: read
security-events: write
jobs:
check:
runs-on: ubuntu-latest
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 }To fail only on serious findings while you adopt, add --fail-on <severity>, for example --fail-on critical.
Next
Was this page helpful?