diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 50b80ff..0254564 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,50 +3,121 @@ Thanks for your interest in improving Diskern! Contributions of all kinds are welcome — bug reports, docs, rules for the safety database, and code. +**New here?** [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) gets the project +running on your machine, including what to install and what the build +errors mean. This file is about what to work on and how to land it. + ## Ground rules Diskern's [safety principles](README.md#principles-non-negotiable) are -non-negotiable. PRs that make scanning mutate state, hard-delete files, or -let a model/network call decide a safety verdict will not be accepted. +non-negotiable. PRs that make scanning mutate state, hard-delete files, +or let a model or network call decide a safety verdict will not be +accepted. + +Two consequences worth stating plainly, because they come up: + +- **A wrong `safe` verdict is a bug of a different class.** `safe` and + `review` are both *actionable* in the app — the UI offers to quarantine + either. A rule that matches more than it means doesn't produce a + cosmetic error, it offers to move someone's data. When in doubt use + `review`, and when still in doubt leave it `unknown`: `report::build` + drops unknown entries, so they never reach the user as an actionable + row. +- **The engine decides, the frontends display.** The CLI and the app both + call the same functions in `diskern-core` and neither contains + scanning or classification logic. A new capability goes into the engine + first, with tests, and the frontends render what it returns. + +## Good first contributions + +**Rules for the safety database.** This is the most useful thing you can +do and the easiest to start on. Diskern only knows what its rules tell +it, and the shipped set is deliberately tiny — thirteen rules covering +Chrome, Firefox and pip caches, Rust and Node build output, system logs +and crash dumps, installer packages, temp directories, and a few +protected Windows paths. Everything else on your disk classifies as +`unknown`, and `report::build` drops those, so it never reaches the user +at all. + +If you know where an application on your platform keeps its regenerable +cache, that is a rule nobody else can write as well as you. Format, +verdict levels and the safety reasoning are in +[docs/RULES.md](docs/RULES.md); the rules themselves are JSON in +[`crates/diskern-core/rules/base.json`](crates/diskern-core/rules/base.json), +and adding one needs no Rust. + +**Issues labelled `good first issue`** are scoped to a single file with +the reasoning already worked out. -## Getting started +**Documentation.** If something here or in `docs/` was wrong or missing +when you followed it, that is a bug report worth filing even if you +don't fix it. -```sh -# Engine + CLI — no GUI dependencies needed -cargo test -p diskern-core -cargo run -p diskern-cli -- scan ~/Downloads +## The loop -# Desktop app (needs Tauri v2 prerequisites) -cd app && npm install && npm run tauri dev -``` +1. Fork, and branch from `main`. Branch names follow the commit prefixes + below: `fix/…`, `feat/…`, `docs/…`, `ci/…`. +2. Make the change, with a test that fails without it. +3. Run [what CI runs](docs/DEVELOPMENT.md#5-running-the-tests). +4. Open the PR. -See each section's README for details: -[`crates/diskern-core`](crates/diskern-core/README.md) · -[`crates/diskern-cli`](crates/diskern-cli/README.md) · -[`app`](app/README.md) · [`site`](site/README.md) +### Commits -## Before you open a PR +`type(scope): summary in the imperative`, where type is `feat`, `fix`, +`docs`, `test`, `refactor`, `perf`, `style`, `chore` or `ci`, and scope +is usually the module or crate (`rules`, `scanner`, `app`, `cli`). -1. `cargo fmt --all` — CI enforces formatting. -2. `cargo clippy --workspace` — fix new warnings. -3. `cargo test --workspace` — all tests green. -4. Keep commits small and focused; one logical change per commit. +One logical change per commit. The body matters more than the summary: +say what was wrong and why the fix is the right shape, not what the diff +already shows. `git log` in this repo is the reference — the useful +messages explain a decision someone would otherwise have to re-derive. -CI runs the same checks on every PR, plus a spell checker -([typos](https://github.com/crate-ci/typos), config in `_typos.toml`), -a markdown link checker, and lint/build of the site and app frontends. -A weekly audit workflow additionally scans dependencies for RustSec -advisories and docs for dead external links. +### Pull requests -## Adding safety rules +The [template](.github/PULL_REQUEST_TEMPLATE.md) asks for what and why. +Include the reasoning you'd want if you were reviewing it cold: what +breaks without the change, what you considered and rejected, and +anything you're unsure about — an explicit "I'm not certain this is the +right layer" gets a better review than silence. -Rules live in [`crates/diskern-core/rules/base.json`](crates/diskern-core/rules/base.json) -and are the main way to contribute — see [docs/RULES.md](docs/RULES.md) -for the format, verdict levels, and guidelines. Prefer conservative -verdicts: when in doubt, use `review` rather than `safe`. +If your change alters a verdict, say so in the description. Verdicts are +what the app acts on, so a rule or risk change is a behaviour change +even when the diff looks like data. + +Before you open it: + +1. `cargo fmt --all` +2. `cargo clippy -p diskern-core -p diskern-cli --all-targets -- -D warnings` +3. `cargo test -p diskern-core -p diskern-cli` +4. Update [CHANGELOG.md](CHANGELOG.md) under `Unreleased` if the change + is user-visible. + +`--workspace` instead of `-p …` also builds the desktop app, which needs +the [platform webview +dependencies](docs/DEVELOPMENT.md#1-what-you-need). CI compiles the app +separately, so scoping to the two crates locally is fine. + +## Where things go + +| You want to… | Goes in | +| --- | --- | +| Teach Diskern about a new cache or artifact directory | [`rules/base.json`](crates/diskern-core/rules/base.json) | +| Change how a verdict is decided | `crates/diskern-core/src/{rules,risk,graph}.rs` | +| Change what a scan finds or how it reports | `crates/diskern-core/src/{scanner,dedup,report}.rs` | +| Change what quarantine does | `crates/diskern-core/src/actions.rs` — the only module that writes | +| Add a terminal flag or change CLI output | `crates/diskern-cli/src/main.rs` | +| Change the app's UI | `app/src/` | +| Expose engine behaviour to the app | `app/src-tauri/src/commands.rs`, then `app/src/` | + +[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) walks a scan through the +engine stage by stage if you want the fuller picture first. ## Reporting bugs Open an issue with your OS, the command or app action you ran, and what -you expected vs. what happened. For security issues, see -[SECURITY.md](SECURITY.md) — please don't open a public issue. +you expected versus what happened. For a wrong verdict, the path that +was misclassified and what it actually is are the two things that make +it fixable — a `diskern scan --json` excerpt is ideal. + +For security issues, see [SECURITY.md](SECURITY.md) — please don't open +a public issue. diff --git a/README.md b/README.md index 5cd995e..64be34b 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,9 @@ site/ landing page (GitHub Pages) docs/ architecture and release docs ``` -Project docs live in [docs/](docs/README.md) — architecture, the -[rules database](docs/RULES.md), an [FAQ](docs/FAQ.md), and release +Project docs live in [docs/](docs/README.md) — [running +locally](docs/DEVELOPMENT.md), architecture, the [rules +database](docs/RULES.md), an [FAQ](docs/FAQ.md), and release instructions. Each section has its own README: [core](crates/diskern-core/README.md) · [cli](crates/diskern-cli/README.md) · @@ -42,14 +43,18 @@ instructions. Each section has its own README: ## Development ```sh -# Engine + CLI (no GUI deps needed) -cargo test -p diskern-core +# Engine + CLI — no GUI dependencies needed +cargo test -p diskern-core -p diskern-cli cargo run -p diskern-cli -- scan ~/Downloads -# Desktop app (needs Tauri v2 prerequisites: https://v2.tauri.app/start/prerequisites/) +# Desktop app cd app && npm install && npm run tauri dev ``` +[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) has the full setup: what to +install per platform, how to run what CI runs, and what the build +failures mean. + ## Releasing See [docs/RELEASING.md](docs/RELEASING.md). Short version: set the two diff --git a/app/README.md b/app/README.md index 2eb5bdd..3890d96 100644 --- a/app/README.md +++ b/app/README.md @@ -6,20 +6,39 @@ Tauri v2 desktop app: React frontend ([`src/`](src)) over the Rust engine ## Develop Needs the [Tauri v2 prerequisites](https://v2.tauri.app/start/prerequisites/) -(Rust toolchain + platform WebView deps), then: +(Rust toolchain + platform WebView deps) — see +[docs/DEVELOPMENT.md](../docs/DEVELOPMENT.md) for the per-platform list +and the errors you get without them. Then: ```sh npm install npm run tauri dev ``` +`npm run build` compiles the frontend alone, without touching Rust, +which is enough to check a JSX change. + ## How it talks to the engine The frontend never touches the filesystem itself. It invokes Tauri commands defined in [`src-tauri/src/commands.rs`](src-tauri/src/commands.rs), -which call into [`diskern-core`](../crates/diskern-core). During a scan the -backend emits `scan-progress` events (~every 150ms) so the UI can show a -live file counter. +which call into [`diskern-core`](../crates/diskern-core). During a scan +the backend emits `scan-progress` events (~every 150ms) so the UI can show +a live file counter. + +| Command | Does | +| --- | --- | +| `start_scan` | Read-only scan; returns a report, or `null` if cancelled | +| `cancel_scan` | Stops the scan in flight | +| `quarantine_finding` | Moves one file to quarantine, re-classifying server-side first | +| `list_quarantine` | Everything currently quarantined, read from the manifest | +| `restore_quarantined` | Puts one file back where it came from | +| `purge_quarantine` | Empties quarantine for good — the only deletion in the app | + +Everything except `quarantine_finding` and `purge_quarantine` is +read-only. Quarantine is manifest-backed, so what was moved in one +session is still restorable in the next; the Quarantine panel renders +before any scan has been run for exactly that reason. ## Updater diff --git a/crates/diskern-cli/README.md b/crates/diskern-cli/README.md index e50afe0..62aeaed 100644 --- a/crates/diskern-cli/README.md +++ b/crates/diskern-cli/README.md @@ -21,8 +21,9 @@ diskern scan ~/Downloads --json ### Output Findings are grouped by verdict (safest first), then by category within -each verdict, with the rule that matched printed underneath as the -evidence: +each verdict, with every reason printed underneath as the evidence — the +rule that matched, and anything that changed the verdict from the rule's +own: ```text Scanned 84,213 files. @@ -32,9 +33,21 @@ Safe to remove — 802 findings · 6.1 GB Browser cache · 641 · 4.8 GB 412.0 MB /home/u/.cache/google-chrome/Default/Cache/data_2 matched rule chrome-cache: Chrome browser cache. … + not accessed in 210 days … … … 636 more + +Risky — not recommended — 3 findings · 0 B + Build artifacts · 3 · 0 B + 0 B /home/u/work/api/node_modules/.package-lock.json + matched rule node-modules: Node.js dependencies. … + referenced by 3 projects ``` +`referenced by 3 projects` is the impact graph: three live projects have +that `node_modules` on their dependency path, so the verdict drops from +`review` to `risky` and its bytes stop counting as reclaimable — +nothing will offer to move it. + | Flag | Default | Effect | | ----------- | ------- | ------------------------------------------------------- | | `--top N` | `5` | Findings shown per category; `0` shows every one. | diff --git a/crates/diskern-core/README.md b/crates/diskern-core/README.md index f6ff8b9..196aa6c 100644 --- a/crates/diskern-core/README.md +++ b/crates/diskern-core/README.md @@ -15,11 +15,17 @@ and tests alike. ## Pipeline ```text -scanner ──► index ──► dedup ──► graph ──► rules + risk ──► report - │ - (optional) ai narration +scanner ──► graph ──► rules + risk ──► dedup ──► report + │ + (optional) ai narration ``` +Dedup runs *after* classification: a duplicate set is an offer to keep +one copy and drop the rest, so entries nothing will act on have no place +in one — and hashing them is the most expensive way to produce a number +nobody can use. [`report::build_with`](src/report.rs) is the whole +sequence in one function. + ## Modules | Module | Purpose | diff --git a/crates/diskern-core/src/lib.rs b/crates/diskern-core/src/lib.rs index 2cbd5a8..25a5013 100644 --- a/crates/diskern-core/src/lib.rs +++ b/crates/diskern-core/src/lib.rs @@ -12,10 +12,19 @@ //! ## Pipeline //! //! ```text -//! scanner ──► index ──► dedup ──► graph ──► rules + risk ──► report -//! │ -//! (optional) ai narration +//! scanner ──► graph ──► rules + risk ──► dedup ──► report +//! │ +//! (optional) ai narration //! ``` +//! +//! Dedup runs *after* classification, not before: a duplicate set is an +//! offer to keep one copy and drop the rest, so entries nothing will act +//! on — protected system files, stores a live project references — have +//! no business in one, and hashing them is the most expensive way to +//! produce a number nobody can use. The graph runs first because the +//! verdict it feeds ([`risk::downgrade`]) is part of deciding that. +//! +//! [`report::build_with`] is where the whole sequence lives. pub mod actions; pub mod dedup; diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 9d3c912..65ee1ea 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -23,9 +23,87 @@ marketing site. | `crates/diskern-cli` | `diskern` binary — engine from the terminal | | `app/` | Tauri v2 desktop app (React frontend, Rust backend) | | `site/` | Landing page, deployed to GitHub Pages | -| `docs/` | Project docs (this file, [RELEASING.md](RELEASING.md)) | +| `docs/` | Project docs — [running locally](DEVELOPMENT.md), [rules](RULES.md), [releasing](RELEASING.md) | | `.github/workflows/` | CI, release builds, Pages deploy | +## A scan, end to end + +One function orchestrates almost all of it: +[`report::build_with`](../crates/diskern-core/src/report.rs). Reading it +alongside this section is the fastest way into the engine. + +```text +scanner ──► graph ──► rules + risk ──► dedup ──► report +``` + +**1. Walk.** [`scanner::scan`](../crates/diskern-core/src/scanner.rs) +walks the roots in parallel with `jwalk`, skipping excluded directories, +and returns a `FileEntry` per file: path, size, modified and accessed +times, whether it is a symlink. Metadata only — nothing is read or +hashed here, and nothing is written ever. + +**2. Graph.** +[`graph::ImpactGraph::from_entries`](../crates/diskern-core/src/graph.rs) +makes one pass looking for two things: directories holding a project +marker (`Cargo.toml`, `package.json`, `pyproject.toml`), and directories +that are dependency stores (`target`, `node_modules`, a virtualenv). It +links each project to the store it owns, so the engine can later answer +"how many live projects reference this?". + +**3. Classify.** [`rules::RulesDb::classify`](../crates/diskern-core/src/rules.rs) +matches the normalized path against the rule globs — first match wins, +which is why `protected` rules are listed first — yielding a `Category` +and a base `Verdict`. [`risk::downgrade`](../crates/diskern-core/src/risk.rs) +then applies the graph's answer. Evidence can only make a verdict *more* +cautious, never less, and `Protected` is final. + +**4. Dedup.** [`dedup::find_duplicates_filtered`](../crates/diskern-core/src/dedup.rs) +buckets by size, BLAKE3-hashes only the files whose sizes collide (which +skips most of a real disk), then buckets by hash. It runs after +classification so that entries nothing will act on can sit it out — they +have no place in an offer to keep one copy and drop the rest, and +hashing them is the most expensive way to produce an unusable number. + +**5. Report.** [`risk::assess`](../crates/diskern-core/src/risk.rs) adds +an informational score and per-file evidence, and each entry becomes a +`Finding` carrying its category, verdict, reclaimable bytes and the +`reasons` that justify them. The headline total counts findings in full +and adds only the duplicate copies nothing has counted yet. + +Acting on a finding is a separate call: +[`actions::quarantine`](../crates/diskern-core/src/actions.rs) is the +only function in the crate that writes, it refuses `Risky` and +`Protected`, and it records every move in a manifest so a restore +survives the process exiting. + +## Adding a feature + +Work out which layer owns it before writing anything — the answer is +usually further down than it first looks. + +**Does a rule cover it?** Teaching Diskern that some directory is a cache +is data, not code: add it to +[`rules/base.json`](../crates/diskern-core/rules/base.json) with a test. +No Rust, no new code paths, and it reaches both frontends at once. + +**Does it change a verdict?** Then it belongs in `rules`, `risk` or +`graph`, and the constraint in [design decisions](#design-decisions) +applies: evidence may only make a verdict more cautious. Add the evidence +as a `reason` too — a verdict the user can't see the basis for is the +thing Diskern exists not to ship. + +**Does it change what a scan finds or reports?** `scanner`, `dedup` and +`report`. Watch the cancellation flag: anything that loops over every +entry has to check it, or a Cancel arriving during that stage does +nothing. + +**Only then, the frontends.** Add a Tauri command in +[`commands.rs`](../app/src-tauri/src/commands.rs) and register it in +[`lib.rs`](../app/src-tauri/src/lib.rs), or a flag in +[`main.rs`](../crates/diskern-cli/src/main.rs). Both should be thin +enough that the interesting part of your change is already tested in the +engine before either sees it. + ## Design decisions **Frontends are thin.** The CLI and the app both call the same engine diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md new file mode 100644 index 0000000..aac8bda --- /dev/null +++ b/docs/DEVELOPMENT.md @@ -0,0 +1,182 @@ +# Running Diskern locally + +Everything you need to get the engine, the CLI, the desktop app and the +site running on your own machine, and what to do when one of them +doesn't. + +If you only want to change the [rules database](RULES.md) — which is the +most useful contribution there is — you need **step 1 and step 2 only**. +The rules are JSON, the tests are pure functions, and none of it needs a +GUI toolchain. + +## 1. What you need + +| For | You need | +| --- | --- | +| Engine + CLI | A Rust toolchain (stable, edition 2021) and a C linker | +| Desktop app | The above, plus Node 20+ and your platform's WebView deps | +| Site | Node 20+ | + +Rust comes from [rustup](https://rustup.rs). Everything in the workspace +builds on current stable; there is no pinned toolchain file, and CI +tracks `stable`. + +**A C linker is not optional.** Rust needs one to link every binary, +including build scripts and proc macros, so a machine with `rustc` but no +linker cannot even run `cargo check`. The error is `linker 'cc' not +found`. + +```sh +# Debian / Ubuntu +sudo apt-get install build-essential +# Fedora +sudo dnf install gcc +# macOS — the Command Line Tools, which also carry `cc` +xcode-select --install +# Windows: install the "Desktop development with C++" workload for MSVC, +# or use the GNU toolchain via rustup. +``` + +For the **desktop app**, Tauri v2 needs your platform's webview +development files. macOS (WKWebView) and Windows (WebView2) ship theirs +with the OS; Linux does not: + +```sh +# Debian / Ubuntu — the same line CI and the release build use +sudo apt-get install libwebkit2gtk-4.1-dev libappindicator3-dev \ + librsvg2-dev patchelf +``` + +Other distributions are covered by the +[Tauri v2 prerequisites](https://v2.tauri.app/start/prerequisites/). + +## 2. The engine and the CLI + +No GUI dependencies. This is the fast loop, and the one to stay in while +you work on the engine: + +```sh +cargo test -p diskern-core -p diskern-cli +``` + +Then point it at something real. Scanning is read-only — it never +modifies, moves or deletes anything, so this is safe to run anywhere: + +```sh +cargo run -p diskern-cli -- scan ~/Downloads +``` + +Useful while developing: `--top 0` shows every finding rather than five +per category, `--verdict risky` narrows to one verdict, and `--json` +gives the whole report for piping into `jq`. See the +[CLI README](../crates/diskern-cli/README.md). + +## 3. The desktop app + +```sh +cd app +npm install +npm run tauri dev +``` + +The first build compiles the whole Tauri dependency tree and takes a +while; later ones are incremental. `npm run tauri dev` starts Vite and +the Rust backend together and reloads the frontend on save — a change to +`src-tauri/` restarts the backend, which is slower. + +To check only that the frontend compiles, `npm run build` skips Rust +entirely. + +## 4. The site + +```sh +cd site +npm install +npm run dev # local preview +npm run lint # oxlint, same as CI +``` + +## 5. Running the tests + +```sh +# What CI runs, and what your change has to pass +cargo test -p diskern-core -p diskern-cli +cargo fmt --check +cargo clippy -p diskern-core -p diskern-cli --all-targets -- -D warnings +``` + +`cargo test --workspace` also builds `diskern-app`, which needs the +webview dependencies from step 1. Without them it fails in a `*-sys` +build script — that is a missing system package, not a broken test. +Scope to the two crates instead. + +Tests live beside the code they cover, in `#[cfg(test)] mod tests` at the +bottom of each module. `crates/diskern-core/src/report.rs` has the +fullest examples: they build a small tree in a `tempfile::tempdir()`, +scan it, and assert on the report. + +Two things worth knowing before you write one: + +- **Don't rely on the embedded rules in a report test.** A temp directory + lives under `/tmp` on Linux and `%LOCALAPPDATA%\Temp` on Windows, both + of which the shipped rules match — so every fixture file classifies as + a temp file and your assertions quietly measure the wrong thing. Build + a small `RulesDb::new(..)` with only the rules the test is about; + `temp_rules()` in `report.rs` is the pattern. +- **Compare paths, not strings containing paths.** `Path::join` and `==` + work on every platform; `contains("a/b")` finds nothing on Windows. + +## 6. What CI runs + +Every pull request gets: + +| Check | What it does | +| --- | --- | +| `lint` | `cargo fmt --check` and clippy with `--all-targets` (tests included) | +| `test (ubuntu / windows / macos)` | The engine and CLI suite on all three platforms | +| `app-tauri (ubuntu / windows / macos)` | Compiles the Tauri shell; only on changes under `crates/`, `app/` or the workflow | +| `app-frontend`, `site` | Vite builds, plus oxlint for the site | +| `typos` | Spell-checks code and docs — config in [`_typos.toml`](../_typos.toml) | +| `doc-links` | Relative links between markdown files resolve | + +The engine tests run on three platforms because the engine is +platform-specific in places: the rules database describes Windows and +macOS paths, `scanner::default_excludes` has a `cfg` arm per platform, +and quarantine has to flatten drive letters that only exist on Windows. + +A weekly [audit workflow](DEPENDENCY-AUTOMATION.md) additionally checks +dependencies against RustSec advisories and docs for dead external links. + +## 7. When it doesn't build + +| Symptom | Cause | +| --- | --- | +| `linker 'cc' not found` | No C toolchain — see step 1. | +| `error: the 'cargo' binary ... is not applicable` | A partial rustup toolchain. `rustup toolchain uninstall stable && rustup toolchain install stable`. | +| ``The system library `glib-2.0` ... was not found`` | Building the app without the Linux webview packages from step 1. It names `glib-2.0` rather than webkitgtk because `glib-sys` is the first `*-sys` crate to fail; installing the four packages fixes all of them. Scope to `-p diskern-core -p diskern-cli` if you didn't mean to build the app. | +| `The pkg-config command could not be found` | Same cause, one step earlier: `pkg-config` itself is missing. | +| `failed to resolve ../dist` from `tauri-build` | The frontend hasn't been built. `npm run build` in `app/` first, or use `npm run tauri dev`, which does it for you. | +| Tests pass locally, fail on Windows CI | Almost always a path assumption — see the two notes in step 5. | + +### Building without a local toolchain + +If you can't install a C compiler on the machine you're working on — +no root, a locked-down box — the engine and CLI build fine in a +container: + +```sh +podman run --rm -v "$PWD":/work:z -w /work docker.io/library/rust:1-slim \ + cargo test -p diskern-core -p diskern-cli +``` + +Add `-v cargo-cache:/usr/local/cargo/registry` to keep the dependency +cache between runs. The image ships neither `clippy` nor `rustfmt`, so +add `rustup component add clippy rustfmt &&` before the `cargo` call when +you need them, and building the *app* this way additionally needs the +webview packages installed into the image. + +Rootless Podman maps the container's root to your own user, so `target/` +comes out owned by you. Rootful Docker does not — it leaves a +root-owned `target/` in your checkout. Mount it elsewhere if that is +what you have: +`-v "$PWD/../diskern-target":/work/target`. diff --git a/docs/FAQ.md b/docs/FAQ.md index b8b80e3..d296445 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -2,8 +2,10 @@ **Can Diskern delete my files?** Not directly. Scanning is read-only, and cleanup actions only move files -to a quarantine folder you can review and restore from. Emptying -quarantine is a separate, explicit step. +to a quarantine folder you can review and restore from. Every move is +recorded in a manifest inside that folder, so what you quarantined today +is still restorable after a restart. Emptying quarantine is a separate, +explicit step, and it removes only the files the manifest lists. **Does it send my data anywhere?** No. Scanning, classification, and risk scoring are fully local. The @@ -25,5 +27,7 @@ Same engine either way. The CLI (`diskern scan `) suits scripting and remote machines; the app adds visual review and quarantine handling. **Which platforms are supported?** -The engine and CLI build anywhere Rust does. The desktop app targets -Windows, macOS, and Linux via Tauri v2. +The engine and CLI build anywhere Rust does, and their test suite runs on +Linux, Windows and macOS. The desktop app is built and released for +Linux and Windows; macOS builds from source and is tested in CI, but no +signed macOS release ships yet. diff --git a/docs/README.md b/docs/README.md index 12f3aaf..e935b6b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,7 +2,8 @@ | Doc | What it covers | | -------------------------------- | ----------------------------------------------- | -| [ARCHITECTURE.md](ARCHITECTURE.md) | Workspace layout and key design decisions | +| [DEVELOPMENT.md](DEVELOPMENT.md) | Running it locally: setup, tests, CI, troubleshooting | +| [ARCHITECTURE.md](ARCHITECTURE.md) | Workspace layout, a scan end to end, where a feature goes | | [RULES.md](RULES.md) | Rules database format and how to contribute rules | | [FAQ.md](FAQ.md) | Common questions about safety and privacy | | [RELEASING.md](RELEASING.md) | Cutting a release; updater signing setup | diff --git a/typos b/typos new file mode 100755 index 0000000..b8cc14b Binary files /dev/null and b/typos differ