An alternative passkey gateway for Nostr. Decentralize your key management across multiple providers. No single point of failure.
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.
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 stores a unique random encryption key in its user.id field. Your nsec is encrypted separately for each credential.
Each encrypted blob is published as a separate kind:30079 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 (Cloudflare)
const result1 = await setupKeytr({
userName: npub, rpId: "keytr.org"
})
// Register passkey against nostkey.org (GitHub Pages)
const result2 = await setupKeytr({
userName: npub, rpId: "nostkey.org",
nsecBytes: existingNsec // reuse same identity
})
// Each credential produces a separate kind:30079 event
// with its own export key vaulted in user.id
// 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:30079 parameterized replaceable events.
{
"kind": 30079,
"content": "<base64 encrypted nsec blob>",
"tags": [
["d", "<credential-id>"],
["rp", "nostkey.org"],
["algo", "aes-256-gcm"],
["scheme", "passkey-vault"],
["v", "1"]
]
}
The only difference from a keytr.org event is the rp tag. Each gateway's passkey holds its own export key — clients query for events matching the gateway they're authenticating against.
Use nostkey.org as an rpId in your Nostr client. Same keytr library, different gateway.
npm install keytr
import { setupKeytr, loginWithKeytr, fetchKeytrEvents } from 'keytr'
// Setup with nostkey.org gateway
const { credential, encryptedBlob, eventTemplate, nsecBytes, npub }
= await setupKeytr({ userName: 'alice', rpId: 'nostkey.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 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.