NIP-K1 Compatible Gateway

Passkey login
for Nostr

An alternative passkey gateway for Nostr. Decentralize your key management across multiple providers. No single point of failure. Works with all authenticators.

Two gateways, zero single points of failure

Your passkey-encrypted keys live on Nostr relays. The gateways just provide the WebAuthn rpId — and now there are two.

Cloudflare
keytr.org
Primary gateway operated by sovIT. Hosted on Cloudflare for global edge performance.
  • Hosting Cloudflare Pages
  • Operator sovIT
  • Status Active
Hostinger
nostkey.org
Alternative gateway hosted on Hostinger. Same protocol, different infrastructure. True decentralization.
  • Hosting Hostinger
  • Operator sovIT
  • Status Active
Why does this matter? WebAuthn passkeys are bound to the domain (rpId) they were created on. If keytr.org goes down or Cloudflare has an outage, passkeys registered against it can't authenticate. By registering passkeys against both gateways, you maintain access even if one provider fails. Each gateway produces a separate kind:31777 event on your relays.

How it works

nostkey.org is a fully compatible keytr gateway. Same protocol, same encryption, different domain.

1

Register passkeys on multiple gateways

Create passkeys on both keytr.org and nostkey.org for redundancy. Each produces an independent credential.

2

Encrypt

Each passkey uses PRF — a hardware-bound secret that never leaves the authenticator — to derive an AES key via HKDF-SHA256. Your nsec is encrypted separately for each credential. Modern password managers and platform authenticators support PRF; if one doesn't, setup fails cleanly rather than falling back to a weaker mode.

3

Publish to relays

Each encrypted blob is published as a separate kind:31777 event. One tags rp: keytr.org, the other tags rp: nostkey.org.

4

Login from any gateway

On a new device, authenticate with whichever gateway is available. Cloudflare down? Use nostkey.org. GitHub down? Use keytr.org.

Architecture

Same encryption. Different infrastructure. True redundancy.

Your device
Passkey + biometric
Gateways (rpId)
keytr.org + nostkey.org
AES-256-GCM
PRF → HKDF-SHA256 key
Nostr relays
Multiple kind:31777 events (v=1)
All cryptography happens client-side. Gateways are static files — they serve the /.well-known/webauthn endpoint and nothing else.

Security

Identical cryptography to keytr. Decentralization adds resilience, not complexity.

Encryption scheme

  • Key source PRF extension secret → HKDF-SHA256
  • One mode PRF — key stays inside the authenticator
  • Cipher AES-256-GCM with 12-byte random IV
  • AAD Credential ID + version byte (binds ciphertext to the credential)

Multi-gateway benefits

  • No SPOF Two providers, two infrastructure stacks
  • Domain diversity Different registrars, DNS, hosting
  • Independent credentials Compromise of one gateway doesn't affect the other
  • Relay redundancy Separate events per gateway on your relays
Why PRF only? keytr briefly experimented with a Key-in-Handle (KiH) mode that stored the encryption key inside the passkey. It was withdrawn as too risky — that key was exposed to page scripts — in favor of PRF, which keeps the key inside the authenticator where page code can't read it.

Federated cross-client login

Both gateways support the same Related Origin Requests spec. Any compatible client works with either.

keytr.org
Cloudflare — primary gateway
nostkey.org
Hostinger — alternative gateway
your-domain.com
Run your own — anyone can
Register on both gateways for redundancy
// Register passkey against keytr.org (PRF)
const { eventTemplate, nsecBytes, npub } = await setup({
  userName, userDisplayName, rpId: "keytr.org", rpName: "keytr"
})

// Register a backup passkey against nostkey.org (Hostinger)
// for the same nsec — one extra biometric prompt
const backup = await addBackupGateway(nsecBytes, {
  userName, userDisplayName, rpId: "nostkey.org", rpName: "keytr"
})

// Each credential produces a separate kind:31777 event (v=1, PRF)
// Lose access to one gateway? The other still works.

Run your own gateway

Three gateways are better than two. All you need is a domain and one file.

/.well-known/webauthn
{
  "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.

The event — NIP-K1

Same event format as keytr. Fully compatible kind:31777 parameterized replaceable events.

kind:31777 (nostkey.org gateway)
{
  "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 derives its own encryption key via PRF, tagged v=1. (v=3 is a reserved legacy value from the withdrawn KiH mode and is never emitted by new events.) Clients query for events matching the gateway they're authenticating against. transports and client tags are optional.

For client developers

Use nostkey.org as an rpId in your Nostr client. Same keytr library, different gateway.

Install
npm install @sovit.xyz/keytr
Usage with nostkey.org gateway
import { setup, discover, publishKeytrEvent } from '@sovit.xyz/keytr'
import { finalizeEvent } from 'nostr-tools/pure'

// Setup with nostkey.org gateway (PRF only)
const { eventTemplate, nsecBytes, npub }
  = await setup({ userName: 'alice', userDisplayName: 'Alice', rpId: 'nostkey.org', rpName: 'keytr' })

// Sign & publish the kind:31777 event to your relays
const signed = finalizeEvent(eventTemplate, nsecBytes)
await publishKeytrEvent(signed, relays)

// Discoverable login on a new device: one biometric tap (PRF)
const { nsecBytes, npub, pubkey } = await discover(relays, { rpId: 'nostkey.org' })

To have your origin authorized for cross-client login via nostkey.org, add your domain to the origins list via PR.

Decentralize your Nostr keys

Register passkeys on both gateways. No single point of failure. No single provider to trust.