Connect+Documentation
Extensions

Packaging & trust

Trust levels, developer mode, signing a .cxt, updates, and Add-to-Connect deep links.

Every catalog extension has a trust level, decided by its signature — and any inconsistency refuses to load rather than "degrading to dev".

Trust levels

LevelMeaning
first-partySignature verifies and the signer is the embedded ARW root key. Unlocks first-party-only surfaces (e.g. the worker substrate).
signedSignature verifies under some other key. If the manifest pins a key, the signer must equal it.
devUnsigned. Loads only when Developer mode is on.

Any inconsistency — bad signature, tampered / extra / missing file, a key pin that doesn't match the signer — refuses to load entirely. Verification runs at catalog scan and again on every per-profile load.

Developer mode (unsigned, iterate fast)

  1. Extensions → Developer mode (on).
  2. Load unpacked… → pick your folder (loaded in place; edits are live).
  3. Reload re-reads the manifest + assets and restarts the instance (grants kept). A broken manifest surfaces as a clear failure.
  4. Remove fully unloads it.

With Developer mode off, only signed packages load, and running dev instances are torn down.

Packaging: .cxt

A .cxt is a ZIP of a signed extension tree. The signature covers a canonical file-hash manifest (content, not archive bytes), so one signature validates a dev folder and a .cxt identically.

Signing CLI

# Generate an Ed25519 signing pair (keep the .pem out of the repo):
node scripts/connect-sign.cjs keygen ./key.pem

# (Re)sign a tree — run after ANY file change (a stale signature refuses to load):
node scripts/connect-sign.cjs sign ./com.example.hello ./key.pem

# Pack a signed tree into a .cxt:
node scripts/connect-sign.cjs pack ./com.example.hello ./hello.cxt ./key.pem

Install via Extensions → Install from file…, or over a deep link (below).

Updates

Declare both key (the pinned signer = update anchor) and updateUrl in the manifest. The updateUrl returns a small JSON update manifest:

{ "id": "com.example.hello", "version": "1.1.0",
  "package": "https://cdn.example.com/hello-1.1.0.cxt",
  "sha256": "<hex sha-256 of the .cxt bytes>" }

(or { "extensions": [ …that shape… ] } so one URL serves many ids). When a newer version is advertised, the browser downloads the .cxt, checks its bytes against sha256, then installs it through the signed-package gate — which enforces the same id signed by the same key (no silent key rotation). Grants and stored data are preserved — an update is not a reinstall. Users trigger this from Extensions → Check for updates.

A store's "Add to Connect" button opens:

connect://install?src=<https url to the .cxt>&sha256=<hex of its bytes>

The desktop downloads, integrity-checks, and verifies the package into the catalog, then opens the manager so the user completes the per-profile install + consent. The desktop re-verifies every package on install — the store is only a distribution layer, never the trust root.

On this page