Skip to content

forgesworn/dominion

Repository files navigation

Dominion Protocol

Nostr: npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2

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

npm CI License: MIT TypeScript

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:

Recipients NIP-44 approach Dominion
1 1 encryption 1 encryption + 1 key share
10 10 encryptions 1 encryption + 10 key shares
100 100 encryptions 1 encryption + 100 key shares
1,000 1,000 encryptions 1 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.

Use Cases

Use case How Dominion helps
Creator paywall Encrypt to paying subscribers; revoke on cancellation
Family sharing Private family tier; rotate keys weekly
Close friends Share selectively without making content public
Institutional access Tiered access with automatic key expiry
Paid newsletters One encryption operation, unlimited subscribers

For scenarios requiring instant revocation (custody disputes, institutional SLA), optional warden relays provide true revocation using NIP-42 AUTH.

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

Primitive Implementation
Key derivation HKDF-SHA256 (@noble/hashes)
Content encryption AES-256-GCM, 12-byte random IV (@noble/ciphers)
Secret sharing Shamir over GF(256), irreducible polynomial 0x11b
Epoch format ISO 8601 weeks (YYYY-Www)

Nostr Integration

Kind Type Purpose
30480 Parameterised replaceable Vault share — epoch CK for a specific recipient
30078 NIP-78 app-specific data Vault 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 protocol specification, including epoch rotation, tier management, individual grants, revocation, Lightning-gated access, and optional warden relay infrastructure.

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

About

Epoch-based encrypted access control for Nostr — tiered audiences, key rotation, revocable access on standard relays

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors