# Account management In this section, you will learn how to manage a Jami account: creating an account, modifying its basic settings, and deleting an account. This section does not cover the meaning of each individual setting or using the account to perform actions such as adding contacts. ## What is an account? In Jami, an account is represented by an X.509 certificate chain composed of three certificates: 1. CA: A Self-Signed certificate if generated locally, or issued by a company. 2. Account: Contains the public key, its fingerprint serves as the {doc}`Jami ID `. 3. Device: Associated with the account. ```{note} This structure allows a company to revoke an entire account if necessary, and enables an account owner to revoke individual devices (e.g., if a device is stolen). ``` ## Creating a New Account ### Daemon-side API The ConfigurationManager API (cx.ring.Ring.ConfigurationManager) provides the `addAccount` method: ```xml Add a new account. When created, the signal accountsChanged is emitted. The clients must then call getAccountList to update their internal data structure. If no details are specified, the default parameters are used. The core tries to register the account as soon it is created. The new account settings A new account ID ``` By default, you can retrieve template parameters using `getAccountTemplate(type)` with `type="JAMI"` or `type="SIP"`. ### Core Implementation The core logic for account creation resides in `src/account_factory.cpp`, within the `AccountFactory::createAccount` method. ```cpp std::shared_ptr AccountFactory::createAccount(std::string_view accountType, const std::string& id) { if (hasAccount(id)) { JAMI_ERROR("Existing account {}", id); return nullptr; } const auto& it = generators_.find(accountType); if (it == generators_.cend()) return {}; std::shared_ptr account = it->second(id); { std::lock_guard lock(mutex_); auto m = accountMaps_.find(accountType); if (m == accountMaps_.end()) m = accountMaps_.emplace(std::string(accountType), AccountMap{}).first; m->second.emplace(id, account); } return account; } ``` After calling `addAccount` (or `createAccount` via the AccountFactory), the `accountsChanged` signal is emitted. Clients should handle this signal and invoke `getAccountList` to update their local state. ### Internal Archive Format When an account is created, its data is stored in a gzip archive. If a password is provided, the archive is encrypted with AES: `dht::crypto::aesEncrypt(archive, password)` (`dht::crypto::aesEncrypt` is defined in OpenDHT and use `nettle/{aes,gcm}`). Inside the archive, you will find a JSON file containing: 1. The private key `ringAccountKey` and certificate chain `ringAccountCert`, base64 encoded. 2. The generated CA key `ringCAKey`. 3. The certificate revocation list for devices `ringAccountCRL`. 5. The contact list. 6. The account settings ## Deleting an Account Deleting a Jami account is straightforward: since all key material resides on the local device, removing those key files effectively deletes the account. The only external record is the account’s username on the nameserver, which may persist depending on the server’s policies (for example, `https://ns.jami.net` does not currently support remote name deletion). ### Daemon-side API The ConfigurationManager API provides the `removeAccount` method: ```xml Remove an existing account. When removed, the signal accountsChanged is emitted. The clients must then call getAccountList to update their internal data structure. The account to remove, identified by its ID ``` When the account is deleted, the signal `accountsChanged` will be emitted. The client should update its internal structure after this signal with other methods in ConfigurationManager. #### Core Implementation The core removal logic is in `src/accout_factory.cpp` (`AccountFactory::removeAccount`), which deletes the account files and updates the `dring.yml` configuration. ```cpp void AccountFactory::removeAccount(Account& account) { std::string_view account_type = account.getAccountType(); std::lock_guard lock(mutex_); const auto& id = account.getAccountID(); JAMI_DEBUG("Removing account {:s}", id); auto m = accountMaps_.find(account_type); if (m != accountMaps_.end()) { m->second.erase(id); JAMI_DEBUG("Remaining {:d} {:s} account(s)", m->second.size(), account_type); } } ``` ## Updating the details of an account ### Daemon-side API The ConfigurationManager API provides the `setAccountDetails` method: ```xml Send new account parameters, or account parameters changes, to the core. The hash table is not required to be complete, only the updated parameters may be specified. Account settings are written to the configuration file when the app properly quits. After calling this method, the core will emit the signal accountDetailsChanged with the updated data. The client must subscribe to this signal and use it to update its internal data structure. ``` The map can contains a partial update and `accountDetailsChanged` will be emitted on success. `getAccountDetails` ## Manage Devices ### Add a device There are two methods to add a device to an existing account. #### 1. Restore from Backup Archive Use `exportToFile` to extract the account archive to a local path: ```xml Copy the account archive to the path provided in argument. True if the operation was initialized successfully. ``` #### 2. Export on the DHT network Use `exportOnRing` to publish the encrypted archive on the DHT and obtain a PIN: ```xml Export account on the DHT using the given password and generated PIN (returned through exportOnRingEnded signal). True if the operation was initialized successfully. exportOnRingEnded will be trigered on completion. ``` Then `exportOnRingEnded` is emitted. Clients must listen for the signal to retrieve the PIN. ### Revoke a device To revoke a device, call `revokeDevice`: ```xml Revoke device attached to the given Jami account, and publish the new revocation list. True if the operation was performed successfully. Notify clients when the revokeDevice operation ended. Status code: 0 for success
  • SUCCESS = 0 everything went fine. Device is now revoked.
  • WRONG_PASSWORD = 1 revocation failed: wrong password.
  • UNKNOWN_DEVICE = 2 revocation failed: unknown device.
``` Upon completion, the `deviceRevocationEnded` signal is emitted with a status code: - 0 (SUCCESS) - 1 (WRONG_PASSWORD) - 2 (UNKNOWN_DEVICE) Clients should handle this signal to confirm the revocation status.