neco-vault
Memory-only signing vault built on neco-secp, for Nostr client apps that need to sign events without exposing secret keys.
It's a higher-level layer: secret keys stay inside the vault and aren't returned by any public API — only signed events, ciphertext, and public keys come out.
A Japanese architecture note is available in ARCHITECTURE-ja.md.
Features
nostr: enable Nostr signing throughneco-secpnip04: enable NIP-04 encrypt/decrypt through the vaultnip44: enable NIP-44 encrypt/decrypt through the vaultnip17: enable gift-wrap DM helpers through the vaultencrypted: encrypted import/export via AES-256-CBC with scrypt-based key derivation (addsaes,cbc,getrandom,scryptdeps)encrypted-legacy-v1: opt-in v1 import compatibility via SHA-256(passphrase)security-hardening: enable optional random delay / dummy operation hardening hookswasm: reserved feature for future browser / wasm integration
Design
The vault never returns plaintext secret keys. All operations that need a secret key run inside the vault; only the result (a signed event, ciphertext, or public key) comes out.
Usage
Basic signing
use ;
use ;
let mut vault = new.unwrap;
let secret = generate.unwrap;
vault.import_plaintext.unwrap;
let signed = vault.sign_event.unwrap;
assert_eq!;
Active account
The first imported account is automatically set as active. Use sign_event_active to sign without specifying a label.
vault.import_plaintext.unwrap;
// "alice" is now active
vault.set_active.unwrap;
let label = vault.active_label; // Some("alice")
let signed = vault.sign_event_active.unwrap;
Removing the active account sets active to None.
vault.remove.unwrap;
assert_eq!;
Use labels() to list all stored account labels.
Encrypted import/export (encrypted feature)
Encrypted export uses scrypt-derived keys and AES-256-CBC. Import accepts only the v2 format by default; older SHA-256-derived v1 blobs don't load unless you enable the encrypted-legacy-v1 feature.
let data = vault.export_encrypted.unwrap;
vault.import_encrypted.unwrap;
NIP-04 / NIP-44 through the vault
let bob = vault.public_key.unwrap;
let payload = vault.nip44_encrypt_active.unwrap;
let text = vault.nip44_decrypt_active.unwrap;
assert_eq!;
Security hardening (security-hardening feature)
SecurityConfig lets callers enable constant-time touch points, random delay, and dummy operations for secret-key use paths.
API
| Item | Description |
|---|---|
SecurityConfig |
Optional hardening flags for secret-key use paths |
VaultConfig |
Cache timeout configuration |
Vault |
Memory-only secret storage and signing entry point |
VaultError |
Vault-level error type |
Vault::import_plaintext |
Import a secret key under a label |
Vault::remove |
Delete an account by label |
Vault::labels |
Return all stored labels |
Vault::set_active |
Set the active account label |
Vault::active_label |
Return the current active label |
Vault::public_key |
Return the xonly public key for a label |
Vault::public_key_active |
Return the xonly public key for the active account |
Vault::set_security_config |
Update runtime hardening settings |
Vault::security_config |
Return current hardening settings |
Vault::sign_event |
Sign a Nostr event with a named account |
Vault::sign_event_active |
Sign a Nostr event with the active account |
Vault::nip04_encrypt, Vault::nip04_decrypt |
NIP-04 vault encryption helpers (nip04 feature) |
Vault::nip44_encrypt, Vault::nip44_decrypt |
NIP-44 vault encryption helpers (nip44 feature) |
Vault::create_sealed_dm, Vault::open_gift_wrap_dm |
NIP-17 vault DM helpers (nip17 feature) |
Vault::export_encrypted |
Export a secret key as AES-256-CBC encrypted bytes (encrypted feature) |
Vault::import_encrypted |
Import a secret key from encrypted bytes (encrypted feature) |
License
MIT