Files
bitvid/docs/signing-adapter.md
T

83 lines
3.2 KiB
Markdown

# SigningAdapter guide
`SigningAdapter` is the lightweight interface bitvid uses to sign direct-message
payloads and message metadata without depending on a specific auth provider.
The canonical implementations live in `js/auth/signingAdapter.js`, and callers
such as `NostrClient.sendDirectMessage()` and the DM composer accept a
`signingAdapter` option so they can work with NIP-07 extensions, remote signers,
or test doubles.
## Interface contract
A `SigningAdapter` is an object with the following async methods:
- `getPubkey()` → resolves the hex public key that should be used as the DM
author.
- `getDisplayName()` → optional display name string for UI metadata.
- `signEvent(unsignedEvent)` → returns a fully signed Nostr event object.
- `signMessage(message)` → returns a signature for an arbitrary string.
`signEvent()` is required for DM publishing; `signMessage()` is optional but is
used by the DM composer to attach a signature to the outgoing payload metadata.
## Implementing a new adapter
1. **Normalize the pubkey**: resolve and return a lowercased hex pubkey from
`getPubkey()`. The NIP-07 adapter does this by delegating to
`window.nostr.getPublicKey()` and normalizing the result.
2. **Provide signing primitives**:
- `signEvent()` must return an event with `id` and `sig` populated.
- `signMessage()` should return a stable signature string for the message
body when you need message-level signatures (for example, the DM composer
includes this for UI payloads).
3. **Expose a type label**: include a `type` field (for example `nip07`, `dev`,
or `test`) to make debugging easier.
Reference implementations:
- `createNip07SigningAdapter({ extension })` uses a browser extension signer.
- `createEphemeralDevSigningAdapter()` generates a local, throwaway keypair for
development flows.
- `createTestSigningAdapter()` returns deterministic signatures for tests.
## Testing a SigningAdapter
### Unit tests
For Node-based tests, use `createTestSigningAdapter()` so signatures are stable
and deterministic. You can pass it directly to `NostrClient.sendDirectMessage()`
through the `options.signingAdapter` field to avoid relying on active signers:
```js
import { createTestSigningAdapter } from "../js/auth/signingAdapter.js";
import { nostrClient } from "../js/nostrClientFacade.js";
const adapter = createTestSigningAdapter({
pubkey: "f".repeat(64),
});
const result = await nostrClient.sendDirectMessage(
"npub...",
"hello",
null,
{ signingAdapter: adapter }
);
```
Assertions can then verify that `result.ok` is `true` and that the signed event
was built with the adapter's pubkey and signatures.
### Manual smoke checks
For browser-only checks, wire the adapter into the DM composer by passing it in
as the `signingAdapter` prop. The composer will call `getPubkey()`,
`getDisplayName()`, and `signMessage()` while building the outgoing payload
object so you can inspect the resulting metadata in your handler.
## Usage reminders
- When the signer does **not** implement NIP-44 encryption, NIP-17 sends are
blocked; keep the privacy toggle disabled until encryption is available.
- Always surface errors from `getPubkey()` or `signMessage()` to the caller so
the UI can display a meaningful failure state.