diff --git a/docs/changelog.d/+caddy-docs.doc.md b/docs/changelog.d/+caddy-docs.doc.md new file mode 100644 index 0000000..df088dd --- /dev/null +++ b/docs/changelog.d/+caddy-docs.doc.md @@ -0,0 +1 @@ +Add Caddy reference card and fix replication tutorial sequence diff --git a/docs/changelog.d/random-doc-review.feature.md b/docs/changelog.d/random-doc-review.feature.md new file mode 100644 index 0000000..641a9cc --- /dev/null +++ b/docs/changelog.d/random-doc-review.feature.md @@ -0,0 +1 @@ +Add doc-random mise task for random documentation review diff --git a/docs/how-to/index.md b/docs/how-to/index.md index 4f5ab11..f5f253e 100644 --- a/docs/how-to/index.md +++ b/docs/how-to/index.md @@ -28,6 +28,12 @@ Task-oriented instructions for common BlumeOps operations. These guides assume y |-------|-------------| | [[update-documentation]] | Publish docs via build-blumeops workflow | +## Knowledge Base + +| Guide | Description | +|-------|-------------| +| [[review-documentation]] | Periodically review and maintain documentation | + ## Operations | Guide | Description | diff --git a/docs/how-to/knowledgebase/review-documentation.md b/docs/how-to/knowledgebase/review-documentation.md new file mode 100644 index 0000000..e286e1b --- /dev/null +++ b/docs/how-to/knowledgebase/review-documentation.md @@ -0,0 +1,97 @@ +--- +title: review-documentation +tags: + - how-to + - documentation + - maintenance +--- + +# Review Documentation + +How to periodically review and maintain the BlumeOps knowledge base. + +## Quick Random Review + +Select a random documentation card for review: + +```bash +mise run doc-random +``` + +This displays a random card with a review checklist to guide your assessment. + +## Review Checklist + +When reviewing a documentation card, consider: + +| Check | Description | +|-------|-------------| +| **Accuracy** | Is the information current and correct? | +| **Links** | Are wiki-links working? Should more be added? | +| **Scope** | Is the card appropriately sized (not too large/small)? | +| **Category** | Is it in the right section (reference/how-to/tutorial/explanation)? | +| **Frontmatter** | Are title and tags appropriate? | +| **Related** | Should it link to related cards? | + +## Verify Deployed State + +For service reference cards, verify the documentation matches reality: + +### ArgoCD Apps (Kubernetes services) + +Check if the app is synced and healthy: + +```bash +argocd app get +argocd app diff # Show pending changes +``` + +If out of sync, either the docs are stale or a deployment is pending. + +### Ansible Roles (indri services) + +Check if the role applies idempotently (no changes needed): + +```bash +mise run provision-indri -- --tags --check --diff +``` + +If changes would be made, either the docs are stale or the host has drifted. + +### Pulumi (Tailscale ACLs, DNS) + +Check for drift: + +```bash +# Tailscale ACLs +cd pulumi/tailscale && pulumi preview + +# DNS (Gandi) +cd pulumi/gandi && pulumi preview +``` + +If changes are pending, investigate whether docs or infrastructure is stale. + +## When to Review + +Consider running `mise run doc-random` during: + +- Start of work sessions (quick maintenance) +- After major infrastructure changes (verify docs reflect reality) +- When learning the system (random exploration) + +## Making Changes + +If a card needs updates: + +1. Create a feature branch +2. Make the edits +3. Run `mise run doc-links` to verify links +4. Create a PR for review + +See [[update-documentation]] for publishing changes. + +## Related + +- [[update-documentation]] - Publishing documentation changes +- [[exploring-the-docs]] - Navigating the documentation diff --git a/docs/reference/index.md b/docs/reference/index.md index 8c0ed62..11fd515 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -17,6 +17,7 @@ Individual service reference cards with URLs and configuration details. | [[alloy | Alloy]] | Observability collector (metrics & logs) | indri + k8s | | [[argocd]] | GitOps continuous delivery | k8s | | [[borgmatic]] | Backup system | indri | +| [[caddy]] | Reverse proxy & TLS termination | indri | | [[1password]] | Secrets management | cloud + k8s | | [[forgejo]] | Git forge & CI/CD | indri | | [[grafana]] | Dashboards & visualization | k8s | diff --git a/docs/reference/services/caddy.md b/docs/reference/services/caddy.md new file mode 100644 index 0000000..7430ddf --- /dev/null +++ b/docs/reference/services/caddy.md @@ -0,0 +1,98 @@ +--- +title: caddy +tags: + - service + - networking + - tls +--- + +# Caddy + +Reverse proxy for `*.ops.eblu.me` services with automatic TLS via ACME DNS-01. + +## Quick Reference + +| Property | Value | +|----------|-------| +| **Domain** | `*.ops.eblu.me` | +| **HTTPS Port** | 443 | +| **Config** | `ansible/roles/caddy/templates/Caddyfile.j2` | +| **Binary** | Custom build with Gandi DNS plugin | + +## Why Caddy? + +Caddy provides a single TLS termination point for all BlumeOps services: + +- **Wildcard certificate** for `*.ops.eblu.me` via Let's Encrypt +- **DNS-01 challenge** using Gandi API (no port 80 needed) +- **Unified access** from k8s pods, containers, and tailnet clients + +See [[routing]] for when to use `*.ops.eblu.me` vs `*.tail8d86e.ts.net`. + +## Proxied Services + +### Indri-Local Services + +| Subdomain | Backend | Service | +|-----------|---------|---------| +| `forge.ops.eblu.me` | `localhost:3001` | [[forgejo]] | +| `registry.ops.eblu.me` | `localhost:5050` | [[zot]] | +| `jellyfin.ops.eblu.me` | `localhost:8096` | [[jellyfin]] | + +### Kubernetes Services + +K8s services are proxied via their Tailscale Ingress endpoints: + +| Subdomain | Backend | Service | +|-----------|---------|---------| +| `grafana.ops.eblu.me` | `grafana.tail8d86e.ts.net` | [[grafana]] | +| `argocd.ops.eblu.me` | `argocd.tail8d86e.ts.net` | [[argocd]] | +| `docs.ops.eblu.me` | `docs.tail8d86e.ts.net` | [[docs]] | +| `feed.ops.eblu.me` | `feed.tail8d86e.ts.net` | [[miniflux]] | +| ... | ... | (see defaults/main.yml for full list) | + +### TCP Services (Layer 4) + +| Port | Backend | Service | +|------|---------|---------| +| 2222 | `localhost:2200` | Forgejo SSH | +| 5432 | `pg.tail8d86e.ts.net:5432` | [[postgresql]] | + +## Configuration + +Caddy is managed via the `caddy` Ansible role: + +```bash +# Deploy caddy changes +mise run provision-indri -- --tags caddy +``` + +**Key files:** +- `ansible/roles/caddy/defaults/main.yml` - Service definitions +- `ansible/roles/caddy/templates/Caddyfile.j2` - Caddy config template + +## Secrets + +| Secret | Source | Description | +|--------|--------|-------------| +| `GANDI_BEARER_TOKEN` | 1Password | API token for DNS-01 challenges | + +The token is written to `~/.config/caddy/gandi-token` (chmod 0600) and sourced by the Caddy wrapper script. + +## Custom Build + +Caddy is built from source with the Gandi DNS plugin: + +```bash +# Build location +~/code/3rd/caddy/bin/caddy +``` + +The build includes the `github.com/caddy-dns/gandi` plugin for ACME DNS-01 challenges. + +## Related + +- [[routing]] - Service routing architecture +- [[forgejo]] - Git forge (proxied by Caddy) +- [[zot]] - Container registry (proxied by Caddy) +- [[tailscale-operator]] - K8s services use Tailscale Ingress, then Caddy diff --git a/docs/tutorials/ai-assistance-guide.md b/docs/tutorials/ai-assistance-guide.md index fd67968..b6dbcc3 100644 --- a/docs/tutorials/ai-assistance-guide.md +++ b/docs/tutorials/ai-assistance-guide.md @@ -88,6 +88,7 @@ BlumeOps operations are driven by mise tasks. Run `mise tasks` to list all avail | `doc-links` | Validate wiki-links in documentation | | `doc-titles` | Check for duplicate doc titles | | `doc-filenames` | Check for duplicate doc filenames | +| `doc-random` | Select a random doc card for review | | `indri-runner-logs` | View Forgejo workflow logs from local runner | For ArgoCD operations, use the `argocd` CLI directly: diff --git a/docs/tutorials/replication/core-services.md b/docs/tutorials/replication/core-services.md index 3dc5847..100598e 100644 --- a/docs/tutorials/replication/core-services.md +++ b/docs/tutorials/replication/core-services.md @@ -9,6 +9,8 @@ tags: # Core Services Setup > **Audiences:** Replicator +> +> **Prerequisites:** [[tutorials/replication/tailscale-setup | Tailscale Setup]] This tutorial walks through setting up the foundational services that your GitOps infrastructure depends on: a git forge and optionally a container registry. diff --git a/docs/tutorials/replication/tailscale-setup.md b/docs/tutorials/replication/tailscale-setup.md index 92d0a40..23b9ea2 100644 --- a/docs/tutorials/replication/tailscale-setup.md +++ b/docs/tutorials/replication/tailscale-setup.md @@ -112,12 +112,12 @@ Tags must be defined in ACLs before use. ## Next Steps With networking established: +- [[tutorials/replication/core-services | Set Up Core Services]] - Install Forgejo and optionally a container registry - [[tutorials/replication/kubernetes-bootstrap | Bootstrap Kubernetes]] - Your cluster will join the tailnet -- Set up your server and storage devices ## BlumeOps Specifics -BluemeOps' Tailscale configuration includes: +BlumeOps' Tailscale configuration includes: - Multiple device tags (`homelab`, `nas`, `registry`, `k8s-api`) - Group-based access for family members - SSH access rules with authentication requirements diff --git a/mise-tasks/doc-links b/mise-tasks/doc-links index 7c463f0..625c9ff 100755 --- a/mise-tasks/doc-links +++ b/mise-tasks/doc-links @@ -7,7 +7,7 @@ """Validate that all wiki-links in documentation point to existing files. This script scans all markdown files in the docs/ directory (excluding -changelog.d/ and zk/), extracts wiki-links, and verifies each link target +changelog.d/), extracts wiki-links, and verifies each link target exists as a filename or path in the documentation. Wiki-link formats supported: @@ -61,9 +61,9 @@ def main() -> int: # Track which filenames are ambiguous (appear multiple times) filename_counts: dict[str, list[str]] = {} - # Scan all markdown files (excluding zk/ and changelog.d/) + # Scan all markdown files (excluding changelog.d/) for md_file in DOCS_DIR.rglob("*.md"): - if "changelog.d" in md_file.parts or "zk" in md_file.parts: + if "changelog.d" in md_file.parts: continue # Track filename occurrences filename = md_file.stem @@ -86,9 +86,9 @@ def main() -> int: broken_links: list[tuple[str, int, str]] = [] ambiguous_links: list[tuple[str, int, str, list[str]]] = [] - # Scan all markdown files for wiki-links (excluding zk/ and changelog.d/) + # Scan all markdown files for wiki-links (excluding changelog.d/) for md_file in sorted(DOCS_DIR.rglob("*.md")): - if "changelog.d" in md_file.parts or "zk" in md_file.parts: + if "changelog.d" in md_file.parts: continue rel_path = str(md_file.relative_to(DOCS_DIR)) diff --git a/mise-tasks/doc-random b/mise-tasks/doc-random new file mode 100755 index 0000000..05cad28 --- /dev/null +++ b/mise-tasks/doc-random @@ -0,0 +1,88 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.12" +# dependencies = ["rich>=13.0.0"] +# /// +#MISE description="Select a random documentation card for review" +"""Select a random documentation card for review. + +This script scans all markdown files in the docs/ directory (excluding +changelog.d/), selects one at random, and displays it for review. + +Useful for periodic knowledge base maintenance and verification. + +Usage: mise run doc-random +""" + +import random +import sys +from pathlib import Path + +from rich.console import Console +from rich.markdown import Markdown +from rich.panel import Panel + +DOCS_DIR = Path(__file__).parent.parent / "docs" + + +def get_all_docs() -> list[Path]: + """Get all documentation markdown files, excluding changelog.d.""" + docs = [] + for md_file in DOCS_DIR.rglob("*.md"): + # Skip changelog fragments + if "changelog.d" in md_file.parts: + continue + docs.append(md_file) + return docs + + +def main() -> int: + console = Console() + + docs = get_all_docs() + if not docs: + console.print("[bold red]No documentation files found![/bold red]") + return 1 + + # Select a random document + selected = random.choice(docs) + rel_path = selected.relative_to(DOCS_DIR) + + # Display header + console.print() + console.print(Panel( + f"[bold cyan]{rel_path}[/bold cyan]\n" + f"[dim]{len(docs)} total docs in knowledge base[/dim]", + title="[bold]Random Documentation Card[/bold]", + border_style="cyan", + )) + console.print() + + # Display the file content + content = selected.read_text() + console.print(Markdown(content)) + + # Review checklist + console.print() + console.print(Panel( + "[bold]Review Checklist:[/bold]\n\n" + "• Is the information accurate and up-to-date?\n" + "• Are there broken or missing wiki-links?\n" + "• Should this card link to other related cards?\n" + "• Is the card too large and should be split?\n" + "• Is the card too small and should be merged?\n" + "• Does the frontmatter (tags, title) make sense?\n" + "• Is the card in the correct category (reference/how-to/etc)?\n\n" + "[bold]Verify Deployed State:[/bold]\n\n" + "• If ArgoCD app: is it synced? (argocd app get )\n" + "• If Ansible role: does it apply idempotently? (--check --diff)\n" + "• If Pulumi: is there drift? (pulumi preview)", + title="[bold yellow]Review Guidance[/bold yellow]", + border_style="yellow", + )) + + return 0 + + +if __name__ == "__main__": + sys.exit(main())