3P فارسی Start writing

a protocol to build the trusted network based on the real us

The protocol, in one page

A protocol, not a platform: open formats and shared test vectors that any implementation can check itself against.

One implementation exists today, in Python and in the browser. A second, independent implementation is what would test these formats properly. The vectors below are there for it.

Each person runs their own 3P (the node). It keeps their record on their own computer. A card is what they choose to show: a signed packet of some entries from one face, expiring on its own. Identities survive a lost key. An organisation can countersign one entry with a key it publishes at its own domain. Assistants connect over MCP and need a separate permission for each action.

The formats

Every object carries its format id. Each links to the file that pins it down: a shared vector file (inputs with the exact outputs every verifier must produce) or a JSON Schema.

FormatWhat it is forPinned by
3p.card/1A card: entries from one face, signed by the person, expiring on its own.vectors.json
3p.identity/1The start of an identity: the first key, and the recovery key committed by thumbprint.chains.json
3p.rotate/1Moves an identity to a new key. Signed by the old key and accepted by the new one.chains.json
3p.recover/1Replaces a lost or stolen key, signed by the paper recovery key. That key can only replace keys, never sign a card.chains.json
3p.salience/1What matters now: a signed update that supersedes the earlier one and goes stale on its own.salience.json
3p.domain-keys/1An organisation's key file, at https://<domain>/.well-known/3p-keys.json.this site's demo file · countersigning
3p.countersign/1An organisation's signature over one entry's content hash, made before the person signs the card.countersign.json
3p.head/1A record's length and the hash at its end, shown to someone you meet so they can witness it. Each entry's hash is salted; nothing readable is in it.proof_vectors.json
3p.witness/1Someone else's signature over your head: "I saw this record at this length". A card's written_before uses it to prove an entry was already there.proof_vectors.json
3p.withdraw/1Takes a published card back from a shared notebook.No shared vector yet
3p.capture/1A capture: what happened at the moment of writing, and the entries that cite it. See below.capture.schema.json
constraintA never-do or never-say rule, as it travels inside a card.constraint.schema.json

The vector files hold test keys' public halves only. They identify nobody.

The canonical form

A signature is over bytes, so every implementation must turn the same object into the same bytes. The rules:

{"b":1,"a":[{"z":null,"y":true}],"_":3,"B":2}
→ {"B":2,"_":3,"a":[{"y":true,"z":null}],"b":1}
→ sha256 c75679b877c2402654baf84f2bf8d139567306febef9bcdfa4f9873a18a0b687

How to check a card

  1. Take the card, remove proof.sig, and write the rest in canonical form.
  2. Verify the signature with proof.key. A short, long, zero or malformed signature is a failed check, never a crash.
  3. Check that the key speaks for the subject: its thumbprint, or a valid chain of rotations and recoveries from the first key.
  4. Check the dates: issued, expires, and the date of each key change.
  5. For each countersignature, fetch the domain's key file and check it, as on the countersigning page.

There are three answers, never collapsed into two: valid (with the name you saved, or unknown signer), and invalid.

Anyone can check a card's signature in their own browser.

The verifier answers anything it is given, and accepts only what was signed.

Open the verifier

Check your own implementation against the card vectors, the identity chains, the countersignatures and what matters now. Each case gives the input and the answer it must produce. The Python verifier and the browser verifier, in Chromium, Firefox and WebKit, have been run against the same files.

The verifier runs in your browser. It fetches two kinds of thing: this site's samples, when you try one, and the key file of any domain a card says countersigned it.

The assistant interface

An assistant talks to a person's 3P over MCP (JSON-RPC on stdio, no SDK needed). It asks for one face by name, and gets only that face.

claude mcp add 3p -- python3 /absolute/path/to/mcp_server.py

An assistant is not you. It asks before it reads, and you allow each thing separately: reading, writing, signing, publishing.

This holds for assistants that go through 3P. Software running as you on your computer can read your record file directly.

A permission (a grant) is for one assistant, one face, a set of openness levels and a length of time. It names its actions: read, log, sign, publish. read is always included; the rest must be named.

ToolWhat it doesNeeds
list_scopesThe faces this 3P will serveread
get_personaPreferences, rules and context for one face, with a summary of its entriesread
search_factsSearch entries in one faceread
attested_factsOnly the entries someone else vouched forread
get_salienceWhat matters now, in one faceread
publishedWhat was published, and whether it is live, expired or taken backread
withdrawTake a published card backread: taking back only ever shows less
logWrite an entry in the person's own words, marked as relayed by the assistantlog
correctCorrect an entry; the earlier wording is keptlog
mint_packetSign a card for one facesign
mint_salienceSign an update to what matters nowsign
publishPublish the open part of one face to a shared notebookpublish
python3 consent.py requests                         # what asked, and was refused
python3 consent.py grant claude-code work --tiers public --for 1h
python3 consent.py grant claude-code work --for 8h --allow log,publish
python3 consent.py revoke 3

Without a live grant, a call is refused and recorded as a request for the person to see. Every call is recorded, granted or not. private cannot be granted to anything.

An assistant can never widen what you allowed it.

An MCP client's name is whatever the client says it is. A grant is a boundary against software acting without being asked. It does not prove which software is asking.

The capture contract: 3p.capture/1

What a 3P exports, and what another tool (a capture app, a mirror) may hand one. It has two lists:

Beyond the schema, the conformance check holds that every fact cites an act in the same document, that ids are unique, and that no field of the mirror appears anywhere, at any depth. The mirror never travels.

{"format": "3p.capture/1", "exported_at": "2026-10-19T09:00:00Z",
 "acts":  [{"id": "act:fixture05", "kind": "relayed", "captured_at": "2026-10-16T10:00:00Z",
            "device": "mcp", "relayed_by": "claude-code",
            "content_sha256": "28fea5dcc347d363cc771873322e9e66b4e8e17ada0c2ac0d624c03991c2d2f0"}],
 "facts": [{"id": "fact:5", "text": "Load-tested retries at 5x #work", "occurred": "2026-10-16",
            "recorded": "2026-10-16T10:00:00Z", "entry_mode": "live", "act": "act:fixture05",
            "share_tier": "private", "tags": []}]}

The schema. The example is a test fixture, invented.

Not in the protocol yet

Start your record.

Start writing