Memories / Documentation

On this page

Mail cryptography and application signing

memories crypto uses encrypted credentials with trusted desktop signing tools. It supports S/MIME and OpenPGP mail operations, Android APK/AAB signing, and provisioned iOS .app signing on macOS. It does not send mail or publish apps. These operations run through the local daemon with terminal sudo. They are not available through MCP or mobile FFI. Update both the CLI and daemon and restart the daemon before using these commands; older daemons do not recognize the new operation.

Review and execute

  1. Install the required tools and import the identity and its password as separate credentials. credential import accepts binary PKCS#12/PFX and Android keystores. Use hidden input for passwords; do not pass their values as arguments. An empty-password identity can omit --password_id for mail/iOS.
  2. Select the same --home/MEMORIES_HOME as your running daemon. Use short vault and credential IDs returned by Memories.
  3. Add --dry-run to a command to review its JSON plan and approval digest. This reads inputs/tools but needs no daemon and writes no files.
  4. Run memories sudo in your terminal, then run the same command without --dry-run. Interactive execution displays the plan and asks for USE. Non-interactive execution requires --approve-digest DIGEST from the reviewed plan. Changing the operation, paths, credential IDs, tools or input contents invalidates approval. Never blindly approve a plan generated by untrusted data.
memories credential import VAULT_ID "Signing identity" identity.p12
memories credential add VAULT_ID "Identity password"
memories sudo

--credential, --password_id, and --key-password take credential IDs, not values. --password and --password-id are compatible aliases for --password_id. All three spellings accept the same stored credential ID. --tool NAME=PATH selects an executable by explicit path; repeat for multiple tools. apksigner is the exception: its path is the SDK's lib/apksigner.jar, executed by the selected Java runtime. Tools are installed separately.

The output must be a new path in an existing directory. The input is copied into a private workspace and is never signed in place. Tool output is suppressed; Memories reports the failing step number without exposing tool diagnostics. An operation has a five-minute execution deadline, plus bounded cleanup.

S/MIME

Use an S/MIME identity with the appropriate email-protection certificate usage. A Memories TLS server certificate is not an S/MIME identity. Import either a PEM file containing the private key and its certificate, or a PKCS#12/PFX bundle and pass --pkcs12. The password is read from the referenced credential. Use current OpenSSL; older legacy PKCS#12 ciphers are not enabled automatically.

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

Signing uses SHA-256; encryption uses AES-256-CBC S/MIME enveloped data. Repeat --recipient for multiple PEM recipient certificates. Encryption uses public certificates, so omit --credential and --password_id. It still requires local authorization to run the selected tool. Check recipient ownership and certificate trust before use; encryption does not establish that an address belongs to a key.

The input is processed as exact bytes. Supply the MIME content you want signed or encrypted; transport headers and sending remain your mail client's job. To sign and encrypt, first create the signed message and then encrypt it. Decryption alone does not verify a sender's signature. Verify signatures and certificate chains with a mail client or OpenSSL, for example:

openssl smime -verify -binary -in signed.eml -CAfile trusted-ca.pem -out verified.eml

OpenPGP / GPG

Import an ASCII-armored or binary OpenPGP secret-key export as the identity. Use the full key fingerprint as --identity; names and email addresses are not accepted as selectors. Both gpg and its matching gpgconf are required. On Windows, use native GnuPG paths normally. When selecting the MSYS executables shipped in Git for Windows's usr/bin, add --msys-gpg to translate temporary paths to MSYS drive notation. This option is also bound into the approved plan.

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

pgp-sign creates an armored detached signature over the exact input bytes. pgp-encrypt creates armored OpenPGP encrypted data. These outputs can be used as attachments or by a mail client's OpenPGP integration; this command does not assemble RFC 3156 PGP/MIME transport wrappers. Repeat both --recipient and --identity for multiple recipients. Only explicitly selected fingerprints are encrypted to; selecting a fingerprint is your decision to trust that recipient.

Each operation uses a fresh private GnuPG home with configuration loading disabled. It imports only the supplied keys and terminates that home's agent via gpgconf before deleting its temporary files. Your normal keyring is not used. Hardware token stubs, interactive pinentry and smartcard agents are not supported by this file-credential workflow. Verify detached signatures separately with GPG using an independently trusted public key:

gpg --verify message.sig.asc message.eml

Android

Import a JKS or PKCS#12 keystore and its store password. --identity is the key alias. If the key password differs, import it separately and use --key-password. Android signing requires a password credential, including an explicitly stored empty value if your tool and keystore permit it.

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

APK signing aligns the copied APK with 16 KiB native-library page alignment, signs it with apksigner, and requires its verification to succeed. Select Android Build Tools 35 or newer. Its minimum Android version is derived from the APK manifest. AAB signing uses the JDK's JAR-signing format and verifies the result with jarsigner. App Bundle structure and Play policy are still validated by your build/release pipeline. These commands do not generate APKs or AABs and do not contact Google Play. APK v4 .idsig sidecars are not exported.

iOS (macOS only)

Build a device .app with Xcode first. It must contain a matching embedded.mobileprovision. Import your Apple-issued signing identity as PKCS#12, and provide the exact certificate SHA-1 fingerprint and matching entitlements. The provisioning profile, team, application identifier, devices, expiration and entitlements must permit the intended deployment. Memories does not issue Apple certificates, generate profiles, or establish App Store eligibility.

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

The job extracts the identity in its private workspace, creates a temporary Keychain there, imports the identity for the selected codesign executable, signs embedded frameworks/dylibs from the inside out, then signs the main app with entitlements and verifies the code signature. It deletes the temporary Keychain afterward; it does not select it as your default Keychain. The temporary Keychain uses an empty password and relies on the private directory's OS access controls, just like the short-lived PEM material used for import.

This first adapter accepts a single .app and embedded frameworks/dylibs without symlinks. Nested .app, .appex and .xpc bundles are rejected because they need their own profiles and entitlements. Use Xcode archive/export for those products, IPA packaging and distribution. Code-signature verification does not prove that Apple's provisioning or store checks will accept the artifact. The adapter is unavailable on Windows/Linux and still requires validation on a macOS signing host.

Security and limits

The approval digest binds the public plan, vault, credential references and domain-separated SHA-256 input/tool fingerprints. For directories, the fingerprint also includes sorted entry names and structure. Tool files are rechecked before each launch, and copied inputs must match their approved fingerprints. The selected toolchain and its dependencies must be trusted: tools can read injected credentials and retain their normal OS access. This is not a process sandbox, HSM, PKCS#11 provider, or isolation from other processes running as your OS user.

On Unix temporary directories/files are owner-only. On Windows the workspace uses a protected owner-only DACL inherited by its credential files. Passwords and keys are never interpolated into arguments. They exist temporarily as files because the native tools require them. Normal completion/failure removes the workspace; crashes or power loss can leave plaintext behind. There is no secure deletion guarantee. Tool stdout/stderr are suppressed; signed/encrypted results and deliberately decrypted output are published to the chosen local destination.

Existing outputs are never overwritten. File outputs publish atomically; app directory publication can be observed while copying and is removed on a copy failure. Inputs/outputs are bounded to 2 GiB and 20,000 entries per tree, and symlinks/reparse points are rejected. Up to 32 tool steps and 16 credential bindings are allowed. As with memories run, execution holds the daemon writer lock and only the direct timed-out process is killed; trusted tool descendants are not generally contained. Intent and result are audited without secret values.