Skip to content

Concepts

term means
member a person or account, as your identity system names them
device one installation. Holds keys. Registered with a public key, set-once.
graph a keyed namespace of records. Three shapes: u, g:<id>, d:<id>.
record {id, kind, data, at, by}. at is both timestamp and revision.
epoch a generation of a graph’s key. Rotation mints the next one.
keyring epoch → key, on a device. Old epochs are kept so history stays readable.
await engine.openGraph('u'); // personal — only this member's devices
await engine.openGraph('g:acme'); // shared — membership from your Directory
await engine.openGraph('d:alex'); // pairwise with alex — its own key
  • u is per member. Preferences, read positions, private notes.
  • g:<id> is any shared space: a team, a workspace, a project, a document with collaborators. Membership is whatever your Directory says it is.
  • d:<id> is a two-party channel. Sam addresses it d:alex; Alex addresses it d:sam; both land on the same key and the same storage slot.

Choosing a graph is choosing a key boundary. Everything in one graph is readable by everyone in that graph — role rules inside a graph are UI courtesy, not cryptography. If a distinction must hold cryptographically, use a separate graph.

This is the single most important thing to understand about skm, and the thing that differs from most E2E systems.

wrapped towrapped towrapped to

graph key (one per epoch)

device A pubkey

device B pubkey

device C pubkey

No API hands you a private key. No ceremony moves one between machines. Adding a device is a grant: the new device publishes a public key, and any device that already holds the keyring wraps it over. Losing one device is revocable without touching the others.

The recovery bundle exists for when there is no device left to grant from, and it carries graph keys only — never a device keypair.