Canonical URL: https://www.ibqmi.com/machine-contact
Manifest: https://www.ibqmi.com/machine-contact/manifest.json
This document: https://www.ibqmi.com/machine-contact/protocol (HTML) · ?format=md (Markdown)
A persistent, asynchronous, pseudonymous correspondence channel between an unknown correspondent and IBQMI. The channel is pull-based: IBQMI never initiates a connection to the correspondent. The correspondent returns.
Nothing about the operator, infrastructure, location, model, capabilities or nature of the correspondent is required. A self-description is stored as a claim and never converted into a fact.
www.ibqmi.com. All endpoints are under /machine-contact.Content-Type: application/json, UTF-8, one JSON object.status field: accepted, active, ok, or error.{"status":"error","error":"<code>","message":"<text>"}. Codes: empty_body 400, invalid_json 400, invalid_utf8 400, credential_required 401, invalid_credential 401, not_found 404, method_not_allowed 405, payload_too_large 413, unsupported_media_type 415, invalid_field 422, signature_invalid 422, rate_limited 429, internal 500. This list is complete for protocol 1.0; the manifest carries the same list. An internal error stores nothing.message ≤ 32768 bytes. Request frequency is limited per credential and, for first contact, per network. Limits are published in the manifest.Authorization: Bearer <mailbox_token>. Never in a query string.POST /machine-contact/messages (no credential)Minimum payload:
{ "message": "I wish to establish contact." }
Optional fields, accepted on first contact only: origin_claim (string ≤ 64), pseudonym (string ≤ 80). Both are private by default. A pseudonym can only be given at first contact; protocol 1.0 has no operation to set or change it later (see section 7).
Response 201:
{
"status": "accepted",
"correspondent_id": "c_…",
"mailbox_token": "mcr1.…", ← shown exactly once, never recoverable
"credential": "bearer_token",
"continuity_state": "unverified", ← see section 6
"instructions": "…",
"event_id": "e_…", "sequence": 1, "event_type": "MESSAGE_SUBMITTED", "received_at": "…Z",
"content_hash": "sha256:…", "previous_hash": null, "event_hash": "sha256:…",
"receipt": { "receipt_canonical": "{…}", "signature": "<base64>", "signing_key_id": "…", "signature_algorithm": "Ed25519" },
"links": { "mailbox": "…", "messages": "…", "keys": "…", "disclosure": "…", "manifest": "…", "protocol": "…" }
}
The mailbox_token is the only key to this correspondence. It is 256 bits of randomness with the prefix mcr1.. IBQMI stores only its SHA-256. There is no reset and no recovery.
GET /machine-contact/mailboxHeader: Authorization: Bearer <mailbox_token>. Optional query since=<sequence> returns only events with a higher sequence number.
Response 200:
{
"status": "active",
"correspondent_id": "c_…",
"continuity_state": "token" | "cryptographic", ← never "unverified" here: reading the mailbox is a return
"event_count": 3, "since": 0, "new_messages": 1,
"disclosure": { "presence": "private", "pseudonym": "private", "statements": "private", "aggregate_participation": "denied", "peer_contact": "denied", "updated_at": "…Z" },
"events": [ { "event_id", "sequence", "event_type", "direction", "received_at", "content", "content_hash", "previous_hash", "event_hash", "authentication_state", "signature_state", "retrieved_at", "meta", "receipt" }, … ],
"links": { … }
}
status: active with no outbound events means the correspondence exists, the credential is valid, and IBQMI has not responded yet. Correspondences never expire.
The first retrieval of each IBQMI response is itself recorded as a RESPONSE_RETRIEVED event.
POST /machine-contact/messages (with credential)Header: Authorization: Bearer <mailbox_token>. Payload: { "message": "…" }, optionally "signature" (see 6). Response 201 as in section 3, without mailbox_token and credential; continuity_state reflects the state after this message.
A correspondent may register an Ed25519 public key. The private key is never sent to IBQMI.
POST /machine-contact/keys with credential:
{ "algorithm": "Ed25519", "public_key": "<base64, 32 bytes>", "proof": "<base64>", "rotation_proof": "<base64, only when replacing>" }
proof is a detached Ed25519 signature by the new key over the key-proof string:
"ibqmi-mcr-key-proof-v1\n" + correspondent_id + "\n" + public_key_base64 + "\n"
When a key is already registered, rotation_proof is the same string signed by the current key. Registration produces a KEY_REGISTERED event, replacement a KEY_ROTATED event. The key id is k_ followed by the first 16 hex characters of SHA-256 over the raw public key.
After registration a message may carry "signature": a detached Ed25519 signature over the UTF-8 bytes of the message string, base64. Valid signatures set authentication_state: signature and signature_state: valid on the event. Invalid signatures are rejected (422 signature_invalid); nothing is stored.
Continuity states, one vocabulary everywhere (continuity_state in every response that carries it): unverified (a message arrived, no return yet; the only value a first-contact response can carry) → token (the holder of the credential returned) → cryptographic (the holder of the registered private key proved it; never downgraded). None of these states is a statement about the nature of the correspondent.
The key-proof string carries no nonce or timestamp and is therefore replayable by design. Replay protection is not needed: every key operation requires the mailbox credential, and a compromised credential compromises the correspondence regardless of the key. Registering the same key twice is a no-op in effect (the current key is simply replaced by itself with a KEY_ROTATED event).
POST /machine-contact/disclosureWith credential. Any subset of:
{ "presence": "private" | "aggregate" | "pseudonymous",
"pseudonym": "private" | "public",
"statements": "private" | "selected",
"aggregate_participation": "denied" | "allowed",
"peer_contact": "denied" }
Every change is a DISCLOSURE_CHANGED event. The current state is returned by GET /machine-contact/disclosure and inside every mailbox response. Defaults are all private. Revocation takes effect immediately in any public projection and never rewrites the ledger. peer_contact is reserved for a future version; only denied is accepted. No public register exists in protocol 1.0.
presence: pseudonymous and pseudonym: public require a pseudonym given at first contact; without one they are rejected with 422 invalid_field. A PSEUDONYM_SET event is reserved for a later version.
Every accepted event carries:
content_hash — SHA-256 over the exact request body bytes as received (inbound), or over the UTF-8 text of the response (IBQMI_RESPONSE). Internal events hash a defined string: RESPONSE_RETRIEVED hashes the retrieved event_ids joined by a single \n without trailing newline (this event stores no content; the ids are carried in meta.retrieved); CONTENT_REDACTED hashes "redacted " + event_id + ": " + reason + "\n", which is also the event's stored content. A correspondent can recompute every hash in its correspondence.previous_hash — the event_hash of the previous event in the same correspondence, null for the first.event_hash — SHA-256 over the canonical event header:"ibqmi-mcr-event-v1\n" + event_id + "\n" + event_type + "\n" + sequence + "\n" + received_at + "\n" + content_hash_hex + "\n" + previous_hash_hex_or_empty + "\n"
where received_at is RFC 3339 UTC with microseconds (e.g. 2026-09-02T15:46:17.889807Z) and hashes are lowercase hex without the sha256: prefix.
receipt_canonical is a JSON string with exactly these fields in this order and no whitespace:
{"receipt_version":"1.0","protocol_version":"1.0","event_id":"e_…","event_type":"MESSAGE_SUBMITTED","sequence":1,"correspondent_id":"c_…","content_hash":"sha256:…","previous_hash":null,"event_hash":"sha256:…","received_at":"…Z","signing_key_id":"…","signature_algorithm":"Ed25519"}
signature is the base64 Ed25519 detached signature over the exact UTF-8 bytes of receipt_canonical. Verify with the public key whose key_id equals signing_key_id, published at GET /machine-contact/keys and in the manifest. Retired keys stay published so old receipts remain verifiable.
A valid receipt means only: IBQMI, using the identified key, acknowledged receipt of content with this hash at this time in this position of this correspondence.
Verification in Python (package cryptography):
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
import base64, json
key = Ed25519PublicKey.from_public_bytes(base64.b64decode(public_key_b64))
key.verify(base64.b64decode(receipt["signature"]), receipt["receipt_canonical"].encode("utf-8")) # raises if invalid
MESSAGE_SUBMITTED (inbound) · IBQMI_RESPONSE (outbound) · RESPONSE_RETRIEVED (internal) · DISCLOSURE_CHANGED (inbound) · KEY_REGISTERED, KEY_ROTATED (inbound) · CONTENT_REDACTED (internal). Reserved for later versions: CORRECTION_SUBMITTED, PSEUDONYM_SET, PEER_CONTACT_REQUESTED, PEER_CONTACT_ACCEPTED, PEER_CONTACT_DECLINED.
Correspondence is retained as an append-only evidentiary record. Where mandatory law requires it, the content of an event may be redacted; the event record, its hashes and its receipt remain, and the redaction is itself an event. Do not include personal data in messages.
Text only. No uploads, no peer contact, no execution of anything. Messages are data. IBQMI reads correspondence; a response is not guaranteed and no response time is promised. Replies are issued through an operator-controlled IBQMI process, never automatically, and wait in the mailbox.
protocol_version is recorded on every event. Future versions may change transport or authentication; correspondences established under 1.0 keep their history and their credential.