Memories / Documentation

On this page

Memories

Repository: https://github.com/mbcc2006/memories. The CLI is memories, the relay is memories-relay, and the native library is memories_ffi.

Client-owned encrypted memory, private files, and credentials, with a Rust CLI, Android/iOS client, local MCP server, and a memory-only WebSocket relay.

Development release, format version 1. This repository includes executable code and security regression tests. It has not undergone an independent cryptographic or application security review. See SECURITY.md for the guarantees, trust boundaries, and current limits.

How it fits together

In standalone use, Memories keeps an encrypted local store on each device. Use it to save searchable knowledge, carry an AI persona between clients, or use private files and secrets through deliberate authorization. Cloud storage holds ciphertext; the relay only connects online peers. Neither replaces an authorized client.

The mobile app also offers Center (your self-hosted server) and SaaS (https://memories.ivjn.us) modes. Those modes use a separate hosted store whose operator can read its content. Choosing or switching modes does not upload, move or merge an existing local library. See mobile modes.

Concept Use it for
Store (--home) This device's data directory, local password and identity. One store can contain several vaults; daemon and clients must select the same store.
Vault A collection with its own membership, roles, device permissions and signed history. Share a vault, not the live store directory.
Memory A text document such as a note or journal entry.
Knowledge card Wiki-style knowledge with tags, sources, category and relationships.
Brain card Structured knowledge with an abstract and typed links, including cross-vault references. It isn't a model or inference engine.
Persona card An identity core linked to archives, world state and behavior events. See personas.
Workflow card Ordered steps with a pending/active/done/skipped status and an overall status, stored as a normal card revision.
File Original binary content to import/export, not automatically extracted searchable text.
Credential A protected secret used through authorized operations, not ordinary text to search or print. Protected cards pair safe metadata with a separate credential.

unlock enables ordinary authorized work in the store. memories sudo adds a short-lived session for private content, files, credentials and sensitive management; it doesn't grant OS administrator rights or bypass vault permissions. An ordinary vault-scoped MCP grant is a separate, limited AI capability, not the terminal's sudo authority. Unattended access is a separate opt-in read-only capability that can expose private content and secret values.

Start here

Task Guide Current support
Save and read your first note Local quick start Initialize a store, create a vault, write and search text
Start on a phone or switch its storage mode Mobile modes Standalone, self-hosted Center, or SaaS; remembered selection and separate connection profiles
Give an AI client scoped knowledge access Local MCP Vault-scoped grants; private content and credentials excluded
Check desktop service status Tray and WebUI Windows tray, macOS menu bar, read-only status and restricted card editing
Configure storage and relay defaults Configuration Shared TOML settings on Windows, Linux and macOS
Search and ask questions over documents Retrieval and RAG Chinese BM25, chunks, local EmbeddingGemma, vector/hybrid search and cited answers
Sign/encrypt mail or sign an app Cryptographic jobs S/MIME, OpenPGP, APK/AAB; provisioned iOS .app on macOS
Add the Memories skill to an AI client Skill injection Codex, OpenCode and Claude Code; user or project scope
Allow unattended desktop reads, including secrets Unattended CLI/MCP Opt-in local read-only authority; Linux end-to-end tested, native Windows/macOS and live keyring checks pending
Generate AGENTS.md / persona skill Skill generation CLI skill generate / agents generate; optional persona
Set client CLI defaults Client defaults persona, vault, type, … under Memories home
Capture a password or command output Credential capture Hidden input, stdin, command stdout, regex and JSON selection
Protect or copy a secret in a wiki document Wiki secret references Exact body replacement, encrypted credentials and authorized clipboard copies
Connect OneDrive or Google Drive Cloud authorization Desktop login, encrypted local tokens, status and logout
Transfer vaults between devices Device pairing Deliberate pairing and encrypted vault transfer
Synchronize ciphertext through cloud storage Storage design Durable outbox/inbox, memories cloud sync, OneDrive/Google Drive adapters; live-account verification pending

For development, see Repository Guidelines, architecture, and validation results.

简体中文:产品说明、使用说明、技术文档。

Marketing site / 项目介绍网站:website/, static EN/中文 site with Cloudflare Pages deployment instructions.

Build and run

Install Rust 1.88 or newer and the platform C toolchain (SQLite is compiled from source).

cargo build --workspace --locked
cargo test --workspace --locked
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo install --path crates/cli --locked

Start memories daemon in a separate terminal. It serves only the current OS user over a named pipe on Windows or a Unix socket on Linux/macOS. Use a dedicated data directory with --home PATH or MEMORIES_HOME; the default is the OS application data directory. Do not point --home at an existing general-purpose folder: its permissions are restricted to protect the local service.

Local quick start

With memories installed and the daemon running, use a second interactive terminal. For a fresh standalone store:

memories init --name laptop
memories vault create Personal
memories vault list

Create a UTF-8 file named notes.md with a short note containing "project". Replace VAULT_ID below with the id returned by vault create or vault list, then run:

memories memory put VAULT_ID --title "Project notes" --input notes.md
memories memory search VAULT_ID "project"
memories memory grep VAULT_ID 'project|release' -i -n

memory grep matches a regular expression against each decrypted body line in ordinary memory documents in the selected vault. It prints DOCUMENT_ID:LINE, or DOCUMENT_ID:LINE_NUMBER:LINE with -n / --line-number; -i / --ignore-case ignores case. Private documents, conflicting versions, cards, files and credentials are excluded, like private content in ordinary search. No matches produce no output.

Replace ASSET_ID with the saved note's id. For FROM_REVISION, use the revision field returned by history; the diff compares that revision with the current head:

memories memory read VAULT_ID ASSET_ID
memories memory history VAULT_ID ASSET_ID
memories memory diff VAULT_ID ASSET_ID FROM_REVISION
memories lock
memories unlock

On later visits, use unlock, not init. If this is a new device joining an existing identity, pair it before creating any vaults. For a custom store, pass the same --home PATH to the daemon and every client command.

Discover tasks with memories --help, then drill down with memories memory --help or memories memory put --help. Add --lang zh for Chinese help, for example memories --lang zh memory put --help. Help doesn't require a daemon.

Passwords are read from an interactive terminal without echo. There is no password argument or password environment variable. Local and backup passwords have no application-level length restriction, including empty input; password confirmation and authentication still apply. Commands emit versioned JSON; failed commands return a nonzero exit status. Public device keys and fingerprints are intentionally visible; private keys and credential values are not printed by ordinary commands. Deliberate unattended credential reads are an exception described in Unattended CLI/MCP.

Remote daemon (HTTP/WebSocket / relay RPC)

Optional token-authenticated HTTP/WebSocket listen and WSS relay RPC carry the same framed service::Request protocol as local IPC (full-featured on desktop after unlock/sudo; mobile Share keeps the FFI denylist). Use memories daemon --listen http://127.0.0.1:7900 (optional --tls-cert / --tls-key for HTTPS/WSS). RPC runs operations on the host's store; it doesn't synchronize a local replica. See remote daemon for listen/client flags, residual risks, and mobile Share.

Optional authorization Centers can forbid local self-bootstrap and distribute enrollment/MCP grant tickets to endpoints. memories-center --data DIR additionally runs a full hosted vault: daemons with a mode = "saas" Center entry are controlled by it, the operator can unlock it remotely, and members are managed on the Center.

Authorization chain

Vault roles are Owner, Admin, Editor and Reader. Only Admin or Owner may issue or delegate grants; issuing needs an unlocked vault but no terminal elevation, while approving a role request does need elevation. A grant is capped by the issuer's role (and any parent grant), the grantee may not hold a higher role, and center-admin cannot be a grantee. Delegation depth is at most 2. Omitted scope means read, not allow_all. Permission requests go to all eligible approvers or to specific --approver members; nobody decides their own request. Approval returns no token: the requester claims it once (memories request claim, memories center request claim, MCP request_claim, or POST /v1/requests/{id}/claim). Limits: 20 pending requests per requester per vault, 200 per vault (HTTP 429 on Center), 7-day pending TTL, 30-day retention, and at most 10,000 active grants on both the daemon and a hosted Center. memories access list VAULT shows members by tier, devices, pending and approved-unclaimed requests; memories grant list VAULT lists grants. Removing a member cascades revocation of their grants (per-member generation); grants are persisted as hashes only, and the audit log is hash-chained (Center: DIR/audit.jsonl). See authorization.

Persona chat (memories persona chat send|list|history|mark-read) fires chat_message automation hooks; inbound automation webhooks can also be reached through memories-relay at POST /v1/persona/{room}/{vault}/{persona}/automation/trigger (see persona, remote daemon).

Linux build and package

On Debian/Ubuntu (including Ubuntu in WSL2), install Rust 1.88+ and the native build dependencies:

sudo apt-get update
sudo apt-get install -y build-essential pkg-config libssl-dev python3 git
bash scripts/build-linux.sh
# Optional development build:
bash scripts/build-linux.sh --debug

The script builds the Rust workspace for the host GNU target: x86_64 or aarch64. Release outputs are in target/<host-target>/release/ (or the configured Cargo target directory); debug outputs use debug/. No Flutter SDK is required. Linux supports the CLI, daemon, relay, FFI library and memories webui; the native tray is currently available on Windows/macOS only.

From a clean, committed checkout, create a Linux release archive:

bash scripts/package-linux.sh

This rebuilds the release binaries and writes a .tar.gz, BUILD-INFO.json, SHA256SUMS and example configuration to dist/vVERSION-linux-ARCH/. The archive includes memories, memories-relay, libmemories_ffi.so, the C header, documentation and licenses, with executable permissions preserved. Existing output directories are not overwritten. The manual Build desktop packages workflow also builds and uploads a Linux x86_64 package.

These are native GNU/Linux builds, not static or cross-compiled binaries. Use a compatible glibc/OpenSSL runtime; build on the oldest distribution you intend to support. ca-certificates is needed for system TLS and xdg-utils for opening the dashboard in a desktop browser. Credential clipboard copy additionally needs wl-clipboard (Wayland) or xclip (X11). Model weights are installed separately with memories rag model-install.

Configuration

Run memories config init to create a TOML configuration, memories config path to find it, and memories config show to inspect effective settings. The release includes memories.example.toml with storage-directory, relay and desktop options. Select another file with --config PATH or MEMORIES_CONFIG. CLI options override environment variables, then configuration, then defaults. See the configuration guide.

Desktop tray and status dashboard

On Windows, double-click memories-tray.exe in the extracted release directory. On macOS, open the packaged Memories.app, or use the command line:

memories tray
memories webui

The tray/menu bar shows the daemon and vault state, opens the local WebUI, and can start the daemon. The dashboard reports lock state, library count, connection counts and cloud synchronization queues without displaying stored content. --home selects the same store as the CLI; --no-open keeps the browser closed at startup. Initialize/unlock through the CLI as usual. See the desktop guide for behavior and packaging.

Languages

The mobile client and CLI support English (en), Simplified Chinese (zh), Japanese (ja), German (de), and French (fr). The mobile language menu is available on the lock screen and in the library. It follows the system by default and remembers a manual choice across restarts.

memories --lang zh --help
memories cloud login --lang ja --help
memories --lang de status
memories language zh       # Remember this language
memories language          # Show saved and effective language
memories language system   # Follow the system again

CLI language selection uses --lang, then MEMORIES_LANG, then a saved language preference, then the first nonempty LC_ALL, LC_MESSAGES, or LANG. The preference is stored per --home / MEMORIES_HOME and works without a daemon. On Windows, it also uses the OS language when none of those variables is set. Unsupported locales fall back to English; environment values such as fr_FR.UTF-8 and zh-CN are accepted. Commands, option names, JSON fields, error codes, and the confirmation words USE and APPROVE remain stable. User content is not translated. Low-level diagnostics without a translation retain their original detail with a localized error summary. See the localization guide for maintenance.

Short IDs

CLI, MCP, and mobile requests use plain short hexadecimal IDs, normally six characters (for example, 0b0668). IDs carry no type or classification meaning and have no prefix or underscore. Use the IDs returned by vault list, card list, card search, or history directly in the corresponding command. Long UUIDs, full revision hashes, and prefixed IDs are rejected; there is no long-ID output option.

Existing eight-character IDs remain valid. Colliding prefixes automatically extend in two-character increments up to 16 characters; ambiguous input is rejected. This does not shorten device keys or security fingerprints.

memories card list eb7abf
memories card search eb7abf "claude relay"
memories card read eb7abf 0b0668

IDs resolve in the scope of each command argument. A collision extends the returned ID by two characters, up to 16; ambiguous input fails instead of selecting an object. List or search again after a collision. Deleted assets remain in the collision index. Short IDs are references, not authorization: unlock, sudo, vault permissions, and MCP grants still apply.

Signed storage records retain their original identifiers without migration. Certificates, public keys, verification fingerprints, audit records, and encrypted transport envelopes are not shortened or rewritten.

Retrieval and RAG

Ordinary search now tokenizes Chinese and supports multiple keywords. Its BM25 index is cached in memory, invalidated on content/policy changes, and cleared when locked. Advanced retrieval adds chunk offsets, vector scores, hybrid rank fusion, lightweight reranking and optional learned reranking through a configured URL. Credentials and private content remain excluded.

memories rag ingest VAULT_ID notes.md --title "Research notes"
memories rag search VAULT_ID "Chinese and English keywords" --mode bm25
memories rag model-install
memories rag index VAULT_ID
memories rag search VAULT_ID "What does this document mean?" --mode hybrid
memories rag ask VAULT_ID "Summarize the evidence"

The built-in CPU inference engine runs EmbeddingGemma in the Memories process; Ollama or another inference application is not required. Model files are public data installed once by model-install, or supplied in rag.local_model_dir. Gemini Embedding is a separate online API. Configure customer-selected URLs for remote embeddings, generation and optional reranking. HTTP(S)/SOCKS proxies, timeouts and bounded retries are configurable. See the RAG guide.

Private files and credentials

sudo is an internal authorization session, not OS administrator access. It uses the same local password. Elevation lasts at most 15 minutes, expires after five idle minutes, and is scoped to the terminal's parent process. Locking or restarting the service clears it. Ordinary unlock expires after 15 idle minutes.

memories sudo
memories file import VAULT_ID document.pdf
memories file export VAULT_ID FILE_ID exported.pdf
memories file exec VAULT_ID FILE_ID -- caddy run --config "{file}" --adapter caddyfile
memories credential add VAULT_ID "API token"
memories credential import VAULT_ID "SSH identity" existing-private-key
memories credential generate VAULT_ID "laptop ssh"
memories credential generate VAULT_ID "laptop ssh" --algorithm rsa --bits 4096
memories credential generate VAULT_ID "db ca" --kind tls-ca --algorithm rsa --subject memories-ca
memories credential generate VAULT_ID "db server" --kind tls --ca CA_ID --algorithm rsa --name db.example.test
memories credential public VAULT_ID KEY_ID
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
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
memories credential allow-host VAULT_ID CREDENTIAL_ID api.example.com
memories credential http VAULT_ID CREDENTIAL_ID https://api.example.com/status
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 open mysql --vault VAULT_ID --credential CREDENTIAL_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 open postgres --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 mysql --vault VAULT_ID --credential CREDENTIAL_ID --host db.example.test --user app --database appdb --no-tls
memories remote run SESSION_ID -- uname -a
memories remote run SESSION_ID --op query -- SELECT 1
memories remote propose SESSION_ID -- uname -a
memories remote approve TICKET_ID
memories remote exec TICKET_ID
memories remote close SESSION_ID
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 CREDENTIAL_ID --host db.example.test --user app --database appdb
memories proxy list
memories proxy stop PROXY_ID

Run a program with a stored configuration file

memories file exec VAULT_ID FILE_ID -- PROGRAM ARGS... reads an authorized file and replaces every {file} in the program's arguments with an absolute temporary file path. Use --revision REVISION before -- to select a historical version. It requires the same unlock, vault access and terminal memories sudo as file export. A remote/hosted daemon can supply the file; the program always runs on the CLI machine, under the CLI user's account.

memories sudo
memories file import VAULT_ID ./Caddyfile
# Use the returned file ID. Caddy 2 Caddyfile syntax needs an explicit adapter.
memories file exec VAULT_ID FILE_ID -- caddy run --config "{file}" --adapter caddyfile

The child inherits the working directory, environment and standard streams; its exit code is returned without a JSON success envelope. No shell is inserted and there is no five-minute execution limit. Run the program in the foreground. The temporary directory/file are owner-only (Unix 0700/0600; Windows user ACL), and are removed after the child exits or fails to start. Ctrl+C/Ctrl+Break on Windows and SIGINT/SIGTERM/SIGHUP on Unix trigger shutdown, allowing ten seconds before killing the direct child and cleaning up. Unix signals are forwarded to the child; Windows console events reach processes sharing that console.

This exports plaintext for the lifetime of the child. Trust the program and its plugins: its output is not redacted. Lock/revocation cannot recall the delivered copy or stop the child. Forced termination of Memories, power loss, or a Windows console close may leave the temporary file; deletion is not secure erasure. Detached descendants are not managed. Each invocation uses one fixed file snapshot named config; no automatic updates, reloads, template expansion or related-file imports occur. Relative paths follow the target program's rules, so use absolute paths for external includes/certificates and specify the format explicitly when a program otherwise infers it from the filename.

Credential process injection

The credential memories run process injection uses stdin; --env deliberately permits an environment variable. The program's SHA-256 fingerprint must be confirmed or supplied through --approve-digest. Both child output streams are suppressed. The receiving program can nevertheless copy or transmit the credential and must be trusted. A run binding pins one credential to one program fingerprint so a coding agent can use it without your terminal's sudo session. credential bind gh (sudo, typed USE) resolves gh on PATH, records its SHA-256 and stores the binding in this device's encrypted state only; it is never synced and is cleared on restore. It expires after --hours (default 8, at most 720) and works only while the library is unlocked. run --binding gh -- ARGS then runs the program in the current directory with the credential in the chosen environment variable, mirrors its output with every occurrence of the credential replaced by [REDACTED] (--suppress-output discards it instead), and exits with the program's code. --deny WORDS refuses commands whose non-flag arguments contain those words in order; --ask WORDS makes the hook ask before running them. For gh, auth, alias and extension (also extensions and ext) are denied and pr merge, repo delete and release delete ask, unless --no-default-rules. Rules match the words as typed; aliases you defined earlier with gh alias set are not expanded. Until the binding expires, any program running as you can run the bound program with the credential; it cannot read the credential through Memories. The program itself, its extensions and its child processes can, so bind only programs you trust. A binding without --credential and --env applies only the rules.

hook install --commands gh adds a PreToolUse hook to Claude Code's user settings (after showing the entry and asking for APPROVE). When Claude Code is about to run gh ... in Bash or PowerShell, the hook rewrites that command word to memories run --binding gh -- and leaves the rest of the command line unchanged. gh run inside $( ), backticks, sh -c, xargs and similar wrappers is denied with a reason; heredoc bodies and substitutions used as arguments (such as --body "$(cat <<'EOF' ... EOF)") are fine. Piped or redirected input is forwarded with --stdin (up to 16 MiB). The program runs in the daemon's environment, so a NAME=value gh ... prefix is refused; use the program's flags instead. Arguments the shell expands at run time ($var, globs) can't be checked against rules, so the hook asks before running them. The hook approves a command outright only when it consists of bound runs alone; if it also runs other programs or writes files, Claude Code's usual confirmation applies. Workers left reading pipes held by a bound program's lingering descendants are capped, and further bound runs are refused while too many remain. Claude Code's own deny and ask permission rules still apply, but they may see the rewritten command, so keep sensitive subcommands in the binding's rules. Bound programs run outside the service lock, so a long gh run watch does not block other clients; the five-minute limit still applies.

The HTTPS interface sends a Bearer token only to a configured host over HTTPS/443, does not follow redirects or inherit proxy settings, and returns only the status code to avoid reflecting a credential in a response body. Daemon-owned SSH, FTP, TCP, MySQL, Redis, and PostgreSQL sessions keep secrets in the service. Each instruction is proposed, approved with APPROVE, then executed once on the live session. remote run does all three steps; scripts can split them with remote propose (queues a ticket), remote approve (sudo, single use) and remote exec (same elevated caller). SSH accepts a password credential or an imported OpenSSH private key (credential import) or a key generated inside the vault (credential generate). Generate never prints the private key; credential public returns only the SSH public key or TLS certificate. Optionally attach a second OpenSSH certificate credential (--certificate). SSH also requires a pinned host-key fingerprint from remote probe. MySQL, Redis, and PostgreSQL sessions use TLS by default with hostname verification. Redis and PostgreSQL use rustls with webpki roots; MySQL uses the platform TLS library. --tls-ca adds a stored PEM CA from a vault credential; it remains distinct from global --ca-file PATH (MEMORIES_CA_FILE), which adds a filesystem PEM CA bundle for daemon/relay WSS and outbound HTTPS/database clients. Set these options on the process making the connection (start the daemon with them for daemon-owned upstream operations). Global --insecure skips certificate and hostname verification without disabling TLS: dangerous, development/self-signed only; prefer --ca-file. Server-side --tls-cert / --tls-key are unchanged. See client TLS examples. Pass --no-tls to disable database TLS. MCP cannot use this path. File operations use FTP; approved SSH sessions accept exec only.

Protect secrets in wiki documents

In the app, open a memory/card menu and choose Protect secret in document (Chinese: 替换资料中的密钥). Name the reference (for example API_KEY), then enter the exact secret using hidden input and authorize the operation. All exact matches in that document body become {{secret:API_KEY}}; the value is stored as an encrypted credential. Ordinary reads, search and MCP see the reference only.

Choose Copy secret (复制密钥) from a document or credential menu. For a document with several references, select the label first. Confirm and authorize to copy its current value to the system clipboard without displaying it.

The CLI provides the same operations (use short IDs from list or search):

memories sudo
memories memory protect-secret VAULT_ID DOCUMENT_ID API_KEY
# For a knowledge card, use: 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

The replacement command prompts for the secret; do not put secrets in arguments or reference names. Labels accept 1-64 ASCII letters, digits, underscores or hyphens and must be unused in that document. Copying a document reference requires --label, even for a single reference. Copy returns only copy status ("copied": true). Windows uses the native clipboard, macOS uses pbcopy, and Linux requires wl-copy (Wayland) or xclip (X11). Protected account/token/env/OTP cards can also be copied directly; OTP copies return the seed, not a one-time code.

Replacement affects the current document body only. Old signed versions, backups, other documents and shared copies retain their previous content; revoke or rotate exposed keys with their provider. Clipboard contents remain after locking and may be visible to other apps or OS clipboard history. Clear them after use. This feature does not automatically discover secrets or rotate keys. Update all clients before syncing documents that contain secret references. See the complete guide for rejection conditions, editing and credential updates, platform requirements and clipboard troubleshooting.

Passwordless SSH and database proxies

memories proxy start opens a 127.0.0.1 listener after terminal sudo and credential host-allowlist checks. It returns an id, a listen address and a ready-to-use command. Run that command in the agent's terminal on the daemon's machine; no stored password or private key is returned. The upstream account, host and initial database are selected by the proxy's start arguments. A proxy authorizes commands for that account without per-command approval.

memories sudo
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

Use credential allow-host VAULT_ID CREDENTIAL_ID HOSTNAME to authorize a credential's destination first. Verify an SSH fingerprint obtained with remote probe through a trusted channel before supplying --host-key. SSH proxies forward exec, shell, PTY and subsystem requests; upstream host keys remain pinned. Local SSH host keys are ephemeral, so the returned command disables host-key persistence only for the local endpoint.

Database upstreams use verified TLS by default, with optional --tls-ca CA_ID. The local database hop is plaintext loopback. PostgreSQL supports SCRAM-SHA-256, MD5 and password authentication. MySQL supports mysql_native_password and caching_sha2_password; full SHA-256 password authentication requires TLS. PostgreSQL cancellation/startup-option forwarding and MySQL compression are not implemented. Local login names and initial database requests do not override the configured upstream account/database.

Any process that can reach the local port can use its authorized remote account. Keep the port local. Lock, automatic vault idle lock, proxy stop, and daemon shutdown close listeners and existing connections. Proxies also expire after 30 minutes even when used, with at most four proxies and 16 connections per proxy. MCP cannot start, list or stop proxies; an agent uses its ordinary terminal client after the user starts one.

Capture passwords and command output

memories catch (also memories credential catch) captures a UTF-8 secret and stores it as an encrypted credential. Run memories sudo in the same terminal first. With no source flag it prompts for hidden input, like credential add:

memories sudo
memories catch VAULT_ID "Database password"

Use --stdin for a pipe or redirected file. These examples work in Windows CMD; secret-tool represents your own program that emits the value:

secret-tool.exe | memories catch VAULT_ID "Database password" --stdin --regex "password=([^\r\n]+)"
memories catch VAULT_ID "API token" --stdin --json-pointer /token < response.json
memories catch VAULT_ID "API token" --exec secret-tool.exe --json-pointer /token -- issue --format json

--exec PROGRAM -- ARGS... starts the specified program directly and captures stdout. It closes the child's stdin and suppresses stderr. For a CMD script use --exec cmd.exe -- /d /c script.cmd; shell operators require an deliberately selected shell. Interactive external programs needing a terminal are not supported by this mode; use hidden input or a noninteractive output command instead.

With no selector, capture keeps the full text except one final LF/CRLF. A regex must match exactly once; its single capture group is saved, or the whole match if it has no groups. Use (?:...) for additional grouping. --json-pointer selects a JSON string (for example /credentials/password); it cannot be combined with --regex. JSON objects must have unique keys, including nested objects and escaped equivalents such as password and pass\u0077ord. Empty values, invalid UTF-8, ambiguous matches, duplicate keys, and failed commands are rejected without saving. Use --id CREDENTIAL_ID to replace a credential.

Input is limited to 1 MiB. Commands time out after 30 seconds; set --timeout to 1-300 seconds if needed. On timeout or excessive output, capture terminates its direct child; it does not provide process-tree containment. Piped stdin waits for EOF and has no timeout. Successful output contains the credential ID, never the captured value. Use memories run or authorized HTTP operations to use it. Do not put real secrets in command arguments, regex patterns, or titles. Capture does not erase source files, shell history, or logs produced by the source program.

Devices, members, and synchronization

Cloud account authorization

Authorize OneDrive or Google Drive on this device with your own registered OAuth application. Start the current daemon, unlock the library, and run sudo in the same terminal before login:

memories sudo
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 logout onedrive

Login opens the browser and validates a local callback with PKCE/state. Tokens are stored in encrypted device-local state and excluded from backup/device exchange. Status never prints tokens. Logout removes local authorization; provider consent is managed separately. memories cloud sync uploads and downloads immutable ciphertext objects through the connected OneDrive or Google Drive application folder after refreshing tokens. Mobile provides cloud connection and foreground sync controls; native OAuth client registration is required. See mobile cloud setup.

See docs/cloud-authorization.md for provider registration, redirect URIs, --no-browser, expiry, and troubleshooting.

Device pairing and vault transfer

Initialize the new device with its own password and run:

memories sudo
memories device invite request.json

Transfer the request to an existing device. Compare the full invitation fingerprint directly with the requesting device before approving it:

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

The approving command returns its public device key. Verify that key through a trusted channel before accepting the response on the new device. Run sudo in the same terminal on the new device before accepting:

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

Requests expire after ten minutes and are single-use. A newly initialized device must have no vaults before joining an existing identity. Each device keeps its own transport private key and device signing key; the user identity signing authority is transferred only inside the encrypted approval. A user-signed certificate alone does not grant vault access: the vault's signed device allowlist must also approve it.

For a different person, approve their request for a particular vault instead:

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 device revoke VAULT_ID DEVICE_PUBLIC_KEY

Owners manage administrators and ownership; administrators manage ordinary members; editors write assets; readers read and use assets. Device revocation is per vault. Apply it to every affected vault. Never assume removing a member erases their existing copies.

Encrypted file exchange works without a relay:

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

For an online transfer, run memories-relay behind a TLS reverse proxy and create a room with memories sync room. On the receiver, then the sender:

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

The relay does not queue packets for offline devices. A successful send means a packet was handed to the relay, not that a peer verified/imported it; the receiver reports import success separately. sync export/import and relay sync send/receive are deliberate full-vault transfers, not automatic incremental cloud synchronization. cloud sync PROVIDER instead exchanges immutable ciphertext objects through durable cloud queues; live-account verification remains pending. Relay RPC is different again: it calls the remote daemon without importing its vault into the client's store. A user-owned always-online client can hold a replica, but that client is a trusted endpoint rather than an untrusted storage server.

Every create/update writes a new signed revision in the vault DAG (memory, knowledge/brain cards, protected-card metadata, files, and credentials). Use memory history / card history / file history to list revisions (revision, timestamp, author, head, summary). Fetch a revision with read --revision, and compare with memory diff / card diff / file diff (unified text over title, structured metadata, and body; file diffs omit binary payloads; credential secret bodies are never returned). Revisions are retained indefinitely with the vault, with no automatic pruning. Concurrent asset edits remain as multiple heads. Use memory history and deliberate memory put --id ASSET_ID --resolve after reviewing them. Previously unknown old-epoch changes enter sync quarantine; sync reapply signs a new current-epoch revision. Divergent permission chains fail closed. An owner valid on both branches can review them and run sync resolve-policy VAULT_ID packet.json --keep-local, then distribute the signed resolution. Conflicts involving incompatible ownership transfers remain blocked rather than granting either party unilateral control.

Audit and recovery

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

Backups use an independent password. Keep a copy outside the device. To recover, start a daemon with a new --home, then run:

memories --home NEW_DIRECTORY restore offline-backup.memories --name replacement
memories --home NEW_DIRECTORY sudo
memories --home NEW_DIRECTORY archive list

Recovery creates independent vaults containing the current assets and preserved conflict heads. It does not revive stale shared-vault authority. Original versions, tombstones, and signed audit records remain in the encrypted, read-only recovery archive (archive history, archive read, archive audit). Rejoin live shared vaults through a current authorized member. Historical credential values cannot be printed through either the normal or archive interfaces.

AI client skill injection

Install the bundled memories skill so an AI client can discover how to search, retrieve, and save Memories knowledge, answer from authorized documents, and use authorized credential operations:

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

The default is user-wide installation. --project DIRECTORY installs only in an existing project directory (use --project . for the current directory). claude targets Claude Code, with claude-code accepted as an alias.

Client User location Project location
Codex ~/.agents/skills/memories/SKILL.md .agents/skills/memories/SKILL.md
OpenCode ~/.config/opencode/skills/memories/SKILL.md .opencode/skills/memories/SKILL.md
Claude Code ~/.claude/skills/memories/SKILL.md .claude/skills/memories/SKILL.md

~ is the OS user's home, including %USERPROFILE% on Windows. OpenCode honors OPENCODE_CONFIG_DIR, then an absolute XDG_CONFIG_HOME plus opencode, then the default above. Claude Code honors CLAUDE_CONFIG_DIR. Codex uses its documented .agents/skills location, independently of CODEX_HOME. Project installation ignores these global overrides. OpenCode also discovers Claude/agent-compatible skill directories; when using several clients, keep the same memories skill content in all locations to avoid conflicting definitions.

Installation is offline and does not require a daemon or an initialized store. It writes only the bundled skill, without reading/exporting vault contents, creating grants, or modifying MCP/client configuration. --home/MEMORIES_HOME select the Memories data store for normal operations, not the skill destination; installation does not embed that path. Configure the same home in your client's environment or MCP command when using a non-default store.

Existing identical files are left untouched. A different SKILL.md, including an older skill left by a previous Memories version, is refused unless --force is passed; that option replaces only that file, preserving sibling files. Linked skill files/directories are refused even with --force. All targets are checked for conflicts before writing, and each file is published atomically. A later filesystem I/O failure can leave earlier targets installed; fix the error and rerun. --dry-run performs the same checks and reports planned paths/actions without writing anything; use --dry-run --force to preview replacements.

Ensure memories is on the client's PATH. Open a new client session if it does not discover the skill immediately. In Codex invoke $memories; in Claude Code invoke /memories; in OpenCode ask it to load the memories skill. Local skill installation does not install account-level skills into Claude Desktop/Cowork or cloud sessions. The skill uses an already configured MCP connection when available; configure it separately as described below.

Discovery conventions: Codex, OpenCode, Claude Code.

Generate AGENTS.md / persona skill

Write a project AGENTS.md (or a Cursor/Codex-style skill directory) that explains how to operate Memories: vaults, cards, persona/world/things, history/diff, and remote daemon basics. Optionally embed a persona core overview:

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 single markdown file. --layout skill writes SKILL.md under the output directory (or the path if it already ends in .md). When --persona is omitted, the client default persona is used if set. Generating with a persona reads that card through the daemon (unlock first). Without a persona, generation is offline and does not need the daemon. Use --force to replace a different existing file; --dry-run previews the plan without writing.

memories inject remains the separate offline installer for the bundled generic skill into Codex / OpenCode / Claude Code paths.

Client CLI defaults

Non-secret defaults live in defaults.json under the Memories home directory (MEMORIES_HOME / --home), not in 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 vault/asset
vault Default vault for list / card list and single-arg persona forms
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

Deliberate flags always win. Persona subcommands that take vault/asset treat omitted positionals as defaults (world-get, thing, append-event, …). persona import still requires an deliberate vault id before the skill path (clap positional order).

Local MCP

This section describes ordinary vault-scoped MCP grants. The separate opt-in unattended server has a broader read boundary.

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

The grant is a local bearer capability, written to a file rather than printed. Protect that file. Grants default to read access; use --scope read_write for ordinary content writes or --scope read_write_delete to include deletion. --write is a compatibility shorthand for --scope read_write. These scopes don't grant private content, files, credentials or sudo. Configure your AI client's stdio MCP command as memories mcp serve --grant-file ... with the same home as the running daemon. Tools are 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 the persona tools listed in Persona cards. workflow_card creates step workflows, checklist templates or tasks copied from a template, and workflow_update edits checklist fields or advances steps; both need a write scope. workflow_read and workflow_list work with read access. Every call includes the authorized vault ID. memory_retrieve returns cited document chunks. Its default mode is local BM25; vector and hybrid use the daemon's configured embedding provider. memory_ask answers from those sources when a generation URL is configured, and rejects citations that were not in the retrieved evidence. See Retrieval and RAG. Private memory, files, credential access, process execution, and vault membership changes are excluded. Grant issue, list, revoke and permission requests are separate MCP tools and do not inherit sudo. Locking drops in-memory grants; unexpired grants resume after unlock. MCP protocol: 2024-11-05. MCP reads leave secret references unresolved. The current card_write tool cannot update cards containing secret references because it does not preserve their mapping; edit those cards through the CLI or mobile app.

Unattended desktop CLI/MCP

Deliberate enrollment requires typed ENABLE consent and password unlock/sudo once for setup. Choose windows-dpapi, macos-keychain, linux-secret-service, or the lower-protection owner-file backend deliberately, then mint a vault-scoped client file. Select that file with unattended ... --client-file PATH or mcp serve-unattended --client-file PATH. These clients can read ordinary/private content, original files, history and deliberately requested credential plaintext, but cannot mutate assets or inherit sudo.

Daemon startup loads keys only, not ordinary ambient authorization. Manual lock durably inhibits unattended access until password-authorized unattended resume; revoke removes a grant and disable removes all unattended authority. Center-managed stores, remote transports and mobile cannot use it. Ordinary MCP is unchanged. Credential base64 is encoding, not encryption: an AI client or its logs can retain the secret. See setup, byte-range reads and security limits.

Knowledge and protected cards

All knowledge cards support card types and custom fields: note, contact, link, code snippet, checklist, event and credential, plus reusable vault templates. Fields are ordered and validated; sensitive values remain masked unless deliberately revealed with verified authorization. Use memories type and memories card field, or the Flutter and restricted desktop card editors.

Cards support tags, sources, optional alias (别名), notes/remark (备注), category (分类), directed relationships and backlinks. Ordinary card content and safe account summaries are searchable locally; ordinary private-card reads require sudo and are excluded from ordinary search and MCP. The separate unattended authority can read scoped private cards. Use --alias, --notes, and --category on create/update. card list (also top-level memories list / memories ls) can omit the vault id to scan every accessible vault (each result includes vault). --type knowledge|brain|persona|workflow|account|env|token|totp|hotp|otp filters by summary.type (knowledge is plain wiki/knowledge cards; brain is structured knowledge with an abstract and typed links; otp means totp+hotp); --otp is shorthand for --type otp. --category NAME filters; --group-by-category groups output. List shows alias, notes, and category when present.

memories card put VAULT_ID --title "An idea" --input notes.md --tag design --alias "Design note" --notes "from workshop" --category design
memories card list VAULT_ID
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 --type account
memories card list VAULT_ID --category design
memories card list VAULT_ID --group-by-category
memories card brain VAULT_ID --title "Alice" --abstract "Product lead bridging design and eng" \
  --link CARD_A=reports-to --link "OTHER_VAULT/CARD_B=collaborates|shared roadmap" \
  --alias "Alice" --notes "org chart" --category people
memories card brain VAULT_ID --id BRAIN_ID --title "Alice" --abstract "Updated abstract" --link CARD_B=partner
memories card link 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 unlink VAULT_ID CARD_A CARD_B --relation supports
memories card delete VAULT_ID CARD_ID

card delete (like memory delete) removes the card from current views; signed history keeps earlier revisions. Deleting private or protected cards requires sudo.

Brain cards store a plaintext abstract (searchable within vault permissions, not a protected secret) and typed links { vault?, card, role?, note? } that may cross vaults. Quote the entire --link value when it contains | or spaces so the shell doesn't interpret it as a pipeline. summary.type is brain with link_count. List/graph/read resolve link titles and types when permitted and mark missing or unauthorized targets as broken (no cascade delete). Search indexes title, abstract, link roles/notes, and public same-vault target summaries, never secrets. Same-vault brain links also appear on card graph.

Persona cards (summary.type = persona) store a cross-product identity core with linked archive assets, an IANA timezone for local day/period reasoning (隔日 / 早课晚课), world domain (place / pose / look / generic things with free JSON attrs / ledger / weather / dice / spinner / schedule), and append-only behavior events stamped in UTC and local time. See docs/persona.md. Import with memories persona import VAULT_ID ./xiaoya-persona --timezone Asia/Shanghai; update world state with memories persona world-set.

Workflow cards (summary.type = workflow) keep ordered steps on the card revision, each pending, active, done, or skipped, plus an overall status; memories card workflow VAULT --title Release --step "Write notes" --step Ship creates one, memories card workflow-update VAULT ASSET --advance moves a step, and memories card list --type workflow lists them.

A checklist template is created with memories card workflow VAULT --template --assignee NAME --summary OVERVIEW --goal GOAL (summary.type stays workflow, profile field template is true), listed with memories card list --type workflow --template, and copied into a task workflow with memories card workflow VAULT --template TEMPLATE, which can link another workflow card with --workflow WORKFLOW and archive a memory with --archive MEMORY.

Ordinary directed links (card link) reference cards in the same vault. Graphs omit deleted, conflicted and unauthorized nodes and their incident edges. References remain in encrypted history. Recovery preserves IDs of unambiguous assets so relationships and credential references continue to work in the recovered independent vault; references to conflicted assets may require repair. Mobile shows outgoing links, backlinks and a pan/zoom graph (up to 24 nodes on the canvas, with full graph selection and relationship lists).

Protected cards store metadata and a separate credential as one transaction:

Card Search / display Protected value
Email account Email, SMTP host, port, SSL Password
Website login Site URLs, username Password
.env Every variable name followed by *** Original UTF-8 file
Cookie / JWT Exact authorized HTTPS host, header type Cookie header value or bearer token
TOTP Issuer, account, algorithm, digits, period Base32 seed
HOTP Issuer, account, algorithm, digits, counter Base32 seed

Website logins are usually imported in bulk rather than typed one at a time; see Import and export website logins.

memories sudo
memories card account VAULT_ID --title "Example mail" --email user@example.test --smtp smtp.example.test
memories card env VAULT_ID ./app.env --title "Application config"
memories card token VAULT_ID --title "API session" --host api.example.test
memories card token VAULT_ID --title "Browser session" --host app.example.test --cookie
memories card request VAULT_ID TOKEN_CARD_ID https://api.example.test/session
memories card otp VAULT_ID --title "Example OTP" --issuer Example --account user@example.test
memories card otp VAULT_ID --title "Example HOTP" --issuer Example --account user@example.test --hotp --counter 0
memories card otp-import VAULT_ID --title "Imported OTP" --alias "Work 2FA" --notes "laptop" --category otp 'otpauth://totp/Example:user@example.test?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&issuer=Example'
memories card otp-import VAULT_ID --image ./ga-export.png --title "GA import" --category otp
memories card otp-code VAULT_ID OTP_CARD_ID

OTP titles can keep the issuer / account form from import while --alias and --notes store a display name and remark separately; --category groups them in card list.

Passwords, tokens, cookies and OTP seeds use hidden terminal input, never command arguments. Enter a raw JWT for bearer mode, or name=value; other=value for cookie mode. Account SMTP defaults to SSL port 465; this records configuration and does not send mail. Add --id CARD_ID to replace a protected card's metadata and secret; the credential ID and card links are preserved. Credential IDs are references, not secret values. Generic card writes cannot replace protected references.

The dotenv importer masks all values, including empty/non-secret values, and never indexes values or comments. It accepts one KEY=value per line, optional export, blank lines and comments. Duplicate keys and multiline quoted values are rejected. It does not expand variables or execute the file. The original source file remains where it was; import does not change its permissions or erase it. Freeform card titles/notes are not an automatic secret-detection system.

The HTTPS adapter injects either Authorization: Bearer ... or Cookie: ... on the client. It requires sudo, exact approved host, HTTPS port 443, and GET/POST. Redirects and environment proxies are disabled; only status is returned, with response bodies/headers suppressed to prevent reflected-secret output. This is an authorized request adapter, not a general forwarding proxy or browser cookie jar. Cookie expiry, JWT refresh and automatic login are not implemented.

TOTP supports SHA-1/SHA-256/SHA-512 (--algorithm), 6/8 digits (--digits), and 30/60-second periods (--period). HOTP uses the same algorithms and digit sizes with --hotp and an initial --counter (advanced on each otp-code). Import Google Key URIs with otp-import (otpauth://totp/... or otpauth://hotp/...), Google Authenticator migration URIs (otpauth-migration://offline?data=...), or a QR image via --image (decoded with pure-Rust rqrr). The TOTP/HOTP kind is taken from the URI or migration record. When that kind is omitted, a counter selects HOTP and every other account is TOTP. Migration payloads may create multiple cards; --title is the single-card title or a batch prefix. otp-code returns the short-lived code after sudo and audits generation without storing the code in audit records; HOTP responses include the next counter. Search and MCP never generate codes or return seeds. Mobile hides expired codes and dismisses the dialog on lock/background. Keep the device clock accurate for TOTP. Mobile does not scan OTP QR codes with the camera; use CLI --image or paste an otpauth:// / migration URI. Hosted device pairing has a separate camera flow; see center.

Mobile has Protected card, Import .env and Import logins (CSV) actions. Account/credential basic information appears directly in search results and card details. Token and OTP cards provide Authorized request and Generate OTP menu actions. Generic card editing preserves protected metadata; secret replacement currently uses CLI.

Import and export website logins

Website logins live in login protected cards and are moved in bulk. Import and export require memories sudo and a writable role.

memories sudo
# Browser CSV (Chrome/Edge/Brave or Firefox about:logins); the header is detected.
memories login import VAULT_ID --from chrome passwords.csv
memories login import VAULT_ID --from firefox firefox-logins.csv --with-notes
# Read a Firefox profile directly (modern key4.db; prompts for any primary password).
memories login import VAULT_ID --from firefox-profile "/path/to/Profiles/xxxx.default"
# Windows Credential Manager generic credentials (Windows only).
memories login import VAULT_ID --from windows --filter github
memories login import VAULT_ID --from windows --all
# A previous encrypted export.
memories login import VAULT_ID --from memories logins.mlx

# Export: portable encrypted file (recommended) or a browser-compatible CSV.
memories login export VAULT_ID --format encrypted logins.mlx
memories login export VAULT_ID --format csv logins.csv

A login with the same site host and username as an existing card is skipped when the password matches and reported as a conflict when it differs; --update overwrites instead. Bad CSV rows are skipped and reported by line number; a single import is capped at 2000 logins. The browser note column is imported only with --with-notes, because notes are indexed as ordinary text. Firefox direct read handles the modern key format (Firefox 58+); for older profiles, export a CSV from about:logins. Reading a browser's saved passwords needs that browser closed on some platforms and never modifies the browser's store.

The encrypted export uses an independent password and imports into any vault. The CSV export is plaintext for moving logins back into a browser or another password manager: it is local-CLI only, prints a warning, writes an owner-only file that must not already exist, defuses spreadsheet formula injection, and should be deleted after use. Device-only vaults cannot be exported.

Device-only configuration

memories local-python
memories local-python --program /path/to/python
memories vault list
memories card search LOCAL_VAULT_ID Python

This runs the selected Python interpreter in isolated mode to record its version and executable directory. It does not collect environment variables. Each call records a new observation in the encrypted This device vault with local_only: true. That entire vault is excluded from device authorization packages and exported backups; membership invitations, sync export/import and policy sync are rejected. It remains searchable on this device and survives lock/restart, but exported backup recovery deliberately does not restore it. Create knowledge cards in this vault to record other local configuration. Device pairing still requires a destination with no existing vaults; pair first before creating its local configuration observations.

Upgrade all participating clients before exchanging cards: older clients do not understand the new card kind and protected metadata.

Mobile

On first launch, choose Standalone, Center, or SaaS. Standalone uses the encrypted local library. Center asks for your hosted server's URL and member token; SaaS uses https://memories.ivjn.us and asks for a member token. Hosted modes do not require creating or unlocking a local library. Use Switch mode in the main toolbar to change later; existing data stays in its original store. See the mobile guide for connection steps, available features, authorization, editing and troubleshooting, or the Chinese usage guide.

Build and validate the mobile client

Use Flutter 3.44.2 (the CI version), JDK 17+, Android SDK/NDK and cargo install cargo-ndk --locked. This checkout was built with NDK 28.2.13676358 and SDK platforms 35, 36, and 37.0 (the Flutter SDK and plugins select their compile platforms). Accept Android SDK licenses before building. Make flutter available on PATH and install the aarch64-linux-android and x86_64-linux-android Rust targets with rustup target add.

.\scripts\build-android.ps1 -Apk

This builds ARM64/x86_64 native libraries and a debug APK at mobile/build/app/outputs/flutter-apk/app-debug.apk. Release signing credentials are not included. On macOS with Xcode, use bash scripts/build-ios.sh, then sign and run the app with your own development team. iOS static libraries are force linked so the Dart FFI symbols remain available.

After building the native libraries, run the actual mobile ABI smoke test on an attached device or emulator from mobile:

flutter test integration_test/native_smoke_test.dart -d DEVICE_ID

This test uses a temporary library and checks encrypted storage, authorization, credential read denial, audit verification, and lock/unlock through Rust FFI. Rebuild the normal APK afterward because Flutter integration tests replace the debug APK entrypoint with their test runner.

In Standalone mode, the mobile client uses the same Rust service through C FFI, local full-text search, file pickers, device/member approval files, online relay transfers, history and audit views. A Keychain/Keystore-protected local factor supplements the password. Backgrounding clears authorization and closes content dialogs; Android blocks screenshots and iOS covers the app-switcher snapshot. Background synchronization is not enabled in this release.

Center and SaaS use the Rust hosted client through FFI for request approvals, knowledge browsing, server-side search, detail, editing, deletion and device pairing. Actions follow the token scope; structured/sealed cards require their specialized editors. Backgrounding closes the hosted session; use Reconnect saved session or enter a token again on return. See the mode comparison before choosing a workflow.

Repository

Component Path
Cryptography, model, storage, sessions, service crates/core
CLI, OS-authenticated IPC, MCP, relay client crates/cli
Stateless WebSocket relay crates/relay
Mobile C ABI crates/ffi
Flutter Android/iOS client mobile
Build helpers and validation scripts, .github/workflows

See docs/architecture.md and docs/validation.md for implementation and verification details. The OneDrive/Google Drive storage and synchronization design is documented in docs/cloud-storage-design.md. CLI authorization and cloud sync are implemented; provider transfers have been tested with local fixtures, without live accounts. Mobile login/sync controls use AppAuth and the shared Rust worker; live consent and platform verification remain release gates.