diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml
index 7ab3976b..0656b540 100644
--- a/.github/workflows/cicd.yml
+++ b/.github/workflows/cicd.yml
@@ -21,35 +21,18 @@ concurrency:
cancel-in-progress: true
jobs:
- prepare:
- name: Prepare
- outputs:
- release_tag: ${{ steps.vars.outputs.release_tag }}
- runs-on: ubuntu-latest
- steps:
- - name: Resolve required vars
- id: vars
- run: |
- echo "release_tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
-
docs:
name: Docs
- needs:
- - prepare
uses: ./.github/workflows/reusable-docs.yml
permissions:
contents: read
- pages: write
- id-token: write
with:
- deploy: ${{ startsWith(github.ref, 'refs/tags/') }}
- version: ${{ needs.prepare.outputs.release_tag }}
+ version: dev
success:
name: Success
if: ${{ !cancelled() && !contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }}
needs:
- - prepare
- docs
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/docs-release.yml b/.github/workflows/docs-release.yml
new file mode 100644
index 00000000..0974b7d5
--- /dev/null
+++ b/.github/workflows/docs-release.yml
@@ -0,0 +1,165 @@
+# SPDX-FileCopyrightText: Copyright (c) 2026 Cisco and/or its affiliates.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Ordered release pipeline (decoupled phases):
+# 1. Build docs — validate MkDocs output
+# 2. Mike — push versioned site to gh-pages (from mkdocs/mike_versions.ini [versions] release)
+# 3. Git tag — create v* tag on the commit that was published
+# 4. Finalize — placeholder / optional GitHub Release (GitHub Pages already serves gh-pages after step 2)
+
+name: Docs release
+
+on:
+ workflow_dispatch:
+ inputs:
+ create_github_release:
+ description: 'After tagging, create a GitHub Release for the same tag'
+ type: choice
+ options:
+ - 'no'
+ - 'yes'
+ default: 'no'
+
+permissions:
+ contents: write
+
+concurrency:
+ group: docs-release
+ cancel-in-progress: false
+
+jobs:
+ build:
+ name: '1. Build docs'
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup Taskfile
+ shell: bash
+ run: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Build
+ shell: bash
+ run: |
+ export VERSION="$(cd mkdocs && uv run python mike_version.py release)"
+ task build
+
+ mike:
+ name: '2. Mike (gh-pages)'
+ needs: build
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup Taskfile
+ shell: bash
+ run: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Configure Git for mike
+ run: |
+ git config user.name github-actions[bot]
+ git config user.email github-actions[bot]@users.noreply.github.com
+
+ - name: Fetch gh-pages
+ continue-on-error: true
+ run: git fetch origin gh-pages --depth=1
+
+ - name: Mike deploy
+ shell: bash
+ run: task mike:deploy
+
+ git-tag:
+ name: '3. Git tag'
+ needs: mike
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Create and push tag
+ shell: bash
+ env:
+ TAG_SHA: ${{ github.sha }}
+ run: |
+ set -euo pipefail
+ VER="$(cd mkdocs && uv run python mike_version.py release)"
+ TAG="v${VER}"
+ if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then
+ echo "::error::Tag ${TAG} already exists on origin. Bump [versions] release in mkdocs/mike_versions.ini or delete the remote tag."
+ exit 1
+ fi
+ git tag -a "${TAG}" -m "Documentation release ${TAG}" "${TAG_SHA}"
+ git push origin "${TAG}"
+
+ finalize:
+ name: '4. Deploy / release'
+ needs: git-tag
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: GitHub Release
+ if: ${{ github.event.inputs.create_github_release == 'yes' }}
+ shell: bash
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ set -euo pipefail
+ VER="$(cd mkdocs && uv run python mike_version.py release)"
+ TAG="v${VER}"
+ gh release create "${TAG}" --title "Documentation ${TAG}" --notes "Published from workflow [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). Site is served from the gh-pages branch."
+
+ - name: Pages note
+ if: ${{ github.event.inputs.create_github_release != 'yes' }}
+ run: |
+ echo "::notice::GitHub Pages (if configured from gh-pages) is already serving the mike version from step 2. Set **create_github_release** to **yes** to open a GitHub Release on the new tag."
diff --git a/.github/workflows/reusable-docs.yml b/.github/workflows/reusable-docs.yml
index ad823862..091ec3ff 100644
--- a/.github/workflows/reusable-docs.yml
+++ b/.github/workflows/reusable-docs.yml
@@ -1,44 +1,38 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 Cisco and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
+#
+# Build-only documentation check. Versioned publish (mike), tagging, and releases
+# use .github/workflows/docs-release.yml
name: Documentation
on:
workflow_call:
inputs:
- deploy:
- description: 'Deploy documentation artifacts'
- required: true
- type: boolean
- default: false
version:
- description: 'Version to use for documentation artifacts'
+ description: 'MkDocs macros VERSION (docs_build_version)'
required: true
type: string
default: dev
workflow_dispatch:
inputs:
- deploy:
- description: 'Deploy documentation artifacts'
- required: true
- type: boolean
- default: false
version:
- description: 'Version to use for documentation artifacts'
- required: true
+ description: 'MkDocs macros VERSION'
+ required: false
type: string
default: dev
permissions:
contents: read
- pages: write
- id-token: write
jobs:
- build:
- name: Build artifacts
+ docs:
+ name: Build docs
runs-on: ubuntu-latest
+ env:
+ # Use Node 24 for JS-based actions (checkout, etc.); see GitHub deprecation of Node 20 on runners
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -46,15 +40,6 @@ jobs:
fetch-depth: 0
submodules: 'recursive'
- - name: Setup Pages
- id: pages
- uses: actions/configure-pages@v5
-
- - name: Setup Golang
- uses: actions/setup-go@v5
- with:
- go-version: '1.23.1'
-
- name: Setup Taskfile
shell: bash
run: |
@@ -71,29 +56,7 @@ jobs:
- name: Build docs
shell: bash
- env:
- VERSION: ${{ inputs.version }}
run: |
+ V="${{ inputs.version }}"
+ export VERSION="${V:-dev}"
task build
-
- - name: Upload artifact
- uses: actions/upload-pages-artifact@v4
- with:
- name: docs-website
- path: ./.build/site
-
- deploy:
- name: Deploy artifacts
- if: ${{ inputs.deploy == true || inputs.deploy == 'true' }}
- needs:
- - build
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
- runs-on: ubuntu-latest
- steps:
- - name: Deploy to GitHub Pages
- id: deployment
- uses: actions/deploy-pages@v4
- with:
- artifact_name: docs-website
diff --git a/.shared-config/README.md b/.shared-config/README.md
new file mode 100644
index 00000000..3b45695f
--- /dev/null
+++ b/.shared-config/README.md
@@ -0,0 +1,132 @@
+# AGNTCY shared docs theme
+
+Drop this `.shared-config` directory at the root of a repository to reuse the AGNTCY MkDocs Material look (CSS, logos, favicon, header logo partials).
+
+## Layout
+
+| Path | Purpose |
+|------|---------|
+| `stylesheets/custom.css` | Theme variables and layout tweaks (source) |
+| `assets/` | `favicon.ico`, `img/logo-light.svg`, `img/logo-dark.svg`, optional `logo.svg` |
+| `mkdocs/overrides/` | Material `partials` (light/dark logo swap, footer copyright block) |
+| `mkdocs/mkdocs.template.yml` | Full starter `mkdocs.yml` (theme, mike, macros, redirects, nav, API docs, Swagger, includes, analytics) |
+| `mkdocs/hooks.py` | SSL via `certifi` for `include-markdown` (referenced from the template; keep path in sync) |
+| `mkdocs/mike_version.py` | Resolves version label from `mike_versions.ini` (for Task / CI) |
+| `mkdocs/mike_versions.ini` | `[versions]` `local` / `release` labels for mike |
+| `mkdocs/main.py.example` | Copy to `mkdocs/main.py` for `var_tag()` and `docs_build_version` |
+| `requirements-agntcy-docs-theme.txt` | All template plugins (Material, mike, macros, redirects, awesome-pages, mkdocstrings, swagger UI, `include-markdown`, `certifi`, …) |
+| `requirements-agntcy-docs-lint.txt` | `codespell` and `pymarkdown` for lint tasks |
+| `Taskfile.yml` | Local build, serve (`run`), and lint tasks ([Task](https://taskfile.dev/) ≥ 3.35) |
+| `codespellrc` | Spelling check config (used by `lint:spelling`) |
+| `pymarkdown.yaml` | Markdown style config (used by `lint:markdown`) |
+| `lychee.toml` | Link checker config (used by `lint:links`) |
+| `install.sh` | Copies CSS and assets into `docs/`; optional `--with-mike-macros` installs mike + `main.py` |
+| `github/workflows/` | CI, docs release (mike + tag), links, PR link checks — see below |
+
+## GitHub Actions
+
+Copy the YAML files from `github/workflows/` into `.github/workflows/` at the repository root (keep filenames). They assume `.shared-config/` exists beside `docs/` and `mkdocs/`.
+
+| Workflow | Role |
+|----------|------|
+| `reusable-docs.yml` | Build docs (`workflow_call` + `workflow_dispatch`); used by `cicd.yml`. |
+| `cicd.yml` | On PR/push (paths: `docs/**`, `mkdocs/**`, `.shared-config/**`) and on `v*.*.*` tags, runs the reusable build. |
+| `docs-release.yml` | Manual Docs release: build → mike deploy to gh-pages → git tag `v{version}` → optional GitHub Release. Set `[versions] release` in `mkdocs/mike_versions.ini` first. |
+| `links.yml` | Scheduled / manual lychee on `.build/site`, config `.shared-config/lychee.toml`. |
+| `pr-links.yml` | PRs to main/master: lychee on HTML for changed `docs/**/*.md` only. Add a `--remap` in the workflow if you need production URLs rewritten to the local build (see comment in the file). |
+
+Each workflow runs `./.shared-config/install.sh` (and `--with-mike-macros`) before `task -t .shared-config/Taskfile.yml build`, then `uv sync` in `mkdocs/` when `pyproject.toml` or `uv.lock` exists, otherwise `python3 -m pip install -r .shared-config/requirements-agntcy-docs-theme.txt`.
+
+Enable GitHub Pages from the gh-pages branch if you use mike. Grant Actions appropriate permissions (the release workflow uses `contents: write`).
+
+## What the template enables
+
+Aligned with `agntcy/docs` `mkdocs.yml`:
+
+- `hooks` — `../.shared-config/mkdocs/hooks.py` (paths are relative to the directory that contains `mkdocs/mkdocs.yml`); uses `certifi` for HTTPS when using `include-markdown`.
+- `extra.version` / `mike` — versioned docs and Material’s version menu.
+- `extra.var` + `macros` — `[[[ var.docs_url ]]]`, etc., with `[[[` / `]]]` delimiters; `main.py` from `main.py.example` (or `./install.sh --with-mike-macros`).
+- `redirects` — same `redirect_maps` as the main docs site (trim or replace for a new project).
+- `awesome-pages` — `.index` navigation files.
+- `mkdocstrings` — Python API docs (install the packages you document in the same environment; `griffe-pydantic` is configured).
+- `swagger-ui-tag` and `include-markdown` — OpenAPI embeds and Markdown includes.
+- `extra.analytics` / `extra.consent` — Google Analytics + cookie consent; replace `G-XXXXXXXXXX` with your Measurement ID (or remove those blocks).
+
+Copy `mike_version.py`, `mike_versions.ini`, and `main.py` into `mkdocs/` when using mike/macros:
+
+```bash
+./.shared-config/install.sh --with-mike-macros
+```
+
+Use `task -t .shared-config/Taskfile.yml mike:deploy-local`, `mike:deploy`, and `mike:serve` (requires mike via `uv run` or on `PATH`).
+
+**Important:** Copy `mkdocs.template.yml` into `mkdocs/mkdocs.yml` (do not point `-f` at the template inside `.shared-config/`, or `docs_dir`, `hooks`, and `theme.custom_dir` paths will resolve incorrectly).
+
+## Build and lint with Task
+
+From the repository root (expects `docs/` and `mkdocs/mkdocs.yml`):
+
+```bash
+task -t .shared-config/Taskfile.yml build # → .build/site
+task -t .shared-config/Taskfile.yml run # mkdocs serve
+task -t .shared-config/Taskfile.yml lint # spelling + markdown + links
+task -t .shared-config/Taskfile.yml lint:fix # auto-fix spelling + markdown
+task -t .shared-config/Taskfile.yml mike:deploy-local # local gh-pages + mike (after install.sh --with-mike-macros)
+task -t .shared-config/Taskfile.yml mike:serve
+```
+
+Or include the file from your main `Taskfile.yml`:
+
+```yaml
+includes:
+ docs: .shared-config/Taskfile.yml
+```
+
+Then run namespaced tasks such as `task docs:build`.
+
+**Tools:** `lint:links` needs [lychee](https://github.com/lycheeverse/lychee) on your `PATH`. Spelling and Markdown lint use either `uv run` (if `mkdocs/uv.lock` or `mkdocs/pyproject.toml` exists) or global `codespell` / `pymarkdown` after:
+
+```bash
+pip install -r .shared-config/requirements-agntcy-docs-lint.txt
+```
+
+Tune `codespellrc`, `pymarkdown.yaml`, and `lychee.toml` in `.shared-config/` for your project (for example, extend `ignore-words-list` or `exclude` URL patterns).
+
+## Why `install.sh`?
+
+MkDocs loads `extra_css` and theme static paths relative to `docs_dir`. Files under `.shared-config/` are not inside `docs/` unless you copy or symlink them. Running `install.sh` places:
+
+- `docs/stylesheets/agntcy-docs.css`
+- `docs/assets/agntcy/favicon.ico`
+- `docs/assets/agntcy/img/logo-*.svg`
+
+Re-run after updating `.shared-config` from upstream.
+
+## Quick start
+
+1. Add or create a `docs/` tree with at least `docs/index.md`.
+2. From the repository root:
+
+ ```bash
+ chmod +x .shared-config/install.sh
+ ./.shared-config/install.sh
+ ./.shared-config/install.sh --with-mike-macros # optional: mike + macros files in mkdocs/
+ ```
+
+3. Copy `.shared-config/mkdocs/mkdocs.template.yml` to `mkdocs/mkdocs.yml` and set `site_name`, `site_url`, `repo_*`, `edit_uri`, `extra.var`, `extra.analytics.property`, and edit `plugins.redirects` as needed. Remove plugins you do not use (for example `mkdocstrings` if you have no API pages).
+
+4. Install deps and serve:
+
+ ```bash
+ cd mkdocs
+ pip install -r ../.shared-config/requirements-agntcy-docs-theme.txt
+ mkdocs serve
+ ```
+
+If `mkdocs.yml` lives somewhere else, adjust `docs_dir`, `theme.custom_dir`, and paths in the template so `custom_dir` still points at `.shared-config/mkdocs/overrides` relative to that file.
+
+## Merging into an existing site
+
+- Set `theme.custom_dir` to `../.shared-config/mkdocs/overrides` (from `mkdocs/mkdocs.yml`).
+- Add `stylesheets/agntcy-docs.css` to `extra_css` (after running `install.sh`).
+- Set `theme.favicon`, `theme.logo_light`, and `theme.logo_dark` to the `assets/agntcy/...` paths from the template, or merge the `theme.palette` / `theme.features` blocks as needed.
diff --git a/.shared-config/Taskfile.yml b/.shared-config/Taskfile.yml
new file mode 100644
index 00000000..5365cf20
--- /dev/null
+++ b/.shared-config/Taskfile.yml
@@ -0,0 +1,166 @@
+# Copyright AGNTCY Contributors (https://github.com/agntcy)
+# SPDX-License-Identifier: CC-BY-4.0
+#
+# Portable docs tasks. Requires Task >= 3.35 (TASKFILE_DIR).
+# From repo root: task -t .shared-config/Taskfile.yml build
+# Or include in your Taskfile: includes: { agntcy-docs: .shared-config/Taskfile.yml }
+
+version: "3"
+
+interval: "500ms"
+
+vars:
+ SHARED: "{{.TASKFILE_DIR}}"
+ REPO:
+ sh: cd "{{.SHARED}}/.." && pwd
+ DOCS: "{{.REPO}}/docs"
+ MKDOCS: "{{.REPO}}/mkdocs"
+ BUILD_SITE: "{{.REPO}}/.build/site"
+ SPELL_CONFIG: "{{.SHARED}}/codespellrc"
+ MD_CONFIG: "{{.SHARED}}/pymarkdown.yaml"
+ LYCHEE_CONFIG: "{{.SHARED}}/lychee.toml"
+
+tasks:
+ default:
+ desc: List tasks
+ cmds:
+ - task --list
+
+ build:
+ desc: Build documentation site to .build/site
+ cmds:
+ - mkdir -p "{{.BUILD_SITE}}"
+ - |
+ if [ -f "{{.MKDOCS}}/uv.lock" ] || [ -f "{{.MKDOCS}}/pyproject.toml" ]; then
+ (cd "{{.MKDOCS}}" && uv run mkdocs build --site-dir "{{.BUILD_SITE}}")
+ else
+ (cd "{{.MKDOCS}}" && mkdocs build --site-dir "{{.BUILD_SITE}}")
+ fi
+ - 'echo "Docs built: file://{{.BUILD_SITE}}/index.html"'
+
+ run:
+ desc: Run mkdocs serve (live reload)
+ cmds:
+ - |
+ if [ -f "{{.MKDOCS}}/uv.lock" ] || [ -f "{{.MKDOCS}}/pyproject.toml" ]; then
+ (cd "{{.MKDOCS}}" && uv run mkdocs serve)
+ else
+ (cd "{{.MKDOCS}}" && mkdocs serve)
+ fi
+
+ test:
+ desc: Run all documentation lint checks
+ cmds:
+ - task: lint
+
+ lint:
+ desc: Run spelling, markdown, and link checks
+ cmds:
+ - task: lint:spelling
+ - task: lint:markdown
+ - task: lint:links
+
+ lint:spelling:
+ desc: Check spelling under docs/
+ cmds:
+ - |
+ if [ -f "{{.MKDOCS}}/uv.lock" ] || [ -f "{{.MKDOCS}}/pyproject.toml" ]; then
+ (cd "{{.MKDOCS}}" && uv run codespell --config "{{.SPELL_CONFIG}}" "{{.DOCS}}")
+ else
+ codespell --config "{{.SPELL_CONFIG}}" "{{.DOCS}}"
+ fi
+
+ lint:markdown:
+ desc: Check Markdown style under docs/
+ cmds:
+ - |
+ if [ -f "{{.MKDOCS}}/uv.lock" ] || [ -f "{{.MKDOCS}}/pyproject.toml" ]; then
+ (cd "{{.MKDOCS}}" && uv run pymarkdown --config "{{.MD_CONFIG}}" scan "{{.DOCS}}")
+ else
+ pymarkdown --config "{{.MD_CONFIG}}" scan "{{.DOCS}}"
+ fi
+
+ lint:links:
+ desc: Check links in markdown under docs/ (requires lychee on PATH)
+ cmds:
+ - lychee --config "{{.LYCHEE_CONFIG}}" "{{.DOCS}}"
+
+ lint:fix:
+ desc: Auto-fix spelling and markdown where supported
+ cmds:
+ - task: lint:fix:spelling
+ - task: lint:fix:markdown
+
+ lint:fix:spelling:
+ desc: Apply codespell write-fixes to docs/
+ cmds:
+ - |
+ if [ -f "{{.MKDOCS}}/uv.lock" ] || [ -f "{{.MKDOCS}}/pyproject.toml" ]; then
+ (cd "{{.MKDOCS}}" && uv run codespell --config "{{.SPELL_CONFIG}}" "{{.DOCS}}" --write-changes)
+ else
+ codespell --config "{{.SPELL_CONFIG}}" "{{.DOCS}}" --write-changes
+ fi
+
+ lint:fix:markdown:
+ desc: Apply pymarkdown fixes under docs/
+ cmds:
+ - |
+ if [ -f "{{.MKDOCS}}/uv.lock" ] || [ -f "{{.MKDOCS}}/pyproject.toml" ]; then
+ (cd "{{.MKDOCS}}" && uv run pymarkdown --config "{{.MD_CONFIG}}" fix -r "{{.DOCS}}")
+ else
+ pymarkdown --config "{{.MD_CONFIG}}" fix -r "{{.DOCS}}"
+ fi
+
+ mike:deploy-local:
+ desc: >-
+ Deploy current docs to local gh-pages with version from mkdocs/mike_versions.ini [versions] local (+ latest alias); no git push
+ dir: "{{.MKDOCS}}"
+ cmds:
+ - 'test -f mike_version.py || { echo "Run .shared-config/install.sh --with-mike-macros"; exit 1; }'
+ - git fetch origin gh-pages --depth=1 2>/dev/null || true
+ - |
+ set -euo pipefail
+ if [ -f uv.lock ] || [ -f pyproject.toml ]; then
+ MIKE_LOCAL="$(uv run python mike_version.py local)"
+ uv run mike deploy "$MIKE_LOCAL" latest --update-aliases
+ uv run mike set-default latest
+ uv run mike list
+ else
+ MIKE_LOCAL="$(python mike_version.py local)"
+ mike deploy "$MIKE_LOCAL" latest --update-aliases
+ mike set-default latest
+ mike list
+ fi
+ echo "Local gh-pages updated. Run task mike:serve and open http://127.0.0.1:8000/"
+
+ mike:deploy:
+ desc: >-
+ Push versioned docs to origin/gh-pages (mike). Set [versions] release in mkdocs/mike_versions.ini or export VERSION=…
+ dir: "{{.MKDOCS}}"
+ cmds:
+ - 'test -f mike_version.py || { echo "Run .shared-config/install.sh --with-mike-macros"; exit 1; }'
+ - git fetch origin gh-pages --depth=1 2>/dev/null || true
+ - |
+ set -euo pipefail
+ if [ -f uv.lock ] || [ -f pyproject.toml ]; then
+ MIKE_VERSION="$(uv run python mike_version.py release)"
+ uv run mike deploy --push --update-aliases "$MIKE_VERSION" latest
+ uv run mike set-default --push latest
+ uv run mike list
+ else
+ MIKE_VERSION="$(python mike_version.py release)"
+ mike deploy --push --update-aliases "$MIKE_VERSION" latest
+ mike set-default --push latest
+ mike list
+ fi
+
+ mike:serve:
+ desc: Serve versioned site from local gh-pages (run mike:deploy-local first)
+ dir: "{{.MKDOCS}}"
+ cmds:
+ - |
+ if [ -f uv.lock ] || [ -f pyproject.toml ]; then
+ uv run mike serve -a 127.0.0.1:8000
+ else
+ mike serve -a 127.0.0.1:8000
+ fi
diff --git a/.shared-config/assets/favicon.ico b/.shared-config/assets/favicon.ico
new file mode 100644
index 00000000..4e15e04a
Binary files /dev/null and b/.shared-config/assets/favicon.ico differ
diff --git a/.shared-config/assets/img/logo-dark.svg b/.shared-config/assets/img/logo-dark.svg
new file mode 100644
index 00000000..00495ef3
--- /dev/null
+++ b/.shared-config/assets/img/logo-dark.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/.shared-config/assets/img/logo-light.svg b/.shared-config/assets/img/logo-light.svg
new file mode 100644
index 00000000..f6f57d73
--- /dev/null
+++ b/.shared-config/assets/img/logo-light.svg
@@ -0,0 +1,3 @@
+
diff --git a/.shared-config/assets/logo.svg b/.shared-config/assets/logo.svg
new file mode 100644
index 00000000..079955de
--- /dev/null
+++ b/.shared-config/assets/logo.svg
@@ -0,0 +1,6 @@
+
+
+
\ No newline at end of file
diff --git a/.shared-config/codespellrc b/.shared-config/codespellrc
new file mode 100644
index 00000000..4a007e3d
--- /dev/null
+++ b/.shared-config/codespellrc
@@ -0,0 +1,6 @@
+[codespell]
+skip = .git,.build,node_modules,*.png,*.jpg,*.jpeg,*.svg,*.ico,*.drawio,site,.venv
+ignore-words-list = agntcy,authn,authz,github,localhost,mkdocs,pymdown,repo,repos,tls,url,urls,uri,uris,uuid,uuids,metadata,namespace,cli,api,apis,json,yaml,workflow,workflows
+count =
+check-filenames =
+check-hidden =
diff --git a/.shared-config/docs-stub/getting-started.md b/.shared-config/docs-stub/getting-started.md
new file mode 100644
index 00000000..c4eaf081
--- /dev/null
+++ b/.shared-config/docs-stub/getting-started.md
@@ -0,0 +1,5 @@
+# Getting started
+
+This is a placeholder page so MkDocs can build with an empty tree.
+
+Add your real guides here, or delete this file and adjust navigation (for example `.index` files with [awesome-pages](https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin)).
diff --git a/.shared-config/docs-stub/index.md b/.shared-config/docs-stub/index.md
new file mode 100644
index 00000000..e4e96554
--- /dev/null
+++ b/.shared-config/docs-stub/index.md
@@ -0,0 +1,5 @@
+# Welcome
+
+This is a placeholder home page. Replace this file with your project documentation.
+
+After editing, remove or update any dummy pages added by `./.shared-config/install.sh`.
diff --git a/.shared-config/github/workflows/cicd.yml b/.shared-config/github/workflows/cicd.yml
new file mode 100644
index 00000000..4eef8369
--- /dev/null
+++ b/.shared-config/github/workflows/cicd.yml
@@ -0,0 +1,44 @@
+# SPDX-FileCopyrightText: Copyright (c) 2026 Cisco and/or its affiliates.
+# SPDX-License-Identifier: Apache-2.0
+#
+# CI: build docs on PR/push when docs or mkdocs change. Copy to .github/workflows/ at repo root.
+
+name: CI/CD Pipeline
+
+on:
+ push:
+ tags:
+ - 'v*.*.*'
+ paths:
+ - 'docs/**'
+ - 'mkdocs/**'
+ - '.shared-config/**'
+
+ pull_request:
+ paths:
+ - 'docs/**'
+ - 'mkdocs/**'
+ - '.shared-config/**'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ docs:
+ name: Docs
+ uses: ./.github/workflows/reusable-docs.yml
+ permissions:
+ contents: read
+ with:
+ version: dev
+
+ success:
+ name: Success
+ if: ${{ !cancelled() && !contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }}
+ needs:
+ - docs
+ runs-on: ubuntu-latest
+ steps:
+ - name: Echo Success
+ run: echo "::notice Success!"
diff --git a/.shared-config/github/workflows/docs-release.yml b/.shared-config/github/workflows/docs-release.yml
new file mode 100644
index 00000000..ad3d08eb
--- /dev/null
+++ b/.shared-config/github/workflows/docs-release.yml
@@ -0,0 +1,229 @@
+# SPDX-FileCopyrightText: Copyright (c) 2026 Cisco and/or its affiliates.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Ordered release pipeline:
+# 1. Build docs — validate MkDocs output
+# 2. Mike — push versioned site to gh-pages (from mkdocs/mike_versions.ini [versions] release)
+# 3. Git tag — create v* tag on the commit that was published
+# 4. Finalize — optional GitHub Release
+#
+# Copy to .github/workflows/ at repo root. Requires .shared-config/, mkdocs/, and mkdocs/pyproject.toml or uv.lock.
+
+name: Docs release
+
+on:
+ workflow_dispatch:
+ inputs:
+ create_github_release:
+ description: 'After tagging, create a GitHub Release for the same tag'
+ type: choice
+ options:
+ - 'no'
+ - 'yes'
+ default: 'no'
+
+permissions:
+ contents: write
+
+concurrency:
+ group: docs-release
+ cancel-in-progress: false
+
+jobs:
+ build:
+ name: '1. Build docs'
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup Taskfile
+ shell: bash
+ run: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Install AGNTCY theme into docs/ (shared-config)
+ shell: bash
+ run: |
+ chmod +x .shared-config/install.sh
+ ./.shared-config/install.sh
+ ./.shared-config/install.sh --with-mike-macros
+
+ - name: Sync Python environment (mkdocs)
+ shell: bash
+ run: |
+ if [ -f mkdocs/pyproject.toml ] || [ -f mkdocs/uv.lock ]; then
+ (cd mkdocs && uv sync)
+ else
+ python3 -m pip install -r .shared-config/requirements-agntcy-docs-theme.txt
+ fi
+
+ - name: Build
+ shell: bash
+ run: |
+ if [ -f mkdocs/uv.lock ] || [ -f mkdocs/pyproject.toml ]; then
+ export VERSION="$(cd mkdocs && uv run python mike_version.py release)"
+ else
+ export VERSION="$(cd mkdocs && python mike_version.py release)"
+ fi
+ task -t .shared-config/Taskfile.yml build
+
+ mike:
+ name: '2. Mike (gh-pages)'
+ needs: build
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup Taskfile
+ shell: bash
+ run: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Install AGNTCY theme into docs/ (shared-config)
+ shell: bash
+ run: |
+ chmod +x .shared-config/install.sh
+ ./.shared-config/install.sh
+ ./.shared-config/install.sh --with-mike-macros
+
+ - name: Sync Python environment (mkdocs)
+ shell: bash
+ run: |
+ if [ -f mkdocs/pyproject.toml ] || [ -f mkdocs/uv.lock ]; then
+ (cd mkdocs && uv sync)
+ else
+ python3 -m pip install -r .shared-config/requirements-agntcy-docs-theme.txt
+ fi
+
+ - name: Configure Git for mike
+ run: |
+ git config user.name github-actions[bot]
+ git config user.email github-actions[bot]@users.noreply.github.com
+
+ - name: Fetch gh-pages
+ continue-on-error: true
+ run: git fetch origin gh-pages --depth=1
+
+ - name: Mike deploy
+ shell: bash
+ run: task -t .shared-config/Taskfile.yml mike:deploy
+
+ git-tag:
+ name: '3. Git tag'
+ needs: mike
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Sync Python environment (mkdocs)
+ shell: bash
+ run: |
+ if [ -f mkdocs/pyproject.toml ] || [ -f mkdocs/uv.lock ]; then
+ (cd mkdocs && uv sync)
+ else
+ python3 -m pip install -r .shared-config/requirements-agntcy-docs-theme.txt
+ fi
+
+ - name: Create and push tag
+ shell: bash
+ env:
+ TAG_SHA: ${{ github.sha }}
+ run: |
+ set -euo pipefail
+ if [ -f mkdocs/uv.lock ] || [ -f mkdocs/pyproject.toml ]; then
+ VER="$(cd mkdocs && uv run python mike_version.py release)"
+ else
+ VER="$(cd mkdocs && python mike_version.py release)"
+ fi
+ TAG="v${VER}"
+ if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then
+ echo "::error::Tag ${TAG} already exists on origin. Bump [versions] release in mkdocs/mike_versions.ini or delete the remote tag."
+ exit 1
+ fi
+ git tag -a "${TAG}" -m "Documentation release ${TAG}" "${TAG_SHA}"
+ git push origin "${TAG}"
+
+ finalize:
+ name: '4. Deploy / release'
+ needs: git-tag
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Sync Python environment (mkdocs)
+ shell: bash
+ run: |
+ if [ -f mkdocs/pyproject.toml ] || [ -f mkdocs/uv.lock ]; then
+ (cd mkdocs && uv sync)
+ else
+ python3 -m pip install -r .shared-config/requirements-agntcy-docs-theme.txt
+ fi
+
+ - name: GitHub Release
+ if: ${{ github.event.inputs.create_github_release == 'yes' }}
+ shell: bash
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ set -euo pipefail
+ if [ -f mkdocs/uv.lock ] || [ -f mkdocs/pyproject.toml ]; then
+ VER="$(cd mkdocs && uv run python mike_version.py release)"
+ else
+ VER="$(cd mkdocs && python mike_version.py release)"
+ fi
+ TAG="v${VER}"
+ gh release create "${TAG}" --title "Documentation ${TAG}" --notes "Published from workflow [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}). Site is served from the gh-pages branch."
+
+ - name: Pages note
+ if: ${{ github.event.inputs.create_github_release != 'yes' }}
+ run: |
+ echo "::notice::GitHub Pages (if configured from gh-pages) is already serving the mike version from step 2. Set **create_github_release** to **yes** to open a GitHub Release on the new tag."
diff --git a/.shared-config/github/workflows/links.yml b/.shared-config/github/workflows/links.yml
new file mode 100644
index 00000000..aa164538
--- /dev/null
+++ b/.shared-config/github/workflows/links.yml
@@ -0,0 +1,71 @@
+# SPDX-FileCopyrightText: Copyright (c) 2026 Cisco and/or its affiliates.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Scheduled / manual full-site link check on built HTML. Copy to .github/workflows/ at repo root.
+
+name: Links
+
+on:
+ repository_dispatch:
+ workflow_dispatch:
+ schedule:
+ - cron: "00 06 * * *"
+
+jobs:
+ linkChecker:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup Golang
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.23.1'
+
+ - name: Setup Task
+ uses: arduino/setup-task@v2
+ with:
+ version: '3.48.0'
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update GITHUB_PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Install AGNTCY theme into docs/ (shared-config)
+ shell: bash
+ run: |
+ chmod +x .shared-config/install.sh
+ ./.shared-config/install.sh
+ ./.shared-config/install.sh --with-mike-macros
+
+ - name: Sync Python environment (mkdocs)
+ shell: bash
+ run: |
+ if [ -f mkdocs/pyproject.toml ] || [ -f mkdocs/uv.lock ]; then
+ (cd mkdocs && uv sync)
+ else
+ python3 -m pip install -r .shared-config/requirements-agntcy-docs-theme.txt
+ fi
+
+ - name: Build docs
+ shell: bash
+ run: task -t .shared-config/Taskfile.yml build
+
+ - name: Link Checker
+ id: lychee
+ uses: lycheeverse/lychee-action@v2.0.2
+ with:
+ args: '--config .shared-config/lychee.toml --exclude-path "(^|/)404\\.html$" .build/site'
+ fail: true
diff --git a/.shared-config/github/workflows/pr-links.yml b/.shared-config/github/workflows/pr-links.yml
new file mode 100644
index 00000000..f1d3eb6a
--- /dev/null
+++ b/.shared-config/github/workflows/pr-links.yml
@@ -0,0 +1,135 @@
+# SPDX-FileCopyrightText: Copyright (c) 2026 Cisco and/or its affiliates.
+# SPDX-License-Identifier: Apache-2.0
+#
+# PR: lychee on changed docs pages only. Copy to .github/workflows/ at repo root.
+#
+# Optional: if your docs reference a fixed production URL, add a --remap line below
+# (see https://lychee.cli.rs/recipes/remapping/) so those links resolve to .build/site.
+
+name: Check links in diffs
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened]
+ branches: [main, master]
+ workflow_dispatch:
+
+jobs:
+ check-links:
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - name: Clone repository (PR)
+ if: github.event_name == 'pull_request'
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.pull_request.head.ref }}
+ repository: ${{ github.event.pull_request.head.repo.full_name }}
+ submodules: recursive
+
+ - name: Clone repository (manual)
+ if: github.event_name == 'workflow_dispatch'
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup Golang
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.23.1'
+
+ - name: Setup Task
+ uses: arduino/setup-task@v2
+ with:
+ version: '3.48.0'
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update GITHUB_PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Install AGNTCY theme into docs/ (shared-config)
+ shell: bash
+ run: |
+ chmod +x .shared-config/install.sh
+ ./.shared-config/install.sh
+ ./.shared-config/install.sh --with-mike-macros
+
+ - name: Sync Python environment (mkdocs)
+ shell: bash
+ run: |
+ if [ -f mkdocs/pyproject.toml ] || [ -f mkdocs/uv.lock ]; then
+ (cd mkdocs && uv sync)
+ else
+ python3 -m pip install -r .shared-config/requirements-agntcy-docs-theme.txt
+ fi
+
+ - name: Build docs
+ shell: bash
+ run: task -t .shared-config/Taskfile.yml build
+
+ - name: Fetch base branch
+ if: github.event_name == 'pull_request'
+ run: |
+ git remote add upstream https://github.com/${{ github.repository }}.git 2>/dev/null || true
+ git fetch upstream ${{ github.event.pull_request.base.ref }}
+
+ - name: Get paths to check
+ id: get_paths
+ run: |
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
+ echo "paths=.build/site" >> $GITHUB_OUTPUT
+ echo "has_paths=true" >> $GITHUB_OUTPUT
+ exit 0
+ fi
+ BASE=$(git merge-base FETCH_HEAD HEAD)
+ changed=$(git diff --name-only $BASE...HEAD -- docs/)
+ paths=""
+ for f in $changed; do
+ [ -z "$f" ] && continue
+ case "$f" in *.md) ;; *) continue ;; esac
+ if [ "$f" = "docs/index.md" ]; then
+ paths="$paths .build/site/index.html"
+ else
+ path="${f#docs/}"
+ path="${path%.md}"
+ paths="$paths .build/site/${path}/"
+ fi
+ done
+ paths=$(echo $paths)
+ echo "paths=$paths" >> $GITHUB_OUTPUT
+ [ -n "$paths" ] && echo "has_paths=true" >> $GITHUB_OUTPUT || echo "has_paths=false" >> $GITHUB_OUTPUT
+
+ - name: Remove paths lychee cannot parse
+ if: steps.get_paths.outputs.has_paths == 'true'
+ run: |
+ find .build/site -name '.index' -type f -delete
+ find .build/site -depth -name '.index' -type d -exec rm -rf {} \;
+
+ - name: Check links
+ if: steps.get_paths.outputs.has_paths == 'true'
+ uses: lycheeverse/lychee-action@v2.0.2
+ with:
+ lycheeVersion: "v0.22.0"
+ args: |
+ --no-progress
+ --config .shared-config/lychee.toml
+ --root-dir $PWD/.build/site
+ --exclude-all-private
+ ${{ steps.get_paths.outputs.paths }}
+ fail: true
+ output: lychee/out.md
+
+ - name: Suggestions
+ if: failure()
+ run: |
+ echo -e "\nPlease review the links reported in the Check links step above."
+ echo -e "If a link is valid but fails due to a CAPTCHA challenge, IP blocking, login requirements, etc.,"
+ echo -e "consider adding such links to .shared-config/lychee.toml exclude list to bypass future checks.\n"
+ exit 1
diff --git a/.shared-config/github/workflows/reusable-docs.yml b/.shared-config/github/workflows/reusable-docs.yml
new file mode 100644
index 00000000..6194075c
--- /dev/null
+++ b/.shared-config/github/workflows/reusable-docs.yml
@@ -0,0 +1,75 @@
+# SPDX-FileCopyrightText: Copyright (c) 2026 Cisco and/or its affiliates.
+# SPDX-License-Identifier: Apache-2.0
+#
+# Build-only documentation check. Copy to .github/workflows/ at repo root.
+# Versioned publish (mike), tagging, and releases: docs-release.yml in this folder.
+
+name: Documentation
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ description: 'MkDocs macros VERSION (docs_build_version)'
+ required: true
+ type: string
+ default: dev
+
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'MkDocs macros VERSION'
+ required: false
+ type: string
+ default: dev
+
+permissions:
+ contents: read
+
+jobs:
+ docs:
+ name: Build docs
+ runs-on: ubuntu-latest
+ env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Setup Taskfile
+ shell: bash
+ run: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin
+
+ - name: Setup UV
+ shell: bash
+ run: curl -LsSf https://astral.sh/uv/install.sh | sh
+
+ - name: Update GITHUB_PATH
+ shell: bash
+ run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"
+
+ - name: Install AGNTCY theme into docs/ (shared-config)
+ shell: bash
+ run: |
+ chmod +x .shared-config/install.sh
+ ./.shared-config/install.sh
+ ./.shared-config/install.sh --with-mike-macros
+
+ - name: Sync Python environment (mkdocs)
+ shell: bash
+ run: |
+ if [ -f mkdocs/pyproject.toml ] || [ -f mkdocs/uv.lock ]; then
+ (cd mkdocs && uv sync)
+ else
+ python3 -m pip install -r .shared-config/requirements-agntcy-docs-theme.txt
+ fi
+
+ - name: Build docs
+ shell: bash
+ run: |
+ V="${{ inputs.version }}"
+ export VERSION="${V:-dev}"
+ task -t .shared-config/Taskfile.yml build
diff --git a/.shared-config/install.sh b/.shared-config/install.sh
new file mode 100755
index 00000000..d65c9824
--- /dev/null
+++ b/.shared-config/install.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+# Copy AGNTCY theme assets into docs/ (MkDocs serves extra_css and theme static files from docs_dir).
+# Bootstraps mkdocs/mkdocs.yml from mkdocs.starter.yml and placeholder docs when those files are missing.
+# Optional: ./install.sh --with-mike-macros → copies mike_version.py, mike_versions.ini, main.py into mkdocs/
+set -euo pipefail
+
+SHARED_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "$SHARED_ROOT/.." && pwd)"
+DOCS="$REPO_ROOT/docs"
+MKDOCS="$REPO_ROOT/mkdocs"
+
+mkdir -p "$DOCS/stylesheets" "$DOCS/assets/agntcy/img" "$MKDOCS"
+cp -f "$SHARED_ROOT/stylesheets/custom.css" "$DOCS/stylesheets/agntcy-docs.css"
+cp -f "$SHARED_ROOT/assets/favicon.ico" "$DOCS/assets/agntcy/"
+cp -f "$SHARED_ROOT/assets/img/"*.svg "$DOCS/assets/agntcy/img/"
+if [[ -f "$SHARED_ROOT/assets/logo.svg" ]]; then
+ cp -f "$SHARED_ROOT/assets/logo.svg" "$DOCS/assets/agntcy/"
+fi
+
+echo "AGNTCY docs theme installed under $DOCS (stylesheets/agntcy-docs.css, assets/agntcy/)."
+
+created_mkdocs=0
+if [[ ! -f "$MKDOCS/mkdocs.yml" ]]; then
+ cp -f "$SHARED_ROOT/mkdocs/mkdocs.starter.yml" "$MKDOCS/mkdocs.yml"
+ created_mkdocs=1
+ echo "Created $MKDOCS/mkdocs.yml from mkdocs.starter.yml (replace placeholders; use mkdocs.template.yml for full agntcy/docs parity)."
+else
+ echo "Leaving existing $MKDOCS/mkdocs.yml unchanged."
+fi
+
+# Placeholder pages only when bootstrapping a new mkdocs.yml (avoids adding stubs to mature docs trees).
+if [[ "$created_mkdocs" -eq 1 ]]; then
+ if [[ ! -f "$DOCS/index.md" ]]; then
+ cp -f "$SHARED_ROOT/docs-stub/index.md" "$DOCS/index.md"
+ echo "Created $DOCS/index.md (placeholder)."
+ else
+ echo "Leaving existing $DOCS/index.md unchanged."
+ fi
+ if [[ ! -f "$DOCS/getting-started.md" ]]; then
+ cp -f "$SHARED_ROOT/docs-stub/getting-started.md" "$DOCS/getting-started.md"
+ echo "Created $DOCS/getting-started.md (placeholder)."
+ else
+ echo "Leaving existing $DOCS/getting-started.md unchanged."
+ fi
+fi
+
+if [[ "${1:-}" == "--with-mike-macros" ]]; then
+ cp -f "$SHARED_ROOT/mkdocs/mike_version.py" "$MKDOCS/"
+ cp -f "$SHARED_ROOT/mkdocs/mike_versions.ini" "$MKDOCS/"
+ if [[ ! -f "$MKDOCS/main.py" ]]; then
+ cp -f "$SHARED_ROOT/mkdocs/main.py.example" "$MKDOCS/main.py"
+ echo "Created $MKDOCS/main.py from main.py.example"
+ else
+ echo "Leaving existing $MKDOCS/main.py unchanged."
+ fi
+ echo "Mike + macros helpers installed under $MKDOCS (mike_version.py, mike_versions.ini, main.py)."
+fi
diff --git a/.shared-config/lychee.toml b/.shared-config/lychee.toml
new file mode 100644
index 00000000..3dcba3c9
--- /dev/null
+++ b/.shared-config/lychee.toml
@@ -0,0 +1,25 @@
+# https://github.com/lycheeverse/lychee
+max_concurrency = 10
+timeout = 10
+accept = [200, 201, 204, 403, 429, 503]
+
+exclude = [
+ 'localhost',
+ 'http://127\\.0\\.0\\.1',
+ 'https://127\\.0\\.0\\.1',
+ 'http://0\\.0\\.0\\.0',
+ 'host\\.docker\\.internal',
+ '\\.well-known',
+ 'example\\.com',
+ 'your-username',
+ 'company\\.com',
+ 'w3\\.org',
+ 'gnu\\.org',
+]
+
+exclude_path = ['overrides', '\\.index(/|$)', '(^|/)404\\.html$']
+cache = true
+max_retries = 3
+method = "get"
+include_verbatim = false
+user_agent = "lychee/agntcy-docs-theme"
diff --git a/.shared-config/mkdocs/hooks.py b/.shared-config/mkdocs/hooks.py
new file mode 100644
index 00000000..911276de
--- /dev/null
+++ b/.shared-config/mkdocs/hooks.py
@@ -0,0 +1,13 @@
+"""MkDocs hooks to configure SSL certificates for include-markdown plugin."""
+import ssl
+import certifi
+
+
+def on_startup(**kwargs):
+ """Configure SSL context to use certifi certificates."""
+ import urllib.request
+
+ ssl_context = ssl.create_default_context(cafile=certifi.where())
+ https_handler = urllib.request.HTTPSHandler(context=ssl_context)
+ opener = urllib.request.build_opener(https_handler)
+ urllib.request.install_opener(opener)
diff --git a/.shared-config/mkdocs/main.py.example b/.shared-config/mkdocs/main.py.example
new file mode 100644
index 00000000..923d4efb
--- /dev/null
+++ b/.shared-config/mkdocs/main.py.example
@@ -0,0 +1,23 @@
+"""MkDocs macros: shared variables (from mkdocs.yml `extra`) and reusable helpers."""
+
+from __future__ import annotations
+
+import html
+import os
+
+
+def define_env(env):
+ """Register variables and macros for mkdocs-macros-plugin."""
+
+ env.variables["docs_build_version"] = os.environ.get("VERSION", "dev")
+
+ @env.macro
+ def var_tag(label: str, hint: str = "") -> str:
+ """Inline tag for env vars, flags, or keys. Use [[[ var_tag('MY_ENV') ]]] in Markdown."""
+ safe_label = html.escape(str(label), quote=True)
+ safe_hint = html.escape(str(hint), quote=True) if hint else ""
+ title = f' title="{safe_hint}"' if safe_hint else ""
+ return (
+ f''
+ f"{safe_label}"
+ )
diff --git a/.shared-config/mkdocs/mike_version.py b/.shared-config/mkdocs/mike_version.py
new file mode 100644
index 00000000..c9f70eeb
--- /dev/null
+++ b/.shared-config/mkdocs/mike_version.py
@@ -0,0 +1,46 @@
+"""Print mike version from mike_versions.ini; optional VERSION env overrides release."""
+
+from __future__ import annotations
+
+import argparse
+import configparser
+import os
+from pathlib import Path
+
+
+def strip_leading_v(value: str) -> str:
+ value = value.strip()
+ return value[1:] if value.startswith("v") else value
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("mode", choices=("local", "release"))
+ args = parser.parse_args()
+
+ ini = Path(__file__).resolve().parent / "mike_versions.ini"
+ cfg = configparser.ConfigParser()
+ if not cfg.read(ini, encoding="utf-8"):
+ raise SystemExit(f"could not read {ini}")
+
+ if args.mode == "local":
+ raw = cfg.get("versions", "local", fallback="dev")
+ out = strip_leading_v(raw) or "dev"
+ print(out)
+ return
+
+ env = os.environ.get("VERSION", "").strip()
+ if env:
+ print(strip_leading_v(env))
+ return
+
+ raw = cfg.get("versions", "release", fallback="").strip()
+ if not raw:
+ raise SystemExit(
+ "Set [versions] release in mkdocs/mike_versions.ini or export VERSION=..."
+ )
+ print(strip_leading_v(raw))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/.shared-config/mkdocs/mike_versions.ini b/.shared-config/mkdocs/mike_versions.ini
new file mode 100644
index 00000000..5fe92ebc
--- /dev/null
+++ b/.shared-config/mkdocs/mike_versions.ini
@@ -0,0 +1,8 @@
+# Mike version labels (edit by hand).
+#
+# local — task mike:deploy-local (no git push)
+# release — task mike:deploy; set before CI or export VERSION=v1.0.0 for one run.
+
+[versions]
+local = dev
+release =
diff --git a/.shared-config/mkdocs/mkdocs.starter.yml b/.shared-config/mkdocs/mkdocs.starter.yml
new file mode 100644
index 00000000..74dd642c
--- /dev/null
+++ b/.shared-config/mkdocs/mkdocs.starter.yml
@@ -0,0 +1,119 @@
+# Minimal MkDocs config for a new repo (install.sh copies this to mkdocs/mkdocs.yml when missing).
+# For full parity with agntcy/docs (redirects, macros, API docs, Swagger), use mkdocs.template.yml instead.
+
+site_name: MY_PROJECT_DOCS
+site_url: https://example.org/
+
+docs_dir: ../docs
+
+repo_name: org/repo
+repo_url: https://github.com/org/repo
+edit_uri: edit/main/docs/
+
+extra:
+ version:
+ provider: mike
+ var:
+ docs_url: https://example.org/
+ repo_url: https://github.com/org/repo
+ repo_slug: org/repo
+ org: AGNTCY
+
+ copyright: >
+ © Copyright AGNTCY Contributors.
+ Change cookie settings
+ analytics:
+ provider: google
+ property: G-XXXXXXXXXX
+ consent:
+ title: Cookie consent
+ description: >-
+ We use cookies to recognize your repeated visits and preferences, as well
+ as to measure the effectiveness of our documentation and whether users
+ find what they are searching for. With your consent, you're helping us to
+ make our documentation better.
+ actions:
+ - accept
+ - reject
+
+extra_css:
+ - stylesheets/agntcy-docs.css
+
+markdown_extensions:
+ - admonition
+ - attr_list
+ - def_list
+ - md_in_html
+ - pymdownx.details
+ - pymdownx.superfences
+ - pymdownx.tabbed:
+ alternate_style: true
+ - pymdownx.emoji:
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
+ - pymdownx.superfences:
+ custom_fences:
+ - name: mermaid
+ class: mermaid
+ format: !!python/name:pymdownx.superfences.fence_code_format
+
+hooks:
+ - ../.shared-config/mkdocs/hooks.py
+
+plugins:
+ search:
+ mike:
+ alias_type: redirect
+ redirects:
+ redirect_maps: {}
+ awesome-pages:
+ filename: ".index"
+ collapse_single_pages: true
+ strict: false
+
+theme:
+ name: material
+ custom_dir: ../.shared-config/mkdocs/overrides
+
+ features:
+ - announce.dismiss
+ - content.action.edit
+ - content.action.view
+ - content.code.annotate
+ - content.code.copy
+ - content.tabs.link
+ - content.tooltips
+ - navigation.footer
+ - navigation.indexes
+ - navigation.top
+ - navigation.tracking
+ - search.highlight
+ - search.share
+ - search.suggest
+ - toc.follow
+
+ palette:
+ - media: "(prefers-color-scheme)"
+ toggle:
+ icon: material/link
+ name: Switch to light mode
+ - media: "(prefers-color-scheme: light)"
+ scheme: default
+ primary: indigo
+ accent: indigo
+ toggle:
+ icon: material/toggle-switch
+ name: Switch to dark mode
+ - media: "(prefers-color-scheme: dark)"
+ scheme: slate
+ primary: black
+ accent: indigo
+ toggle:
+ icon: material/toggle-switch-off
+ name: Switch to system preference
+ font:
+ text: Roboto
+ code: Roboto Mono
+ favicon: assets/agntcy/favicon.ico
+ logo_light: assets/agntcy/img/logo-light.svg
+ logo_dark: assets/agntcy/img/logo-dark.svg
diff --git a/.shared-config/mkdocs/mkdocs.template.yml b/.shared-config/mkdocs/mkdocs.template.yml
new file mode 100644
index 00000000..652a039c
--- /dev/null
+++ b/.shared-config/mkdocs/mkdocs.template.yml
@@ -0,0 +1,152 @@
+# AGNTCY MkDocs starter — copy to mkdocs/mkdocs.yml and replace placeholders.
+# Run ./.shared-config/install.sh (and --with-mike-macros if using mike + macros).
+#
+# Parity with agntcy/docs: hooks, redirects, awesome-pages, macros, mkdocstrings,
+# swagger-ui-tag, include-markdown, mike, analytics + consent (replace GA property).
+
+site_name: MY_PROJECT_DOCS
+site_url: https://example.org/
+
+docs_dir: ../docs
+
+repo_name: org/repo
+repo_url: https://github.com/org/repo
+edit_uri: edit/main/docs/
+
+extra:
+ version:
+ provider: mike
+ var:
+ docs_url: https://example.org/
+ repo_url: https://github.com/org/repo
+ repo_slug: org/repo
+ org: AGNTCY
+
+ copyright: >
+ © Copyright AGNTCY Contributors.
+ Change cookie settings
+ analytics:
+ provider: google
+ property: G-XXXXXXXXXX
+ consent:
+ title: Cookie consent
+ description: >-
+ We use cookies to recognize your repeated visits and preferences, as well
+ as to measure the effectiveness of our documentation and whether users
+ find what they're searching for. With your consent, you're helping us to
+ make our documentation better.
+ actions:
+ - accept
+ - reject
+
+extra_css:
+ - stylesheets/agntcy-docs.css
+
+markdown_extensions:
+ - admonition
+ - attr_list
+ - def_list
+ - md_in_html
+ - pymdownx.details
+ - pymdownx.superfences
+ - pymdownx.tabbed:
+ alternate_style: true
+ - pymdownx.emoji:
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
+ - pymdownx.superfences:
+ custom_fences:
+ - name: mermaid
+ class: mermaid
+ format: !!python/name:pymdownx.superfences.fence_code_format
+
+hooks:
+ - ../.shared-config/mkdocs/hooks.py
+
+plugins:
+ search:
+ mike:
+ alias_type: redirect
+ redirects:
+ redirect_maps:
+ 'how-to-guides/identity-quickstart/index.md': 'identity/identity-quickstart.md'
+ 'how-to-guides/agent-directory/index.md': 'dir/getting-started.md'
+ 'messaging/slim-core.md': 'slim/overview.md'
+ 'messaging/slim-a2a.md': 'slim/slim-a2a.md'
+ 'messaging/slim-authentication.md': 'slim/slim-authentication.md'
+ 'messaging/slim-controller.md': 'slim/slim-controller.md'
+ 'messaging/slim-data-plane-config.md': 'slim/slim-data-plane-config.md'
+ 'messaging/slim-data-plane.md': 'slim/slim-data-plane.md'
+ 'messaging/slim-group-tutorial.md': 'slim/slim-group-tutorial.md'
+ 'messaging/slim-group.md': 'slim/slim-group.md'
+ 'messaging/slim-howto.md': 'slim/slim-howto.md'
+ 'messaging/slim-mcp.md': 'slim/slim-mcp.md'
+ 'messaging/slim-rpc.md': 'slim/slim-rpc.md'
+ 'messaging/slim-session.md': 'slim/slim-session.md'
+ 'messaging/slim-slimrpc-compiler.md': 'slim/slim-slimrpc-compiler.md'
+ awesome-pages:
+ filename: ".index"
+ collapse_single_pages: true
+ strict: false
+ macros:
+ module_name: main
+ j2_variable_start_string: "[[["
+ j2_variable_end_string: "]]]"
+ mkdocstrings:
+ handlers:
+ python:
+ options:
+ docstring_style: sphinx
+ docstring_section_style: list
+ extensions:
+ - griffe_pydantic:
+ schema: true
+ swagger-ui-tag:
+ include-markdown:
+
+theme:
+ name: material
+ custom_dir: ../.shared-config/mkdocs/overrides
+
+ features:
+ - announce.dismiss
+ - content.action.edit
+ - content.action.view
+ - content.code.annotate
+ - content.code.copy
+ - content.tabs.link
+ - content.tooltips
+ - navigation.footer
+ - navigation.indexes
+ - navigation.top
+ - navigation.tracking
+ - search.highlight
+ - search.share
+ - search.suggest
+ - toc.follow
+
+ palette:
+ - media: "(prefers-color-scheme)"
+ toggle:
+ icon: material/link
+ name: Switch to light mode
+ - media: "(prefers-color-scheme: light)"
+ scheme: default
+ primary: indigo
+ accent: indigo
+ toggle:
+ icon: material/toggle-switch
+ name: Switch to dark mode
+ - media: "(prefers-color-scheme: dark)"
+ scheme: slate
+ primary: black
+ accent: indigo
+ toggle:
+ icon: material/toggle-switch-off
+ name: Switch to system preference
+ font:
+ text: Roboto
+ code: Roboto Mono
+ favicon: assets/agntcy/favicon.ico
+ logo_light: assets/agntcy/img/logo-light.svg
+ logo_dark: assets/agntcy/img/logo-dark.svg
diff --git a/.shared-config/mkdocs/overrides/partials/copyright.html b/.shared-config/mkdocs/overrides/partials/copyright.html
new file mode 100644
index 00000000..bdf2dca2
--- /dev/null
+++ b/.shared-config/mkdocs/overrides/partials/copyright.html
@@ -0,0 +1,3 @@
+
{safe_label}"
+ )
diff --git a/mkdocs/mike_version.py b/mkdocs/mike_version.py
new file mode 100644
index 00000000..c9f70eeb
--- /dev/null
+++ b/mkdocs/mike_version.py
@@ -0,0 +1,46 @@
+"""Print mike version from mike_versions.ini; optional VERSION env overrides release."""
+
+from __future__ import annotations
+
+import argparse
+import configparser
+import os
+from pathlib import Path
+
+
+def strip_leading_v(value: str) -> str:
+ value = value.strip()
+ return value[1:] if value.startswith("v") else value
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("mode", choices=("local", "release"))
+ args = parser.parse_args()
+
+ ini = Path(__file__).resolve().parent / "mike_versions.ini"
+ cfg = configparser.ConfigParser()
+ if not cfg.read(ini, encoding="utf-8"):
+ raise SystemExit(f"could not read {ini}")
+
+ if args.mode == "local":
+ raw = cfg.get("versions", "local", fallback="dev")
+ out = strip_leading_v(raw) or "dev"
+ print(out)
+ return
+
+ env = os.environ.get("VERSION", "").strip()
+ if env:
+ print(strip_leading_v(env))
+ return
+
+ raw = cfg.get("versions", "release", fallback="").strip()
+ if not raw:
+ raise SystemExit(
+ "Set [versions] release in mkdocs/mike_versions.ini or export VERSION=..."
+ )
+ print(strip_leading_v(raw))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/mkdocs/mike_versions.ini b/mkdocs/mike_versions.ini
new file mode 100644
index 00000000..c2812c01
--- /dev/null
+++ b/mkdocs/mike_versions.ini
@@ -0,0 +1,11 @@
+# Mike version labels (edit by hand).
+#
+# local — task mike:deploy-local (no git push)
+# release — task mike:deploy; GitHub: workflow "Docs release" (build → mike → tag → finalize).
+# Bump release before running that workflow; tags are created after mike in CI.
+#
+# Optional: VERSION=v1.2.0 task mike:deploy overrides release for that run only.
+
+[versions]
+local = dev
+release =
diff --git a/mkdocs/mkdocs.yml b/mkdocs/mkdocs.yml
index 46e5ad8c..83503c8c 100644
--- a/mkdocs/mkdocs.yml
+++ b/mkdocs/mkdocs.yml
@@ -8,6 +8,15 @@ repo_url: https://github.com/agntcy/docs
edit_uri: edit/main/docs/
extra:
+ version:
+ provider: mike
+ # Shared Jinja variables for mkdocs-macros (use as [[[ var.docs_url ]]], etc.)
+ var:
+ docs_url: https://docs.agntcy.org/
+ repo_url: https://github.com/agntcy/docs
+ repo_slug: agntcy/docs
+ org: AGNTCY
+
copyright: >
© Copyright AGNTCY Contributors.
Change cookie settings
@@ -50,6 +59,8 @@ hooks:
plugins:
search:
+ mike:
+ alias_type: redirect
redirects:
redirect_maps:
'how-to-guides/identity-quickstart/index.md': 'identity/identity-quickstart.md'
@@ -71,6 +82,12 @@ plugins:
filename: ".index"
collapse_single_pages: true
strict: false
+ macros:
+ # main.py in this directory defines env.variables and @env.macro helpers
+ module_name: main
+ # Avoid clashing with GitHub Actions (${{ }}) and JSX ({{ }}) in fenced examples
+ j2_variable_start_string: "[[["
+ j2_variable_end_string: "]]]"
mkdocstrings:
handlers:
python:
diff --git a/mkdocs/pyproject.toml b/mkdocs/pyproject.toml
index c00e63e8..52f9ac2f 100644
--- a/mkdocs/pyproject.toml
+++ b/mkdocs/pyproject.toml
@@ -9,7 +9,7 @@ dependencies = [
"mkdocs-exclude==1.0.2",
"mkdocs-awesome-pages-plugin==2.10.1",
"markdown>=3.2",
- "mkdocs>=1.3.0",
+ "mkdocs>=1.3.0,<2",
"mkdocs-material-extensions>=1.0.3",
"mkdocs-autorefs==1.4.1",
"pygments>=2.12",
@@ -18,14 +18,14 @@ dependencies = [
"mkdocstrings-python==1.16.10",
"griffe-pydantic==1.1.4",
"griffe==1.7.2",
- "agntcy-acp>=1.5.2", # 1.5.x allows langgraph 0.4+ so we can use langgraph-checkpoint>=3.0
+ "agntcy-acp>=1.5.2", # 1.5.x allows langgraph 0.4+ so we can use langgraph-checkpoint>=3.0
"agntcy-iomapper==0.2.2",
- "langgraph-checkpoint>=3.0.0", # JsonPlusSerializer RCE in "json" mode
- "llama-index>=0.12.38", # JSONReader DoS (uncontrolled recursion) fixed in 0.12.38
- "llama-index-core>=0.12.38,<0.13", # pin to 0.12.x so resolver does not pick 0.13.0 on Python 3.12.4+
- "nltk>=3.9.3", # CVE-2025-14009: zip slip / RCE in downloader
- "orjson>=3.11.6", # CVE-2025-67221: recursion limit for deeply nested JSON
- "pillow>=12.1.1", # CVE-2025-48379, CVE-2026-25990
+ "langgraph-checkpoint>=3.0.0", # JsonPlusSerializer RCE in "json" mode
+ "llama-index>=0.12.38", # JSONReader DoS (uncontrolled recursion) fixed in 0.12.38
+ "llama-index-core>=0.12.38,<0.13", # pin to 0.12.x so resolver does not pick 0.13.0 on Python 3.12.4+
+ "nltk>=3.9.3", # CVE-2025-14009: zip slip / RCE in downloader
+ "orjson>=3.11.6", # CVE-2025-67221: recursion limit for deeply nested JSON
+ "pillow>=12.1.1", # CVE-2025-48379, CVE-2026-25990
"beautifulsoup4>=4.13.3",
"mkdocs-swagger-ui-tag==0.7.1",
"mkdocs-include-markdown-plugin>=7.2.0",
@@ -35,4 +35,6 @@ dependencies = [
"pymarkdownlnt>=0.9.0",
"mkdocs-htmlproofer-plugin>=1.0.0",
"certifi>=2025.1.31",
+ "mike>=2.1.4",
+ "mkdocs-macros-plugin>=1.5.0",
]
diff --git a/mkdocs/uv.lock b/mkdocs/uv.lock
index 80d1ae49..2f7d9e2f 100644
--- a/mkdocs/uv.lock
+++ b/mkdocs/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
resolution-markers = [
"python_full_version >= '3.12.4' and python_full_version < '4'",
@@ -46,6 +46,7 @@ dependencies = [
{ name = "llama-index" },
{ name = "llama-index-core" },
{ name = "markdown" },
+ { name = "mike" },
{ name = "mkdocs" },
{ name = "mkdocs-autorefs" },
{ name = "mkdocs-awesome-pages-plugin" },
@@ -53,6 +54,7 @@ dependencies = [
{ name = "mkdocs-git-revision-date-plugin" },
{ name = "mkdocs-htmlproofer-plugin" },
{ name = "mkdocs-include-markdown-plugin" },
+ { name = "mkdocs-macros-plugin" },
{ name = "mkdocs-material" },
{ name = "mkdocs-material-extensions" },
{ name = "mkdocs-redirects" },
@@ -79,13 +81,15 @@ requires-dist = [
{ name = "llama-index", specifier = ">=0.12.38" },
{ name = "llama-index-core", specifier = ">=0.12.38,<0.13" },
{ name = "markdown", specifier = ">=3.2" },
- { name = "mkdocs", specifier = ">=1.3.0" },
+ { name = "mike", specifier = ">=2.1.4" },
+ { name = "mkdocs", specifier = ">=1.3.0,<2" },
{ name = "mkdocs-autorefs", specifier = "==1.4.1" },
{ name = "mkdocs-awesome-pages-plugin", specifier = "==2.10.1" },
{ name = "mkdocs-exclude", specifier = "==1.0.2" },
{ name = "mkdocs-git-revision-date-plugin", specifier = "==0.3.2" },
{ name = "mkdocs-htmlproofer-plugin", specifier = ">=1.0.0" },
{ name = "mkdocs-include-markdown-plugin", specifier = ">=7.2.0" },
+ { name = "mkdocs-macros-plugin", specifier = ">=1.5.0" },
{ name = "mkdocs-material", specifier = "==9.6.12" },
{ name = "mkdocs-material-extensions", specifier = ">=1.0.3" },
{ name = "mkdocs-redirects", specifier = ">=1.2.0" },
@@ -990,6 +994,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
+[[package]]
+name = "hjson"
+version = "3.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/82/e5/0b56d723a76ca67abadbf7fb71609fb0ea7e6926e94fcca6c65a85b36a0e/hjson-3.1.0.tar.gz", hash = "sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75", size = 40541, upload-time = "2022-08-13T02:53:01.919Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/7f/13cd798d180af4bf4c0ceddeefba2b864a63c71645abc0308b768d67bb81/hjson-3.1.0-py3-none-any.whl", hash = "sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89", size = 54018, upload-time = "2022-08-13T02:52:59.899Z" },
+]
+
[[package]]
name = "httpcore"
version = "1.0.9"
@@ -1658,6 +1671,23 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" },
]
+[[package]]
+name = "mike"
+version = "2.1.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jinja2" },
+ { name = "mkdocs" },
+ { name = "pyparsing" },
+ { name = "pyyaml" },
+ { name = "pyyaml-env-tag" },
+ { name = "verspec" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ec/09/de1cab0018eb5f1fbd9dcc26b6e61f9453c5ec2eb790949d6ed75e1ffe55/mike-2.1.4.tar.gz", hash = "sha256:75d549420b134603805a65fc67f7dcd9fcd0ad1454fb2c893d9e844cba1aa6e4", size = 38190, upload-time = "2026-03-08T02:46:29.187Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/48/f7/10f5e101db25741b91e4f4792c5d97b4fa834ead5cf509ae91097d939424/mike-2.1.4-py3-none-any.whl", hash = "sha256:39933e992e155dd70f2297e749a0ed78d8fd7942bc33a3666195d177758a280e", size = 33820, upload-time = "2026-03-08T02:46:28.149Z" },
+]
+
[[package]]
name = "mkdocs"
version = "1.6.1"
@@ -1774,6 +1804,27 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/f9/783338d1d7fd548c7635728b67a0f8f96d9e6c265aa61c51356c03597767/mkdocs_include_markdown_plugin-7.2.0-py3-none-any.whl", hash = "sha256:d56cdaeb2d113fb66ed0fe4fb7af1da889926b0b9872032be24e19bbb09c9f5b", size = 29548, upload-time = "2025-09-28T21:50:49.373Z" },
]
+[[package]]
+name = "mkdocs-macros-plugin"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "hjson" },
+ { name = "jinja2" },
+ { name = "mkdocs" },
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "python-dateutil" },
+ { name = "pyyaml" },
+ { name = "requests" },
+ { name = "super-collections" },
+ { name = "termcolor" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/92/15/e6a44839841ebc9c5872fa0e6fad1c3757424e4fe026093b68e9f386d136/mkdocs_macros_plugin-1.5.0.tar.gz", hash = "sha256:12aa45ce7ecb7a445c66b9f649f3dd05e9b92e8af6bc65e4acd91d26f878c01f", size = 37730, upload-time = "2025-11-13T08:08:55.545Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/51/62/9fffba5bb9ed3d31a932ad35038ba9483d59850256ee0fea7f1187173983/mkdocs_macros_plugin-1.5.0-py3-none-any.whl", hash = "sha256:c10fabd812bf50f9170609d0ed518e54f1f0e12c334ac29141723a83c881dd6f", size = 44626, upload-time = "2025-11-13T08:08:53.878Z" },
+]
+
[[package]]
name = "mkdocs-material"
version = "9.6.12"
@@ -2820,6 +2871,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/06/43084e6cbd4b3bc0e80f6be743b2e79fbc6eed8de9ad8c629939fa55d972/pymdown_extensions-10.16.1-py3-none-any.whl", hash = "sha256:d6ba157a6c03146a7fb122b2b9a121300056384eafeec9c9f9e584adfdb2a32d", size = 266178, upload-time = "2025-07-28T16:19:31.401Z" },
]
+[[package]]
+name = "pyparsing"
+version = "3.3.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
+]
+
[[package]]
name = "pypdf"
version = "5.4.0"
@@ -3288,6 +3348,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/cf/0fea4f4ba3fc2772ac2419278aa9f6964124d4302117d61bc055758e000c/striprtf-0.0.26-py3-none-any.whl", hash = "sha256:8c8f9d32083cdc2e8bfb149455aa1cc5a4e0a035893bedc75db8b73becb3a1bb", size = 6914, upload-time = "2023-07-20T14:30:35.338Z" },
]
+[[package]]
+name = "super-collections"
+version = "0.6.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "hjson" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e0/de/a0c3d1244912c260638f0f925e190e493ccea37ecaea9bbad7c14413b803/super_collections-0.6.2.tar.gz", hash = "sha256:0c8d8abacd9fad2c7c1c715f036c29f5db213f8cac65f24d45ecba12b4da187a", size = 31315, upload-time = "2025-09-30T00:37:08.067Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/17/43/47c7cf84b3bd74a8631b02d47db356656bb8dff6f2e61a4c749963814d0d/super_collections-0.6.2-py3-none-any.whl", hash = "sha256:291b74d26299e9051d69ad9d89e61b07b6646f86a57a2f5ab3063d206eee9c56", size = 16173, upload-time = "2025-09-30T00:37:07.104Z" },
+]
+
[[package]]
name = "tenacity"
version = "9.1.2"
@@ -3297,6 +3369,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" },
]
+[[package]]
+name = "termcolor"
+version = "3.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/46/79/cf31d7a93a8fdc6aa0fbb665be84426a8c5a557d9240b6239e9e11e35fc5/termcolor-3.3.0.tar.gz", hash = "sha256:348871ca648ec6a9a983a13ab626c0acce02f515b9e1983332b17af7979521c5", size = 14434, upload-time = "2025-12-29T12:55:21.882Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/33/d1/8bb87d21e9aeb323cc03034f5eaf2c8f69841e40e4853c2627edf8111ed3/termcolor-3.3.0-py3-none-any.whl", hash = "sha256:cf642efadaf0a8ebbbf4bc7a31cec2f9b5f21a9f726f4ccbb08192c9c26f43a5", size = 7734, upload-time = "2025-12-29T12:55:20.718Z" },
+]
+
[[package]]
name = "tiktoken"
version = "0.9.0"
@@ -3501,6 +3582,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/d0/5bf7cbf1ac138c92b9ac21066d18faf4d7e7f651047b700eb192ca4b9fdb/uuid_utils-0.14.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:258186964039a8e36db10810c1ece879d229b01331e09e9030bc5dcabe231bd2", size = 364700, upload-time = "2026-02-20T22:50:21.732Z" },
]
+[[package]]
+name = "verspec"
+version = "0.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/44/8126f9f0c44319b2efc65feaad589cadef4d77ece200ae3c9133d58464d0/verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e", size = 27123, upload-time = "2020-11-30T02:24:09.646Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31", size = 19640, upload-time = "2020-11-30T02:24:08.387Z" },
+]
+
[[package]]
name = "watchdog"
version = "6.0.0"