Encrypt your nsec with a passkey. Log in to any Nostr client with your fingerprint. No seed phrases. No clipboard. No extensions.
Copy-pasting nsec between devices leaks your identity to every app that reads the clipboard.
Browser extensions don't follow you across devices. Hardware signers add complexity.
Leak your nsec once and your Nostr identity is compromised permanently. There's no rotation.
Six steps from nsec chaos to passkey-protected login.
Create a new nsec or bring your existing one into any keytr-compatible client.
Your device creates a WebAuthn credential bound to a gateway (e.g. keytr.org) or the client's own domain.
A random encryption key is generated, used to encrypt your nsec, then packed alongside your npub into the passkey's user.id field — creating a split-knowledge vault that only your biometric can unlock.
The encrypted blob is published as a kind:30079 parameterized replaceable event to your Nostr relays.
On a new device, open any keytr-compatible client and tap "Login with Passkey".
Your passkey syncs via iCloud / Google / Windows — authenticate with your fingerprint or face to decrypt your nsec instantly.
No servers to trust. No passwords to remember. Just math and hardware.
Designed to make the right thing easy and the wrong thing impossible.
The encryption key is embedded inside your passkey's user.id field — it only leaves the authenticator after biometric/PIN verification. Even if the encrypted event is public on relays, it is useless without your passkey.
Your passkey-encrypted keys live on Nostr relays. The gateways just provide the WebAuthn rpId — and now there are two.
kind:30079 event on your relays.
One passkey works across every Nostr client. No single domain controls the protocol.
WebAuthn binds passkey outputs to the domain (rpId) they were created on. To enable cross-client login, keytr uses a federated gateway model built on the W3C Related Origin Requests spec.
Any domain can become a passkey gateway by hosting a /.well-known/webauthn file listing authorized client origins. Register passkeys against multiple gateways, producing a separate kind:30079 event for each. keytr.org runs on Cloudflare, nostkey.org runs on GitHub Pages — different providers, different infrastructure, zero single points of failure.
// Pack npub + export key into user.id (64 bytes)
const userHandle = new Uint8Array(64)
userHandle.set(npubBytes, 0) // bytes 0–31: public key
userHandle.set(exportKey, 32) // bytes 32–63: encryption key
// Register passkey with gateway rpId
navigator.credentials.create({
publicKey: {
rp: { id: "keytr.org", name: "keytr" },
user: { id: userHandle, name: npub, displayName: npub },
authenticatorSelection: {
residentKey: "required",
userVerification: "required"
}
}
})
// On login, authenticator returns userHandle
// → extract npub + export key → decrypt nsec
Decentralize the protocol further. All you need is a domain and one file.
{
"origins": [
"https://your-gateway.example",
"https://client-a.com",
"https://client-b.com"
]
}
Clients listed in your origins can register passkeys under your domain's rpId. The browser verifies authorization automatically via the Related Origin Requests spec.
Encrypted keys are stored as kind:30079 parameterized replaceable events.
{
"kind": 30079,
"content": "<base64 encrypted nsec blob>",
"tags": [
["d", "<credential-id>"],
["rp", "keytr.org"],
["algo", "aes-256-gcm"],
["scheme", "passkey-vault"],
["v", "1"]
]
}
Multiple passkeys can be registered — each produces a separate event with a different d tag. Lose one passkey, your others still work.
Integrate keytr into your Nostr client in minutes.
npm install keytr
import { setupKeytr, loginWithKeytr, fetchKeytrEvents } from 'keytr'
// Setup: register passkey + encrypt nsec
const { credential, encryptedBlob, eventTemplate, nsecBytes, npub }
= await setupKeytr({ userName: 'alice', rpId: 'keytr.org' })
// Sign & publish the kind:30079 event to relays
// Login on new device: one biometric tap
const events = await fetchKeytrEvents(pubkey, relays)
const { nsecBytes, npub } = await loginWithKeytr(events[0])
To have your origin authorized for cross-client login, add your domain to the origins list via PR.
Open protocol. Open source. Open to everyone.