Skip to main content

Packaging

Packaging turns your solution directory into a single immutable artifact: a canonical JSON document with a SHA-256 checksum and an Ed25519 signature. The registry accepts nothing else.

ADR-003

Installable apps must include a src/ SDK tree (and usually a Dockerfile for hosting: managed). The signed registry package still carries declarative manifest + components (workflows, UI, …); the runnable /qefro process is the container (or external endpoint) bound at install time. qefro solution build / create-app reject packages without src/. See Managed apps.

Build command

qefro solution build .

Output:

checksum: 9f2c1e…
signature: 71ab04…
package: ./dist/package.json

The signed package is written to dist/package.json inside the solution directory.

Assembly steps

  1. Assemble. The manifest, UI definitions, workflow definitions and connector declarations are parsed into one document. Assets are validated (images only) and attached.
  2. Canonicalize. The document is serialized to canonical JSON — sorted keys, compact separators. Canonicalization is the checksum contract: the same content always yields the same bytes.
  3. Checksum. SHA-256 of the canonical bytes becomes the package checksum.
  4. Sign. Ed25519 signs the message id|version|checksum.
  5. Emit. The package document carries:
{
"manifest": { "...": "parsed manifest" },
"components": { "workflows": [], "ui": {}, "assets": [] },
"signature": "hex…",
"signature_kid": "k1",
"publisher_id": "qefro"
}

Signing keys

The CLI loads a signing key from one of:

SourceSetting
Environment variableQEFRO_SIGNING_KEY_HEX — 32 bytes of hex
Keys fileQEFRO_KEYS_FILE pointing to a file containing REGISTRY_PRIVATE_KEY=<hex>

The publisher identity is taken from QEFRO_PUBLISHER_ID (defaults to the built-in publisher UUID). That id must be listed in solution-service QEFRO_PLATFORM_ADMIN_IDS — only platform admins can publish. See Publishing.

warning

The signing key is the root of trust for everything you publish. Keep it out of source control, CI logs and shell history; rotate it through the platform key process, never ad hoc.

Why canonical JSON

  • Deterministic checksums — two builds of identical content produce identical checksums, so the registry can detect any tampering or drift.
  • Reproducible signaturesid|version|checksum binds the identity and content of the release; changing either invalidates the signature.
  • Version immutability — a published version's checksum can never change; fixes ship as new versions. See Publishing.

What does not enter the package

  • Local caches, editor metadata and dist/ itself.
  • Anything outside the solution directory — packages are self-contained.
  • Secrets of any kind; connector credentials are tenant data collected at install time. See Connectors.