You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #107 establishes a binary split between local and remote signing: a wallet's signing material lives in one place. DefaultWalletProvider.GetSignerAsync returns:
Wallet.Secret
Transport claims it
→
Signer
present
(ignored)
local (HD or single-key)
null/empty
yes
RemoteArkadeWalletSigner
null/empty
no
null (watch-only)
This is correct and simpler to reason about as phase 1. It does, however, foreclose a class of use cases worth designing for explicitly rather than ad-hoc later.
The hybrid model
Push key resolution down one layer: an IPrivateKeyProvider resolves "the private key for this descriptor", and a single LocalArkadeWalletSigner composes a set of providers (try each, first hit wins). Concretely:
Bip39KeyProvider(mnemonic) — derives anything below the wallet's account descriptor.
NsecKeyProvider(nsec) — provides exactly its single key.
RemoteTransportKeyProvider(IRemoteSignerTransport, walletId) — delegates to the multi-wallet transport for descriptors it doesn't have local material for.
Then LocalArkadeWalletSigner becomes:
publicclassLocalArkadeWalletSigner(IReadOnlyList<IPrivateKeyProvider>providers):IArkadeWalletSigner{privateasyncTask<IPrivateKeyProvider>Resolve(OutputDescriptord,CancellationTokenct){foreach(varpinproviders)if(awaitp.CanProvideAsync(d,ct))returnp;thrownewInvalidOperationException($"No provider for descriptor {d}.");}// …}
Concrete use cases this unblocks
HWI / hardware wallet integration — local mnemonic derives view-key paths cheaply; spend-key paths live on the device behind a RemoteTransportKeyProvider.
Multi-party signing — some cosigners local, some remote, single signer instance bridges them.
Recovery flows — historical paths held locally (BIP-39 mnemonic the user already has), new paths routed to a remote signer policy.
Threshold / key-splitting — extends to N providers with a quorum policy on top.
Trade-offs vs. the current binary design
Pro: composability; the dual axis (derivation flavor × signing capability) becomes truly independent rather than sequentially-resolved.
Con: every signing call pays a per-descriptor "who provides this?" dispatch. Tractable, but a layer of indirection in the hot path.
Migration: HierarchicalDeterministicWalletSigner, NSecWalletSigner, RemoteArkadeWalletSigner collapse into provider implementations behind one signer class. Consumers of IArkadeWalletSigner are unaffected (the interface doesn't change).
Raised during the PR #107 review discussion about why RemoteArkadeWalletSigner exists as a separate class rather than something the local signers compose internally. Filed so the architectural question can be discussed without holding PR #107's WalletType-as-capability rework.
Context
PR #107 establishes a binary split between local and remote signing: a wallet's signing material lives in one place.
DefaultWalletProvider.GetSignerAsyncreturns:Wallet.SecretRemoteArkadeWalletSignernull(watch-only)This is correct and simpler to reason about as phase 1. It does, however, foreclose a class of use cases worth designing for explicitly rather than ad-hoc later.
The hybrid model
Push key resolution down one layer: an
IPrivateKeyProviderresolves "the private key for this descriptor", and a singleLocalArkadeWalletSignercomposes a set of providers (try each, first hit wins). Concretely:Implementations:
Bip39KeyProvider(mnemonic)— derives anything below the wallet's account descriptor.NsecKeyProvider(nsec)— provides exactly its single key.RemoteTransportKeyProvider(IRemoteSignerTransport, walletId)— delegates to the multi-wallet transport for descriptors it doesn't have local material for.Then
LocalArkadeWalletSignerbecomes:Concrete use cases this unblocks
RemoteTransportKeyProvider.Trade-offs vs. the current binary design
HierarchicalDeterministicWalletSigner,NSecWalletSigner,RemoteArkadeWalletSignercollapse into provider implementations behind one signer class. Consumers ofIArkadeWalletSignerare unaffected (the interface doesn't change).Reference
Raised during the PR #107 review discussion about why
RemoteArkadeWalletSignerexists as a separate class rather than something the local signers compose internally. Filed so the architectural question can be discussed without holding PR #107's WalletType-as-capability rework.