feat: implement logging.file and configurable decision types, rebuild the docs (#94)
Phase 2 and 3 of the documentation plan, plus three items requested
directly. Every gate here was mutation-tested, because three of them
shipped failing open and were only caught that way.
logging.file is implemented. It was parsed and read by nothing; all
output went to stderr. Lines now go to BOTH stderr and the file, so
journalctl and docker logs keep working — the file is an addition, not a
redirection. Append, mode 0640 (log lines record banned clients' IP
addresses), parent directories deliberately not created, and a failure
to open is a startup error naming the path rather than a silent fallback.
A log file you configured and did not get is the failure class the
previous PR spent its time removing from the docs.
crowdsec.supported_decisions_types does something now. It was declared,
defaulted, mapped to an environment variable and read by nothing while
parseDecision hardcoded "ban". CrowdSec's decision type is a free string,
so a scenario emitting a custom type now has a way to be enforced. The
design follows what the Local API actually does, measured rather than
assumed: `type` matches exactly (type=ban,throttle returns zero) and
omitting it returns every type, so the default set keeps its server-side
filter and only a widened set fetches everything and filters locally.
The configuration reference is generated from the Go struct: a committed
artefact the docs render, regenerated and diff-checked in CI, retiring
110 hand-typed Env/Default lines across both locales. It immediately
caught six keys publishing defaults they do not have — per-protocol
rule-placement overrides inherit from the global placement rather than
from the constructor.
The token layer replaces a 681-line stylesheet with seven single-concern
sheets. Dark on bare :root, light on :root[data-theme="light"], every
colour token restated in both. The heading ladder is monotonic at every
viewport width now; it used to render h3 smaller than h4, and the first
fix left h5 and h6 below the body copy they introduce.
The landing is rebuilt from one HomeContent interface both locales must
satisfy, with the FAQ structured data generated from the same array as
the visible answers. Its new "What it writes to your router" section
publishes the limits on the front page, including a CPU figure measured
on the production router — a transient peaking at 29-34% against a 7%
baseline for about six seconds, in 11 of 11 cycles — together with the
warning that SNMP monitoring will not show it, because hrProcessorLoad
reports a one-minute average that flattens the spike to roughly 9%.
The firewall rules are single-sourced from internal/manager and rendered
on both the reference page and the landing. This corrected a
long-standing undercount: a stock configuration writes EIGHT rules, not
four. The two passthrough counting rules were absent from every listing
on the site, and they are written even when metrics.enabled is false.
Verified byte-for-byte against a production RB5009.
Three gates were failing open and now are not. The contrast gate's
symmetry check compared resolved palettes, so it was structurally blind
to a colour declared on bare :root and never restated for light. The
schema extractor still produced 93 keys after every SetDefault call was
deleted. The i18n gate could not see a section vanishing from one locale
when its heading stayed behind, so it now compares component
invocations.
Off-site documentation links use the canonical jmrp.io address the
repository homepage advertises; the Astro site keeps the Pages address it
is generated for.
Thirteen review threads addressed. Two CodeQL alerts in the gate scripts
themselves, both about hand-rolled markdown parsing, are fixed — that
file is scheduled to move onto the MDX AST.
Claude-Session: https://claude.ai/code/session_01ENguejGi5gZcxoMbCKWFBy