Signed manifest
The on-wire format the browser, CLI, and cloud all verify.
Wire shape
The signed manifest is a CBOR-encoded envelope:
SignedManifest = {
body: bytes, # the CBOR-encoded Manifest body (verbatim)
sig: bytes, # raw Ed25519 signature over `body`
}The signature covers the exact CBOR bytes the signer produced, not
a re-encoded copy. This is intentional: different CBOR encoders can
produce subtly different byte sequences for the same logical object,
and a re-encoded verifier wouldn't agree with the signer. Carrying
body as opaque bytes makes the wire format immune to that drift.
Manifest body fields
Manifest = {
v: u32, # schema version (currently 1)
id: string, # pod id
name: string,
persona: string,
system_prompt: string,
model: string,
owner_pubkey: string (hex), # auto-filled from --key
transport: {…}, # mirrors pod.toml's [transport] section
tools?: [{ name, description }, …],
}Where it lives in a packed .html
Two base64-inlined globals:
<script>
window.__GEMMAPOD_MANIFEST_B64 = "…"; /* base64(SignedManifest CBOR) */
window.__GEMMAPOD_WASM_B64 = "…"; /* base64(gemmapod_core_bg.wasm) */
</script>
<script>/* @gemmapod/embed IIFE inlined here */</script>
<script>GemmaPod.boot(document.getElementById("pod"));</script>boot() reads both globals, initialises the WASM core, calls
GemmaPodCore.verifyManifest(b64ToBytes(__GEMMAPOD_MANIFEST_B64)), and
either mounts the runtime or shows a visible red error box.
Verification API
The same WASM is built for both web (pkg/) and node (pkg-node/)
targets so the verifier code path is byte-identical everywhere.
// JS / TS
import init, { GemmaPodCore } from "@gemmapod/core/web";
await init({ module_or_path: wasmBytes });
const manifest = GemmaPodCore.verifyManifest(signedBytes);
// throws on bad signature; returns Manifest on success.The Rust API surface:
GemmaPodCore::testing::verify(&bytes) -> Result<Manifest, String>
GemmaPodCore::testing::sign(manifest, &secret_hex) -> Vec<u8>
GemmaPodCore::testing::generate_key() -> (pubkey_hex, secret_hex)
GemmaPodCore::testing::sign_bytes(payload, &secret_hex) -> Vec<u8>
GemmaPodCore::testing::verify_bytes(payload, signature, &public_hex) -> Result<(), String>Multi-side verification
Three places verify the same bytes via the same code:
- Browser shim — before mounting any UI.
@gemmapod/toolkit— before writing the.htmlartefact (round-trip check).@gemmapod/signal— before storing a blob uploaded viaPOST /pods.
Adding a fourth verifier means using the same WASM artefact, not re-implementing the parser.