Skip to main content

Module cooperation

Module cooperation 

Source
Expand description

Cooperation: the runtime that turns discovered, trusted runtimes of one repository into linked peers and keeps their journal converged. It owns the one peer-link table, the one journal of this runtime, the link handlers the HTTP surface answers, the dialers, the heartbeat and the expiry — and the operations (sessions, claims, handovers, reviews) every surface performs through it.

The sequence one link goes through, and where each step is decided:

candidate  a trusted registry record of the same repository, or a declared seed
   ↓
dial       hello → welcome  (link::)  signature, freshness, nonce, protocol,
                                       repository, self, trust — both directions
   ↓
connected  sync every heartbeat: marks + missing events, both ways, signed
   ↓        a failed round: degraded → unreachable (backoff, capped at the expiry)
expired    no exchange within the expiry: the link is dropped, the runtime's
   ↓        streams stop beating everywhere, its claims stop excluding
reconnect  the dialer says hello again; an unknown link id or a new instance is a
            re-handshake, never a second peer — the table is keyed by runtime

StreamLiveness has two layers and they are deliberately separate. A link is this runtime’s connection to one peer (connected, degraded, unreachable, expired). A stream is alive while its beat keeps rising anywhere in the mesh — a runtime linked only through a relay is still alive, and a runtime that crashed stops beating for everyone at once. Claims follow streams, not links.

The whole of it, two runtimes in one process. Only the socket is replaced: the handshake, the signatures, the admission and the fold are the ones a LAN takes.

use std::collections::BTreeMap;
use std::sync::{Arc, Mutex, Weak};

use majordomus_cli::mesh::config::{CooperationConfig, TrustConfig};
use majordomus_cli::mesh::cooperation::{CheckoutFacts, Cooperation, CooperationSetup};
use majordomus_cli::mesh::identity::NodeIdentity;
use majordomus_cli::mesh::journal::{ClaimMode, SessionInfo};
use majordomus_cli::mesh::link::{LinkReply, LinkTransport, Signed, HELLO_PATH};
use majordomus_cli::mesh::registry::MeshRegistry;
use majordomus_cli::mesh::repository::{of_root_commits, runtime_id};
use majordomus_cli::mesh::trust::TrustPolicy;

#[derive(Default)]
struct Net(Mutex<BTreeMap<String, Weak<Cooperation>>>);

impl LinkTransport for Net {
    fn post(&self, endpoint: &str, path: &str, m: &Signed) -> Result<LinkReply, String> {
        let peer = (self.0.lock().unwrap().get(endpoint).and_then(Weak::upgrade))
            .ok_or_else(|| format!("{endpoint}: nobody there"))?;
        Ok(if path == HELLO_PATH { peer.accept_hello(m) } else { peer.accept_sync(m) })
    }
}

let net = Arc::new(Net::default());
let runtime = |endpoint: &str| {
    let c = Cooperation::new(CooperationSetup {
        identity: Arc::new(NodeIdentity::ephemeral().unwrap()),
        runtime: runtime_id(endpoint),
        repository: of_root_commits(&["root".into()]),
        endpoints: vec![endpoint.into()],
        version: "doc".into(),
        config: CooperationConfig::default(),
        trust: TrustConfig { policy: TrustPolicy::Tofu, allow: vec![] },
        journal_path: None,
        registry: Arc::new(MeshRegistry::new()),
        transport: Arc::clone(&net) as Arc<dyn LinkTransport>,
        board: None,
        checkout: CheckoutFacts::default(),
    })
    .unwrap();
    net.0.lock().unwrap().insert(endpoint.into(), Arc::downgrade(&c));
    c
};

let (a, b) = (runtime("a:1"), runtime("b:1"));
let key = a.dial(&["b:1".into()]).expect("B welcomes A");
assert_eq!(key, b.runtime_key(), "the link is keyed by the peer's runtime");

a.claim(&SessionInfo::named("s1", "cli"), vec!["apps".into()], None,
    ClaimMode::Exclusive, None).unwrap();
for _ in 0..2 {
    a.journal().beat_own();
    a.sync_with(&key).unwrap();
}
assert_eq!(b.state().claims.len(), 1, "the claim crossed the link");
assert_eq!(a.state().digest, b.state().digest, "one state, two runtimes");

Structs§

CheckoutFacts
What the projected sessions say about the checkout they work in. Computed once at start — branch and head are refreshed when a board session changes, never on a heartbeat, so liveness never rescans the repository.
Cooperation
One runtime’s whole part in the mesh, behind one handle. It holds the single journal this process writes to, the single table of links it has, and the admission lock that decides claims — and it is deliberately one object rather than a set of services, because every one of those is the other’s context: a claim is admitted against state folded from events that arrived over a link, and a link is admitted against trust the same object evaluates.
CooperationCounters
The link and replication counters since start: what this runtime did, not what it holds. They are monotonic and never reset, so two readings a minute apart are a rate — which is the only way to tell a mesh that is working from one that is retrying, since both show the same peers in the same states.
CooperationSetup
Everything one runtime needs before it can link to another, gathered in one place so that the runtime never reaches for it later. Each field is a decision somebody else already made — the declaration’s policy, the checkout’s identity, this process’s key, the socket to speak through — and passing them in is what lets a test replace any of them without a fixture: a fake transport, an ephemeral key, a journal that never touches the disk.
CooperationStatus
The whole cooperation runtime, as every surface reports it: the command line, the HTTP surface and MCP all render this one value, so that a person and an agent asking the same question of the same runtime are never told different things.
MeshVerifyCheck
One verification check: what was checked, whether it holds, the evidence, and — when something is wrong or limited — the impact and what to do about it.
MeshVerifyReport
What mesh verify found: the local checks, every peer’s round, and the state digest afterwards. The digest is what makes the report worth running on two machines — two runtimes that print the same digest have the same sessions, claims, handovers and reviews, and two that do not have something to reconcile, whatever their links say.
PeerVerdict
What one sync round, run now rather than remembered, said about one peer. Verification does not report the link’s standing and stop there: it exchanges with the peer and says whether the exchange worked and whether both sides ended holding the same marks, which is the difference between a peer that answers and a peer that agrees.
PeerView
One peer as every surface shows it: who it is, how this runtime reaches it, and what the link has been through. The counters are carried per peer rather than only in the totals because the question a person asks of a flapping mesh is which peer is flapping, and restarts answers a different one from reconnects — a peer whose process keeps dying, against a link that keeps dropping under a process that never did.
RefusedView
A candidate that was refused, with the rule that refused it. A refusal is kept and shown rather than logged and dropped, because the commonest mesh failure is a link that never forms: a person who declared a seed and sees no peer needs the decision that was made about it — the wrong repository, a key nobody allowed, a clock too far out — and the direction says which side made it.
Written
What an operation wrote, as its answer to the caller. Every mesh operation is an event appended to this runtime’s journal, and the caller is handed the three facts it cannot work out for itself: the mesh-wide key of the thing it created, the event that says so, and the Lamport stamp that places it against everyone else’s events.

Enums§

CooperationError
Why a cooperation operation did not happen. Every variant is a decision rather than a fault: nothing here is a transport failure or a bug, so a caller that sees one has been told something true about the mesh and should say it to its user rather than retry.
LinkState
A link’s standing, from this runtime’s side. It is a statement about a connection and never about the peer: a runtime reachable only through a third party is Expired here while its streams beat on everywhere, which is why claims follow streams and not this.
RoundError
What a dial or a sync round did not do. The three are kept apart because they are acted on differently: an unreachable peer is retried with a backoff, a refusal is a decision that will be made the same way next time and is recorded rather than retried, and the absence of a link is this runtime’s own state and not the peer’s.

Constants§

MAX_PEERS
The most peers one runtime links to.
MAX_TARGETS_PER_NODE
The most runtimes of one node this runtime dials.
PEER_RETENTION
How long an expired peer stays listed before it leaves the table.