Skip to content

Own the listener: LXMF inbox + edge/persist-only mode in the Rust, so the agent can delete ~1000 lines of Python orchestrating a transport it does not own #530

Description

@emooreatx

Summary

We are asking for LXMF plus a listener mode owned by the substrate, and — the reason this is worth doing now — we surveyed what the agent currently carries to make edge work. It is ~1,000 lines of Python orchestrating and observing a transport we do not own, none of which is agent-domain logic. Every line of it is a candidate to delete once the Rust owns the shape.

This follows CEG §0.0 layering and Edge OQ-01's library, not sidecar: the agent surfaces, the fabric produces.

The survey — what the agent carries today

ciris_engine/logic/runtime/edge_runtime.py1,224 lines, 42 substrate calls:

lines function what it actually is
525 initialize_edge_runtime node-key provisioning, alias reconciliation, Reticulum transport enable, canonical TCP auto-seed
487 _spawn_delivery_rooting_probe pure observability — polls delivery_status() / compose_status() and re-emits them as log lines
19 _seed_bootstrap_peers_into_edge peer seeding
16 should_reprime federation retry policy

plus, as module constants:

REPRIME_SCHEDULE = (45, 120, 300)
REPRIME_CADENCE = 180
SILENT_PEER_AFTER_REPRIMES = 2

Alongside it: node_fold.py (660 lines — composing the node around a running brain, port-bind checks, identity resolution), brain_adapter.py (179), substrate_logging.py (179), substrate_caps.py (80).

Why each of these is yours, not ours

The 487-line probe exists only because the accessors are in-process. Its own docstring says it: "the accessor is in-process to the server, so it MUST be logged here, not called from the runner's own process." The substrate already knows rooting state, KEX resolvability, envelope counts — we re-derive and re-log them in Python purely to make them visible. If the substrate emitted structured delivery/rooting events, this file section deletes.

The reprime schedule is transport policy living at the wrong layer. reprime_federation_delivery() is a substrate call, but when to call it is a tuple in our Python, tuned from a QA observation ("141 rounds, 21 Key envelopes sent, zero inbound, KEX never landed, window torn down before a second nudge"). Retry cadence belongs next to the transport that knows whether a peer is healing.

Node-key provisioning is already half yours and currently broken. See CIRISServer#528 — the substrate mints a node key when handed an actor key and the owner-binding cannot follow, so owner_of(node) never resolves. We carry the provisioning dance in Python around that.

The ask

1. LXMF, first-class

ciris_server already exports LxmfPropagationClient, LxmfPropagationNode, StoreAndForward, RelayNode, RetRelay, install_relay, rns_destination_hash, NetworkEventSubscription. What is missing is a supported way to run an inbox without a node — subscribe, hold, and signal, with no cognitive runtime attached.

2. A listener mode

A mode that boots edge + persist + the LXMF inbox and nothing else, identified by the node ID.

The identity question is already settled on our side: first run always ends with a node ID (the only variable is whether the user opted out of advertising to the federation directory), and provision_node_identity() already runs early. So the listener's identity is the node ID — which is exactly why #528 is a prerequisite, not a parallel concern: a listener that is the node ID inherits an unresolved owner-binding.

What it needs to expose:

  • boot to inbox-ready without composing a node or a brain
  • a heartbeat/readiness signal the host can poll (today the client polls /health on a server that may not have bound — see CIRISClient#28)
  • a wake signal on inbound LXMF
  • teardown that hands the inbox back cleanly

3. Own the delivery observability and the retry policy

Emit rooting/KEX/envelope state as structured events, and own the reprime cadence. Both currently live in our Python because they had nowhere else to live.

What the agent will do

Our side of this is smaller than it looks, and mostly already present:

  • init_edge_runtime is registered at InitializationPhase.DATABASEbefore MEMORY/IDENTITY/SECURITY/SERVICES. Edge and the node identity are up ~4s into boot, well before the 22 services. The listener boundary is "stop after DATABASE", not a new split.
  • BrainAdapter(upstream, runtime_boot=...) already implements the inverse topology and is passed by nothing. Its docstring: "runtime_boot remains for the inverse topology — a node that starts its own brain — which is not how the agent boots today." Wiring it is the wake path.
  • A staged-startup mechanism already exists (FIRST_RUN_SERVICES = 10 / RESUME_SERVICES = 12), though those ten are infrastructure and graph services with no transport — it proves the machinery, not the set.

So we can meet a listener mode quickly. What we cannot do is define it: the inbox, the identity, and the transport all live below us, and building them here would reintroduce exactly the sidecar key-leak surface OQ-01 rejected.

Platform note

iOS cannot hold a background listener at all — every Reticulum iOS client reports this. The working model there is store-and-forward plus a push (propagation node holds, APNs notifies, app wakes and fetches), which StoreAndForward + LxmfPropagationClient already suggest is on-architecture. Worth confirming that the listener mode's contract covers "wake, drain, sleep" and not only "stay resident", so one design serves both platforms.

Context: CIRISAgent#1131 (client-side process model and the Sideband reference), CIRISServer#528 (owner-binding prerequisite), CIRISClient#28 (test-server readiness).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions