MiCyte

← The wiki

90 — Network and contracts

BuiltRendered from 90-network-contract-architecture.md in the public repository. Edits belong there.

This page states how one instance publishes its identity, how two instances form an encrypted contract, and how a sandbox reads documents another sandbox published to it. The canonical contracts are contract_formation.md, hosted_alias_interface.md and tool_package_update_contract.md under docs/contracts/. The channel surface built on this layer is described in 91.

The implementation is in fnd_app/instances/_shared/runtime/. File names below are in that directory unless a path is given.

Identity: the contact card

Each instance has a contact card in the registrar. Two card fields carry its network identity (micyte/core/datum_ops/field_registry.py):

FieldAddressContent
public_signature3-1-17The instance's Ed25519 public key, as base64 of the raw 32 bytes (44 characters).
instance_endpoint3-1-18The address the instance accepts contract requests at.

The dns cell is the instance's website domain; it is not the endpoint. An instance with no instance_endpoint is not reachable and its inbound contract route does not answer.

A handshake verifies the requester against the key on its card. The contract record does not carry the key; a contract that carried its own authorizing key would authorize itself.

The public_signature field holds 64 characters. A PEM-encoded Ed25519 key (about 113 characters) does not fit; the PEM is rebuilt from the base64 at verify time.

Signing keys

instance_keys.py keeps keys in the instance's private key directory: files 0600 inside a 0700 directory. It refuses to write with wider permissions and refuses to re-mint an existing key with different bytes.

The signing key is derived, not random: HKDF-SHA256(root_seed, salt="mycite.v2.identity.v1", info="<msn_id>|<generation>") gives the Ed25519 seed (derive_signing_seed). The root seed is generated once per instance. The same address under the same root always gives the same key; recover_instance_key re-derives a lost key file from a restored root. A new msn gives a new key; a rotation increments the generation.

Per-contract symmetric keys are random and minted during the handshake.

The handshake

contract_handshake.py:

StepFunctionWhat it does
Requestbuild_requestSigned with the requester's card key; carries a fresh X25519 ephemeral key.
Verifyverify_requestChecks the signature against the requester's card.
Offerbuild_offerThe responder mints the contract's symmetric key and seals it to the ECDH shared secret.
Openopen_offerThe requester unseals the key.
Trafficseal_traffic / open_trafficEvery later message is sealed with that key.
Expirykey_is_expiredKey age against key_period_seconds (default 30 days).

Schemas: mycite.v2.network.contract.request.v1, .offer.v1, .sealed.v1. Clock skew tolerance: 120 seconds.

The ephemeral key is signed so that a third party cannot substitute its own.

build_request does not store its ephemeral key, so sending the same request again also collects the answer: an undecided request returns "recorded"; a decided one returns the existing key re-sealed to the ephemeral in hand. A repeated request never mints a new key and keeps the original key_minted_at.

Replay protection

contract_sequence_store.py keeps the last accepted sequence number per contract and key epoch in contract_transport.sqlite3. accept_sequence uses one atomic UPDATE … WHERE last_seq < ?, so a message with an old or repeated sequence number is refused.

Contract lifecycle

contract_negotiation.py: open_request, receive_request, accept_request, answer_request, refuse_request, revoke_contract. _legal_events derives the allowed events from the record's state; an event that is not allowed raises. contract_store.py stores records with schema mycite.portal.contract.v2 in the instance's private contracts directory. Revoking a contract destroys its key.

Inbound and outbound routes

  • Outbound. instance_client.py: post_contract_request posts to /__instance/contract/request at the endpoint endpoint_for reads off the peer's card.
  • Inbound. The /__instance/ route verifies the card signature before it records anything. It is separate from /__channel/, which has only GET routes.
  • Closed channels. closed_channel_admission.py admits a caller that presents traffic sealed with the contract key, or a signed bootstrap request (clock skew 300 seconds). Every refusal returns the same 404 an unknown channel returns; the reason is logged, not returned.

The local audit log refuses records carrying key material (FORBIDDEN_LOCAL_AUDIT_KEYS in fnd_app/packages/modules/cross_domain/local_audit/service.py).

Reading another sandbox's documents: sources manifests

micyte/core/sources.py. A sandbox does not read another sandbox's documents directly. It reads its own sources manifest, whose rows the owning sandbox published to it. A row holds a name, a kind and a content hash at rf.3-1-12.

The hash depends on the row's kind:

KindHash pinned
datum_documentThe document-id version hash.
boundary, taxonomy, profiles, eventsThe MSS bitstream hash of the document's datum closure.

The two hashes differ for the same document. A stale pin is a fault and is fixed by re-pinning. A missing or unverifiable source is reported, not treated as a failure.

Citation

A hyphae (micyte/core/mss/hyphae.py, schema mycite.v2.mos.hyphae.v2) is the network's citation form. build_hyphae(address, *, index, document, source_msn) requires the document, refuses a document that does not contain the address, and writes the canonical document_id into the payload. Hyphae v1 is refused. A datum address is local to its document; the same address appears in many documents, so a closure is resolved through DocumentScopedIndex (micyte/core/mss/document_adapter.py), which reads the document's own rows first.

Not built

  • Discovery without a contract. There is no published registry an instance can pull, without a contract, to find a peer's card. An instance must already know the peer.
  • Resolving a magnet reference on a remote instance. The reference form hy.<msn>.<sandbox>.<document>.<version_hash>.<address> is defined; no operation resolves it across instances.
  • A rotation policy. Expiry is time-based only; whether rotation re-handshakes or ratchets is undecided.
  • Joining from an open channel. Joining requires a contract, so a browser on an open channel cannot join. /__channel/ has no write route and will not get one.
  • Delivery between two instances on different machines. It has not happened. fnd_app/tests/integration/test_contract_delivery_over_tcp.py runs two instances over a real socket with separate private directories.

fnd_app/packages/modules/cross_domain/network_root/service.py is a presenter over the local audit log. Its contract_id and contract_filters filter audit rows and are unrelated to network contracts.