- JavaScript 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .forgejo/workflows | ||
| bin | ||
| docs | ||
| examples | ||
| scripts | ||
| src | ||
| test | ||
| .gitignore | ||
| CHANGELOG.md | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| PRIVACY.md | ||
| README.md | ||
| SECURITY.md | ||
TimerProof
TimerProof is a local-only, read-only Node.js CLI and API for inventorying classic cron entries and systemd timers on Linux. It separates configured schedule evidence, manager snapshots, and unknowns so a timer listing is not mistaken for proof that a command or service succeeded.
TimerProof does not run jobs, mutate scheduler state, inspect logs, contact network services, or collect telemetry. Cron command text is omitted unless --include-commands or includeCommands: true is explicitly supplied.
Requirements
- Node.js 20 or newer
- Linux for live cron and systemd inventory
crontaband/orsystemctlwhen those scheduler views are wanted
The pure parsers and formatters can be used on any platform supported by Node.js.
Run from a checkout
npm ci --ignore-scripts
node bin/timerproof.js --help
node bin/timerproof.js
node bin/timerproof.js --json --scope system
To install a locally built tarball:
npm pack
npm install --global ./timerproof-0.1.0.tgz
The package has no runtime dependencies.
CLI
timerproof [options]
Common options:
--jsonemits canonical, recursively key-sorted JSON.--scope all|system|userselects scheduler views.--observed-at <date>sets the report's comparison reference. Cron is evaluated at that host-local minute; live systemd rows remain snapshots collected during the run.--include-commandsreveals cron command text. Commands remain omitted by default.--timeout-ms,--max-bytes,--max-units, and--max-cron-sourcesreduce or raise bounded inspection limits.
A completed report exits with status 0 even when a scheduler is absent, inaccessible, or only partially readable; those conditions are represented in the report. Invalid CLI input or an inability to create a report exits with status 2.
See docs/cli.md for the full behavior and docs/evidence-model.md for interpretation.
API
import { formatHuman, inventory, stableStringify } from "timerproof";
const report = await inventory({
scope: "system",
observedAt: "2026-07-18T12:00:00.000Z",
});
process.stdout.write(formatHuman(report));
process.stdout.write(`${stableStringify(report)}\n`);
The package also exports bounded cron parsers, structured systemd list parsers, a parser for already-captured systemctl show text, and the default fixed-allowlist command runner. See examples/api.mjs.
What is inspected
Default live inventory is deliberately narrow:
/etc/crontab- portable regular files in
/etc/cron.d crontab -l- fixed
systemctl --systemandsystemctl --userlist-timersinvocations using JSON output - fixed
systemctl --systemandsystemctl --userlist-unit-filesinvocations using JSON output
No discovered value is ever inserted into a subprocess argument. In particular, live inventory does not invoke systemctl show for discovered units. The exported parseSystemdShow function only parses text supplied by the caller.
Supported cron grammar
TimerProof evaluates numeric five-field expressions with:
*- comma lists
- inclusive numeric ranges
- steps on
*or ranges - weekday
0or7for Sunday
Cron's day-of-month/day-of-week OR rule is applied. A field containing a wildcard list item, including 1,*, is treated as wildcard-bearing for that rule. Names, nicknames such as @reboot, implementation-specific extensions, and timezone assignments are reported as unsupported or unknown instead of guessed.
Files whose names are ignored by cron's portable /etc/cron.d naming rule, including .placeholder, are reported as ignored and do not degrade manager status.
Evidence boundaries
- Cron eligibility means only that a supported expression matches the observed host-local minute.
- A systemd next-elapse value is a manager snapshot, not a guarantee of future activation.
- A systemd last-trigger timestamp is evidence that the timer manager recorded a trigger, not proof that the activated service started or succeeded.
- Unit-file state is installation or enablement evidence, not runtime state.
- TimerProof does not parse unit files, inspect journal history, or establish command outcomes.
Safety and privacy
Read SECURITY.md and PRIVACY.md. The short version is that the default implementation has a fixed read-only subprocess allowlist, uses no shell, applies output and time limits, sanitizes terminal control sequences, performs no network I/O, and stores nothing.
Development
npm run check
npm test
npm run test:coverage
npm run test:all
npm pack --dry-run
npm run test:tarball
npm test refuses to pass if no *.test.js files exist. npm run check recursively enumerates every .js, .mjs, and .cjs file under the package source, scripts, tests, examples, and binary directories before running node --check on each one.
License
MIT