MainSDK
Search⌘K
Architecture

Supply chain

How we build, sign and deliver MainSDK itself, from reproducible SLSA level 3 builds to packs delivered through The Update Framework.

Planned for 0.1

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

We hold our own releases to the bar we ask of every app. Two paths matter: how our packages reach your machine, and how packs and driver metadata reach your running app.

flowchart LR
  subgraph build["Release workflow (only place that publishes)"]
    src["Tagged commit"] --> b["Hosted, isolated build<br/>reproducible"]
    b --> sig["Sigstore signature<br/>keyless, GitHub OIDC"]
    b --> prov["SLSA L3 provenance"]
    b --> sbom["CycloneDX SBOM"]
  end
  sig & prov & sbom --> reg["npm: mainsdk, @mainsdk/*"]
  reg --> you["Your install<br/>verify before use"]
  subgraph tuf["TUF repository"]
    packs["Baseline and market packs<br/>driver metadata"]
  end
  packs -- "mainsdk sync, startup, background" --> app["Your running app"]
Diagram

Our releases

PracticeWhat it means
SLSA level 3Hosted, isolated build with provenance nobody can forge
Reproducible buildsTwo clean builds of the same commit give byte-identical artifacts
SigstoreEvery artifact is signed keyless with the release workflow's identity. An artifact changed by one byte fails verification
SBOMA CycloneDX SBOM with every release
No install scriptsNo published package declares preinstall, install or postinstall, and none needs a native binary: mainsdk is pure TypeScript with zero runtime dependencies (ADR-0066)
Few dependencies, pinnedpnpm-lock.yaml with integrity, GitHub Actions pinned to commit SHAs (Cargo.lock too, once the Rust SDK lands in phase 5)
No laptop publishingOnly the release workflow publishes; never a developer machine
Apache-2.0 and DCOEverything in the app is Apache-2.0; every commit carries a Signed-off-by line, checked in CI

Each release ships these artifacts:

text
mainsdk, @mainsdk/cli,     pure JavaScript tarballs, ESM only, no native binary
@mainsdk/testing
*.sigstore.json            Sigstore bundle per artifact
provenance.intoto.jsonl    SLSA level 3 provenance
sbom.cdx.json              CycloneDX SBOM

Package names stay placeholders until the owner approves them, and the pipeline defaults to a dry run.

Delivery to running apps: TUF

Signing proves who signed a file. It does not stop an attacker who steals one key, replays an old but validly signed pack, hides newer ones, or mixes files from different releases. The Update Framework does (ADR-0005). From milestone 0.4, every baseline pack, market pack and driver metadata file is a TUF target.

TUF roleIn MainSDKDefault expiry
RootOur root of trust, kept offline and signed by a threshold of keys (for example 3 of 5). Rotating it never breaks running apps; they walk the chain of root versions1 year
Targets, with delegationsWe sign baseline and market packs; verified driver publishers get delegated roles limited to their own paths90 days
SnapshotOne consistent set of versions, so files from different releases are never mixed7 days
TimestampShort-lived freshness, so a hidden newer set is detected1 day

Still open: who holds the root keys, where they are stored, the final threshold and the host that serves the repository.

Attacks it refuses

flowchart TB
  f["Fetch metadata and target"] --> r{"Version lower than trusted?"}
  r -- yes --> rb["Refuse: MAIN-TUF-0001 rollback"]
  r -- no --> t{"Timestamp expired?"}
  t -- yes --> st["Keep last good set: MAIN-TUF-0004 stale"]
  t -- no --> s{"Target listed in current snapshot?"}
  s -- no --> mm["Refuse: mix and match"]
  s -- yes --> h{"Bytes match the hash?"}
  h -- no --> bad["Refuse; never handed to the engine"]
  h -- yes --> ok["Load"]
Diagram

A publisher signing outside its delegation is refused too. On expiry or an unreachable mirror, the app keeps its last good verified targets and never blocks traffic. The app verifies TUF metadata in TypeScript on WebCrypto with no dependency; the signing pipeline and CLI helpers use tuf-js.

main.lock records each target's hash, so two clean builds from the same lock load byte-identical packs.

Tenant-signed Fleet policies

Phase 4 (Fleet). Each Fleet tenant gets a delegated TUF role signed only with its own keys, with a threshold (for example 2 of 3, one offline). We cannot forge a tenant's policy: a policy signed with one tenant key, or with our own key, is rejected and the app keeps its last good policy. Fleet sends only signed, declarative policy and data, never code.

Security policy

SECURITY.md states:

  • private disclosure through GitHub security advisories, with CVE numbering;
  • critical issues fixed within 7 days and high within 30, with an advisory for each;
  • a bug bounty live at public beta (platform and budget not chosen yet);
  • a long-term support line of at least 24 months for each baseline.

Later

WhatPhase
Verified developers; MainSDK countersigns passing releases as their Main mark2
Independent security audit of the TypeScript core and SDK, published in fullBefore 1.0, then yearly
Status page for MainSDK Cloud and TUF delivery2

Next

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet