mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-30 07:37:38 +00:00
The 33 descriptions (~16k chars, ~19k with paths) overran Codex's skill-list budget (2% of the context window, or 8,000 chars when unknown), and a review found most bodies stale: paths from before the commons/commonsUI moves, the deleted legacy desktop app, a callback signer API that no longer exists, and example APIs that were never in the tree. Kept, rewritten and checked against the code (every path, class and command): account-state (+feeds), amy-expert, android-expert, auth-signers, compose-expert (+API style, Navigation 3 back stacks), desktop-expert (+run/package), event-store-semantics (+NIP-50 search), find-missing- translations, kotlin-coroutines (+structured concurrency, flow modeling), kotlin-multiplatform (+Gradle, composable extraction), ngit-pr, nip85-trusted-assertions, nostr-expert (+NIP lookup, generated NIP catalog), relay-client. Dropped: textbook Compose/Kotlin technique skills, kotlin-expert, kotlin-types-value-class, gradle-expert, quartz-integration (external-facing; see the Quartz docs commit), find-non-lambda-logs (audit finished; the rule is now in AGENTS.md Kotlin Style), and the /nip, /desktop-run, /extract commands. The skill list is now ~4k chars. Also drops .claude/core-skills-plan.md (stale history) and repoints SearchableEvent's KDoc to event-store-semantics. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
2.7 KiB
Markdown
56 lines
2.7 KiB
Markdown
# NIP-46 remote signer (bunker)
|
|
|
|
A bunker holds the user's key and signs on request. Amethyst talks to it with kind-24133
|
|
`NostrConnectEvent`s, NIP-44-encrypted between a local **transport** keypair and the
|
|
bunker's pubkey.
|
|
|
|
## Layout (`quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip46RemoteSigner/`)
|
|
|
|
- `BunkerRequest*.kt` — `Connect`, `GetPublicKey`, `GetRelays`, `Ping`, `Sign`,
|
|
`Nip04Encrypt/Decrypt`, `Nip44Encrypt/Decrypt`, `Invalid`; `BunkerRequestParser.kt`.
|
|
- `BunkerResponse*.kt` — `Ack`, `Event`, `Encrypt`, `Decrypt`, `PublicKey`, `GetRelays`,
|
|
`Pong`, `Error`.
|
|
- `NostrConnectEvent.kt` (kind 24133), `NostrConnectURI.kt`, `BunkerMessage.kt`,
|
|
`kotlinSerialization/`.
|
|
- `signer/` — the client side: `NostrSignerRemote` (the `NostrSigner`),
|
|
`RemoteSignerManager` (request/response correlation), per-method `*Response` parsers,
|
|
`SignerResult`.
|
|
- `server/` — the bunker side Quartz can also run: `NostrConnectSignerService`,
|
|
`BunkerRequestProcessor`, `Nip46RequestAuthorizer`.
|
|
|
|
## Connection
|
|
|
|
```
|
|
bunker://<bunker-pubkey>?relay=wss://…&secret=…
|
|
-> NostrSignerRemote.fromBunkerUri(uri, transportSigner: NostrSignerInternal, client)
|
|
-> remote.connect() // BunkerRequestConnect(remoteKey, permissions, secret, clientMetadata)
|
|
-> remote.getPublicKey() // binds the user identity
|
|
```
|
|
|
|
`fromBunkerUri` takes optional `permissions` and `clientMetadata`; the constructor also takes
|
|
`onAuthUrl` for NIP-46 `auth_url` challenges (surface the URL; the request keeps waiting on
|
|
the same id).
|
|
|
|
## Identity
|
|
|
|
The constructor's `signer` is the transport key, **not** the user. `NostrSignerRemote.pubKey`
|
|
returns the identity resolved by `getPublicKey()` or set with `bindUserPubkey(...)` (persisted
|
|
accounts, the CLI's stored identity), and falls back to the transport key until then. Anything
|
|
that encrypts to self or checks authorship must use `pubKey` after binding. Transport plumbing
|
|
(response `p` filter, request addressing) uses `signer.pubKey`/`remotePubkey` on purpose.
|
|
|
|
## Request lifecycle (`RemoteSignerManager.launchWaitAndParse`)
|
|
|
|
Each call creates a request with a random id, publishes it to the bunker relays
|
|
(`client.publish(event, relayList)`), parks a channel in a `LargeCache` keyed by id, and waits
|
|
up to `timeout` (65 s). No answer → one retry with a fresh id after 2 s → then
|
|
`SignerResult.RequestAddressed.TimedOut`, which `NostrSignerRemote` throws as
|
|
`SignerExceptions.TimedOutException`. Concurrent requests never interleave because responses
|
|
route by id (`newResponse`).
|
|
|
|
## Gotchas
|
|
|
|
- Show progress for long waits; approval can take tens of seconds.
|
|
- Only Nostr relays carry the traffic — a feature that can sign offline cannot with a bunker.
|
|
- `signPsbt` is not implemented for remote bunkers (`UnsupportedMethodException`).
|