Public key infrastructure for signing and verifying boundary crossing records. A functional module operating over storage — keys live as crossings in the :pki:keys:{id} namespace, not in a separate key ring. Algorithm-agnostic (RSA, EC).
Wanderland::Activities::PKI is the entire API. Stateless functions over storage. No singleton, no backend abstraction — just a module with extend self whose methods take (storage, ...) and work on whatever storage registry you hand them.
| Method | Purpose |
|---|---|
generate(storage, name, algorithm:) |
Create a new keypair. Idempotent — skips if the key already exists at :pki:keys:{name}. RSA by default, EC supported. |
sign(storage, key_name, payload) |
Sign a String payload, return a base64 signature. Reads the key record from :pki:keys:{key_name}. |
verify(storage, key_name, payload, signature) |
Verify a signature. Returns true/false — does not raise on bad signatures, only on OpenSSL errors. |
demote(storage, key_name) |
Remove sign scope from a key (verify-only). Writes a new key record; append-only history preserved. |
key_exists?(storage, key_name) |
Check if a key is provisioned. |
.to_s on hashes — that's non-deterministic. The caller canonicalizes (sorted keys, JSON.generate) before signing. Crossing#canonical is the canonical canonicalizer for request-time signing..public_key returns a Point, not a verifiable key. Fixed with OpenSSL::PKey.read(private_key.public_to_pem) round-trip.:pki:keys:{name}. Scope demotion appends a new record rather than mutating — the full lifecycle is in the append log. Any storage driver works: in-memory sqlite for dev, persistent sqlite for prod, resolver tags (!SSM, !Vault) can populate :pki: at boot with real key material.Crossing#sign auto-generates a key on first use if none exists. Production provisions real keys at boot; dev/test gets ephemeral keys for free.Crossing#sign(storage) reads the boundary's declared identity, calls Activities::PKI.generate (idempotent) then Activities::PKI.sign over Crossing#canonical. The signature is set on @signature and included in the record's canonical form for future verification.
Context#soft_verify runs crossing.verified?(storage) on append, stashing a boolean in a parallel array. The .signed filter reads that array — forged crossings enter the audit trail but are invisible through trust filters. See wanderland-core-context for the full trust model and wanderland-core-merkle-dag for the chain construction.
wanderland-core/lib/wanderland/activities/pki.rb — the moduleThe earlier OO abstraction (Wanderland::PKI::Backend / PKI::Local) was removed in commit a9cd4f8's followup — it was never actively used once Activities::PKI landed. The storage-backed functional style is the single path.