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.
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:
built [email protected]
checksum: 9f2c1e…
signature: 71ab04…
package: ./dist/package.json
The signed package is written to dist/package.json inside the solution
directory.
Assembly steps
- Assemble. The manifest, UI definitions, workflow definitions and connector declarations are parsed into one document. Assets are validated (images only) and attached.
- 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.
- Checksum. SHA-256 of the canonical bytes becomes the package checksum.
- Sign. Ed25519 signs the message
id|version|checksum. - 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:
| Source | Setting |
|---|---|
| Environment variable | QEFRO_SIGNING_KEY_HEX — 32 bytes of hex |
| Keys file | QEFRO_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.
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 signatures —
id|version|checksumbinds 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.
Related topics
- Validation — checks run before assembly
- Publishing — submitting the signed package
- Security — the trust model signatures support