Your identity is a key pair
BIP-39, the derivation path, the dc1 address, and the honest consequence of a public identity registry.
Your identity is a key pair
There is no username-and-password anywhere in DChat. The account is a secp256k1 key pair generated on your device, and the recovery phrase, the address, and the DID are all derived from or built around that one pair.
New accounts default to a 24-word BIP-39 recovery phrase (256 bits of entropy) generated on the English wordlist; 12 words is also supported for those who choose it. Derivation follows the standard Cosmos BIP-32/BIP-44 path m/44'/118'/0'/0/0 where 118 is the registered Cosmos SLIP-44 coin type, on the secp256k1 curve. The account address is the standard Cosmos derivation - bech32-encoding of RIPEMD160(SHA256(compressed_pubkey)) - using the human-readable prefix dc so addresses look like dc1…. Signing is ECDSA over secp256k1, deterministic per RFC 6979, SHA-256 pre-hashed, and emitted as a raw 64-byte R‖S pair per Cosmos ADR-036 rather than DER, though verification also accepts 70-72 byte DER for interoperability.
Documentation sometimes simplifies this to "three keys." There are actually five, and they are never interchanged: the identity/wallet key that signs transactions and ADR-036 payloads; the chat key a separately and randomly generated secp256k1 key (not derived from the seed) used for message key agreement, which rotates on its own policy; per-group sender keys 32 random bytes per member per group; an account rotating key that turns over every 7 days with a 72-hour grace window and is shared across a user's own linked devices through ConfigSync; and an Ed25519 IPNS signing key derived from the identity seed via HKDF, whose only job is signing IPNS records - the device registry, the backup pointer, and the group-authority pointer described in the groups guide. It never touches a message.
DID format is one place the codebase is genuinely inconsistent, and it is worth saying so rather than inventing a clean answer: the SDK constructs did:dchat:{username} client-side, the chain's identity module independently generates did:dchat: followed by a base58-encoded hash of the address, and the profile module uses a third form, did:dm:…. Treat the DID as an internal identifier rather than a stable public format.
Every signature DChat produces is checked against its own public key immediately after generation, and generation fails closed if that check does not pass - a broken signature is never handed to the network. The chain's identity module is what actually stores your public record: dtag/DID, username, node type, status, linked account, and (if set) gender, country and verification level, alongside ban fields and a report count used for moderation. It is written through messages like RegisterIdentity UpdateChatKey and UpdateParams and read through queries like UsernameExists IdentityByUsername IdentityByAddress and BanStatus - all public, all queryable by anyone, by design. A governance- or operator-gated PurgeIdentity message exists for erasure requests, but it cannot rewrite history already recorded on the ledger; it can only stop new lookups from resolving.
Registering a username writes it, alongside your address and any display name or avatar you set, to the public chain. That means username → DID → wallet address is a chain anyone can query not a private mapping - the identity module's own queries (IdentityByUsername IdentityByAddress UsernameExists) exist specifically to make that lookup easy. The practical advice that follows: a username tied to your real name links your wallet activity and your messaging identity to you personally, permanently and publicly. An address with no registered username discloses far less.
Related: Spec §2 - identity and account keys · FAQ - what about me is public on the chain?