diff --git a/Cargo.lock b/Cargo.lock index 5987018..aeb5c20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -847,6 +847,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1760,6 +1781,15 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "libredox" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +dependencies = [ + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -2089,6 +2119,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "os_str_bytes" version = "6.6.1" @@ -2429,6 +2465,17 @@ dependencies = [ "bitflags 2.11.0", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + [[package]] name = "regex" version = "1.12.3" @@ -4323,6 +4370,7 @@ dependencies = [ "async-trait", "axum 0.7.9", "bytes", + "dirs", "futures-util", "prometheus", "quinn", diff --git a/crates/wzp-relay/Cargo.toml b/crates/wzp-relay/Cargo.toml index df6dae4..9e85240 100644 --- a/crates/wzp-relay/Cargo.toml +++ b/crates/wzp-relay/Cargo.toml @@ -28,6 +28,7 @@ prometheus = "0.13" axum = { version = "0.7", default-features = false, features = ["tokio", "http1", "ws"] } tower-http = { version = "0.6", features = ["fs"] } futures-util = "0.3" +dirs = "6" [[bin]] name = "wzp-relay" diff --git a/crates/wzp-relay/src/main.rs b/crates/wzp-relay/src/main.rs index dc59472..1f73636 100644 --- a/crates/wzp-relay/src/main.rs +++ b/crates/wzp-relay/src/main.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use std::time::Duration; use tokio::sync::Mutex; -use tracing::{error, info}; +use tracing::{error, info, warn}; use wzp_proto::MediaTransport; use wzp_relay::config::RelayConfig; @@ -207,8 +207,39 @@ async fn main() -> anyhow::Result<()> { tokio::spawn(wzp_relay::metrics::serve_metrics(port, m, p, rr)); } - // Generate ephemeral relay identity for crypto handshake - let relay_seed = wzp_crypto::Seed::generate(); + // Load or generate relay identity — persisted in ~/.wzp/relay-identity + let relay_seed = { + let config_dir = dirs::home_dir() + .unwrap_or_else(|| std::path::PathBuf::from(".")) + .join(".wzp"); + let identity_path = config_dir.join("relay-identity"); + if identity_path.exists() { + if let Ok(hex) = std::fs::read_to_string(&identity_path) { + if let Ok(s) = wzp_crypto::Seed::from_hex(hex.trim()) { + info!("loaded relay identity from {}", identity_path.display()); + s + } else { + warn!("corrupt relay identity file, generating new"); + let s = wzp_crypto::Seed::generate(); + let hex: String = s.0.iter().map(|b| format!("{b:02x}")).collect(); + let _ = std::fs::write(&identity_path, &hex); + s + } + } else { + let s = wzp_crypto::Seed::generate(); + let hex: String = s.0.iter().map(|b| format!("{b:02x}")).collect(); + let _ = std::fs::write(&identity_path, &hex); + s + } + } else { + let s = wzp_crypto::Seed::generate(); + let _ = std::fs::create_dir_all(&config_dir); + let hex: String = s.0.iter().map(|b| format!("{b:02x}")).collect(); + let _ = std::fs::write(&identity_path, &hex); + info!("generated relay identity at {}", identity_path.display()); + s + } + }; let relay_fp = relay_seed.derive_identity().public_identity().fingerprint; info!(addr = %config.listen_addr, fingerprint = %relay_fp, "WarzonePhone relay starting"); diff --git a/scripts/build-linux-notify.sh b/scripts/build-linux-notify.sh new file mode 100755 index 0000000..a2bc44b --- /dev/null +++ b/scripts/build-linux-notify.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Build WarzonePhone Linux x86_64 binaries via Hetzner Cloud VPS. +# Fire and forget — notifies via ntfy.sh/wzp with rustypaste URL. +# +# Usage: +# ./scripts/build-linux-notify.sh Full: create VM → build → upload → notify → destroy +# ./scripts/build-linux-notify.sh --keep Keep VM after build +# ./scripts/build-linux-notify.sh --pull Git pull (for existing VM) + +SSH_KEY_NAME="wz" +SSH_KEY_PATH="/Users/manwe/CascadeProjects/wzp" +SERVER_TYPE="cx33" +IMAGE="debian-12" +SERVER_NAME="wzp-linux-builder" +NTFY_TOPIC="https://ntfy.sh/wzp" +LOCAL_OUTPUT="target/linux-x86_64" +PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)" + +SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=15 -o ServerAliveInterval=15 -o LogLevel=ERROR" + +KEEP_VM=0 +DO_PULL=0 +for arg in "$@"; do + case "$arg" in + --keep) KEEP_VM=1 ;; + --pull) DO_PULL=1 ;; + esac +done + +log() { echo -e "\033[1;36m>>> $*\033[0m"; } +err() { echo -e "\033[1;31mERROR: $*\033[0m" >&2; } + +get_vm_ip() { + hcloud server list -o columns=name,ipv4 -o noheader 2>/dev/null | grep "$SERVER_NAME" | awk '{print $2}' | tr -d ' ' +} + +ssh_cmd() { + local ip=$(get_vm_ip) + [ -n "$ip" ] || { err "No VM found"; exit 1; } + ssh $SSH_OPTS -i "$SSH_KEY_PATH" "root@$ip" "$@" +} + +notify() { curl -s -d "$1" "$NTFY_TOPIC" > /dev/null 2>&1 || true; } + +# --- Create VM if needed --- +existing=$(hcloud server list -o columns=name -o noheader 2>/dev/null | grep "$SERVER_NAME" | tr -d ' ' || true) +if [ -z "$existing" ]; then + log "Creating Hetzner VM ($SERVER_TYPE, $IMAGE)..." + hcloud server create --name "$SERVER_NAME" --type "$SERVER_TYPE" --image "$IMAGE" --ssh-key "$SSH_KEY_NAME" --location fsn1 --quiet + + log "Waiting for SSH..." + ip=$(get_vm_ip) + for i in $(seq 1 30); do + ssh $SSH_OPTS -i "$SSH_KEY_PATH" "root@$ip" "echo ok" &>/dev/null && break + sleep 2 + done + + log "Installing deps..." + ssh_cmd "apt-get update -qq && apt-get install -y -qq build-essential cmake pkg-config libasound2-dev libssl-dev curl git > /dev/null 2>&1" + ssh_cmd "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable > /dev/null 2>&1" +fi + +# --- Upload source --- +log "Uploading source..." +ip=$(get_vm_ip) +rsync -az --delete \ + --exclude='target' --exclude='.git' --exclude='.claude' \ + --exclude='node_modules' --exclude='dist' --exclude='android/app/build' \ + -e "ssh $SSH_OPTS -i $SSH_KEY_PATH" \ + "$PROJECT_DIR/" "root@$ip:/root/wzp-build/" + +# --- Build --- +log "Building all binaries..." +notify "WZP Linux build started..." + +ssh_cmd "source ~/.cargo/env && cd /root/wzp-build && \ + cargo build --release --bin wzp-relay --bin wzp-client --bin wzp-web --bin wzp-bench 2>&1 | tail -5 && \ + echo '--- audio client ---' && \ + cargo build --release --bin wzp-client --features audio 2>&1 | tail -3 && \ + cp target/release/wzp-client target/release/wzp-client-audio && \ + cargo build --release --bin wzp-client 2>&1 | tail -3 && \ + echo 'BUILD_DONE' && \ + ls -lh target/release/wzp-relay target/release/wzp-client target/release/wzp-client-audio target/release/wzp-web target/release/wzp-bench" + +# --- Package + upload to rustypaste --- +log "Packaging and uploading..." +UPLOAD_URL=$(ssh_cmd "cd /root/wzp-build && \ + tar czf /tmp/wzp-linux-x86_64.tar.gz \ + -C target/release wzp-relay wzp-client wzp-client-audio wzp-web wzp-bench \ + -C /root/wzp-build/crates/wzp-web/static index.html audio-processor.js 2>/dev/null && \ + curl -s -F 'file=@/tmp/wzp-linux-x86_64.tar.gz' \ + -H 'Authorization: DAxAAGghkn1WKv1+RpPKkg==' \ + https://paste.dk.manko.yoga") + +if [ -n "$UPLOAD_URL" ]; then + notify "WZP Linux binaries ready! $UPLOAD_URL" + log "Uploaded: $UPLOAD_URL" +else + notify "WZP Linux build FAILED" + err "Upload failed" +fi + +# --- Transfer locally --- +log "Downloading binaries..." +mkdir -p "$LOCAL_OUTPUT" +for bin in wzp-relay wzp-client wzp-client-audio wzp-web wzp-bench; do + scp $SSH_OPTS -i "$SSH_KEY_PATH" "root@$ip:/root/wzp-build/target/release/$bin" "$LOCAL_OUTPUT/$bin" 2>/dev/null +done +ls -lh "$LOCAL_OUTPUT"/wzp-* + +# --- Cleanup --- +if [ "$KEEP_VM" = "1" ]; then + log "VM kept alive. Destroy: hcloud server delete $SERVER_NAME" +else + log "Destroying VM..." + hcloud server delete "$SERVER_NAME" +fi + +log "Done!" +echo " Deploy: scp $LOCAL_OUTPUT/wzp-relay user@server:~/wzp/"