ADR-161 honestly relabelled the manifest's wasm_module_hash / wasm_module_sig /
publisher_key as "(P4 — not yet enforced)" and the homecore_permissions claims
as deferred P5 authority isolation. This makes both real and tested.
P4 (signature/integrity verification, SECURITY):
- New `verify` module: SHA-256 module-hash check + Ed25519 signature
verification over the digest against publisher_key, with a PluginPolicy
trust allowlist and an explicit AllowUnsigned dev escape hatch (loud warn).
Secure default rejects unsigned / unknown-publisher / tampered modules.
- Reuses the in-repo cog-ha-matter::witness_signing Ed25519 pattern; sha2 is a
workspace dep, ed25519-dalek/hex/base64 already in the lock — no new external
dep tree (only new edges in homecore-plugins).
- WasmtimeRuntime::load_plugin verifies before instantiation; legacy load_wasm
retained for trusted/test modules.
P5 (authority/capability isolation, SECURITY):
- New `permissions` module: PermissionSet distilled from homecore_permissions
(state:write:<glob> or bare entity glob). hc_state_set now consults it and
returns a typed -3 to the guest on an undeclared write (no host panic).
Tests (fail on old code, which had no load_plugin/verify and an unchecked
hc_state_set): tampered module rejected; valid sig from trusted key loads;
valid sig from untrusted key rejected; unsigned rejected by default and loads
only under AllowUnsigned; light.* plugin writes light.kitchen but is denied
lock.front_door; no-permission plugin can write nothing. Real deterministic
keypair signs real bytes.
Manifest doc updated: P4/P5 now ENFORCED (was "not yet enforced").
homecore-plugins --features wasmtime: 32 passed (lib 23, integration 9), 0 failed.
Co-Authored-By: claude-flow <ruv@ruv.net>