Reconnect and renumber a dormant FreeBSD/CBSD fleet host behind the OpenWrt firewall: deterministic switch-inventory/reconcile/attach/cutover steps driven by the harness, with a PLAN/APPLY split, journaled state transitions (RollbackFailed is terminal), and stale-state refusal. Pure decision logic here; DB/subprocess/model I/O lives in the binary. Sprint 4.48.
| Tier | tools |
| Role | tooling |
| Path | crates/tools/fleet-reconnect |
| Edition | 2021 |
| Targets | fleet-reconnect, tools_fleet_reconnect |
| Public items | 143 across 7 modules |
| Tests | 121 |
What it is for
tools-fleet-reconnect pure core (sprint 4.48).
The decidable halves of the fleet-host cutover, kept free of DB, subprocess, and model I/O so every branch is testable and mutation-provable — the same pure/impure split tools-work-runner uses. The binary wires ssh/swconfig/ uci/cbsd/psql as argument vectors around these functions; nothing here shells out or talks to a model.
Gate 1.5 (2026-09-04) adopted redlines encoded here: a PLAN/APPLY split with stale-state refusal, a per-jail transition machine whose RollbackFailed is a first-class terminal state, and a typed reachability verdict (not "ping worked").
Capabilities
crate root
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
fn next_state(from : JailState, on : Event) -> Result <JailState, IllegalTransition> |
fn guard_apply(plan : & CutoverPlan, live : & Observed) -> ApplyGuard |
fn verdict(p : ProbeInputs) -> Reachability |
fn upsert_host_psql(node_id : & str, ip : Ipv4Addr) ->(String, Vec <(String, String)>) |
AddrError
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub enum AddrError |
fn remap_preserving_octet(source : Ipv4Addr, src24 : u8; 3, dst24 : u8; 3,) -> Result <Ipv4Addr, AddrError> |
ApplyGuard
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub enum ApplyGuard |
CutoverPlan
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub struct CutoverPlan |
Event
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub enum Event |
IllegalTransition
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub struct IllegalTransition |
JailState
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub enum JailState |
fn is_terminal(s : JailState) -> bool |
Observed
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub struct Observed |
ProbeInputs
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub struct ProbeInputs |
Reachability
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub enum Reachability |
RunState
tools-fleet-reconnect pure core (sprint 4.48).
| Item |
|---|
pub enum RunState |
attach (other)
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
fn parse_default_via(route : & str) -> Option <Ipv4Addr> |
fn plan_attach(live : & BTreeMap <u16, Vec <VlanMember>>, pvids : & BTreeMap <u8, u16>, persisted : & BTreeMap <u16, UciVlanSection>, cpu_port : Option <u8>, port : u8, vlan : u16,) -> Result <AttachDecision, AttachRefusal> |
pub const CONFIG_PATH: & str |
pub const LOCK_DIR: & str |
fn backup_path(ts : & str) -> Option <String> |
fn lock_script() -> String |
fn unlock_script() -> String |
fn parse_revert_output(out : & str) -> Option <bool> |
fn attach_next(from : AttachState, on : AttachEvent,) -> Result <AttachState, IllegalAttachTransition> |
ApplyOutcome
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
pub enum ApplyOutcome |
fn parse_apply_output(out : & str) -> ApplyOutcome |
AttachDecision
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
pub enum AttachDecision |
AttachEvent
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
pub enum AttachEvent |
AttachPlan
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
pub struct AttachPlan |
fn revert_commands(plan : & AttachPlan) -> String |
fn apply_script(plan : & AttachPlan, backup : & str, postcondition_target : Ipv4Addr) -> String |
fn revert_script(plan : & AttachPlan) -> String |
fn persist_script(plan : & AttachPlan) -> String |
AttachRefusal
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
pub enum AttachRefusal |
AttachRefusal :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result |
AttachState
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
pub enum AttachState |
fn attach_is_terminal(s : AttachState) -> bool |
IllegalAttachTransition
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
pub struct IllegalAttachTransition |
UciVlanSection
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
pub struct UciVlanSection |
fn parse_uci_vlans(uci : & str) -> BTreeMap <u16, UciVlanSection> |
VlanMember
attach: add an orphan switch port to a VLAN, PLAN then APPLY (sprint 4.48 unit 3).
| Item |
|---|
pub struct VlanMember |
fn parse_members(field : & str) -> Vec <VlanMember> |
fn canonicalize(members : & mut VlanMember) |
fn render_members(members : & VlanMember) -> String |
fn parse_live_vlans(show : & str) -> BTreeMap <u16, Vec <VlanMember>> |
verb:batch
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn batch_order(plans : Vec <CutoverPlan>) -> Vec <CutoverPlan> |
verb:by
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn by_address(bindings : & JailBinding) -> BTreeMap <Ipv4Addr, Vec <String>> |
verb:cbsd
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn cbsd_wrapped(argv : & String) -> String |
verb:constants
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
pub const MOVABLE_INTERFACES: & & str |
pub const CBSD_LIFECYCLE_TIMEOUT_SECS: u64 |
pub const CBSD_EXIT_MARKER: & str |
verb:jset
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn jset_argv(jail : & str, ip : Ipv4Addr, interface : & str) -> Vec <String> |
verb:jstart
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn jstart_argv(jail : & str) -> Vec <String> |
verb:jstop
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn jstop_argv(jail : & str) -> Vec <String> |
verb:observed
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn observed_of(b : & JailBinding) -> Option <Observed> |
verb:parse
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn parse_cbsd_exit(out : & str) -> Option <(i32, String)> |
verb:ping
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn ping_wrapped(ip : Ipv4Addr) -> String |
verb:validate
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
fn validate_iface(name : & str) -> Result <(), String> |
fn validate_workdir(path : & str) -> Result <(), String> |
fn validate_jname(name : & str) -> Result <(), String> |
PlanOutcome
The jail cutover, pure (sprint 4.48 unit 6): the PLAN half of
| Item |
|---|
pub enum PlanOutcome |
fn build_plans_on(bindings : & JailBinding, registered : & String, src24 : u8; 3, dst24 : u8; 3, target_interface : & str, only : & String,) -> Vec <PlanOutcome> |
fn build_plans(bindings : & JailBinding, src24 : u8; 3, dst24 : u8; 3, target_interface : & str, only : & String,) -> Vec <PlanOutcome> |
fn home_jails(outcomes : & PlanOutcome) -> Vec <String> |
detect (other)
Graduated detectors (sprint 4.48 unit 5): deterministic tools that own a
| Item |
|---|
pub const CLASS_NEIGHBOR_MAC_MATCH: & str |
pub const EXIT_ABSTAIN: i32 |
fn detect_neighbor_mac_match(shard : & Shard) -> Option <Verdict> |
Drift
Reconcile: diff the live CBSD jail set against the USD hosts record
| Item |
|---|
pub enum Drift |
fn diff_jails_vs_usd(live : & Jail, usd : & (String, Ipv4Addr)) -> Vec <Drift> |
gate (other)
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub const MAX_CANDIDATE_HOSTS: usize |
pub const MIN_CONFIDENCE: f64 |
pub const EXIT_DECIDED: i32 |
pub const EXIT_NEEDS: i32 |
pub const EXIT_REFUSED: i32 |
fn decide(shard : & Shard) -> GateDecision |
fn schema_text() -> & 'static str |
fn extract_json_object(text : & str) -> Option <& str> |
fn route_from_results(results : & (String, DetectorResult), shard : & Shard) -> Routed |
CandidateHost
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub struct CandidateHost |
fn parse_usd_candidates(psql_at : & str) -> Vec <CandidateHost> |
DetectorResult
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub enum DetectorResult |
Gate
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub const GATE_PORT_TO_HOST_MAP: & str |
pub struct Gate |
pub const PORT_TO_HOST_MAP: Gate |
GateDecision
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub enum GateDecision |
GraduationCandidate
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub struct GraduationCandidate |
Neighbor
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub const MAX_NEIGHBORS: usize |
pub struct Neighbor |
OrphanPort
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub struct OrphanPort |
Reuse
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub enum Reuse |
fn reuse(record : & VerdictRecord, live_shard_hash : & str) -> Reuse |
Routed
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub enum Routed |
Segment
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub struct Segment |
Segment :: fn contains(& self, ip : Ipv4Addr) -> bool |
Shard
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub const SHARD_VERSION: u32 |
pub const MAX_SHARD_BYTES: usize |
pub struct Shard |
fn build_shard(ports : & PortState, l3 : & BTreeSet <u16>, cpu : Option <u8>, arl : & BTreeMap <u8, Vec <String>>, hosts : & CandidateHost, neighbors : & (Ipv4Addr, String), segments : & (u16, Ipv4Addr, u8),) -> Result <Shard, ShardError> |
fn shard_bytes(shard : & Shard) -> String |
ShardError
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub enum ShardError |
ShardError :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result |
Verdict
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub enum Verdict |
fn parse_verdict(deliverable : & str, shard : & Shard, min_confidence : f64) -> VerdictOutcome |
fn verdict_target(v : & Verdict) -> Option <(u8, u16)> |
VerdictOutcome
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub enum VerdictOutcome |
fn exit_code(outcome : & VerdictOutcome) -> i32 |
VerdictRecord
The analysis gate, pure (sprint 4.48 unit 4).
| Item |
|---|
pub struct VerdictRecord |
fn attach_target_for(rec : & VerdictRecord, live_shard_hash : & str,) -> Result <Option <(u8, u16)>, String> |
graduation (other)
The graduation ratchet's data (sprint 4.48 unit 5): the binding table
| Item |
|---|
pub const TABLE_PATH: & str |
pub const HEADER: & str |
pub const MAX_ARGV_ITEMS: usize |
fn parse_table(text : & str) -> Result <Vec <Row>, TableError> |
fn clean_cell(text : & str) -> String |
fn validate_argv(argv : & String) -> Result <(), String> |
fn validate_commit(landed : & str) -> Result <(), String> |
Propose
The graduation ratchet's data (sprint 4.48 unit 5): the binding table
| Item |
|---|
pub enum Propose |
fn propose(rows : & mut Vec <Row>, gate : & str, cand : & GraduationCandidate, proposed_at : & str,) -> Propose |
Row
The graduation ratchet's data (sprint 4.48 unit 5): the binding table
| Item |
|---|
pub struct Row |
fn render_table(rows : & Row) -> Result <String, String> |
fn bind(rows : & mut Row, gate : & str, class : & str, tool_argv : & String, landed : & str,) -> Result <(), String> |
fn graduated_for <'a>(rows : & 'a Row, gate : & str) -> Vec <& 'a Row> |
Status
The graduation ratchet's data (sprint 4.48 unit 5): the binding table
| Item |
|---|
pub enum Status |
TableError
The graduation ratchet's data (sprint 4.48 unit 5): the binding table
| Item |
|---|
pub enum TableError |
TableError :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result |
parse (other)
Pure parsers for the read-only path (sprint 4.48 unit 2).
| Item |
|---|
fn parse_port_links(show : & str) -> Vec <(u8, bool)> |
fn parse_l3_vids(uci_network : & str) -> BTreeSet <u16> |
fn parse_cpu_port(show : & str) -> Option <u8> |
fn parse_default_gateway(netstat_rn : & str) -> Option <Ipv4Addr> |
fn parse_port_pvids(lines : & str) -> Vec <(u8, u16)> |
fn parse_arl_table(arl : & str) -> BTreeMap <u8, Vec <String>> |
fn parse_neighbors(ip_neigh : & str) -> Vec <(Ipv4Addr, String)> |
fn parse_segments(uci_network : & str, ip_addr : & str) -> Vec <(u16, Ipv4Addr, u8)> |
fn parse_usd_hosts(psql_at : & str) -> Vec <(String, Ipv4Addr)> |
IfAddr
Pure parsers for the read-only path (sprint 4.48 unit 2).
| Item |
|---|
pub struct IfAddr |
fn parse_ifconfig_inet(ifconfig : & str) -> Vec <IfAddr> |
Jail
Pure parsers for the read-only path (sprint 4.48 unit 2).
| Item |
|---|
pub struct Jail |
fn parse_cbsd_jls(jls : & str) -> Vec <Jail> |
JailBinding
Pure parsers for the read-only path (sprint 4.48 unit 2).
| Item |
|---|
pub struct JailBinding |
fn parse_cbsd_jls_bindings(jls : & str) -> Vec <JailBinding> |
PortState
Pure parsers for the read-only path (sprint 4.48 unit 2).
| Item |
|---|
pub struct PortState |
fn classify_orphans(ports : & PortState, l3_vids : & BTreeSet <u16>, cpu_port : Option <u8>,) -> Vec <u8> |
fn assemble_ports(links : & (u8, bool), pvids : & (u8, u16)) -> Vec <PortState> |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
tools_fleet_reconnect
attachcutoverdetectdiffgategraduationparse
flowchart TD n_tools_fleet_reconnect["tools_fleet_reconnect"] n_tools_fleet_reconnect --> n_attach["attach"] n_tools_fleet_reconnect --> n_cutover["cutover"] n_tools_fleet_reconnect --> n_detect["detect"] n_tools_fleet_reconnect --> n_diff["diff"] n_tools_fleet_reconnect --> n_gate["gate"] n_tools_fleet_reconnect --> n_graduation["graduation"] n_tools_fleet_reconnect --> n_parse["parse"]
Public surface
`crate root`
| Item | What it is |
|---|---|
pub enum AddrError | — |
fn remap_preserving_octet(source : Ipv4Addr, src24 : u8; 3, dst24 : u8; 3,) -> Result <Ipv4Addr, AddrError> | Map source (which must lie in src24.0/24) onto dst24.0/24, preserving the last octet |
pub enum JailState | — |
pub enum Event | — |
pub struct IllegalTransition | — |
fn next_state(from : JailState, on : Event) -> Result <JailState, IllegalTransition> | The only legal edges |
fn is_terminal(s : JailState) -> bool | A terminal state accepts no further events. |
pub enum RunState | — |
pub struct Observed | What we observed about a jail before planning. |
pub struct CutoverPlan | An immutable cutover plan for one jail (the APPLY step executes exactly this). |
pub enum ApplyGuard | — |
fn guard_apply(plan : & CutoverPlan, live : & Observed) -> ApplyGuard | Stale-state refusal: apply only if the live observation still matches the plan's observed |
pub struct ProbeInputs | — |
pub enum Reachability | — |
fn verdict(p : ProbeInputs) -> Reachability | Reachable iff the host owns the address AND the service answered. |
fn upsert_host_psql(node_id : & str, ip : Ipv4Addr) ->(String, Vec <(String, String)>) | The fleet host record's upsert for psql |
`attach`
| Item | What it is |
|---|---|
pub struct VlanMember | — |
fn parse_members(field : & str) -> Vec <VlanMember> | Parse a ports field such as 0t 1 2 into members (t suffix = tagged), in CANONICAL order (sorted by port) |
fn canonicalize(members : & mut VlanMember) | Sort members by (port, tagged): the one order every comparison uses. |
fn render_members(members : & VlanMember) -> String | Render members back into the ports field shape swconfig/uci accept. |
fn parse_live_vlans(show : & str) -> BTreeMap <u16, Vec <VlanMember>> | Live membership from swconfig dev switch0 show: each vid: N block's ports: line |
pub struct UciVlanSection | One persisted config switch_vlan section, addressed by its anonymous index. |
fn parse_uci_vlans(uci : & str) -> BTreeMap <u16, UciVlanSection> | Persisted membership from uci show network, keyed by vid. |
fn parse_default_via(route : & str) -> Option <Ipv4Addr> | The router's own default gateway from ip -4 route show default (default via A dev X ...): the WAN-liveness postcondition target, read live rather than hardcoded. |
pub struct AttachPlan | The immutable attach plan; APPLY executes exactly this. |
pub enum AttachDecision | — |
pub enum AttachRefusal | — |
AttachRefusal :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result | — |
fn plan_attach(live : & BTreeMap <u16, Vec <VlanMember>>, pvids : & BTreeMap <u8, u16>, persisted : & BTreeMap <u16, UciVlanSection>, cpu_port : Option <u8>, port : u8, vlan : u16,) -> Result <AttachDecision, AttachRefusal> | Decide the attach |
pub const CONFIG_PATH: & str | — |
pub const LOCK_DIR: & str | — |
fn backup_path(ts : & str) -> Option <String> | The backup path for a timestamp |
fn lock_script() -> String | Advisory lock (Gate 1.5.6 item 3), keyspace = this gateway's switch config: an atomic mkdir on the router itself, so two operators cannot reprogram the switch concurrently |
fn unlock_script() -> String | — |
fn revert_commands(plan : & AttachPlan) -> String | The live-revert commands for a plan (also printed for the operator). |
fn apply_script(plan : & AttachPlan, backup : & str, postcondition_target : Ipv4Addr) -> String | The router-side apply: take the lock, back up, apply live (never a network restart), assert the postcondition, revert on failure |
fn revert_script(plan : & AttachPlan) -> String | Revert from the workstation side (used when the live re-read after apply disagrees with the plan): restore, report, release the lock. |
fn parse_revert_output(out : & str) -> Option <bool> | Type a revert_script run: Some(true) restored, Some(false) failed, None when the marker is missing (ssh died — treat as failed). |
fn persist_script(plan : & AttachPlan) -> String | Persist the plan's after members into the resolved uci section |
pub enum ApplyOutcome | — |
fn parse_apply_output(out : & str) -> ApplyOutcome | Type the script's marker output |
pub enum AttachState | — |
pub enum AttachEvent | — |
pub struct IllegalAttachTransition | — |
fn attach_next(from : AttachState, on : AttachEvent,) -> Result <AttachState, IllegalAttachTransition> | The only legal edges; terminal states accept nothing. |
fn attach_is_terminal(s : AttachState) -> bool | — |
`cutover`
| Item | What it is |
|---|---|
pub const MOVABLE_INTERFACES: & & str | Interfaces a jail may be observed on and still be moved: yoda's NICs and cbsd's auto |
pub enum PlanOutcome | — |
fn build_plans_on(bindings : & JailBinding, registered : & String, src24 : u8; 3, dst24 : u8; 3, target_interface : & str, only : & String,) -> Vec <PlanOutcome> | Build the plans for every jail in src24, in the order given |
fn build_plans(bindings : & JailBinding, src24 : u8; 3, dst24 : u8; 3, target_interface : & str, only : & String,) -> Vec <PlanOutcome> | build_plans_on with every listed jail taken as registered: the pure decision table without the registry; the driver always passes the registry. |
fn validate_iface(name : & str) -> Result <(), String> | An interface name is a token, never text: A-Za-z0-9.+ (bge0.4) |
fn validate_workdir(path : & str) -> Result <(), String> | The cbsd workdir is an absolute path of tokens, never text: /A-Za-z0-9._/-+ (/jails, /usr/jails) |
fn ping_wrapped(ip : Ipv4Addr) -> String | The router-side reachability probe, wrapped like a cbsd command: the line exits 0 and carries ping's exit in the marker, so "the router cannot reach the address" (exit 1) and "the link to the router died" (no marker) are told apart |
fn validate_jname(name : & str) -> Result <(), String> | A cbsd jail name is a token, never text: A-Za-z0-9._-+. |
fn jstop_argv(jail : & str) -> Vec <String> | sudo -n cbsd jstop jname=<j> |
fn jset_argv(jail : & str, ip : Ipv4Addr, interface : & str) -> Vec <String> | sudo -n cbsd jset jname=<j> ip4_addr=<ip> interface=<iface> |
fn jstart_argv(jail : & str) -> Vec <String> | sudo -n cbsd jstart jname=<j> |
fn home_jails(outcomes : & PlanOutcome) -> Vec <String> | Jails a plan found already home |
pub const CBSD_LIFECYCLE_TIMEOUT_SECS: u64 | A cbsd start or stop may take minutes on a heavy jail; reads take seconds. |
pub const CBSD_EXIT_MARKER: & str | The marker the wrapped cbsd command ends with; the runner discards stdout on a non-zero exit (unit 3's finding), so the command exits 0 and the marker carries the real exit, with cbsd's own words kept above it. |
fn cbsd_wrapped(argv : & String) -> String | <argv> 2>&1; echo CBSD_EXIT=$? — the remote line for one cbsd command. |
fn parse_cbsd_exit(out : & str) -> Option <(i32, String)> | Split the wrapped output into (exit code, cbsd's text) |
fn batch_order(plans : Vec <CutoverPlan>) -> Vec <CutoverPlan> | Off jails first (no downtime), then On jails; each group in plan order. |
fn observed_of(b : & JailBinding) -> Option <Observed> | The observed binding a plan was built from, as guard_apply compares it. |
fn by_address(bindings : & JailBinding) -> BTreeMap <Ipv4Addr, Vec <String>> | Group bindings by address: the duplicate detector's input. |
`detect`
| Item | What it is |
|---|---|
pub const CLASS_NEIGHBOR_MAC_MATCH: & str | — |
pub const EXIT_ABSTAIN: i32 | Exit code for "not my class": the router runs the next binding. |
fn detect_neighbor_mac_match(shard : & Shard) -> Option <Verdict> | Decide iff exactly one orphan port qualifies: exactly one learned MAC, which the neighbor table binds to exactly one candidate host's recorded address, and exactly one segment contains that address |
`diff`
| Item | What it is |
|---|---|
pub enum Drift | — |
fn diff_jails_vs_usd(live : & Jail, usd : & (String, Ipv4Addr)) -> Vec <Drift> | Diff live jails (those with a static IPv4) against the USD (node_id, ip) rows |
`gate`
| Item | What it is |
|---|---|
pub const GATE_PORT_TO_HOST_MAP: & str | The one gate this sprint declares. |
pub const SHARD_VERSION: u32 | Bumped when the shard's shape changes; part of the shard, so the hash moves |
pub const MAX_CANDIDATE_HOSTS: usize | Caps (Gate 1.5.6 item 1): refuse over, never truncate. |
pub const MAX_NEIGHBORS: usize | — |
pub const MAX_SHARD_BYTES: usize | — |
pub const MIN_CONFIDENCE: f64 | A verdict below this confidence types as Needs. |
pub const EXIT_DECIDED: i32 | Exit codes are the probe contract: 0 decided, 3 NEEDS (escalate), 1 refused. |
pub const EXIT_NEEDS: i32 | — |
pub const EXIT_REFUSED: i32 | — |
pub struct Gate | The declared gate record. |
pub const PORT_TO_HOST_MAP: Gate | — |
pub struct OrphanPort | — |
pub struct CandidateHost | — |
pub struct Neighbor | One router neighbor-table entry (an address the router has resolved). |
pub struct Segment | One L3-bearing segment: the VLAN and the network it carries. |
Segment :: fn contains(& self, ip : Ipv4Addr) -> bool | — |
pub struct Shard | — |
pub enum ShardError | — |
ShardError :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result | — |
fn build_shard(ports : & PortState, l3 : & BTreeSet <u16>, cpu : Option <u8>, arl : & BTreeMap <u8, Vec <String>>, hosts : & CandidateHost, neighbors : & (Ipv4Addr, String), segments : & (u16, Ipv4Addr, u8),) -> Result <Shard, ShardError> | Build the shard from typed reads |
fn shard_bytes(shard : & Shard) -> String | The bytes the model sees and the hash is taken over |
pub enum GateDecision | — |
fn decide(shard : & Shard) -> GateDecision | — |
fn parse_usd_candidates(psql_at : & str) -> Vec <CandidateHost> | Parse psql -At rows `node_id\ |
pub struct GraduationCandidate | Unit-5 hook: a gate that solved a novel class names the tool that should own it next time |
pub enum Verdict | — |
pub enum VerdictOutcome | What a worker deliverable became after validation against the shard. |
fn schema_text() -> & 'static str | The schema text appended to the prompt: the exact deliverable shape. |
fn extract_json_object(text : & str) -> Option <& str> | The JSON object inside a deliverable that may carry prose around it: from the first { to the last }. |
fn parse_verdict(deliverable : & str, shard : & Shard, min_confidence : f64) -> VerdictOutcome | Parse and validate a deliverable against the shard it answers. |
pub struct VerdictRecord | The verdict file: the verdict plus the hash of the shard it answers. |
pub enum Reuse | — |
fn reuse(record : & VerdictRecord, live_shard_hash : & str) -> Reuse | — |
fn exit_code(outcome : & VerdictOutcome) -> i32 | — |
pub enum DetectorResult | What one graduated detector did with the shard. |
pub enum Routed | — |
fn route_from_results(results : & (String, DetectorResult), shard : & Shard) -> Routed | The routing decision, pure: bindings in table order, the first decisive one wins, abstention continues, failure refuses. |
fn attach_target_for(rec : & VerdictRecord, live_shard_hash : & str,) -> Result <Option <(u8, u16)>, String> | The attach target a deterministic step may act on, given the record and the hash of the shard live NOW: a record that answers a different world is refused (Gate 4 N1), a Needs record is refused (Gate 4 B1), NoOrphans is None (no change), a segment verdict is its target. |
fn verdict_target(v : & Verdict) -> Option <(u8, u16)> | The attach target a verdict names, if it names one. |
`graduation`
| Item | What it is |
|---|---|
pub const TABLE_PATH: & str | — |
pub const HEADER: & str | — |
pub const MAX_ARGV_ITEMS: usize | — |
pub enum Status | — |
pub struct Row | — |
pub enum TableError | — |
TableError :: fn fmt(& self, f : & mut std::fmt::Formatter <'_>) -> std::fmt::Result | — |
fn parse_table(text : & str) -> Result <Vec <Row>, TableError> | Parse the table |
fn render_table(rows : & Row) -> Result <String, String> | Render the table, header first, one row per line. |
pub enum Propose | — |
fn propose(rows : & mut Vec <Row>, gate : & str, cand : & GraduationCandidate, proposed_at : & str,) -> Propose | Add a candidate for (gate, class) |
fn clean_cell(text : & str) -> String | Model prose becomes one TSV cell: tabs, carriage returns and newlines (ordinary in a model's text) are folded to spaces so a proposal can never fail to render (Gate 4 B1, unit 5). |
fn bind(rows : & mut Row, gate : & str, class : & str, tool_argv : & String, landed : & str,) -> Result <(), String> | Bind a landed tool to a candidate: candidate -> graduated. |
fn graduated_for <'a>(rows : & 'a Row, gate : & str) -> Vec <& 'a Row> | The graduated bindings for a gate, in table order. |
fn validate_argv(argv : & String) -> Result <(), String> | A tool argv is data, never a shell line (same rule as a done-predicate). |
fn validate_commit(landed : & str) -> Result <(), String> | A landed commit is 7-40 lowercase hex characters. |
`parse`
| Item | What it is |
|---|---|
pub struct PortState | — |
fn parse_port_links(show : & str) -> Vec <(u8, bool)> | Parse swconfig dev switch0 show link lines: link: port:N link:up ... |
fn parse_l3_vids(uci_network : & str) -> BTreeSet <u16> | The VLAN ids that carry an L3 interface, from uci show network: any ...ifname='eth0.N' (or .ifname=eth0.N) contributes vid N. |
fn parse_cpu_port(show : & str) -> Option <u8> | The CPU (trunk) port from `swconfig .. |
fn classify_orphans(ports : & PortState, l3_vids : & BTreeSet <u16>, cpu_port : Option <u8>,) -> Vec <u8> | Orphan = link up AND the port's untagged VLAN (pvid) carries no L3 interface, EXCLUDING the CPU/trunk port (a host cannot plug into it) |
pub struct IfAddr | — |
fn parse_ifconfig_inet(ifconfig : & str) -> Vec <IfAddr> | Parse ifconfig -a inet lines, tracking the current interface from the ifname: flags=... header |
fn parse_default_gateway(netstat_rn : & str) -> Option <Ipv4Addr> | The IPv4 default gateway from netstat -rn -f inet (the default row). |
pub struct Jail | — |
fn parse_cbsd_jls(jls : & str) -> Vec <Jail> | Parse cbsd jls header=1 display=jname,ip4_addr,status,path (ANSI tolerated) |
pub struct JailBinding | A jail's network binding, from cbsd jls display=jname,ip4_addr,interface,status (unit 6): what cutover observes and what it moves. |
fn parse_cbsd_jls_bindings(jls : & str) -> Vec <JailBinding> | Parse cbsd jls header=1 display=jname,ip4_addr,interface,status (ANSI tolerated) |
fn parse_port_pvids(lines : & str) -> Vec <(u8, u16)> | Parse portN=V lines (the wiring emits one per port via get pvid). |
fn assemble_ports(links : & (u8, bool), pvids : & (u8, u16)) -> Vec <PortState> | Join link states with pvids into PortState rows (inner join on port). |
fn parse_arl_table(arl : & str) -> BTreeMap <u8, Vec <String>> | Parse swconfig dev switch0 get arl_table — one Port N: MAC xx:.. line per learned address — into per-port MAC lists (sorted, deduplicated) |
fn parse_neighbors(ip_neigh : & str) -> Vec <(Ipv4Addr, String)> | Parse ip -4 neigh show: `<ip> dev <dev> lladdr <mac> .. |
fn parse_segments(uci_network : & str, ip_addr : & str) -> Vec <(u16, Ipv4Addr, u8)> | Segments: (vid, network, prefix) for every uci interface on eth0.N, joined by device with ip -4 -o addr show (a bridge is br-<name>, else the ifname itself) |
fn parse_usd_hosts(psql_at : & str) -> Vec <(String, Ipv4Addr)> | Parse psql -At rows of `node_id\ |
No pub use re-exports: every item above is declared in this crate.
Boundary
Reaches into foundation, operations.
Shares tier tools with 84 other crates: tools-advisory-reach, tools-archive-guard, tools-artifact-scaffold, tools-ask-ai-core, tools-ask-ais, tools-ask-gemini, tools-book, tools-book-report, … (84 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | tools |
| Architectural role (taxonomy) | tooling |
| Location | crates/tools/fleet-reconnect |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_tools["tools"] --> n_foundation["foundation"] n_tools["tools"] --> n_operations["operations"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `foundation-bounded-io` | foundation | no | always |
| `operations-control-plane` | operations | no | always |
| `tools-packet-compile` | tools | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
anyhow | ^1 | — | no | always |
clap | ^4 | derive | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
sqlx | ^0.8 | runtime-tokio, postgres, chrono, uuid, json, runtime-tokio, … (8 total) | no | always |
tokio | ^1 | rt-multi-thread, macros, time | no | always |
uuid | ^1 | v4, v7, serde, js, v4, serde | no | always |
Development, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `tools-cli-conformance` | tools | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
tempfile | ^3 | — | no | always |
Build. None.
Depended on by. Nothing in this workspace.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR SELF["tools-fleet-reconnect"] SELF -->|development| n_tools_cli_conformance["tools-cli-conformance"] SELF -->|runtime| n_foundation_bounded_io["foundation-bounded-io"] SELF -->|runtime| n_operations_control_plane["operations-control-plane"] SELF -->|runtime| n_tools_packet_compile["tools-packet-compile"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| bin | fleet-reconnect | `src/main.rs` |
| lib | tools_fleet_reconnect | `src/lib.rs` |
Error model
| Error type | Named by |
|---|---|
AddrError | remap_preserving_octet |
ShardError | build_shard |
TableError | parse_table |
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | yes |
| database access | yes |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | yes |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
| Variable | Read in |
|---|---|
CARGO_PKG_VERSION | src/gate_run.rs |
CORPUS_SOURCECODE_APP_DB | src/run.rs |
Related capabilities
No workspace crate depends on this one.
Verification
| Kind | Count |
|---|---|
| Unit tests | 121 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
crate root | 16 | 0 | 0 |
attach | 29 | 0 | 0 |
cutover | 19 | 0 | 0 |
detect | 3 | 0 | 0 |
diff | 2 | 0 | 0 |
gate | 37 | 0 | 0 |
graduation | 15 | 0 | 0 |
parse | 18 | 0 | 0 |
What the tests establish, by name:
apply_output_types_every_marker_path—src/attach.rsapply_script_backs_up_applies_live_asserts_and_reverts—src/attach.rsattach_machine_happy_path_and_terminals—src/attach.rsattach_machine_revert_paths_are_terminal—src/attach.rsbackup_path_stays_under_etc_config_and_refuses_junk—src/attach.rsdefault_via_reads_the_router_gateway—src/attach.rslive_vlans_parse_every_vid_block—src/attach.rsmember_order_is_canonical_so_equality_is_set_equality—src/attach.rsmembers_round_trip_and_tag_suffix—src/attach.rspersist_script_targets_resolved_section_and_releases_lock—src/attach.rsplan_attaches_orphan_port_additively—src/attach.rsplan_is_no_change_when_already_attached_with_matching_pvid—src/attach.rsplan_refuses_cpu_unknown_and_moves—src/attach.rsplan_refuses_member_with_wrong_pvid—src/attach.rsplan_refuses_when_live_and_persisted_drift—src/attach.rsrevert_script_and_its_output_are_typed—src/attach.rsuci_sections_are_keyed_by_vid_not_index—src/attach.rsa_foreign_interface_refuses_and_bge1_and_auto_are_movable—src/cutover.rsa_shared_source_octet_refuses_both_jails—src/cutover.rsa_target_another_jail_holds_refuses—src/cutover.rsalready_on_target_is_no_change_and_outside_source_net_is_not_planned—src/cutover.rsan_unregistered_jail_is_refused_and_does_not_hold_an_address—src/cutover.rsbatch_runs_off_jails_before_on_jails_in_plan_order—src/cutover.rscbsd_argument_vectors_are_exact—src/cutover.rscbsd_output_is_kept_and_its_exit_typed—src/cutover.rshome_jails_are_the_no_change_outcomes_only—src/cutover.rsiface_is_a_token—src/cutover.rsjname_is_a_token—src/cutover.rsobserved_and_by_address_are_what_the_guard_and_the_detector_read—src/cutover.rsonly_narrows_to_the_named_jails—src/cutover.rs- _… 91 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 91 | 143 |
Public modules with a //! block | 7 | 7 |
pie showData
title Public items with rustdoc
"Documented" : 91
"No rustdoc detected" : 52
Metrics
| Metric | Value |
|---|---|
| Rust source files | 12 |
| Source lines | 8162 |
| Code lines | 6920 |
| Public API items | 143 |
| Public modules | 7 |
| Tests | 121 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 10 |
| Workspace reverse dependencies | 0 |
pie showData
title Public API by kind
"constant" : 20
"enum" : 23
"function" : 75
"method" : 4
"struct" : 21
pie showData
title Rust source composition
"Code" : 6920
"Blank or comment" : 1242
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.