NIP-46 decrypted bridge
The nip46-decrypted-bridge-v0 contract defines what the companion
does with an already-decrypted NIP-46 payload. The bridge is
intentionally narrow: it does not implement relay sessions, does not
do encryption, and does not derive keys.
What the bridge accepts
The companion accepts a decrypted NIP-46 payload as JSON. The bridge:
- Validates the payload against the NIP-46 contract: it is a
sign_eventrequest,get_public_key, or another supported method. - Maps it to the equivalent v0 nSealr signing request — or, if the request shape isn’t supported, returns a deterministic non-signing decision with a typed reason.
- Returns the decision to the calling companion surface. Routing, relay sessions, and signer I/O stay outside this narrow bridge contract.
$ nsealr nip46 decide --message decrypted.json --permissions sign_event:1 --out decision.json
# decision.json contains a sign_event conversion or a deterministic refusal
or:
$ nsealr nip46 decide --message unsupported-method.json --permissions sign_event:1 --out decision.json
⊘ deterministic non-signing decision: UNSUPPORTED_METHOD
What it does not do
| Out of scope | Why |
|---|---|
| Open a relay session | Encrypted relay sessions are upstream of the secretless companion |
| Decrypt NIP-46 envelopes | Decryption requires keying material; the companion is secretless |
| Sign anything itself | The companion never holds keys |
| Implement remote signer logic | That’s the signer’s job |
Per-signer policy
The bridge produces standard v0 signing-request decisions for the
calling companion surface. The signer then applies its policy
(scoped-policy-automation-v0 on the USB/NIP-46 line, manual-only on
QR vaults, external review acknowledgement on smartcards). The bridge
does not enforce policy on its own.
Deterministic non-signing decisions
When the bridge cannot convert or allow a request, it returns one of a fixed enumeration. The caller can rely on these as typed values.
| Decision | When |
|---|---|
UNSUPPORTED_METHOD | The NIP-46 method is not part of the v0 surface |
BAD_PAYLOAD_SHAPE | The decrypted JSON doesn’t match the NIP-46 schema |
IMPLEMENTATION_LIMIT | Request exceeds shared v0 implementation limits |
POLICY_DENY_BY_DEFAULT | No explicit allow rule covers this request |
SIGNER_UNREACHABLE | No active signer route can be reached now |
A bridge that returns one of these has not signed and will not sign. The decision is final for the request id; the caller must build a fresh request to try again.
See also
- Transports overview — how the bridge sits alongside QR, serial, and smartcard.
- ESP32 USB/NIP-46 signer page — the primary route for bridged requests.
nSealr/specsundervectors/nip46/— conformance vectors for the bridge.