presence_desired.rs (38205B)
1 //! Deterministic durable desired state for RHI service presence. 2 3 use core::fmt; 4 use std::error::Error; 5 6 use radroots_service_sqlite::{ 7 ServiceSqliteTransaction, ServiceSqliteTransactionError, ServiceSqliteTransactionErrorKind, 8 }; 9 use serde_json::Value; 10 use sha2::{Digest, Sha256}; 11 use sqlx::Row; 12 13 use crate::{ 14 RhiConfigDocumentV1, RhiDesiredPresenceRepository, RhiStateHostMode, 15 state_metadata::normalized_config_digest, 16 }; 17 18 /// Exact version of the deterministic presence desired-state contract. 19 pub const RHI_PRESENCE_DESIRED_CONTRACT_VERSION: u32 = 1; 20 21 /// Maximum number of configured relay targets in one desired state. 22 pub const RHI_PRESENCE_DESIRED_MAX_TARGETS: usize = 32; 23 24 const TARGET_SET_DOMAIN: &[u8] = b"radroots.rhi.presence_target_set.v1\0"; 25 const DESIRED_STATE_DOMAIN: &[u8] = b"radroots.rhi.presence_desired_state.v1\0"; 26 27 const READ_CURRENT_CONFIG_SQL: &str = r#"SELECT 28 CASE WHEN typeof(normalized_config_sha256) = 'blob' 29 AND length(normalized_config_sha256) = 32 30 THEN normalized_config_sha256 ELSE NULL END AS normalized_config_sha256, 31 length(CAST(service_public_key AS BLOB)) AS service_public_key_bytes, 32 substr(service_public_key, 1, 65) AS service_public_key 33 FROM rhi_config_bindings 34 ORDER BY generation DESC 35 LIMIT 1"#; 36 37 const READ_DESIRED_SQL: &str = r#"SELECT singleton, generation, 38 enabled, profile, application_handler, 39 CASE WHEN typeof(target_set_sha256) = 'blob' AND length(target_set_sha256) = 32 40 THEN target_set_sha256 ELSE NULL END AS target_set_sha256, 41 target_count, required_target_count, queue_capacity, 42 CASE WHEN typeof(desired_sha256) = 'blob' AND length(desired_sha256) = 32 43 THEN desired_sha256 ELSE NULL END AS desired_sha256 44 FROM presence_desired_state 45 LIMIT 2"#; 46 47 const INSERT_DESIRED_SQL: &str = r#"INSERT INTO presence_desired_state ( 48 singleton, generation, enabled, profile, application_handler, 49 target_set_sha256, target_count, required_target_count, 50 queue_capacity, desired_sha256 51 ) VALUES (1, 1, ?, ?, ?, ?, ?, ?, ?, ?)"#; 52 53 const UPDATE_DESIRED_SQL: &str = r#"UPDATE presence_desired_state 54 SET generation = generation + 1, 55 enabled = ?, profile = ?, application_handler = ?, 56 target_set_sha256 = ?, target_count = ?, required_target_count = ?, 57 queue_capacity = ?, desired_sha256 = ? 58 WHERE singleton = 1 AND generation = ? AND desired_sha256 = ?"#; 59 60 /// Closed configured presence posture. 61 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 62 pub enum RhiPresenceDesiredMode { 63 Disabled, 64 Enabled, 65 } 66 67 impl RhiPresenceDesiredMode { 68 /// Returns the exact machine-contract spelling. 69 #[must_use] 70 pub const fn code(self) -> &'static str { 71 match self { 72 Self::Disabled => "disabled", 73 Self::Enabled => "enabled", 74 } 75 } 76 } 77 78 /// Closed ordered inventory of presence documents selected by configuration. 79 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 80 pub enum RhiPresenceDocumentKind { 81 ServiceProfile, 82 ApplicationHandler, 83 } 84 85 impl RhiPresenceDocumentKind { 86 /// Returns the exact machine-contract spelling. 87 #[must_use] 88 pub const fn code(self) -> &'static str { 89 match self { 90 Self::ServiceProfile => "service_profile", 91 Self::ApplicationHandler => "application_handler", 92 } 93 } 94 } 95 96 /// One immutable presence relay target derived from the admitted configuration. 97 #[derive(Clone, PartialEq, Eq, Hash)] 98 pub struct RhiPresenceTarget { 99 ordinal: u8, 100 relay_id: Box<str>, 101 required: bool, 102 } 103 104 impl RhiPresenceTarget { 105 /// Returns the stable zero-based target position. 106 #[must_use] 107 pub const fn ordinal(&self) -> u8 { 108 self.ordinal 109 } 110 111 /// Returns the validated stable relay identifier. 112 #[must_use] 113 pub fn relay_id(&self) -> &str { 114 &self.relay_id 115 } 116 117 /// Returns whether this relay is required by the admitted relay authority. 118 #[must_use] 119 pub const fn required(&self) -> bool { 120 self.required 121 } 122 } 123 124 impl fmt::Debug for RhiPresenceTarget { 125 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 126 formatter 127 .debug_struct("RhiPresenceTarget") 128 .field("ordinal", &self.ordinal) 129 .field("relay_id", &"[redacted]") 130 .field("required", &self.required) 131 .finish() 132 } 133 } 134 135 /// Sealed deterministic presence authority derived from one admitted config. 136 /// 137 /// This value contains desired document kinds and stable relay authority only. 138 /// It contains no rendered event, signature, delivery attempt, time, entropy, 139 /// connection, or retry state. 140 /// 141 /// ```compile_fail 142 /// use rhi::RhiPresenceDesiredAuthority; 143 /// 144 /// let _forged = RhiPresenceDesiredAuthority { mode: todo!() }; 145 /// ``` 146 #[derive(Clone, PartialEq, Eq)] 147 pub struct RhiPresenceDesiredAuthority { 148 configuration_sha256: [u8; 32], 149 service_public_key: Box<str>, 150 mode: RhiPresenceDesiredMode, 151 document_kinds: Box<[RhiPresenceDocumentKind]>, 152 targets: Box<[RhiPresenceTarget]>, 153 queue_capacity: u32, 154 target_set_sha256: [u8; 32], 155 desired_sha256: [u8; 32], 156 } 157 158 impl RhiPresenceDesiredAuthority { 159 /// Derives the only presence desired-state authority from one admitted config. 160 pub fn from_config(config: &RhiConfigDocumentV1) -> Result<Self, RhiPresenceDesiredError> { 161 derive_authority(config.normalized(), config.profile()) 162 } 163 164 /// Returns the explicit configured posture. 165 #[must_use] 166 pub const fn mode(&self) -> RhiPresenceDesiredMode { 167 self.mode 168 } 169 170 /// Returns the exact ordered desired-document inventory. 171 #[must_use] 172 pub fn document_kinds(&self) -> &[RhiPresenceDocumentKind] { 173 &self.document_kinds 174 } 175 176 /// Returns the exact ordered presence target inventory. 177 #[must_use] 178 pub fn targets(&self) -> &[RhiPresenceTarget] { 179 &self.targets 180 } 181 182 /// Returns the configured presence work-queue capacity, or zero when disabled. 183 #[must_use] 184 pub const fn queue_capacity(&self) -> u32 { 185 self.queue_capacity 186 } 187 188 /// Returns the domain-separated exact target-set identity. 189 #[must_use] 190 pub const fn target_set_sha256(&self) -> &[u8; 32] { 191 &self.target_set_sha256 192 } 193 194 /// Returns the domain-separated semantic desired-state identity. 195 #[must_use] 196 pub const fn desired_sha256(&self) -> &[u8; 32] { 197 &self.desired_sha256 198 } 199 200 pub(crate) fn service_public_key(&self) -> &str { 201 &self.service_public_key 202 } 203 } 204 205 impl fmt::Debug for RhiPresenceDesiredAuthority { 206 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 207 formatter 208 .debug_struct("RhiPresenceDesiredAuthority") 209 .field("mode", &self.mode) 210 .field("document_count", &self.document_kinds.len()) 211 .field("target_count", &self.targets.len()) 212 .field("queue_capacity", &self.queue_capacity) 213 .finish_non_exhaustive() 214 } 215 } 216 217 /// Independently re-derives and validates one desired-state authority. 218 pub fn validate_rhi_presence_desired_authority( 219 config: &RhiConfigDocumentV1, 220 authority: &RhiPresenceDesiredAuthority, 221 ) -> Result<(), RhiPresenceDesiredError> { 222 let expected = RhiPresenceDesiredAuthority::from_config(config)?; 223 (expected == *authority) 224 .then_some(()) 225 .ok_or_else(|| failure(RhiPresenceDesiredErrorKind::Binding)) 226 } 227 228 /// One validated durable desired-state snapshot. 229 #[derive(Clone, Copy, PartialEq, Eq)] 230 pub struct RhiPresenceDesiredState { 231 generation: u64, 232 mode: RhiPresenceDesiredMode, 233 profile: bool, 234 application_handler: bool, 235 target_set_sha256: [u8; 32], 236 target_count: u8, 237 required_target_count: u8, 238 queue_capacity: u32, 239 desired_sha256: [u8; 32], 240 } 241 242 impl RhiPresenceDesiredState { 243 /// Returns the monotonically committed desired-state generation. 244 #[must_use] 245 pub const fn generation(self) -> u64 { 246 self.generation 247 } 248 249 /// Returns the configured desired-state posture. 250 #[must_use] 251 pub const fn mode(self) -> RhiPresenceDesiredMode { 252 self.mode 253 } 254 255 /// Returns whether the service-profile document is desired. 256 #[must_use] 257 pub const fn profile(self) -> bool { 258 self.profile 259 } 260 261 /// Returns whether the application-handler document is desired. 262 #[must_use] 263 pub const fn application_handler(self) -> bool { 264 self.application_handler 265 } 266 267 /// Returns the target-set identity without exposing relay endpoints. 268 #[must_use] 269 pub const fn target_set_sha256(&self) -> &[u8; 32] { 270 &self.target_set_sha256 271 } 272 273 /// Returns the total configured target count. 274 #[must_use] 275 pub const fn target_count(self) -> u8 { 276 self.target_count 277 } 278 279 /// Returns the number of configured required targets. 280 #[must_use] 281 pub const fn required_target_count(self) -> u8 { 282 self.required_target_count 283 } 284 285 /// Returns the configured presence queue bound, or zero when disabled. 286 #[must_use] 287 pub const fn queue_capacity(self) -> u32 { 288 self.queue_capacity 289 } 290 291 /// Returns the semantic desired-state identity. 292 #[must_use] 293 pub const fn desired_sha256(&self) -> &[u8; 32] { 294 &self.desired_sha256 295 } 296 } 297 298 impl fmt::Debug for RhiPresenceDesiredState { 299 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 300 formatter 301 .debug_struct("RhiPresenceDesiredState") 302 .field("generation", &self.generation) 303 .field("mode", &self.mode) 304 .field("profile", &self.profile) 305 .field("application_handler", &self.application_handler) 306 .field("target_count", &self.target_count) 307 .field("required_target_count", &self.required_target_count) 308 .field("queue_capacity", &self.queue_capacity) 309 .field("digests", &"[redacted]") 310 .finish() 311 } 312 } 313 314 /// Result of one durable desired-state compare-and-swap operation. 315 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 316 pub struct RhiPresenceDesiredCommitOutcome { 317 state: RhiPresenceDesiredState, 318 changed: bool, 319 } 320 321 impl RhiPresenceDesiredCommitOutcome { 322 /// Returns the exact committed state. 323 #[must_use] 324 pub const fn state(self) -> RhiPresenceDesiredState { 325 self.state 326 } 327 328 /// Returns whether this operation created a new durable generation. 329 #[must_use] 330 pub const fn changed(self) -> bool { 331 self.changed 332 } 333 } 334 335 /// Stable source-free desired-state failure classes. 336 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 337 pub enum RhiPresenceDesiredErrorKind { 338 InvalidConfiguration, 339 TargetInventory, 340 InvalidMode, 341 Binding, 342 ResourceExhausted, 343 Storage, 344 CommitOutcomeUnknown, 345 } 346 347 impl RhiPresenceDesiredErrorKind { 348 /// Returns the stable machine-readable failure code. 349 #[must_use] 350 pub const fn code(self) -> &'static str { 351 match self { 352 Self::InvalidConfiguration => "presence_desired_configuration_invalid", 353 Self::TargetInventory => "presence_desired_target_inventory_invalid", 354 Self::InvalidMode => "presence_desired_mode_invalid", 355 Self::Binding => "presence_desired_binding_invalid", 356 Self::ResourceExhausted => "resource_exhausted", 357 Self::Storage => "presence_desired_storage_failed", 358 Self::CommitOutcomeUnknown => "presence_desired_commit_outcome_unknown", 359 } 360 } 361 } 362 363 /// Redacted source-free desired-state failure. 364 #[derive(Clone, Copy, PartialEq, Eq)] 365 pub struct RhiPresenceDesiredError { 366 kind: RhiPresenceDesiredErrorKind, 367 } 368 369 impl RhiPresenceDesiredError { 370 /// Returns the stable failure class. 371 #[must_use] 372 pub const fn kind(self) -> RhiPresenceDesiredErrorKind { 373 self.kind 374 } 375 376 /// Returns the stable machine-readable failure code. 377 #[must_use] 378 pub const fn code(self) -> &'static str { 379 self.kind.code() 380 } 381 } 382 383 impl fmt::Display for RhiPresenceDesiredError { 384 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 385 formatter.write_str(match self.kind { 386 RhiPresenceDesiredErrorKind::InvalidConfiguration => { 387 "RHI presence desired-state configuration is invalid" 388 } 389 RhiPresenceDesiredErrorKind::TargetInventory => { 390 "RHI presence desired-state target inventory is invalid" 391 } 392 RhiPresenceDesiredErrorKind::InvalidMode => { 393 "RHI presence desired-state operation mode is invalid" 394 } 395 RhiPresenceDesiredErrorKind::Binding => "RHI presence desired-state binding is invalid", 396 RhiPresenceDesiredErrorKind::ResourceExhausted => { 397 "RHI presence desired-state capacity is exhausted" 398 } 399 RhiPresenceDesiredErrorKind::Storage => "RHI presence desired-state storage failed", 400 RhiPresenceDesiredErrorKind::CommitOutcomeUnknown => { 401 "RHI presence desired-state commit outcome is unknown" 402 } 403 }) 404 } 405 } 406 407 impl fmt::Debug for RhiPresenceDesiredError { 408 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 409 formatter 410 .debug_struct("RhiPresenceDesiredError") 411 .field("kind", &self.kind) 412 .finish() 413 } 414 } 415 416 impl Error for RhiPresenceDesiredError {} 417 418 impl RhiDesiredPresenceRepository<'_> { 419 /// Commits one exact desired state before any presence rendering or relay I/O. 420 pub async fn commit( 421 &self, 422 authority: &RhiPresenceDesiredAuthority, 423 ) -> Result<RhiPresenceDesiredCommitOutcome, RhiPresenceDesiredError> { 424 require_writable(self)?; 425 let authority = authority.clone(); 426 self.host() 427 .sqlite_host() 428 .transaction(move |transaction| { 429 Box::pin(async move { commit_desired(transaction, &authority).await }) 430 }) 431 .await 432 .map_err(map_transaction_error) 433 } 434 435 /// Reads the current validated desired-state snapshot without mutation. 436 pub async fn current( 437 &self, 438 ) -> Result<Option<RhiPresenceDesiredState>, RhiPresenceDesiredError> { 439 self.host() 440 .sqlite_host() 441 .transaction(move |transaction| { 442 Box::pin(async move { read_desired(transaction).await }) 443 }) 444 .await 445 .map_err(map_transaction_error) 446 } 447 } 448 449 fn derive_authority( 450 document: &Value, 451 profile: crate::RhiConfigProfile, 452 ) -> Result<RhiPresenceDesiredAuthority, RhiPresenceDesiredError> { 453 let configuration_sha256 = *normalized_config_digest(profile, document) 454 .map_err(|_| failure(RhiPresenceDesiredErrorKind::InvalidConfiguration))? 455 .as_bytes(); 456 let service_public_key = document 457 .pointer("/identity/service/expected_public_key") 458 .and_then(Value::as_str) 459 .filter(|value| valid_public_key(value)) 460 .ok_or_else(|| failure(RhiPresenceDesiredErrorKind::InvalidConfiguration))?; 461 let enabled = boolean(document, "/presence/enabled")?; 462 let profile_document = boolean(document, "/presence/profile")?; 463 let application_handler = boolean(document, "/presence/application_handler")?; 464 let configured_queue = 465 integer(document, "/resource_limits/queues/presence").and_then(|value| { 466 u32::try_from(value) 467 .map_err(|_| failure(RhiPresenceDesiredErrorKind::InvalidConfiguration)) 468 })?; 469 if configured_queue == 0 || configured_queue > 4_096 { 470 return Err(failure(RhiPresenceDesiredErrorKind::InvalidConfiguration)); 471 } 472 473 let mode = if enabled { 474 RhiPresenceDesiredMode::Enabled 475 } else { 476 RhiPresenceDesiredMode::Disabled 477 }; 478 let mut document_kinds = Vec::with_capacity(2); 479 let (targets, queue_capacity) = match mode { 480 RhiPresenceDesiredMode::Disabled => { 481 if profile_document 482 || application_handler 483 || document.pointer("/presence/target_relay_ids").is_some() 484 { 485 return Err(failure(RhiPresenceDesiredErrorKind::InvalidConfiguration)); 486 } 487 (Vec::new(), 0) 488 } 489 RhiPresenceDesiredMode::Enabled => { 490 if profile_document { 491 document_kinds.push(RhiPresenceDocumentKind::ServiceProfile); 492 } 493 if application_handler { 494 document_kinds.push(RhiPresenceDocumentKind::ApplicationHandler); 495 } 496 if document_kinds.is_empty() { 497 return Err(failure(RhiPresenceDesiredErrorKind::InvalidConfiguration)); 498 } 499 ( 500 derive_targets(document, "/presence/target_relay_ids")?, 501 configured_queue, 502 ) 503 } 504 }; 505 let target_set_sha256 = target_set_digest(&targets)?; 506 let desired_sha256 = desired_state_digest( 507 mode, 508 &document_kinds, 509 &targets, 510 queue_capacity, 511 service_public_key, 512 )?; 513 Ok(RhiPresenceDesiredAuthority { 514 configuration_sha256, 515 service_public_key: service_public_key.into(), 516 mode, 517 document_kinds: document_kinds.into_boxed_slice(), 518 targets: targets.into_boxed_slice(), 519 queue_capacity, 520 target_set_sha256, 521 desired_sha256, 522 }) 523 } 524 525 fn derive_targets( 526 document: &Value, 527 pointer: &str, 528 ) -> Result<Vec<RhiPresenceTarget>, RhiPresenceDesiredError> { 529 let target_ids = document 530 .pointer(pointer) 531 .and_then(Value::as_array) 532 .ok_or_else(|| failure(RhiPresenceDesiredErrorKind::TargetInventory))?; 533 if target_ids.is_empty() || target_ids.len() > RHI_PRESENCE_DESIRED_MAX_TARGETS { 534 return Err(failure(RhiPresenceDesiredErrorKind::TargetInventory)); 535 } 536 let relays = document 537 .pointer("/relays") 538 .and_then(Value::as_array) 539 .ok_or_else(|| failure(RhiPresenceDesiredErrorKind::TargetInventory))?; 540 let mut targets = Vec::with_capacity(target_ids.len()); 541 for (ordinal, target_id) in target_ids.iter().enumerate() { 542 let relay_id = target_id 543 .as_str() 544 .filter(|value| valid_relay_id(value)) 545 .ok_or_else(|| failure(RhiPresenceDesiredErrorKind::TargetInventory))?; 546 if targets 547 .iter() 548 .any(|target: &RhiPresenceTarget| target.relay_id() == relay_id) 549 { 550 return Err(failure(RhiPresenceDesiredErrorKind::TargetInventory)); 551 } 552 let relay = relays 553 .iter() 554 .find(|relay| relay.pointer("/id").and_then(Value::as_str) == Some(relay_id)) 555 .ok_or_else(|| failure(RhiPresenceDesiredErrorKind::TargetInventory))?; 556 if relay.pointer("/write").and_then(Value::as_bool) != Some(true) { 557 return Err(failure(RhiPresenceDesiredErrorKind::TargetInventory)); 558 } 559 targets.push(RhiPresenceTarget { 560 ordinal: u8::try_from(ordinal) 561 .map_err(|_| failure(RhiPresenceDesiredErrorKind::TargetInventory))?, 562 relay_id: relay_id.into(), 563 required: relay 564 .pointer("/required") 565 .and_then(Value::as_bool) 566 .ok_or_else(|| failure(RhiPresenceDesiredErrorKind::TargetInventory))?, 567 }); 568 } 569 Ok(targets) 570 } 571 572 fn target_set_digest(targets: &[RhiPresenceTarget]) -> Result<[u8; 32], RhiPresenceDesiredError> { 573 let mut digest = Sha256::new(); 574 digest.update(TARGET_SET_DOMAIN); 575 digest.update( 576 u32::try_from(targets.len()) 577 .map_err(|_| failure(RhiPresenceDesiredErrorKind::TargetInventory))? 578 .to_be_bytes(), 579 ); 580 for target in targets { 581 digest.update(u32::from(target.ordinal).to_be_bytes()); 582 digest.update( 583 u64::try_from(target.relay_id.len()) 584 .map_err(|_| failure(RhiPresenceDesiredErrorKind::TargetInventory))? 585 .to_be_bytes(), 586 ); 587 digest.update(target.relay_id.as_bytes()); 588 digest.update([u8::from(target.required)]); 589 } 590 Ok(digest.finalize().into()) 591 } 592 593 fn desired_state_digest( 594 mode: RhiPresenceDesiredMode, 595 document_kinds: &[RhiPresenceDocumentKind], 596 targets: &[RhiPresenceTarget], 597 queue_capacity: u32, 598 service_public_key: &str, 599 ) -> Result<[u8; 32], RhiPresenceDesiredError> { 600 let mut digest = Sha256::new(); 601 digest.update(DESIRED_STATE_DOMAIN); 602 digest.update([match mode { 603 RhiPresenceDesiredMode::Disabled => 0, 604 RhiPresenceDesiredMode::Enabled => 1, 605 }]); 606 digest.update( 607 u32::try_from(document_kinds.len()) 608 .map_err(|_| failure(RhiPresenceDesiredErrorKind::InvalidConfiguration))? 609 .to_be_bytes(), 610 ); 611 for kind in document_kinds { 612 digest.update([match kind { 613 RhiPresenceDocumentKind::ServiceProfile => 0, 614 RhiPresenceDocumentKind::ApplicationHandler => 1, 615 }]); 616 } 617 digest.update( 618 u32::try_from(targets.len()) 619 .map_err(|_| failure(RhiPresenceDesiredErrorKind::TargetInventory))? 620 .to_be_bytes(), 621 ); 622 for target in targets { 623 digest.update(u32::from(target.ordinal).to_be_bytes()); 624 digest.update( 625 u64::try_from(target.relay_id.len()) 626 .map_err(|_| failure(RhiPresenceDesiredErrorKind::TargetInventory))? 627 .to_be_bytes(), 628 ); 629 digest.update(target.relay_id.as_bytes()); 630 digest.update([u8::from(target.required)]); 631 } 632 digest.update(queue_capacity.to_be_bytes()); 633 digest.update( 634 u64::try_from(service_public_key.len()) 635 .map_err(|_| failure(RhiPresenceDesiredErrorKind::InvalidConfiguration))? 636 .to_be_bytes(), 637 ); 638 digest.update(service_public_key.as_bytes()); 639 Ok(digest.finalize().into()) 640 } 641 642 fn require_writable( 643 repository: &RhiDesiredPresenceRepository<'_>, 644 ) -> Result<(), RhiPresenceDesiredError> { 645 if repository.host().mode() == RhiStateHostMode::ReadWriteExisting { 646 Ok(()) 647 } else { 648 Err(failure(RhiPresenceDesiredErrorKind::InvalidMode)) 649 } 650 } 651 652 async fn commit_desired( 653 transaction: &mut ServiceSqliteTransaction<'_>, 654 authority: &RhiPresenceDesiredAuthority, 655 ) -> Result<RhiPresenceDesiredCommitOutcome, OperationError> { 656 require_current_config(transaction, authority).await?; 657 let current = read_desired(transaction).await?; 658 if let Some(current) = current { 659 if matches_authority(current, authority) { 660 return Ok(RhiPresenceDesiredCommitOutcome { 661 state: current, 662 changed: false, 663 }); 664 } 665 if current.generation == i64::MAX as u64 { 666 return Err(OperationError::ResourceExhausted); 667 } 668 let result = sqlx::query(UPDATE_DESIRED_SQL) 669 .bind(bool_i64(authority.mode == RhiPresenceDesiredMode::Enabled)) 670 .bind(bool_i64(has_document( 671 authority, 672 RhiPresenceDocumentKind::ServiceProfile, 673 ))) 674 .bind(bool_i64(has_document( 675 authority, 676 RhiPresenceDocumentKind::ApplicationHandler, 677 ))) 678 .bind(authority.target_set_sha256.as_slice()) 679 .bind(i64_count(authority.targets.len())?) 680 .bind(i64_count( 681 authority 682 .targets 683 .iter() 684 .filter(|target| target.required) 685 .count(), 686 )?) 687 .bind(i64::from(authority.queue_capacity)) 688 .bind(authority.desired_sha256.as_slice()) 689 .bind(i64_value(current.generation)?) 690 .bind(current.desired_sha256.as_slice()) 691 .execute(&mut *transaction) 692 .await 693 .map_err(|_| OperationError::Storage)?; 694 if result.rows_affected() != 1 { 695 return Err(OperationError::Binding); 696 } 697 } else { 698 let result = sqlx::query(INSERT_DESIRED_SQL) 699 .bind(bool_i64(authority.mode == RhiPresenceDesiredMode::Enabled)) 700 .bind(bool_i64(has_document( 701 authority, 702 RhiPresenceDocumentKind::ServiceProfile, 703 ))) 704 .bind(bool_i64(has_document( 705 authority, 706 RhiPresenceDocumentKind::ApplicationHandler, 707 ))) 708 .bind(authority.target_set_sha256.as_slice()) 709 .bind(i64_count(authority.targets.len())?) 710 .bind(i64_count( 711 authority 712 .targets 713 .iter() 714 .filter(|target| target.required) 715 .count(), 716 )?) 717 .bind(i64::from(authority.queue_capacity)) 718 .bind(authority.desired_sha256.as_slice()) 719 .execute(&mut *transaction) 720 .await 721 .map_err(|_| OperationError::Storage)?; 722 if result.rows_affected() != 1 { 723 return Err(OperationError::Binding); 724 } 725 } 726 let committed = read_desired(transaction) 727 .await? 728 .filter(|state| matches_authority(*state, authority)) 729 .ok_or(OperationError::Binding)?; 730 Ok(RhiPresenceDesiredCommitOutcome { 731 state: committed, 732 changed: true, 733 }) 734 } 735 736 async fn require_current_config( 737 transaction: &mut ServiceSqliteTransaction<'_>, 738 authority: &RhiPresenceDesiredAuthority, 739 ) -> Result<(), OperationError> { 740 let rows = sqlx::query(READ_CURRENT_CONFIG_SQL) 741 .fetch_all(&mut *transaction) 742 .await 743 .map_err(|_| OperationError::Storage)?; 744 if rows.len() != 1 { 745 return Err(OperationError::Binding); 746 } 747 let row = &rows[0]; 748 let configuration_sha256 = digest(row, "normalized_config_sha256")?; 749 let key_bytes = row 750 .try_get::<i64, _>("service_public_key_bytes") 751 .ok() 752 .and_then(|value| usize::try_from(value).ok()) 753 .filter(|value| *value == 64) 754 .ok_or(OperationError::Binding)?; 755 let service_public_key = row 756 .try_get::<String, _>("service_public_key") 757 .map_err(|_| OperationError::Binding)?; 758 if service_public_key.len() != key_bytes 759 || !valid_public_key(&service_public_key) 760 || configuration_sha256 != authority.configuration_sha256 761 || service_public_key != authority.service_public_key.as_ref() 762 { 763 return Err(OperationError::Binding); 764 } 765 Ok(()) 766 } 767 768 async fn read_desired( 769 transaction: &mut ServiceSqliteTransaction<'_>, 770 ) -> Result<Option<RhiPresenceDesiredState>, OperationError> { 771 let rows = sqlx::query(READ_DESIRED_SQL) 772 .fetch_all(&mut *transaction) 773 .await 774 .map_err(|_| OperationError::Storage)?; 775 match rows.as_slice() { 776 [] => Ok(None), 777 [row] => decode_desired(row).map(Some), 778 _ => Err(OperationError::Binding), 779 } 780 } 781 782 fn decode_desired( 783 row: &sqlx::sqlite::SqliteRow, 784 ) -> Result<RhiPresenceDesiredState, OperationError> { 785 if row.try_get::<i64, _>("singleton").ok() != Some(1) { 786 return Err(OperationError::Binding); 787 } 788 let generation = positive_u64(row, "generation")?; 789 let enabled = boolean_i64(row, "enabled")?; 790 let profile = boolean_i64(row, "profile")?; 791 let application_handler = boolean_i64(row, "application_handler")?; 792 let target_set_sha256 = digest(row, "target_set_sha256")?; 793 let target_count = count_u8(row, "target_count", RHI_PRESENCE_DESIRED_MAX_TARGETS)?; 794 let required_target_count = count_u8(row, "required_target_count", usize::from(target_count))?; 795 let queue_capacity = row 796 .try_get::<i64, _>("queue_capacity") 797 .ok() 798 .and_then(|value| u32::try_from(value).ok()) 799 .filter(|value| *value <= 4_096) 800 .ok_or(OperationError::Binding)?; 801 let desired_sha256 = digest(row, "desired_sha256")?; 802 let valid = if enabled { 803 (profile || application_handler) && target_count > 0 && queue_capacity > 0 804 } else { 805 !profile 806 && !application_handler 807 && target_count == 0 808 && required_target_count == 0 809 && queue_capacity == 0 810 }; 811 if !valid { 812 return Err(OperationError::Binding); 813 } 814 Ok(RhiPresenceDesiredState { 815 generation, 816 mode: if enabled { 817 RhiPresenceDesiredMode::Enabled 818 } else { 819 RhiPresenceDesiredMode::Disabled 820 }, 821 profile, 822 application_handler, 823 target_set_sha256, 824 target_count, 825 required_target_count, 826 queue_capacity, 827 desired_sha256, 828 }) 829 } 830 831 fn matches_authority( 832 state: RhiPresenceDesiredState, 833 authority: &RhiPresenceDesiredAuthority, 834 ) -> bool { 835 state.mode == authority.mode 836 && state.profile == has_document(authority, RhiPresenceDocumentKind::ServiceProfile) 837 && state.application_handler 838 == has_document(authority, RhiPresenceDocumentKind::ApplicationHandler) 839 && state.target_set_sha256 == authority.target_set_sha256 840 && usize::from(state.target_count) == authority.targets.len() 841 && usize::from(state.required_target_count) 842 == authority 843 .targets 844 .iter() 845 .filter(|target| target.required) 846 .count() 847 && state.queue_capacity == authority.queue_capacity 848 && state.desired_sha256 == authority.desired_sha256 849 } 850 851 pub(crate) fn presence_authority_matches_state( 852 state: RhiPresenceDesiredState, 853 authority: &RhiPresenceDesiredAuthority, 854 ) -> bool { 855 matches_authority(state, authority) 856 } 857 858 fn has_document(authority: &RhiPresenceDesiredAuthority, kind: RhiPresenceDocumentKind) -> bool { 859 authority.document_kinds.contains(&kind) 860 } 861 862 fn digest(row: &sqlx::sqlite::SqliteRow, field: &str) -> Result<[u8; 32], OperationError> { 863 row.try_get::<Vec<u8>, _>(field) 864 .map_err(|_| OperationError::Binding)? 865 .try_into() 866 .map_err(|_| OperationError::Binding) 867 } 868 869 fn positive_u64(row: &sqlx::sqlite::SqliteRow, field: &str) -> Result<u64, OperationError> { 870 row.try_get::<i64, _>(field) 871 .ok() 872 .and_then(|value| u64::try_from(value).ok()) 873 .filter(|value| *value != 0) 874 .ok_or(OperationError::Binding) 875 } 876 877 fn boolean_i64(row: &sqlx::sqlite::SqliteRow, field: &str) -> Result<bool, OperationError> { 878 match row.try_get::<i64, _>(field) { 879 Ok(0) => Ok(false), 880 Ok(1) => Ok(true), 881 Ok(_) | Err(_) => Err(OperationError::Binding), 882 } 883 } 884 885 fn count_u8( 886 row: &sqlx::sqlite::SqliteRow, 887 field: &str, 888 maximum: usize, 889 ) -> Result<u8, OperationError> { 890 row.try_get::<i64, _>(field) 891 .ok() 892 .and_then(|value| u8::try_from(value).ok()) 893 .filter(|value| usize::from(*value) <= maximum) 894 .ok_or(OperationError::Binding) 895 } 896 897 fn bool_i64(value: bool) -> i64 { 898 i64::from(value) 899 } 900 901 fn i64_count(value: usize) -> Result<i64, OperationError> { 902 i64::try_from(value).map_err(|_| OperationError::InvalidInput) 903 } 904 905 fn i64_value(value: u64) -> Result<i64, OperationError> { 906 i64::try_from(value).map_err(|_| OperationError::ResourceExhausted) 907 } 908 909 fn boolean(document: &Value, pointer: &str) -> Result<bool, RhiPresenceDesiredError> { 910 document 911 .pointer(pointer) 912 .and_then(Value::as_bool) 913 .ok_or_else(|| failure(RhiPresenceDesiredErrorKind::InvalidConfiguration)) 914 } 915 916 fn integer(document: &Value, pointer: &str) -> Result<u64, RhiPresenceDesiredError> { 917 document 918 .pointer(pointer) 919 .and_then(Value::as_u64) 920 .ok_or_else(|| failure(RhiPresenceDesiredErrorKind::InvalidConfiguration)) 921 } 922 923 fn valid_public_key(value: &str) -> bool { 924 value.len() == 64 925 && value 926 .bytes() 927 .all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase()) 928 && nostr::PublicKey::from_hex(value).is_ok_and(|key| key.xonly().is_ok()) 929 } 930 931 fn valid_relay_id(value: &str) -> bool { 932 !value.is_empty() 933 && value.len() <= 64 934 && value.as_bytes()[0].is_ascii_lowercase() 935 && value.bytes().all(|byte| { 936 byte.is_ascii_lowercase() || byte.is_ascii_digit() || matches!(byte, b'_' | b'-') 937 }) 938 } 939 940 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 941 enum OperationError { 942 InvalidInput, 943 Binding, 944 ResourceExhausted, 945 Storage, 946 } 947 948 fn map_transaction_error( 949 error: ServiceSqliteTransactionError<OperationError>, 950 ) -> RhiPresenceDesiredError { 951 if error.kind() == ServiceSqliteTransactionErrorKind::CommitOutcomeUnknown { 952 return failure(RhiPresenceDesiredErrorKind::CommitOutcomeUnknown); 953 } 954 failure(match error.operation_error().copied() { 955 Some(OperationError::InvalidInput) => RhiPresenceDesiredErrorKind::InvalidConfiguration, 956 Some(OperationError::Binding) => RhiPresenceDesiredErrorKind::Binding, 957 Some(OperationError::ResourceExhausted) => RhiPresenceDesiredErrorKind::ResourceExhausted, 958 Some(OperationError::Storage) | None => RhiPresenceDesiredErrorKind::Storage, 959 }) 960 } 961 962 const fn failure(kind: RhiPresenceDesiredErrorKind) -> RhiPresenceDesiredError { 963 RhiPresenceDesiredError { kind } 964 } 965 966 #[cfg(test)] 967 mod tests { 968 use super::*; 969 use crate::{RhiConfigProfile, parse_rhi_config_v1}; 970 971 const EXAMPLE: &str = include_str!("../contracts/services_hardening/config.v1.example.toml"); 972 973 fn config(source: &str) -> RhiConfigDocumentV1 { 974 parse_rhi_config_v1(source.as_bytes(), RhiConfigProfile::RepoLocal).expect("config") 975 } 976 977 #[test] 978 fn authority_is_deterministic_ordered_and_sealed() { 979 let config = config(EXAMPLE); 980 let first = RhiPresenceDesiredAuthority::from_config(&config).expect("authority"); 981 let second = RhiPresenceDesiredAuthority::from_config(&config).expect("authority"); 982 assert_eq!(first, second); 983 validate_rhi_presence_desired_authority(&config, &first).expect("independent validation"); 984 assert_eq!(first.mode(), RhiPresenceDesiredMode::Enabled); 985 assert_eq!( 986 first.document_kinds(), 987 &[ 988 RhiPresenceDocumentKind::ServiceProfile, 989 RhiPresenceDocumentKind::ApplicationHandler, 990 ] 991 ); 992 assert_eq!(first.targets().len(), 2); 993 assert_eq!(first.targets()[0].ordinal(), 0); 994 assert_eq!(first.targets()[0].relay_id(), "relay-primary"); 995 assert!(first.targets()[0].required()); 996 assert_eq!(first.targets()[1].ordinal(), 1); 997 assert_eq!(first.targets()[1].relay_id(), "relay-secondary"); 998 assert!(!first.targets()[1].required()); 999 assert_eq!(first.queue_capacity(), 64); 1000 assert_eq!( 1001 first.target_set_sha256(), 1002 &[ 1003 0x95, 0x9f, 0x04, 0x01, 0x28, 0x41, 0xae, 0x6e, 0x9b, 0xf3, 0xe1, 0x09, 0x46, 0x8b, 1004 0x4f, 0x66, 0xcf, 0xa9, 0xd9, 0x66, 0xaa, 0xc1, 0xdf, 0x36, 0xdf, 0x09, 0xe4, 0x5c, 1005 0x1e, 0x1c, 0x48, 0xf9, 1006 ] 1007 ); 1008 assert_eq!( 1009 first.desired_sha256(), 1010 &[ 1011 0x72, 0x35, 0xf1, 0xe3, 0x86, 0xe8, 0x39, 0x42, 0x76, 0x25, 0xdc, 0x36, 0x4d, 0xf7, 1012 0xb5, 0x1e, 0xe7, 0x4d, 0x39, 0xd5, 0xf1, 0x70, 0xe1, 0x2b, 0x25, 0xcf, 0x2c, 0x42, 1013 0xfd, 0x77, 0x31, 0xf0, 1014 ] 1015 ); 1016 let rendered = format!("{first:?} {:?}", first.targets()[0]); 1017 assert!(!rendered.contains("relay-primary")); 1018 assert!(!rendered.contains(&"2".repeat(64))); 1019 } 1020 1021 #[test] 1022 fn semantic_changes_change_only_the_deterministic_authority() { 1023 let baseline = 1024 RhiPresenceDesiredAuthority::from_config(&config(EXAMPLE)).expect("baseline"); 1025 for changed in [ 1026 EXAMPLE.replace("profile = true", "profile = false"), 1027 EXAMPLE.replace( 1028 "target_relay_ids = [\"relay-primary\", \"relay-secondary\"]", 1029 "target_relay_ids = [\"relay-secondary\", \"relay-primary\"]", 1030 ), 1031 EXAMPLE.replacen("required = true", "required = false", 1), 1032 EXAMPLE.replace("presence = 64", "presence = 63"), 1033 EXAMPLE.replace(&"2".repeat(64), &"3".repeat(64)), 1034 ] { 1035 let changed = RhiPresenceDesiredAuthority::from_config(&config(&changed)) 1036 .expect("changed authority"); 1037 assert_ne!(changed.desired_sha256(), baseline.desired_sha256()); 1038 } 1039 } 1040 1041 #[test] 1042 fn disabled_authority_contains_no_document_target_or_queue_state() { 1043 let disabled = EXAMPLE.replace( 1044 "[presence]\nenabled = true\nprofile = true\napplication_handler = true\ntarget_relay_ids = [\"relay-primary\", \"relay-secondary\"]", 1045 "[presence]\nenabled = false\nprofile = false\napplication_handler = false", 1046 ); 1047 let authority = RhiPresenceDesiredAuthority::from_config(&config(&disabled)) 1048 .expect("disabled authority"); 1049 assert_eq!(authority.mode(), RhiPresenceDesiredMode::Disabled); 1050 assert!(authority.document_kinds().is_empty()); 1051 assert!(authority.targets().is_empty()); 1052 assert_eq!(authority.queue_capacity(), 0); 1053 } 1054 1055 #[test] 1056 fn diagnostics_are_closed_source_free_and_redacted() { 1057 for kind in [ 1058 RhiPresenceDesiredErrorKind::InvalidConfiguration, 1059 RhiPresenceDesiredErrorKind::TargetInventory, 1060 RhiPresenceDesiredErrorKind::InvalidMode, 1061 RhiPresenceDesiredErrorKind::Binding, 1062 RhiPresenceDesiredErrorKind::ResourceExhausted, 1063 RhiPresenceDesiredErrorKind::Storage, 1064 RhiPresenceDesiredErrorKind::CommitOutcomeUnknown, 1065 ] { 1066 let error = failure(kind); 1067 assert_eq!(error.kind(), kind); 1068 assert!(!error.code().is_empty()); 1069 assert!(Error::source(&error).is_none()); 1070 let rendered = format!("{error} {error:?}"); 1071 assert!(!rendered.contains("wss://")); 1072 assert!(!rendered.contains("relay-primary")); 1073 assert!(!rendered.contains(&"2".repeat(64))); 1074 } 1075 } 1076 }