feat(docs): read the Person entity from its canonical source (#75)
Part of a rollout across the six sites that publish the
`https://jmrp.io/#person` entity. jmrp.io now serves it as a standalone
document; this site consumes it instead of restating it.
## Problem
This site restated the entity by **copying its values**. Across the five
project sites doing the same, that convention failed twice:
- `cs-routeros-bouncer` published *"Open-source developer; author and
maintainer of cs-routeros-bouncer"* as the `description` of the shared
entity — a project description on a person node.
- `Cloudflare-DNS-Updater` advertised an avatar URL that **returned
404** — a fingerprinted Astro asset the portfolio had long since
rebuilt.
Both are fixed upstream. Values that must not diverge should not be
copied at all.
## Change
The node is fetched at build time from the canonical document and
spliced verbatim, so this document and jmrp.io describe the same entity
with the same values **by construction**.
```
raw.githubusercontent.com/jmrplens/jmrp.io/main/public/identity/person.jsonld
```
Fetched from GitHub rather than `https://jmrp.io` **on purpose**: this
build runs on a CI runner, and jmrp.io sits behind Cloudflare, CrowdSec
and a MikroTik bouncer — the one place a blocked runner IP would
silently degrade this site to a stale snapshot. GitHub serves the same
bytes and is already a hard dependency of the build (the checkout comes
from it). If GitHub is down there is no build anyway, which makes the
committed snapshot a belt-and-braces fallback rather than a real
dependency.
## What it gains
Beyond removing the drift risk, this site now asserts things it never
did:
| | Before | After |
| --- | --- | --- |
| `alternateName` | `"jmrplens"` | **8 observed variants**, including
`José M. Requena-Plens` — the citation form in every published paper |
| `sameAs` | 6–10 | **14**, including two forge accounts with signed
OpenPGP proofs |
| `owns` | absent | **11 projects** |
## Deliberate choices
- **`knowsAbout` stays local and is merged, not replaced.** It is
multi-valued, so the project's own topics reinforce the canonical list
instead of conflicting with it. The full local set is kept — not just
the entries the canonical lacks — so this site keeps asserting its own
topics even if that list changes.
- **`creator` + `maintainer` added alongside `author`** on the software
nodes, matching the three agents `jmrp.io/projects/` emits for those
`@id`s. They merge, so a partial set per document was untidy rather than
wrong.
- **Visible footer link to the project index**, localized where the site
is bilingual. Deliberately **not** `rel="me"`: it is a page, not an
identity profile.
- **`pnpm run identity:sync`** refreshes the snapshot deliberately, in
its own commit, so the identity this site would ship without a network
is visible in review rather than frozen at whatever it was the day the
file was added.
## Verification
Built and re-parsed out of the built HTML:
```
props identical to canonical : YES
extra props : none
alternateName 8 · owns 11 · sameAs 14
knowsAbout: canonical 19 + this project's own, no duplicates
```
The full JSON-LD node inventory was compared before and after — not just
the `Person` — after an earlier attempt in this rollout silently deleted
two sibling nodes while still producing a green build.
https://claude.ai/code/session_01Ecf6hESxYdc7f1UV1vHrQU
<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/jmrplens/cs-routeros-bouncer/pull/75?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. -->
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 -->
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 -->