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.