Skip to content

Releases: helixmap/sigwood

sigwood v0.2.8

Choose a tag to compare

@helixmap helixmap released this 25 Jul 02:57

Added

  • The graph player shows byte flow as moving grain. Ribbons carry small
    particles drifting from source to destination - density and speed follow
    each flow's current rate, inside the same frozen layout. On by default for
    byte-active conn replays (where rate is a measured claim), off for
    count-only graphs, always toggleable (flow on|off), and the default
    respects reduced-motion preferences. Filter clicks now snap the scale
    instantly instead of gliding.

  • The graph player carries a scale gauge. A small bottom-left bracket shows
    what a ribbon's height is worth (200 B/s, 5k/s), reading the exact scale
    the ribbons are drawn with - so the gauge can never disagree with the
    picture. It breathes with the scale in fill, sits still in absolute,
    follows the bytes/conns metric, and rides into saved clips.

  • sigwood init offers to skip detectors you have no logs for. Decline a
    source during setup and the wizard now names the default-hunt detectors that
    would have read it and offers a one-keystroke detect exclusion (e.g.
    detect = "default, !beacon, !scan") - so a box without Zeek stops seeing
    per-run "conn.log not found" notes it can do nothing about. Re-running init
    after adding the source offers to lift the exclusion; a hand-written custom
    detect value is never touched, and declining the offer writes nothing.

  • Pick a graph by name: sigwood graph conn (also dns, pihole). Kind
    names select just those artifacts from configured sources; names compose
    (sigwood graph conn dns) and a single selected kind may stream to stdout or
    an exact --out file. Kind names and paths don't mix - a file literally named
    conn is reached as ./conn. With kinds selected, only those families are
    probed, so a broken or unreadable unselected source no longer fails the run.

  • Graph flow grain is bidirectional. Byte-active conn replays now show
    responder traffic as reverse grain - particles drifting destination-to-source
    in the source's color - split from forward grain by each flow's true
    responder-byte share (Zeek's resp_bytes, newly carried in the canonical
    connection schema). Ratios are exact at every aggregation tier - server
    folds and in-player rollups both sum bytes before dividing - so a
    download-heavy flow honestly shows mostly reverse grain. Ribbon shapes,
    counts, and saved-artifact geometry are unchanged; the optional direction
    ratio travels with clips, and graphs without responder data simply show
    forward grain as before.

  • The report says how it was made. Text and HTML/PDF reports now carry a
    provenance row - generated: <timestamp> · sigwood <version> - and, for CLI
    runs, as: <the exact command line> - so a saved report answers "which tool,
    which version, when, invoked how" by itself. The JSON feed carries both facts
    as run_summary.generated_at (ISO UTC) and run_summary.invocation
    (additive; no schema bump).

Changed

  • An "admin session" can no longer span days. Hosts with periodic privileged
    automation (a sudo cron under 45 minutes) used to chain their session
    open/close lines into one enormous "admin session" review unit - up to two
    weeks wide on long lookbacks - hiding unrelated findings under a wrong label.
    Session recognition now declines any candidate longer than 8 hours (measured:
    real interactive sessions top out well under 2 hours, automation chains start
    near a day), and the affected findings simply keep their own shapes. Genuine
    sessions, update runs, and reboot handling are unchanged.
  • Hardware-key logins and kernel dumps no longer flood the syslog report.
    A second calibrated drain3 mask recognizes space-separated hex-pair runs (four
    or more pairs - FIDO2/CBOR debug dumps, kernel oops Code: lines), so a dump
    that repeats at every login shares one template instead of minting a fresh
    "rare" burst each time. Measured on a month of real fleet logs: the per-login
    burst class disappears while every MEDIUM finding and every genuinely
    once-ever event is preserved. Colon-joined MAC addresses and ordinary
    two-pair text (dates, port 22 80) are never masked; raw log lines are
    displayed byte-for-byte as always.
  • Syslog rows share one grammar. Burst spans render through the same compact form
    as family and transaction rows (with a new honest seconds tier below one minute -
    45s, not 0m), burst counts pluralize properly, and a transaction row now leads
    with its rare-line magnitude (19 rare lines) instead of the internal
    "member findings" phrase; the member count remains in evidence and JSON. Program
    mixes de-duplicate case variants for display (CRON, cron reads once, first
    spelling kept) - counts and machine output keep both.
  • The report header names sources, not glob patterns. The records: line reads
    12,345 syslog · 90,000 Zeek conn instead of 12,345 *.log* · 90,000 conn*.log*.
    The JSON feed additively carries the mapping as run_summary.record_labels
    (no schema bump); record_counts keys are unchanged.
  • Syslog review units roll up only when it pays. Family and burst folding now
    starts at four rare lines (previously two and three): below that, a summary row plus
    its expansion costs as much space as the lines themselves, so one to three rare
    lines surface individually. Recognized transactions still group from two findings -
    their label carries meaning beyond compression. Tunable as before via
    family_min_size / burst_min_size under [detectors.syslog].
  • Capsule summaries are tokens-only. The per-template digest lines inside family
    and burst rows are gone; the tokens: scent line may now span up to two lines, and
    opening a row's sample always reveals something new (the raw lines). rsyslog's
    #011/#012/#015 whitespace escapes read as separators while distilling tokens,
    so multi-line commands no longer render as glued tokens; raw lines stay verbatim.
  • Transaction rows in HTML expand straight to their raw log lines. The
    severity-pill toggle on an admin-session or update-run row now opens the members'
    sampled raw lines under thin per-member separators instead of one-line member
    summaries; JSON member records additively carry the same bounded samples, in
    chronological member order. Separators name severity, program, and line count
    only - internal grouping vocabulary no longer appears in any output - and a JSON
    member record carries a tier key only when the member really is a family or
    burst rollup (a plain rare line omits it). Log content is never rewritten:
    whatever words an operator's own lines contain render verbatim.
  • Graph artifacts open in the full three-column view. The player's view
    segment now lands with the middle tier on - services for conn, resolvers or
    query types for dns, dispositions for Pi-hole - matching how the graph is
    actually read; one click on hosts collapses it. The segment labels name the
    view states plainly. Clips inherit the new default; previously saved artifacts
    are unchanged (they are self-contained).
  • graph windows a directory like the hunt does. A bare sigwood graph
    against a Pi-hole (or any flat) directory now reads the last default_window
    of available data - peeking rotation files' first timestamps and skipping the
    rest instead of decompressing the whole archive - and the artifact discloses
    the window. --all, explicit timeframes, and single named files behave
    exactly as before.
  • warn_above = 0 now disables the large-dataset prompt. Previously 0 meant
    prompt before analyzing any amount of data; it now switches the advisory prompt
    off entirely - the config-side equivalent of passing -y every run, suited to
    cron and other unattended schedules. Set a small positive value to keep an
    aggressive prompt.
  • The report wordmark matches the graph player's brand mark. The sigwood
    token in the HTML/PDF report header now renders in the same serif face and
    light/dark color pair as the graph player's wordmark; the · threat hunt
    tagline keeps its bold sans treatment. A parity test pins both artifacts to one
    identical font stack and color pair so the two marks cannot drift apart.

Fixed

  • A reboot is never mislabeled an "update run", and its rebooted marker can
    no longer vanish from the report.
    On RHEL-family hosts a normal boot emits
    lines that satisfy the update-run grammar (dracut, the auditd stop/start pair,
    SELinux policy load), so a routine reboot could form a medium "update run"
    review unit that swallowed the reboot itself. Update-run recognition now
    ignores anchors inside a detected boot window (measured bounds), and a
    rebooted burst is never claimed by any review unit - the reboot always
    stays a visible row, including after a genuine update-then-reboot.
  • Graph flow particles read as streams, not stripes. Same-moment spawns used
    to move in lockstep and bunch into vertical bands that marched across a
    ribbon, most visibly after a filter click. Each particle now carries its own
    slight speed variation, a restarting ribbon fills uniformly along its length,
    and the round dots are replaced by short motion streaks that follow the
    ribbon's curve - longer for faster flow - with a gentle brightness shimmer
    lifted off the ribbon color. Particles now pause with playback: a paused
    frame is fully still, and a filter click while paused recomposes the grain
    without motion. Flow direction, the toggle and its defaults, and saved
    artifacts are unchanged.
  • The graph header now counts the files that fed the graph, not the archive it
    scanned.
    The source cell used to report every file discovered under the
    source directory (+ 294 others on a large rotation archive) even when the
    rendered window drew from a handful of them. The loader now records each
    file's kept time span and the header names only the files whose data
    ...
Read more

sigwood v0.2.7

Choose a tag to compare

@helixmap helixmap released this 22 Jul 05:20

Added

  • Recognized syslog transactions. The syslog detector now folds an administrative
    session (login through logout) or a system update run (package, kernel-module, and
    policy activity) into one labeled review unit per host - for example update run · 4 member findings · 1m - with every member finding preserved behind it: a compact
    drill-down at -v in text, an expandable row in HTML, and complete member evidence
    in JSON. Severity still comes only from the privileged program class; recognition
    groups findings, it never grades them. Default on; recognize_transactions = false
    under [detectors.syslog] restores the previous behavior exactly.
  • Host suppression allowlist. A third flat-list kind silences a chatty machine's
    system logs whole-host: dot-free hosts* drop-ins in allowlist.d/ (one fnmatch glob
    or re: regex per line, matched case-insensitively against the syslog host column),
    applied before analysis across all three syslog feeds - flat files, the system journal,
    and Zeek syslog.log. DNS and connection logs are untouched. The run banner gains a
    third coverage clause (suppressed 9,412 rows from 2 hosts), JSON carries the new
    host_rows / host_total / hosts_matched suppression fields, and sigwood init
    seeds a blank hosts template whose header warns that host suppression removes the
    whole host's story and shifts relative rarity. Host lists are local-only - sigwood
    never ships one.

Changed

  • Steadier terminal narration. The terminal cursor is hidden during narrated
    analyze, digest, graph, and export runs and restored across clean exits, failures,
    and interrupts; interactive prompts show it while you type. Quiet runs,
    dumb terminals, and redirected output see no cursor-control bytes at all.

Fixed

  • Cross-feed syslog duplication no longer hides unique lines. When the same
    host's messages reach both the local system-log feed (files or the journal) and
    Zeek's syslog.log, the duplicate coverage doubled that host's template counts, so
    a line that should have surfaced as a unique rare event silently left the rare set.
    sigwood now arbitrates per host: a host present in the local feed keeps its local
    rows only, and Zeek contributes just the hosts the local feed lacks. The run
    summary discloses it in counts (system logs: 1 host carried by both the local feed and Zeek syslog.log - kept the local rows (16,094 Zeek rows set aside));
    loaded record counts are unchanged. Hosts only Zeek can see are unaffected, and
    hostless (unknown) lines are never arbitrated.

Security

  • Artifacts are private by default. Every directory sigwood creates is now mode
    0700 and every file it writes is 0600 - reports, digests, graph artifacts, exports,
    config and its backups, and allowlist seeds - independent of the process umask, and
    re-applied when an artifact is overwritten in place. The CLI also sets a 077 umask
    backstop for every command except init.
  • Loose-home advisory. When an existing sigwood home is group- or world-accessible,
    each run prints a one-line stderr reminder with the exact chmod 700 to close it.
    sigwood never changes permissions on a directory it did not create.
  • A system-wide /etc/sigwood config keeps ordinary umask-governed permissions so
    non-root users can still read it.

sigwood v0.2.6

Choose a tag to compare

@helixmap helixmap released this 21 Jul 04:42

Changed

  • Syslog capsule detail lines distill harder and fill the row. A capsule whose
    sampled content fits on one line now shows a single tokens: line: the members'
    words in order, duplicates removed, complete. Larger capsules keep up to three
    per-pattern lines, now allowed 200 characters instead of 80. Opaque identifiers are
    filtered more precisely: audit record ids such as msg=audit(...) and kernel
    ring-buffer stamps no longer crowd out readable content, while IP addresses, ports,
    sizes, dates, and version strings are always kept.
  • The severity pill is the sample expander in HTML reports. The separate
    "sampled log lines" control is gone; the [M]/[L]/[I] pill toggles it instead
    (a muted + beside the pill marks expandable rows; the colored pill itself keeps
    one constant width everywhere), and opening a capsule swaps its distilled lines for
    the raw sample. Sampled lines are syntax-highlighted: timestamp bright, host and
    program each in their own color, in both light and dark themes. The report remains
    JavaScript-free, and printed or PDF output carries no interactive control.
  • The HTML timestamp column hugs its content. The leading stamp column is sized
    to the stamp itself (wider only under --utc, whose stamps carry a zone suffix),
    removing the fixed dead gap that previously sat between a capsule's timestamp and
    its hostname.

Fixed

  • No more stray | above the report. The render spinner no longer runs while the
    report itself streams to the same terminal, which previously stranded an orphaned
    spinner frame above the findings on every interactive run.
  • Capsule detail lines align with the timestamp column in text reports; they
    previously sat one column to the left.
  • [detectors.syslog].line_trim_limit now works. The published config key was
    read by nothing; it now trims lone rare-line titles as documented.
  • Severity pill text is readable in both themes. White-on-color pill text
    measured as low as 2.1:1 contrast in dark mode; each severity now carries a
    per-theme ink color, every pairing clears the 4.5:1 accessibility bar, and a test
    pins the bar so future palette changes cannot regress it. One background moved
    slightly to reach the bar: the light-theme low-severity blue.

sigwood v0.2.5

Choose a tag to compare

@helixmap helixmap released this 20 Jul 06:30

Fixed

  • A lone rare line read from the systemd journal now leads with its timestamp. Journal
    entries carry their time as a separate field rather than inside the message text, so these
    rows previously rendered as bare message text in an otherwise time-ordered section - the
    common case on a modern Linux install, where sigwood prefers the journal. The row now
    starts with the same syslog-shaped stamp the grouped rows use. Lines from syslog files are
    unchanged: they already begin with their own wall clock, and sigwood never adds a second
    stamp to a line that has one.

Changed

  • Grouped syslog rows now show a few distilled lines from what they group, and stay on one
    line each in HTML reports.
    A review unit, burst, or reboot row is followed by up to three
    short fragments drawn from distinct message shapes inside it, so the row invites a closer
    look instead of only counting one. Fragments keep addresses, ports, process ids, exit codes
    and sizes, and drop only long opaque identifiers such as hashes and session tokens. On
    screen, syslog rows are clipped with a trailing ellipsis rather than wrapped or scrolled -
    widen the window to see more; printed reports still wrap so nothing is cut from a PDF. The
    leading stamp on those rows is now written in syslog's own wall-clock shape
    (Jul 12 21:57:33), so it reads in the same grammar as the log lines beside it. On a 7-day
    measurement corpus every grouped row carried fragments, with none empty.

  • Rare syslog findings now separate a privileged program channel from the routine
    rarity sieve.
    Exact program membership in a shipped, operator-replaceable roster keeps
    security-critical families and singletons at MEDIUM; other rare families and singletons
    render as default-visible LOW, while bursts and reboots remain INFO. Family and burst rows
    now lead with their first timestamp, -v shows a three-line member sample, and HTML
    reports provide a closed full-sample expansion without leaking that body into printed
    reports. On the same 7-day measurement corpus, the report reads 12 privileged + 16
    routine + 10 info findings (38 total, under a minute to scan) - the increase over the
    prior 29 is privileged rows formerly buried inside info bursts, now independently
    reviewable.

  • Syslog rare-event output now groups isolated lines into per-host, per-program review
    units and normalizes long identifier-like hexadecimal-character runs during template
    mining.
    Temporal bursts and reboot handling remain separate, while raw log text stays
    intact in evidence. On the measurement corpus, a 7-day multi-host run that previously
    reported 44 isolated rare-line findings now reports 19 review units (29 findings in
    total, from 56); a single quiet day reports about a dozen. Grouping changes how the
    findings are presented and counted, not what the detector observes - every rare line is
    still present in exactly one burst, family, or standalone finding.

sigwood v0.2.4

Choose a tag to compare

@helixmap helixmap released this 19 Jul 00:51

Fixed

  • scan vertical and horizontal findings now describe the time window that actually
    triggered them
    , not the whole loaded span. These scans fire when enough distinct ports
    or hosts appear inside a sliding window, but the reported evidence - connection count,
    scan-state ratio, top states, port-range entropy, and host velocity - was previously
    computed over every connection the pair exchanged across the entire log. A short scan
    burst buried in a long benign baseline between the same two hosts was therefore diluted:
    because severity and ranking are driven by the scan-state ratio, a real burst could be
    under-severitied and pushed down the report by the surrounding benign traffic. Evidence,
    severity, and rank now all reflect the triggering window. Block and slow scans are
    unchanged (their evidence was already window- and span-correct by design).

Changed

  • The DNS detector's default surface threshold now matches its 1.8 high-entropy bar.
    On the measurement corpus, 87 of 105 DNS findings scored from 1.5 up to but not
    including 1.8; the 18 findings at or above 1.8 remain under the new default.
  • The default hunt is now a curated set rather than every available detector. Fresh
    installs, omitted selection, --detect=, and --detect=default select aws, beacon, dns,
    scan, and syslog; duration remains runnable by name while its severity evidence is rebuilt.
    Reports and dry runs disclose that opt-in remainder. Explicit --detect=all and existing
    configs with detect = "all" are unchanged and continue to run everything. This is a
    quieter default, not a claim that detector logic became smarter.

sigwood v0.2.3

Choose a tag to compare

@helixmap helixmap released this 16 Jul 21:26

Added

  • The syslog detector now reads the live systemd journal directly - no sudo, no export
    step, no durable capture file. On a systemd host sigwood syslog (and sigwood hunt) invoke
    journalctl --output=json for the invoking user's readable system journal, normalize every
    entry into the same five columns as flat syslog, and analyze them identically. A new
    --syslog-source=auto|journal|files|off flag (and [sigwood].syslog_source, default auto)
    chooses the local carrier: auto prefers the journal and falls back to the configured
    syslog_dir files when journalctl is absent/unusable or the journal has no usable entries;
    journal requires it; files uses the flat directory only; off disables the local lane.
    Exactly one local carrier is used per run - sigwood never merges the journal and flat files.
    Journal access depends entirely on the invoking user's permissions; sigwood never invokes sudo.
    Requires systemd 236+ (for --output-fields); a single journal entry over 1 MiB fails the run
    visibly rather than being silently truncated. sigwood init gains a compound "system logs"
    choice that detects and recommends the best local source.
  • syslog ingestion now reads ISO-8601 / RFC-3339 timestamps in addition to RFC 3164 - the
    high-precision format stock rsyslog writes on Ubuntu/Pop 24.04 and newer. ISO stamps carry an
    explicit year and offset, so they convert directly to UTC and are not subject to the RFC 3164
    year-guess. Discovery accepts an ISO line only when it also carries a host and a colon-terminated
    program tag, so an ISO-timestamped application log (such as dnf.log) is not mistaken for syslog.

Changed

  • A connection graph now replays bytes across each connection's lifetime. sigwood graph on a
    Zeek conn log previously drew a connection's entire byte total in the single bin where it started,
    so one long transfer landed as a spike at its start and the rest of its life looked idle. Byte
    ribbons now spread that recorded total at a constant rate across the connection's recorded
    duration, while connection counts stay anchored at starts (those surfaces read conn starts).
    Zeek records a connection's total and duration but not the timing of bytes within it, so the even
    spread is an explicit model, not a claim that a bursty transfer was uniform - the artifact names
    the assumption, and discloses any recorded byte mass clipped outside the window it shows. Bands
    engage only where a row carries both positive bytes and a positive duration; zero-duration rows,
    DNS, and Pi-hole graphs are unchanged.
  • Existing installs migrate from files-only to journal-preferred auto. A config that never
    set syslog_source now defaults to auto on a systemd host, so sigwood syslog prefers the
    live journal (falling back to your syslog_dir files if the journal is unavailable or empty).
    The run discloses which source it used. To keep the previous file-only behavior, set
    syslog_source = "files". A config with an explicitly-empty syslog_dir continues to disable
    the local lane unchanged.
  • The "permission denied" message for an unreadable log now gives correct, least-privilege advice:
    it suggests usermod -aG only for a group-readable file owned by a known log-reader group
    (adm); for a root-only (0600) log, or one owned by a privileged group, it points at adjusting
    group ownership or an ACL instead of recommending a group join that would not help or would
    over-grant.

sigwood v0.2.2

Choose a tag to compare

@helixmap helixmap released this 15 Jul 05:35

Changed

  • The graph verb is now resilient on real-world data: a valid log always produces a graph.
    Instead of failing on a dense or very large source, the builder degrades within the player -
    adapting host and service rankings, bin width, and smoothing - until it fits. Timelines with
    a long, sparse lead-in are trimmed to the active window, so a handful of long-lived flows no
    longer stretch the axis. The player's header is now a labeled provenance readout - source,
    window, entity counts, bin size, and the exact sigwood hunt command for the same log - and a
    Zeek directory that lacks byte or service detail still renders a connection-count graph.

Fixed

  • sigwood graph no longer aborts with a "too dense for smooth interaction" error on an
    ordinary day of dense logs; an oversized graph degrades to fit the animation instead of
    failing.

Security

  • Releases are now published through PyPI Trusted Publishing: each distribution is built and
    uploaded by a tag-triggered CI workflow that authenticates to PyPI over OIDC, with no
    long-lived upload token stored anywhere. Every published file carries a PEP 740 publish
    attestation
    - a Sigstore-backed, verifiable record that it was uploaded by this project's
    release pipeline. This is publication provenance (which pipeline uploaded the file), not a
    claim about how the code was built or that the code is safe.

sigwood v0.2.1

Choose a tag to compare

@helixmap helixmap released this 13 Jul 07:40

[0.2.1] - 2026-07-13

Added

  • A graph verb (sigwood graph <path>) that writes a self-contained HTML artifact -
    one file, no server, no external resources, no network calls - replaying a log's flows
    as an animated Sankey with time, speed, and filter controls. A Zeek directory produces
    a conn graph (hosts vs the services they reach) and a dns graph (clients vs the domains
    they look up); Pi-hole adds a disposition lane showing what happened to each query
    (blocked, forwarded, cached, or local). Like digest, it reads before the
    allowlist and states facts, not verdicts, and every artifact ends with the exact
    sigwood hunt command for the same log.

Changed

  • The curated common domain allowlist now ships its generic UUID-label rule disabled
    (commented out). It suppressed any query embedding a UUID under any domain - a lexical
    shape an exfil or beacon labeling scheme can simply choose - so it could drop hostile
    queries before analysis. Devices that chatter with UUID labels are better suppressed by
    their vendor apex; some users may see new DNS findings from such devices after
    upgrading. The rule stays in the file, commented, for anyone who wants to re-enable it
    knowingly.
  • A syslog burst finding without an observed reboot is no longer described as
    "resembling a boot or batch event" - the neutral description states the cluster and
    nothing more, and the reboot wording appears only on bursts a detected boot event
    actually claimed.
  • TOML allowlist stanzas are now documented as what they do today - structured
    suppression with a comment and per-detector scope - rather than as a classification
    mechanism no shipped detector consumes yet. A stanza missing its match key (or a
    stanza file that isn't valid TOML) now fails with an actionable message naming the
    file instead of a raw KeyError.

Fixed

  • Pointing sigwood at a directory that holds a mix of log families no longer silently
    drops the minority families: the run now says on stderr which family won the routing
    vote and what was sampled, a detector skipped because the positional target routed
    elsewhere now says so (instead of the misleading "not configured"), and digest
    notes each directory it skips in a multi-file invocation instead of passing over it
    without a word.
  • A detector that crashes mid-run no longer reads as a clean night. The run still
    continues past the crash (sibling detectors' findings are unaffected), but the failure
    is now disclosed everywhere a scheduled run looks: the process exits nonzero, the JSON
    feed carries it under run_summary.detectors_failed (name → reason; empty {} on a
    clean run), the HTML/PDF report header shows a failure row, and the text report ends
    with a failed: line so a saved report is honest too. The FAQ's cron alerting recipe
    now pages on failed detectors as well as findings.

sigwood v0.1.1

Choose a tag to compare

@helixmap helixmap released this 11 Jul 07:33

[0.1.1] - 2026-07-11

Changed

  • Source and development install instructions in the README now create a virtualenv
    first, so a pip install -e in a fresh clone doesn't hit the PEP 668
    externally-managed-environment refusal on Debian, Raspberry Pi OS, or Fedora.
  • Splunk export now reports a TLS certificate-verification failure with an actionable
    message, naming the verify_tls = false setting under [export.splunk] for a
    self-signed certificate on a trusted network, instead of a generic connection error.
  • Source distributions no longer bundle the test suite - it shipped without the support
    files needed to collect it, so it was unusable - and a release-time gate checks the
    sdist carries no tests.
  • Package metadata now declares the license as the SPDX license = "MIT" expression with
    license-files, dropping the deprecated MIT license classifier, and sets the operating
    system classifier to POSIX.

Fixed

  • The beacon detector no longer silently drops a genuine beaconing flow to a unicast host
    whose IPv4 address ends in .255 (a valid host in any network wider than a /24). Its
    pre-filter now classifies non-unicast destinations and sources with the standard-library
    ipaddress module rather than a string-prefix test, so multicast, link-local (including
    IPv4 169.254.0.0/16), and the 255.255.255.255 limited broadcast are still excluded
    before scoring while real unicast hosts are kept.
  • Copy polish in the init wizard prompts.
  • sigwood init no longer lets a source you explicitly skip during setup quietly come
    back. A skipped default source (Zeek or syslog) is now written as
    <key> = "" # disabled during setup, so the config merge cannot silently re-enable it;
    removing a source still reverts it to the shipped default.
  • The --dry-run preview now counts Zeek logs in dated zeekctl layouts (the
    YYYY-MM-DD/ and current/ subdirectories) the way a real run discovers them, and shows
    (unreadable) for a directory it cannot read instead of a misleading (0 files).
  • A closed downstream pipe (for example sigwood hunt | head) now exits quietly with
    Unix SIGPIPE semantics instead of printing a BrokenPipeError traceback.
  • The loader no longer crashes at import on platforms without the POSIX grp/pwd
    modules; permission-denied diagnostics fall back to numeric owner and group ids there.

Security

  • Output now strips terminal control bytes through a single sanitizer - including the
    surrogate-escaped bytes a non-UTF-8 filename decodes to - so a hostile file or directory
    name in a scanned tree can no longer inject terminal escape sequences, whether it reaches
    the analyst through a stderr diagnostic or a text, html, or csv report.
  • That sanitizer now also covers the remaining command-line surfaces - the error boundary,
    digest narration, the --dry-run banner, and loader and export status messages - so a
    hostile file, directory, or configured path name cannot inject terminal escapes through
    any of them.