PTWM
Guides

Trust and security

How PTWM decides whether to load an extension — keyring, capabilities, and the threat model.

PTWM refuses to load an extension unless its signature chains to a key the user has explicitly trusted. This page covers the trust state machine, the policy controls, and the threat model.

The bundled-then-pinned model

On a fresh install, PTWM ships with no trusted keys at all. Running:

ptwm trust add --bundled

imports the curated bundled keyring (a snapshot of "official" contribution authors) and pins its blake3 hash to $XDG_CONFIG_HOME/ptwm/trust/bundled.lock. From that moment on, opening a .ptwm file referencing one of those contributions succeeds.

If a subsequent PTWM upgrade ships a different bundled keyring — a new author added, a compromised key revoked — the on-disk lock no longer matches. The reader treats the bundled keyring as inactive until the user explicitly accepts the new state:

ptwm trust update --bundled

update --bundled shows you the diff (added / removed keys) before applying it. This means a PTWM upgrade can't silently grant a new publisher the ability to ship code into your loader — that step always requires consent.

Three kinds of trust entry

Per ptwm trust list, your keyring may hold:

  • author_key — trust everything signed by this Ed25519 public key. The most common entry. Use for "I trust everything this author ships".
  • contribution_hash — pin trust to a single canonical id (a specific contribution at a specific version). Use for one-off approvals where you don't want a key to grant transitive trust.
  • key_with_capability_constraints — trust this author only for contributions whose declared capabilities are a subset of an allowed list. Use for "trust author X but only their pure codecs, not anything that declares host_imports".

Add entries with ptwm trust add; remove with ptwm trust remove <identifier>; inspect with ptwm trust show.

Capabilities

Every contribution declares its requirements in a capability map that's signed alongside the manifest. The v1 baseline keys:

KeyMeaning
determinismPromises byte-identical output across runs / flavors.
native_depsLists <library>=<version-constraint> requirements.
host_importsWASM host functions the module needs imported.
hardware_classcpu / cuda / rocm / mps.
sandbox_classpure_decode / read_only_filesystem / network.
mem_factorMultiplier for the default 256 MiB WASM memory budget.
fuel_factorMultiplier for the default 100M fuel budget.

A capability the host doesn't know is denied at verify time, not silently accepted. This means a future PTWM upgrade introducing a new capability key can't be exploited by replaying an old extension that declared the new key — the contribution authors must re-sign.

Policy files

Capabilities + canonical IDs feed into the policy file that ptwm bench compress --variant consumes. The TOML schema is the one parsed by PolicyFile::from_toml and resolved against the active keyring by ResolvedPolicy. Example:

[allow]
extra = [
    "blake3:5c3a4cd0c46825ae6dbb471459e63347ea6e1d62c8a0dbfdf5d532a124525f09",
]

[ignore]
extensions = [
    "blake3:da39a3ee5e6b4b0d3255bfef95601890afd80709…",
]

[per_role.scale]
deny = ["blake3:<canonical-id>"]

[capabilities]
allow_host_imports = []   # WASM modules with host_imports declared → rejected
deny_native_deps_unverified = true

The resolved policy enters the encoder via CompressionConfig.from_resolved_policy(rp) and constrains the dispatcher's trial-encode menu per-plane. An empty intersection (the policy excludes every codec a plane can use) is a hard InvalidContainer error — silent identity fallback would falsify ablation numbers.

ContributionUntrusted at decode

When ContainerReader::open encounters an extension-table entry referring to a non-builtin canonical id, it:

  1. Looks up the matching installed bundle on disk ($PTWM_EXTENSION_PATH or $XDG_DATA_HOME/ptwm/extensions/).
  2. Recomputes blake3(manifest ‖ binaries) from the on-disk files.
  3. Verifies the bundle's signature.bin against the author key from the manifest.
  4. Confirms the author key is in the active keyring (or that the canonical id is pinned via contribution_hash).

Failure at any step surfaces as a ContributionUntrusted error with a specific reason. The reader does not silently downgrade; tampered binaries fail loudly. (Verified by the open_verifies_signed_bundle_end_to_end test in crates/ptwm-core/src/container.rs.)

If you genuinely want to inspect a container that references an untrusted extension (e.g. listing tensor names without executing codec code), pass skip_missing=True to Decompressor. Decoding any tensor that needs the untrusted extension still fails.

Threat model in two paragraphs

Assets: the user's signing keys, the on-disk extensions directory ($XDG_DATA_HOME/ptwm/extensions/), and the integrity of decompressed tensor bytes flowing into a training/inference loop.

Mitigations: WASM extensions execute in a Wasmtime sandbox with no host imports granted by default, finite fuel, finite memory, and no filesystem or network access. Native cdylib extensions have no sandbox — their security boundary is the signing key, which is why the verifier always checks the on-disk binary hash, not just the header signature. The trust state is never updated silently: every change requires an explicit ptwm trust add/update/remove invocation. The bundled keyring is treated as untrusted on hash mismatch, so a malicious PTWM upgrade can't graft new publishers into your loader.

Out of scope: confidentiality of compressed weights (use filesystem-level encryption), denial-of-service via deliberately slow extensions (use fuel_factor to bound), and side-channel attacks against the signing key (use a hardware token).

For researchers: keep ptwm trust add --bundled plus a single local --key for your own development. Re-sign on every commit; the v1 CLI doesn't ship a gen-key subcommand, so generate the 32-byte raw seed yourself (openssl rand 32 > key.priv) and re-feed it to ptwm ext sign --key key.priv. Key rotation is "generate a new seed, add the new public key with ptwm trust add --key, then remove the old entry with ptwm trust remove <label>".

For operators shipping models with custom codecs: don't ship the private key with the model. Sign at release time, distribute the public key out-of-band (e.g. in the model card), and let downstream users explicitly ptwm trust add --key …. The bundled keyring update flow makes this audit-trail-friendly.