diff --git a/warzone/CLAUDE.md b/warzone/CLAUDE.md index 5b982e8..0a3c885 100644 --- a/warzone/CLAUDE.md +++ b/warzone/CLAUDE.md @@ -1,5 +1,15 @@ # featherChat — Design Principles & Conventions +## MANDATORY: Version Bumping + +**After every set of changes that modifies functionality, bump the version:** +1. `Cargo.toml` workspace version (e.g. `0.0.22` → `0.0.23`) +2. `crates/warzone-protocol/Cargo.toml` standalone version (same) +3. `crates/warzone-server/src/routes/web.rs` JS `VERSION` constant +4. `crates/warzone-server/src/routes/web.rs` service worker `CACHE` version (`wz-vN` → `wz-v(N+1)`) + +Never commit functional changes without bumping all four. The service worker cache MUST be bumped or browsers will serve stale WASM. + ## Architecture Principles 1. **Single seed, multiple identities** — Ed25519 (messaging), X25519 (encryption), secp256k1 (ETH address) all derived from one BIP39 seed via HKDF with domain-separated info strings. diff --git a/warzone/Cargo.lock b/warzone/Cargo.lock index f2bd0a8..27cffb7 100644 --- a/warzone/Cargo.lock +++ b/warzone/Cargo.lock @@ -2956,7 +2956,7 @@ dependencies = [ [[package]] name = "warzone-client" -version = "0.0.21" +version = "0.0.22" dependencies = [ "anyhow", "argon2", @@ -2989,7 +2989,7 @@ dependencies = [ [[package]] name = "warzone-mule" -version = "0.0.21" +version = "0.0.22" dependencies = [ "anyhow", "clap", @@ -2998,7 +2998,7 @@ dependencies = [ [[package]] name = "warzone-protocol" -version = "0.0.21" +version = "0.0.22" dependencies = [ "base64", "bincode", @@ -3023,7 +3023,7 @@ dependencies = [ [[package]] name = "warzone-server" -version = "0.0.21" +version = "0.0.22" dependencies = [ "anyhow", "axum", @@ -3053,7 +3053,7 @@ dependencies = [ [[package]] name = "warzone-wasm" -version = "0.0.21" +version = "0.0.22" dependencies = [ "base64", "bincode", diff --git a/warzone/Cargo.toml b/warzone/Cargo.toml index da33c53..132b47a 100644 --- a/warzone/Cargo.toml +++ b/warzone/Cargo.toml @@ -9,7 +9,7 @@ members = [ ] [workspace.package] -version = "0.0.21" +version = "0.0.22" edition = "2021" license = "MIT" rust-version = "1.75" diff --git a/warzone/crates/warzone-protocol/Cargo.toml b/warzone/crates/warzone-protocol/Cargo.toml index 5a525de..b118c06 100644 --- a/warzone/crates/warzone-protocol/Cargo.toml +++ b/warzone/crates/warzone-protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "warzone-protocol" -version = "0.0.21" +version = "0.0.22" edition = "2021" license = "MIT" description = "Core crypto & wire protocol for featherChat (Warzone messenger)" diff --git a/warzone/crates/warzone-server/src/routes/web.rs b/warzone/crates/warzone-server/src/routes/web.rs index 9b775b2..5ee6af0 100644 --- a/warzone/crates/warzone-server/src/routes/web.rs +++ b/warzone/crates/warzone-server/src/routes/web.rs @@ -50,7 +50,7 @@ async fn pwa_manifest() -> impl IntoResponse { async fn service_worker() -> impl IntoResponse { ([(header::CONTENT_TYPE, "application/javascript")], r##" -const CACHE = 'wz-v2'; +const CACHE = 'wz-v3'; const SHELL = ['/', '/wasm/warzone_wasm.js', '/wasm/warzone_wasm_bg.wasm', '/icon.svg', '/manifest.json']; self.addEventListener('install', e => { @@ -242,7 +242,7 @@ let pollTimer = null; let ws = null; // WebSocket connection let wasmReady = false; -const VERSION = '0.0.17'; +const VERSION = '0.0.22'; let DEBUG = true; // toggle with /debug command // ── Receipt tracking ── @@ -388,11 +388,12 @@ function loadSavedIdentity() { async function registerKey() { const fp = normFP(myFingerprint); const bundleBytes = wasmIdentity.bundle_bytes(); - dbg('Registering key, fp:', fp, 'bundle size:', bundleBytes.length); + myEthAddress = wasmIdentity.eth_address(); + dbg('Registering key, fp:', fp, 'bundle size:', bundleBytes.length, 'eth:', myEthAddress); await fetch(SERVER + '/v1/keys/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ fingerprint: fp, bundle: Array.from(bundleBytes) }) + body: JSON.stringify({ fingerprint: fp, bundle: Array.from(bundleBytes), eth_address: myEthAddress }) }); dbg('Key registered'); } @@ -862,20 +863,14 @@ async function enterChat() { document.getElementById('hdr-server').textContent = SERVER; await registerKey(); - addSys('Identity loaded: ' + myFingerprint); + addSys('Identity: ' + myEthAddress); + addSys('Fingerprint: ' + myFingerprint); addSys('Key registered with server'); - // Fetch ETH address from server - try { - const resolveResp = await fetch(SERVER + '/v1/resolve/' + normFP(myFingerprint)); - const resolveData = await resolveResp.json(); - if (resolveData.eth_address) { - myEthAddress = resolveData.eth_address; - addSys('ETH: ' + myEthAddress); - document.getElementById('hdr-eth').textContent = myEthAddress.slice(0, 10) + '...'; - document.getElementById('hdr-eth').title = myEthAddress; - } - } catch(e) { dbg('ETH resolve failed:', e); } + if (myEthAddress) { + document.getElementById('hdr-eth').textContent = myEthAddress.slice(0, 10) + '...'; + document.getElementById('hdr-eth').title = myEthAddress; + } addSys('v' + VERSION + ' | DM: paste peer fingerprint or @alias above'); addSys('/alias · /g · /gleave · /gkick · /gmembers · /glist · /friend · /file · /info'); diff --git a/warzone/crates/warzone-wasm/src/lib.rs b/warzone/crates/warzone-wasm/src/lib.rs index 879393d..0c84ae4 100644 --- a/warzone/crates/warzone-wasm/src/lib.rs +++ b/warzone/crates/warzone-wasm/src/lib.rs @@ -52,6 +52,12 @@ impl WasmIdentity { Seed::from_bytes(self.seed_bytes).to_mnemonic() } + /// Get the Ethereum address derived from this seed. + pub fn eth_address(&self) -> String { + let eth = warzone_protocol::ethereum::derive_eth_identity(&self.seed_bytes); + eth.address.to_checksum() + } + /// Get the pre-key bundle as bincode bytes (for server registration). /// The bundle is generated once and cached. The SPK secret is stored internally. pub fn bundle_bytes(&mut self) -> Result, JsValue> {