Dominion Protocol

April 12, 2026 · View on GitHub

Nostr: npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2

Your content. Your keys. Your rules. No platform required.

npm CI License: MIT TypeScript GitHub Sponsors

Protocol Spec · NIP Draft · LLM Reference · Contributing

Dominion — interactive explainer (click to open)
Interactive explainer — open in browser and click to advance

The Problem

Nostr content is either public or NIP-44 encrypted to specific recipients. There is no native mechanism for:

  • Audience tiers — different groups seeing different content (family, close friends, subscribers)
  • Revocable access — removing a recipient's ability to decrypt future content
  • Scalable encryption — encrypting to hundreds of recipients without per-recipient encryption operations

Per-recipient NIP-44 encryption works for DMs but doesn't scale:

RecipientsNIP-44 approachDominion
11 encryption1 encryption + 1 key share
1010 encryptions1 encryption + 10 key shares
100100 encryptions1 encryption + 100 key shares
1,0001,000 encryptions1 encryption + 1,000 key shares

Content is encrypted once with an epoch-based Content Key. Only the lightweight key distribution scales with audience size.

How It Works

Dominion introduces vaults — encrypted content containers on standard Nostr relays:

  1. Derive a Content Key (CK) from your private key, the current epoch, and the tier
  2. Encrypt your content with the CK (AES-256-GCM) and publish to any relay
  3. Distribute the CK to each tier member via NIP-44 encrypted, NIP-59 gift-wrapped events
  4. Rotate — new epoch, new CK. Revoked members don't get the new key

No custom relay software. No middleware. No platform. Just standard Nostr events and proven cryptography.

Combine with nostr-attestations to gate vault access on verified credentials (e.g. only verified professionals can decrypt tier-2 content).

Use Cases

Use caseHow Dominion helps
Creator paywallEncrypt to paying subscribers; revoke on cancellation
Family sharingPrivate family tier; rotate keys weekly
Close friendsShare selectively without making content public
Institutional accessTiered access with automatic key expiry
Paid newslettersOne encryption operation, unlimited subscribers

For scenarios requiring instant revocation (custody disputes, institutional SLA), the designed warden relays extension provides true revocation using NIP-42 AUTH. The extension is on the post-v1.0 roadmap; v1.0 ships with forward-only revocation only.

Install

npm install dominion-protocol

Quick Start

Encrypt Content for a Tier

import { deriveContentKey, contentKeyToHex, getCurrentEpochId, encrypt, decrypt } from 'dominion-protocol';

// Derive this week's Content Key for the "family" tier
const epochId = getCurrentEpochId();  // e.g. "2026-W11"
const ck = deriveContentKey(privateKeyHex, epochId, 'family');

// Encrypt content — one operation regardless of audience size
const ciphertext = encrypt('Hello family!', ck);  // base64 string
const plaintext = decrypt(ciphertext, ck);         // "Hello family!"

Manage Your Vault

import { defaultConfig, addToTier, removeFromTier, revokePubkey } from 'dominion-protocol';

let config = defaultConfig();
config = addToTier(config, 'family', alicePubkey);
config = addToTier(config, 'close_friends', bobPubkey);
config = revokePubkey(config, formerFriendPubkey);  // excluded from next rotation

Distribute Keys via Nostr

import { buildVaultShareEvent } from 'dominion-protocol/nostr';

// Build a kind 30480 vault share (caller handles NIP-44 + NIP-59 wrapping)
const event = buildVaultShareEvent(authorPubkey, recipientPubkey, ckHex, epochId, 'family');

Shamir Secret Sharing

Split Content Keys across multiple parties or relays for redundancy:

import { splitCK, reconstructCK, encodeCKShare, decodeCKShare } from 'dominion-protocol';

const shares = splitCK(ck, 2, 3);           // 2-of-3 threshold
const encoded = shares.map(encodeCKShare);   // ["1:ab12...", "2:cd34...", "3:ef56..."]

const decoded = encoded.slice(0, 2).map(decodeCKShare);
const recovered = reconstructCK(decoded);    // original CK

Architecture

Two-layer exports — use what you need:

  • dominion-protocol — universal crypto primitives. Pure functions, zero Nostr knowledge. Works anywhere.
  • dominion-protocol/nostr — Nostr event builders and parsers. Returns unsigned, unencrypted events. The caller handles NIP-44 encryption and NIP-59 gift wrapping.

Cryptography

PrimitiveImplementation
Key derivationHKDF-SHA256 (@noble/hashes)
Content encryptionAES-256-GCM, 12-byte random IV (@noble/ciphers)
Secret sharingShamir over GF(256), irreducible polynomial 0x11b
Epoch formatISO 8601 weeks (YYYY-Www)

Nostr Integration

KindTypePurpose
30480Parameterised replaceableVault share — epoch CK for a specific recipient
30078NIP-78 app-specific dataVault config — self-encrypted tier memberships

Built on NIP-01, NIP-09, NIP-40, NIP-44, NIP-59, and NIP-78. No new cryptographic primitives.

Protocol Spec

See spec/protocol.md for the full v1.0 protocol specification: epoch rotation, tier management, individual grants, revocation, and Lightning-gated access. Designed extensions live in spec/extensions/ — currently warden relays (instant revocation) and quantum vault (post-quantum CK derivation).

Part of the ForgeSworn Toolkit

ForgeSworn builds open-source cryptographic identity, payments, and coordination tools for Nostr.

LibraryWhat it does
nsec-treeDeterministic sub-identity derivation
ring-sigSAG/LSAG ring signatures on secp256k1
range-proofPedersen commitment range proofs
canary-kitCoercion-resistant spoken verification
spoken-tokenHuman-speakable verification tokens
toll-boothL402 payment middleware
geohash-kitGeohash toolkit with polygon coverage
nostr-attestationsNIP-VA verifiable attestations
dominionEpoch-based encrypted access control
nostr-veilPrivacy-preserving Web of Trust

Licence

MIT

Support

For issues and feature requests, see GitHub Issues.

If you find Dominion useful, consider sending a tip:

  • Lightning: thedonkey@strike.me
  • Nostr zaps: npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2