From 116e42aef929298cce778967beeedeb0a717eaf0 Mon Sep 17 00:00:00 2001 From: George Coles Date: Tue, 15 Sep 2026 10:29:10 -0400 Subject: [PATCH] Onboarding: --listen override and auto-pick free port; live TLS demo verified --- src/commands.rs | 91 +++++++++++++++++++++++++++++++++++++++------ src/main.rs | 4 +- src/onboard.rs | 15 +++++++- tests/onboarding.rs | 2 +- 4 files changed, 97 insertions(+), 15 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 96d1610..bfae7b0 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -692,29 +692,96 @@ async fn registry_page() -> Response { .into_response() } -const REGISTRY_PAGE: &str = r#" -FRX membership +const REGISTRY_PAGE: &str = r##" + + + + +FRX — Federated Retrieval Exchange + + -

Request FRX membership

+

FRX

+

Federated Retrieval Exchange — a membership federation for retrieval.

+

Members answer broadcast queries from content they already hold. The protocol is deliberately +small: signed messages, budgets, honest truncation, aggregate courtesy. No announce stream, no +scores on the wire, no in-protocol payment.

+

Everything else — matching, ranking, retention, trust — is local.

+ +

Join the federation

+
-
-
+
+
-

+

+
+ +

After you get credentials

+
    +
  1. Install the single static binary: frxd.
  2. +
  3. Run frxd --onboarding and paste the credential block it gives you.
  4. +
  5. The wizard binds your keys, verifies the signed registry, and wires your relays — no domains, DNS, or ports needed on your side.
  6. +
+ +

Who is in

+

+

The registry is a signed, versioned, public snapshot: GET /registry.json. Membership is governed by the MA — questions and codes come from there.

+ - -"#; + + +"##; pub async fn status(config_path: &Path) -> Result<()> { let config = Config::load(config_path)?; diff --git a/src/main.rs b/src/main.rs index 55b833e..1a1e995 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,8 @@ struct Cli { config: PathBuf, #[arg(long)] onboarding: bool, + #[arg(long)] + listen: Option, #[command(subcommand)] command: Option, } @@ -204,7 +206,7 @@ async fn main() -> Result<()> { if cli.onboarding { let mut input = std::io::stdin().lock(); let mut output = std::io::stdout().lock(); - onboard::run(&mut input, &mut output, &cli.config).await?; + onboard::run(&mut input, &mut output, &cli.config, cli.listen.as_deref()).await?; return Ok(()); } let Some(command) = cli.command else { diff --git a/src/onboard.rs b/src/onboard.rs index 59892d2..bef822b 100644 --- a/src/onboard.rs +++ b/src/onboard.rs @@ -13,6 +13,7 @@ pub async fn run( reader: &mut impl BufRead, writer: &mut impl Write, config_path: &Path, + listen_override: Option<&str>, ) -> Result<()> { writeln!(writer, "FRX onboarding")?; writeln!( @@ -150,7 +151,10 @@ pub async fn run( } let data_dir = prompt(writer, reader, "data directory", "./frx-data")?; - let mut config = Config::new(&id, "127.0.0.1:7701", relays.clone(), &data_dir); + let listen = listen_override + .map(str::to_string) + .unwrap_or_else(|| pick_listen("127.0.0.1:7701")); + let mut config = Config::new(&id, &listen, relays.clone(), &data_dir); config.node.id = Some(id.clone()); config.node.registry = Some(registry.clone()); config.node.ma_key = Some(ma_key.clone()); @@ -180,6 +184,7 @@ pub async fn run( writeln!(writer, " config: {}", config_path.display())?; writeln!(writer, " registry: {registry}")?; writeln!(writer, " relays: {}", relays.join(", "))?; + writeln!(writer, " listening: {listen}")?; writeln!( writer, "Next: frxd serve, then frx query \"...\" to broadcast." @@ -228,6 +233,14 @@ fn parse_block(block: &str) -> Result { }) } +fn pick_listen(preferred: &str) -> String { + if std::net::TcpListener::bind(preferred).is_ok() { + return preferred.to_string(); + } + let fallback = std::net::TcpListener::bind("127.0.0.1:0").expect("bind ephemeral port"); + fallback.local_addr().expect("local addr").to_string() +} + fn registry_base(registry_url: &str) -> String { match registry_url.rfind('/') { Some(index) => registry_url[..index].to_string(), diff --git a/tests/onboarding.rs b/tests/onboarding.rs index bbab8d8..839e196 100644 --- a/tests/onboarding.rs +++ b/tests/onboarding.rs @@ -120,7 +120,7 @@ async fn wizard_enrolls_and_writes_config() { let input = format!("{}\n{credentials}\n\n\n\nn\n", config_path.display()); let mut reader = input.as_bytes(); let mut output = Vec::new(); - onboard::run(&mut reader, &mut output, &config_path) + onboard::run(&mut reader, &mut output, &config_path, None) .await .unwrap();