# Identity > **Phase 1 note:** the client stores the keypair as plain JSON in the data directory. > Passphrase encryption (Argon2id), seed phrase UI, and keyfile export are Phase 2. ## Model VNOX has no accounts. No email. No phone number. No central registry. On first launch, the client generates a keypair locally: ```json { "id": "4f3a8b2c9d1e7a0f3b5c8d2e1a9f4b7c2d8e3f1a", "nickname": "user", "pubkey": "ed25519:4f3a8b2c...", "created_at": 1716000000 } ``` The private key never leaves the device (unless explicitly exported by the user). The public key is the identity. Everything — permissions, history, session tokens — is tied to the pubkey. --- ## Keypair Algorithm: Ed25519 - fast signature verification - small key size (32 bytes public, 64 bytes private) - widely supported in Rust ecosystem (dalek-cryptography) The keypair is stored locally in the client's data directory. **Phase 1 (current):** plain JSON file (`identity.json`). **Phase 2 (planned):** encrypted at rest with a user-chosen passphrase (Argon2id KDF). --- ## Auth flow ``` Client Gateway │ │ │── TCP connect ────────────────────▶│ │ │ │◀── HELLO { server_pubkey, │ │ challenge_nonce } ──────│ │ │ │── AUTH { │ │ client_pubkey, │ │ nickname, │ │ lnex_version, │ │ sig: sign(challenge_nonce, │ │ client_privkey) │ │ } ──────────────────────────────▶│ │ │ verify signature │ │ check if banned │◀── SESSION { │ │ session_id, │ │ token, │ │ expires_at │ │ } ──────────────────────────────│ │ │ │ [authenticated] │ ``` The gateway verifies the signature against the challenge nonce. If valid, a session token is issued. The token is used for subsequent requests within the TCP session. The gateway does not store private keys. It only stores public keys of users who have connected (for banning and permission assignment). --- ## Session Sessions are in-memory on the gateway. They expire when the TCP connection closes or after a configurable idle timeout. There is no persistent login. Each connection requires a fresh auth exchange. The session token is not a password — it proves nothing without the underlying keypair. --- ## Nickname Nickname is a human-readable label, not unique. Two users may have the same nickname. The pubkey (or its short form) is the unique identifier. Nickname is self-asserted and can be changed at any time. Servers may impose length or character limits. --- ## Permissions Permissions are assigned to pubkeys on each node independently. There is no global permission registry. Permission levels (example model — configurable per node): ``` guest — read channels, no voice member — read + write + voice moderator — member + kick + mute others + manage channels admin — full control of the node owner — same as admin, cannot be demoted by admins ``` A user's permission level on node A has no bearing on their level on node B. Federated permission model is a Phase 4 design question. --- ## Keypair backup > **Phase 2.** UI and export flows below are not implemented in v0.1.x. If the keypair is lost, the identity is lost. There is no recovery without a backup. This is intentional. There is no central authority to reset your account. Two backup methods are planned: ### Seed phrase A 24-word BIP39-compatible mnemonic derived from the private key entropy. ``` word1 word2 word3 ... word24 ``` The seed phrase can regenerate the keypair deterministically. Store it offline. Never share it. To enable: Settings → Identity → Seed phrase backup → Show phrase. ### Encrypted keyfile Export the keypair as a `.vnox` file, encrypted with a passphrase (AES-256-GCM, passphrase stretched with Argon2id). ``` Settings → Identity → Export keypair → Save as .vnox ``` To restore: launch client → Import keypair → select .vnox file → enter passphrase. --- ## Rotating the keypair If a keypair is compromised, the user can generate a new one. The old keypair becomes inactive immediately. Effects: - new identity on all nodes (the old pubkey is a different person) - permissions tied to old pubkey remain on the server (admins can clean up) - history attributed to old pubkey is not migrated Rotation is permanent and cannot be undone. `Settings → Identity → Rotate keypair → Confirm`