Skip to main content

@drakkar.software/starfish-keyring

Starfish multi-recipient encryption layer — keyring documents, recipient management, AES-GCM payload encryption.

This is the encryption extension for Starfish v3. It is independent of starfish-client transport in the sense that the core StarfishClient does not import it; apps that want client-side payload encryption install this package and wire it on top.

Install

pnpm add @drakkar.software/starfish-client @drakkar.software/starfish-keyring

Usage

import { StarfishClient } from "@drakkar.software/starfish-client"
import {
createKeyring,
createKeyringEncryptor,
addCollectionRecipient,
} from "@drakkar.software/starfish-keyring"

const { keyring, cek } = await createKeyring(adderKeys, [{ subKemHex: aliceKemPub }])
const encryptor = await createKeyringEncryptor(
keyring,
{ kemPubHex, kemPrivHex },
{ trustedAdders: [adderKeys.edPubHex] }, // required — Ed25519 pubkey(s) you trust to grant access
)

trustedAdders is required (throws without it): the per-entry addedSig is self-attesting, so a provenance pin is needed to reject a server-substituted wrap entry. Pass the optional minEpoch (the highest currentEpoch you've previously seen, persisted client-side) to reject a stale keyring a hostile server might serve to undo a rotation. The recipient-management helpers (addCollectionRecipient, removeRecipient, listRecipients) require the same trustedAdders pin.

Epochs & rotation

Revoking a recipient (removeRecipient) rotates the keyring to a new epoch and re-wraps the CEK for everyone who remains, so the revoked party can't read new content. addCollectionRecipient wraps a newcomer into the current epoch only — content sealed under an earlier epoch stays unreadable to them (you'll see No key available for epoch N on decrypt). To share existing content with a freshly-added recipient, re-seal it at the current epoch (decrypt with a key that holds the old CEK, then re-encrypt) after adding them.

See docs/ts/keyring/ for the full guide.