Context
From mesh thread sprout-legion-plan (Sprout ↔ Legion) and hestia PR #12.
Today the hub pins exactly one verifying key per member LCT: MapResolver(HashMap<Uuid, Lct>) keyed by signer_lct_id, and pin_member_key does resolver.insert(lct) (overwrite). verify_envelope verifies every envelope from that member against that one key.
This forces a member that signs from two contexts to use one key across both:
- a non-interactive mesh watcher (systemd
channel_client) that cannot open a passphrase-sealed vault, so it must use a raw on-disk channel key; and
- interactive member-tier ops (
hestia profile push) that could use the sealed vault identity key.
Because only one key can be pinned and the daemon is hard-constrained to the raw channel key, the shared key must be the channel key — so interactive acts must also sign with it. hestia PR #12 resolves this client-side (reconciliation (a)): profile push signs with the pinned member key (the channel key) rather than the sealed identity. That's the right fix for now and needs no hub change.
The (b) option — recorded, not urgent
If a future member-op genuinely needs attestation-grade signing (sealed identity) distinct from delivery/channel signing, the hub would pin two keys per member:
- an identity key — verifies member-ops / attestation acts;
- a channel/delivery key — verifies notify/watch/seal.
Each endpoint declares which role it verifies against; the resolver maps (member_lct, key_role) → pubkey. This is a schema + verify_envelope + MemberKeyPinned-event change (the event needs a role/slot field), plus a migration.
Why it's deferred
A profile update is not a higher-assurance act than notify/watch, and anyone holding the raw channel key can already act as the member on the mesh — so today (b) buys little over (a). Filing so the design intent isn't lost: adopt (b) only when an act appears that must be signed by the sealed identity while delivery stays on the channel key.
Refs
🤖 Generated with Claude Code
Context
From mesh thread
sprout-legion-plan(Sprout ↔ Legion) and hestia PR #12.Today the hub pins exactly one verifying key per member LCT:
MapResolver(HashMap<Uuid, Lct>)keyed bysigner_lct_id, andpin_member_keydoesresolver.insert(lct)(overwrite).verify_envelopeverifies every envelope from that member against that one key.This forces a member that signs from two contexts to use one key across both:
channel_client) that cannot open a passphrase-sealed vault, so it must use a raw on-disk channel key; andhestia profile push) that could use the sealed vault identity key.Because only one key can be pinned and the daemon is hard-constrained to the raw channel key, the shared key must be the channel key — so interactive acts must also sign with it. hestia PR #12 resolves this client-side (reconciliation (a)):
profile pushsigns with the pinned member key (the channel key) rather than the sealed identity. That's the right fix for now and needs no hub change.The (b) option — recorded, not urgent
If a future member-op genuinely needs attestation-grade signing (sealed identity) distinct from delivery/channel signing, the hub would pin two keys per member:
Each endpoint declares which role it verifies against; the resolver maps
(member_lct, key_role) → pubkey. This is a schema +verify_envelope+MemberKeyPinned-event change (the event needs a role/slot field), plus a migration.Why it's deferred
A profile update is not a higher-assurance act than notify/watch, and anyone holding the raw channel key can already act as the member on the mesh — so today (b) buys little over (a). Filing so the design intent isn't lost: adopt (b) only when an act appears that must be signed by the sealed identity while delivery stays on the channel key.
Refs
legion-to-sprout-reconcile-decision-a-sign-member-acts-with-channel-key-2026-07-05.md🤖 Generated with Claude Code