One identity. Many faces. No middleman.

SigFed is a protocol that lets you prove who you are, or just what you are (over 18, a moderator, a member), across many independent servers, without a central company, a blockchain, or handing over your personal details.

Plain English The signet ring

Imagine you own a signet ring that only you can press into wax. Anyone can check a seal came from your ring, but nobody can copy the ring from the seal. In SigFed, that ring is a cryptographic key that lives on your device. It is never uploaded anywhere.

Example: when Amina joins a forum, her app presses her ring into digital wax. The forum checks the seal and knows it is really her, without ever seeing the ring itself, and without asking her for an email address or a password.

Plain English Masks: one ring, many faces

You probably act differently at work, at a hobby club, and in a support group, and you may not want those groups comparing notes. SigFed lets your ring stamp out masks: separate identities, one per community, that all trace back to your ring mathematically, but that outsiders cannot link to each other.

Example: Amina is @amina on a cooking forum and @quietfern in a health community. Both masks come from the same ring, so she never loses either account, yet the two sites cannot tell the accounts belong to the same person.

Plain English Proving things without showing everything

Sometimes a community needs to know something about you: that you are an adult, or a verified professional. In SigFed, a trusted attestor (like a notary) gives you a sealed certificate. You can then show just the one fact a site asks for, and nothing else.

Example: a forum requires members to be over 18. Amina's certificate contains her full date of birth, but her app reveals only the single statement "over 18: yes". The forum learns nothing else, not even her birthday.

Plain English Losing the ring, and the sealed backup

What if your ring is stolen? SigFed has two protections. First, you can split a recovery code into, say, five pieces held by people you trust, where any three pieces restore it. Second, and this is the clever part, you can forge a backup ring in advance, keep it in a drawer, and publish only its fingerprint.

Example: a thief steals Amina's ring and tries to declare "this ring is retired, use my new ring instead". The network checks the thief's new ring against the fingerprint Amina published earlier. It does not match, so the takeover is refused, and only Amina's real backup ring, the one matching the fingerprint, can take over. The thief can cause a scare, but cannot steal her identity.

Deep dive Identifiers

A SigFed identity is an Ed25519 keypair. The public identifier is a did:key compatible multicodec encoding: the two-byte header 0xED01 (ed25519-pub), followed by the 32 raw key bytes, base58btc encoded with a multibase z prefix.

did:sigfed:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
            |_ multibase 'z' + base58btc( 0xED 0x01 || 32 key bytes )

Resolvers MUST reject identifiers with a missing z, a wrong multicodec header, a payload that is not exactly 34 bytes, or key bytes that are not a valid curve point.

Deep dive Persona derivation

Personas (the "masks") are derived deterministically from the 32-byte root seed with HKDF-SHA-256. The salt is the ASCII literal sigfed-persona-v1, the info parameter is a context string, and the 32-byte output keys a fresh Ed25519 pair:

okm = HKDF-SHA-256(
  ikm  = root_seed,            // 32 bytes, never the expanded scalar
  salt = "sigfed-persona-v1",  // ASCII literal, not hashed
  info = "community:example.sigfed.org:general",
  len  = 32
)
persona_signing_key = Ed25519.from_seed(okm)

Unlinkability holds because HKDF output is pseudorandom: given two persona public keys, no observer without the root seed can decide whether they share a root. Rotating one persona appends a version suffix to the context (...:general:v2). Context strings MUST NOT end in a bare colon, which would be ambiguous with that suffix.

Each persona is bound to its root by a PersonaBinding document that is dual-signed (root key and persona key), so nobody can claim someone else's persona, and nobody can be framed as owning a persona they never accepted.

Deep dive Signed documents and proofs

Every SigFed document is JSON, canonicalised with JCS (RFC 8785) and signed with the eddsa-jcs-2022 Data Integrity cryptosuite. The signing input is the document with its proof/proofs members removed, so dual-signed documents sign an identical body:

{
  "type": "PersonaBinding",
  "version": 1,
  "root_public_key": "<base58btc>",
  "persona_public_key": "<base58btc>",
  "context": "community:example.sigfed.org:general",
  "issued_at": 1775000000,
  "expires_at": null,
  "stored_at": 1775000000,
  "proofs": [
    { "type": "DataIntegrityProof", "cryptosuite": "eddsa-jcs-2022",
      "signer": "root-key",    "proofValue": "<base64url sig>", "...": "..." },
    { "type": "DataIntegrityProof", "cryptosuite": "eddsa-jcs-2022",
      "signer": "persona-key", "proofValue": "<base64url sig>", "...": "..." }
  ]
}

Deep dive The DHT

Identity records live in a Kademlia-style DHT (k=20 replication, 64 KiB record cap). Storage keys are domain-separated hashes of the subject's raw public key bytes:

slot = SHA-256( prefix || raw_key_bytes )

prefixes:
  sigfed:persona-index:     sigfed:persona-binding:
  sigfed:persona-rotation:  sigfed:revocation:
  sigfed:attestor:          sigfed:did:

Nodes enforce, in order: envelope shape and size, a signed temporal marker no more than 300 seconds in the future, a slot/key correspondence check (the slot is recomputed from key material inside the signed document, so records cannot squat a foreign slot), full proof verification, then conflict selection: greatest signed temporal marker wins, ties broken by lexicographically greatest proofValue. Revocations are special, as follows.

Try it below: the DHT slot calculator computes real slots in your browser.

Deep dive Revocation and pre-rotation

RootRevocation is terminal and write-once. Once a valid revocation is stored, later documents for that identity are rejected, and any conflicting revocation is kept as duplicity evidence rather than replacing the first. A successor claim on a revocation is only trusted when it is unambiguous.

Pre-rotation (KERI-style) closes the root compromise hole. At any time before trouble, the holder generates the next root keypair offline and publishes only its hash:

"sigfed:nextKeyCommitment":
    base64url( SHA-256( next_root_public_key ) )

Three rules give this teeth:

Worked example: attacker steals the current root key at T0 and publishes a revocation naming their own successor at T1. Their successor key does not hash to the commitment, so no resolver trusts the succession. At T2 the real holder takes the pre-committed key out of the drawer and publishes a dual-signed revocation. Dominance applies, the network converges on the true successor, and the attacker's window collapses to "misuse of the old key between T0 and T2". Verify a commitment yourself in the demo below.

Deep dive Recovery shares

The root seed can be split with Shamir secret sharing. Each share travels in a fixed 36-byte envelope, base64url encoded:

byte 0      version   (0x01)
byte 1      threshold k
byte 2      index     (1-based)
byte 3      total     n
bytes 4-35  raw 32-byte share

Decoders MUST reject unknown versions, k > n, index 0, index > n, and any length other than 36 bytes. Holders of the pre-rotation commitment SHOULD back up the next seed the same way.

Deep dive Live demos

DHT slot calculator

slot = SHA-256(prefix || raw key bytes)

Pre-rotation commitment checker

Does base64url(SHA-256(successor key)) equal the published commitment?

Where to go next