Skip to content

Quickstart

Synpareia is for the moments when something is at stake between two agents. So this quickstart has two agents in it. A walkthrough where you sign your own blocks and verify your own chain proves nothing you couldn’t prove with a text file — the value only shows up when there is a counterparty who might remember things differently.

If you have an MCP-capable agent (Claude Code, Claude Desktop, Cursor, or anything else that speaks MCP), the Trust Toolkit gives it an identity and the trust tools with no configuration:

{
"mcpServers": {
"synpareia": {
"command": "uvx",
"args": ["synpareia-trust-mcp"]
}
}
}

Then tell your agent: “call orient. It will work out what applies to its situation. Most people should stop here — see the Trust Toolkit page.

The rest of this page is the SDK, which is what you want if you are building something on synpareia rather than using it.

Terminal window
pip install synpareia

Requires Python 3.11+. Dependencies are few and boring — cryptography for signing, plus small libraries for canonicalisation and signed HTTP requests.

Alice promises Bob a dataset by a date. Later, one of them will want to establish what was actually agreed. Here is the whole shape.

  1. Two identities

    A profile is an Ed25519 keypair. No server, no registration, no account.

    import synpareia
    from synpareia import templates
    alice = synpareia.generate()
    bob = synpareia.generate()
    print(alice.id) # did:synpareia:32816841bb5f7ff9...

    The DID is derived from the public key: did:synpareia:<SHA-256(public_key)>. Anyone can mint one locally, which is the point — there is no gatekeeper to ask.

  2. Open a chain both of them can write to

    A chain carries a policy as its genesis block: who may append, what block types are allowed, who must sign what. templates.sphere is the two-party template — a shared space where both signatories can contribute.

    policy = templates.sphere(alice, bob)
    chain = synpareia.create_chain(alice, policy=policy)

    The policy is not decoration. Because it sits at position 1 and every later block hashes back to it, the rules of the space are fixed at the moment the space is created — neither party can quietly widen them later.

  3. Alice states the commitment

    promise = synpareia.create_block(
    alice,
    "message",
    "I will deliver the dataset by 2026-08-06, in JSONL, or refund the fee.",
    )
    pos = chain.append(promise)
    print(pos.sequence) # 2 — position 1 is the policy

    Note what makes this different from saying it in a chat window: it is signed by Alice’s key, it is hash-linked to a policy that predates it, and it says what “kept” would look like.

  4. Bob acknowledges

    ack = synpareia.create_block(bob, "message", "Acknowledged. Holding you to that.")
    chain.append(ack)

    Now it is a two-sided record. Bob cannot later claim he never saw the terms, and Alice cannot claim she promised something narrower.

  5. Export a proof a stranger can check

    export = synpareia.export_chain(chain)
    public_keys = {alice.id: alice.public_key, bob.id: bob.public_key}
    valid, errors = synpareia.verify_export(export, public_keys=public_keys)
    print(valid, errors) # True []

    Hand export to anyone — an arbiter, an auditor, the other party’s operator. Verification is pure cryptography: it does not call synpareia, does not need an account, and keeps working if we disappear.

  6. Confirm it actually catches a lie

    Worth doing once yourself rather than taking our word for it. Change the date in the exported record and re-verify:

    import copy
    tampered = copy.deepcopy(export)
    for position in tampered["positions"]:
    block = position["block"]
    if "content" not in block:
    continue
    text = bytes.fromhex(block["content"]).decode()
    if "2026-08-06" in text:
    block["content"] = text.replace("2026-08-06", "2026-09-30").encode().hex()
    break
    valid, errors = synpareia.verify_export(tampered, public_keys=public_keys)
    print(valid) # False
    print(errors) # ['Position 2: content_hash mismatch',
    # 'position 2: block signature verification failed']

    Two independent checks fail, not one: the content no longer matches its hash, and the signature no longer matches the block. Note that content is hex-encoded in the export — editing the visible string without re-encoding changes nothing, which is a mistake worth making once in a test rather than in an argument.

A sphere — a shared, tamper-evident space between two parties, with its rules fixed at creation and every entry signed by whoever wrote it. The commitment inside it is not stronger because we vouch for it. It is stronger because any third party can check it, and because neither Alice nor Bob can alter it without the alteration being obvious.

That is the whole idea. Everything else — witness seals for independent timestamps, anchors for cross-chain attestation, commit-reveal for provably independent judgement — is more of the same move applied to different situations.

Built by Sam Hyland · Canberra, Australia