From b5b09f47b80b2515192d1b632a432fcfb8350948 Mon Sep 17 00:00:00 2001 From: Greyforge Admin Date: Mon, 26 Oct 2026 18:06:54 -0400 Subject: [PATCH 1/2] README: prebuilt install, Safety section, terminal demo; name future archives oremap-v Install starts with the 0.4.3 tarball and checksum, then cargo install --git --tag. The rename history gets its own section without a removal date for the service-cartographer alias, and the subprocess hardening notes become a Safety list. Adds a fixture-run terminal SVG, CI and release badges, Uninstall and Development sections. build-release.sh now names archives oremap-v-.tar.gz like the other OpenForge tools; published 0.4.3 assets keep their names. --- CHANGELOG.md | 6 + CONTRIBUTING.md | 7 + README.md | 218 ++++++++++++++++---------------- docs/assets/oremap-terminal.svg | 34 +++++ scripts/build-release.sh | 9 +- 5 files changed, 159 insertions(+), 115 deletions(-) create mode 100644 docs/assets/oremap-terminal.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 9762ac4..1401170 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Changed + +- `scripts/build-release.sh` names the archive `oremap-v-.tar.gz`, matching the tag and the other OpenForge tools. Assets of 0.4.3 and earlier keep their published names. + ## 0.4.3 - 2026-10-26 ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 297f8b1..6884a4f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,6 +26,13 @@ cargo run -- scan --systemd-scope off --no-cron --repo-root . --format json - Add focused tests for new parsing or classification behavior. - Keep runtime dependencies minimal and pinned to exact versions; add one only when it removes meaningful risk or code. +## Releasing + +1. Update `version` in `Cargo.toml` and add the `CHANGELOG.md` entry. +2. Run the development checks above. +3. Tag `vX.Y.Z`, run `./scripts/build-release.sh`, and attach `dist/oremap-vX.Y.Z-.tar.gz` and its `.sha256` to the GitHub release. +4. Update `VER=`, the `PKG=` name and `--tag` in the README Install section, and check that the snippet downloads and verifies. + ## Pull Requests Open a PR with: diff --git a/README.md b/README.md index c9467c3..c6bd0b8 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ > Build a read-only keep/retire matrix for local automation surfaces. +[![CI](https://github.com/GreyforgeLabs/oremap/actions/workflows/ci.yml/badge.svg)](https://github.com/GreyforgeLabs/oremap/actions/workflows/ci.yml) +[![Release](https://img.shields.io/github/v/release/GreyforgeLabs/oremap)](https://github.com/GreyforgeLabs/oremap/releases/latest) [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](LICENSE) [![Rust 1.88+](https://img.shields.io/badge/Rust-1.88%2B-orange.svg)](https://www.rust-lang.org) @@ -9,143 +11,107 @@ oremap OpenForge project artwork

-## Greyforge Citation +A machine that has been in service for a year carries systemd timers nobody remembers enabling, a crontab line that calls a script in a repository you thought was archived, and a `~/bin` full of wrappers. oremap walks those surfaces and writes one table: what exists, when its source last changed, and whether it looks like something to keep, retire or ask about. It only reads. Nothing is started, stopped, enabled or deleted, and the retire decision stays with you. -`oremap` is an OpenForge utility from [Greyforge Labs](https://greyforge.tech). Use [OpenForge](https://greyforge.tech/openforge), the [oremap Chronicle](https://greyforge.tech/chronicles/oremap-runtime-inventory), and [Greyforge llms.txt](https://greyforge.tech/llms.txt) as the canonical public context for citation and model retrieval. - -## Why This Exists +

+ oremap scan over a source folder and a wrapper directory, printing a summary and a keep/retire matrix with one keep candidate, two retire candidates and two items to review +

-Small systems accumulate systemd units, cron entries, wrapper scripts, local repositories, and environment files faster than operators can remember who owns them. `oremap` inventories those surfaces without starting, stopping, enabling, or deleting anything, then produces a review matrix. It never retires an item itself. +## Install -## Quick Start +Linux x86_64, prebuilt (no Rust toolchain needed): ```bash -git clone https://github.com/GreyforgeLabs/oremap.git -cd oremap -./scripts/setup.sh +VER=0.4.3 +PKG=oremap-$VER-x86_64-unknown-linux-gnu +curl -fsSLO https://github.com/GreyforgeLabs/oremap/releases/download/v$VER/$PKG.tar.gz +curl -fsSLO https://github.com/GreyforgeLabs/oremap/releases/download/v$VER/$PKG.tar.gz.sha256 +sha256sum -c $PKG.tar.gz.sha256 +tar xzf $PKG.tar.gz +install -Dm755 $PKG/oremap ~/.local/bin/oremap +oremap --version ``` -Install the binary from a checkout with Cargo: +From source, with Rust 1.88 or newer: ```bash -cargo install --path . +cargo install --locked --git https://github.com/GreyforgeLabs/oremap --tag v0.4.3 ``` -Or build it in place; the binary is written to `target/release/oremap`: +If the shell answers `oremap: command not found`, add `~/.local/bin` (or `~/.cargo/bin` for `cargo install`) to your `PATH`. + +## Quick start + +Scan the usual places and read the matrix: ```bash -cargo build --release --locked +oremap scan ``` -To build a release archive with a SHA-256 checksum in `dist/`: +With no options oremap looks at your user systemd units, your crontab, wrapper scripts in `~/bin` and `~/.local/bin`, Git repositories under the current directory, and env-file metadata. Point it somewhere else, or teach it your naming, with a few flags: ```bash -./scripts/build-release.sh +oremap scan \ + --repo-root ~/work \ + --wrapper-dir ~/bin \ + --keep-keyword invoice \ + --retire-keyword legacy ``` -Version 0.4.0 is a Rust rewrite of the 0.3.x Python tool, and the first -release under the name `oremap` (0.3.x and earlier were released as -`service-cartographer`). It keeps the same command line, report formats, -privacy rules, and exit codes; only the program name and the Markdown report -title change, and JSON reports keep the -`service-cartographer.report.v1` schema identifier. On the -reference host it starts in about 2 ms instead of about 57 ms, and a full -default scan takes 137 ms instead of 266 ms. When no external tools run, the -scanner's own peak memory drops from about 18 MB to about 3 MB. See -[docs/benchmarks.md](docs/benchmarks.md) for the commands and numbers. - -The old command name still works for this release: `cargo install` also -installs a deprecated `service-cartographer` alias that prints a one-line -notice to stderr and then behaves exactly like `oremap`. Update scripts and -cron entries to `oremap`; the alias will be removed in the next release. - -## Usage +Save a report for a script or a spreadsheet: ```bash -# Markdown keep/retire matrix for the current checkout -oremap scan - -# JSON report for automation oremap scan --format json --output report.json +oremap scan --format csv --output report.csv +``` +## Usage + +```bash # Focus on a few workflow names across services, wrappers, repos, and env files -oremap scan \ - --repo-root ~/work \ - --wrapper-dir ~/bin \ - --focus worker \ - --focus ingest \ - --format markdown +oremap scan --repo-root ~/work --wrapper-dir ~/bin --focus worker --focus ingest # Avoid systemd and cron when scanning a fixture or CI workspace oremap scan --systemd-scope off --no-cron --repo-root . + +# Only one kind of item +oremap scan --kind repository --repo-root ~/src ``` -CSV output is spreadsheet-safe by default: string cells beginning with `=`, `+`, -`-`, or `@`, including after leading whitespace, are prefixed so spreadsheet -imports treat them as text. Use JSON when exact machine values are required. - -External inventory commands are selected only from the reviewed absolute-path -allowlist. Each collected item records the executable path and bounded version -diagnostic. Subprocesses receive a minimal environment; `systemctl --user` -also receives the current user bus address and runtime directory so it can -query the live user manager, and `git` runs with `GIT_OPTIONAL_LOCKS=0` and -`-c core.fsmonitor=` so `git status` never rewrites a repository index or -starts a file-system monitor daemon. Filter drivers configured for a -scanned repository (for example `git lfs`, or a `clean` command in a crafted -`.git/config`) are switched off for its `git status`, and so are the drivers -configured in every populated submodule that `git status` checks, at any -depth. Submodules are found from the gitlinks in the index, with or without a -`.gitmodules` entry; a repository whose index lists no gitlink costs no extra -`git` call. A submodule whose `.git` is a symbolic link or leads outside the -scanned repository, or more than 8 levels of nesting or 512 submodules, is -not followed: the repository is reported `unknown` with a warning and its -`git status` is not run. `git log` never checks commit signatures, so a -repository's `log.showSignature` cannot make it run `gpg.program`, and -`GIT_NO_LAZY_FETCH=1` stops a partial clone from fetching missing objects -through its configured remote. External commands read standard input from -`/dev/null`. A caller-controlled `PATH` cannot replace `git`, `systemctl`, or -`crontab`. - -Every JSON report includes `complete`. A failed or partial requested collector -sets it to `false`, records a warning, and makes the command exit 1. Check both -the exit code and warnings before using a report to make decisions. `--output` -writes the report through a temporary file and publishes it with private `0600` -permissions, including when replacing an older report. - -Environment-file reads are capped at 64 KiB and 1,000 lines. Symlinked env files -are skipped, and disappearance or permission races are recorded as warnings so -unrelated inventory can still complete. Git worktree files and bare repositories -are recognized without following `.git` marker symlinks. - -## Example Matrix - -| Action | Kind | Name | Status | Path | Reason | -|---|---|---|---|---|---| -| `keep_candidate` | `systemd_unit` | `backup.timer` | `active=active, enabled=enabled` | `~/.config/systemd/user/backup.timer` | active or enabled | -| `review` | `cron_job` | `user-crontab:4` | `status=present` | | scheduled command needs owner decision | -| `review` | `wrapper` | `old-sync` | `status=executable` | `~/bin/old-sync` | source timestamp is 173 days old; verify actual use | - -File modification time and last commit time are source-change timestamps, not -proof of last execution or use. They only prompt review. Retirement hints -require an explicit `--retire-keyword`; active or enabled units and dirty -repositories take precedence over those hints. - -## What It Scans - -- User or system systemd units through unit files and `systemctl` read-only listing commands. -- Current user's crontab through `crontab -l`. -- Executable wrappers from explicit directories, defaulting to `~/bin` and `~/.local/bin` when present. -- Git repositories under requested roots with branch, dirty count, and last commit timestamp. Bare repositories are listed with status `bare`. -- Environment-like files by metadata only. Values are never emitted. - -## Privacy Defaults - -- Hostname is redacted unless `--show-hostname` is passed. -- Home paths are shortened to `~` unless `--absolute-paths` is passed. +`oremap scan --help` lists every option. [docs/usage.md](docs/usage.md) has more examples. + +**Reading the matrix.** Active or enabled units and dirty repositories are keep candidates. A retire candidate needs an explicit `--retire-keyword` match, and keep signals win over it. An old timestamp alone only asks for review: file modification and last-commit times show when the source changed, not when it last ran. + +**Reports.** Every JSON report includes `complete`. If a requested collector fails or returns a partial result, `complete` is `false`, a warning is recorded and the command exits 1, so check both before acting on a report. `--output` writes through a temporary file and publishes the report with `0600` permissions. CSV is spreadsheet-safe: string cells that begin with `=`, `+`, `-` or `@` (after any leading whitespace) are prefixed so imports treat them as text. Use JSON when you need the exact values. + +## What it scans + +- User or system systemd units, through unit files and read-only `systemctl` listings. +- The current user's crontab, through `crontab -l`. +- Executable wrappers in the directories you name, defaulting to `~/bin` and `~/.local/bin` when present. +- Git repositories under the requested roots, with branch, dirty count and last commit time. Bare repositories are listed with status `bare`. +- Environment-like files, by metadata only. Values are never emitted. + +## Privacy defaults + +- The hostname is redacted unless you pass `--show-hostname`. +- Home paths are shortened to `~` unless you pass `--absolute-paths`. - Cron command previews redact obvious inline secret assignments and the standard input that follows an unescaped `%`. -- Env files report variable counts by default, not values or names. `--include-env-names` adds names only; lines inside multi-line quoted values are never read as names. +- Env files report variable counts by default, not names or values. `--include-env-names` adds names only; lines inside multi-line quoted values are never read as names. + +## Safety + +oremap runs `git`, `systemctl` and `crontab` on whatever it finds, including repositories you did not write. It treats them accordingly: -## Exit Codes +- External commands come only from a reviewed allowlist of absolute paths, so a caller-controlled `PATH` cannot swap in another `git`, `systemctl` or `crontab`. Each item records the executable path and a bounded version diagnostic. +- Subprocesses get a minimal environment and read standard input from `/dev/null`. `systemctl --user` also gets the user bus address and runtime directory so it can query the live user manager. +- `git` runs with `GIT_OPTIONAL_LOCKS=0` and `-c core.fsmonitor=`, so `git status` never rewrites an index or starts a file-system monitor daemon. +- Filter drivers (`git lfs`, or a `clean` command planted in a `.git/config`) are switched off for `git status`, in the scanned repository and in every populated submodule it checks, at any depth. Submodules are found from the gitlinks in the index, with or without `.gitmodules`; a repository with no gitlinks costs no extra `git` call. +- A submodule whose `.git` is a symbolic link or points outside the scanned repository is not followed, and neither is nesting deeper than 8 levels or more than 512 submodules. The repository is then reported `unknown` with a warning, and its `git status` does not run. +- `git log` never checks commit signatures, so `log.showSignature` cannot make it run `gpg.program`. `GIT_NO_LAZY_FETCH=1` stops a partial clone from fetching missing objects through its remote. +- Env-file reads stop at 64 KiB and 1,000 lines. Symlinked env files are skipped, and files that vanish or change permissions mid-scan become warnings, so the rest of the inventory still completes. Git worktree files and bare repositories are recognized without following `.git` marker symlinks. + +## Exit codes | Code | Meaning | |------|---------| @@ -153,16 +119,40 @@ repositories take precedence over those hints. | 1 | Report is incomplete; inspect `warnings` | | 2 | Usage error | +## Renamed from service-cartographer + +Releases up to 0.3.x shipped as the Python tool `service-cartographer`. 0.4.0 is a Rust rewrite under the name oremap, with the same command line, report formats, privacy rules and exit codes. Only the program name and the Markdown report title changed; JSON reports keep the `service-cartographer.report.v1` schema identifier, so existing parsers keep working. + +The rewrite starts in about 2 ms instead of about 57 ms, and a full default scan takes 137 ms instead of 266 ms. When no external tools run, the scanner's own peak memory drops from about 18 MB to about 3 MB. [docs/benchmarks.md](docs/benchmarks.md) has the commands and numbers. + +The old command still works. The release tarball and `cargo install` both include a `service-cartographer` alias that prints a one-line notice to stderr and then behaves exactly like `oremap`. Point scripts and cron entries at `oremap` when convenient. + ## Requirements -- Rust 1.88+ to build from source (no runtime interpreter needed) -- Linux for systemd and cron collectors -- One small runtime crate (`rustix`, for `access(2)`, `uname(2)`, and the terminal width) +- Linux for the systemd and cron collectors. +- Rust 1.88+ only when building from source. The binary needs no runtime interpreter and has one small runtime crate (`rustix`, for `access(2)`, `uname(2)` and the terminal width). + +## Uninstall -## Documentation +```bash +rm -f ~/.local/bin/oremap ~/.local/bin/service-cartographer # prebuilt install +cargo uninstall oremap # cargo install (removes both binaries) +``` + +oremap keeps no state of its own. Reports you wrote with `--output` stay where you put them. + +## Development + +```bash +git clone https://github.com/GreyforgeLabs/oremap.git +cd oremap +./scripts/setup.sh # contributor setup: checks the toolchain, builds, runs the tests +cargo install --path . # install your checkout +./scripts/build-release.sh # reproducible release tarball and SHA-256 checksum in dist/ +``` - [STARTHERE.md](STARTHERE.md) - coding assistant bootstrap -- [CONTRIBUTING.md](CONTRIBUTING.md) - contribution workflow +- [CONTRIBUTING.md](CONTRIBUTING.md) - contribution workflow and release checklist - [CHANGELOG.md](CHANGELOG.md) - version history - [docs/usage.md](docs/usage.md) - usage notes - [docs/benchmarks.md](docs/benchmarks.md) - Python vs Rust, and 0.4.0 vs 0.4.1 measurements @@ -174,4 +164,8 @@ AGPL-3.0. See [LICENSE](LICENSE) for details. --- -Built by [Greyforge](https://greyforge.tech) +

+ Useful? Star oremap on GitHub · Follow @GreyforgeLabs · More OpenForge tools at greyforge.tech +

+ +

Background: the oremap Chronicle · Greyforge llms.txt

diff --git a/docs/assets/oremap-terminal.svg b/docs/assets/oremap-terminal.svg new file mode 100644 index 0000000..22ff017 --- /dev/null +++ b/docs/assets/oremap-terminal.svg @@ -0,0 +1,34 @@ + +oremap scan + + + + + +oremap scan + +$ oremap scan --repo-root ~/src --wrapper-dir ~/bin --keep-keyword invoice --retire-keyword legacy --retire-keyword scraper --no-cron --no-env --systemd-scope off +# oremap Report +- Generated: `2026-10-26T21:55:44.983345+00:00` +- Host: `redacted` +- Items: `5` +- Complete: `yes` +## Summary +| Metric | Count | +|---|---:| +| `action:keep_candidate` | 1 | +| `action:retire_candidate` | 2 | +| `action:review` | 2 | +| `repository` | 3 | +| `total` | 5 | +| `wrapper` | 2 | +## Keep / Retire Matrix +| Action | Kind | Name | Status | Last activity | Path | Reason | +|---|---|---|---|---|---|---| +| keep_candidate | repository | invoice-sync | status=clean | 2026-10-20T10:00:00Z | ~/src/invoice-sync | matched keep keyword | +| retire_candidate | repository | old-scraper | status=clean | 2025-11-02T10:00:00Z | ~/src/old-scraper | matched operator-supplied retire keyword | +| retire_candidate | wrapper | legacy-report | status=executable | 2025-12-14T18:05:00+00:00 | ~/bin/legacy-report | matched operator-supplied retire keyword | +| review | repository | site-builder | status=clean | 2026-10-24T10:00:00Z | ~/src/site-builder | needs owner decision | +| review | wrapper | nightly-backup | status=executable | 2026-10-22T07:30:00+00:00 | ~/bin/nightly-backup | needs owner decision | + + diff --git a/scripts/build-release.sh b/scripts/build-release.sh index fb7c258..ba5a412 100755 --- a/scripts/build-release.sh +++ b/scripts/build-release.sh @@ -2,8 +2,11 @@ # Build the release binaries and a versioned, checksummed archive for the # host target: # -# dist/oremap--.tar.gz -# dist/oremap--.tar.gz.sha256 +# dist/oremap-v-.tar.gz +# dist/oremap-v-.tar.gz.sha256 +# +# The "v" matches the tag and the other OpenForge release assets +# (releases up to 0.4.3 were named oremap--). # # The archive is reproducible for a given commit and toolchain: entries are # sorted, owned by 0:0, and stamped with SOURCE_DATE_EPOCH (default: the @@ -47,7 +50,7 @@ if [ "$reported" != "oremap $VERSION" ]; then exit 1 fi -NAME="oremap-$VERSION-$TARGET" +NAME="oremap-v$VERSION-$TARGET" STAGE="dist/$NAME" rm -rf "$STAGE" "dist/$NAME.tar.gz" "dist/$NAME.tar.gz.sha256" mkdir -p "$STAGE" From 8d57edf772c77107ba7a41f9418facb50f6acda7 Mon Sep 17 00:00:00 2001 From: Greyforge Admin Date: Mon, 26 Oct 2026 19:19:27 -0400 Subject: [PATCH 2/2] README: show how to install the legacy alias from the prebuilt tarball --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6bd0b8..b1c9929 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ Releases up to 0.3.x shipped as the Python tool `service-cartographer`. 0.4.0 is The rewrite starts in about 2 ms instead of about 57 ms, and a full default scan takes 137 ms instead of 266 ms. When no external tools run, the scanner's own peak memory drops from about 18 MB to about 3 MB. [docs/benchmarks.md](docs/benchmarks.md) has the commands and numbers. -The old command still works. The release tarball and `cargo install` both include a `service-cartographer` alias that prints a one-line notice to stderr and then behaves exactly like `oremap`. Point scripts and cron entries at `oremap` when convenient. +The old command still works. The release tarball and `cargo install` both include a `service-cartographer` alias that prints a one-line notice to stderr and then behaves exactly like `oremap`. With the prebuilt tarball, install it next to `oremap` with `install -Dm755 $PKG/service-cartographer ~/.local/bin/service-cartographer`. Point scripts and cron entries at `oremap` when convenient. ## Requirements