MainSDK
Search⌘K
How-to

Hand a project to a client

End an agency job cleanly. Eject to plain provider code, transfer the project to the client's organisation on free view-only seats, and hand over evidence.

Planned for 0.7

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

At the end of a job you give the client one of two things: code that no longer needs MainSDK, or a project they can read in their own organisation, plus evidence that the app meets the standards you agreed. This page shows the three steps and what each one needs. Plan about an hour for the eject or the transfer, and longer if the client wants an assurance bundle.

What you hand over

What you hand overCommandMilestoneStatus
Plain provider code, no MainSDK dependencymainsdk eject --all0.3Planned, beta. Recipes for email, pay, auth and db are proposed; the final list is not decided.
The project in the client's organisation, with history, policy and launch reportsmainsdk project transfer0.7Planned
Client people reading the project on free view-only seatsmainsdk org invite --role viewer0.7Planned
Signed evidence an assessor can verifymainsdk assure, mainsdk assure verify0.7Planned, beta

You need

  • A clean git working tree and a passing test suite.
  • For the transfer: a Teams organisation, and the client's organisation name.
  • For the evidence: the standards or industry pack the client asked for (for example pci-dss-4.0.1).

1. Leave MainSDK (optional)

Choose this when the client wants no dependency on MainSDK. Plan first, then run it. See Eject.

bash
npx mainsdk eject --all --dry-run
npx mainsdk eject --all

You should see: your tests pass with the same count as before, main.yaml, the gates and every mainsdk and @mainsdk/* dependency are gone, and EJECT-REPORT.md lists each guarantee the client no longer gets. Give the client that report.

Be honest with the client about coverage. The spec says eject recipes cover the reference architecture drivers first, and the interfaces with recipes in 0.3 are still an open question. The proposal is email, pay, auth and db. Any other interface has no recipe yet, so plan to rewrite those calls by hand, or use step 2 instead.

2. Transfer the project to the client

Choose this when the client keeps using MainSDK. The transfer moves the project with its history, policy and launch reports in one step and keeps the project ID. The client's people read it on free view-only seats; client viewers never count as active developers (see Free in the app, paid in the Cloud).

bash
npx mainsdk org invite ana@client.example --role viewer
npx mainsdk project transfer prj_01JC8Q… --to client-co --keep-maintainer
  • --keep-maintainer keeps one maintainer seat for your agency, so you can still fix things.
  • --bill-addons-to client puts any Teams add-ons on the client's invoice instead of yours.

You should see: the project in client-co with its history, the same project ID, and the client's people able to read it without a charge.

3. Hand over evidence

Run mainsdk assure against the standards the client asked for, before you eject or transfer. Running it and reading the results is free.

bash
npx mainsdk assure --standard pci-dss-4.0.1
npx mainsdk assure verify .mainsdk/assure/latest.bundle

You should see: the failing controls with owners and fixes, then a verified signature on the bundle, which holds the results, SBOM, VEX, policy test results, drill records and exceptions. Hand the client the bundle. Whether mainsdk assure still runs after a full eject is not decided yet.

Verify

Before the handover, run the check and save its output with the evidence.

bash
npx mainsdk check --env prod

A pass exits 0. After an eject, run your test suite instead, since the check is gone with MainSDK.

If it fails

CodeCauseFix
MAIN-ASSURE-0003mainsdk assure pointed at a URL found a tampered /.well-known/mainsdk.json (signature invalid, exit 2).Do not hand it over. Rebuild the record from your own repo and run assure again.
MAIN-CONFIG-0004A literal secret in main.yaml, found before you hand the repo over.Move it to a secret: reference and rotate the secret.

Run npx mainsdk explain <code> for the cause and fix offline.

For agents

text
Goal: prepare this project for handover to a client.
Read: docs/guides/eject.md, docs/specs/eject.md, docs/specs/teams-plan.md, docs/specs/assure.md.
Touch only: files that eject rewrites, EJECT-REPORT.md, and .mainsdk/assure/.
Do: run mainsdk assure and keep the bundle; run mainsdk eject --all --dry-run and show the plan to the owner;
run the real eject only after the owner approves; never run project transfer without the owner.
Verify: the test suite passes with the same count, and npx mainsdk assure verify .mainsdk/assure/latest.bundle succeeds.
Never weaken, skip or loosen a check to make it pass.

Next

Open questions

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet