Monorepos
Run several MainSDK apps in one repository with a shared main.workspace.yaml, one main.lock, and mainsdk check only on what a change touches.
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.
Share policies, drivers and overlays across several apps in one repository, and check only what a change touches. Each app keeps its own main.yaml; one optional main.workspace.yaml at the root holds what they share. It is configuration only and adds no runtime code to your bundles.
Before you start: a pnpm, npm or Yarn workspace, or a Turborepo or Nx repo, with at least one app that has a main.yaml.
1. Add the workspace file
pinecart/
main.workspace.yaml # shared policies, drivers, overlays
main.lock # one per workspace
policies/
apps/web/main.yaml # Pinecart web app, Next.js
apps/api/main.yaml # Hono API
packages/ui/schema: 1
packages: auto # from pnpm, npm or Yarn workspaces, Turborepo or Nx
policies: [policies/]
providers: { email: resend }
overlays: { order: [market, segment, env] }
network: { default: deny, allow: ["*.pinecart.dev"] }schema: 1
project: prj_01JC8Q4W7ZK3M5N8P2R6T9V0XY
baseline: main27
targets: { web: { framework: nextjs } }
auth: { driver: github }The web app inherits providers.email: resend, the policies and the network list. Packages are discovered from pnpm, npm and Yarn workspaces, Turborepo and Nx. Inheritance runs org, workspace, app, overlays; see main.yaml and overlays.
2. Tighten, never weaken
An app may tighten what the workspace sets. Weakening it needs an exception with an owner and an expiry.
network: { default: allow } # weakens the workspace
exceptions:
- rule: net.default_deny
reason: Partner VPN gateway until the move to a fixed host
owner: "@pinecart/platform"
expires: 2026-11-30Without the exception, check fails and names the weakened key. An expired exception fails like no exception.
3. Verify where each value comes from
npx mainsdk explain config --package apps/web$ npx mainsdk explain config --package apps/web
schema = 1 (apps/web/main.yaml:1)
packages = auto (main.workspace.yaml:2)
providers.email = resend (main.workspace.yaml:4)
network.default = deny (main.workspace.yaml:6)
project = web (apps/web/main.yaml:2)
auth.driver = github (apps/web/main.yaml:5)Each value names the file and line it came from. Add --json for tools.
4. Check only what changed
npx mainsdk check --affected --base origin/develop--affected checks the packages a change touches, the packages that depend on them, and every app when main.workspace.yaml or main.lock changes. Use --package apps/web for one app or --all for everything. sync takes the same flags.
name: mainsdk
on: [pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: pnpm/action-setup@v4
- run: pnpm install --frozen-lockfile
- run: pnpm mainsdk check --affected --base origin/develop5. Keep one lock for every app
npx mainsdk sync --allmainsdk sync --all writes one main.lock at the root, so every app resolves the same contract versions, with hashes. No app folder has its own lock.
lock: 1
interfaces:
email: { range: "^1.2", resolved: "1.4.0" }
drivers:
resend:
version: 2.3.1
implements: ["email@^1.2"]
sha256: "9f2c…"Org-wide defaults for repos outside this workspace live in main.org.yaml.
Next
Was this page helpful?