Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

450 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

specter

Specter watches paths and fires commands when the tree settles — not when "something happened," but when nothing has happened for long enough that the tree is observably stable. Commands run against a snapshot that includes every change up to the quiescent point.

Conventional file-watch tools fire on every kernel event. The result is a flurry of redundant runs against partially-written files. Specter inverts that contract:

  • Coarse file-tree settling — no double-fires on git checkout, multi-file editor saves, or build outputs writing dozens of artifacts.
  • Hierarchical subtree hashing — each directory caches a fingerprint of everything beneath it, so unchanged subtrees are pruned in a single comparison. Reaction cost scales with the size of the change, not the size of the watched tree.
  • Self-event absorption — the reaction itself usually writes inside the watched tree; Specter folds those events into the post-fire rebase rather than treating them as a fresh burst.
  • Hot config reload — SIGHUP, an operator IPC verb, or edit-and-save against a watched config path.
  • Watching non-existent paths — watched paths (and discovery pattern anchors) do not need to exist at startup; Specter dynamically probes and descends parent directories to install watches as components materialize.
  • Operator control surface — nine client verbs over a UNIX socket (status, list, show, disable, enable, absorb, reload, tail, wait) for live inspection and runtime overrides.

Under the hood, a pure engine drives a kqueue/inotify sensor (BSD, macOS, Linux) and a subprocess actuator over bounded channels.

Quick start

make install-all                 # binary + config + host-OS service template
$EDITOR /usr/local/etc/specter.toml
# launchd / systemd / FreeBSD rc.d will start the daemon
specter status                   # confirm it's up

See docs/install.md for variables, scopes, and service-template details.

A watch

[[watch]]
name    = "rebuild"
path    = "/srv/repo/src"
actions = [{ exec = ["cargo", "build"] }]

Each [[watch]] block declares one reaction: a name, an absolute path, and an actions array. Optional knobs cover settle window, scope (subtree vs per-file), glob filters, event mask, recursion, and child stdio routing. The actions array supports sequences, pipes (a | b | c with pipefail-on), and conditionals (when / then / else). Argv slots accept ${specter.*} and ${env.*} placeholders; the child process receives a matching SPECTER_* environment set.

See:

Commands

specter run --config <file>         # the daemon (typically run by the supervisor)
specter status                      # daemon snapshot
specter list                        # every watch + state
specter show <name>                 # one watch in detail
specter disable <name>              # runtime override (survives reload)
specter enable <name>               # clear the runtime override
specter absorb <name> [--for …]     # fold the next change(s) instead of firing
specter reload                      # equivalent to SIGHUP
specter tail [--filter <tag> …]     # stream diagnostics
specter wait <name> [--timeout …]   # block until the watch fires (or detaches)

specter --help prints the full surface. Clients reach the daemon by resolving the same per-platform socket convention it binds, so no flag is needed in the common case; --socket <path> or $SPECTER_SOCK overrides it.

See docs/control.md for the IPC reference — socket resolution, wire format, error codes, subscribe semantics, exit codes.

Subprocess output

Child stdout/stderr go to /dev/null by default — Specter doesn't parse, format, or annotate user command output. Set log_output = true per watch and the actuator inherits Specter's own stdio fds, so the supervisor's log facility captures the bytes. For richer routing (notifications, conditional teeing), wrap the action with sh -c.

Layout

crates/
  specter-core      # types, snapshot, diff, traits — pure
  specter-engine    # Engine::step — pure, depends only on core
  specter-sensor    # kqueue/inotify watcher + worker prober pool
  specter-actuator  # subprocess pool, coalescing, env vars
  specter-config    # TOML + CLI parse / validate / diff
  specter-bin       # wiring, signals, hot reload, IPC surface
etc/                # systemd / launchd / FreeBSD rc.d templates
docs/               # operator-facing reference
Makefile            # build + install conventions

License

Dual-licensed under MIT or Apache-2.0, at your option.

About

Prove the absence of change

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages