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 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.

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
Per-gateway encryption
Nostr relays
Multiple kind:31777 events
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 or KiH random key → HKDF-SHA256
  • Two modes PRF (hardware-bound) or KiH (universal fallback)
  • Cipher AES-256-GCM with 12-byte random IV
  • AAD Credential ID + mode-specific version byte (prevents cross-mode attacks)

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

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-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.

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 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.

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'

// 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.

Decentralize your Nostr keys

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