use nedb_engine::server;
const HELP: &str = "\
nedbd-v2 — NEDB v2/v3 DAG storage daemon
USAGE:
nedbd-v2 [OPTIONS] [DATA_DIR]
OPTIONS:
-d, --data <DIR> Data directory (default: ./nedb-data)
--dag-v3 Use the v3 segment/pack object store (sets NEDB_DAG_V3=1)
--fast-fsync macOS fast fsync — plain fsync(2) instead of F_FULLFSYNC
(sets NEDB_FAST_FSYNC=1; no-op off macOS).
Alias: --dag-fast-sync
-H, --host <ADDR> Bind address (default: 127.0.0.1 — loopback only)
-p, --port <PORT> HTTP port (default: 7070)
--token <TOKEN> Bearer token required on every request
-m, --memory Pure in-memory mode (no disk I/O; data lost on exit)
-h, --help Print this help and exit
-V, --version Print version and exit
ENVIRONMENT (flags take precedence when both are set):
NEDBD_HOST NEDBD_PORT NEDBD_TOKEN NEDBD_MEMORY NEDB_DAG_V3 NEDB_FAST_FSYNC
NEDB_TMK 32-byte hex master key for AES-256-GCM (env-only — never a flag)
EXAMPLES:
nedbd-v2 ./data # v2 DAG (loose objects) at ./data
nedbd-v2 --dag-v3 ./data # v3 segment store at ./data
nedbd-v2 --dag-v3 --data ./data --port 7171
NEDB_DAG_V3=1 nedbd-v2 ./data # env form (equivalent to --dag-v3)
";
fn die(msg: String) -> ! {
eprintln!("nedbd-v2: {}", msg);
eprintln!("Try 'nedbd-v2 --help' for usage.");
std::process::exit(2);
}
fn need_val(args: &[String], i: &mut usize, inline: Option<&str>, name: &str) -> String {
if let Some(v) = inline {
return v.to_string();
}
*i += 1;
if *i >= args.len() {
die(format!("option '{}' requires a value", name));
}
args[*i].clone()
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut data_dir: Option<String> = None;
let mut host = std::env::var("NEDBD_HOST").unwrap_or_else(|_| "127.0.0.1".to_string());
let mut port: u16 = std::env::var("NEDBD_PORT")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(7070);
let mut token = std::env::var("NEDBD_TOKEN").ok().filter(|s| !s.is_empty());
let mut memory_mode = std::env::var("NEDBD_MEMORY")
.map(|v| matches!(v.as_str(), "1" | "true" | "yes"))
.unwrap_or(false);
let args: Vec<String> = std::env::args().skip(1).collect();
let mut i = 0;
while i < args.len() {
let raw = args[i].clone();
let (key, inline): (String, Option<String>) = match raw.split_once('=') {
Some((k, v)) => (k.to_string(), Some(v.to_string())),
None => (raw.clone(), None),
};
match key.as_str() {
"-h" | "--help" => {
print!("{}", HELP);
return Ok(());
}
"-V" | "--version" => {
println!("nedbd-v2 {}", env!("CARGO_PKG_VERSION"));
return Ok(());
}
"--dag-v3" => {
std::env::set_var("NEDB_DAG_V3", "1");
}
"--fast-fsync" | "--dag-fast-sync" => {
std::env::set_var("NEDB_FAST_FSYNC", "1");
}
"-m" | "--memory" => {
memory_mode = true;
}
"-d" | "--data" => {
data_dir = Some(need_val(&args, &mut i, inline.as_deref(), "--data"));
}
"-H" | "--host" => {
host = need_val(&args, &mut i, inline.as_deref(), "--host");
}
"-p" | "--port" => {
let v = need_val(&args, &mut i, inline.as_deref(), "--port");
port = v
.parse()
.unwrap_or_else(|_| die(format!("invalid --port '{}': expected 0-65535", v)));
}
"--token" => {
token = Some(need_val(&args, &mut i, inline.as_deref(), "--token"))
.filter(|s| !s.is_empty());
}
_ if key.starts_with('-') && key != "-" => {
die(format!("unknown option '{}'", key));
}
_ => {
if data_dir.is_some() {
die(format!("unexpected extra argument '{}'", raw));
}
data_dir = Some(raw);
}
}
i += 1;
}
let tmk: Option<[u8; 32]> = std::env::var("NEDB_TMK")
.ok()
.and_then(|s| hex::decode(s).ok())
.and_then(|b| b.try_into().ok());
let data_dir = data_dir.unwrap_or_else(|| "./nedb-data".to_string());
server::run(&host, port, &data_dir, tmk, token, memory_mode).await
}