# Legible — experimental two-participant LAN bench

This bench lets two participants save private drafts, share selected versions
and reply with related formulations through a browser on their own Wi-Fi.
The documented Android path uses manual file exchange with Gemini. Its purpose
is to test how easily one participant can describe work and another can
understand and continue it. Stop after the manual friction test below.
The bench provides no public deployment, model backend, collection packaging
or semantic retrieval.

The browser workflow and MCP use the same Python draft store and Rust
validation/build path. Genealogy 0.1 stays the only draft format. An explicit
core proposal creates a new unsigned Legible declaration with exact draft bytes as
evidence. Sharing a draft never signs it or exports evidence files.

## Host setup

The bench uses three local source directories: protocol contains the technical
components; website contains the reading site and its generator; publications
contains the book and papers used by the generator. The maintainer
can prepare them before creating or populating any remote repository. After
publication, participants can obtain each directory from its GitHub repository.

A prepared local delivery already includes the protocol and publications build dependencies. If
starting with separate folders, attach the matching local files from website:

~~~sh
cd website
python3 tools/attach_protocol.py --source ../protocol
python3 tools/attach_publications.py --source ../publications
~~~

The helpers verify the exact files identified by `protocol-source.json` and
`publications-source.json` before copying ignored local dependencies. Select
matching source revisions if either helper reports a mismatch. The site does not distribute complete source archives.
Use Python 3.11+, Rust 1.88+, OpenSSL or LibreSSL with `req -x509` and support
for extensions supplied through a configuration file, and existing MkDocs
dependencies. The initializer does not require `req -addext`.
The second machine/Android device only needs a browser for the
participation circuit; do not install Rust on the phone.

From the website directory:

~~~sh
cargo build --manifest-path protocol/reference/legible-core/Cargo.toml
python3 -m venv .venv
.venv/bin/python -m pip install -r site/requirements.lock
.venv/bin/python site/legible_build.py --mode preview
export PYTHONPATH="$PWD/protocol/reference/legible-mcp"
export LEGIBLE_BIN="$PWD/protocol/reference/legible-core/target/debug/legible"
.venv/bin/python -m legible_mcp.pilot init --runtime "$PWD/.local/pilot" --lan-host YOUR_WIFI_IP --participant your-id --participant other-id
.venv/bin/python -m legible_mcp.pilot doctor --runtime "$PWD/.local/pilot"
.venv/bin/python -m legible_mcp.pilot serve --runtime "$PWD/.local/pilot" --bind 0.0.0.0
~~~

Replace your-id and other-id with two distinct handles: lowercase ASCII letters,
digits and single hyphens. Each handle identifies a private store and declared
draft author, not a verified civil identity or DID. Use diogo and manu to
reproduce the initial pilot. Omission retains that example for existing scripts.
No existing runtime is renamed or migrated by this option.

Reuse the existing environment when already installed. Initialize once: a
second init refuses to overwrite private stores or credentials. On macOS,
ipconfig getifaddr en0 usually reports Wi-Fi IPv4; check the actual interface
in network settings if different. Do not use 0.0.0.0 as a browser address.

Open https://YOUR_WIFI_IP:8443/pt/ (or /en/) for the static site and
https://YOUR_WIFI_IP:8443/pilot/ for the private workflow. API: /api. MCP: /mcp.
Port 8000 remains a separate static-only preview. Use a network that permits
device-to-device connections. Keep the host awake; allow this Python process
through the firewall on the trusted LAN only. No port forwarding/public tunnel.

The server creates a 30-day local TLS certificate, not a record-signing key.
Transfer only tls-cert.pem to the peer through a trusted local route. Compare
its SHA-256 fingerprint with the host:

~~~sh
openssl x509 -in .local/pilot/tls-cert.pem -noout -fingerprint -sha256
~~~

Trust that certificate in the test browser/OS according to the device settings.
Android menus vary; look for certificate installation / encryption and
credentials and CA trust for HTTPS, not a Wi-Fi authentication certificate.
A managed device may prohibit this. Do not disable certificate verification
in the MCP client. Confirm trust on the actual Android device before using
confidential material. The host does not modify system trust stores automatically.
Remove pilot trust after testing. If IP changes/certificate expires, stop and
configure a separate new runtime rather than overwriting contributions.

## Participant access

Give each participant only their own .local/pilot/YOUR_ID-access.json through
a trusted channel. Paste browser_token into the page.
Never paste credentials into Gemini, a public issue, URL or screenshot.
MCP uses mcp_token, a separate credential with no share/evidence-read tool.
Credentials stay in browser memory; reload/log out clears local access.
Revocation means stopping the server, changing the affected secret in config.json
privately and restarting; update the access file too. Closing a browser does
not revoke a copied token.

Private repositories: .local/pilot/participants/<participant>. Shared drafts
are exact selected copies in the recipient's store; private drafts, records
and evidence do not become a shared corpus. Origin identifies this pilot
instance and participant; it is declared, not a signing identity. Draft
author claims remain unauthenticated.

The boundary protects application requests, not against the OS administrator,
malware, stolen credentials or an unrestricted terminal agent. Separate OS
accounts/sandboxing are needed for those threat models. Files are not encrypted
at rest; these stores are not an encrypted CH implementation.

## Android and Gemini

Copy the page's ordinary-work instruction and download Genealogy 0.1. Attach it
in Gemini, or paste its contents if attachments are unavailable. Google cannot
be presumed to read private local URLs. Select only authorized work/context.
Gemini provides the model under your account; the server makes no provider calls.

Work normally. Request a small draft describing directions, criteria, changes
of course and judgment, with actual artifact/version references. Paste Markdown
back into the page, revise and save privately. Select a version and recipient;
review the sharing dialog. The recipient refreshes, reads, prepares a related
Trace or new formulation, then selects a reply to share.

After a draft has been shared, prepare a new Appoint draft and a separate
Trace draft for a substantial reformulation under Genealogy 0.1; save/share
both when relevant. Do not upload restricted evidence just because a shared
draft cites it. Test with a fictional text artifact in the private evidence field;
cite its hash and confirm the peer cannot retrieve it. This helper accepts UTF-8
text, not binary artifacts. Real files may remain in existing authorized storage.

This recipe uses manual attachment or paste rather than direct Gemini access
to the local server. Check the linked official guidance for the attachment,
account and administrator conditions that apply to the actual device.
The local credential endpoint does not implement a Gemini custom app, and
this bench does not establish native Gemini-to-LAN compatibility.

Sources: [Android attachments](https://support.google.com/gemini/answer/14903178?co=GENIE.Platform%3DAndroid&hl=en),
[custom apps](https://support.google.com/gemini/answer/17209137?hl=en),
[Android certificates](https://support.google.com/pixelphone/answer/2844832?hl=en).

## MCP from a second desktop

Copy the MCP sources, peer access file and public TLS certificate to that desktop.
No signing key travels with them. From the protocol root:

~~~sh
export PYTHONPATH="$PWD/reference/legible-mcp"
python3 -m legible_mcp.probe --url https://YOUR_WIFI_IP:8443/mcp --access /PRIVATE/PATH/YOUR_ID-mcp-access.json --ca /PATH/tls-cert.pem
~~~

The probe negotiates, initializes, lists tools and retrieves the accessible draft
count, without printing credentials/content. It closes its session. A local MCP
host needs Streamable HTTP, the participant Bearer header and verified CA trust.
Configuration syntax varies; no real third-party desktop host is certified by
this probe. A client requiring OAuth will not work with this credential profile.

HTTP uses JSON responses, no SSE, POST and GET=405; it negotiates 2025-03-26,
2025-06-18 and 2025-11-25. Older stdio support is not advertised on HTTP.
Initialize, retain Mcp-Session-Id, send notifications/initialized and use
MCP-Protocol-Version subsequently. Sessions expire after one hour, are
participant-bound and close with DELETE. Browser and MCP credentials differ.
Host/Origin checks are enforced.

Stdio now requires LEGIBLE_REPO_ROOT. Set LEGIBLE_PARTICIPANT,
LEGIBLE_DRAFT_ORIGIN and LEGIBLE_AUTHOR_DID explicitly; use the pilot config's
values if accessing that participant store. Without a root, tools fail closed.
Repo arguments cannot choose another root. Verification is restricted to
legible record files; packet imports to imports/ inside the participant root.
Store symlinks and external discovery are refused.

References: [MCP transports](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports).
Standard optional [MCP OAuth](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization)
is not implemented by this local credential profile.

For an existing stdio host, use the local lan_stdio bridge, not a second writer
opening the pilot's files. It forwards messages over verified TLS to the same
participant-scoped server. Replace paths in this host configuration template:

~~~json
{
  "mcpServers": {
    "legible-pilot": {
      "command": "/ABSOLUTE/PATH/TO/python3",
      "args": [
        "-m", "legible_mcp.lan_stdio",
        "--url", "https://YOUR_WIFI_IP:8443/mcp",
        "--access", "/PRIVATE/PATH/YOUR_ID-mcp-access.json",
        "--ca", "/PATH/tls-cert.pem"
      ],
      "env": {"PYTHONPATH": "/YOUR/CHECKOUT/reference/legible-mcp"}
    }
  }
}
~~~

Use each participant's own YOUR_ID-mcp-access.json. These
contain only the MCP credential; never give an agent host the browser access
file. Init creates both. Existing runtimes can create missing MCP-only files
with the access operation, which refuses overwrites. Keep the server as the sole
writer for pilot stores; standalone stdio deployments use their own repository.
The Python library separately supports concurrent local draft ingestion through
atomic creation without replacement on filesystems supporting hard links. That
limited support does not make all pilot or CH storage operations transactional.
Test permissions and installation in the actual host. The probe/bridge establishes
local transport behavior, not certification of every AI host.

## Optional formal preparation

No DID is needed for drafts. For a formal proposal, enter your public plain
Ed25519 did:key and associate it with your own access. This is a claim, not
proof of key control. Review the exact content and the NCO adopted by the signed
schema: [canonical Portuguese v2 text](../spec/editions/v2/nco-PT.txt) and
[English reference translation](../spec/editions/v2/nco-EN.txt). Review the NRC
conditions for an Appoint as well. Appoint has
a starting template; Trace/Nod need an explicit core because references, weights
and consequences must not be invented. Rust applies its implemented structural
checks to every submitted core. See [first Appoint, step 3](first-appoint.md#3-build-the-engine-validates--and-never-signs)
for their limits; passing these checks does not establish full conformance.

Prepare/download the new unsigned file. Inspect the exact core, including its
draft evidence digest. In a trusted terminal, copy the exact ID printed during preparation and set
RECORD_PATH to the downloaded file. Set LEGIBLE_SIGNING_KEY to your own key
path. From the protocol checkout root, pass the key as the third argument:

```sh
ID='<exact ID printed during preparation>'
RECORD_PATH='/absolute/path/to/downloaded-record'
LEGIBLE_SIGNING_KEY='/absolute/path/to/your/key'
adapters/git-native/sign.sh "$ID" "$RECORD_PATH" "$LEGIBLE_SIGNING_KEY"
```

Replace every placeholder, review the exact declaration, and confirm interactively.
The script checks the supplied ID against the inspected content before signing.

The server stores no declaration-signing
keys. Follow first-appoint.md for exact review/sign/seal/validate/verify. Do not
blindly run a host-path signing command on another computer. Private signed
storage is valid; browser import/sharing of signed files is not delivered here.

## Pause and friction test

The browser and MCP share the explicit partial-search contract: healthy results
remain listed, while `complete: false` and `issues` identify unreadable entries.
The browser keeps a separate warning visible even after another operation updates
its status. Draft reads/exports still check the exact selected version; another
entry's corruption does not block a healthy target. Per-participant and symlink
boundaries are unchanged. No automatic repair, deletion or external sending occurs.

A conflict diagnostic may identify only LF/CRLF differences when a known original
is available. The page explains this without changing bytes or manufacturing a
new contribution. Resend the exported original and manifest together. Origin
and author remain unauthenticated declarations; a hash is not proof of identity.

Use one short real work segment, then stop. Measure:

- time from judgment to an acceptable draft;
- corrections to author, formulation, context, dependency and evidence;
- Gemini/browser switches and attachment/copy operations;
- time for the other participant to understand and return a situated contribution;
- whether that reply changed a choice or clarified an alternative;
- model inventions, permission confusion and validation failures.

Test unchanged resend, substantial reformulation plus Trace, ambiguous speakers,
restricted evidence and private formal preparation. Do not score absence.
Compare against the same artifact plus deliberately maintained ordinary notes,
with the same AI and similar effort. This measures correctness and friction,
not economic superiority. Gather feedback from this test before extending the
bench to collection packaging or contextual retrieval.
