From 2c8788d4b72f53a661498ec31532fdd8e4c206c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Jul 2026 03:13:48 +0000 Subject: [PATCH 1/3] Add SEO metadata, robots.txt/sitemap, and PyPI keywords - Homepage: Open Graph + Twitter Card tags, canonical URL, SVG favicon, SoftwareApplication JSON-LD, and a keyword-forward title (closes #479) - Pages build: emit robots.txt pointing at both sitemaps and a root sitemap.xml covering the homepage; publish og-card.png (closes #480) - pyproject: expand PyPI keywords for discovery (closes #483) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Mf6GAt4wpvSXPTDKGu2q6B --- pyproject.toml | 7 ++++++- scripts/build_pages_site.sh | 20 +++++++++++++++++++ src/homepage/index.html | 37 ++++++++++++++++++++++++++++++++++-- src/homepage/og-card.png | Bin 0 -> 52195 bytes 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/homepage/og-card.png diff --git a/pyproject.toml b/pyproject.toml index d7cfee0..83e8904 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,12 @@ description = "CLI-first CAN security research toolkit" readme = "README.md" requires-python = ">=3.12" license = "GPL-3.0-or-later" -keywords = ["can", "can-bus", "j1939", "uds", "security", "cli"] +keywords = [ + "can", "can-bus", "canfd", "j1939", "uds", "doip", "iso-tp", + "automotive", "automotive-security", "car-hacking", "vehicle", + "socketcan", "candump", "dbc", + "fuzzing", "reverse-engineering", "security", "cli", "mcp", +] classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", diff --git a/scripts/build_pages_site.sh b/scripts/build_pages_site.sh index cb9949a..48b5db5 100644 --- a/scripts/build_pages_site.sh +++ b/scripts/build_pages_site.sh @@ -10,6 +10,26 @@ uv run mkdocs build --strict cp "$repo_root/src/homepage/index.html" "$site_root/index.html" cp "$repo_root/src/homepage/site-brutalist.jsx" "$site_root/site-brutalist.jsx" +cp "$repo_root/src/homepage/og-card.png" "$site_root/og-card.png" + +site_url="https://hexsecs.github.io/canarchy" + +cat > "$site_root/robots.txt" < "$site_root/sitemap.xml" < + + + $site_url/ + + +EOF if [[ ! -f "$site_root/index.html" ]]; then echo "missing published homepage: $site_root/index.html" >&2 diff --git a/src/homepage/index.html b/src/homepage/index.html index 3919ab6..aa50679 100644 --- a/src/homepage/index.html +++ b/src/homepage/index.html @@ -3,8 +3,41 @@ - CANarchy — CAN bus security for the humans and agents - + CANarchy — CAN bus & J1939 security research toolkit (CLI, TUI, MCP) + + + + + + + + + + + + + + + + + + + diff --git a/src/homepage/og-card.png b/src/homepage/og-card.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7e6a41619581a8715e1ef6337347a39e1f60b4 GIT binary patch literal 52195 zcmeFZWl)@5yCuwnBse4lhX4UWa1X8t?w+6_1PJafjgz3k2^!oXxYGoJy9IY|+_k&s zZt|Wp?@Y~~sjuprsj0)SyBgTN``VYTwRV41RhGfQB*R2PLc)=geXEXyg!&T+3E34L z8F;1r#Xc4i5bVV!k(c-|b?bF$qP3a&yF#0fiAh|h3bB1||I~TB-!xSS2@Vu*++I4{ zzG}R0-Cn|4a5`*lzi7*dzB{I|Z(Q&?6xjAP+=kwCMp6nTA~u7BNzkg5u>LM6F1J|96cbMpVg%m;5+_#nB(0@bmv` zvHyLWN%P~owt)QqS`}Rn!a3KtyG`7!br9Oq1}q6X&t({<7XdZIUo) zz^GAMUjAE~XS(7)LAY|+5HfF^p!N0K>v#LBh z?GQ7x#$`oFQ8u=`Svn<|1$4HzMoIdDFhCT>2ZiYQ9%S=*zrS57qrIFHv&k2`b61kI?}nj-t&DGq$FetyK0~Bl#oA^~>hw z%M`I_>gt?cYXVR^s%i~m@hK$9zXu|&HijPPcU|+{@P-+BF>CUC?lVaS&rWe|`qGVz z!L5kNb9a5v)`U0_9ZFQZBK*t)>2gE|FZ5U|ni&4u$6k;HSAFmm3SbY87x?I*(UAqd zjc@qq+1K1mo9Zr~;F?rWjQc|OGtSp_ty8t9NTv^U+qDj zd>jd_Sn20`M?v;6$L#PP*hw!sP5=d)fyVBwC)mJI$y|OhS(kZeJ3$We(lqB!39|+<`2w*X2Uta}I%qXjqr^GJ#-RXYtkazseac zia*Dfr7|-!=*;yA_>BoI-@EU6#8MFc0h5f$n5J9~}r?I)LE( zK|*b~Z>J)-farQoAc3gP;yQJwzKe!Y zltKSW(hGcAT>cO9LxqkYLG3`Q&d58%lq@YMEQks}zXF+E*La{BN*zDCYjsT~lw!)I z6!`W0_AIXsHMm%ue^>Bd?uU7h()amn`8CtklXdg$z}TGb9ytfj(}bjf%Jo9#?!Fk4 zjXKw++VtBsNy_`hN8US+qbAIg@qE}gp}PjI187W^EQI43x2>>@`OqUJjNn2(hPY^!BK^H-T9dTwe*>8mQp(C2vWJ5`86RtaVb1V{2%(KJ9mUz4C zon`5^T|u=;P<~#L?RY3$eK>2Zd+=lZMJ?QhkkSaqvwTX4ZD&e^wN*eMQAG#cOMN@D z;pq#Sq|bt7M!dQuL)TDyj%d-)c~CpruQzd_0^m^Ga{bTnmD+H+fn5|%$q3UTs!eo| zSFO{m_v6jb<)i6VBO!{rzBWwdDM?E1g0KzVB^axVfp}nLe%G-VjQ21HWPqul`a#YU zVYrxJaS|kR(LAG>J)Onfuz}6~^z^+XLALN*WUGA=NMGjMm6!?b1&@4*l2C{VE{QA1)Mv$AbJuR1)XeiPH}UrN(o z4d2li9cU7wbLLl~T4y>=We=)WGFy_p<@md>AF%M= zH2dCLvHn22XS3xh6!~VhK{1{eAP+4{mb(_mR}D1s2kXloR⁡T$lixeAjC_ts*eg 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

&D?;BsUi(6;UJQRcf*XE}I%t)V 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�n@ z-d}!2Jl&sDf3;CTYi`V@#!~LsDegXS5H}D%uJX^xB>ao?^g{io}1*Wg@S2h z-e8{%oGW%{$5%MVn8K4kDKkIQeLZkj^2ooP?_6veNPD)OeII)1HA}~_%I|$R%{ zK(#p}Q)}*{4w&^_=lF6^N_H*uMnE0rBDa6ubxCsEJl1(7B|rXC^J zjX>G^X@bT)^Y8PDC1^5}b*5UG=zzSJ`go--6c_Z`+I3pULDsk%m2uK zMaR=^w2>4ScO&*))fx=0_zv#FLvzhE-W5*1udyStxhhAL7K~c`wNKkt4&|BW(TsI zdQPhBt|Z8$VTNahOX8z0sS7S(Tj7ahkoL!7j8BU!cGN$;tiqm>X_-5|_I4%98^esv z7-5&zr*Y@kWrXU0nX(&$%#y$PR1ON=3wC8mv)EfZt%Jh3H=ZgPJH-oBhYL0bzv8es zQ@BB*cUQl*euOJt%8C+cBHoXZ$=lsEUD+Cv{!l1DPbRDmI^55 z9Js6n+*vu`W12l7Laox(NsS~@P@(@F^!jzPQ{ee) z#L;{?8!r9T@{n)r+2-r>`qfzd+2eX9yvBR@a&;6A0Z`Kz^s0odG<|=5ama@>-qp;? zLWu#U)bgYuqosVyvKLP>im4cfB3lsiC2SdQeTp*WG=s#Y7mN|2tgAk$n+6E+6$D=6 z16U!{*Xe7Lq2v>Fzl1V_qQkwQ)}gYsTFgB?;zt;_Iv?M08^#r2x9B~pQ2}n7w9Za% zv;hD{LIwO=u=DL+2U#~W?yx^!nU`OlKg1}@Qpq{xBX092kIt{a?bWJ-Ma|TW|0UTI zVrp`Q>zjhFVaDynoG&jQ-x^r2mbAsrhE87`iCGB;f9U=WvVWQ;1S2QRIWDyQJ!8zh zn4USX|G@Lz#$L5Qpjq*hxKgmKUTX=xgB=POhd9N{x-?;!7`vbNmtGm_p%L=PgUmLk8L-zSjYN!f3ukbXb z8!v>ycTqEtS*;i<)bKtfb7yZW&VY6QuX`fxzs;pv+L*)x@J)^^m1&As^} zVPQnAILiS;rO)5;n1lC&{7W
elH@K&_dKf00u-P8*$4qMd$5Zi>S-#0f|#wOSH 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 literal 0 HcmV?d00001 From a992aa6b2665941a0ae85794e5bacab39d4081cf Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Jul 2026 03:16:25 +0000 Subject: [PATCH 2/3] Add changelog entry for site SEO metadata Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Mf6GAt4wpvSXPTDKGu2q6B --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6fed7f..11c8bad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht ### Added +* **SEO and link-preview metadata for the published site (#479, #480, #483).** The homepage now ships Open Graph and Twitter Card tags with a generated 1200×630 brand card (`src/homepage/og-card.png`), a canonical URL, an inline SVG favicon, a `SoftwareApplication` JSON-LD block, and a keyword-forward ``. The Pages build (`scripts/build_pages_site.sh`) additionally emits a root `robots.txt` referencing both the root and docs sitemaps and a root `sitemap.xml` covering the homepage, and publishes the social card. PyPI keywords in `pyproject.toml` expanded from 6 to 19 discovery terms. Closes #479, #480, #483. + * **`canarchy tui` is now a full-screen Textual app that streams the CAN bus live.** The TUI was a line-oriented shell that reprinted six panes after each command finished — it had no live/async streaming (`/capture` ran to completion then dumped a batch), no interactivity (no pane focus, filter, sort, row navigation, or scrollback), and hard-coded tiny backlogs. It is now a full-screen [Textual](https://textual.textualize.io/) application whose headline feature is watching the bus in real time. The engine/view split is preserved: the deterministic fold layer in `src/canarchy/tui.py` (`TuiState`, `_update_state`, and per-pane row extractors) stays as the tested model, a new `src/canarchy/tui_app.py` consumes it as the view, and a new `src/canarchy/tui_capture.py` `CaptureSession` streams frames through the existing `LocalTransport.capture_stream_events` generator on a daemon thread + queue (mirroring the transport gateway pattern), drained on the UI loop so one frame at a time flows through the same folding logic batch results use. Panes (Bus Status, Live Traffic, Decoded Signals, J1939 summary ribbon + table, UDS, an append-only Alerts log) are interactive Textual widgets: `/capture <iface>` starts a background live capture and `/stop` (or the `x` key) ends it; `/filter <pane> [text]` and `/sort <pane> [column]` filter and sort a pane; arrow keys navigate rows; `[`/`]` resize the operator-controlled backlog; `space` pauses the live feed; `c` clears; `ctrl+f` maximizes the focused pane. The command entry still runs real CANarchy commands and slash hotkeys through the shared parser, and the nested-front-end guard (`TUI_COMMAND_UNSUPPORTED`) is retained. `textual` is now a core dependency. **Breaking:** the interactive-only rewrite retires the text-mode shell and the one-shot `tui --command` mode; the TUI requires an interactive terminal and a non-TTY invocation prints guidance and exits non-zero — use the individual commands for scripted/automation runs. New Pilot-based app tests (`tests/test_tui_app.py`) and `CaptureSession` unit tests (`tests/test_tui_capture.py`) drive the scaffold backend's deterministic frame stream; the fold-layer snapshot tests are unchanged. A remaining follow-up: a finite-timeout `capture_stream` loop so live capture stops instantly on real hardware. * **`datasets convert`/`stream` gain a `decoded-signal-csv` source format for pre-decoded per-ID signal CSVs like SynCAN (#461).** `datasets convert`/`stream` only understood raw arbitration-ID + payload-byte sources (`hcrl-csv`, `candump`, `comma-rlog`), but a meaningful class of public CAN intrusion-detection/ML datasets — SynCAN being a well-known MIT-licensed example — publish data as pre-decoded, normalized per-ID signal values (`Label,Time,ID,Signal1_of_ID,...`) rather than frames. Ingesting them previously meant hand-rolling a one-off byte-packing script. A new `--source-format decoded-signal-csv` (also a `datasets_convert` MCP enum value) handles this shape: it auto-detects the `Time`/`ID`/optional `Label` columns plus any per-ID signal columns, maps each row's `ID` token to a deterministic arbitration ID (`0x`-hex, plain decimal, a trailing integer like `id10` -> `10`, else a stable SHA-1-derived 11-bit ID), and packs the present normalized signal values into payload bytes as big-endian uint16 (`round(value * 0xFFFF)`, clamped), skipping empty signal cells. Real `Time` and `Label` are preserved — `Label` surfaces as `payload.label` in JSONL output, like `hcrl-csv`. The reconstructed payload bytes are synthetic (derived from normalized signal values, not captured verbatim), so the output suits frame-level analysis (`stats`, `re_*`, `compare`) with real timing/ID structure rather than byte-exact ECU replay; this is documented inline. The adapter (`src/canarchy/dataset_convert.py`) is pure and streaming, with new unit/CLI tests (`tests/test_dataset_provider.py`) over a SynCAN-style fixture and updated command/design specs. Closes #461. * **`fuzz identify` — narrow a fuzz log to the culprit frame by replaying bisected windows (#464).** After a fuzz campaign or a captured anomaly, an operator knows *that* some frame in a log caused an effect but not *which* one; CANarchy had replay/fuzz/guided-fuzz primitives but no explicit narrowing workflow (CaringCaribou's `fuzzer identify`). A new `canarchy fuzz identify <log>` loads a candump capture or a JSONL fuzz log / replay plan and bisects toward the culprit: each round replays the lower half of the current candidate range, and the operator records whether the effect reproduced. Observations are supplied non-interactively via `--observe effect|no-effect` (repeatable) and/or a JSON `--observations` file, so the workflow stays automation-friendly with no hidden prompts. The narrowing engine (`src/canarchy/fuzz_identify.py`) is pure and deterministic — given the frame count and observations it returns the candidate range, the next window, and a resolved culprit in `ceil(log2(N))` rounds — so it is exhaustively unit-tested without hardware. One round runs per invocation: with an `--interface` (and without `--dry-run`) it replays the next window live behind the active-transmit safety model (`stderr` preflight, `--ack-active`) and reports the state so the operator records an observation and re-invokes; with a complete observation sequence up front it resolves the culprit with no transmission. JSON output includes the replayed/next window, the recorded observations, the candidate range and frames, the resolved culprit, a `confidence` value and `rationale`, and provenance (`source`, `frame_count`, `planned_rounds`). `--max-window` refuses to replay an over-large window (`FUZZ_IDENTIFY_WINDOW_TOO_LARGE`); invalid logs/observations return structured errors (exit 1). `fuzz identify` is a stateful, multi-round human-in-the-loop workflow and stays CLI-only (a documented MCP exclusion). New module + unit/CLI tests (`tests/test_fuzz_identify.py`) and design/test specs (`fuzz-identify.md`). Closes #464. From d1a594ceb9f3aaec1d817f7bf9d49b7a6e399aac Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Mon, 6 Jul 2026 03:21:40 +0000 Subject: [PATCH 3/3] Document robots.txt limitation on GitHub project pages Crawlers only honor robots.txt at the host root, so the generated /canarchy/robots.txt cannot advertise the sitemaps on its own; they must be submitted to search engines directly. Noted in the build script per PR review. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mf6GAt4wpvSXPTDKGu2q6B --- scripts/build_pages_site.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/build_pages_site.sh b/scripts/build_pages_site.sh index 48b5db5..a8f2ba2 100644 --- a/scripts/build_pages_site.sh +++ b/scripts/build_pages_site.sh @@ -14,6 +14,12 @@ cp "$repo_root/src/homepage/og-card.png" "$site_root/og-card.png" site_url="https://hexsecs.github.io/canarchy" +# On a GitHub *project* page this file is served from /canarchy/robots.txt, +# which crawlers do not treat as authoritative (only the host root counts), +# so the sitemaps below must also be submitted directly to search engines +# (e.g. Google Search Console / Bing Webmaster Tools). The file still serves +# SEO tooling that probes it and becomes authoritative if the site ever +# moves to a custom domain served from the root. cat > "$site_root/robots.txt" <<EOF User-agent: * Allow: /