13 KiB
BitLogin NWC Connections
Status: Experimental design Document version: 0.1 Date: July 22, 2026 Implementation status: Not implemented
1. Summary
This document defines the proposed BitLogin Connection Vault profile for Nostr Wallet Connect (NWC) credentials.
The profile allows a user to:
- Import an NWC connection once.
- Recover that connection on another BitLogin device.
- Keep separate wallet connections for separate applications.
- Inspect known wallet capabilities, budget information, and expiration.
- Explicitly reveal or broker wallet operations to compatible applications.
- Remove a stored connection without changing the BitLogin identity.
NWC credentials are stored as independent encrypted Connection Vault records.
They are never stored directly in the BitLogin credential or recovery capsule.
See connection-vault.md for the common record, encryption, synchronization,
and recovery model.
2. Terminology
- Wallet service: The NWC wallet endpoint that receives encrypted requests.
- Client key: The Nostr private key embedded in the NWC URI and used to sign requests to the wallet service.
- Connection URI: The complete
nostr+walletconnect://URI supplied by the wallet provider. - Application binding: Optional BitLogin metadata associating one stored connection with a particular application origin or Nostr application key.
- Reveal mode: BitLogin returns the raw NWC URI to an application.
- Broker mode: BitLogin keeps the NWC secret and performs approved NWC requests on behalf of the application.
3. Security model
An NWC URI is a bearer-style credential containing active signing authority. Anyone who obtains the client secret can make every request permitted by the wallet service for that connection.
BitLogin therefore treats the complete NWC URI and its client secret as sensitive values.
The wallet service remains authoritative for:
- Supported NWC methods.
- Spending budgets.
- Maximum payment size.
- Rate limits.
- Expiration.
- Revocation.
- Any other wallet-side policy.
BitLogin may display cached or declared values, but it must not claim to enforce wallet policy unless every request is brokered through an isolated BitLogin execution boundary.
4. One connection per application
BitLogin should strongly encourage users and applications to create separate NWC connections rather than reuse one universal credential.
Recommended arrangement:
BitRoad -> commerce wallet connection
Satisfied -> AI inference spending connection
Game -> low-budget in-game payment connection
Dashboard -> read-only or reporting connection
Separate connections improve:
- Revocation.
- Budget assignment.
- Auditability.
- Application isolation.
- Privacy between unrelated applications.
- Damage containment after credential compromise.
Reusing one client key across applications should be shown as an advanced and potentially unsafe choice.
5. Accepted import format
The initial profile accepts a standard NWC connection URI:
nostr+walletconnect://<wallet-service-pubkey>?relay=<encoded-relay-url>&secret=<client-secret>&lud16=<encoded-lightning-address>
Example with non-secret placeholder values:
nostr+walletconnect://0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef?relay=wss%3A%2F%2Frelay.example&secret=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&lud16=adam%40example.com
The imported URI may include multiple relay query parameters. Unknown query
parameters must be retained for lossless export unless they are syntactically
invalid or explicitly prohibited by a later profile version.
The client must never place an imported URI in:
- Browser history.
- A page URL.
- Analytics.
- Logs.
- Error strings.
- Clipboard history without an explicit user copy action.
- Unencrypted local or relay storage.
6. Canonical internal credential
After import, BitLogin parses the URI and stores a structured credential object rather than relying only on the original string.
{
"schema": "bitlogin.connection.nwc.v1",
"wallet_service_pubkey": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"client_secret": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"relays": [
"wss://relay.example"
],
"lud16": "[email protected]",
"extra_parameters": {},
"declared_capabilities": [
"pay_invoice",
"get_balance"
],
"declared_budget": {
"amount_msats": 1000000,
"period": "monthly"
},
"expires_at": null,
"original_uri": null
}
Required fields:
schemawallet_service_pubkeyclient_secretrelays
Optional fields:
lud16extra_parametersdeclared_capabilitiesdeclared_budgetexpires_atoriginal_uri
original_uri should ordinarily be null. It may be retained only when lossless
round-trip compatibility cannot otherwise be achieved. When present, it is as
sensitive as the client secret.
7. Validation
An NWC import must fail if:
- The scheme is not exactly
nostr+walletconnect. - The wallet-service public key is not exactly 32 bytes encoded as 64 lowercase or uppercase hexadecimal characters.
- The client secret is not exactly 32 bytes encoded as 64 hexadecimal characters or is not a valid secp256k1 private scalar.
- No relay is present.
- A relay URL does not use
wss://, except for explicitly enabled local development usingws://localhostor loopback addresses. - A relay URL contains credentials.
- The total URI or decoded metadata exceeds implementation limits.
- Duplicate parameters conflict in a way the profile cannot interpret.
- Percent encoding is malformed.
The parser should normalize:
- Hexadecimal keys to lowercase.
- Relay URL host casing according to URL rules.
- Duplicate identical relay URLs into one entry while preserving first-seen order.
- Empty optional parameters to null or omission.
The parser must not silently repair a malformed secret or wallet public key.
8. Complete Connection Vault record
Example decrypted record:
{
"schema": "bitlogin.connection.v1",
"connection_id": "Vx7LgdZCsVbT_2uvB0YoGA",
"connection_type": "nwc",
"state": "active",
"label": "Satisfied spending wallet",
"created_at": 1784750000,
"updated_at": 1784750000,
"credential": {
"schema": "bitlogin.connection.nwc.v1",
"wallet_service_pubkey": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"client_secret": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"relays": [
"wss://relay.example"
],
"lud16": "[email protected]",
"extra_parameters": {},
"declared_capabilities": [
"pay_invoice",
"get_balance"
],
"declared_budget": {
"amount_msats": 1000000,
"period": "monthly"
},
"expires_at": null,
"original_uri": null
},
"application_binding": {
"origin": "https://satisfied.example",
"app_pubkey": null
},
"notes": null
}
Labels, application bindings, declared capabilities, and declared budgets are BitLogin metadata. They do not alter the actual authority granted by the wallet service.
9. Capability discovery
After import, BitLogin may connect to the wallet service and request supported NWC methods according to the current NWC protocol.
The result may be cached as declared_capabilities, but the interface must make
clear that:
- Wallet capabilities can change.
- A listed method may still fail.
- A missing cached method does not necessarily prove the wallet no longer supports it.
- Wallet-side policy is authoritative.
Capability refreshes update only the relevant NWC record.
10. Budgets and expiration
When the wallet service communicates budget or expiration information, BitLogin may store a display-oriented snapshot:
{
"declared_budget": {
"amount_msats": 1000000,
"period": "monthly"
},
"expires_at": 1787356800
}
expires_at is a Unix timestamp in seconds.
These fields are descriptive unless BitLogin broker mode independently limits all outgoing operations. Reveal mode necessarily gives the application the raw credential, after which BitLogin cannot enforce its own budget.
11. Export
11.1 Reconstructed NWC URI
BitLogin may reconstruct a standard URI for explicit export:
nostr+walletconnect://<wallet-service-pubkey>?relay=<relay>&secret=<client-secret>&lud16=<lud16>
Rules:
- Include one
relayparameter per relay. - Percent-encode all query values.
- Preserve unknown parameters from
extra_parameters. - Never include BitLogin-only labels, notes, budgets, or application bindings.
- Require user confirmation immediately before reveal or copy.
- Clear the displayed URI after the shortest practical interval.
11.2 Encrypted BitLogin export
A normal BitLogin account export includes the encrypted signed Connection Vault record, not a plaintext NWC URI.
A plaintext export must be separately selected and prominently marked as sensitive.
12. Application API
12.1 Listing metadata
An application may request non-secret connection metadata:
const wallets = await bitlogin.wallet.list();
Possible result:
[
{
"id": "Vx7LgdZCsVbT_2uvB0YoGA",
"label": "Satisfied spending wallet",
"lud16": "[email protected]",
"capabilities": ["pay_invoice", "get_balance"],
"expiresAt": null
}
]
The result must not include the client secret or full URI.
12.2 Access request
const grant = await bitlogin.wallet.requestAccess({
connectionId: "Vx7LgdZCsVbT_2uvB0YoGA",
methods: ["pay_invoice", "get_balance"],
reason: "Pay for AI inference",
mode: "broker"
});
The user-facing prompt should display:
- Requesting origin or application identity.
- Selected wallet label.
- Requested methods.
- Requested maximum amount when relevant.
- Whether the raw credential will be revealed.
- Whether approval is once, for the session, or persistent.
12.3 Brokered execution
Conceptual API:
const result = await bitlogin.wallet.execute({
grantId: grant.id,
method: "pay_invoice",
params: {
invoice: "lnbc..."
}
});
A broker should reject:
- Methods not present in the grant.
- Expired grants.
- Requests from a different origin or application identity.
- Payment amounts above the approved BitLogin-side ceiling.
- Requests after the connection is suspended, deleted, expired, or revoked.
This BitLogin-side check is additive. The wallet service must still enforce its own policy.
12.4 Reveal mode
Conceptual API:
const uri = await bitlogin.wallet.revealConnection({
connectionId: "Vx7LgdZCsVbT_2uvB0YoGA",
reason: "Connect this wallet to the application"
});
Before returning the URI, BitLogin must warn:
This application will receive a wallet credential. It can perform every action permitted by that connection until you revoke it at the wallet.
Reveal approval should not silently become a permanent grant.
13. Browser integration limitations
A same-origin embedded widget cannot keep an NWC URI secret from its host page while simultaneously returning that URI to the host page.
For meaningful broker isolation, NWC signing and relay communication should run inside:
- A BitLogin browser extension.
- A native BitLogin client.
- A cross-origin authorization window or iframe.
- A remote signer or broker with an authenticated protocol.
The initial widget may support metadata listing and explicit reveal mode, but it must not present same-origin JavaScript isolation as a strong security boundary.
14. Revocation and deletion
BitLogin cannot generally revoke an NWC connection merely by deleting its local or relay record. The connection must be revoked by the wallet service.
The user interface should provide:
- Open wallet to revoke when a provider URL or supported mechanism exists.
- Mark suspended to prevent BitLogin from offering the connection.
- Remove from BitLogin to tombstone and delete the portable record.
After provider-side revocation, BitLogin should mark the connection deleted or revoked and clear any persistent application grants.
A future provider-specific adapter may automate revocation, but the profile must not assume all NWC wallet services support a common revocation API.
15. Privacy considerations
A Connection Vault record is authored by a dedicated vault identity rather than the public everyday Nostr key. Nevertheless, correlation remains possible through:
- Relay selection.
- IP address.
- Connection timing.
- Record publication timing.
- Wallet relay traffic.
- Reused NWC client keys.
- Reused wallet service pubkeys.
Clients should avoid fetching public profile data and private Connection Vault records over the same relay connection where practical.
Users seeking stronger compartmentalization should use separate NWC connections for separate applications.
16. Test vectors
Before implementation, the project should publish test vectors covering:
- Parsing one and multiple relay parameters.
- Percent-encoded
lud16values. - Unknown parameter preservation.
- Uppercase-to-lowercase hex normalization.
- Invalid public keys and client secrets.
- Invalid secp256k1 scalars.
- Insecure and malformed relay URLs.
- Canonical credential serialization.
- Encrypted Connection Vault event construction.
- Exported URI reconstruction.
- Tombstone replacement.
All examples in this document use non-production placeholder secrets and must never be interpreted as usable wallet credentials.