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
- Install the required tools and import the identity and its password as
separate credentials.
credential importaccepts 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_idfor mail/iOS. - Select the same
--home/MEMORIES_HOMEas your running daemon. Use short vault and credential IDs returned by Memories. - Add
--dry-runto a command to review its JSON plan andapprovaldigest. This reads inputs/tools but needs no daemon and writes no files. - Run
memories sudoin your terminal, then run the same command without--dry-run. Interactive execution displays the plan and asks forUSE. Non-interactive execution requires--approve-digest DIGESTfrom 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.