pub struct ProtocolConfig {Show 13 fields
pub app_id: String,
pub profile: String,
pub transport: TransportConfig,
pub dors: DorsConfig,
pub relay: RelayConfig,
pub mesh_relay: MeshRelayConfig,
pub reliability: ReliabilityConfig,
pub encryption: EncryptionConfig,
pub initial_ttl: u8,
pub group: GroupConfig,
pub security: SecurityConfig,
pub data: DataConfig,
pub custody: CustodyConfig,
}Expand description
Main configuration for the Offline Protocol.
Fields§
§app_id: StringApplication identifier (required).
Two jobs. It is one of the two inputs to the storage namespace, with
Self::profile, so changing it selects fresh storage and a fresh
identity. And it is the id stamped on every frame this instance
originates: messages, media chunks, acknowledgements and control
traffic. A rich send may name another id for its own frame
(crate::SendMessageOptions::app_id), which is how one instance
serves several local applications behind one identity; the
acknowledgements and control frames the engine sends always carry
this one. The bindings also derive the BLE app tag from it.
It is cleartext on the wire and covered by no signature, so every carrier and hop can read it and rewrite it (threat model R17). It names an application, so it is not secret, but treat it as routing metadata only: the receive path delivers a message whatever id it carries.
profile: StringLocal profile selector (required).
Selects which stored identity this instance runs as — one namespace per
(app_id, profile) pair — so an app hosting several accounts gives each
its own value, and an app hosting one can pass a constant like
"default".
It is not the device’s identity. The wire identity is the
self-certifying address derived from the identity key held in this
profile’s storage: it is minted on first start, reported by
crate::OfflineProtocol::local_address, and it is what gets stamped
as Message.sender, what peers see as NeighborDiscovered.peer_id,
and what others use as recipient. An app cannot choose it, because
choosing it would mean claiming a key one does not hold.
Under any configuration that reaches the wire the profile stays local.
The one exception is the explicit
EncryptionConfig::require_encryption = false opt-out, which lets a
plaintext send go out with MLS never initialized — no address has been
derived at that point, so the frame carries the profile as its sender
and the send emits
crate::events::SecurityWarningCode::PlaintextSend. The default
(require_encryption = true) fails such a send closed instead. Treat
the profile as private if that opt-out is in use.
transport: TransportConfigTransport configuration.
dors: DorsConfigDORS (Dynamic Offline Relay Switch) configuration.
relay: RelayConfigRelay management configuration: whether this device carries traffic for other people at all, and the battery floors under which it stops.
mesh_relay: MeshRelayConfigHow this device carries traffic for other people: how far frames travel, how many neighbors each is handed to, and the ceilings that keep forwarding from crowding the radio out.
Whether to forward is Self::relay’s decision; this is the shape of
it once the answer is yes.
reliability: ReliabilityConfigReliability layer configuration.
encryption: EncryptionConfigEncryption configuration for automatic MLS handling.
initial_ttl: u8Initial TTL (Time-To-Live) for messages.
group: GroupConfigMesh group messaging configuration.
security: SecurityConfigSecurity configuration for transport and control-message hardening.
data: DataConfigReplicated-document data layer configuration.
custody: CustodyConfigCustody: holding a neighbour’s replication frames for hours. Off by
default; see CustodyConfig.
Implementations§
Source§impl ProtocolConfig
impl ProtocolConfig
Sourcepub fn new(app_id: impl Into<String>, profile: impl Into<String>) -> Self
pub fn new(app_id: impl Into<String>, profile: impl Into<String>) -> Self
Creates a new protocol configuration.
§Arguments
app_id- Application identifierprofile- Local profile selector (storage namespace, never on the wire)
Sourcepub fn builder(
app_id: impl Into<String>,
profile: impl Into<String>,
) -> ProtocolConfigBuilder
pub fn builder( app_id: impl Into<String>, profile: impl Into<String>, ) -> ProtocolConfigBuilder
Creates a builder for more granular configuration.
Examples found in repository?
57fn main() -> Result<(), Box<dyn std::error::Error>> {
58 let root = match std::env::args_os().nth(1) {
59 Some(root) => PathBuf::from(root),
60 None => {
61 let mut suffix = [0u8; 8];
62 OsRng.fill_bytes(&mut suffix);
63 std::env::temp_dir().join(format!("replicated-notes-{}", hex::encode(suffix)))
64 }
65 };
66 let key: Box<dyn StoreKeyProvider> =
67 if std::env::var_os(EnvStoreKey::DEFAULT_VARIABLE).is_some() {
68 Box::new(EnvStoreKey::default())
69 } else {
70 let mut key = [0u8; 32];
71 OsRng.fill_bytes(&mut key);
72 Box::new(StaticStoreKey::new(key))
73 };
74 println!("stores: {}", root.display());
75
76 // Both stores are on disk, which is what makes the reopen at the end a
77 // reopen rather than a fresh start. The secure store matters as much as
78 // the record store: documents are sealed with a key that lives in it, so
79 // losing only that half looks exactly like data loss.
80 let (secure, records) = open_stores(&root, key.as_ref())?;
81
82 // `data.enabled` defaults to true, so nothing here switches the layer on.
83 let config = ProtocolConfig::builder(APP_ID, PROFILE).build()?;
84 let mut protocol = OfflineProtocol::new(config)?;
85
86 // Documents are sealed at rest and the record key is minted here, so this
87 // call is the prerequisite for every method below.
88 protocol.initialize_mls(secure.clone(), records.clone())?;
89
90 // A space is an MLS scope: a peer's address for a 1:1 space, or the group
91 // id for a group. Nothing replicates in this example, so the name only
92 // has to be a valid one.
93 let space = "demo-space";
94
95 protocol.data_create_doc(space, "trip")?;
96
97 // A map: last writer wins per key, and different keys never conflict.
98 protocol.data_map_set(
99 space,
100 "trip",
101 "meta",
102 "title",
103 DataValue::Text {
104 value: "Coast road".to_string(),
105 },
106 )?;
107
108 // A list: concurrent insertions both survive, in a deterministic order.
109 for item in ["water", "map", "torch"] {
110 protocol.data_list_push(
111 space,
112 "trip",
113 "packing",
114 DataValue::Text {
115 value: item.to_string(),
116 },
117 )?;
118 }
119
120 // Text: character-level merge, so two people typing in one paragraph keep
121 // both sets of words. Positions are character offsets, not byte offsets.
122 protocol.data_text_insert(space, "trip", "notes", 0, "Meet at the bridge")?;
123
124 // A counter: increments add up rather than overwriting each other.
125 protocol.data_counter_increment(space, "trip", "opened", 1.0)?;
126
127 // Edits batch before they reach storage. This is the call that makes them
128 // durable, and `data_changed` fires after the same point, never before.
129 protocol.data_flush(space, "trip")?;
130
131 println!("spaces: {:?}", protocol.data_list_spaces()?);
132 println!("documents: {:?}", protocol.data_list_docs(space)?);
133 println!(
134 "compacted: {} bytes",
135 protocol.data_doc_size(space, "trip")?
136 );
137 println!(
138 "history: {} bytes",
139 protocol.data_export_raw(space, "trip")?.len()
140 );
141 println!("json: {}", protocol.data_doc_json(space, "trip")?);
142
143 // Both halves of the escape hatch are above: `data_doc_json` is the plain
144 // JSON of the current state, `data_export_raw` the engine-native history.
145 // An application can always take its data and leave.
146
147 // Reopen: a new engine over the same two directories, opened afresh as a
148 // restarted process would. The records were sealed by the first engine
149 // and are read back by the second, which is the property an application
150 // depends on across a restart.
151 drop(protocol);
152 drop((secure, records));
153 let (secure, records) = open_stores(&root, key.as_ref())?;
154 let config = ProtocolConfig::builder(APP_ID, PROFILE).build()?;
155 let mut reopened = OfflineProtocol::new(config)?;
156 reopened.initialize_mls(secure, records)?;
157
158 println!("after reopen");
159 println!(
160 " title: {:?}",
161 reopened.data_map_get(space, "trip", "meta", "title")?
162 );
163 println!(
164 " packing: {}",
165 reopened.data_list_len(space, "trip", "packing")?
166 );
167 println!(
168 " notes: {:?}",
169 reopened.data_text_value(space, "trip", "notes")?
170 );
171 println!(
172 " opened: {}",
173 reopened.data_counter_value(space, "trip", "opened")?
174 );
175
176 Ok(())
177}Trait Implementations§
Source§impl Clone for ProtocolConfig
impl Clone for ProtocolConfig
Auto Trait Implementations§
impl !RefUnwindSafe for ProtocolConfig
impl !UnwindSafe for ProtocolConfig
impl Freeze for ProtocolConfig
impl Send for ProtocolConfig
impl Sync for ProtocolConfig
impl Unpin for ProtocolConfig
impl UnsafeUnpin for ProtocolConfig
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Declassify for T
impl<T> Declassify for T
type Declassified = T
fn declassify(self) -> T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);