pub struct MobileMeshSession { /* private fields */ }Expand description
Long-lived Rust protocol engine used by the mobile app.
ping is the only ping operation exposed to Swift. The existing Rust node
layer owns its nonce, authenticated echo request, counter reservation,
response matching, and timeout.
Implementations§
Source§impl MobileMeshSession
impl MobileMeshSession
pub async fn new( identity: Arc<MobileIdentity>, counter_store: Arc<MobileCounterStore>, ) -> Result<Arc<Self>, MobileMeshError>
pub fn ping( &self, peer_address: String, timeout_ms: u64, ) -> Result<u64, MobileMeshError>
Sourcepub fn node_public_key(&self) -> Vec<u8> ⓘ
pub fn node_public_key(&self) -> Vec<u8> ⓘ
This phone’s own node public key, which is what a device lists in
PROP_DEV_ADMINS to let this phone manage it over the mesh.
Handing this to a radio the phone is attached to —
MobileUlcpSession::insert_device_admin — is the whole of making
this phone an administrator of that radio. Nothing else is
exchanged: the session both ends derive comes from their two
identities.
Sourcepub fn begin_management_get(
&self,
peer_address: String,
property_id: u32,
) -> Result<u64, MobileMeshError>
pub fn begin_management_get( &self, peer_address: String, property_id: u32, ) -> Result<u64, MobileMeshError>
Read one property from a device across the mesh.
Every begin_management_* call returns immediately with an
operation identifier, and reports through poll_update — the same
shape as ping, because it is the same kind of thing: a
round-trip over a network that promises nothing. One operation runs
at a time; starting another while one is outstanding fails it.
Sourcepub fn begin_management_set(
&self,
peer_address: String,
property_id: u32,
value: Vec<u8>,
) -> Result<u64, MobileMeshError>
pub fn begin_management_set( &self, peer_address: String, property_id: u32, value: Vec<u8>, ) -> Result<u64, MobileMeshError>
Write one property on a device across the mesh.
The answer echoes what the property is now worth, which is what the
device kept rather than what was sent. The change is live and
unsaved; begin_management_save is what makes it survive a reboot.
Sourcepub fn begin_management_insert(
&self,
peer_address: String,
property_id: u32,
item: Vec<u8>,
) -> Result<u64, MobileMeshError>
pub fn begin_management_insert( &self, peer_address: String, property_id: u32, item: Vec<u8>, ) -> Result<u64, MobileMeshError>
Add one item to a multiple-value property on a device across the mesh — a peer key, an administrator key, a channel key.
Sourcepub fn begin_management_remove(
&self,
peer_address: String,
property_id: u32,
selector: Vec<u8>,
) -> Result<u64, MobileMeshError>
pub fn begin_management_remove( &self, peer_address: String, property_id: u32, selector: Vec<u8>, ) -> Result<u64, MobileMeshError>
Take one item out of a multiple-value property on a device across the mesh.
Sourcepub fn begin_management_insert_admin(
&self,
peer_address: String,
public_key: Vec<u8>,
) -> Result<u64, MobileMeshError>
pub fn begin_management_insert_admin( &self, peer_address: String, public_key: Vec<u8>, ) -> Result<u64, MobileMeshError>
Let one more node manage this device over the mesh, by adding its
public key to PROP_DEV_ADMINS.
Named rather than left to Self::begin_management_insert for the
same reason MobileUlcpSession::insert_device_admin is: this is a
decision about who may configure a node, and a caller should not
have to name the property — or be able to reach a different one by
naming it wrong. The device holds it live until a save.
Sourcepub fn begin_management_remove_admin(
&self,
peer_address: String,
public_key: Vec<u8>,
) -> Result<u64, MobileMeshError>
pub fn begin_management_remove_admin( &self, peer_address: String, public_key: Vec<u8>, ) -> Result<u64, MobileMeshError>
Take a node’s authority to manage this device away again.
A device that removes the administrator it is answering keeps answering this exchange — the reply is already authorized — and refuses the next one.
Sourcepub fn begin_management_insert_peer(
&self,
peer_address: String,
public_key: Vec<u8>,
) -> Result<u64, MobileMeshError>
pub fn begin_management_insert_peer( &self, peer_address: String, public_key: Vec<u8>, ) -> Result<u64, MobileMeshError>
Store one more peer public key on a device’s identity
(PROP_DEV_PEERS), so it can hold a secure session with that node
on its own.
Named for the same reason the administrator pair is: the caller is deciding who a device talks to, not writing to a numbered property. Live until a save.
Sourcepub fn begin_management_remove_peer(
&self,
peer_address: String,
public_key: Vec<u8>,
) -> Result<u64, MobileMeshError>
pub fn begin_management_remove_peer( &self, peer_address: String, public_key: Vec<u8>, ) -> Result<u64, MobileMeshError>
Drop a peer public key from a device’s identity.
Sourcepub fn begin_management_set_alert(
&self,
peer_address: String,
state: UlcpAlertState,
) -> Result<u64, MobileMeshError>
pub fn begin_management_set_alert( &self, peer_address: String, state: UlcpAlertState, ) -> Result<u64, MobileMeshError>
Tell a device to make itself conspicuous, or to stop
(PROP_ALERT).
Live state, never saved: an alert is a thing happening now, and one restored at boot would be a device that woke up beeping. The device ends it on its own deadline as well, so a search that outlasts that is kept alive by asking again — the same contract as the local link, with the round trip of the mesh in front of it.
Sourcepub fn begin_management_get_many(
&self,
peer_address: String,
property_ids: Vec<u32>,
) -> Result<u64, MobileMeshError>
pub fn begin_management_get_many( &self, peer_address: String, property_ids: Vec<u32>, ) -> Result<u64, MobileMeshError>
Read several properties in one exchange.
A device answers as many as fit and stops; the answers that arrive
are the ones it sent, and the rest are simply absent. Requires
CAP_CMD_MULTI on the device — one that lacks it refuses the whole
request rather than answering part of it.
Sourcepub fn begin_management_set_many(
&self,
peer_address: String,
writes: Vec<MobileMeshPropertyWriteRecord>,
) -> Result<u64, MobileMeshError>
pub fn begin_management_set_many( &self, peer_address: String, writes: Vec<MobileMeshPropertyWriteRecord>, ) -> Result<u64, MobileMeshError>
Write several properties in one exchange, in order.
A device applies them until the next answer would not fit and stops there, so a short answer means the remainder was never attempted. Each position echoes what that property is now worth.
Sourcepub fn begin_management_save(
&self,
peer_address: String,
) -> Result<u64, MobileMeshError>
pub fn begin_management_save( &self, peer_address: String, ) -> Result<u64, MobileMeshError>
Persist a device’s live configuration across the mesh.
Sourcepub fn begin_management_ble_clear_bonds(
&self,
peer_address: String,
) -> Result<u64, MobileMeshError>
pub fn begin_management_ble_clear_bonds( &self, peer_address: String, ) -> Result<u64, MobileMeshError>
Clear a device’s Bluetooth bonds across the mesh
(CMD_BLE_CLEAR_BONDS): forget every paired host, the pairing
PIN, and the pairing lockout, then open a pairing window.
The command answers with a status rather than silence: it is not
reset-class, and what it did is the whole of what it reports.
STATUS_UNIMPLEMENTED is a device that does not manage its own
bonds.
Clearing bonds over the mesh does not touch this exchange — the administrator is addressing the device’s node, not one of its Bluetooth hosts — so unlike the same command over Bluetooth, the reply arrives on a link that survives it.
Sourcepub fn begin_management_reset(
&self,
peer_address: String,
scope: MobileMeshResetScope,
) -> Result<u64, MobileMeshError>
pub fn begin_management_reset( &self, peer_address: String, scope: MobileMeshResetScope, ) -> Result<u64, MobileMeshError>
Reset a device across the mesh.
A device answers a reset with nothing — it is busy doing what was
asked — so the operation ends Acknowledged on the MAC
acknowledgment. Restore on a device holding no snapshot resets
nothing and answers like any other command, which arrives as an
ordinary Replied status.
Sourcepub fn begin_management_fetch(
&self,
peer_address: String,
property_ids: Vec<u32>,
multi_hint: bool,
) -> Result<u64, MobileMeshError>
pub fn begin_management_fetch( &self, peer_address: String, property_ids: Vec<u32>, multi_hint: bool, ) -> Result<u64, MobileMeshError>
Read a named set of properties across the mesh.
The caller names what it wants, in as many exchanges as the answers need — a screenful of settings is normally one. Every property comes back answered, refusals included, so a caller can tell “the device would not say” from “nobody asked”.
multi_hint says whether to open with a batched request. A device
that declines one is asked again a property at a time, so the hint
costs a round trip when wrong rather than an answer. Pass what
CAP_CMD_MULTI last said, or true before anything has.
Every exchange is airtime over a link that may be several hops deep. Ask for what a screen needs and no more.
Sourcepub async fn advertise_identity(
&self,
name: Option<String>,
timestamp: Option<u32>,
) -> Result<(), MobileMeshError>
pub async fn advertise_identity( &self, name: Option<String>, timestamp: Option<u32>, ) -> Result<(), MobileMeshError>
Broadcast a signed node-identity advertisement describing this phone.
The bundle always carries the standalone EdDSA signature because a broadcast frame has no MIC to authenticate it.
Sourcepub async fn advertise_identity_scheduled(
&self,
name: Option<String>,
timestamp: Option<u32>,
) -> Result<(), MobileMeshError>
pub async fn advertise_identity_scheduled( &self, name: Option<String>, timestamp: Option<u32>, ) -> Result<(), MobileMeshError>
The same advertisement, sent because the phone’s own interval came round rather than because someone asked for it.
Reaches only direct neighbours. A repeated statement of who this phone is does not need to cross the mesh every time; introducing it, which is what the manual send does, is the case that does.
Sourcepub async fn send_beacon(&self) -> Result<(), MobileMeshError>
pub async fn send_beacon(&self) -> Result<(), MobileMeshError>
Broadcast an empty beacon: no payload, so what it publishes is the path back to this phone rather than who this phone is. Costs a fraction of an advertisement.
Sourcepub async fn request_identity(
&self,
peer_address: String,
) -> Result<(), MobileMeshError>
pub async fn request_identity( &self, peer_address: String, ) -> Result<(), MobileMeshError>
Solicit a specific peer’s current node identity by sending a targeted
MAC Identity Request (command 1). This resolves once the request has
been handed to the transport; the peer’s identity response arrives
later as a NodeIdentity advertisement on the normal receive path
(surfaced through poll_update’s advertisement events).
Sourcepub async fn discover_identities(
&self,
role_code: Option<u8>,
capability_bits: Option<u8>,
node_hint: Option<Vec<u8>>,
source_route: Vec<Vec<u8>>,
) -> Result<(), MobileMeshError>
pub async fn discover_identities( &self, role_code: Option<u8>, capability_bits: Option<u8>, node_hint: Option<Vec<u8>>, source_route: Vec<Vec<u8>>, ) -> Result<(), MobileMeshError>
Solicit identities with one broadcast MAC Identity Request, either from this node’s own neighbors or from a remote vantage point.
With an empty source_route the request goes out as a direct
broadcast with no flood budget, so repeaters never carry it — the
blast radius is exactly the nodes in radio range. Given a route, the
request is steered along it instead: each repeater consumes its hint,
so the request arrives with an empty Route option in the neighborhood
the route ends at, and the nodes there are the ones that answer. A
steered request also carries a trace route, which is what gives the
answering strangers a path back — without it their replies would have
no route and no flood budget, and would die on their own transmitter.
Either way it carries this phone’s full source address, so a matching
node can reply with a targeted unicast without any prior contact;
replies arrive as ordinary NodeIdentity advertisements on the
receive path. role_code and capability_bits narrow which nodes
respond (AND-combined when both are given); None for all three
filters asks every node the request reaches.
node_hint narrows the ask to one node by the leading bytes of its
node hint. Two bytes is a router hint, which is all a route reveals
about the hops it crosses: pair it with a route ending at the hop
before the one in question, since a repeater consumes its own hint
only when forwarding and drops a request that still names it.
Each entry of source_route is one 2-byte router hint in send order.
Sourcepub async fn request_identity_by_hint(
&self,
conversation_address: String,
hint: Vec<u8>,
) -> Result<(), MobileMeshError>
pub async fn request_identity_by_hint( &self, conversation_address: String, hint: Vec<u8>, ) -> Result<(), MobileMeshError>
Ask a channel member who is known only by their claimed hint to send their identity.
A group message carries a 3-byte hint and nothing else, so there is no address to unicast a request to. This goes out over the channel itself, filtered to that hint, and only the member it names answers — with a targeted unicast, since the request carries this phone’s full address.
The request is routed by what that member’s own frames have shown: their observed trace route if one is known, otherwise a flood budget bounded by the hops their last message took rather than a default.
Sourcepub async fn request_peer_repeaters(
&self,
peer: Vec<u8>,
) -> Result<Vec<MobileMeshPeerRepeaterRecord>, MobileMeshError>
pub async fn request_peer_repeaters( &self, peer: Vec<u8>, ) -> Result<Vec<MobileMeshPeerRepeaterRecord>, MobileMeshError>
Ask one repeater which repeaters it knows of, and return the whole listing.
Unlike discover_identities, which scatters a request and lets the
answers arrive as events, this is one node’s own account of its
neighborhood: a single addressed exchange, paged when it does not fit
one frame, so it resolves to a list rather than a stream. Pages are
followed here; the caller sees only the finished listing.
Sourcepub async fn set_chat_display_name(
&self,
name: String,
) -> Result<(), MobileMeshError>
pub async fn set_chat_display_name( &self, name: String, ) -> Result<(), MobileMeshError>
Set whether this phone answers Identity Requests with its own
identity — the passive counterpart of [discover_identities]:
discoverable phones show up in other people’s Discover sessions.
name is the display name carried in replies (truncated to the
24-byte wire limit). The session starts discoverable with no name;
the app pushes the stored preference and name right after install
and again whenever either changes. Replies are targeted
authenticated unicasts, never broadcasts.
Set the name carried on this phone’s own group messages.
A multicast reaches members holding no identity for us, so a group message says who sent it or arrives anonymous. Direct messages never carry it: the recipient authenticated us by key. Empty clears it.
pub async fn set_discoverable( &self, enabled: bool, name: Option<String>, ) -> Result<(), MobileMeshError>
Sourcepub async fn set_advertised_location(
&self,
location: Option<MobileMeshSharedLocationRecord>,
) -> Result<(), MobileMeshError>
pub async fn set_advertised_location( &self, location: Option<MobileMeshSharedLocationRecord>, ) -> Result<(), MobileMeshError>
Set the position this phone’s identity carries, or None to stop
sharing one.
Reaches every live identity payload — advertisements, manual and
scheduled, and Identity Request replies while discoverable — but
never the shareable QR/URI bundle: that bundle is durable, and a
position frozen into it would go stale and then travel wherever
the QR is pasted. The coordinate is reduced to the cell named by
precision_bytes before it is stored, so nothing finer ever sits
in this session, whatever later reads it.
Sourcepub async fn peer_route(
&self,
peer_address: String,
) -> Result<MobileMeshRouteRecord, MobileMeshError>
pub async fn peer_route( &self, peer_address: String, ) -> Result<MobileMeshRouteRecord, MobileMeshError>
Report the route the MAC will use for the next frame sent to peer.
Read-only: an unregistered peer reads as Unknown rather than being
registered as a side effect of being inspected.
Sourcepub async fn clear_peer_route(
&self,
peer_address: String,
) -> Result<bool, MobileMeshError>
pub async fn clear_peer_route( &self, peer_address: String, ) -> Result<bool, MobileMeshError>
Forget the route cached for peer, returning whether one was held.
The peer, its keys, and its counters are untouched; only the learned path is discarded, so the next send starts over from flood delivery.
Sourcepub async fn sign_identity_bundle(
&self,
name: Option<String>,
timestamp: Option<u32>,
) -> Result<Vec<u8>, MobileMeshError>
pub async fn sign_identity_bundle( &self, name: Option<String>, timestamp: Option<u32>, ) -> Result<Vec<u8>, MobileMeshError>
Build and sign this phone’s node-identity bundle without transmitting
it, for embedding in the shareable umsh:n: URI and QR code.
pub async fn register_peers( &self, peer_addresses: Vec<String>, ) -> Result<(), MobileMeshError>
Sourcepub async fn remove_peers(
&self,
peer_addresses: Vec<String>,
) -> Result<(), MobileMeshError>
pub async fn remove_peers( &self, peer_addresses: Vec<String>, ) -> Result<(), MobileMeshError>
Remove peers from the live MAC. Idempotent: a peer that was never registered is already in the requested state, so it is not an error. A removed peer that transmits again may be auto-re-registered (unpinned) by the MAC — removal here tracks the app’s stored peer list, it is not a block list.
Sourcepub async fn register_channels(
&self,
keys: Vec<Vec<u8>>,
) -> Result<(), MobileMeshError>
pub async fn register_channels( &self, keys: Vec<Vec<u8>>, ) -> Result<(), MobileMeshError>
Register channel keys with the live MAC so their traffic is accepted.
Membership itself is persisted by the platform, which replays the whole joined set through this call when a session starts. Re-registering a channel already held is harmless.
Sourcepub async fn remove_channels(
&self,
keys: Vec<Vec<u8>>,
) -> Result<(), MobileMeshError>
pub async fn remove_channels( &self, keys: Vec<Vec<u8>>, ) -> Result<(), MobileMeshError>
Drop channel keys from the live MAC, so its traffic is no longer
decrypted. Idempotent, like Self::remove_peers.
pub fn receive(&self, frame: MobileMeshRxRecord) -> Result<(), MobileMeshError>
Sourcepub fn complete_outbound_frame(
&self,
frame_id: u64,
transmitted: bool,
) -> Result<(), MobileMeshError>
pub fn complete_outbound_frame( &self, frame_id: u64, transmitted: bool, ) -> Result<(), MobileMeshError>
Report the actual physical radio result for an outbound frame. This is intentionally distinct from accepting the frame into the BLE/CRP queue: the MAC starts ACK and retry timing only after success.
pub async fn restore_chat( &self, checkpoints: Vec<MobileChatCheckpointRecord>, ) -> Result<(), MobileMeshError>
Sourcepub async fn compose_text(
&self,
conversation_address: String,
client_token: u32,
body: String,
) -> Result<MobileChatComposeBatchRecord, MobileMeshError>
pub async fn compose_text( &self, conversation_address: String, client_token: u32, body: String, ) -> Result<MobileChatComposeBatchRecord, MobileMeshError>
Compose a message into a conversation, addressed either by a peer’s address or by a channel’s conversation address.
Sourcepub async fn compose_edit(
&self,
conversation_address: String,
client_token: u32,
original: MobileChatOriginalRef,
body: String,
) -> Result<MobileChatComposeBatchRecord, MobileMeshError>
pub async fn compose_edit( &self, conversation_address: String, client_token: u32, original: MobileChatOriginalRef, body: String, ) -> Result<MobileChatComposeBatchRecord, MobileMeshError>
Compose an edit of a previously sent message. The original may come
from an earlier app launch: its persisted (wire_id, epoch) is used
when the facade session no longer holds a live handle, and the engine
rejects it (ChatComposeFailed) if stream continuity was lost since.
Sourcepub async fn compose_delete(
&self,
conversation_address: String,
client_token: u32,
original: MobileChatOriginalRef,
) -> Result<MobileChatComposeBatchRecord, MobileMeshError>
pub async fn compose_delete( &self, conversation_address: String, client_token: u32, original: MobileChatOriginalRef, ) -> Result<MobileChatComposeBatchRecord, MobileMeshError>
Compose a deletion (empty edit on the wire) of a previously sent
message. Same original-reference rules as Self::compose_edit.
Sourcepub async fn compose_reaction(
&self,
conversation_address: String,
client_token: u32,
target: MobileChatRegardingRef,
body: String,
) -> Result<MobileChatComposeBatchRecord, MobileMeshError>
pub async fn compose_reaction( &self, conversation_address: String, client_token: u32, target: MobileChatRegardingRef, body: String, ) -> Result<MobileChatComposeBatchRecord, MobileMeshError>
React to a message with a short emote body, or withdraw an earlier reaction by passing an empty body. A sender has at most one live reaction per message: sending another simply supersedes it, so there is nothing to edit or delete.
Unlike an edit, the target may be a message the peer sent, and usually one persisted before this launch; the reference carries the direction and (for channel groups) the sender hint needed to name it.
pub async fn commit_chat_batch( &self, batch_id: u64, ) -> Result<(), MobileMeshError>
pub async fn reject_chat_batch( &self, batch_id: u64, checkpoints: Vec<MobileChatCheckpointRecord>, ) -> Result<(), MobileMeshError>
pub fn apply_chat_archive_result( &self, request_id: u32, kind: MobileChatArchiveResultKind, payload: Vec<u8>, ) -> Result<(), MobileMeshError>
pub fn acknowledge_chat_batch( &self, batch_id: u64, ) -> Result<(), MobileMeshError>
Sourcepub fn fail_outbound_transmissions(&self) -> Result<(), MobileMeshError>
pub fn fail_outbound_transmissions(&self) -> Result<(), MobileMeshError>
Fail every chat transmission currently owned by the mobile radio
bridge. The platform calls this when ULCP-link delivery failed
after the MAC had accepted the frames, ensuring optimistic UI rows do
not remain in Sending indefinitely.
Sourcepub fn set_wake_listener(&self, listener: Arc<dyn MobileMeshWakeListener>)
pub fn set_wake_listener(&self, listener: Arc<dyn MobileMeshWakeListener>)
Register (or replace) the listener that is told when this session
has new data for poll_update. If data is already pending, the
listener fires immediately.