Expand description
NIP-55 “offline” signer — an on-device signer app (Amber) reached over local Android IPC instead of a relay.
This is the third signer mode alongside Local (nsec in the MY_SECRET_KEY
vault) and Bunker (NIP-46 over relays, see crate::signer). A NIP-55
account holds nothing secret on this device — not even the client
keypair a bunker account keeps. Every signing op is a local IPC hop to the
signer app; no network, works offline.
Layering:
Nip55Backendis a platform hook (mirrorscrate::traits::EventEmitter): the Android shell registers the concrete JNI/ContentResolver/Intent impl at startup. vector-core stays Tauri- and Android-decoupled. When no backend is registered (desktop, CLI, tests), every op returns a clean runtime error — never a compile-time platform stub, so a stray shared-call-site reference can’t break the desktop build.Nip55SignerimplementsVectorSigneron top of the hook, so every existingclient.signer()call site (DM seals, Blossom auth, Concord v2 identity ops) uses it agnostically with no changes.
Wire-identity: NIP-55 sign_event / nip44_* produce byte-identical output
to the local path (same NIP-01 id computation, same NIP-44 conversation key),
which is why an Amber account and a local account share communities without
forking. Fail-closed: a signer returning an event authored by the wrong
identity is rejected here, not silently published.
Structs§
- Nip55
Signer - A
VectorSignerthat routes every identity op to an external NIP-55 signer over the platform hook. Cheap to clone (just a pubkey + a session generation snapshot).
Enums§
- Nip55
Error - Failure modes of a NIP-55 signing operation. The three variants map to distinct observable states so the UI can tell “reopen and re-grant” apart from “Amber is gone” apart from “transient hiccup”.
- Nip55
Resolver Outcome - Outcome of a background ContentResolver op — three distinct meanings the caller must keep apart. A null/empty cursor is NEVER a valid empty decrypt.
- Nip55
State - Observable state of the NIP-55 pairing. Backed by an atomic for hot-path
reads; transitions fan out to the frontend as
nip55_stateevents.
Constants§
- VECTOR_
NIP55_ ENCRYPT_ TYPES - Encryption/decryption permissions Vector requests at pairing. These are
granted blanket (no
kind) because Amber only drops akind-less permission when its type issign_event/nip— nip04/nip44 with a null kind are kept. - VECTOR_
NIP55_ SIGN_ KINDS - Event kinds Vector signs, requested per-kind at pairing.
Traits§
- Nip55
Backend - The Android-side transport for a NIP-55 signer. Implemented in the Tauri
shell (JNI + ContentResolver + Intent-for-result); registered once at
startup via
set_nip55_backend.
Functions§
- drain_
nip55_ state - Reset NIP-55 observable state to Idle. Called by
reset_session()on swap so a stale state from the previous account doesn’t leak onto the new one. (The Android shell separately cancels any stranded Intent waiters.) - nip55_
backend - The registered backend, if any.
Noneon platforms without an external signer. - nip55_
is_ installed - Whether an external signer is installed. Returns
Ok(false)on platforms without a registered backend (desktop) rather than erroring — the login screen treats “not installed” and “not supported” the same (hide the button). - nip55_
pair - Run the pairing handshake: fire the
get_public_keyIntent with Vector’s remembered permission set and return the discovered identity pubkey plus the signer’s package name. Blocking (Activity round-trip) — wrapped inspawn_blocking. - nip55_
perms_ json - Render the pairing
permissionsJSON: blanket encrypt/decrypt plus one{"type":"sign_event","kind":K}per signed kind. Never includesget_private_key— same policy ascrate::signer::VECTOR_NIP46_PERMS; the whole point of an external signer is that the identity nsec never leaves it. - nip55_
state - Read the live NIP-55 pairing state. Backed by an atomic; cheap to call.
- set_
nip55_ backend - Register the platform NIP-55 backend. Call once during app startup on
Android. No-op on desktop/CLI (nothing registers), so every op below returns
Nip55Error::Missingthere — the runtime stub. - set_
nip55_ state - Install a new state and fan out a
nip55_stateevent. No-op if unchanged, so per-op confirmation of an already-known state doesn’t spam the UI.