This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

knot2/server,Containerfile: run key-fill & take its budget from config

Lewis: May this revision serve well! <did:plc:3fwecdnvtcscjnrx2p4n7alz>

author did:plc:3fwecdnvtcscjnrx2p4n7a… date (Aug 3, 2026, 11:38 PM +0300) commit 29c4aaa3 parent ef66668f change-id yqmxouln
+135 -1
+1
Cargo.lock
··· 4829 4829 "knot-events", 4830 4830 "knot-git", 4831 4831 "knot-index", 4832 + "knot-keyfill", 4832 4833 "knot-lfs", 4833 4834 "knot-maintenance", 4834 4835 "knot-messages",
+1
knot2/Containerfile
··· 5 5 ENV RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=mold" 6 6 WORKDIR /src 7 7 COPY Cargo.toml Cargo.lock rust-toolchain.toml ./ 8 + COPY crates ./crates 8 9 COPY bobbin/crates ./bobbin/crates 9 10 COPY shuttle ./shuttle 10 11 COPY lexicons ./lexicons
+80
knot2/crates/knot-config/src/lib.rs
··· 41 41 #[config(nested)] 42 42 pub lfs: LfsConfig, 43 43 #[config(nested)] 44 + pub keyfill: KeyfillConfig, 45 + #[config(nested)] 44 46 pub resources: ResourcesConfig, 45 47 #[config(nested)] 46 48 pub homepage: HomepageConfig, ··· 435 437 } 436 438 437 439 #[derive(Debug, Config)] 440 + pub struct KeyfillConfig { 441 + #[config(env = "KNOT_KEYFILL_KEY_BUDGET_MIB", default = 64)] 442 + pub key_budget_mib: u32, 443 + 444 + #[config(env = "KNOT_KEYFILL_TTL_SECS", default = 3_600)] 445 + pub ttl_secs: u64, 446 + 447 + #[config(env = "KNOT_KEYFILL_REPRIEVE_RETRY_SECS", default = 300)] 448 + pub reprieve_retry_secs: u64, 449 + 450 + #[config(env = "KNOT_KEYFILL_REPRIEVE_BUDGET_SECS", default = 21_600)] 451 + pub reprieve_budget_secs: u64, 452 + } 453 + 454 + #[derive(Debug, Config)] 438 455 pub struct ResourcesConfig { 439 456 #[config(env = "KNOT_MAX_THREADS", default = 0)] 440 457 pub max_threads: u32, ··· 758 775 "xrpc.events_max_subscribers must be at least xrpc.events_max_per_peer", 759 776 ), 760 777 check( 778 + (1..=MAX_KEYFILL_BUDGET_MIB).contains(&self.keyfill.key_budget_mib), 779 + "keyfill.key_budget_mib must be between one mebibyte and one tebibyte", 780 + ), 781 + check( 782 + (1..=MAX_KEYFILL_SPAN_SECS).contains(&self.keyfill.ttl_secs), 783 + "keyfill.ttl_secs must be between one second and one year", 784 + ), 785 + check( 786 + (1..=MAX_KEYFILL_SPAN_SECS).contains(&self.keyfill.reprieve_retry_secs), 787 + "keyfill.reprieve_retry_secs must be between one second and one year", 788 + ), 789 + check( 790 + (1..=MAX_KEYFILL_SPAN_SECS).contains(&self.keyfill.reprieve_budget_secs), 791 + "keyfill.reprieve_budget_secs must be between one second and one year", 792 + ), 793 + check( 794 + self.keyfill.reprieve_budget_secs >= self.keyfill.reprieve_retry_secs, 795 + "keyfill.reprieve_budget_secs must be at least keyfill.reprieve_retry_secs", 796 + ), 797 + check( 761 798 self.maintenance.interval_secs > 0, 762 799 "maintenance.interval_secs must be greater than zero", 763 800 ), ··· 1019 1056 1020 1057 const MASTER_KEY_MIN_BYTES: usize = 32; 1021 1058 1059 + const MAX_KEYFILL_SPAN_SECS: u64 = 365 * 24 * 60 * 60; 1060 + 1061 + const MAX_KEYFILL_BUDGET_MIB: u32 = 1024 * 1024; 1062 + 1022 1063 fn verify_writable_dir(field: &'static str, path: &Path) -> Result<(), EnvError> { 1023 1064 let metadata = std::fs::metadata(path).map_err(|source| EnvError::DirInaccessible { 1024 1065 field, ··· 1268 1309 gc_interval_secs: 21_600, 1269 1310 max_ssh_transfers: 16, 1270 1311 max_http_downloads: 64, 1312 + }, 1313 + keyfill: KeyfillConfig { 1314 + key_budget_mib: 64, 1315 + ttl_secs: 3_600, 1316 + reprieve_retry_secs: 300, 1317 + reprieve_budget_secs: 21_600, 1271 1318 }, 1272 1319 resources: ResourcesConfig { 1273 1320 max_threads: 0, ··· 1559 1606 config.xrpc.events_max_per_peer = 8; 1560 1607 }, 1561 1608 "events_max_subscribers must be at least", 1609 + ), 1610 + ( 1611 + "a_key_budget_too_small_for_any_key", 1612 + |config| config.keyfill.key_budget_mib = 0, 1613 + "keyfill.key_budget_mib", 1614 + ), 1615 + ( 1616 + "a_key_budget_past_what_any_machine_has", 1617 + |config| config.keyfill.key_budget_mib = u32::MAX, 1618 + "keyfill.key_budget_mib", 1619 + ), 1620 + ( 1621 + "a_key_ttl_that_expires_on_the_read", 1622 + |config| config.keyfill.ttl_secs = 0, 1623 + "keyfill.ttl_secs", 1624 + ), 1625 + ( 1626 + "a_key_ttl_past_what_a_unix_timestamp_can_represent", 1627 + |config| config.keyfill.ttl_secs = u64::MAX, 1628 + "keyfill.ttl_secs", 1629 + ), 1630 + ( 1631 + "a_zero_second_reprieve_retry", 1632 + |config| config.keyfill.reprieve_retry_secs = 0, 1633 + "keyfill.reprieve_retry_secs", 1634 + ), 1635 + ( 1636 + "a_reprieve_budget_under_one_retry", 1637 + |config| { 1638 + config.keyfill.reprieve_retry_secs = 600; 1639 + config.keyfill.reprieve_budget_secs = 300; 1640 + }, 1641 + "keyfill.reprieve_budget_secs must be at least", 1562 1642 ), 1563 1643 ( 1564 1644 "a_non_https_plc_directory",
+1
knot2/crates/knot-server/Cargo.toml
··· 14 14 knot-resource = { workspace = true } 15 15 knot-cache = { workspace = true } 16 16 knot-index = { workspace = true } 17 + knot-keyfill = { workspace = true } 17 18 knot-atproto = { workspace = true } 18 19 knot-runtime = { workspace = true } 19 20 knot-ssh = { workspace = true }
+35 -1
knot2/crates/knot-server/src/main.rs
··· 31 31 use tower_http::services::ServeFile; 32 32 33 33 const MAINTENANCE_SHUTDOWN_DRAIN: Duration = Duration::from_secs(30); 34 + const KEYFILL_SHUTDOWN_DRAIN: Duration = Duration::from_secs(10); 34 35 const EDGE_SHUTDOWN_DRAIN: Duration = Duration::from_secs(40); 35 36 36 37 const DEFAULT_HOMEPAGE: &str = include_str!("homepage.html"); ··· 120 121 Ok(()) 121 122 } 122 123 124 + fn keyfill_pace(config: &knot_config::KeyfillConfig) -> knot_keyfill::Pace { 125 + knot_keyfill::Pace { 126 + ttl: knot_index::KeyTtl::from_secs(config.ttl_secs), 127 + reprieve: knot_index::KeyReprieve::from_secs( 128 + config.reprieve_retry_secs, 129 + config.reprieve_budget_secs, 130 + ), 131 + ..knot_keyfill::Pace::default() 132 + } 133 + } 134 + 123 135 fn subcommand(name: &str) -> Option<anyhow::Result<()>> { 124 136 match name { 125 137 "config-template" => { ··· 210 222 .meta_path(&knot_did) 211 223 .context("resolve meta-repo path")?; 212 224 213 - let index = Arc::new(Index::new(meta_path.clone(), layout.clone())); 225 + let key_pace = keyfill_pace(&config.keyfill); 226 + let index = Arc::new(Index::with_key_budget( 227 + meta_path.clone(), 228 + layout.clone(), 229 + knot_index::KeyBudget::from_mib(config.keyfill.key_budget_mib as usize), 230 + )); 214 231 index.rebuild().context("rebuild index from meta-repo")?; 215 232 tracing::info!(coverage = ?index.coverage(), "index ready"); 216 233 ··· 527 544 .with_maintenance(maintenance_handle.clone()) 528 545 .with_limits(pack_limits) 529 546 .with_slots(slots.clone()) 547 + .with_key_ttl(key_pace.ttl) 530 548 .with_catalog(Arc::clone(&catalog)); 531 549 let ssh_state = Arc::new(match &lfs_handle { 532 550 Some(handle) => ssh_base.with_lfs(handle.clone(), lfs_max_ssh_transfers), ··· 626 644 627 645 let shutdown = CancellationToken::new(); 628 646 tokio::spawn(allocator::govern_decay(shutdown.clone())); 647 + let keyfill_task = knot_keyfill::spawn( 648 + Arc::clone(&index), 649 + Arc::clone(&atproto), 650 + slots.clone(), 651 + key_pace, 652 + shutdown.clone(), 653 + ); 629 654 let mut edge_task = tokio::spawn(knot_edge::serve( 630 655 edge_config, 631 656 app, ··· 667 692 tracing::warn!( 668 693 timeout_secs = EDGE_SHUTDOWN_DRAIN.as_secs(), 669 694 "aborting edge drain after timeout" 695 + ); 696 + } 697 + if tokio::time::timeout(KEYFILL_SHUTDOWN_DRAIN, keyfill_task) 698 + .await 699 + .is_err() 700 + { 701 + tracing::warn!( 702 + timeout_secs = KEYFILL_SHUTDOWN_DRAIN.as_secs(), 703 + "aborting key fill drain after timeout" 670 704 ); 671 705 } 672 706 if let Some(shutdown) = maintenance_shutdown {
+17
knot2/example.toml
··· 415 415 # Default value: 64 416 416 #max_http_downloads = 64 417 417 418 + [keyfill] 419 + # Can also be specified via environment variable `KNOT_KEYFILL_KEY_BUDGET_MIB`. 420 + # Default value: 64 421 + #key_budget_mib = 64 422 + 423 + # Can also be specified via environment variable `KNOT_KEYFILL_TTL_SECS`. 424 + # Default value: 3600 425 + #ttl_secs = 3600 426 + 427 + # Can also be specified via environment variable `KNOT_KEYFILL_REPRIEVE_RETRY_SECS`. 428 + # Default value: 300 429 + #reprieve_retry_secs = 300 430 + 431 + # Can also be specified via environment variable `KNOT_KEYFILL_REPRIEVE_BUDGET_SECS`. 432 + # Default value: 21600 433 + #reprieve_budget_secs = 21600 434 + 418 435 [resources] 419 436 # Can also be specified via environment variable `KNOT_MAX_THREADS`. 420 437 # Default value: 0