Tupi-Guarani for “small”
mirim
An embedded SQL database, encrypted at rest, with post-quantum sealed exports.
A Rust core with #![forbid(unsafe_code)], XChaCha20-Poly1305 storage, and ML-KEM-768 exports. The 1.1.1 maintenance release is in preparation, with security fixes and complete guides in English and Brazilian Portuguese.
secrets
access_log
7bd0 228a
1655 e27f
key wrapped per recipient · recipient secret key required
1.1.1 · Release preparation
Maintenance with clear security and documentation.
The fixes preserve existing binary layouts. This page introduces the source update; 1.1.0 remains the previous stable release until publication is complete.
| Area | Change |
|---|---|
| Private files | Exclusive creation of randomly named temporary files with private Unix permissions; key generation avoids overwriting existing files. |
| WAL | Statements too large for the WAL format are rejected before acknowledgement, keeping accepted writes recoverable. |
| Writer locking | Exclusive Unix and Windows locks through safe Rust; unsupported platforms or filesystems fail the open. |
| Desktop | Updated graphics dependencies and accurate documentation of the secret retained by a session for checkpoints. |
| Documentation | Complete English and Brazilian Portuguese guides, consistent backup instructions, and explicit security and platform limits. |
| Publishing | Release assets and packaging for more systems are being prepared. Actual availability is defined by release attachments, not by a submitted PR. |
Downloads
Choose a published release.
Version 1.1.1 is in preparation. For the previous stable version, find 1.1.0 on the mirrors below. Check each release’s attachments and notes; binaries are not guaranteed for every platform.
| Mirror | Downloads |
|---|---|
| git.securityops.co | Browse releases and attachments |
| git.securityops.com.br | Browse releases and attachments |
| GitHub | Browse releases and attachments |
| Codeberg | Browse releases and attachments |
Verify before installing
sha256sum -c SHA256SUMS --ignore-missing- Require OK for each file you will install. A checksum does not authenticate its publisher.
- When a .mf signature is attached, use a trusted verifier and a previously authenticated public key.
mirim-sign verify <artifact> <artifact>.mf mirim-release.pub
Choose your platform
- Select the OS, architecture, and version from actual attachments; native packages and GUI availability may differ.
- The prepared v1.1.1 GNU/Linux x86_64 CLI, signer, and CLI AppImage require glibc 2.34 or newer; the GUI requires glibc 2.39 or newer. Build from source for musl or older glibc systems.
- The local GNU Guix recipe repackages x86_64-linux binaries. Its presence does not establish inclusion in official Guix.
- GNU/Linux, BSD, macOS, and Windows installation
What it does
Small data, kept private and durable.
mirim holds data that should stay in memory, encrypted on disk, and recoverable after a crash: keys, credentials, capability records, audit trails, configuration.
Encrypted at rest
Snapshots use XChaCha20-Poly1305 with authenticated headers. Keys come from Argon2id or a caller-provided 32-byte raw key.
Post-quantum sealed exports
ML-KEM-768 seals a snapshot for one or several recipients. Each opens it with a matching secret key. Authenticating the sender requires a signature.
Crash-safe durability
DurableDb syncs mutations to the WAL before reporting success. The SIGKILL harness checks process crashes; physical power-loss behavior also depends on the filesystem and device.
SQL with an index
CREATE, INSERT, SELECT, UPDATE, and DELETE with parameters, ORDER BY, LIMIT, and COUNT(*). PRIMARY KEY and UNIQUE have equality indexes; data remains in RAM.
Rotation, and one writer
rotate_secret replaces current ciphertext and rebinds the WAL. Sessions use exclusive locks on Unix and Windows; opening fails if the system does not support locking.
Embed from C
The C API has documented pointer and error contracts. Its harness uses Rust ASan and C ASan/UBSan; invalid pointers remain the caller’s responsibility.
Dialect
The whole SQL surface, on one screen.
Two types, no coercion, no surprises. What follows is not a summary of the language. It is the language.
-- Line comments, and /* block comments */. CREATE TABLE users ( id INTEGER PRIMARY KEY, -- unique, indexed email TEXT UNIQUE, -- allows many NULLs age INTEGER ); -- One row per INSERT. Unlisted columns become NULL. INSERT INTO users VALUES (1, 'ana@x.io', 30); INSERT INTO users (id, email) VALUES (2, 'bia@x.io'); SELECT email, age FROM users WHERE age >= 18 AND email IS NOT NULL; SELECT * FROM users ORDER BY age DESC; SELECT id FROM users ORDER BY id LIMIT 10 OFFSET 20; SELECT COUNT(*) FROM users WHERE age IS NULL; -- Atomic per statement: a violation changes nothing. UPDATE users SET age = 31 WHERE id = 1; DELETE FROM users WHERE id = 2;
+ In the language
- INTEGER (i64) and TEXT (UTF-8). Nothing else.
- Single-column
PRIMARY KEYandUNIQUE, each carrying an O(1) equality index. WHEREwith AND-chained comparisons andIS [NOT] NULL.ORDER BY, multi-key and stable. NULL sorts first under ASC, last under DESC.LIMIT,OFFSETandCOUNT(*).?parameter binding. Bound values are data, never SQL.- Line and block comments.
- Deliberately absent
- Joins and sub-queries.
GROUP BY,OR, and every aggregate exceptCOUNT(*).- Transactions spanning statements. Each statement is atomic on its own.
- Secondary indexes beyond PRIMARY KEY / UNIQUE, and a query planner.
- Type coercion.
REAL,BLOB,DATE,BOOLEAN. FOREIGN KEY,NOT NULL,DEFAULT,CHECK, composite keys.- Data larger than RAM. Durability is real, out-of-core storage is not.
Untrusted input goes in as a bound ? parameter, never as SQL text. Full walkthrough in the guide. Five worked schemas in samples/.
Use cases
Match the limits to your application.
Configuration, local secrets, and small application datasets can benefit from encrypted storage. Evaluate size, queries, concurrency, and recovery before choosing.
| Aspect | Behavior |
|---|---|
| Small data | The whole database and its indexes must fit in RAM. |
| SQL | Tables, filters, ordering, and parameters; no joins or multi-statement transactions. |
| Indexes | PRIMARY KEY and UNIQUE equality predicates use expected constant-time hash lookups; other predicates scan rows. |
| Persistence | DurableDb uses a snapshot and WAL. Db persists only when the application explicitly saves it. |
| Evaluation | Run cargo bench --locked --bench core on your hardware and test behavior on the target OS. |
Cryptography
Named primitives, with their standards.
Mirim combines primitives from RustCrypto dependencies in its own file formats. The suite includes pinned cryptographic vectors; this is not an independent certification.
| Role | Primitive | Parameters | Standard |
|---|---|---|---|
| Key encapsulation | ML-KEM-768 | 1088-byte ciphertext, implicit rejection | FIPS 203 |
| Signatures | ML-DSA-87 | 4627-byte signature, sign feature | FIPS 204 |
| AEAD | XChaCha20-Poly1305 | 24-byte nonce, header bound as associated data | RFC 8439 and extension |
| Passphrase KDF | Argon2id | 64 MiB, 3 passes, 4 lanes | RFC 9106 |
| Key derivation | HKDF-SHA256 | domain-separated info string per format | RFC 5869 |
| Hashing | SHA-256 | snapshot identity, manifest target digest | FIPS 180-4 |
| Randomness | OS CSPRNG | via getrandom | platform |
Authentication failures return Error::Crypto. Public structure, version, and secret-kind errors can differ. See SECURITY.md for the full model.
On disk
Versioned, documented file formats.
Binary layouts remain compatible in this maintenance update. The guide consolidates field sizes, layouts, and key derivation.
| Artifact | Magic | Version | Layout |
|---|---|---|---|
| Vault | MIRIM1\0\0 | v1 | 50-byte header (magic, version, KDF id, salt, nonce), then the AEAD ciphertext of the snapshot. File extension .mrm. |
| Sealed export, one recipient | MIRIMSL1 | v1 | 1088-byte ML-KEM-768 ciphertext, 24-byte nonce, then the AEAD payload. |
| Sealed export, many recipients | MIRIMSL1 | v2 | Payload encrypted once under a random key, which is then wrapped per recipient: a 1088-byte KEM ciphertext and a 48-byte wrapped key each. Any one of them opens it. |
| Write-ahead log | MIRIMWL1 | v1 | 41-byte header carrying the SHA-256 of the vault it extends, then AEAD records with authenticated consecutive sequence numbers. Recovery can truncate an incomplete or invalid final record. |
| Signed manifest | MIRIMMF1 | v1 | 4677 bytes: header, target kind, counter, the SHA-256 of the target, and an ML-DSA-87 signature over the first 50. File extension .mf. |
| Trust file | MIRIMTS1 | v1 | 49 bytes: the highest accepted counter and the hash of the last accepted target. This is the local trust root for rollback rejection. |
Encryption headers are authenticated, but local trust state is not ciphertext. Recovery can discard a damaged WAL tail or a WAL naming another snapshot; this does not prevent malicious rollback. Binary reference.
Assurance
Checked more than once.
Complementary methods check different properties. A test in the repository is not evidence that it ran for a particular release or platform.
- +Differential model. Compares the SQL evaluator with an independent model over generated sequences.
- +Properties. Checks round trips, replay, checkpoints, and uniqueness invariants.
- +Fuzzing. Five targets cover wire, SQL, exports, manifests, and WAL; a finite run is not exhaustive.
- +FFI. C conformance harness with sanitizers and separately checked dependencies.
- +Recovery. SIGKILL harness for process crashes; it does not simulate every physical power failure.
- +Dependencies. cargo audit, cargo deny, and an SBOM; GUI and FFI have separate lockfiles.
- +Results. Consult release notes for the checks and platforms actually exercised.
$ mirim secrets.mrm creating new vault: secrets.mrm new passphrase: repeat passphrase: mirim 1.1.1 — .help for commands mirim> .read samples/secrets_vault.sql ok: ran 30 statement(s) from samples/secrets_vault.sql mirim> SELECT COUNT(*) FROM access_log WHERE allowed = 0; count(*) -------- 4 mirim> SELECT actor, ts FROM access_log ...> WHERE allowed = 0 ORDER BY ts DESC LIMIT 3; actor | ts ------------------------- unknown-host | 1717301000 ex-employee | 1717300800 svc-web | 1717300500 mirim>
Security model
Understand the protections and their limits.
Choose a deployment with memory, storage, recovery, and key trust in mind.
Protections
- Encrypted storage. Snapshots and WAL records protect content with authenticated encryption; metadata remains visible.
- Passphrases. Argon2id increases guessing cost. A weak passphrase remains vulnerable to guessing.
- Exports. ML-KEM-768 is designed to resist known quantum attacks, without a guarantee against every future cryptanalytic advance.
- Malformed inputs. Decoders check structure and lengths; this does not impose a total memory budget.
Limits
- Compromised host. Open process memory, swap, and core dumps can expose data and secrets.
- Rollback. An attacker can restore older database copies or truncate the WAL. Signatures require explicit checks and protected trust state.
- Metadata. Size, existence, times, and access patterns are visible, as are WAL record sizes.
- Concurrency. Sessions acquire exclusive locks on Unix and Windows. Direct vault::save and path aliases need coordination; unsupported locking fails the open.
- Rotation. Old backups and residual blocks are not erased. Lost secrets cannot be recovered.
Constant-time behavior principally depends on the cryptographic implementations. The complete binary has no independent constant-time certification. SECURITY.md · Operational guide
New · desktop
A front end that refuses by default.
mirim-gui is a small point-and-click way to open a vault, built on eframe and egui. It lives in its own crate so the core stays tiny and unsafe-free.
- +Local. The application works with local vaults and uses the same database core.
- +Explicit writes. Allow writes enables mutating SQL. Opening can create or repair the WAL; this is not forensic read-only access.
- +Session secret. Login fields are cleared, but DurableDb retains a copy for checkpoints until the session closes.
- +Durability. Acknowledged mutations are synced; sessions acquire exclusive writer locks on Unix and Windows.
- +Controls. Ctrl+Enter executes SQL; controls provide checkpointing, passphrase rotation, and an offline Licenses window with bundled font notices.
# Build from the checkout $ cargo build --release --locked \ --manifest-path gui/Cargo.toml $ ./gui/target/release/mirim-gui Check release attachments for prebuilt binaries.
License
Dual-licensed. Pick the one that fits.
Open by default under AGPL-3.0-only. A commercial license exists for the cases the AGPL's terms do not suit.
AGPL-3.0-only open source
Files carrying the public option can be used under AGPL-3.0-only according to its terms. MIT-marked files and dependencies retain their own licenses.
Commercial on request
The commercial option requires a separate agreement signed by the applicable rights holder. LICENSE-COMMERCIAL is an inquiry notice, not a public grant. Contact: sac@securityops.co.
Full terms in LICENSE and LICENSING.md. Copyright (c) 2026 Cristian Cezar Moisés.
1.1.1 · In preparation
Read the source and the guides.
The 1.1.1 update is in preparation. Consult published notes and attachments to choose a release; the guides cover installation, backups, APIs, and verification.