at main
2 folders
5 files
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
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
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
feat(docs): new brand mark, CrowdSec indigo, four diagrams, and the i18n gate on the MDX AST (#95)
## Description
Phases 5 to 7 of the documentation plan, plus the technical debt that
had accumulated in the i18n gate, plus a new brand mark chosen from
twenty-nine candidates.
## Type of change
- [x] ✨ New feature (non-breaking change that adds functionality)
- [x] 📖 Documentation update
- [x] 🧪 Tests / tooling / CI
- [x] 🧹 Refactoring / code cleanup
## Changes
### The i18n parity gate is rewritten onto the MDX AST
It had produced **three defects in one area** — an HTML-comment
terminator that missed `--!>`, a chained-replace sanitisation that left
a bare `<!--` behind, and a one-character backtick delimiter that leaked
double-backtick spans. Two were found by CodeQL, one in review. Three
findings in one file say the approach is wrong, not that the patches
were bad; parsing makes those classes unreachable.
All four parser packages were **already resolved in the lockfile**, so
declaring them added nothing to install.
Verified by a **differential harness over all 56 corpus files across six
dimensions**, which caught a regression the rewrite introduced and
surfaced a genuine improvement: three multi-path `ConfigOption`
invocations that used to collapse into one indistinguishable bucket are
now keyed individually. Demonstrated: a Spanish page pointing at a
non-existent config path **passed** the old gate and fails the new one.
It also corrected a claim the old file asserted: an HTML comment closed
with `--!>` is **not** closed for the renderer. CommonMark ends an HTML
block only at `-->`, so the earlier "fix" was wrong in the other
direction.
### A new mark
The shield is gone. Rendered side by side at the sizes that matter it
was, unmistakably, **the WiFi glyph** — a shield containing concentric
arcs radiating from a dot is the standard signal icon at 104px and at
23px. Nobody noticed until the candidates were laid out and looked at.
For a network tool that is worse than generic; it is misleading.
What replaces it: four lanes meeting a rail, three crossing and
continuing, one stopping dead at it. The proportion is deliberate — a
firewall does not block everything, it blocks the exception. **Three
primitives, 282 bytes of geometry, no ids, no defs, no mask**, against
the 3.8 KB the shield needed.
Chosen from 29 candidates across two waves. The first wave's five
directions all came from the data side — list, chain, loop, flow — and
its winner drew a generic list; the siblings anchor on a git ref and an
open book, objects that only exist in their domains. The second wave
entered through the domain and beat it on all four judging lenses.
### The accent moves to CrowdSec's indigo
`#4d4a98` light, `#807dc0` dark — the same hue and saturation raised
until it clears 4.5:1 against the **surface** rather than merely the
page. The page-only value failed twice, on the terminal prompt glyph and
the sidebar pill, and the contrast gate caught both.
This is ecosystem alignment, not a return to the co-branding retired
earlier: that palette paired CrowdSec indigo *with* MikroTik teal,
implying endorsement by two vendors including the hardware one. This is
a CrowdSec bouncer, listed on their Hub. **Worth checking their brand
guidelines before a release goes out.**
The rail takes the accent because the boundary is what the product is.
Pass and stop are carried by **length before colour**: the accent and
the muted tone measure 1.19:1 apart, so a mark relying on that pair to
tell them apart would come very close to collapsing into one tone in
greyscale.
CrowdSec's amber moves to `--rb-status-warn`, where it does semantic
work rather than decorating.
### Four architecture diagrams, under one convention
The rule-placement ladder, the decision funnel in three swimlanes, the
reconciliation diff and the shutdown order. Solid is a write, dashed is
a read, carried on three channels — line pattern, then colour, then a
legend inside each diagram — so they survive greyscale, colour vision
deficiency and a printout.
Six accuracy defects in them were corrected against the Go source. One
claim was rewritten rather than deleted: entries whose comment does not
match the prefix were described as "never listed", but `ListAddresses`
queries on the list name alone and filters client-side, so they **are**
fetched every pass and only then dropped — which costs transfer on a
list an operator may be sharing.
### Navigation
Sidebar depth hierarchy, pagination that names its destination (two
entries were both labelled "Overview"), scroll-into-view, and a **mobile
theme toggle** — there was none, the header hid the control below 50rem.
It never writes `data-theme` or `localStorage` itself: it drives
Starlight's own select and reads state back via a MutationObserver,
because anything else desynchronises from the desktop control and the
before-paint script.
## Testing
- [x] `pnpm analyze` exits 0 — config schema check, brand raster check,
i18n parity, contrast, `astro check`, ESLint, Prettier, build,
`html-validate`
- [x] `go build ./...`, `go test -race ./...`, golangci-lint, gosec,
staticcheck, govulncheck all clean; markdownlint clean
- [x] Every gate mutation-tested in both directions, including the
parity gate's twelve behaviours and the new brand-raster check
- [x] Rendered SVGs inspected in `dist/` — all 62 diagram edges parse,
dashed edges carry a real `stroke-dasharray`, and the mermaid CSS
ordering was checked so the dotted class actually wins
- [ ] Tested against a real MikroTik router (not applicable — no
behavioural change ships here)
## Notes for review
Four claims **of my own** were wrong and are corrected in the last
commit: a stale contrast figure in all three copies of the mark, the
`prefers-contrast` block still pointing at the retired teal, a comment
describing lanes "cut by the rail" that are unbroken rectangles, and a
false provenance claim about the sibling status fills' saturation.
An intermittent `analyze` failure reported by three agents did not
reproduce — 8 clean builds out of 8 in isolation. The cause was
concurrent agents building into the same `dist/`.
https://claude.ai/code/session_01ENguejGi5gZcxoMbCKWFBy
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/jmrplens/cs-routeros-bouncer/pull/95?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Introduced refreshed CrowdSec Indigo branding across the documentation
site, including responsive wordmark, icons, and social images.
* Added light/dark theme support for diagrams and documentation images.
* Added mobile theme switching and improved sidebar, header, pagination,
and navigation behavior.
* Added clearer architecture diagrams, flow explanations, and Grafana
dashboard imagery.
* **Documentation**
* Expanded English and Spanish guidance for decision filtering,
firewall-rule placement, reconciliation, shutdown behavior, and decision
lifecycles.
* Clarified read/write diagram conventions and operational edge cases.
* **Bug Fixes**
* Improved validation for localized documentation structure and
generated brand assets.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
feat(docs): new brand mark, CrowdSec indigo, four diagrams, and the i18n gate on the MDX AST (#95)
## Description
Phases 5 to 7 of the documentation plan, plus the technical debt that
had accumulated in the i18n gate, plus a new brand mark chosen from
twenty-nine candidates.
## Type of change
- [x] ✨ New feature (non-breaking change that adds functionality)
- [x] 📖 Documentation update
- [x] 🧪 Tests / tooling / CI
- [x] 🧹 Refactoring / code cleanup
## Changes
### The i18n parity gate is rewritten onto the MDX AST
It had produced **three defects in one area** — an HTML-comment
terminator that missed `--!>`, a chained-replace sanitisation that left
a bare `<!--` behind, and a one-character backtick delimiter that leaked
double-backtick spans. Two were found by CodeQL, one in review. Three
findings in one file say the approach is wrong, not that the patches
were bad; parsing makes those classes unreachable.
All four parser packages were **already resolved in the lockfile**, so
declaring them added nothing to install.
Verified by a **differential harness over all 56 corpus files across six
dimensions**, which caught a regression the rewrite introduced and
surfaced a genuine improvement: three multi-path `ConfigOption`
invocations that used to collapse into one indistinguishable bucket are
now keyed individually. Demonstrated: a Spanish page pointing at a
non-existent config path **passed** the old gate and fails the new one.
It also corrected a claim the old file asserted: an HTML comment closed
with `--!>` is **not** closed for the renderer. CommonMark ends an HTML
block only at `-->`, so the earlier "fix" was wrong in the other
direction.
### A new mark
The shield is gone. Rendered side by side at the sizes that matter it
was, unmistakably, **the WiFi glyph** — a shield containing concentric
arcs radiating from a dot is the standard signal icon at 104px and at
23px. Nobody noticed until the candidates were laid out and looked at.
For a network tool that is worse than generic; it is misleading.
What replaces it: four lanes meeting a rail, three crossing and
continuing, one stopping dead at it. The proportion is deliberate — a
firewall does not block everything, it blocks the exception. **Three
primitives, 282 bytes of geometry, no ids, no defs, no mask**, against
the 3.8 KB the shield needed.
Chosen from 29 candidates across two waves. The first wave's five
directions all came from the data side — list, chain, loop, flow — and
its winner drew a generic list; the siblings anchor on a git ref and an
open book, objects that only exist in their domains. The second wave
entered through the domain and beat it on all four judging lenses.
### The accent moves to CrowdSec's indigo
`#4d4a98` light, `#807dc0` dark — the same hue and saturation raised
until it clears 4.5:1 against the **surface** rather than merely the
page. The page-only value failed twice, on the terminal prompt glyph and
the sidebar pill, and the contrast gate caught both.
This is ecosystem alignment, not a return to the co-branding retired
earlier: that palette paired CrowdSec indigo *with* MikroTik teal,
implying endorsement by two vendors including the hardware one. This is
a CrowdSec bouncer, listed on their Hub. **Worth checking their brand
guidelines before a release goes out.**
The rail takes the accent because the boundary is what the product is.
Pass and stop are carried by **length before colour**: the accent and
the muted tone measure 1.19:1 apart, so a mark relying on that pair to
tell them apart would come very close to collapsing into one tone in
greyscale.
CrowdSec's amber moves to `--rb-status-warn`, where it does semantic
work rather than decorating.
### Four architecture diagrams, under one convention
The rule-placement ladder, the decision funnel in three swimlanes, the
reconciliation diff and the shutdown order. Solid is a write, dashed is
a read, carried on three channels — line pattern, then colour, then a
legend inside each diagram — so they survive greyscale, colour vision
deficiency and a printout.
Six accuracy defects in them were corrected against the Go source. One
claim was rewritten rather than deleted: entries whose comment does not
match the prefix were described as "never listed", but `ListAddresses`
queries on the list name alone and filters client-side, so they **are**
fetched every pass and only then dropped — which costs transfer on a
list an operator may be sharing.
### Navigation
Sidebar depth hierarchy, pagination that names its destination (two
entries were both labelled "Overview"), scroll-into-view, and a **mobile
theme toggle** — there was none, the header hid the control below 50rem.
It never writes `data-theme` or `localStorage` itself: it drives
Starlight's own select and reads state back via a MutationObserver,
because anything else desynchronises from the desktop control and the
before-paint script.
## Testing
- [x] `pnpm analyze` exits 0 — config schema check, brand raster check,
i18n parity, contrast, `astro check`, ESLint, Prettier, build,
`html-validate`
- [x] `go build ./...`, `go test -race ./...`, golangci-lint, gosec,
staticcheck, govulncheck all clean; markdownlint clean
- [x] Every gate mutation-tested in both directions, including the
parity gate's twelve behaviours and the new brand-raster check
- [x] Rendered SVGs inspected in `dist/` — all 62 diagram edges parse,
dashed edges carry a real `stroke-dasharray`, and the mermaid CSS
ordering was checked so the dotted class actually wins
- [ ] Tested against a real MikroTik router (not applicable — no
behavioural change ships here)
## Notes for review
Four claims **of my own** were wrong and are corrected in the last
commit: a stale contrast figure in all three copies of the mark, the
`prefers-contrast` block still pointing at the retired teal, a comment
describing lanes "cut by the rail" that are unbroken rectangles, and a
false provenance claim about the sibling status fills' saturation.
An intermittent `analyze` failure reported by three agents did not
reproduce — 8 clean builds out of 8 in isolation. The cause was
concurrent agents building into the same `dist/`.
https://claude.ai/code/session_01ENguejGi5gZcxoMbCKWFBy
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/jmrplens/cs-routeros-bouncer/pull/95?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Introduced refreshed CrowdSec Indigo branding across the documentation
site, including responsive wordmark, icons, and social images.
* Added light/dark theme support for diagrams and documentation images.
* Added mobile theme switching and improved sidebar, header, pagination,
and navigation behavior.
* Added clearer architecture diagrams, flow explanations, and Grafana
dashboard imagery.
* **Documentation**
* Expanded English and Spanish guidance for decision filtering,
firewall-rule placement, reconciliation, shutdown behavior, and decision
lifecycles.
* Clarified read/write diagram conventions and operational edge cases.
* **Bug Fixes**
* Improved validation for localized documentation structure and
generated brand assets.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
docs: Spanish locale, documentation audit fixes, and dependency updates (#63)
## Description
Three related pieces of work on the documentation and tooling:
1. **Spanish (es) locale** for the Starlight docs site — full
translation of all 27 pages, following the same i18n pattern as
gitlab-mcp-server.
2. **Documentation audit fixes** — a docs-vs-code audit found
inaccuracies (wrong Prometheus label names, a fictional bulk-script
name, a TLS troubleshooting remedy that targeted the wrong subsystem,
stale Grafana dashboard inventory, broken README screenshots, etc.). All
confirmed against the source and fixed in both languages.
3. **Dependency updates** — Go tooling, docs packages, and pnpm 11
pinning.
## Type of change
- [x] 📖 Documentation update
- [x] 🔒 Security / dependency update
- [x] 🧪 Tests / tooling / CI
## Changes
- Add `es` locale: 27 translated pages under `src/content/docs/es/`,
translated sidebar/badges, language switcher, sitemap `hreflang`
alternates, locale-aware JSON-LD (TechArticle/BreadcrumbList/HowTo),
localized footer, and a Languages section in `llms.txt`
- Prometheus reference: correct label names (`proto` not `ip_type`;
`operation` not `component`), real `group`/`param`/`value` schema for
`config_info`, document `last_operation_duration_seconds`,
`routeros_identity`, `bulk_add`/`bulk_remove`, `unchanged`
- README: fix broken Grafana screenshot paths, regenerate dashboard
inventory from `dashboard.json` (41 panels / 9 rows), sync metrics
table, fix Router Setup anchor
- Troubleshooting: RouterOS TLS errors now point at
`mikrotik.tls_insecure` (no CA-path option exists for the MikroTik
connection)
- Firewall docs: `block_output` `interface` + `interface_list` are ANDed
(no precedence); bulk script is `crowdsec-bulk-import` run by internal
id; `/health` is always served
- Homepage version badge now derives from the latest git tag at build
time (shared `src/lib/latest-release.mjs`, also used by the JSON-LD)
- Deps: gosec v2.27.1, golang.org/x/tools v0.47.0, golang.org/x/vuln
v1.5.0; astro 7.0.6, @astrojs/starlight 0.41.3, typescript-eslint
8.63.0; pin `packageManager: pnpm@11.10.0` and switch
`pnpm/action-setup` to `package_json_file` (was hardcoded `version: 10`)
## Testing
- [x] Unit tests pass (`go test ./...`)
- [x] Static analysis passes (`actionlint` on edited workflows;
eslint/prettier on docs)
- [x] Documentation checks pass (`pnpm analyze`: astro check, lint,
format, build 55 pages, html-validate)
- [ ] Tested against a real MikroTik router (not applicable — docs/CI
only)
- [ ] Docker image builds (not applicable — no Go source changes, only
go.mod tool deps)
Additionally verified: all 90 internal doc links + anchors resolve in
both languages, and en/es structural parity (headings/code fences) holds
on all 27 pages.
## RouterOS compatibility
Not applicable — no runtime behavior changes.
## Checklist
- [x] My code follows the project style guidelines
- [ ] I have added tests for my changes (not applicable)
- [x] I have updated the documentation (if applicable)
- [ ] I have updated CHANGELOG.md (docs/CI-only change; no
release-facing behavior)
- [ ] I have updated release/configuration files when behavior changed
(not applicable)
- [x] My changes do not introduce new warnings
https://claude.ai/code/session_01DqSrSogARwnqPjVcJWYAXb
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/jmrplens/cs-routeros-bouncer/pull/63?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Expanded and clarified setup, configuration, monitoring, security, and
troubleshooting docs.
* Added a full Spanish documentation set, including getting started,
configuration, architecture, development, monitoring, and examples.
* Updated metrics and Grafana dashboard references with a broader set of
panels and metric details.
* **New Features**
* Added language-aware site content, including localized footer text and
Spanish navigation/support.
* Version badges now automatically show the latest release by default.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->