Packaging & signing your app
Once your app compiles, it still has to become something a node can install.
That deliverable is an .mpk bundle: a single archive carrying your
calimero:app component, a manifest.json, and an Ed25519 signature.
This page walks the builder workflow that produces one.
The signing tool is mero-sign (tools/mero-sign/); scripts/pack-component.py
writes the manifest, signs it through mero-sign and packs the archive. The node
side — how a bundle is parsed, verified, and trusted on install — lives in
Upgrades & migration; this page does not duplicate that
detail.
The workflow
Section titled “The workflow”-
Build the component.
calimero-app-buildcompiles your crate forwasm32-wasip2and turns it into acalimero:appcomponent, with the ABI the SDK’s macros embed (itscalimero:abisection):Terminal window scripts/install-mero-app-build.sh # once: puts mero-app-build on PATHmero-app-build apps/my-app --out my-app.wasmA WASM core module (an SDK 0.12 app) is refused by the node (
400 UNSUPPORTED_BUNDLE). -
Generate a signing key (one-time).
mero-sign generate-keycreates an Ed25519 keypair and derives your signer id — adid:keyidentifier built from the public key (the0xed01ed25519-pub multicodec tag plus the 32-byte key, base58btc-encoded under thedid:key:prefix).Terminal window mero-sign generate-key --output my-signing-key.json# Generated new keypair: my-signing-key.json# signerId: did:key:z6Mkrb81NKkv7Mw4dZAWA5PTuwBhbq9u9eCPuby3icBUdirgThe key file is JSON with base64url-encoded
privateKeyandpublicKeyplus the derivedsignerId. Keep it secret and out of version control — it is your update authority (see below). -
Write and sign the manifest, and create the
.mpk.scripts/pack-component.pywritesmanifest.json(version,package,appVersion,minRuntimeVersionand theapp.wasmartifact with its SHA-256 and size), hasmero-sign signsign it, and packsmanifest.jsonandapp.wasminto a gzip-compressed tar.mero-signderives thesignerIdfrom the key, refuses to sign ifminRuntimeVersionis absent (a signer must not rewrite the document it signs), canonicalizes the manifest with RFC 8785 (JCS), signs the domain-tagged SHA-256 of those canonical bytes, and writes asignatureobject back into the manifest.Terminal window python3 -I scripts/pack-component.py --wasm my-app.wasm --package com.example.my-app \--app-version 1.0.0 --key my-signing-key.json --out dist/com.example.my-app-1.0.0.mpk# omit --key to sign with the development keyAn app with compute methods also builds a compute component (
mero-app-build <crate> --out app.wasm --compute-out compute.wasm). Pass it with--compute compute.wasm: the manifest then declares it ascompute, and the.mpkcarries it ascompute.wasm. Without it a release cannot run compute, and neither can a raw.wasmupload. -
Install with
meroctl.Terminal window meroctl --node node1 app install --path dist/com.example.my-app-1.0.0.mpkThe node reads
manifest.jsonstraight out of the archive and verifies the signature before admitting the app. Installing the bundle is what produces an application: the manifest’spackageandsignerIdfix the application id.
The signer key is the update authority
Section titled “The signer key is the update authority”For a bundle, the application id is derived from the package and the
signerId together — not from the bytecode. That makes the application id
version-stable: a new release signed by the same key under the same
package keeps the same application id and is recognized as an upgrade of the
existing app.
The corollary is the rule to remember: a new signed version must use the same signer key. Sign a new release with a different key and the node computes a different application id — it is a different application, not an upgrade, and existing contexts will not pick it up. Treat the signing key as the long-lived identity of your app and guard it accordingly.