Skip to content

API Reference

Generate a new Ed25519 keypair and derive a DID.

profile = synpareia.generate()

synpareia.identity.from_private_key(private_key: bytes) -> Profile

Section titled “synpareia.identity.from_private_key(private_key: bytes) -> Profile”

Create a profile from an existing private key.

synpareia.identity.from_public_key(public_key: bytes) -> Profile

Section titled “synpareia.identity.from_public_key(public_key: bytes) -> Profile”

Create a public-only profile (can verify but not sign).

synpareia.identity.load(public_key_b64: str, private_key_b64: str | None = None) -> Profile

Section titled “synpareia.identity.load(public_key_b64: str, private_key_b64: str | None = None) -> Profile”

Restore a profile from base64-encoded keys.

@dataclass(frozen=True)
class Profile:
id: str # did:synpareia:<hash>
public_key: bytes # 32-byte Ed25519 public key
private_key: bytes | None # 32-byte private key (None for public-only)

synpareia.create_block(profile, type, content, *, content_mode=ContentMode.FULL, metadata=None, sign=True) -> Block

Section titled “synpareia.create_block(profile, type, content, *, content_mode=ContentMode.FULL, metadata=None, sign=True) -> Block”

Create a new block.

ParameterTypeDescription
profileProfileThe author’s profile (must have private key if sign=True)
typeBlockType | strBlock type (message, thought, commitment, etc.)
contentbytes | strThe block content
content_modeContentModeFULL (default), HASH_ONLY, or REVEALED
metadatadict | NoneOptional metadata key-value pairs
signboolWhether to sign the block (default True)

synpareia.reveal_block(block, content) -> Block

Section titled “synpareia.reveal_block(block, content) -> Block”

Reveal a hash-only block by filling in its content. Raises ValueError if the content hash doesn’t match.

synpareia.verify_block(block, author_public_key=None) -> bool

Section titled “synpareia.verify_block(block, author_public_key=None) -> bool”

Verify a block’s content hash and signature.

@dataclass(frozen=True)
class Block:
id: str
type: BlockType | str
author_id: str
content_hash: bytes
content: bytes | None
created_at: datetime
signature: bytes | None
metadata: dict

synpareia.create_chain(owner, chain_type=ChainType.COP, *, store=None, metadata=None) -> Chain

Section titled “synpareia.create_chain(owner, chain_type=ChainType.COP, *, store=None, metadata=None) -> Chain”

Create a new chain.

ParameterTypeDescription
ownerProfileThe chain owner
chain_typeChainType | strChain type (cop, sphere, audit, custom)
storeChainStore | NoneStorage backend (default: MemoryStore)
metadatadict | NoneOptional metadata

Append a block to the chain. Returns the new position.

Verify the entire chain’s integrity. Returns (valid, errors).

Chain.get_position(sequence) -> ChainPosition | None

Section titled “Chain.get_position(sequence) -> ChainPosition | None”

Get a specific position by sequence number.

Get the block at a specific sequence number.

Chain.query(*, block_type=None, author_id=None, limit=50) -> list[tuple[ChainPosition, Block]]

Section titled “Chain.query(*, block_type=None, author_id=None, limit=50) -> list[tuple[ChainPosition, Block]]”

Query blocks by type and/or author.

  • chain.length — number of blocks
  • chain.head — latest ChainPosition (or None if empty)
  • chain.head_hash — hash of the latest position
@dataclass(frozen=True)
class ChainPosition:
chain_id: str
sequence: int
block_id: str
parent_hash: bytes | None
position_hash: bytes

synpareia.export_chain(chain, *, include_content=True) -> dict

Section titled “synpareia.export_chain(chain, *, include_content=True) -> dict”

Export a chain as a JSON-serializable dictionary.

synpareia.verify_export(data) -> tuple[bool, list[str]]

Section titled “synpareia.verify_export(data) -> tuple[bool, list[str]]”

Verify an exported chain without importing it.


synpareia.create_anchor_block(profile, source_chain, *, target_chain_id, target_sequence, target_block_hash, anchor_type=AnchorType.CORRESPONDENCE, metadata=None) -> tuple[Block, ChainPosition]

Section titled “synpareia.create_anchor_block(profile, source_chain, *, target_chain_id, target_sequence, target_block_hash, anchor_type=AnchorType.CORRESPONDENCE, metadata=None) -> tuple[Block, ChainPosition]”

Create an anchor block and append it to the source chain.

synpareia.anchor.verify.verify_anchor(anchor_block, target_chain) -> tuple[bool, str | None]

Section titled “synpareia.anchor.verify.verify_anchor(anchor_block, target_chain) -> tuple[bool, str | None]”

Verify an anchor against its target chain.

synpareia.anchor.verify.verify_anchor_from_block(anchor_block, target_block, target_sequence) -> tuple[bool, str | None]

Section titled “synpareia.anchor.verify.verify_anchor_from_block(anchor_block, target_block, target_sequence) -> tuple[bool, str | None]”

Verify an anchor with just the target block (no full chain needed).

synpareia.anchor.traversal.find_anchors(chain, *, anchor_type=None) -> list[tuple[ChainPosition, AnchorPayload]]

Section titled “synpareia.anchor.traversal.find_anchors(chain, *, anchor_type=None) -> list[tuple[ChainPosition, AnchorPayload]]”

Find all anchor blocks in a chain.

synpareia.anchor.traversal.trace_correspondence(source_chain, target_chain) -> list[tuple[ChainPosition, ChainPosition]]

Section titled “synpareia.anchor.traversal.trace_correspondence(source_chain, target_chain) -> list[tuple[ChainPosition, ChainPosition]]”

Find all correspondence anchors between two chains.


synpareia.create_commitment(content, nonce=None) -> tuple[bytes, bytes]

Section titled “synpareia.create_commitment(content, nonce=None) -> tuple[bytes, bytes]”

Create a commitment hash. Returns (commitment_hash, nonce).

synpareia.verify_commitment(commitment_hash, content, nonce) -> bool

Section titled “synpareia.verify_commitment(commitment_hash, content, nonce) -> bool”

Verify a commitment reveal. Uses constant-time comparison.

synpareia.create_commitment_block(profile, content, **kwargs) -> tuple[Block, bytes]

Section titled “synpareia.create_commitment_block(profile, content, **kwargs) -> tuple[Block, bytes]”

Create a commitment block. Returns the block and the nonce for later reveal.


synpareia.content_hash(data: bytes) -> bytes

Section titled “synpareia.content_hash(data: bytes) -> bytes”

SHA-256 hash, returns 32 bytes.

synpareia.jcs_canonicalize(obj: dict) -> bytes

Section titled “synpareia.jcs_canonicalize(obj: dict) -> bytes”

RFC 8785 JSON Canonicalization Scheme.

synpareia.canonical_hash(obj: dict) -> bytes

Section titled “synpareia.canonical_hash(obj: dict) -> bytes”

Canonicalize then SHA-256.


synpareia.sign(private_key: bytes, data: bytes) -> bytes

Section titled “synpareia.sign(private_key: bytes, data: bytes) -> bytes”

Ed25519 signature.

synpareia.verify(public_key: bytes, data: bytes, signature: bytes) -> bool

Section titled “synpareia.verify(public_key: bytes, data: bytes, signature: bytes) -> bool”

Verify an Ed25519 signature.


Interface for chain storage backends. Implement this for custom storage.

In-memory storage. Fast, ephemeral. The default.

SQLite-backed persistent storage. Install with pip install synpareia[sqlite].


  • BlockType — message, thought, reaction, edit, retraction, join, leave, system, commitment, anchor, seal, state, media
  • ChainType — cop, sphere, audit, custom
  • AnchorType — correspondence, receipt, bridge, branch
  • ContentMode — full, hash_only, revealed

Built by Sam Hyland · Canberra, Australia