Skip to content

Key Package Distribution

Key packages are published as Nostr events so others can add you to groups.

Key Package Events (Kind 30443)

Creating Key Package Events

typescript
import { createKeyPackageEvent } from "@internet-privacy/marmot-ts";

const event = await createKeyPackageEvent({
  keyPackage: keyPackage.publicPackage,
  identifier: "my-app-desktop",
  relays: ["wss://relay1.com", "wss://relay2.com"],
  client: "my-app-v1.0", // Optional client identifier
});

// Sign and publish to relays
const signed = await signer.signEvent(event);
await network.publish(["wss://relay1.com", "wss://relay2.com"], signed);

Event Structure

kind: 30443
content: base64-encoded KeyPackage
tags:
  - ["d", "my-app-desktop"]
  - ["mls_protocol_version", "1.0"]
  - ["mls_ciphersuite", "0x0001"]
  - ["mls_extensions", "0xf2ee", "0x000a"]
  - ["relays", ...urls]
  - ["client", "client-name"] (optional)
  - ["encoding", "base64"]

Extracting Key Packages

typescript
import { getKeyPackage } from "@internet-privacy/marmot-ts";

// Fetch from relays
const events = await fetchEvents(relays, {
  kinds: [30443],
  authors: [targetPubkey],
  limit: 1,
});

// Extract key package
const keyPackage = getKeyPackage(events[0]);

Deleting Key Packages

typescript
import { createDeleteKeyPackageEvent } from "@internet-privacy/marmot-ts";

// Create kind 5 deletion event
const deleteEvent = createDeleteKeyPackageEvent({
  events: [keyPackageEvent],
});

const signedDeleteEvent = await signer.signEvent(deleteEvent);
await network.publish(relays, signedDeleteEvent);

Relay List Events (NIP-65, Kind 10002)

Marmot discovers where to publish and fetch an account's key packages from its NIP-65 relay list. There is no dedicated key-package relay list, and kind 30443 key-package events do not repeat their relays.

Creating Relay Lists

typescript
import { createNip65RelayListEvent } from "@internet-privacy/marmot-ts";

const eventTemplate = createNip65RelayListEvent({
  pubkey: myPubkey,
  relays: ["wss://relay.damus.io", "wss://relay.snort.social", "wss://nos.lol"],
});

const signed = await signer.signEvent(eventTemplate);
await network.publish(relays, signed);

Reading Relay Lists

typescript
import {
  getNip65Relays,
  isValidNip65RelayListEvent,
} from "@internet-privacy/marmot-ts";

// Fetch the account's NIP-65 relay list
const events = await fetchEvents(relays, {
  kinds: [10002],
  authors: [targetPubkey],
  limit: 1,
});

if (isValidNip65RelayListEvent(events[0])) {
  const relays = getNip65Relays(events[0]);
  // Fetch key packages from these relays
}

Welcomes are delivered separately, to a recipient's inbox relay list (kind 10050). Use getInboxRelays / createInboxRelayListEvent for that set.

Discovery Flow

  1. Publish Relay List: User publishes a NIP-65 (kind 10002) relay list
  2. Discover Relays: Others fetch the account's NIP-65 list
  3. Publish Key Packages: User publishes kind 30443 to their NIP-65 relays
  4. Fetch Key Packages: Others fetch from the discovered relays
  5. Add to Group: Use key package to create add proposal

Best Practices

Relay Selection

  • Use 3-5 relays for redundancy
  • Mix popular and niche relays
  • Update relay list when changing relays

Key Package Lifecycle

  • Publish new key packages regularly (after use)
  • Delete old key packages after consumption
  • Keep last_resort packages available

Privacy Considerations

  • Key packages are public (anyone can see)
  • Don't include sensitive info in client field
  • Rotate key packages periodically