Auth
Accounts
An account is derived from a twelve-word BIP-39 mnemonic. The same phrase always yields the same account id, while each machine that restores it mints its own device key — that is how a second device joins without cloning the first.
Mnemonic ⇒ account id
mnemonic + index ──► account key ──► account id
The derivation index defaults to 1, the any account index. Index 0 is reserved for accounts derived by anytype, so one phrase can serve both products with two distinct accounts. Restoring an anytype-derived account needs an explicit index 0.
Note. The mnemonic is the only recovery path.
anynever stores it; it is printed once at generation and never again. Back it up before writing data you care about.
First run: any init
any init # generate a fresh account, print the mnemonic once
any init --mnemonic-stdin < phrase.txt # restore an existing account (fresh device key)
any init --mnemonic "w1 … w12" --index 0 # restore an anytype-derived account
any init --new # force an additional fresh account
| Flag | Effect |
|---|---|
| (none) | generate a fresh account under <root>/<accountId>/; a no-op that lists accounts when any already exist |
--mnemonic, --mnemonic-stdin |
authorize an existing account; prefer stdin so the phrase stays out of shell history |
--index N |
derivation index for --mnemonic; default 1 |
--new |
create an additional account even when one exists |
Restoring writes a new wallet.key with a freshly generated device key. Never copy wallet.key between machines: that clones the device key, the two peers then present one network identity, and realtime sync between them breaks.
Authorizing over HTTP: /v1/auth
any run does not create wallets. When no account resolves from the data dir — a fresh root, or several accounts and no --account / ANY_ACCOUNT selector — the server starts unauthorized. Every /v1 route except /v1/health, /v1/shutdown, /v1/openapi.json and /v1/auth returns 401 auth.required.
curl -s http://127.0.0.1:7001/v1/auth
{ "authorized": false,
"accounts": [ { "id": "A8tR…", "default": true }, { "id": "A8g1…" } ] }
POST /v1/auth boots the engine in place — no restart. mnemonic and accountId are mutually exclusive:
# generate
curl -s -X POST http://127.0.0.1:7001/v1/auth -d '{}'
# restore at index 1 (device key freshly generated)
curl -s -X POST http://127.0.0.1:7001/v1/auth -d '{"mnemonic": "w1 … w12"}'
# restore an anytype-derived account
curl -s -X POST http://127.0.0.1:7001/v1/auth -d '{"mnemonic": "w1 … w12", "index": 0}'
# select a wallet already on disk
curl -s -X POST http://127.0.0.1:7001/v1/auth -d '{"accountId": "A8g1…"}'
{ "accountId": "A8g1…",
"created": true,
"mnemonic": "w1 … w12" }
created reports that a new wallet file was written. mnemonic is present only when the server generated it. index is valid only together with mnemonic; a selected account's index is baked into its wallet.
CLI equivalents:
any auth login # generate
any auth login --mnemonic-stdin # restore
any auth login --account A8g1… # select
any auth status # GET /v1/auth
If the engine fails to boot after a fresh wallet was created in this call, the half-created account dir is removed so a retry starts clean instead of auto-selecting an un-backed account.
| Status | Code | When |
|---|---|---|
| 400 | auth.bad_mnemonic |
phrase fails BIP-39 validation |
| 400 | request.invalid_field |
mnemonic + accountId together, or index without mnemonic |
| 404 | auth.account_not_found |
accountId has no local wallet |
| 409 | auth.account_in_use |
another process holds that account's pid lock |
| 409 | auth.mnemonic_mismatch |
the wallet on disk disagrees with the supplied phrase / index |
| 409 | auth.already_authorized |
the server already booted an account |
| 400 | auth.passkey_required |
encrypted wallet; the passkey comes from the configured env var, never the body |
One server, one account
A process serves exactly one account for its lifetime. To switch, stop it and start with --account <id> (or let POST /v1/auth pick on an unauthorized server). Two accounts at once means two any run processes on different ports; they may share one data-dir root because every account dir carries its own pid lock.
Data dir layout
The data dir is a root that can hold several accounts:
<root>/ # default ~/.any
├── config.yaml
├── models/ # embedder model cache, shared by all accounts
├── wallet.key # legacy flat layout = the DEFAULT account
├── server.pid # (its sdk/ and index/ sit directly at the root)
└── <accountId>/
├── wallet.key # mode 0600
├── server.pid # per-account single-instance lock
├── sdk/ # any-store databases, owned by the SDK
├── files/ # file content, owned by the SDK
└── index/ # local search index
Account selection at boot: auth.walletPath / --wallet → that wallet; account: / ANY_ACCOUNT / --account → <root>/<id>/; otherwise a root wallet.key is the default, else a sole <root>/<id>/ dir, else no account. GET /v1/health reports the booted account in account ("" while unauthorized).
Why it matters. Because the account is a key and the data dir is just files, there is nothing to migrate between hosting providers and nothing a provider can lock: restoring is "type twelve words on a new machine", and every space the account belongs to syncs back down from the network.
See also Devices for what the fresh device key registers, and the operations data-dir page for what is safe to delete.