Files
amethyst/.claude/skills/auth-signers/references/nip55-android-signer.md
Vitor PamplonaandClaude Opus 5.5 395af4dc9c chore(skills): consolidate 33 skills into 14, verified against the tree
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>
2026-10-11 12:21:01 -04:00

42 lines
2.4 KiB
Markdown

# NIP-55 Android external signer
Another Android app (Amber, …) holds the key; Amethyst asks it through `Intent`s
(foreground, with UI) or its `ContentProvider` (background, pre-approved). Android only.
## Layout (`quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip55AndroidSigner/`)
- `api/CommandType.kt` — `sign_event`, `nip04_encrypt`/`decrypt`, `nip44_encrypt`/`decrypt`,
`get_public_key`, `decrypt_zap_event`, `derive_key`, `sign_psbt`.
- `api/foreground/` — `IntentRequestManager` (id → channel, launches the intent, waits
`foregroundApprovalTimeout` = 30 s) and `intents/requests|responses|results/` per command.
- `api/background/queries/` — one `ContentResolver` query per command (`SignQuery`,
`Nip44EncryptQuery`, `LoginQuery`, …).
- `api/permission/` — `Permission` + its serializers (the permissions requested at login).
- `client/` — `NostrSignerExternal(pubKey, packageName, contentResolver)` (the `NostrSigner`),
`ExternalSignerLogin` (`createIntent(permissions, packageName)`, `parseResult(intent)`),
`IActivityLauncher` (`registerForegroundLauncher`, `unregisterForegroundLauncher`,
`newResponse`, `hasForegroundActivity`), `IsExternalSignerInstalled.kt`
(`isExternalSignerInstalled(context)`, `getExternalSignersInstalled(context)`),
`handlers/BackgroundRequestHandler` + `ForegroundRequestHandler`.
- `SignString.kt`, `JsonMapperNip55.kt`.
## Flow
1. Login: `ExternalSignerLogin.createIntent(...)` → the signer app returns pubkey + package →
`parseResult(data)` → `NostrSignerExternal(pubKey, packageName, contentResolver)`.
2. Each call tries the background query first; if the signer has not pre-approved that
command, it falls back to a foreground intent through the registered launcher.
3. No registered Activity → `SignerExceptions.RunningOnBackgroundWithoutAutomaticPermissionException`;
no answer in 30 s → `TimedOutException`.
The Android entry point is
`amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedOff/login/ExternalSignerButton.kt`.
## Gotchas
- Without "always allow", every reaction or zap is an Activity round-trip — bad UX; ask for
the permissions at login.
- Desktop has no NIP-55; it uses `NostrSignerInternal` or `NostrSignerRemote`.
- Tests: `quartz/src/androidHostTest/.../nip55AndroidSigner/` (JVM host) and
`quartz/src/androidDeviceTest/.../nip55AndroidSigner/` (device/emulator only).