From a03751b72d443bcee94b5a222806915329bfa03e Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Mon, 4 May 2026 11:02:44 +0300 Subject: docs: render llms.txt at build time from a Jinja template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the static curated llms.txt with a build-time render that interpolates html_baseurl and release into the file, so the file always matches the branch it was built from. llms-full.txt is already auto-generated by sphinx-llms-txt. πŸ€– Generated by [robots](https://vyos.io) --- docs/_html_extra/llms.txt | 52 --------------------------------------------- docs/_templates/llms.txt.j2 | 52 +++++++++++++++++++++++++++++++++++++++++++++ docs/conf.py | 16 ++++++++++++++ 3 files changed, 68 insertions(+), 52 deletions(-) delete mode 100644 docs/_html_extra/llms.txt create mode 100644 docs/_templates/llms.txt.j2 (limited to 'docs') diff --git a/docs/_html_extra/llms.txt b/docs/_html_extra/llms.txt deleted file mode 100644 index a8f5b002..00000000 --- a/docs/_html_extra/llms.txt +++ /dev/null @@ -1,52 +0,0 @@ -# VyOS - -> VyOS is a free, open-source network operating system based on Debian GNU/Linux. -> It provides routing, firewall, and VPN functionality via a unified CLI using -> `set`/`delete`/`show` command hierarchy. Current rolling release is 1.5.x (circinus). - -VyOS configuration follows a tree structure. All configuration commands start with -`set` (to add/change) or `delete` (to remove). Changes are staged and applied with -`commit`. The CLI hierarchy maps directly to the documentation structure. - -## Quick Start - -- [Quick Start Guide](https://docs.vyos.io/en/latest/quick-start.html): Minimal setup walkthrough -- [CLI Overview](https://docs.vyos.io/en/latest/cli.html): Command-line interface usage - -## Configuration - -- [Firewall](https://docs.vyos.io/en/latest/configuration/firewall/index.html): Zone-based firewall, rules, groups -- [Interfaces](https://docs.vyos.io/en/latest/configuration/interfaces/index.html): Ethernet, bonding, bridge, VLAN, tunnel, wireless -- [Protocols](https://docs.vyos.io/en/latest/configuration/protocols/index.html): BGP, OSPF, IS-IS, static routing, MPLS -- [VPN](https://docs.vyos.io/en/latest/configuration/vpn/index.html): IPsec, OpenVPN, WireGuard, L2TP, PPTP -- [NAT](https://docs.vyos.io/en/latest/configuration/nat/index.html): Source NAT, destination NAT, NAT66 -- [System](https://docs.vyos.io/en/latest/configuration/system/index.html): DNS, NTP, syslog, users, task scheduler -- [High Availability](https://docs.vyos.io/en/latest/configuration/highavailability/index.html): VRRP -- [Load Balancing](https://docs.vyos.io/en/latest/configuration/loadbalancing/index.html): WAN and reverse proxy -- [Containers](https://docs.vyos.io/en/latest/configuration/container/index.html): Podman-based container support -- [PKI](https://docs.vyos.io/en/latest/configuration/pki/index.html): Certificate management -- [Policy](https://docs.vyos.io/en/latest/configuration/policy/index.html): Route maps, prefix lists, access lists -- [Traffic Policy](https://docs.vyos.io/en/latest/configuration/trafficpolicy/index.html): QoS and shaping -- [Service](https://docs.vyos.io/en/latest/configuration/service/index.html): DHCP, DNS forwarding, SNMP, SSH, HTTPS API -- [VRF](https://docs.vyos.io/en/latest/configuration/vrf/index.html): Virtual routing and forwarding - -## Operations - -- [Operational Commands](https://docs.vyos.io/en/latest/operation/index.html): Show, monitor, restart commands - -## Installation - -- [Installation Guide](https://docs.vyos.io/en/latest/installation/index.html): Bare metal, virtual, cloud deployments - -## Automation - -- [Automation](https://docs.vyos.io/en/latest/automation/index.html): Ansible, Terraform, HTTP API, NETCONF - -## Configuration Examples - -- [Blueprints](https://docs.vyos.io/en/latest/configexamples/index.html): Real-world topology examples - -## Optional - -- [Contributing](https://docs.vyos.io/en/latest/contributing/index.html): Development workflow -- [VPP](https://docs.vyos.io/en/latest/vpp/index.html): Vector Packet Processing integration diff --git a/docs/_templates/llms.txt.j2 b/docs/_templates/llms.txt.j2 new file mode 100644 index 00000000..db579e83 --- /dev/null +++ b/docs/_templates/llms.txt.j2 @@ -0,0 +1,52 @@ +# VyOS + +> VyOS is a free, open-source network operating system based on Debian GNU/Linux. +> It provides routing, firewall, and VPN functionality via a unified CLI using +> `set`/`delete`/`show` command hierarchy. This documentation covers {{ release }}. + +VyOS configuration follows a tree structure. All configuration commands start with +`set` (to add/change) or `delete` (to remove). Changes are staged and applied with +`commit`. The CLI hierarchy maps directly to the documentation structure. + +## Quick Start + +- [Quick Start Guide]({{ baseurl }}quick-start.html): Minimal setup walkthrough +- [CLI Overview]({{ baseurl }}cli.html): Command-line interface usage + +## Configuration + +- [Firewall]({{ baseurl }}configuration/firewall/index.html): Zone-based firewall, rules, groups +- [Interfaces]({{ baseurl }}configuration/interfaces/index.html): Ethernet, bonding, bridge, VLAN, tunnel, wireless +- [Protocols]({{ baseurl }}configuration/protocols/index.html): BGP, OSPF, IS-IS, static routing, MPLS +- [VPN]({{ baseurl }}configuration/vpn/index.html): IPsec, OpenVPN, WireGuard, L2TP, PPTP +- [NAT]({{ baseurl }}configuration/nat/index.html): Source NAT, destination NAT, NAT66 +- [System]({{ baseurl }}configuration/system/index.html): DNS, NTP, syslog, users, task scheduler +- [High Availability]({{ baseurl }}configuration/highavailability/index.html): VRRP +- [Load Balancing]({{ baseurl }}configuration/loadbalancing/index.html): WAN and reverse proxy +- [Containers]({{ baseurl }}configuration/container/index.html): Podman-based container support +- [PKI]({{ baseurl }}configuration/pki/index.html): Certificate management +- [Policy]({{ baseurl }}configuration/policy/index.html): Route maps, prefix lists, access lists +- [Traffic Policy]({{ baseurl }}configuration/trafficpolicy/index.html): QoS and shaping +- [Service]({{ baseurl }}configuration/service/index.html): DHCP, DNS forwarding, SNMP, SSH, HTTPS API +- [VRF]({{ baseurl }}configuration/vrf/index.html): Virtual routing and forwarding + +## Operations + +- [Operational Commands]({{ baseurl }}operation/index.html): Show, monitor, restart commands + +## Installation + +- [Installation Guide]({{ baseurl }}installation/index.html): Bare metal, virtual, cloud deployments + +## Automation + +- [Automation]({{ baseurl }}automation/index.html): Ansible, Terraform, HTTP API, NETCONF + +## Configuration Examples + +- [Blueprints]({{ baseurl }}configexamples/index.html): Real-world topology examples + +## Optional + +- [Contributing]({{ baseurl }}contributing/index.html): Development workflow +- [VPP]({{ baseurl }}vpp/index.html): Vector Packet Processing integration diff --git a/docs/conf.py b/docs/conf.py index 1c1014e0..942c25c9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -217,5 +217,21 @@ def _prefer_webp(app): app.builder.supported_image_types = ['image/webp'] + types +def _write_llms_txt(app, exception): + if exception is not None or app.builder.name != 'html': + return + from pathlib import Path + from jinja2 import Template + tpl_path = Path(app.srcdir) / '_templates' / 'llms.txt.j2' + out_path = Path(app.outdir) / 'llms.txt' + baseurl = (app.config.html_baseurl or '').rstrip('/') + '/' + rendered = Template(tpl_path.read_text(encoding='utf-8')).render( + baseurl=baseurl, + release=app.config.release, + ) + out_path.write_text(rendered, encoding='utf-8') + + def setup(app): app.connect('builder-inited', _prefer_webp) + app.connect('build-finished', _write_llms_txt) -- cgit v1.2.3 From 75d66f5dda90a6ebeeb1f338a241f3bad2490fb1 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Mon, 4 May 2026 11:20:04 +0300 Subject: docs: switch html_baseurl to /en/rolling/ (current branch canonical) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RTD slug rename: the canonical version slug for the current branch is moving from `latest` to `rolling`, with `latest` becoming a redirect to `rolling`. Match html_baseurl to the new canonical so and the sitemap point at the URL RTD will actually serve. Do not merge until the RTD slug rename and the new latest -> rolling redirect are in place; otherwise the canonical URL will 404. πŸ€– Generated by [robots](https://vyos.io) --- docs/conf.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/conf.py b/docs/conf.py index 942c25c9..a7a44d85 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -113,13 +113,13 @@ html_static_path = ['_static'] html_extra_path = ['_html_extra'] -html_baseurl = 'https://docs.vyos.io/en/latest/' +html_baseurl = 'https://docs.vyos.io/en/rolling/' -# sphinx-sitemap: baseurl already includes /en/latest/, so skip lang+version +# sphinx-sitemap: baseurl already includes /en/rolling/, so skip lang+version sitemap_url_scheme = '{link}' -# sphinx-llms-txt: disable auto-generated llms.txt, keep curated one from -# _html_extra; llms-full.txt is still auto-generated +# sphinx-llms-txt: disable auto-generated llms.txt, keep curated render via +# setup hook; llms-full.txt is still auto-generated llms_txt_file = False # Custom sidebar templates, must be a dictionary that maps document names -- cgit v1.2.3 From 54f40acfb2a157fa001d44870454267a03f153bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 13:45:13 +0000 Subject: docs(conf.py): also write llms.txt for dirhtml and readthedocs builders Agent-Logs-Url: https://github.com/vyos/vyos-documentation/sessions/782645e7-c7a9-4da7-8c0f-46a0c82f9314 Co-authored-by: andamasov <12631358+andamasov@users.noreply.github.com> --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/conf.py b/docs/conf.py index a7a44d85..99c4ee70 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -218,7 +218,8 @@ def _prefer_webp(app): def _write_llms_txt(app, exception): - if exception is not None or app.builder.name != 'html': + if exception is not None or app.builder.name not in ( + 'html', 'dirhtml', 'readthedocs'): return from pathlib import Path from jinja2 import Template -- cgit v1.2.3 From 8bfa3e59895716c872a2cbf4693e90094ed49ef5 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Wed, 6 May 2026 22:54:56 +0300 Subject: docs(conf.py): clarify llms_txt_file comment points to _templates source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pre-existing comment said "keep curated render via setup hook" but didn't say where the source template lives. After moving llms.txt off `html_extra_path` and onto a build-time render, point readers explicitly at `_templates/llms.txt.j2` and `_write_llms_txt()` so future updates land in the right place. Addresses Copilot review feedback on PR #1874. πŸ€– Generated by [robots](https://vyos.io) --- docs/conf.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/conf.py b/docs/conf.py index 99c4ee70..e9b28b71 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -118,8 +118,10 @@ html_baseurl = 'https://docs.vyos.io/en/rolling/' # sphinx-sitemap: baseurl already includes /en/rolling/, so skip lang+version sitemap_url_scheme = '{link}' -# sphinx-llms-txt: disable auto-generated llms.txt, keep curated render via -# setup hook; llms-full.txt is still auto-generated +# sphinx-llms-txt: disable the package's auto-generated index llms.txt. +# The curated llms.txt is rendered at build time from +# _templates/llms.txt.j2 by the _write_llms_txt() build-finished hook +# below; llms-full.txt is still auto-generated by sphinx-llms-txt. llms_txt_file = False # Custom sidebar templates, must be a dictionary that maps document names -- cgit v1.2.3 From 6ce9145fa101e623c61f009c9cf5bb75a8a4a108 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Wed, 6 May 2026 23:02:55 +0300 Subject: docs(conf.py): drop dirhtml from llms.txt builder list, use StrictUndefined Two follow-up Copilot findings on the curated llms.txt render: 1. Drop `dirhtml` from the builder allow-list. The template hard-codes `.html` URLs (`quick-start.html`), which don't exist under `dirhtml` output (`quick-start/index.html`). Production publishes via the `html`/`readthedocs` builders, so `make dirhtml` would only emit a misleading file. Cleaner to skip than to branch URL generation on builder type for a path we don't actually ship. 2. Use a Jinja `Environment` with `StrictUndefined` instead of a bare `Template`. A typo in `llms.txt.j2` (e.g. `{{ relase }}`) now raises at build time instead of silently rendering as an empty string and shipping a half-blank `llms.txt`. Also set `keep_trailing_newline` so the rendered file's terminating newline is preserved. Addresses Copilot review feedback on PR #1874. \xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io) --- docs/conf.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/conf.py b/docs/conf.py index e9b28b71..72c4595a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -220,15 +220,24 @@ def _prefer_webp(app): def _write_llms_txt(app, exception): + # Skip dirhtml: the curated template encodes `.html` URLs (e.g. + # `quick-start.html`), which don't exist under `dirhtml` output + # (`quick-start/index.html`). Production publishes via the html / + # readthedocs builders, so dirhtml output would just be misleading. if exception is not None or app.builder.name not in ( - 'html', 'dirhtml', 'readthedocs'): + 'html', 'readthedocs'): return from pathlib import Path - from jinja2 import Template + from jinja2 import Environment, StrictUndefined tpl_path = Path(app.srcdir) / '_templates' / 'llms.txt.j2' out_path = Path(app.outdir) / 'llms.txt' baseurl = (app.config.html_baseurl or '').rstrip('/') + '/' - rendered = Template(tpl_path.read_text(encoding='utf-8')).render( + # StrictUndefined: missing template variables raise rather than + # silently render as empty strings, so a typo in llms.txt.j2 fails + # the build instead of shipping a half-blank llms.txt. + env = Environment(undefined=StrictUndefined, keep_trailing_newline=True) + template = env.from_string(tpl_path.read_text(encoding='utf-8')) + rendered = template.render( baseurl=baseurl, release=app.config.release, ) -- cgit v1.2.3 From 86e4d1844434b93b086efcd9448706effc980576 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Wed, 6 May 2026 23:46:50 +0300 Subject: docs(conf.py): fail loudly when html_baseurl is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the silent `(app.config.html_baseurl or '').rstrip('/') + '/'` fallback with an explicit `RuntimeError`. Every supported branch sets `html_baseurl`, so a missing value would only ever happen as a regression β€” better to fail the build than to ship an llms.txt full of broken root-relative URLs (`/quick-start.html`, `/cli.html`, etc.). Addresses CodeRabbit review feedback on PR #1874. \xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io) --- docs/conf.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/conf.py b/docs/conf.py index 34cb88b0..3ac6ca8d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -256,11 +256,17 @@ def _write_llms_txt(app, exception): if exception is not None or app.builder.name not in ( 'html', 'readthedocs'): return + if not app.config.html_baseurl: + # Fail loudly rather than rendering /quick-start.html etc. as a + # silently-broken root-relative URL β€” every supported branch + # sets html_baseurl, so a missing value is a regression. + raise RuntimeError( + 'html_baseurl must be set to render llms.txt') from pathlib import Path from jinja2 import Environment, StrictUndefined tpl_path = Path(app.srcdir) / '_templates' / 'llms.txt.j2' out_path = Path(app.outdir) / 'llms.txt' - baseurl = (app.config.html_baseurl or '').rstrip('/') + '/' + baseurl = app.config.html_baseurl.rstrip('/') + '/' # StrictUndefined: missing template variables raise rather than # silently render as empty strings, so a typo in llms.txt.j2 fails # the build instead of shipping a half-blank llms.txt. -- cgit v1.2.3 From 17e840a3b54e992233278f819d9c5ac2a938ba5f Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Thu, 7 May 2026 08:26:56 +0300 Subject: docs(conf.py): name the rolling docs as "rolling (current)", not stale circinus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `current` branch's `release` literal was `'1.5.x (circinus)'` β€” stale since circinus split off as its own LTS branch. The rendered llms.txt verification on the RTD preview surfaced the bug as the canonical version label of the rolling docs: > This documentation covers 1.5.x (circinus). …on a build whose URLs are `/en/rolling/`. The new build-time render introduced by this PR makes the inconsistency a user-visible artifact exposed to LLM consumers, so fix the source-of-truth literal here. Now `release = 'rolling (current)'` and `version = 'rolling'`. Sphinx also exposes `release` in the page footer ("v: rolling (current)") and search metadata, so fixing this string is a single source-of-truth change for the rolling docs. Sagitta and circinus `release` strings are correct already (`'1.4.x (sagitta)'` and `'1.5.x (circinus)'` respectively); no cross-branch sweep needed for `release`. Addresses CodeRabbit review feedback on PR #1874 (the originally pushed-back release-mismatch finding β€” verification proved the push-back wrong). \xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io) --- docs/conf.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'docs') diff --git a/docs/conf.py b/docs/conf.py index 3ac6ca8d..f31dfbf4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,11 +26,16 @@ project = u'VyOS' copyright = u'2026, VyOS maintainers and contributors' author = u'VyOS maintainers and contributors' -# The short X.Y version -version = u'1.5' - -# The full version, including alpha/beta/rc tags -release = u'1.5.x (circinus)' +# The short X.Y version (rolling β€” next major; bumped at branch cut) +version = u'rolling' + +# The full version, including alpha/beta/rc tags. The `current` branch +# is the rolling tip and serves at /en/rolling/ on RTD; the literal +# below is exposed in the page footer ("v: rolling (current)") and is +# interpolated into _templates/llms.txt.j2 ("This documentation covers +# {{ release }}."). Pin this to a value that names what the rolling +# docs actually serve, not a stale LTS codename. +release = u'rolling (current)' # -- General configuration --------------------------------------------------- -- cgit v1.2.3 From 28ed24cc1b97a8861db6963233f6496134827611 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Thu, 7 May 2026 09:16:23 +0300 Subject: docs(conf.py): use FileSystemLoader/get_template for better tracebacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch from `Environment.from_string(tpl_path.read_text(...))` to `Environment(loader=FileSystemLoader(...)).get_template('llms.txt.j2')`. When `StrictUndefined` raises on a typo in `llms.txt.j2` (e.g. `{{ relase }}`), the traceback now references the real template filename and line number instead of an anonymous in-memory template. That makes debugging template typos materially easier without changing any other behavior β€” same fail-fast guard, same trailing-newline handling, same rendered output. Same change applied symmetrically across [#1874](https://github.com/vyos/vyos-documentation/pull/1874) (current), [#1876](https://github.com/vyos/vyos-documentation/pull/1876) (sagitta), and [#1903](https://github.com/vyos/vyos-documentation/pull/1903) (circinus) so the hook stays identical across branches. Addresses CodeRabbit/Copilot review feedback on PR #1903. \xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io) --- docs/conf.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/docs/conf.py b/docs/conf.py index f31dfbf4..00324771 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -268,15 +268,23 @@ def _write_llms_txt(app, exception): raise RuntimeError( 'html_baseurl must be set to render llms.txt') from pathlib import Path - from jinja2 import Environment, StrictUndefined - tpl_path = Path(app.srcdir) / '_templates' / 'llms.txt.j2' + from jinja2 import Environment, FileSystemLoader, StrictUndefined + tpl_dir = Path(app.srcdir) / '_templates' out_path = Path(app.outdir) / 'llms.txt' baseurl = app.config.html_baseurl.rstrip('/') + '/' - # StrictUndefined: missing template variables raise rather than - # silently render as empty strings, so a typo in llms.txt.j2 fails - # the build instead of shipping a half-blank llms.txt. - env = Environment(undefined=StrictUndefined, keep_trailing_newline=True) - template = env.from_string(tpl_path.read_text(encoding='utf-8')) + # FileSystemLoader + get_template (rather than from_string) makes + # Jinja tracebacks reference the real template filename and line + # number β€” useful when StrictUndefined trips on a typo in + # llms.txt.j2. StrictUndefined: missing template variables raise + # rather than silently render as empty strings, so a typo in + # llms.txt.j2 fails the build instead of shipping a half-blank + # llms.txt. + env = Environment( + loader=FileSystemLoader(str(tpl_dir)), + undefined=StrictUndefined, + keep_trailing_newline=True, + ) + template = env.get_template('llms.txt.j2') rendered = template.render( baseurl=baseurl, release=app.config.release, -- cgit v1.2.3 From 8a7e96d6fa25a1098c134146883d4f577ab229ca Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Thu, 7 May 2026 09:25:54 +0300 Subject: docs(conf.py): set Jinja autoescape=False explicitly for llms.txt template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The llms.txt template renders plain text (markdown), not HTML, so the HTML autoescape rules that bandit/ruff S701 flags don't apply. Explicit `autoescape=False` silences the lint while documenting the intent β€” there's no HTML context to escape into here. Behavior unchanged (Jinja's default `autoescape=False` was already in effect); this just makes it visible. Same change applied symmetrically across [#1874](https://github.com/vyos/vyos-documentation/pull/1874) (current), [#1876](https://github.com/vyos/vyos-documentation/pull/1876) (sagitta), and [#1903](https://github.com/vyos/vyos-documentation/pull/1903) (circinus). Addresses CodeRabbit review feedback (S701) on PR #1876. \xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io) --- CLAUDE.md | 11 ++++++++--- docs/conf.py | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/CLAUDE.md b/CLAUDE.md index 4785e8bc..7c204a96 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -107,9 +107,14 @@ Each configuration page should contain: ## LLM-Facing Files (`llms.txt`, `llms-full.txt`) -Both files are regenerated on every Sphinx HTML build. They're shipped at the -docs root for each version (`https://docs.vyos.io/en//llms.txt`, -`.../llms-full.txt`). +Both files are regenerated on every `html` and `readthedocs` builder run. +The `dirhtml` builder is intentionally skipped (the curated template +encodes `.html` URLs which don't match `dirhtml` output paths). Local +`make dirhtml` won't emit `llms.txt` β€” production publishes via the +`html`/`readthedocs` builders, where the file lands. + +Files are shipped at the docs root for each version +(`https://docs.vyos.io/en//llms.txt`, `.../llms-full.txt`). - **`llms-full.txt`** β€” auto-generated by the `sphinx_llms_txt` extension from the full corpus. No curation; configured by `llms_txt_file = False` (which diff --git a/docs/conf.py b/docs/conf.py index 00324771..d3db8d2b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -283,6 +283,10 @@ def _write_llms_txt(app, exception): loader=FileSystemLoader(str(tpl_dir)), undefined=StrictUndefined, keep_trailing_newline=True, + # Plain-text template (not HTML), so HTML autoescape is not + # appropriate. Setting autoescape=False explicitly to silence + # bandit/ruff S701 and document the intent. + autoescape=False, ) template = env.get_template('llms.txt.j2') rendered = template.render( -- cgit v1.2.3 From 5fe7f9ad745696076dae479a23431b22f67abea4 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Thu, 7 May 2026 09:34:19 +0300 Subject: docs(llms.txt): point links at .md sources, not .html MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the [llms.txt spec](https://llmstxt.org/), `/llms.txt` links should point at markdown files (the spec example: `[API docs](https://example.com/api.md)`). We were using `.html` URLs, which still work but force LLM consumers through HTML parsing for content that's already available as raw MyST at the same path (the `.md` is copied to HTML root by `_copy_md_sources`). Pivot the curated llms.txt template to `.md` URLs across all 22 entries. Spec-compliant, no extra requests, LLMs get raw markdown without the navigation chrome. Verified locally: `/en//.md` returns 200 with raw MyST content (frontmatter + body) for all linked paths on this branch. Same pivot applied symmetrically across [#1874](https://github.com/vyos/vyos-documentation/pull/1874) (current), [#1876](https://github.com/vyos/vyos-documentation/pull/1876) (sagitta), and [#1903](https://github.com/vyos/vyos-documentation/pull/1903) (circinus). πŸ€– Generated by [robots](https://vyos.io) --- docs/_templates/llms.txt.j2 | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'docs') diff --git a/docs/_templates/llms.txt.j2 b/docs/_templates/llms.txt.j2 index db579e83..1c6a3900 100644 --- a/docs/_templates/llms.txt.j2 +++ b/docs/_templates/llms.txt.j2 @@ -10,43 +10,43 @@ VyOS configuration follows a tree structure. All configuration commands start wi ## Quick Start -- [Quick Start Guide]({{ baseurl }}quick-start.html): Minimal setup walkthrough -- [CLI Overview]({{ baseurl }}cli.html): Command-line interface usage +- [Quick Start Guide]({{ baseurl }}quick-start.md): Minimal setup walkthrough +- [CLI Overview]({{ baseurl }}cli.md): Command-line interface usage ## Configuration -- [Firewall]({{ baseurl }}configuration/firewall/index.html): Zone-based firewall, rules, groups -- [Interfaces]({{ baseurl }}configuration/interfaces/index.html): Ethernet, bonding, bridge, VLAN, tunnel, wireless -- [Protocols]({{ baseurl }}configuration/protocols/index.html): BGP, OSPF, IS-IS, static routing, MPLS -- [VPN]({{ baseurl }}configuration/vpn/index.html): IPsec, OpenVPN, WireGuard, L2TP, PPTP -- [NAT]({{ baseurl }}configuration/nat/index.html): Source NAT, destination NAT, NAT66 -- [System]({{ baseurl }}configuration/system/index.html): DNS, NTP, syslog, users, task scheduler -- [High Availability]({{ baseurl }}configuration/highavailability/index.html): VRRP -- [Load Balancing]({{ baseurl }}configuration/loadbalancing/index.html): WAN and reverse proxy -- [Containers]({{ baseurl }}configuration/container/index.html): Podman-based container support -- [PKI]({{ baseurl }}configuration/pki/index.html): Certificate management -- [Policy]({{ baseurl }}configuration/policy/index.html): Route maps, prefix lists, access lists -- [Traffic Policy]({{ baseurl }}configuration/trafficpolicy/index.html): QoS and shaping -- [Service]({{ baseurl }}configuration/service/index.html): DHCP, DNS forwarding, SNMP, SSH, HTTPS API -- [VRF]({{ baseurl }}configuration/vrf/index.html): Virtual routing and forwarding +- [Firewall]({{ baseurl }}configuration/firewall/index.md): Zone-based firewall, rules, groups +- [Interfaces]({{ baseurl }}configuration/interfaces/index.md): Ethernet, bonding, bridge, VLAN, tunnel, wireless +- [Protocols]({{ baseurl }}configuration/protocols/index.md): BGP, OSPF, IS-IS, static routing, MPLS +- [VPN]({{ baseurl }}configuration/vpn/index.md): IPsec, OpenVPN, WireGuard, L2TP, PPTP +- [NAT]({{ baseurl }}configuration/nat/index.md): Source NAT, destination NAT, NAT66 +- [System]({{ baseurl }}configuration/system/index.md): DNS, NTP, syslog, users, task scheduler +- [High Availability]({{ baseurl }}configuration/highavailability/index.md): VRRP +- [Load Balancing]({{ baseurl }}configuration/loadbalancing/index.md): WAN and reverse proxy +- [Containers]({{ baseurl }}configuration/container/index.md): Podman-based container support +- [PKI]({{ baseurl }}configuration/pki/index.md): Certificate management +- [Policy]({{ baseurl }}configuration/policy/index.md): Route maps, prefix lists, access lists +- [Traffic Policy]({{ baseurl }}configuration/trafficpolicy/index.md): QoS and shaping +- [Service]({{ baseurl }}configuration/service/index.md): DHCP, DNS forwarding, SNMP, SSH, HTTPS API +- [VRF]({{ baseurl }}configuration/vrf/index.md): Virtual routing and forwarding ## Operations -- [Operational Commands]({{ baseurl }}operation/index.html): Show, monitor, restart commands +- [Operational Commands]({{ baseurl }}operation/index.md): Show, monitor, restart commands ## Installation -- [Installation Guide]({{ baseurl }}installation/index.html): Bare metal, virtual, cloud deployments +- [Installation Guide]({{ baseurl }}installation/index.md): Bare metal, virtual, cloud deployments ## Automation -- [Automation]({{ baseurl }}automation/index.html): Ansible, Terraform, HTTP API, NETCONF +- [Automation]({{ baseurl }}automation/index.md): Ansible, Terraform, HTTP API, NETCONF ## Configuration Examples -- [Blueprints]({{ baseurl }}configexamples/index.html): Real-world topology examples +- [Blueprints]({{ baseurl }}configexamples/index.md): Real-world topology examples ## Optional -- [Contributing]({{ baseurl }}contributing/index.html): Development workflow -- [VPP]({{ baseurl }}vpp/index.html): Vector Packet Processing integration +- [Contributing]({{ baseurl }}contributing/index.md): Development workflow +- [VPP]({{ baseurl }}vpp/index.md): Vector Packet Processing integration -- cgit v1.2.3 From ef515553402b9ad86399e0a845f2fccb51aa9b55 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Thu, 7 May 2026 09:40:47 +0300 Subject: docs(llms.txt): update dirhtml-skip rationale after .md pivot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous comment on `_write_llms_txt` (and on the LLM-Facing Files section in CLAUDE.md) justified the `dirhtml` skip by claiming the template's `.html` URLs wouldn't resolve under `dirhtml` output. That became stale after [#1874@5fe7f9ad](https://github.com/vyos/vyos-documentation/pull/1874/commits/5fe7f9ad) (and parity commits on [#1876](https://github.com/vyos/vyos-documentation/pull/1876)/[#1903](https://github.com/vyos/vyos-documentation/pull/1903)) pivoted the curated template to `.md` URLs β€” `_copy_md_sources` puts `.md` files at their source-relative paths regardless of builder, so the rendered llms.txt would actually resolve fine under `dirhtml` now. The real reason we skip `dirhtml` is simpler: production publishes only via the `html`/`readthedocs` builders, and we don't render llms.txt for builds we don't ship. Local `make dirhtml` is a developer convenience, not a publish target. Reword the conf.py comment and the CLAUDE.md section to say that plainly. No code-behavior change. Addresses Copilot review feedback on PR #1874 (3 threads on docs/conf.py, docs/_templates/llms.txt.j2, and CLAUDE.md flagging the inconsistency). \xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io) --- CLAUDE.md | 8 ++++---- docs/conf.py | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'docs') diff --git a/CLAUDE.md b/CLAUDE.md index 7c204a96..6c07c790 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -108,10 +108,10 @@ Each configuration page should contain: ## LLM-Facing Files (`llms.txt`, `llms-full.txt`) Both files are regenerated on every `html` and `readthedocs` builder run. -The `dirhtml` builder is intentionally skipped (the curated template -encodes `.html` URLs which don't match `dirhtml` output paths). Local -`make dirhtml` won't emit `llms.txt` β€” production publishes via the -`html`/`readthedocs` builders, where the file lands. +The `dirhtml` builder is intentionally skipped β€” production publishes +only via `html`/`readthedocs`, and we don't render llms.txt for builds +we don't ship. Local `make dirhtml` is a developer convenience and +won't emit `llms.txt`. Files are shipped at the docs root for each version (`https://docs.vyos.io/en//llms.txt`, `.../llms-full.txt`). diff --git a/docs/conf.py b/docs/conf.py index d3db8d2b..07ffe722 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -254,10 +254,12 @@ def _prefer_webp(app): def _write_llms_txt(app, exception): - # Skip dirhtml: the curated template encodes `.html` URLs (e.g. - # `quick-start.html`), which don't exist under `dirhtml` output - # (`quick-start/index.html`). Production publishes via the html / - # readthedocs builders, so dirhtml output would just be misleading. + # Skip dirhtml: production publishes via the `html` / `readthedocs` + # builders only. The `.md` links in the curated template do + # actually resolve under `dirhtml` (`_copy_md_sources` puts `.md` + # files at their source-relative paths regardless of builder), but + # we still don't render llms.txt for builds we don't ship β€” local + # `make dirhtml` is a developer convenience, not a publish target. if exception is not None or app.builder.name not in ( 'html', 'readthedocs'): return -- cgit v1.2.3 From 04541bda8a3d04ecb9824f92d344ec8bd7aa2523 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Thu, 7 May 2026 09:48:06 +0300 Subject: docs(conf.py): fix stale .html reference in fail-fast comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last residual `.html` reference in the `_write_llms_txt` hook comments β€” the html_baseurl-empty failure-path comment said "rendering /quick-start.html etc." but the template now emits `.md` URLs. Update the example to `/quick-start.md` to match the actual output. Comment-only change. Same fix applied symmetrically across [#1874](https://github.com/vyos/vyos-documentation/pull/1874), [#1876](https://github.com/vyos/vyos-documentation/pull/1876), and [#1903](https://github.com/vyos/vyos-documentation/pull/1903). Addresses Copilot review feedback on PR #1903. \xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io) --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/conf.py b/docs/conf.py index 07ffe722..da462b0e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -264,7 +264,7 @@ def _write_llms_txt(app, exception): 'html', 'readthedocs'): return if not app.config.html_baseurl: - # Fail loudly rather than rendering /quick-start.html etc. as a + # Fail loudly rather than rendering /quick-start.md etc. as a # silently-broken root-relative URL β€” every supported branch # sets html_baseurl, so a missing value is a regression. raise RuntimeError( -- cgit v1.2.3