v0.0.22: version bump, ETH identity in web client, version bump rule
Version: - Workspace + protocol: 0.0.21 → 0.0.22 - Web client VERSION: 0.0.17 → 0.0.22 - Service worker cache: wz-v2 → wz-v3 ETH identity: - Added WasmIdentity::eth_address() export (derives from seed via secp256k1) - Web client sends eth_address during key registration - Identity display shows ETH address first, then fingerprint - No more server-side resolve needed — computed client-side CLAUDE.md: - Added MANDATORY version bump rule (4 places to update) - Must bump on every functional change, never skip SW cache Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
10
warzone/Cargo.lock
generated
10
warzone/Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -9,7 +9,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.21"
|
||||
version = "0.0.22"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.75"
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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<Vec<u8>, JsValue> {
|
||||
|
||||
Reference in New Issue
Block a user