Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kitsune2 Bootstrap Server--Core #10

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions crates/bootstrap_srv/src/bin/kitsune2-bootstrap-srv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! The binary kitsune2-bootstrap-srv.

use kitsune2_bootstrap_srv::*;

#[derive(clap::Parser, Debug)]
#[command(version)]
pub struct Args {
/// By default kitsune2-boot-srv runs in "testing" configuration
/// with much lighter resource usage settings. This testing mode
/// should be more than enough for most developer application testing
/// and continuous integration or automated tests.
///
/// To setup the server to be ready to use most of the resources available
/// on a single given machine, you can set this "production" mode.
#[arg(long)]
pub production: bool,
// TODO - Implement the ability to specify TLS certificates
// TODO - Implement the ability to specify the listening address
// TODO - Implement the ability to override any other relevant
mattyg marked this conversation as resolved.
Show resolved Hide resolved
// config params that we wish to expose
}

fn main() {
let args = <Args as clap::Parser>::parse();

let config = if args.production {
Config::production()
} else {
Config::testing()
};

println!("{args:?}--{config:?}");

let (send, recv) = std::sync::mpsc::channel();

ctrlc::set_handler(move || {
send.send(()).unwrap();
})
.unwrap();

let srv = BootstrapSrv::new(config);

let _ = recv.recv();

println!("Terminating...");
drop(srv);
println!("Done.");
std::process::exit(0);
ThetaSinner marked this conversation as resolved.
Show resolved Hide resolved
}
84 changes: 84 additions & 0 deletions crates/bootstrap_srv/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! config types.

/// Configuration for running a BootstrapSrv.
#[derive(Debug)]
pub struct Config {
/// Worker thread count.
///
/// This server is currently built using blocking io and filesystem
/// storage. It is therefore beneficial to have more worker threads
/// than system cpus, since the workers will be bound on io, not
/// on cpu. On the other hand, increasing this will also increase
/// memory overhead and tempfile handle count, so we don't want to
/// set it too high.
///
/// Defaults:
/// - `testing = 2`
/// - `production = 4 * cpu_count`
pub worker_thread_count: usize,
mattyg marked this conversation as resolved.
Show resolved Hide resolved

/// The maximum agent info entry count per space.
///
/// All entries will be returned in a get space request, so
/// this count should be low enough to reasonably send this response
/// over http without needing pagination.
///
/// Defaults:
/// - `testing = 32`
/// - `production = 32`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be higher in production? Like 10k?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is just for bootstrap. I'd prefer to handle more spaces and less agents per space than the other way around. Once they are in the space, they can get additional peers by gossip.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense!

pub max_entries_per_space: usize,

/// The duration worker threads will block waiting for incoming connections
/// before checking to see if the server is shutting down.
///
/// Setting this very high will cause ctrl-c / server shutdown to be slow.
/// Setting this very low will increase cpu overhead (and in extreme
/// conditions, could cause a lack of responsiveness in the server).
///
/// Defaults:
/// - `testing = 10ms`
/// - `production = 2s`
pub request_listen_duration: std::time::Duration,

/// The address(es) at which to listen.
///
/// Defaults:
/// - `testing = "127.0.0.1:0"`
/// - `production = "0.0.0.0:443"`
pub listen_address: std::net::SocketAddr,

/// The interval at which expired agents are purged from the cache.
/// This is a fairly expensive operation that requires iterating
/// through every registered space and loading all the infos off the disk,
/// so it should not be undertaken too frequently.
///
/// Defaults:
///
/// - `testing = 10s`
/// - `production = 60s`
pub prune_interval: std::time::Duration,
}

impl Config {
/// Get a boot_srv config suitable for testing.
pub fn testing() -> Self {
Self {
worker_thread_count: 2,
max_entries_per_space: 32,
request_listen_duration: std::time::Duration::from_millis(10),
listen_address: ([127, 0, 0, 1], 0).into(),
prune_interval: std::time::Duration::from_secs(10),
}
}

/// Get a boot_srv config suitable for production.
pub fn production() -> Self {
Self {
worker_thread_count: num_cpus::get() * 4,
max_entries_per_space: 32,
request_listen_duration: std::time::Duration::from_secs(2),
listen_address: ([0, 0, 0, 0], 443).into(),
prune_interval: std::time::Duration::from_secs(60),
}
}
}
22 changes: 22 additions & 0 deletions crates/bootstrap_srv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,25 @@
/// interval.
#[cfg(doc)]
pub mod spec {}

fn now() -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.expect("InvalidSystemTime")
.as_micros() as i64
}

mod config;
pub use config::*;

mod parse;
use parse::*;

mod store;
use store::*;

mod space;
use space::*;

mod server;
pub use server::*;
83 changes: 83 additions & 0 deletions crates/bootstrap_srv/src/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/// An entry with known content.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to point to where this is documented from here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: 85dffc7

#[derive(Debug)]
pub struct ParsedEntry {
/// agent
pub agent: ed25519_dalek::VerifyingKey,

/// space
pub space: bytes::Bytes,

/// created_at
pub created_at: i64,

/// expires_at
pub expires_at: i64,

/// is_tombstone
pub is_tombstone: bool,

/// encoded
pub encoded: String,

/// signature
pub signature: ed25519_dalek::Signature,
}

impl ParsedEntry {
/// Parse entry from a slice.
pub fn from_slice(slice: &[u8]) -> std::io::Result<Self> {
jost-s marked this conversation as resolved.
Show resolved Hide resolved
use base64::prelude::*;

#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Outer {
agent_info: String,
signature: String,
}

#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Inner {
agent: String,
space: String,
created_at: String,
expires_at: String,
is_tombstone: bool,
}

let out: Outer = serde_json::from_slice(slice)?;
let inn: Inner = serde_json::from_str(&out.agent_info)?;

let agent: [u8; 32] = BASE64_URL_SAFE_NO_PAD
.decode(inn.agent)
.map_err(std::io::Error::other)?
.try_into()
.map_err(|_| std::io::Error::other("InvalidAgentPubKey"))?;

let signature: [u8; 64] = BASE64_URL_SAFE_NO_PAD
.decode(out.signature)
.map_err(std::io::Error::other)?
.try_into()
.map_err(|_| std::io::Error::other("InvalidSignature"))?;

Ok(ParsedEntry {
agent: ed25519_dalek::VerifyingKey::from_bytes(&agent)
.map_err(std::io::Error::other)?,
space: BASE64_URL_SAFE_NO_PAD
.decode(inn.space)
.map_err(std::io::Error::other)?
.into(),
created_at: inn
.created_at
.parse()
.map_err(std::io::Error::other)?,
expires_at: inn
.expires_at
.parse()
.map_err(std::io::Error::other)?,
is_tombstone: inn.is_tombstone,
encoded: out.agent_info,
signature: ed25519_dalek::Signature::from_bytes(&signature),
})
}
}
Loading