Skip to content
Cockpit0.29.0
A snapshot: read at commit 707a04c7ce8 on 2026-10-18T00:06:01Z by majordomus 0.29.0. Nothing on this page is read again. See it live

project.shared-server-resilience@1

Nothing a client leaves behind locks another client out

What it is

URI
majordomus://rule/project.shared-server-resilience@1
Kind
rule
Identity
project.shared-server-resilience@1
Path
.ai/repo/rules/project/shared-server-resilience.v1.md
Provenance
declared
Media type
text/markdown
As a capability
rule.project.shared-server-resilience@1

Front matter

{
  "id": "project.shared-server-resilience",
  "version": 1,
  "kind": "rule",
  "title": "Nothing a client leaves behind locks another client out",
  "description": "The shared MCP server is a client's child; whatever state a client or a dead server leaves behind is taken over or bypassed, and a client that cannot share is served alone, never turned away.",
  "statement": "A client of the shared server always gets a server, or is served alone with the reason logged; no lease file in any state, no signal, no unwritable directory and no departed peer may leave a client without one.",
  "status": "active",
  "class": "blocking",
  "depends_on": [
    "project.rust-cli-evidence@1"
  ],
  "tags": [
    "rust",
    "mcp",
    "resilience"
  ],
  "x-majordomus": {
    "tests": [
      "test/cases/90_mcp_shared_server.sh",
      "scripts/rust-check"
    ]
  }
}

Content

---
id: project.shared-server-resilience
version: 1
kind: rule
title: Nothing a client leaves behind locks another client out
description: The shared MCP server is a client's child; whatever state a client or a dead server leaves behind is taken over or bypassed, and a client that cannot share is served alone, never turned away.
statement: A client of the shared server always gets a server, or is served alone with the reason logged; no lease file in any state, no signal, no unwritable directory and no departed peer may leave a client without one.
status: active
class: blocking
depends_on: [project.rust-cli-evidence@1]
tags: [rust, mcp, resilience]

x-majordomus:
  tests: [test/cases/90_mcp_shared_server.sh, scripts/rust-check]
---

# Rationale

The shared server is the one process several clients depend on, and its only shared state
is one lease file under the checkout-local half of the layer. Anything that file can
contain after a crash, a kill or a copy of the checkout is a way for one client to lock
the others out, and a repository whose local half refuses writes would otherwise turn
every client away. The design refuses a daemon; what it owes in return is that the process
a client started never fails to serve that client because of what another process left
behind.

# Required behaviour

Under `apps/majordomus-cli/`:

- The election reads the lease on every attempt and classifies it. A server that answers
  for this root is attached to. A lease whose server does not answer, a file that is not a
  lease document, an empty file, and a lease whose owner published no URL within the bind
  grace are each taken over, and the log names which it was. A lease without a URL that is
  younger than the grace is waited for. The attempt is bounded by time, never by a count
  of rounds.
- When the lease cannot be created, joined or replaced, or the shared server cannot start,
  `majordomus mcp` serves its client alone, as `--standalone` does, and logs `cannot use
  the shared server` with the path and the reason. The layer's own errors (no manifest,
  `--strict` refusing a degraded index) still exit with their codes: degrading applies to
  sharing, never to the layer.
- A bridge whose server is gone elects again on its next message; when it can neither
  serve nor attach, the same degrade applies before any error reaches the client.
- `SIGTERM`, `SIGINT` and `SIGHUP` remove the lease before the process dies of the signal.
  `kill -9` cannot be caught; the stale lease it leaves falls under the first point.
- A server whose owner has left ends with its last attached session; an HTTP session that
  never says goodbye expires at the idle timeout, so the server never outlives its clients
  by more than that timeout.
- A server whose lease has been taken over stops acting as the checkout's server, and says
  so before anybody has to ask twice. Its index answers `leaseholder: false`; the probe
  every reader of a remembered address goes through refuses it; a new `initialize` is
  refused with the reason and where the current server is; and its own health report says
  that what it describes is another process. The sessions it already had continue, and the
  process ends with them — a client mid-answer does not lose its server because somebody
  rebuilt the executable. What it must never do is take on a client that could have had
  the real one: that client gets a peer board, an execution history and a generation of the
  layer no other client of the checkout can see, with nothing in any answer saying so.
- A bind to an address that is not loopback is served with a warning naming what it
  exposes.
- A bridged session and a restarted server answer byte for byte what a local session
  answers.

Every point above is proved by a named case in `tests/mcp_shared.rs`, in
`tests/lease_lost.rs` or in `test/cases/90_mcp_shared_server.sh`, and a change to any of
them lands with its case.

# Failure behaviour

CI fails: `cargo test` runs `tests/mcp_shared.rs` in the `rust` job, and `test/run.sh`
runs case 90 in the `test` and `pages` jobs. A reviewer refuses a change to the lease, the
election, the bridge's failover or the signal handling that does not come with its case,
and a claim page for `mcp-lease-resilience` whose test no longer names the behaviour.

# Verification

`RUSTFLAGS='' cargo test --manifest-path apps/majordomus-cli/Cargo.toml --test mcp_shared`,
`RUSTFLAGS='' cargo test --manifest-path apps/majordomus-cli/Cargo.toml --test lease_lost`,
`bash test/run.sh 90_mcp_shared_server`, `scripts/rust-check`, and the claim
`mcp-lease-resilience` in `docs/CLAIMS.yaml`.