refactor(snix/castore-http/cli): ask for root directory digest

Restrict the CLI to only root directories, passing the blake3 digest of
the root directory.

Usually we want to serve a directory, and we now have a `snix-castore
ingest` sucommand, and copying the output from that command is much less
effort than constructing a proto message.

More advanced usecases can still use the get_root_node_contents library
function and pass in other nodes.

Change-Id: I66c2c0a15723b43b5b0cffc1c201391df57dd602
Reviewed-on: https://cl.snix.dev/c/snix/+/30321
Reviewed-by: Stefan Junker <mail@stefanjunker.de>
Tested-by: besadii
This commit is contained in:
Florian Klink 2025-04-14 13:09:35 +02:00
parent fd5f316fb8
commit d1990c9a93
5 changed files with 16 additions and 62 deletions

3
snix/Cargo.lock generated
View file

@ -4258,13 +4258,10 @@ dependencies = [
"axum-range", "axum-range",
"axum-test", "axum-test",
"blake3", "blake3",
"bytes",
"clap", "clap",
"data-encoding",
"mime", "mime",
"mime_guess", "mime_guess",
"path-clean", "path-clean",
"prost",
"snix-castore", "snix-castore",
"tokio", "tokio",
"tokio-listener", "tokio-listener",

View file

@ -13966,19 +13966,11 @@ rec {
name = "axum-range"; name = "axum-range";
packageId = "axum-range"; packageId = "axum-range";
} }
{
name = "bytes";
packageId = "bytes";
}
{ {
name = "clap"; name = "clap";
packageId = "clap"; packageId = "clap";
features = [ "derive" ]; features = [ "derive" ];
} }
{
name = "data-encoding";
packageId = "data-encoding";
}
{ {
name = "mime"; name = "mime";
packageId = "mime"; packageId = "mime";
@ -13991,10 +13983,6 @@ rec {
name = "path-clean"; name = "path-clean";
packageId = "path-clean"; packageId = "path-clean";
} }
{
name = "prost";
packageId = "prost";
}
{ {
name = "snix-castore"; name = "snix-castore";
packageId = "snix-castore"; packageId = "snix-castore";

View file

@ -8,13 +8,10 @@ anyhow.workspace = true
axum = { workspace = true, features = ["tracing"] } axum = { workspace = true, features = ["tracing"] }
axum-extra.workspace = true axum-extra.workspace = true
axum-range.workspace = true axum-range.workspace = true
bytes.workspace = true
clap = { workspace = true, features = ["derive"] } clap = { workspace = true, features = ["derive"] }
data-encoding.workspace = true
mime_guess = "2.0.5" mime_guess = "2.0.5"
mime = "0.3.17" mime = "0.3.17"
path-clean.workspace = true path-clean.workspace = true
prost.workspace = true
tokio = { workspace = true, features = [ "tracing"] } tokio = { workspace = true, features = [ "tracing"] }
tokio-listener = { workspace = true, features = ["axum07", "clap", "multi-listener", "sd_listen"] } tokio-listener = { workspace = true, features = ["axum07", "clap", "multi-listener", "sd_listen"] }
tracing.workspace = true tracing.workspace = true

View file

@ -1,5 +1,5 @@
use clap::Parser; use clap::Parser;
use snix_castore::utils::ServiceUrlsGrpc; use snix_castore::{utils::ServiceUrlsGrpc, B3Digest};
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about)] #[command(author, version, about)]
@ -10,13 +10,9 @@ pub struct CliArgs {
// The castore services addresses // The castore services addresses
#[clap(flatten)] #[clap(flatten)]
pub service_addrs: ServiceUrlsGrpc, pub service_addrs: ServiceUrlsGrpc,
/// The castore root node to serve, URL-safe base64-encoded /// The root directory digest to serve.
#[arg( #[arg(short, long, help = "The root directory digest to serve")]
short, pub root_directory: B3Digest,
long,
help = "The castore root node to serve, URL-safe base64-encoded"
)]
pub root_node: String,
/// The name of the file to serve if a client requests a directory e.g. index.html index.htm /// The name of the file to serve if a client requests a directory e.g. index.html index.htm
#[arg( #[arg(
short, short,

View file

@ -1,47 +1,23 @@
use snix_castore_http::cli::CliArgs;
use anyhow::{bail, Context};
use bytes::Bytes;
use clap::Parser; use clap::Parser;
use data_encoding::BASE64URL_NOPAD; use snix_castore::Node;
use prost::Message; use snix_castore_http::cli::CliArgs;
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt().init(); tracing_subscriber::fmt().init();
let CliArgs { let args: CliArgs = snix_castore_http::cli::CliArgs::parse();
listen_args,
service_addrs,
root_node,
index_names,
auto_index,
}: CliArgs = snix_castore_http::cli::CliArgs::parse();
// b64decode the root node passed *by the user*
let root_entry_proto = BASE64URL_NOPAD
.decode(root_node.as_bytes())
.context("unable to decode root node b64")?;
// check the proto size to be somewhat reasonable before parsing it.
if root_entry_proto.len() > 4096 {
bail!("rejected, too large root node");
}
// parse the proto
let root_entry: snix_castore::proto::Entry = Message::decode(Bytes::from(root_entry_proto))
.context("unable to decode root node proto")?;
let root_node = root_entry
.try_into_anonymous_node()
.context("root node validation failed")?;
snix_castore_http::router::gen_router( snix_castore_http::router::gen_router(
listen_args, args.listen_args,
service_addrs, args.service_addrs,
root_node, Node::Directory {
&index_names, digest: args.root_directory,
auto_index, // size doesn't really matter here, we're not doing inode allocation.
size: 0,
},
&args.index_names,
args.auto_index,
) )
.await .await
} }