An alternative passkey gateway for Nostr. Decentralize your key management across multiple providers. No single point of failure. Works with all authenticators.
Your passkey-encrypted keys live on Nostr relays. The gateways just provide the WebAuthn rpId — and now there are two.
kind:31777 event on your relays.
nostkey.org is a fully compatible keytr gateway. Same protocol, same encryption, different domain.
Create passkeys on both keytr.org and nostkey.org for redundancy. Each produces an independent credential.
Each passkey uses either PRF (hardware-bound secret) or KiH (random key stored in the passkey) to derive an AES key via HKDF-SHA256. Your nsec is encrypted separately for each credential. Most password managers now support PRF; KiH serves as an automatic fallback for authenticators without PRF.
Each encrypted blob is published as a separate kind:31777 event. One tags rp: keytr.org, the other tags rp: nostkey.org.
On a new device, authenticate with whichever gateway is available. Cloudflare down? Use nostkey.org. GitHub down? Use keytr.org.
Same encryption. Different infrastructure. True redundancy.
/.well-known/webauthn endpoint and nothing else.Identical cryptography to keytr. Decentralization adds resilience, not complexity.
Both gateways support the same Related Origin Requests spec. Any compatible client works with either.
// Register passkey against keytr.org (PRF-first, KiH fallback)
const { nsecBytes, mode } = await setup({
userName: npub, userDisplayName: npub, rpId: "keytr.org"
})
// Register backup passkey against nostkey.org (Hostinger)
const backup = await addBackupGateway(nsecBytes, {
userName: npub, userDisplayName: npub, rpId: "nostkey.org"
})
// Each credential produces a separate kind:31777 event
// PRF-first with automatic KiH fallback
// Lose access to one gateway? The other still works.
Three gateways are better than two. All you need is a domain and one file.
{
"origins": [
"https://nostkey.org",
"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.
Same event format as keytr. Fully compatible kind:31777 parameterized replaceable events.
{
"kind": 31777,
"content": "<base64 encrypted 93-byte blob>",
"tags": [
["d", "<credential-id-base64url>"],
["rp", "nostkey.org"],
["algo", "aes-256-gcm"],
["kdf", "hkdf-sha256"],
["v", "1"],
["transports", "internal", "hybrid"],
["client", "<client-name>"]
]
}
The only difference from a keytr.org event is the rp tag. Each gateway's passkey produces its own encryption key for key derivation — via PRF (v=1) or KiH (v=3). Clients query for events matching the gateway they're authenticating against. transports and client tags are optional.
Use nostkey.org as an rpId in your Nostr client. Same keytr library, different gateway.
npm install @sovit.xyz/keytr
import { setup, discover, publishKeytrEvent } from '@sovit.xyz/keytr'
// Setup with nostkey.org gateway (PRF-first, KiH fallback)
const { eventTemplate, nsecBytes, npub, mode }
= await setup({ userName: 'alice', userDisplayName: 'Alice', rpId: 'nostkey.org' })
// Sign & publish the kind:31777 event to relays
// Discoverable login: auto-detects PRF vs KiH, one biometric tap
const { nsecBytes, npub, pubkey } = await discover(relays)
To have your origin authorized for cross-client login via nostkey.org, add your domain to the origins list via PR.
Register passkeys on both gateways. No single point of failure. No single provider to trust.