Machine Memory is a persistent, evidence-based history layer for a developer workstation. It answers deterministic questions about what changed on your machine, when, and how — from an immutable, hash-chained log of real events, not inference or guesswork. The hash chain detects accidental corruption and unsophisticated tampering; see Known Limitations for what it does not defend against.
Status: v0.7.0 — local-only. This release tracks changes to your
PATH, installed applications, environment variables, Windows services,
drivers, certificates, and scheduled tasks, and can correlate related
changes into higher-level Operations — each labeled with an explicit
evidence tier (Proven/Corroborated/TemporalAssociation/
CoObservation, see Correlation rules below)
stating exactly what class of evidence supports it. mm correlate is
also the discovery entry point: it surfaces every finding — rule,
confidence, evidence tier, evidence count — without requiring an
operation_id up front, optionally filtered by --rule-id/
--evidence-tier/--min-confidence, before you drill into
mm explain <operation_id>. A mm-agent run continuous background mode
and a mm status command exist alongside the original one-shot
collection (see docs/10-rfcs/CONTINUOUS-WINDOWS-AGENT.md),
and a desktop application now provides a graphical Overview/Changes/
Findings/Investigate/System-Health view over the same data (see
Desktop Application below and
docs/10-rfcs/DESKTOP-APPLICATION.md).
See Scope of v0.5.0 below
for what's deliberately still out.
Using a pre-built Windows release ZIP rather than building from source? See the Installation and Operations Guide for the exact extract → run → schedule → upgrade → uninstall workflow. The rest of this README covers building from source and the product's overall shape.
"Why did my shell suddenly stop finding python?" is a question every
developer has actually had, and it's usually unanswerable after the fact.
PATH tracking was Machine Memory's first collector and remains its
flagship example: every time something is added to or removed from your
machine's PATH, it's recorded, permanently, with a timestamp —
queryable later with an ordinary CLI command. Six more collectors
(installed applications, environment variables, Windows services,
drivers, certificates, scheduled tasks) and a correlation engine that
groups related changes into named Operations exist alongside it — see
Commands below.
| Collector | Resource kind | Platform |
|---|---|---|
| PATH entries | PATH_ENTRY |
Linux/macOS/Windows |
| Installed applications | APPLICATION |
Linux/macOS/Windows |
| Environment variables | ENVIRONMENT_VARIABLE |
Linux/macOS/Windows |
| Windows services | SERVICE |
Windows only (SCM) |
| Windows drivers | DRIVER |
Windows only (SCM) |
| Certificates | CERTIFICATE |
Windows only (CryptoAPI cert stores) |
| Scheduled tasks | SCHEDULED_TASK |
Windows only (Task Scheduler XML; requires elevation to read C:\Windows\System32\Tasks) |
| Rule | Claim |
|---|---|
ConsecutiveEventsRule |
A tight, same-kind run of events on one machine |
SoftwareInstalledRule |
An APPLICATION Installed event, at full confidence |
ServiceStoppedRule |
A service transitioned to meaningfully stopped |
CertificateExpiryRiskRule |
A certificate entered its expiry-risk window |
PathBrokenRule |
A PATH_ENTRY was removed, optionally corroborated by a nearby APPLICATION event |
CrossResourceCoObservationRule |
Several heterogeneous Installed events were first discovered together in one real collection run — states co-observation only, never a causal or real-world-timing claim (see the rule's own module documentation for the full analysis) |
- A stable Rust toolchain (see
rust-toolchain.tomlfor the pinned version) protoc(the Protocol Buffers compiler) — install via your system package manager, e.g.apt install protobuf-compiler(Debian/Ubuntu) orbrew install protobuf(macOS)
git clone https://github.com/<your-org>/machine-memory.git
cd machine-memory
cargo build --workspace --releaseThis produces two binaries in target/release/:
mm-agent— the collector. Run this to capture the current state of yourPATHand record any changes since the last run.mm— the query CLI. Run this to ask questions about what's been recorded.
# First run: captures your current PATH (and everything else the other
# six collectors observe) as the baseline.
./target/release/mm-agent --machine-id my-laptop
# ...time passes, you install a new tool, your PATH changes...
# Run the agent again: only the delta is recorded, not a full re-scan.
./target/release/mm-agent --machine-id my-laptop
# Now ask questions:
./target/release/mm --machine-id my-laptop timeline
./target/release/mm --machine-id my-laptop diff --range today
./target/release/mm --machine-id my-laptop verifyNeither command needs an explicit --db-path: both default to the same
fixed, per-user location (%APPDATA%\machinememory\data.sqlite3 on
Windows, $XDG_DATA_HOME/machinememory/data.sqlite3 — falling back to
~/.local/share/machinememory/data.sqlite3 — on Linux/macOS), created
automatically on first use. Pass --db-path <file> explicitly if you
want a different location (e.g. a portable install, or several machines'
histories kept in clearly separate files).
--machine-id (and --db-path, if you're overriding it) must be typed
identically across every invocation of both binaries, or you will
silently start a second, disconnected history. To make that impossible
to get wrong, put them in a config file once and drop the flags
entirely:
// Windows: %APPDATA%\machinememory\config.json
// Linux/macOS: ~/.config/machinememory/config.json
// (or point the MM_CONFIG environment variable at any path)
{
"machine_id": "my-laptop"
}Both binaries read the same file through the same code path. Explicit flags still win over the config file when given. A malformed config file is a hard error, never silently ignored.
mm-agent run runs collection cycles on a repeating interval (default 30
minutes) until stopped — no separate scheduler needed:
.\target\release\mm-agent.exe --machine-id my-laptop runIt runs an initial cycle immediately, writes an operational status file next to the database, and logs to a rotating file alongside it. Check on it with:
.\target\release\mm.exe --machine-id my-laptop statusTo have it start automatically at logon, register it with Windows Task Scheduler:
.\target\release\mm-agent.exe --machine-id my-laptop service install
.\target\release\mm-agent.exe service status
.\target\release\mm-agent.exe service uninstall # removes the task only, never the databaseSee docs/10-rfcs/CONTINUOUS-WINDOWS-AGENT.md
for the full design (why Task Scheduler over a native Windows Service,
single-instance safety, failure semantics, and known limitations — this
is the background-execution mechanism, not yet a polished installer).
mm-agent run-once (the default when no subcommand is given) is a
one-shot tool by design — it does not run continuously in the
background. On Linux, a simple systemd user timer works well:
# ~/.config/systemd/user/machinememory.service
[Unit]
Description=Machine Memory PATH collector
[Service]
Type=oneshot
ExecStart=%h/.local/bin/mm-agent --machine-id %H --db-path %h/.local/share/machinememory/data.sqlite3# ~/.config/systemd/user/machinememory.timer
[Unit]
Description=Run Machine Memory collector periodically
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.targetsystemctl --user enable --now machinememory.timerA macOS (launchd) equivalent is not yet packaged — run mm-agent
manually, or adapt the above pattern. (This is unrelated to the
SCHEDULED_TASK collector, which observes existing scheduled tasks on
the machine — it does not create one for mm-agent.)
| Command | Answers |
|---|---|
mm timeline |
What changed, in order |
mm diff --range yesterday|today |
What was added/removed/modified in a time range |
mm history --key <path> |
The full history of one specific PATH entry |
mm verify |
Has this history's hash chain been corrupted or naively tampered with? (see Known Limitations) |
mm replay |
Walk the raw log from the beginning, showing the actual hash-chain linkage |
mm stats |
Aggregate shape of the log: counts, first/last event, chain status |
mm correlate [--rule-id <id>] [--evidence-tier <tier>] [--min-confidence <f>] |
Discover findings: run the correlation rules over the full history, persist, and print every resulting Operation with its rule, confidence, evidence tier, and evidence count — no operation_id needed up front. Excludes a finding whose evidence is entirely a fresh machine's initial baseline discovery (see below); mm explain still resolves any Operation by id regardless |
mm explain --operation-id <id> |
Why was this Operation concluded? Shows the rule, confidence, evidence tier, and the exact evidence events |
mm investigate --range today |
What changed, what correlates, what doesn't, and why — one coherent investigation combining the above instead of stitching five commands together by hand |
mm compact |
Reclaim space: remove raw Events superseded by a later event for the same resource that no persisted Conclusion still cites as evidence — every Operation stays fully explainable via mm explain forever (see docs/03-architecture/RETENTION.md) |
mm status |
Is continuous monitoring running right now (a real, live process check — never inferred from a stale file), and when did any collection (manual or continuous) last complete? See docs/10-rfcs/CONTINUOUS-WINDOWS-AGENT.md §16 |
Baseline vs. genuine change. A collector's very first run against a
machine has nothing to compare against, so it reports every
currently-observed resource as Installed — this is necessary (there
is no other way to learn what already exists), but it is not a burst of
recent real-world installs. mm stats/mm timeline/mm correlate
mark such events is_baseline: true (derived from existing
collection_run_id data — no schema change), and discovery-oriented
findings (mm correlate's findings, mm investigate's
correlated_findings) exclude a Conclusion whose evidence is entirely
baseline discovery. See
docs/10-rfcs/PRODUCT-CORRECTNESS-BASELINE-AND-STATUS.md
for the full model.
Every command supports --format text|json — JSON output is a stable,
versioned schema suitable for scripting. mm verify and mm replay
exit 2 when they detect an integrity violation (and 0 when the
history is intact, 1 if the command itself failed), so
mm verify && ... composes in scripts the way git fsck does.
desktop/ is a Tauri v2 + React/TypeScript desktop application — a
presentation/orchestration layer over the exact same Rust crates the
mm CLI uses (it calls machinememory_cli::run directly, in-process;
it never shells out to mm and parses text, and never opens SQLite
itself). See docs/10-rfcs/DESKTOP-APPLICATION.md
for the full architecture.
Screens: Overview (is monitoring active, last/next collection, recent
activity, findings count, history health), Changes (filterable event
timeline), Findings (discovered Operations with evidence tier and
confidence, click through to a detail view backed by mm explain),
Investigate (a UI over mm investigate's time-window semantics), and
System / Health (agent status, staleness, integrity verification).
Developer commands:
cd desktop
npm install
npm run tauri dev # development: Vite dev server + hot reload, requires the dev server running
npm run tauri build # production: bundled frontend, no dev server, no localhost dependencynpm run tauri build is the only correct production build command.
A bare cargo build --release --bin mm-desktop is not sufficient by
itself — it produces a binary Tauri still treats as a dev build (it
always tries to load http://localhost:1420 and fails with
ERR_CONNECTION_REFUSED once no dev server is running), because the
tauri/custom-protocol Cargo feature the CLI enables automatically was
never passed. desktop/src-tauri/build.rs now hard-fails any
--release build missing that feature, so this mistake fails loudly at
compile time instead of shipping a broken binary; see
docs/10-rfcs/DESKTOP-APPLICATION.md
§26 for the full investigation. If you need a raw Cargo build without
the full Tauri CLI (e.g. in CI), use
cargo build --release --bin mm-desktop --features custom-protocol
after npm run build has produced desktop/dist.
npm run tauri build produces three artifacts: the standalone
executable (target/release/mm-desktop.exe — what you launch directly
to run the app unpackaged) and two installers
(target/release/bundle/msi/*.msi,
target/release/bundle/nsis/*-setup.exe) — neither installer is
signed or polished yet (see the RFC's known limitations).
The desktop app resolves its machine_id/db_path through the exact
same config file (machinememory-config) mm/mm-agent use, so all
three agree on where a machine's history lives. It can run one-shot
collection on demand ("Run collection now", spawning the real
mm-agent.exe run-once), but does not start/stop the continuous
mm-agent run background process in this release — see the RFC's
§10/§24 for why, and for the current, honest state of Windows Task
Scheduler background registration (blocked by policy on some
corporate-managed machines; the app surfaces this rather than hiding
it).
This release ships seven collectors (see Collectors above)
and a Signature Tier correlation engine with six rules (see
Correlation rules above; mm correlate/mm explain)
— see
docs/03-architecture/CORRELATION-ENGINE.md
for exactly what "correlation" means today versus what's still unbuilt.
mm compact (see docs/03-architecture/RETENTION.md)
bounds Raw Fact Log growth for superseded, unreferenced history — but this
is a narrower guarantee than "retention is solved":
Deliberately, this release does not include:
- Capability inference, Resource Graphs, generalized/probabilistic correlation, or any AI/NL interface — those are real, planned, and explicitly out of scope until there's real daily-use evidence they're needed.
- Packaging (no
.deb, Homebrew, or installer) — build from source. - Projection Store compaction — persisted Operations, Conclusions, and CorrelationCandidates are never removed and grow without bound; only the Raw Fact Log is compacted.
- Any retention duration or automatic scheduling —
mm compactis a command an operator runs, not an invented time-based policy.
- Every query currently reads a machine's entire history into memory.
Measured on a real Windows development workstation with ~940 real
persisted events (baseline scan across all seven collectors): every
mmcommand, includingcorrelateandinvestigate, completed in well under a second. This is a known, accepted, structural limitation for v0.2.0, not a silent gap — it has not been measured at the scale of years of continuous collection (tens of thousands of events), and nothing bounds that growth (see below). - macOS PATH collection is written but not execution-verified. Linux
(via
/etc/environment) and Windows (registry, SCM, CryptoAPI, and Task Scheduler — all seven collectors) have been run and proven end-to-end on real machines, including agent re-runs reconstructing baselines from persisted history, correlation, explain, investigate, and live tamper detection against an out-of-band-modified database. The Windows-service, driver, certificate, and scheduled-task collectors are Windows-only by design (no equivalent concept on Linux/macOS to collect). The macOS (/etc/paths) code path compiles and is believed correct but has not been run on that platform. SCHEDULED_TASKcollection requires an elevated process to readC:\Windows\System32\Tasks. Runningmm-agentunelevated is not an error — that one collector is skipped and reported as such in the exit summary, while the other six still run normally.- History is retained indefinitely. No compaction or retention policy is implemented yet.
- The hash chain is not tamper-proof against an attacker who has write
access to the database file.
mm verifyreliably catches accidental corruption and unsophisticated tampering (e.g. hand-editing a row without also recomputing every hash after it). It cannot catch an attacker who recomputes the whole chain forward after modifying it —record_hashis an unkeyed, public function, so anyone with write access to the file can regenerate a fully self-consistent forged chain thatmm verifywill still report asVERIFIED. This is a structural property of any self-contained hash chain (the same limitation a Git repository's own commit history has against someone with filesystem access to.git), not a bug in the hash algorithm or verification logic. Closing it for real requires an external trust anchor — a reference to a known-good chain tip kept somewhere the attacker doesn't also control — which does not exist in this release. Full analysis:docs/05-security/THREAT-MODEL.md.
The full architecture — domain model, subsystem contracts, ADRs, and
design rationale — lives in docs/. It's written for
contributors and maintainers, not end users; if you just want to use
Machine Memory, this README is enough.
See docs/08-contributing/ (in progress).
Issues and pull requests welcome.
MIT — see LICENSE.