Skip to main content

Module nip55

Module nip55 

Source
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:

  • Nip55Backend is a platform hook (mirrors crate::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.
  • Nip55Signer implements VectorSigner on top of the hook, so every existing client.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§

Nip55Signer
A VectorSigner that 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§

Nip55Error
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”.
Nip55ResolverOutcome
Outcome of a background ContentResolver op — three distinct meanings the caller must keep apart. A null/empty cursor is NEVER a valid empty decrypt.
Nip55State
Observable state of the NIP-55 pairing. Backed by an atomic for hot-path reads; transitions fan out to the frontend as nip55_state events.

Constants§

VECTOR_NIP55_ENCRYPT_TYPES
Encryption/decryption permissions Vector requests at pairing. These are granted blanket (no kind) because Amber only drops a kind-less permission when its type is sign_event/nip — nip04/nip44 with a null kind are kept.
VECTOR_NIP55_SIGN_KINDS
Event kinds Vector signs, requested per-kind at pairing.

Traits§

Nip55Backend
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. None on 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_key Intent with Vector’s remembered permission set and return the discovered identity pubkey plus the signer’s package name. Blocking (Activity round-trip) — wrapped in spawn_blocking.
nip55_perms_json
Render the pairing permissions JSON: blanket encrypt/decrypt plus one {"type":"sign_event","kind":K} per signed kind. Never includes get_private_key — same policy as crate::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::Missing there — the runtime stub.
set_nip55_state
Install a new state and fan out a nip55_state event. No-op if unchanged, so per-op confirmation of an already-known state doesn’t spam the UI.