Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d274e0d
Draw the clean surface, so a removal is chosen from a list of what wa…
vecnode Oct 17, 2026
2d74d0c
Check three platforms at the cut, and record what the clean surface is
vecnode Oct 17, 2026
bd62c9c
Put an icon in the status area, and open on paper
vecnode Oct 17, 2026
b3f2dfd
Write one autostart entry, and show exactly where it is
vecnode Oct 17, 2026
ff1aa2e
Let a second launch raise the window instead of opening another one
vecnode Oct 17, 2026
1d5b21a
Repair the encoding that three commits mangled, and stop writing sour…
vecnode Oct 17, 2026
079b827
Record what a resident run costs, one line a minute
vecnode Oct 17, 2026
0d5ea8e
Let a scheduled scan wait on battery, only when asked
vecnode Oct 17, 2026
106b0d4
Refuse a pack before it is believed, and say which line to fix
vecnode Oct 17, 2026
da35649
Verify the signature before believing the pack
vecnode Oct 17, 2026
050a6d0
Ship the catalogue as a pack, and check it with the product's own val…
vecnode Oct 17, 2026
f3a0ec5
Write the authoring guide a pack author actually needs
vecnode Oct 17, 2026
ee179a8
Define the record activation will write, and refuse one that was edited
vecnode Oct 17, 2026
2265f74
Hold the keys ranger trusts in a file a person can read
vecnode Oct 17, 2026
6e93455
Do not refuse to start because one file could not be written
vecnode Oct 17, 2026
3962ed2
Make the overview read like an inspector, not a dashboard
vecnode Oct 17, 2026
718eba5
Let a pack fire: a typed field table, places and a matcher
vecnode Oct 18, 2026
5735606
Activate a pack, roll it back, and journal both ways
vecnode Oct 18, 2026
94b1b35
Show the rule packs, and make the window read like an inspector
vecnode Oct 18, 2026
09da7eb
Read a Sigma rule, and refuse the part that does not map
vecnode Oct 18, 2026
d7b4a36
Put the content scan behind a feature the default build cannot contain
vecnode Oct 18, 2026
e93ec3b
Cut 0.1.6, and say what it does not contain
vecnode Oct 18, 2026
a74aa6e
Read a Sigma file the way an editor on Windows writes one
vecnode Oct 18, 2026
ac4753a
Give a pack activation an id its result can find
vecnode Oct 18, 2026
ee36e52
Format the byte-order-mark test the way the rest of the tree is
vecnode Oct 18, 2026
f8bfbb9
Say which refusal this is, instead of only that it happened
vecnode Oct 18, 2026
3a59394
Read the Windows registry as a table, and make a ServiceDll mean some…
vecnode Oct 18, 2026
91331dc
Read six more surfaces, and keep a multi-string value a list
vecnode Oct 18, 2026
5b22b46
Read scheduled tasks, and stop shipping the EICAR string in the binary
vecnode Oct 18, 2026
368d837
Generate the coverage table, and refuse a rule nothing can fire
vecnode Oct 18, 2026
9350a22
Stop the release binary carrying the test signature
vecnode Oct 18, 2026
69c656d
Read the agent stack, and give the agent subject something to match
vecnode Oct 18, 2026
fb916a5
Catch the accessibility swap and the unquoted service path
vecnode Oct 18, 2026
4cbe634
Give the content scan a consent screen, and keep it off the frame's t…
vecnode Oct 18, 2026
095c69d
Check the binary for the test file instead of claiming it is clean
vecnode Oct 18, 2026
7c507f2
Read the Linux loader surfaces, from files a real machine gave us
vecnode Oct 18, 2026
947ec93
Read the COM in-process servers, a thousand of them at a time
vecnode Oct 18, 2026
7739600
One ignore rule for every scratch profile a test run invents
vecnode Oct 18, 2026
675f6c3
Count the clean dry run off the frame's thread, and say what would go
vecnode Oct 18, 2026
3cb09f3
Read the macOS loader surfaces, and write the 0.1.7 plan
vecnode Oct 18, 2026
0352014
Let the other two platforms have their say about two tests
vecnode Oct 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
name: CI

# Three operating systems, but not on every commit.
#
# A push to main runs one job, on the platform a Windows development machine cannot check
# for itself. The full matrix runs on a pull request, on the Monday schedule, when asked,
# and — through `workflow_call` — as the gate a release passes before it publishes
# anything. That is the whole policy: the check that must never be skipped is the one at
# the cut, and it is the cut that now pays for it.
on:
push:
branches: [main]
Expand All @@ -14,6 +21,13 @@ on:
- 'docs/**'
- 'assets/logo/**'
- 'assets/screenshots/**'
schedule:
# Monday, 04:17 UTC. Early enough in the week that a platform-only break has six days
# to be found before the next one, and off the hour so it does not join the queue of
# every other project's nightly.
- cron: '17 4 * * 1'
workflow_dispatch:
workflow_call:

# A superseded push has nothing to say. Cancel it and let the cache serve the next one.
concurrency:
Expand All @@ -28,15 +42,33 @@ env:
CARGO_INCREMENTAL: '0'

jobs:
# The matrix is chosen by the event rather than written twice, so the caller and the
# push cannot disagree about what "checked" means.
plan:
name: Choose the matrix
runs-on: ubuntu-latest
timeout-minutes: 2
outputs:
os: ${{ steps.pick.outputs.os }}
steps:
- id: pick
run: |
if [ "${{ github.event_name }}" = "push" ]; then
echo 'os=["ubuntu-latest"]' >> "$GITHUB_OUTPUT"
else
echo 'os=["ubuntu-latest","windows-latest","macos-latest"]' >> "$GITHUB_OUTPUT"
fi

check:
name: ${{ matrix.os }}
needs: plan
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
# Ubuntu latest for the quick check; the release workflow uses 22.04 for glibc.
os: [windows-latest, ubuntu-latest, macos-latest]
os: ${{ fromJSON(needs.plan.outputs.os) }}
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -64,3 +96,22 @@ jobs:
# core and the native collectors, and type-checks the UI and shell.
- name: Check
run: cargo xtask ci

# A `#[cfg]` bug lives on the platform that is not being compiled, and this repository
# has just had two: a path that only resolves on Unix, and a helper only Windows called.
# The Windows runner is the one that can type-check the Linux code without spending a
# matrix slot on it.
- name: Type-check the other two platforms from Windows
if: runner.os == 'Windows'
run: |
rustup target add x86_64-unknown-linux-gnu x86_64-apple-darwin
cargo clippy --workspace --all-targets --target x86_64-unknown-linux-gnu -- -D warnings
# The macOS collectors compile without a Mac; only the linking and the running
# need one, and `check` does neither.
cargo clippy --package vnr-native --all-targets --target x86_64-apple-darwin -- -D warnings

# The optional features are deliberately not in the default build, which is exactly
# why they need their own step: code behind a feature flag is code no default check
# compiles. This is the content scan, and it pulls a pattern engine with it.
- name: Optional features
run: cargo xtask features
10 changes: 10 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@ env:
GH_REPO: ${{ github.repository }}

jobs:
# A tag is a promise that the commit it names was checked on three platforms. The gate
# calls the same workflow a pull request runs rather than a copy of it, so the two cannot
# drift: a red check stops the release here, before a draft exists to be published.
gate:
name: Gate
uses: ./.github/workflows/ci.yml
permissions:
contents: read

# Create the release once, as a draft. It becomes visible only after every archive
# and SHA256SUMS.txt are attached, so a half-finished release cannot be installed.
prepare:
name: Prepare release
needs: gate
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
Expand Down
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ desktop.ini
/scratch/
*.local.toml

# Scratch profile for a local test run: the data directory the harness writes
/.tmp-ci/
# Scratch profiles for a local test run: the data directory a harness writes, and the
# throwaway profile a report is written into. They hold this machine's facts - process
# paths, listeners, packages - so they never belong in a commit.
# `probe`, `bench` and any other scratch directory a test run invents start with this
# prefix, so one rule covers what three did and the next one is covered before it exists.
/.tmp-*/

# A commit message written to a file, so a long one survives the shell
/.git-commit-msg.txt
Expand Down
43 changes: 29 additions & 14 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ crates/
core vnr-core pure and IO-free: facts, snapshots, timelines, diffs, rules,
findings, hash signatures, reports, time arithmetic,
journal records, recovery verdicts, pack schema and matcher,
clean policy, schedule arithmetic, the Sigma converter
the allowlist table and its refusals (`action.rs`), the field
vocabulary and the pack schema (`fields.rs`, `pack/`),
schedule arithmetic
native vnr-native the only crate that touches the world:
collect/{processes,autoruns,listeners,packages,tasks,agentstack},
hash, signatures, elevation, paths, store, watch, scan,
journal, action, quarantine, clean, pack, tray, autostart
ui vnr-ui egui: theme, fonts, icons, widgets, the views, World/Action
journal, act, action, quarantine, clean, pack, tray, autostart
ui vnr-ui egui: theme, fonts, icons, widgets, the views, World/Action,
and the clean card's selection state
apps/
vn-ranger the eframe shell: owns the World, runs the worker,
scheduler and tray, carries out actions, and the headless
Expand All @@ -45,7 +48,7 @@ assets/
fonts/ Inter and JetBrains Mono, OFL texts, type notes
screenshots/ the picture the README uses
packs/ the first-party rule pack, signed like any other (from 0.1.6)
docs/ PLAN, RELEASES, BENCHMARKS, and RULES (from 0.1.6)
docs/ PLAN, RELEASES, BENCHMARKS and RULES
```

**Layering is a rule, not a suggestion.**
Expand Down Expand Up @@ -123,9 +126,11 @@ act on — never a panic.
not a trade-off to be weighed; it is out of scope.
- Rule packs and signature databases are local files. They cannot fetch, and rules are
data, not code.
- One local socket is allowed, and only this one: the Linux tray's status-item connection
to the session bus, in `native/src/tray/linux.rs`. `xtask` asserts that it is the only
socket use in the tree, so "local IPC" cannot quietly become something else.
- One local socket exists anywhere near the product, and it belongs to the tray's Linux
backend: the status-item connection to the session bus, opened by `ksni` (pure-Rust
`zbus`), which is reached through `tray-icon`'s `ksni` feature. No file in this tree
opens a socket, `xtask` asserts that, and the dependency that does is the one named in
§8 — so "local IPC" cannot quietly become something else.

### Never hide

Expand Down Expand Up @@ -170,6 +175,9 @@ cargo xtask language # the §5.5 language audit, as a check
cargo run --release # the ranger
cargo run --release -- --report report.json # scan without a window
cargo run --release -- --soak soak.ndjson # sample a 24-hour run (from 0.1.6)
cargo run --release -- --diagnose # where the files are, and whether this
# program may write them
cargo run --release -- --sigma rule.yml # import one Sigma rule as a pack to sign
```

The first build is the slow one: egui, wgpu and winit are most of the tree. After that,
Expand Down Expand Up @@ -206,8 +214,11 @@ capability report.

## 5. Platform rules

CI builds on three platforms, and two of them will disagree with your machine. An
attribute on a **statement** deletes it on the other platforms, which can leave a
CI builds on three platforms, and two of them will disagree with your machine. A push to
`main` checks Linux only; a pull request, the Monday run and the release gate check all
three. So a `#[cfg]` bug can live on `main` until the next Monday or until the cut, and
the target check below is how you find it on the day you write it rather than the day you
tag. An attribute on a **statement** deletes it on the other platforms, which can leave a
binding unused, a variable unread or a branch unreachable:

```rust
Expand Down Expand Up @@ -291,11 +302,15 @@ Every new dependency is a licence, a supply chain and a size decision.
- Not allowed: GPL, AGPL, LGPL without a written exception, CDLA, SSPL, BUSL, or
anything whose licence text has to be guessed.
- No network crate in v0.x: `reqwest`, `ureq`, `hyper`, `h2`, `curl`, `isahc`, `surf`,
`attohttpc`, `socket2` and raw socket code are banned. The one local socket the Linux
tray needs is named in §2 and asserted by `xtask`.
`attohttpc`, `socket2` and raw socket code are banned. The one local socket the tray
needs is named in §2 and asserted by `xtask`.
- No C or C++ build dependency in the default build. A crate that needs one has to
justify it; `netstat2` is Windows-only here for exactly that reason, and the Linux tray
uses `ksni` rather than `tray-icon` precisely to avoid GTK3 and `libappindicator`.
takes `tray-icon` with `default-features = false, features = ["ksni"]` precisely so that
no GTK3 and no `libappindicator` is compiled in. Both halves of that are a decision:
`tray-icon` (MIT OR Apache-2.0) for one menu API on three platforms, and its `ksni`
backend, which speaks StatusNotifierItem through the pure-Rust `zbus` and needs no
toolkit installed on the user's machine.
- OS access goes through a safe wrapper (`winreg`, `netstat2`, `is_elevated`, `runas`,
`sysinfo`, `plist`, `cap-std`), never through a hand-written FFI block.
- **The action path's dependency is `cap-std`**, and it is not negotiable for
Expand Down Expand Up @@ -346,10 +361,10 @@ A change is done when:
| Rules and findings | `core/src/rules.rs` | A rule that cannot name a benign explanation is not ready; every finding carries the evidence it fired on and a stable subject for suppressions. |
| The action path | `native/src/action.rs`, `quarantine.rs`, `clean.rs` | The only code that can destroy data. `cap-std`, four refusals, a journal line before every act, and a test seam for TOCTOU — never a race. |
| The journal | `native/src/journal.rs`, `core/src/journal.rs` | Append-only, `sync_data`, a torn tail tolerated, a rotation that keeps seq monotone, and a recovery verdict for four on-disk states. |
| The allowlist | `native/src/clean.rs`, `core/src/clean.rs` | A table, not a heuristic; the walk never follows a link or crosses a device, and a file changed since the preview is skipped. |
| The allowlist | `native/src/clean.rs`, `core/src/action.rs` | A table, not a heuristic; the walk never follows a link or crosses a device, and a file changed since the preview is skipped. |
| Packs | `core/src/pack/`, `native/src/pack/` | Verified before parsed; a closed vocabulary; an unknown field is an error; activation and rollback are journal entries. |
| The Sigma reader | `core/src/sigma/` | YAML is user input: a restricted reader that refuses anchors, tags and multi-document files rather than guessing. |
| The tray | `native/src/tray/` | `tray-icon` on Windows and macOS, `ksni` on Linux, an event loop per platform, and no icon is a supported state. |
| The tray | `native/src/tray.rs` | One crate for three desktops, so the subtlety is not a per-platform module: it is that the icon must be created on the frame's thread, that a click arrives on a channel this process has to wake the interface for, and that "no status area" is a state rather than an error. |
| Elevation | `native/src/elevation.rs`, `apps/vn-ranger/src/worker.rs` | The elevated copy is one-shot; the unelevated window must not hand it a path to act on. |
| Listener ownership | `native/src/collect/listeners/` | The socket table and the fd table are read at different times; other users' sockets need privilege. |
| Autorun parsing | `native/src/collect/autoruns/` | Registry views, 32/64-bit keys, quoted paths, binary plists, systemd user units, and a surface table read through one trait. |
Expand Down
Loading
Loading