Memories / Documentation

On this page

Memories Usage Guide

This document explains how to complete the main tasks implemented in the current CLI. Replace VAULT_ID, ASSET_ID, and other uppercase identifiers in the examples with the short IDs actually returned by commands. Passwords are read from an interactive terminal without echoing. There are no password arguments or password environment variables. The application imposes no length limit on local or backup passwords, and empty input is allowed; steps that still require confirmation ask you to enter the password again.

All data commands require a running daemon that uses the same data directory. Global options:

Option Purpose
--home PATH or MEMORIES_HOME Select the data directory. The default is the operating system's application data directory
--config PATH or MEMORIES_CONFIG Select the TOML configuration. Command-line arguments take precedence over environment variables, followed by the configuration file, then built-in defaults
--lang en\|zh\|ja\|de\|fr Change the interface language for this command only

Commands output versioned JSON. Failures produce a nonzero exit code. The confirmation words USE and APPROVE remain in English regardless of the interface language.

Linux can run the CLI, daemon, relay, FFI, and the status page described below. Linux has no native tray. Mobile has no model retrieval interface or background synchronization, and cannot import OTP QR codes with the camera. Hosted device pairing can scan pairing codes; see End-to-end secrets between devices. End-to-end validation with real cloud accounts has not yet been completed. The command line can import HOTP, otpauth://, and otpauth-migration://, and decode OTP QR-code images with --image.

Find the Right Starting Point for Your Task

Task Where to read
Save your first note Startup and initialization, then Writing, reading, and searching
Choose a memory, brain, persona, or file Product concepts
Search documents or get answers with citations Retrieval and Q&A
Save passwords, tokens, keys, or OTP Credentials and protected cards
Move to another device, share with someone, or restore a backup Devices, synchronization, and backups
Connect an AI client MCP and skills
Track work with steps or checklists Workflow cards and checklist templates
Have an organization issue grants centrally Authorization Center
Start using mobile or connect to a self-hosted service or SaaS Choosing and switching mobile modes

If you are unsure about arguments, start with the general help, then descend through subcommands. Help does not require a daemon:

memories --help
memories --lang zh memory --help
memories --lang zh memory put --help

--lang zh changes only the language of help and prompts, not command names or argument syntax. The sh examples in this document use a POSIX shell; a trailing \ continues a multiline command. In PowerShell/CMD, combine it into one line.

Start the Daemon and Initialize a Vault

Start the service in one terminal. It accepts connections only from the current operating system user: Windows uses named pipes; Linux and macOS use Unix sockets.

memories daemon

Open another terminal to initialize the device, create a vault, lock it, and unlock it again:

memories init --name laptop
memories vault create Personal
memories vault list
memories lock
memories unlock
memories status
memories identity

--name is optional; the default device name is local-device. identity displays this device's public identity. Initialization, unlocking, and sudo require entering a password in an interactive terminal. Do not point --home at an existing general-purpose folder; the program tightens that directory's permissions.

The local store here is the data directory selected by --home; initialization establishes the local identity and password protection. A vault is a collection of information within it, partitioned by membership and permissions, and one store can contain multiple vaults. With a custom directory, the service and every client command must use the same --home PATH. Once a store exists, use unlock rather than repeating init. To add a new device to an existing identity, complete Device pairing first; do not create a Personal or "This device" vault beforehand.

After completing the steps above, create a UTF-8 file named notes.md in an editor and write a test note containing the Chinese word for "project" used in the commands below. Replace VAULT_ID in the following example with the id returned by vault create or vault list to save and search your first memory:

memories memory put VAULT_ID --title "项目笔记" --input notes.md
memories memory search VAULT_ID "项目"

The id in the save result is the ASSET_ID used for subsequent reads. Use memory for ordinary notes; card when you need tags, categories, and relations; and file for original PDFs and other files, which do not automatically become searchable text. Use credential for secrets rather than putting them in ordinary notes.

Ordinary unlock permits ordinary data operations within your permissions; it does not open access to private content, files, or credentials. sudo is a local authorization session, not operating system administrator privileges. It uses the same local password, lasts at most 15 minutes, expires after 5 minutes of inactivity, and belongs only to the terminal's parent process. Locking or restarting the service clears it. Ordinary unlocking expires after 15 minutes of inactivity.

View or create configuration containing no secrets:

memories config path
memories config init
memories config init --output ./development.toml
memories --config ./development.toml config show

config init does not overwrite existing files. config show does not open the encrypted store. Configuration includes storage_dir, relay_url, [desktop], and [rag]. Do not put passwords, private keys, OAuth tokens, or MCP grants in this file. Restart the daemon after changing the storage directory or model configuration.

Write, Read, and Search Memories and Cards

Write an ordinary memory. Put its body in a UTF-8 file and supply it with --input:

memories memory put VAULT_ID --title "项目笔记" --input notes.md
memories memory search VAULT_ID "项目"
memories memory read VAULT_ID ASSET_ID
memories memory history VAULT_ID ASSET_ID
memories memory diff VAULT_ID ASSET_ID FROM_REVISION
memories memory diff VAULT_ID ASSET_ID FROM_REVISION --to TO_REVISION
memories memory grep VAULT_ID 'project|release' -i -n

memory grep matches the decrypted bodies of ordinary memories in the selected vault line by line with a regular expression, outputting DOCUMENT_ID:LINE. With -n / --line-number, it outputs DOCUMENT_ID:LINE_NUMBER:LINE; -i / --ignore-case ignores case. Private documents, conflicting revisions, cards, files, and credentials do not participate, consistent with ordinary search excluding private content. If nothing matches, nothing is output.

Both FROM_REVISION and TO_REVISION come from the revision field returned by history, not the id field. The base revision is a positional argument, not --from; omitting --to compares against the current head.

Every creation/update appends a version to the vault's signed revision DAG (ordinary memories, knowledge/brain cards, protected card metadata, files, and credentials). history for memory / card / file lists versions (revision, timestamp, author, whether it is a head, and summary); read --revision reads historical bodies; diff outputs a unified text diff (including titles and card metadata). File diffs omit binary bodies and compare only metadata and length; credential secret bodies cannot be returned through diff/read. Revisions are retained with the vault indefinitely, without automatic pruning.

Add --revision REVISION to read a historical version. Add --id ASSET_ID to replace an existing record. When there are multiple unmerged versions, review the history, then submit a new version pointing to all those parents with --id ASSET_ID --resolve. Delete a currently visible record:

memories memory delete VAULT_ID ASSET_ID

Deletion does not erase signed history. --private marks a record as private. Private records require sudo and are excluded from ordinary search and MCP.

Knowledge cards use the same asset subcommands and can include tags, sources, aliases, notes, categories, and relations. card list (also available as top-level memories list / memories ls) can omit the vault ID to scan all accessible vaults (each result includes vault). --type knowledge|brain|persona|workflow|account|env|token|totp|hotp|otp filters by summary.type (knowledge is an ordinary knowledge card; brain is a structured knowledge card with an abstract and typed links; otp means totp+hotp). --otp is equivalent to --type otp. --category filters by category, and --group-by-category groups by category:

memories card put VAULT_ID --title "一个想法" --input notes.md --tag design --source "会议" --alias "设计备忘" --notes "工作坊" --category design
memories list
memories ls --type knowledge
memories card list --type brain
memories list --type otp
memories ls --otp --category security
memories card list VAULT_ID --category design
memories card list VAULT_ID --group-by-category
memories card brain VAULT_ID --title "Alice" --abstract "连接设计与工程的产品负责人" \
  --link CARD_A=reports-to --link "OTHER_VAULT/CARD_B=collaborates|共同路线图" \
  --alias "Alice" --notes "组织图" --category people
memories card brain VAULT_ID --id BRAIN_ID --title "Alice" --abstract "更新后的摘要" --link CARD_B=partner
memories card link VAULT_ID CARD_A CARD_B --relation supports
memories card unlink VAULT_ID CARD_A CARD_B --relation supports
memories card graph VAULT_ID
memories card graph VAULT_ID --include-private
memories card search VAULT_ID design
memories card list VAULT_ID
memories card read VAULT_ID CARD_ID
memories card delete VAULT_ID CARD_ID

Like memory delete, card delete only removes the card from the current view; signed history retains earlier versions. Deleting private or protected cards requires sudo.

Brain cards store a searchable plain-text abstract (not elevated to a protected secret) and links { vault?, card, role?, note? } that can span vaults. --link uses [VAULT/]CARD[=ROLE][|NOTE]; quote the entire value when it contains | or spaces to prevent the shell from treating it as a pipe. summary.type is brain and includes link_count. Lists/graphs/reads resolve linked titles and types when permissions allow; missing or unauthorized targets are marked broken (without cascading deletion). Search indexes titles, abstracts, link roles/notes, and public target abstracts in the same vault, never secrets. Brain links within the same vault also appear in card graph.

The mobile editor supports handwriting and text recognition from photos/images, all performed on the device. See Handwriting and text recognition.

Workflow Cards and Checklist Templates

Workflow cards have summary.type set to workflow. Ordered steps are stored in card revisions. Each step has a status of pending, active, done, or skipped, and the card also has an overall status. --step takes TITLE or ID:TITLE, optionally ending in =CARD to link a card; repeat it for each step:

memories card workflow VAULT_ID --title Release --step "Write notes" --step Ship
memories card workflow-update VAULT_ID WORKFLOW_ID --advance
memories card workflow-update VAULT_ID WORKFLOW_ID --step STEP_ID --step-status done
memories card list --type workflow

--advance advances the specified step; if none is specified, it advances the current active step, or otherwise the first pending step.

Checklist templates are also workflow cards, with template set to true in their data. When creating a template, --template takes no value and requires --assignee, --summary, and --goal. --assignee can be a persona card ID, persona ID, or any name; --assignee-name is the display name, defaulting to the persona's display name when pointing to a persona card. --template TEMPLATE_ID copies the template's checklist fields and workflow link into a task:

memories card workflow VAULT_ID --template --assignee PERSONA_ID --summary "每周发布检查" --goal "按时发布"
memories card list --type workflow --template
memories card workflow VAULT_ID --template TEMPLATE_ID --workflow WORKFLOW_ID --archive MEMORY_ID
memories card workflow-update VAULT_ID TASK_ID --acceptance "已发布并通知" --status done

--workflow links another workflow card in the same vault, and --archive archives a memory or card and can be repeated. workflow-update options --clear-workflow and --clear-archives remove these two kinds of links respectively. If the task itself has no steps, --advance, --step, --step-status, and --status act on its linked workflow. The acceptance report --acceptance may be empty until the task is complete.

Persona Cards

Persona cards synchronize personality and behavior across products such as Grok Bot / Muse. The persona card in the vault and its linked archives are the authoritative source.

memories persona import VAULT_ID /path/to/xiaoya-persona
memories card list --type persona
memories card read VAULT_ID PERSONA_ID
memories card history VAULT_ID PERSONA_ID
memories card diff VAULT_ID PERSONA_ID REVISION_ID
memories persona append-event VAULT_ID PERSONA_ID --text "行为记录" --kind note
  • Core: How to speak and perform; revised through persona put / persona_card.
  • Archives: Separate knowledge cards for contracts, diaries, outfits, perspectives, and more, linked by the persona's archives; do not copy them back into the core.
  • Behavior events: append-event appends to the events archive (append-only).

World state, things, automation, and chats between personas are also maintained through persona subcommands. Omitting VAULT_ID PERSONA_ID uses persona from Client defaults:

memories persona world-get VAULT_ID PERSONA_ID
memories persona world-set VAULT_ID PERSONA_ID --place "书房" --weather-condition "多云" --weather-temp-c 26
memories persona world-rollover VAULT_ID PERSONA_ID
memories persona thing put VAULT_ID PERSONA_ID --id notebook --name 笔记本 --count 1 --attrs '{"color":"blue"}'
memories persona thing list VAULT_ID PERSONA_ID
memories persona thing attr set VAULT_ID PERSONA_ID --id notebook --path color --value '"red"'
memories persona dice-roll VAULT_ID PERSONA_ID --sides 6
memories persona schedule-list VAULT_ID PERSONA_ID
memories persona automation list VAULT_ID PERSONA_ID
memories persona automation trigger VAULT_ID PERSONA_ID --id morning --source manual
memories persona chat send VAULT_ID PERSONA_A PERSONA_B --text "你好"
memories persona chat history VAULT_ID PERSONA_A --with PERSONA_B --limit 20
  • World state: world-set updates location, posture, outfit, item quantities, weather, dice, wheels, schedules, and more. world-rollover explicitly begins a new ledger day when the date changes, clearing only daily fields such as meals, orders_done, the short look summary, and counters_today; cumulative counts are retained.
  • Things: thing put/get/list/delete/image manages arbitrary things; thing attr get/set/delete reads and writes individual attributes using dotted paths or JSON Pointer. attrs is free-form JSON.
  • Automation: automation put/list/run/trigger/delete manages hooks stored in world state, using the persona's timezone. Outbound webhook_out can send only to URLs that exactly match the allowlist; the default empty list blocks all outbound POSTs. Manage it with automation webhook-allow --add URL / --remove URL. Webhook tokens are not returned in responses; only webhook_token_set is shown.
  • Chat: Messages between two persona cards in the same vault trigger both personas' chat_message automation hooks when sent (the payload contains no message body). chat list shows conversations; reading history does not mark them read; use chat mark-read.

See the English Persona card guide.

You can also repeat --link TARGET_ID=RELATION when creating ordinary cards. These relations must point to cards in the same vault. Graphs omit deleted, conflicting, and unauthorized nodes and edges connected to those nodes. --include-private requires sudo.

Private files:

memories sudo
memories file import VAULT_ID document.pdf
memories file export VAULT_ID FILE_ID exported.pdf
memories file history VAULT_ID FILE_ID
memories file diff VAULT_ID FILE_ID FROM_REVISION

file import accepts --title and --id. file export accepts --revision. file history lists a file's signed revisions; file diff compares only title, metadata, and length, without outputting the binary body.

Use file exec to hand a managed configuration to a local program. {file} in command arguments is replaced with the temporary file's absolute path:

memories sudo
memories file import VAULT_ID ./Caddyfile
# 将返回的文件 ID 填入 FILE_ID;这里使用 Caddy 2 的 Caddyfile 格式。
memories file exec VAULT_ID FILE_ID -- caddy run --config "{file}" --adapter caddyfile

Permissions are the same as file export: unlocking, vault access, and memories sudo in the same terminal are required. Add --revision REVISION before -- to use a historical version. Even when reading from a remote or hosted daemon, the program runs on the CLI's local machine as the current user. It is launched directly without a shell; the working directory, environment variables, standard input/output, and exit code are preserved. There is no five-minute execution limit or JSON success response.

The temporary directory and file are accessible only to the current user (Unix permissions 0700/0600; user ACLs on Windows). The program should run in the foreground: the temporary file remains until the child exits and is also cleaned up if launch fails. Windows Ctrl+C/Ctrl+Break and Unix SIGINT/SIGTERM/SIGHUP trigger exit cleanup, waiting up to ten seconds before terminating the direct child. Unix signals are forwarded to the child; Windows console events are received by processes in the same console.

The configuration is plaintext while the program runs, and the program and its plugins can read and output it; output is not redacted. Locking the store or revoking a grant does not recall delivered copies or stop the program. Forcibly terminating Memories, losing power, or closing the Windows console may leave temporary files behind; deletion is not secure erasure, and background descendant processes detached from the parent are not managed. Each run uses only one fixed snapshot named config, without automatic updates, reloads, template expansion, or import of related files. Relative paths follow the target program's own rules; use absolute paths for related configurations and certificates, and specify the configuration format explicitly when needed.

Use the short IDs returned by lists and searches exactly as given. Long UUIDs, full revision hashes, and prefixed IDs are rejected. Ambiguous input causes the command to fail; list or search again.

Record Python only on the local device:

memories local-python
memories local-python --program /path/to/python

It runs the selected interpreter in isolated mode, records its version and executable directory, and does not collect environment variables. Results are written to the "This device" vault marked local_only.

Retrieval and Answers with Citations

First make the daemon use the same configuration containing [rag]. Importing UTF-8 text or Markdown creates an ordinary knowledge card and returns the estimated chunk count:

memories rag ingest VAULT_ID notes.md --title "研究笔记" --source "本地文件"
memories memory search VAULT_ID "中文 关键词"
memories rag search VAULT_ID "关键词" --mode bm25 --match-mode all

Options for rag search and rag ask:

Option Values
--mode bm25, vector, hybrid. Both search and Q&A default to hybrid
--match-mode any, all, phrase. Default: any
--limit 1 to 100; default: 10
--no-rerank Skip optional result reranking
--cards-only Retrieve knowledge cards only

Install the built-in EmbeddingGemma weights and build an in-memory vector index:

memories rag model-install
memories rag index VAULT_ID
memories rag status VAULT_ID
memories rag search VAULT_ID "这段文档在说什么" --mode vector
memories rag search VAULT_ID "找出证据" --mode hybrid --limit 8
memories rag ask VAULT_ID "根据证据概括"

model-install does not initialize the encrypted store. By default, weights are installed under models/embeddinggemma in the selected data directory. Local inference runs within the Memories process and does not require Ollama. Vector and hybrid retrieval compute missing embeddings on demand even before the index is complete.

rag ask requires a generation endpoint in the configuration. Use --mode bm25 for lexical retrieval only; whenever Q&A is invoked, the generation endpoint is still required. Without evidence, the generation service is not called, and insufficient_evidence is true in the result. [S1] in an answer is only a citation label within that result. To open the original, use the asset id from the result, not the chunk hash as an asset ID:

memories memory read VAULT_ID ASSET_ID

PDFs, DOCX files, and binary files do not have text extracted automatically. Convert them to UTF-8 text before importing. Do not import text that should remain secret as an ordinary card. Restart the daemon after changing model configuration.

Configure remote embedding, generation, and reranking under [rag.embedding], [rag.generation], and [rag.rerank]. For API keys, specify only the environment variable name, such as api_key_env = "GEMINI_API_KEY"; put the key value in the daemon's environment. Omitting [rag.embedding] keeps embeddings on the local model. Gemini Embedding is an online API, not offline EmbeddingGemma.

Capture and Use Credentials and Protected Cards

First run memories sudo in the same terminal.

Add credentials through hidden input or import them from an existing file. Importing does not delete the source file:

memories credential add VAULT_ID "API 令牌"
memories credential import VAULT_ID "SSH 身份" existing-private-key

The capture command memories catch is equivalent to memories credential catch. With no source specified, it prompts for hidden input:

memories catch VAULT_ID "数据库密码"

Add --stdin to read from a pipe. Use --regex to extract a unique match; if there is one capture group, that group is saved, otherwise the entire match is saved. Use --json-pointer to select a JSON string. These options cannot be used together:

secret-tool | memories catch VAULT_ID "数据库密码" --stdin --regex "password=([^\r\n]+)"
memories catch VAULT_ID "API 令牌" --stdin --json-pointer /token < response.json
memories catch VAULT_ID "API 令牌" --exec secret-tool --json-pointer /token -- issue --format json

--exec PROGRAM launches the program directly and captures standard output, closes its child's standard input, and discards standard error. Arguments after -- are passed to the program; do not put real secrets in arguments, regular expressions, or titles. --timeout ranges from 1 to 300 seconds, defaulting to 30 seconds. Piped standard input waits until EOF without a timeout. The input limit is 1 MiB. Successful results contain only the credential ID. Use --id CREDENTIAL_ID to replace an existing credential.

Generate keys inside the vault. Private keys are not printed:

memories credential generate VAULT_ID "笔记本 SSH"
memories credential generate VAULT_ID "笔记本 SSH" --algorithm rsa --bits 4096
memories credential generate VAULT_ID "数据库 CA" --kind tls-ca --algorithm rsa --subject memories-ca
memories credential generate VAULT_ID "数据库服务器" --kind tls --ca CA_ID --algorithm rsa --name db.example.test
memories credential public VAULT_ID KEY_ID

--kind can be ssh, tls-ca, or tls. --algorithm can be ed25519, rsa, or ecdsa. credential public returns only an SSH public key or TLS certificate.

Hand a credential to a program at an absolute path. By default, it is injected through standard input; --env explicitly permits an environment variable. The program's SHA-256 must be confirmed interactively or approved beforehand with --approve-digest. Both child output streams are suppressed:

memories run --vault VAULT_ID --credential CREDENTIAL_ID /absolute/program/path
memories run --vault VAULT_ID --credential CREDENTIAL_ID --env API_TOKEN /absolute/program/path

A run binding pins a credential to a program fingerprint so a coding agent can use it without the sudo session in your terminal. credential bind gh requires sudo and confirmation by entering USE: it finds gh on PATH, records its SHA-256, and stores the binding only in local encrypted state. Bindings do not synchronize and are cleared when restoring backups. A binding expires after --hours hours (default 8, maximum 720) and is usable only while the store is unlocked. Subsequently, run --binding gh -- 参数 launches the program in the current directory, places the credential in the specified environment variable, returns output unchanged except that occurrences of the credential are replaced with [REDACTED] (--suppress-output discards output), and exits with the program's own exit code:

memories credential bind gh --vault VAULT_ID --credential CREDENTIAL_ID --env GH_TOKEN
memories credential bindings
memories credential unbind gh
memories run --binding gh -- pr list --state open
memories hook install --commands gh
memories hook uninstall

--deny 词 rejects commands whose non-option arguments contain those words in order; --ask 词 makes the hook request confirmation before execution. For gh, default rules reject auth, alias, and extension (including aliases extensions, ext) and ask for confirmation for pr merge, repo delete, and release delete; add --no-default-rules to disable them. Rules match only the input words; aliases previously defined with gh alias set are not expanded. Until the binding expires, any program running as you can execute this program with the credential, but cannot read the credential itself through memories. The program, its extensions, and its children can read it, so bind only trusted programs. Bindings without --credential and --env apply rules only.

After showing entries and requiring APPROVE, hook install --commands gh writes a PreToolUse hook to the Claude Code user settings. When Claude Code executes gh ... in Bash or PowerShell, the hook rewrites that command word to memories run --binding gh --, leaving the rest of the command line unchanged. Executing gh inside wrappers such as $( ), backticks, sh -c, or xargs is rejected with an explanation; heredoc bodies and command substitutions used as arguments (such as --body "$(cat <<'EOF' ... EOF)") are unaffected. Piped or redirected input is forwarded through --stdin (at most 16 MiB). The program runs in the daemon's environment, so prefixes such as NAME=value gh ... are rejected; use the program's own arguments instead. Arguments expanded only at runtime ($var, wildcards) cannot be checked against rules, so the hook asks for confirmation first. The hook permits execution directly only when the entire command consists of calls to bound programs; if it also runs other programs or writes files, Claude Code's usual confirmation applies. If background processes left by the bound program keep output pipes open, the number of threads reading those pipes is capped, and new bound runs are rejected once the cap is reached. Claude Code's own deny / ask permission rules still apply, but may see the rewritten command, so put sensitive subcommands in binding rules. Bound programs run outside the service lock, so a long-running gh run watch does not block other clients, but the 5-minute limit remains.

Restrict the hosts a credential can access, and have the daemon make an HTTPS request that returns only the status code:

memories credential allow-host VAULT_ID CREDENTIAL_ID api.example.com
memories credential http VAULT_ID CREDENTIAL_ID https://api.example.com/status

credential http uses HTTPS port 443 only, does not follow redirects, and does not inherit proxies.

Protected cards also read secrets through hidden input, without putting them in arguments:

memories card account VAULT_ID --title "示例邮箱" --email user@example.test --smtp smtp.example.test
memories card env VAULT_ID ./app.env --title "应用配置"
memories card token VAULT_ID --title "API 会话" --host api.example.test
memories card token VAULT_ID --title "浏览器会话" --host app.example.test --cookie
memories card request VAULT_ID TOKEN_CARD_ID https://api.example.test/session
memories card otp VAULT_ID --title "示例 OTP" --issuer Example --account user@example.test
memories card otp VAULT_ID --title "示例 HOTP" --issuer Example --account user@example.test --hotp --counter 0
memories card otp-import VAULT_ID --title "导入的 OTP" "otpauth://totp/Example:user@example.test?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&issuer=Example"
memories card otp-import VAULT_ID --image ./ga-export.png --title "GA import"
memories card otp-code VAULT_ID OTP_CARD_ID

Account SMTP defaults to SSL port 465; adjust it with --port and --no-ssl. This records configuration only and does not send mail. --cookie stores a token card as a Cookie header. card request defaults --method to GET and accepts only GET or POST. OTP supports --algorithm sha1|sha256|sha512, --digits, and --period. --hotp switches to HOTP; --counter is the initial counter, defaulting to 0; TOTP ignores it. otp-import accepts a URI argument; if neither a URI nor --image is provided, it reads one line from standard input. Single accounts use otpauth://. totp or hotp in the URI is detected automatically; without a type, a URI containing counter is HOTP, otherwise TOTP. Google Authenticator migration uses otpauth-migration://offline?data=... and may create several cards at once. --image PATH decodes the first otpauth:// or otpauth-migration:// QR code from an image and cannot be combined with a URI. --title is optional: for one card it is the title, and for several cards it is the title prefix; when omitted, the title is "Issuer / Account". --id can replace only one card and cannot be used for migration imports containing several accounts. otp-code returns a short-lived code, not the seed; HOTP results include the next counter. The secret above is a public RFC test vector; do not replace it with a real seed.

Add --id CARD_ID to replace an existing protected card. Credential IDs and card links are retained; omitted --alias / --notes / --category values are preserved, and an empty string clears them. Ordinary card put cannot replace protected references. OTP titles can retain issuer / account, with aliases and notes separately storing display names.

Protect a secret that occurs exactly once in a document body. The label must contain 1 to 64 ASCII letters, digits, underscores, or hyphens and must not already be used in the document:

memories memory protect-secret VAULT_ID DOCUMENT_ID API_KEY
memories card protect-secret VAULT_ID CARD_ID API_KEY
memories credential copy VAULT_ID DOCUMENT_ID --label API_KEY
memories credential copy VAULT_ID CREDENTIAL_ID

protect-secret prompts for the secret. Copying a document reference requires --label. The copy result contains only "copied": true. Windows uses the system clipboard, macOS uses pbcopy, and Linux requires Wayland's wl-copy or X11's xclip. Protected account, token, environment, and OTP cards can also be copied directly; OTP copying copies the seed, not a one-time code.

Mobile offers "Protected cards", "Import .env", and "Import logins (CSV)", and can import TOTP or HOTP by pasting an otpauth:// URI. "Replace a key in the document" and "Copy key" in the document menu correspond to the operations above. "Generate OTP" produces a one-time code. Mobile cannot import OTP QR codes through the camera; use the command line above for OTP images and migration URIs. Scanning hosted device pairing codes is a separate path; see End-to-end secrets between devices.

Import and Export Website Logins

Website logins are stored in protected login cards (public site address and username, with the password as a separate credential), usually imported and exported in batches. Both import and export require memories sudo and a writable role:

memories sudo
# 浏览器 CSV(Chrome/Edge/Brave,或 Firefox 的 about:logins);自动识别表头。
memories login import VAULT_ID --from chrome passwords.csv
memories login import VAULT_ID --from firefox firefox-logins.csv --with-notes
# 直接读取 Firefox 配置文件(现代 key4.db,会提示输入主密码,没有则留空)。
memories login import VAULT_ID --from firefox-profile "/路径/Profiles/xxxx.default"
# Windows 凭据管理器的通用凭据(仅 Windows)。
memories login import VAULT_ID --from windows --filter github
memories login import VAULT_ID --from windows --all
# 之前导出的加密文件。
memories login import VAULT_ID --from memories logins.mlx

# 导出:便携加密文件(推荐)或浏览器兼容的 CSV。
memories login export VAULT_ID --format encrypted logins.mlx
memories login export VAULT_ID --format csv logins.csv

An entry is skipped if its site host and username match an existing card and the password is the same; a different password is recorded as a conflict and creates another card, unless --update is added to overwrite it. Bad CSV rows are skipped and reported by line number; one import is limited to 2000 entries. A browser's note column is imported only with --with-notes, because notes enter the ordinary full-text index. Direct Firefox reading supports modern key formats (Firefox 58 and later); for older profiles, export CSV from about:logins. Reading browser-saved passwords does not modify the browser's own storage.

Encrypted exports use a separate password and can be imported into any vault. CSV exports are plaintext, for importing logins back into browsers or other password managers: they are available only through the local CLI, warn first, write to a file that must not already exist and is readable only by its owner, and protect cells starting with = + - @ against formula injection. Delete the file after use. Device-only vaults cannot be exported.

Remote Sessions and Loopback Proxies

First authorize the target host with credential allow-host. SSH also requires the host key fingerprint obtained by the probe, verified through a trusted channel:

memories remote probe example.test --port 22
memories remote open ssh --vault VAULT_ID --credential KEY_ID --host example.test --user alice --host-key HOST_KEY
memories remote open ssh --vault VAULT_ID --credential KEY_ID --certificate CERT_ID --host example.test --user alice --host-key HOST_KEY
memories remote sessions
memories remote run SESSION_ID -- uname -a
memories remote propose SESSION_ID -- uname -a
memories remote approve TICKET_ID
memories remote exec TICKET_ID
memories remote close SESSION_ID

remote run performs proposal, interactive approval, and execution in one operation. When a script needs separate steps, use remote propose to queue and return a ticket, remote approve to approve it (requires sudo; tickets are single-use), then remote exec from the same elevated caller to execute it.

Databases enable TLS and hostname verification by default. --tls-ca CA_ID adds a PEM CA from the vault. --no-tls explicitly disables TLS. --database is the MySQL/PostgreSQL database name or Redis logical database index:

memories remote open mysql --vault VAULT_ID --credential CREDENTIAL_ID --host db.example.test --user app --database appdb
memories remote open postgres --vault VAULT_ID --credential CREDENTIAL_ID --tls-ca CA_ID --host db.example.test --user app --database appdb
memories remote open redis --vault VAULT_ID --credential CREDENTIAL_ID --host redis.example.test --user default
memories remote run SESSION_ID --op query -- SELECT 1

remote open supports ssh, ftp, tcp, mysql, redis, and postgres. FTP uses file operations; SSH and database sessions use exec or query. Every instruction must be proposed and approved first. remote run combines proposal, interactive approval, and execution. The approval word is APPROVE. MCP cannot open or approve remote sessions.

Loopback proxies listen on 127.0.0.1. The returned command can be run in a terminal on the daemon's machine and contains no stored passwords or private keys:

memories proxy start ssh --vault VAULT_ID --credential KEY_ID --host example.test --user alice --host-key HOST_KEY
memories proxy start postgres --vault VAULT_ID --credential PASSWORD_ID --host db.example.test --user app --database appdb
memories proxy start mysql --vault VAULT_ID --credential PASSWORD_ID --host db.example.test --user app --database appdb
memories proxy start redis --vault VAULT_ID --credential PASSWORD_ID --host cache.example.test --user default
memories proxy list
memories proxy stop PROXY_ID

Proxy protocols are limited to ssh, mysql, redis, and postgres. Optional --listen-port accepts 0 to let the system choose a port. --certificate, --tls-ca, --tls, and --no-tls are optional. Any process able to connect to that local port can use the authorized remote account. Locking, vault idle locking, proxy stop, and daemon exit close the listener and existing connections. Proxies last at most 30 minutes, with at most four proxies and at most 16 connections each. MCP cannot start, list, or stop proxies.

Run Cryptographic Jobs

Install mail and signing tools separately. Import identities and passwords as two separate credentials. --credential, --password_id, and --key-password are credential IDs, not the secrets themselves. --password and --password-id are aliases of --password_id.

Start with --dry-run. It reads inputs and tools and prints a plan and approval digest, without requiring a daemon or writing output files. After confirmation, run sudo in the same terminal, then run the same command without --dry-run. For noninteractive execution, include --approve-digest DIGEST instead. The output must be a new path in an existing directory.

S/MIME uses OpenSSL. Add --pkcs12 for a PKCS#12 identity. Encryption uses only recipients' public keys, so do not supply an identity credential:

memories crypto smime-sign --vault VAULT_ID --input message.eml --output signed.eml --credential IDENTITY_ID --password_id PASSWORD_ID --pkcs12 --tool openssl=/path/to/openssl --dry-run
memories crypto smime-encrypt --vault VAULT_ID --input signed.eml --output encrypted.eml --recipient recipient.pem --tool openssl=/path/to/openssl --dry-run
memories crypto smime-decrypt --vault VAULT_ID --input encrypted.eml --output decrypted.eml --credential IDENTITY_ID --password_id PASSWORD_ID --pkcs12 --tool openssl=/path/to/openssl --dry-run

OpenPGP requires paired gpg and gpgconf. --identity is the full fingerprint. Add --msys-gpg for the MSYS GnuPG bundled with Git for Windows:

memories crypto pgp-sign --vault VAULT_ID --input message.eml --output message.sig.asc --credential KEY_ID --password_id PASSWORD_ID --identity FULL_FINGERPRINT --tool gpg=/path/to/gpg --tool gpgconf=/path/to/gpgconf --dry-run
memories crypto pgp-encrypt --vault VAULT_ID --input message.eml --output message.eml.asc --recipient recipient.asc --identity FULL_FINGERPRINT --tool gpg=/path/to/gpg --tool gpgconf=/path/to/gpgconf --dry-run
memories crypto pgp-decrypt --vault VAULT_ID --input message.eml.asc --output decrypted.eml --credential KEY_ID --password_id PASSWORD_ID --tool gpg=/path/to/gpg --tool gpgconf=/path/to/gpgconf --dry-run

Android signing does not create packages or contact app stores. The apksigner path is the SDK's lib/apksigner.jar:

memories crypto android-apk --vault VAULT_ID --input unsigned.apk --output signed.apk --credential KEYSTORE_ID --password_id STORE_PASSWORD_ID --identity release --tool java=/jdk/bin/java --tool zipalign=/sdk/build-tools/35.0.0/zipalign --tool apksigner=/sdk/build-tools/35.0.0/lib/apksigner.jar --dry-run
memories crypto android-bundle --vault VAULT_ID --input unsigned.aab --output signed.aab --credential KEYSTORE_ID --password_id STORE_PASSWORD_ID --identity release --tool jarsigner=/jdk/bin/jarsigner --dry-run

Use --key-password KEY_PASSWORD_ID when the key password differs from the keystore password.

iOS signing is available only on macOS. The input is an .app that already contains a matching embedded.mobileprovision:

memories crypto ios-app --vault VAULT_ID --input Unsigned.app --output Signed.app --credential APPLE_P12_ID --password_id PASSWORD_ID --identity CERTIFICATE_SHA1 --entitlements App.entitlements --tool openssl=/path/to/openssl --tool security=/usr/bin/security --tool codesign=/usr/bin/codesign --dry-run

These commands do not send mail or publish applications. MCP and mobile cannot execute them.

Pair Devices, Synchronize, and Back Up

Device pairing adds a new device belonging to the same person to an existing identity; member invitations grant another person a role in a vault. Both use request and approval files, but they are not interchangeable.

The new device must first run init with its own password and must not already have vaults before joining. Then:

memories sudo
memories device invite request.json

Give the request file to an existing device. Approve only after comparing the full invitation fingerprint shown on the requesting device. The approval command returns the approving device's public key; verify this key through a trusted channel before accepting on the new device:

memories sudo
memories device approve request.json approval.json --fingerprint REQUEST_FINGERPRINT

Return to the new device, run sudo again in the same terminal that will execute acceptance, then accept the approval and inspect the device list:

memories sudo
memories device accept approval.json --sender APPROVING_DEVICE_PUBLIC_KEY
memories device list

Requests expire after 10 minutes and are single-use.

Approve another person's membership in a vault:

memories member approve VAULT_ID request.json approval.json --fingerprint REQUEST_FINGERPRINT --role reader
memories member list VAULT_ID
memories member role VAULT_ID USER_PUBLIC_KEY editor
memories member remove VAULT_ID USER_PUBLIC_KEY
memories member transfer VAULT_ID USER_PUBLIC_KEY
memories device revoke VAULT_ID DEVICE_PUBLIC_KEY

Use admin, editor, or reader for invitation approval's --role and the role argument of member role, subject to the operator's permissions: owners manage admins; admins can manage only ordinary readers and editors; editors can write, and readers can read and use. Although the CLI accepts owner, vault policy requires exactly one owner, so invitations or ordinary role changes cannot add an owner. To transfer ownership, the current owner runs memories member transfer VAULT_ID USER_PUBLIC_KEY; the target must be an existing member, and the former owner becomes an admin. Revoke a device in every relevant vault. Removing a member does not erase copies they already hold.

Exchange files without a relay:

memories sync export VAULT_ID RECIPIENT_DEVICE_PUBLIC_KEY packet.json
memories sync import packet.json

For online transfer, first run memories-relay behind a TLS reverse proxy, then generate a room ID with sync room. The receiver waits first, then the sender sends:

memories sync room
memories sync receive --relay wss://relay.example.com --room ROOM
memories sync send VAULT_ID RECIPIENT_DEVICE_PUBLIC_KEY --relay wss://relay.example.com --room ROOM

Remote relays accept only wss://. ws:// is allowed only for local development addresses. URLs must not contain usernames or passwords. relay_url in the configuration can supply the default address; --relay overrides it. --slot defaults to a for sending and b for receiving. Successful sending means only that the packet was handed to the relay, not that the recipient imported it. The relay does not queue for offline devices. Both sync export/import and relay sync send/receive are explicit whole-vault transfers, not cloud object synchronization or remote RPC.

Inspect quarantine, re-sign old-epoch revisions into the current epoch, and retain the local policy branch when both owner identities are valid:

memories sync quarantine VAULT_ID
memories sync reapply VAULT_ID REVISION
memories sync resolve-policy VAULT_ID packet.json --keep-local

Cloud accounts use OAuth applications you register yourself. Unlock the vault and run sudo in the same terminal before logging in:

memories cloud login onedrive --client-id YOUR_MICROSOFT_CLIENT_ID
memories cloud login google-drive --client-config /path/to/google-desktop-client.json
memories cloud status
memories cloud sync onedrive
memories cloud logout onedrive

cloud login supports --no-browser and --port. 0 automatically selects a local callback port. Status does not print tokens. Logout deletes only local authorization. cloud sync refreshes tokens and uploads/downloads immutable ciphertext through the connected OneDrive or Google Drive application folder. Mobile offers cloud connections and foreground synchronization controls. End-to-end validation with real OneDrive and Google Drive accounts has not yet been completed. Mobile background synchronization is not enabled. Do not put the database directory in a cloud drive client's synchronized folder.

Audit and offline backup:

memories audit VAULT_ID --verify
memories sudo
memories audit VAULT_ID
memories backup offline-backup.memories

Restoration requires a new data directory and a daemon started for that directory:

memories --home NEW_DIRECTORY restore offline-backup.memories --name replacement
memories --home NEW_DIRECTORY sudo
memories --home NEW_DIRECTORY archive list
memories --home NEW_DIRECTORY archive history VAULT_ID
memories --home NEW_DIRECTORY archive read VAULT_ID REVISION
memories --home NEW_DIRECTORY archive audit VAULT_ID

Restoration produces independent vaults containing current assets and conflicting heads. Original revisions, tombstones, and signed audits remain in read-only archives. Historical credential values cannot be printed through ordinary or archive interfaces. Device-specific vaults and cloud tokens are excluded from backups.

Open the Desktop Status Interface

In Windows releases, double-click memories-tray.exe alongside memories.exe. On macOS, open the packaged Memories.app. From a terminal:

memories tray
memories webui
memories --home /path/to/development-home webui --no-open --port 0
memories webui --open
memories-tray --home /path/to/development-home --no-open

tray opens a native tray or menu bar with a status page. webui opens only the browser dashboard and remains in the foreground. --port 0 selects a free loopback port. --no-open prevents automatically opening the browser. --open opens it even when automatic opening is disabled in configuration. --config and --lang also apply to both commands and memories-tray.

The tray can show offline, initialization required, locked, or unlocked states and can start the daemon. It does not initialize, unlock, or elevate. The status page refreshes approximately every two seconds, displaying vault counts, proxy and remote session counts, configured cloud provider types, and upload, download, and pending synchronization counts. It does not display bodies, vault names, credentials, accounts, paths, or tokens. Exiting the tray or interrupting foreground webui stops only that status page, not an already-running daemon.

Use memories webui on Linux. Linux has no native tray, and Linux releases contain no tray launcher.

Grant MCP Access and Install AI Skills

This section first describes ordinary MCP: grant an AI client capabilities for one vault, a duration, and an operation scope, without inheriting the terminal's sudo. Grants are written to a file, not printed; the file itself is a bearer credential and must be protected. The default is read-only. --scope read_write permits ordinary data reads and writes; --scope read_write_delete also permits deletion; --write is a compatible shorthand for read_write. Set the duration with --minutes; this example uses 15 minutes:

memories sudo
memories mcp grant VAULT_ID PRIVATE_GRANT_FILE --minutes 15
memories mcp serve --grant-file PRIVATE_GRANT_FILE
memories mcp revoke PRIVATE_GRANT_FILE

Configure the AI client's stdio MCP command as memories mcp serve --grant-file ... and use the same data directory as the daemon. Tools include memory_search, memory_retrieve, memory_ask, memory_read, memory_read_revision, memory_history, memory_diff, memory_write, memory_delete, card_write, card_graph, card_link, workflow_card, workflow_read, workflow_update, and workflow_list, plus persona tools listed in the English Persona card guide. workflow_card creates step workflows, checklist templates, or tasks copied from templates; workflow_update changes checklist fields or advances steps. Both require write permission; workflow_read and workflow_list require only read permission. Each call includes the authorized vault ID. memory_retrieve defaults to local BM25; vector and hybrid use the daemon's configured embedding provider. memory_ask answers when a generation endpoint is configured and rejects citations absent from retrieval evidence.

Ordinary MCP excludes private memories, files, credentials, process execution, remote sessions, proxies, cryptographic jobs, member management, and desktop status. Even --scope allow_all does not cross this boundary. Grant issuance, listing, revocation, and permission requests have separate MCP tools and still do not inherit sudo. Locking temporarily makes these grants unavailable; unexpired grants resume after unlocking. The protocol version is 2024-11-05. Cards with secret references cannot be updated with the current card_write, because it does not preserve reference mappings; edit them with the CLI or mobile instead.

For unattended reading, separately read Unattended CLI/MCP. This path requires explicitly selecting a key custody backend, entering ENABLE, completing password authorization, and creating a vault-scoped client file. Use mcp serve-unattended --client-file PATH, rather than ordinary mcp serve. It is local-only and read-only, but includes private data, original files, history, and explicitly requested credential plaintext, without inheriting sudo. AI clients and their logs may retain secrets; Base64 is not encryption. Manual locking continues to block this access until password-authorized unattended resume; remote transports, mobile, and Center-managed stores cannot use it.

memories unattended enable --backend windows-dpapi
memories unattended mint --vault VAULT_ID --client-file /private/dir/client.json
memories unattended list VAULT_ID --client-file /private/dir/client.json
memories unattended history VAULT_ID ASSET_ID --client-file /private/dir/client.json
memories unattended read VAULT_ID ASSET_ID --client-file /private/dir/client.json
memories unattended credential-bytes VAULT_ID CREDENTIAL_ID --client-file /private/dir/client.json
memories mcp serve-unattended --client-file /private/dir/client.json
memories unattended status
memories unattended resume
memories unattended revoke GRANT_ID
memories unattended disable

Available backends are windows-dpapi, macos-keychain, linux-secret-service, or the weaker owner-file, which must be explicitly selected. The client file's directory must already exist and be accessible only to its owner; mint does not overwrite existing files. read and credential-bytes return byte segments in base64url: start at --offset 0, use the previous response's next as the next --offset, and pin the revision returned first with --revision, until next is null. Then concatenate the decoded bytes in order. list / history return at most 100 entries per page. status, resume, revoke, and disable require an unlocked terminal with sudo authorization.

Install the bundled skill. Installation itself neither opens the vault nor configures MCP:

memories inject all --dry-run
memories inject all
memories inject codex
memories inject opencode claude
memories inject all --project /path/to/project

all includes Codex, OpenCode, and Claude Code. claude-code is an alias of claude. --project installs into an existing project directory. After upgrading the CLI, use --force to replace previously installed content that differs:

memories inject all --force

--force replaces only SKILL.md. Identical content is left unchanged. Links pointing to skill files or directories are rejected.

Generate a project's AGENTS.md explaining how to operate Memories, or a Cursor/Codex-style skill directory, optionally embedding the core summary of a persona card. agents is an alias of skill:

memories skill generate --out AGENTS.md
memories skill generate --out AGENTS.md --persona VAULT/PERSONA
memories agents generate --out ./skills/memories-persona --layout skill --persona VAULT/PERSONA

--layout agents (default) writes a Markdown file; --layout skill writes SKILL.md under the output directory (if the output path already ends in .md, it writes directly to that file). Omitting --persona uses persona from client defaults. Generation with a persona reads the card through the daemon and requires unlocking first; without a persona, generation is offline and needs no daemon. --force replaces existing files whose content differs; --dry-run previews without writing.

Client Defaults

Non-secret command-line defaults are stored in defaults.json under the data directory (--home / MEMORIES_HOME), outside vault ciphertext:

memories default set persona VAULT/PERSONA
memories default set vault VAULT_ID
memories default set type persona
memories default get
memories default get type
memories default delete type
Key Meaning
persona VAULT/ASSET used when persona commands omit the vault and asset
vault Default vault for list / card list and single-argument persona commands
type Default --type filter for list commands
category Default --category filter for list commands
daemon Default --daemon URL
daemon_relay Default --daemon-relay URL
daemon_room Default --daemon-room

Explicit arguments always take precedence. Persona subcommands accepting a vault and asset (world-get, thing, append-event, etc.) use defaults for omitted positional arguments; persona import still requires an explicit vault ID before the skill path.

Authorization Center

Center is an external authorization control plane that distributes enrollment and capability tickets to endpoints. If the configuration lists any [[centers]], that endpoint is Center-managed: local init / restore bootstrapping and local MCP grants are disabled in favor of Center-issued tickets. Vault unlocking still uses the device password locally; Center cannot obtain vault keys or secret plaintext. Listener tokens, Center signing keys, and admin tokens must not be written to TOML.

# 管理员:生成写入配置的公钥并签发票据(或使用 memories-center HTTP 服务)
export MEMORIES_CENTER_SECRET="…"   # 32 字节,URL 安全 base64 或 64 位十六进制
memories center public-key
memories center issue-bootstrap --center corp --device-name laptop
memories center issue-grant --center corp --vault VAULT_ID --scope read --minutes 15

# 终端(守护进程已按含 centers 的配置运行)
memories center list
memories center enroll --ticket "$BOOTSTRAP_TICKET"
memories sudo
memories center import-grant --ticket "$GRANT_TICKET"

For grant scopes (read, write, read_write, read_write_delete, allow_all, and their aliases), configuration format, and the threat model, see the English Authorization Center.

Hosted Vaults (SaaS)

memories-center --data DIR (or environment variable MEMORIES_CENTER_DATA) runs Center as a full hosted vault: the server encrypts and stores the Store with a password held by the operator, reusing the daemon's service interfaces. The sole configuration entry with mode = "saas" puts the local daemon into SaaS mode, where that Center controls content operations (the token comes from MEMORIES_CENTER_TOKEN). Administrators can lock/unlock remotely through /v1/admin/lock, /v1/admin/unlock, and manage members through /v1/admin/members. Hosted data is visible to the operator and is not end-to-end encrypted. Request metadata is stored in DIR/requests.json, and hash-chain audits in DIR/audit.jsonl.

Authorization Chains

Vault roles are Owner, Admin, Editor, and Reader. Only Admin and above can issue or delegate grants; issuance requires only an unlocked vault, without terminal elevation, but approving role requests requires elevation. Grant scopes cannot exceed the issuer's role (or parent grant), grantee roles cannot exceed the issuer's, and center-admin cannot be a grantee. Delegation depth is limited to 2 levels. An omitted scope defaults to read, not allow_all. The local daemon and hosted Center each support at most 10000 active grants simultaneously.

Permission requests can be broadcast to all eligible approvers or directed with --approver. Requests can be approved or rejected, but you cannot decide your own request. Approval returns no token; the requester must claim it once through memories request claim VAULT ID, memories center request claim ID, MCP request_claim, or POST /v1/requests/{id}/claim. Each requester can have at most 20 pending requests per vault, and each vault at most 200 (Center returns HTTP 429). Pending and approved-but-unclaimed requests expire after 7 days; finished requests are retained for 30 days.

memories access list VAULT          # 按级别列出成员、设备、待处理与已批准未领取的请求
memories grant list VAULT
memories request create VAULT --scope read --approver MEMBER_ID
memories request approve VAULT ID
memories request claim VAULT ID

Removing a member cascades revocation to grants they issued (invalidated by member generation). Only grant hashes are persisted; audit logs form a hash chain. See the English Authorization.

Choose a Language

Mobile has language menus on both the lock screen and vault interface. It follows the system by default; manual selections persist across restarts.

CLI precedence is --lang, MEMORIES_LANG, the saved language, then the first nonempty LC_ALL, LC_MESSAGES, or LANG. On Windows, when none of these variables is set, the operating system language is also consulted. Unrecognized locales fall back to English. zh-CN and fr_FR.UTF-8 are recognized.

memories --lang zh --help
memories language zh
memories language
memories language system

Omit the argument after language to display the saved and currently effective languages. system clears the selection and resumes following the system. Saved languages are separate for each --home and do not require a daemon. Command names, option names, JSON fields, error codes, USE, and APPROVE are not translated. User content is also not translated.

Remote Daemon (HTTP/WebSocket / Relay RPC)

The optional token-authenticated HTTP/WebSocket listener and WSS relay RPC use the same WebSocket binary JSON frames (AuthFrame followed by Wire), matching local IPC's service::Request protocol. RPC executes commands against the remote host's store without creating a vault replica on the caller. Use sync to exchange replicas; use cloud sync PROVIDER to synchronize through persistent cloud object queues on OneDrive/Google Drive. The relay is not offline asset storage when carrying either RPC or whole-vault transfers. Listener tokens can be supplied only through flags or environment variables, not TOML. Default plaintext HTTP/WS requires no certificate; enable HTTPS/WSS with --tls-cert / --tls-key when needed. After remote unlocking/elevation, desktop daemons provide the same full operations as local IPC (ws:remote / relay:remote elevation namespaces); mobile Share still follows the FFI denylist. See remote-daemon.md.

The relay can also forward persona automation webhooks: POST /v1/persona/{room}/{vault}/{persona}/automation/trigger. The request body and webhook token are the same as for the daemon. The relay only forwards to a connected host and does not initiate outbound POSTs.

Global TLS options: --ca-file PATH (MEMORIES_CA_FILE) adds a PEM CA for outbound TLS clients; --insecure skips certificate and hostname verification while still using TLS. Use only in development/self-signed environments; prefer --ca-file.

memories daemon --listen http://127.0.0.1:7900 --listen-token "$TOKEN"
export MEMORIES_DAEMON=http://127.0.0.1:7900
export MEMORIES_DAEMON_TOKEN="$TOKEN"
memories status

Choose and Switch Mobile Modes

Choose a mode at first launch; the selection page also appears after upgrading from an older version without a mode setting. The app remembers your choice and enters that mode directly next time.

Mode Data location Getting started
Standalone mode Encrypted local store on the phone, available offline Create a store or unlock an existing one
Center mode Hosted vault on the customer's self-hosted server Enter the full HTTPS service address and member token, then tap "Connect"
SaaS mode Hosted vault at https://memories.ivjn.us The address is fixed; enter that service's member token and connect

Here, Center mode is a self-hosted managed service, and the server must enable memories-center --data DIR. It differs from the authorization Center that only issues device and permission tickets. Mobile does not require editing config.toml, setting mode = "saas", or first unlocking a local vault; these TOML settings are for desktop daemons.

Enter only the server origin for the Center address, such as https://center.example.com; do not add /v1/rpc, query parameters, or usernames/passwords. Non-loopback addresses must use HTTPS; localhost on a phone refers to the phone itself. Member tokens are supplied by the service operator, and permitted operations depend on their vault, role, scope, and expiry. The operator's admin token cannot directly replace a member token to read or write content.

Switching While Using the App

The double-headed arrow at the top of the main screen is "Switch mode", available before and after login. The dialog marks the current mode; selecting another mode switches to it, while canceling retains the current session. Save or exit editing pages first; unsaved content prompts for confirmation.

Action Result
Switch modes Lock the local vault, stop sharing, close the current hosted connection, clear page content, and remember the new mode
Switch back to Standalone Preserve the original store; unlocking is required again
Enter Center or SaaS again Attempt to restore the corresponding connection if saved connection information exists
Disconnect Remove the saved token for the current mode, retain its address, and leave the other mode's connection information unaffected
App enters the background Lock local access, close hosted sessions and sensitive pages, and discard unsaved drafts
Return from the background On hosted pages, tap "Reconnect saved session" or enter the token again to connect

Switching does not upload, move, merge, or delete vault data. Disconnecting does not revoke tokens on the server. Center and SaaS addresses and tokens are stored separately in system secure storage; older connections migrate only to the corresponding mode: the production SaaS address goes to SaaS, and other addresses to Center.

Features Available in Each Mode

Standalone mode retains browsing, searching, editing, backup/restoration, sharing, and cloud connection controls for local data. Center / SaaS provide hosted knowledge lists, search, details, editing, creation, and deletion, plus grant request approval and paired device management. After connecting, you can view the current vault and grant scope and enter "Hosted knowledge" to manage content. New cards do not yet offer tags, sources, or relations; editing existing ordinary cards supports these fields and preserves other metadata. Ordinary memories use the body editor; structured configurations, protected cards, type templates, and end-to-end cards are view-only here and require dedicated editors for changes.

The authorization page has "Pending" and "History" sections. Before approving or rejecting, you can inspect the requester, vault, scope, duration, and full reason, and must confirm; after approval, the requester still claims the grant separately. History includes actions within the current connection and approved requests returned by the server, rather than a complete audit log. If connecting fails, check the network, certificate, address, member token, and whether the self-hosted Center has enabled hosted storage.

Mobile Hosted Mode (Hosted Center)

Search runs on the server and excludes private content, credentials, and files. Clearing the keywords returns the knowledge list; with read permission, private cards in that list can be viewed. Read-only accounts do not show modification controls; write-only accounts can create directly; deletion requires delete permission and another confirmation. Failed saves retain drafts, and details close only after the server confirms deletion. Current saves do not provide atomic concurrency checks based on an expected revision, so simultaneous edits to the same card may overwrite one another's latest fields.

When upgrading the app, also upgrade your self-hosted Center to provide /v1/session. If a session expires or is revoked, disconnect and reconnect with a valid token. Contact the service administrator for insufficient permissions; retry later if the service is unavailable or the vault is locked.

Center and SaaS share FFI memories_call interfaces named hosted_*: connection, request listing and approval, access listing, disconnection, and restricted hosted_rpc. Connections are validated through hosted_status, corresponding to GET /v1/session, returning the current vault, scope, role, expiry, and read/write/delete capabilities without the token. hosted_rpc supports listing, reading, searching, deleting, card types, knowledge card creation, field operations, and credential operations; put supports memory, card, and credential. For the complete allowlist and device interfaces, see Mobile hosted interfaces. Not every FFI operation has a corresponding Flutter page.

Hosted mode is server-readable: the Store is encrypted on disk, but the operator holds the unlock password and can read hosted content; the app displays a "Server-readable" badge. The Rust client keeps active tokens in memory, and Flutter stores each mode's connection information in system secure storage. Sealed secrets between devices have a separate end-to-end protection boundary; see the next section.

End-to-End (E2E) Secret Synchronization Between Devices

Ordinary content on a hosted Center is server-readable. For secrets the server must not see, pair devices and send sealed envelopes (X25519 encryption, signed by the sender with Ed25519); Center stores only public keys and ciphertext.

Pairing process: 1. Computer: memories device hosted init --backend … generates a device identity in system key custody. 2. Computer (member token): memories device hosted pair --vault V makes Center create a permission request with a device fingerprint, returning a one-time pairing code and QR-code contents (including the Center id). The pairing code expires after 5 minutes. The QR code printed in the terminal (stderr) also contains the one-time pairing code and can be scanned with the phone; stdout JSON is unchanged. Re-pairing an active device returns 409; after revocation, the same key can pair again. 3. Phone (administrator): enter/scan the pairing code to obtain the device public key (retrievable only once; excessive failures are rate-limited). 4. The phone approves through /v1/requests/{id}/approve, attaching a device attestation signed with the phone's Ed25519 key. 5. Computer: memories device hosted claim --request-id ID claims the device grant once. The token is stored in system key custody; Center stores only its hash. 6. Compare SAS: the phone shows it before approval (grouped, such as 123 456); after approval, running memories device hosted sas --pairing-id P on the computer should show the same digits. If they differ, revoke the device and pair again.

Phone scanning rejects QR codes that are not Memories pairing codes, whose Center id differs from the current connection, or whose device fingerprint differs. Permissions: Android CAMERA (camera is optional), iOS NSCameraUsageDescription. If camera permission is denied, the phone prompts you to enable it in system settings and allows manual pairing code entry. CLI and mobile convert Center status codes into actionable messages (404 expired/used/another Center, 409 already processed or paired, 403 unauthorized or device revoked, 422 e2e content rejected, 429 too many requests).

SAS definition: interpret the first 8 bytes of SHA-256("memories/device-sas/v1\0" ‖ 手机公钥 ‖ "\0" ‖ 电脑公钥 ‖ "\0" ‖ pairing_id) as a big-endian integer, take it modulo 10^6, and zero-pad to 6 digits. The two public-key operands are the phone public key followed by the computer public key. A mismatch means a public key was replaced; revoke immediately.

Rules: device grants bind the device and member generation, with delegation depth ≤ 2; devices cannot issue grants or approve requests; member changes invalidate devices and delete their envelopes; cards with e2e: true reject plaintext secrets on hosted Center; access lists include devices; MCP provides device_list and sealed_reveal, which requires explicit intent (intent: "reveal_plaintext" + a reason).

Threat model: Center cannot read sealed secrets, but can see metadata and may withhold envelopes. Revocation cannot recall plaintext already delivered to devices; rotate the secret at its source after revocation.

E2E Hardening (P2)

  • The e2e flag cannot be silently removed: once a card is e2e: true, writes removing the flag are rejected (422); only an Owner grant (not a device grant) can clear it with a write containing no secrets.
  • OTP parameters: sealed OTP can be a Base32 seed (SHA1/6 digits/30 seconds) or a full otpauth://totp/... URI; sealed-get --otp follows its algorithm (SHA1/SHA256/SHA512), digits (6/8), and period (30/60). device hosted pair prints the pairing QR code in the terminal (stderr).
  • Re-encryption after revocation: revoking a device deletes its envelopes and marks related cards needs_rotation (visible in sealed lists, /v1/sealed/rotation, access lists, and the phone re-encryption page). Delivered plaintext cannot be recalled: rotate the secret at its source first, then use "Re-encrypt for all devices" on the phone. It sends only to currently approved devices and clears the flag.
  • Phone identity recovery: Center never stores the phone identity. Initial creation displays a one-time recovery code (Base32 of a 32-byte seed + 2-byte checksum, grouped every 4 characters); write it down. "Restore with recovery code" restores the same identity.
  • Identity rotation: "Migrate to a new phone key" generates a new identity and submits a rotation record signed by both old and new keys (only administrator members may submit it; some device must already trust the old key). On first use, the CLI pins the approving phone's public key (hosted-device-trust.json) and accepts only signers reachable from that root through a verified rotation chain.
  • Hardware storage: phone seeds are stored with flutter_secure_storage's strongest built-in options: iOS/macOS Keychain passcode (local-only, no synchronization, device passcode required); Android Keystore wrapping with biometric or device credential authentication, resetOnError: false. Limitation: signing/encryption still brings the seed into application memory (Secure Enclave/StrongBox do not support X25519/Ed25519); this protects only storage at rest.
  • Transparency log: Center maintains an append-only, hash-linked device log (pairing, approval, identity rotation, revocation), GET /v1/devices/log. memories device hosted log --verify verifies the chain, all signatures, and continuation from the previously verified chain head (hosted-device-log.json).

Mobile Biometrics and Device Lock

After unlocking the store in Standalone mode, you can enable "Unlock with biometrics/device lock" in the More menu. Enabling it requires a system authentication first; this security switch is also used for sensitive operations such as hosted approvals.

  • Android: uses the system BiometricPrompt (fingerprint/face), with fallback to the device PIN, pattern, or password. The store password needed for quick unlock is protected under an Android Keystore-bound key requiring user authentication (setUserAuthenticationRequired) and automatically invalidated when fingerprints/faces are added or removed (setInvalidatedByBiometricEnrollment(true)).
  • iOS: uses Face ID/Touch ID (with fallback to the device passcode); Keychain entries are available only when a device passcode is set.
  • Protected operations: unlocking the store, displaying or copying credential plaintext, using protected cards/generating OTP, approving paired devices, approving hosted requests, sealing, and re-sealing.
  • Biometric changes: the app explicitly reports key invalidation without deleting the store or data. After authenticating with device credentials, enter the store password once to recreate the key.
  • No biometrics or switch disabled: if the device has no available biometrics/device lock, or the switch is off, the existing password flow is used. Disabling the switch deletes saved quick-unlock information.

Create Content (Mobile)

New memories, credentials, and hosted Center entries share one editor: import from a file (the filename becomes the default title and can be changed); configuration content such as .env automatically switches to monospaced multiline editing (also switchable manually); the name may initially be blank, and saving infers it from the filename or the first nonempty line of text, prompting only if it remains empty.

Knowledge cards use a full-screen form with a fixed Save button at the bottom. "Edit body" opens the shared body editor for file import, handwriting, or scanning. Memories and knowledge cards close only after successful saving; failures retain drafts for retry and prevent duplicate submissions. Importing over an existing body and leaving unsaved content both require confirmation. Drafts are kept only in memory and are not restored after background locking. Editing Standalone cards and hosted ordinary cards preserves tags, sources, relations, and custom fields; hosted creation offers only metadata accepted by the current creation interface.

Multi-Field Credentials

A credential can simultaneously store multiple independent fields, all of which may be empty: username/account, password, OTP (otpauth:// URI or base32 seed and parameters), API Key, URL, notes, and custom fields (which can be marked secret). Passwords, OTP, API Key, and secret custom fields are stored only in the encrypted body; usernames, URLs, notes, and non-secret custom fields can be viewed directly.

memories credential set VAULT [ASSET] --username 我 --url https://example.com --password --otp --field 团队=core --secret-field PIN
memories credential show VAULT ASSET
memories credential get VAULT ASSET --field username
memories credential get VAULT ASSET --field password --reveal
memories credential copy VAULT ASSET --field api_key
memories credential otp VAULT ASSET
memories credential migrate VAULT ASSET

Secret values are entered only through hidden input, never as command-line arguments. Old single-field credentials can still be read directly: otpauth:// or titles starting with OTP: map to OTP; those with HTTP authentication or allowed hosts map to API Key; others map to password. The first modification through credential set (or running credential migrate) rewrites them into structured format. Old versions remain in history, without data loss. Existing features using one secret (HTTP injection, run bindings, copying without --field) use the "primary secret": password first, then API Key, OTP, and the first secret custom field.

Multi-Field Credentials: MCP, Hosted Center, and End-to-End Synchronization

  • MCP credential_fields (read permission) returns only non-secret fields and secret field names; credential_reveal (allow_all grant) requires intent: "reveal_plaintext" and a reason, returning and auditing each field separately.
  • Hosted Center: end-to-end (e2e) credentials store only descriptive information at Center; secret fields exist only in device sealed envelopes.
  • Paired devices: memories device sealed-get --vault V --card C --field password --reveal; --otp generates a verification code.
  • Mobile: new credentials use a sectioned form (login information, one-time codes, API, More, custom fields), with all fields optional. Copying secret fields and viewing live codes (with a countdown) require system unlock authentication.