Documentation Site Implementation Plan¶
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Deploy a searchable MkDocs Material documentation site on Cloudflare Pages with Access protection.
Architecture: MkDocs builds markdown from docs/ into a static site. GitHub Actions deploys to Cloudflare Pages on push. Cloudflare Access protects with Authentik/GitHub/OTP authentication, restricted to approved email domains.
Tech Stack: MkDocs Material, Cloudflare Pages, Cloudflare Access, Cloudflare Tunnel, Terraform, GitHub Actions
Design Document: docs/plans/2025-12-29-docs-site-design.md
Phase 1: Foundation¶
Task 1: Create MkDocs Configuration¶
Files:
- Create: mkdocs.yml
Step 1: Create mkdocs.yml
site_name: fzymgc-house Cluster Docs
site_url: https://docs.fzymgc.house
repo_url: https://github.com/fzymgc-house/selfhosted-cluster
repo_name: fzymgc-house/selfhosted-cluster
theme:
name: material
features:
- navigation.instant
- navigation.tracking
- navigation.sections
- navigation.expand
- search.suggest
- search.highlight
- content.code.copy
palette:
- scheme: slate
primary: deep purple
accent: amber
toggle:
icon: material/brightness-4
name: Switch to light mode
- scheme: default
primary: deep purple
accent: amber
toggle:
icon: material/brightness-7
name: Switch to dark mode
plugins:
- search
- tags
markdown_extensions:
- admonition
- pymdownx.details
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- tables
- toc:
permalink: true
nav:
- Home: index.md
- Getting Started:
- getting-started/index.md
- Environment Setup: getting-started/environment.md
- Cluster Access: getting-started/cluster-access.md
- AI Tooling: getting-started/ai-tooling.md
- Operations:
- operations/index.md
- Vault: operations/vault.md
- Authentik: operations/authentik.md
- Cloudflare: operations/cloudflare.md
- HCP Terraform: operations/hcp-terraform.md
- Windmill: operations/windmill.md
- GitHub Tokens: operations/github-tokens.md
- Reference:
- reference/index.md
- Services Catalog: reference/services.md
- Technologies: reference/technologies.md
- Network: reference/network.md
- Secrets & Vault Paths: reference/secrets.md
- Architecture:
- architecture/index.md
- System Overview: architecture/overview.md
- Decisions: architecture/decisions/
- Design Plans:
- plans/index.md
- Archive: plans/archive/
Step 2: Verify YAML is valid
Run: python -c "import yaml; yaml.safe_load(open('mkdocs.yml'))"
Expected: No output (success)
Step 3: Commit
git add mkdocs.yml
git commit -m "feat(docs): add MkDocs Material configuration"
Task 2: Create Directory Structure¶
Files:
- Create: docs/index.md
- Create: docs/getting-started/index.md
- Create: docs/operations/index.md
- Create: docs/reference/index.md
- Create: docs/architecture/index.md
- Create: docs/architecture/decisions/.gitkeep
- Modify: docs/plans/index.md (create if missing)
Step 1: Create docs/index.md
# fzymgc-house Cluster Documentation
Welcome to the documentation for the fzymgc-house self-hosted Kubernetes cluster.
## Quick Links
| Section | Description |
|---------|-------------|
| [Getting Started](getting-started/index.md) | Environment setup, cluster access, AI tooling |
| [Operations](operations/index.md) | How-to guides for Vault, Authentik, Cloudflare, etc. |
| [Reference](reference/index.md) | Services catalog, technologies, network info |
| [Architecture](architecture/index.md) | System design and decision records |
| [Design Plans](plans/index.md) | Current and archived design documents |
## About This Cluster
Self-hosted Kubernetes cluster on TuringPi 2 hardware (RK1 compute modules). Three-layer architecture:
| Layer | Location | Purpose |
|-------|----------|---------|
| Ansible | `ansible/` | Cluster deployment, node configuration, k3s installation |
| Terraform | `tf/` | Infrastructure configuration (Vault, Authentik, Grafana) |
| Kubernetes | `argocd/` | Application manifests, managed by ArgoCD |
**Stack:** k3s + Calico CNI, Vault (secrets), Authentik (SSO), Grafana + VictoriaMetrics (observability)
Step 2: Create docs/getting-started/index.md
# Getting Started
New to this cluster? Start here.
## Sections
| Guide | Description |
|-------|-------------|
| [Environment Setup](environment.md) | Python venv, Vault login, tool prerequisites |
| [Cluster Access](cluster-access.md) | OIDC authentication, kubeconfig setup |
| [AI Tooling](ai-tooling.md) | MCP servers for Claude Code integration |
## Prerequisites
- Access to Vault at `https://vault.fzymgc.house`
- Kubernetes context configured
- Python 3.11+ for Ansible work
Step 3: Create docs/operations/index.md
# Operations
How-to guides and procedures for cluster operations.
## Guides
| Service | Description |
|---------|-------------|
| [Vault](vault.md) | Secrets management, policies, Kubernetes auth |
| [Authentik](authentik.md) | SSO configuration, OIDC troubleshooting |
| [Cloudflare](cloudflare.md) | DNS, Tunnels, Workers |
| [HCP Terraform](hcp-terraform.md) | Remote execution, agents, GitOps |
| [Windmill](windmill.md) | Workflow automation |
| [GitHub Tokens](github-tokens.md) | PAT setup for Actions Runner Controller |
Step 4: Create docs/reference/index.md
# Reference
Current state documentation and catalogs.
## Catalogs
| Reference | Description |
|-----------|-------------|
| [Services Catalog](services.md) | All services, hostnames, auth methods |
| [Technologies](technologies.md) | Tech stack with documentation links |
| [Network](network.md) | IPs, CIDRs, DNS, VIPs |
| [Secrets & Vault Paths](secrets.md) | Secret locations and policies |
Step 5: Create docs/architecture/index.md
# Architecture
System design and decision records.
## Overview
- [System Overview](overview.md) — High-level architecture
## Decisions
Architecture Decision Records (ADRs) are stored in the [decisions/](decisions/) directory.
Step 6: Create docs/architecture/decisions/.gitkeep
mkdir -p docs/architecture/decisions
touch docs/architecture/decisions/.gitkeep
Step 7: Create docs/plans/index.md
# Design Plans
Design documents for features and changes.
## Active Plans
| Plan | Date | Status |
|------|------|--------|
| [Documentation Site](2025-12-29-docs-site-design.md) | 2025-12-29 | In Progress |
## Archive
Completed plans are moved to [archive/](archive/).
Step 8: Verify structure
Run: find docs -name "*.md" -o -name ".gitkeep" | sort
Expected: All new files listed
Step 9: Commit
git add docs/
git commit -m "feat(docs): create MkDocs directory structure"
Task 3: Install MkDocs and Test Locally¶
Step 1: Install mkdocs-material
Run: pip install mkdocs-material
Expected: Successfully installed mkdocs-material and dependencies
Step 2: Build docs (will have warnings for missing files)
Run: mkdocs build
Expected: Builds with warnings about missing linked files (this is expected)
Step 3: Serve locally
Run: mkdocs serve
Expected: Serving on http://127.0.0.1:8000/
Step 4: Verify in browser
Open: http://127.0.0.1:8000/ Expected: See homepage with navigation, search box, dark mode toggle
Step 5: Stop server (Ctrl+C)
Phase 2: Content Migration¶
Task 4: Move Existing Operations Docs¶
Files:
- Move: docs/vault.md → docs/operations/vault.md
- Move: docs/authentik.md → docs/operations/authentik.md
- Move: docs/cloudflare.md → docs/operations/cloudflare.md
- Move: docs/hcp-terraform.md → docs/operations/hcp-terraform.md
- Move: docs/windmill.md → docs/operations/windmill.md
- Move: docs/github-token-setup.md → docs/operations/github-tokens.md
Step 1: Move files
mv docs/vault.md docs/operations/vault.md
mv docs/authentik.md docs/operations/authentik.md
mv docs/cloudflare.md docs/operations/cloudflare.md
mv docs/hcp-terraform.md docs/operations/hcp-terraform.md
mv docs/windmill.md docs/operations/windmill.md
mv docs/github-token-setup.md docs/operations/github-tokens.md
Step 2: Verify moves
Run: ls docs/operations/
Expected: All 6 .md files plus index.md
Step 3: Test build
Run: mkdocs build --strict
Expected: Build succeeds (may have some warnings)
Step 4: Commit
git add docs/
git commit -m "refactor(docs): move operations docs to new structure"
Task 5: Move Getting Started Docs¶
Files:
- Move: docs/kubernetes-access.md → docs/getting-started/cluster-access.md
- Move: docs/mcp-servers.md → docs/getting-started/ai-tooling.md
- Create: docs/getting-started/environment.md
Step 1: Move files
mv docs/kubernetes-access.md docs/getting-started/cluster-access.md
mv docs/mcp-servers.md docs/getting-started/ai-tooling.md
Step 2: Create environment.md
# Environment Setup
Prerequisites and setup for working with this cluster.
## Python Environment
```bash
# One-time setup
./setup-venv.sh
# Before Ansible work
source .venv/bin/activate
Vault Authentication¶
export VAULT_ADDR=https://vault.fzymgc.house
vault login
# Verify connectivity
./scripts/vault-helper.sh status
Required Tools¶
| Tool | Purpose | Install |
|---|---|---|
kubectl |
Kubernetes CLI | kubernetes.io |
vault |
Vault CLI | vaultproject.io |
terraform |
IaC | terraform.io |
ansible |
Configuration management | pip install ansible |
gh |
GitHub CLI | cli.github.com |
|
Task 6: Create Reference Docs¶
Files:
- Create: docs/reference/services.md
- Create: docs/reference/technologies.md
- Create: docs/reference/network.md
- Create: docs/reference/secrets.md
Step 1: Create services.md (from Notion Services Catalog)
# Services Catalog
All services deployed in the cluster.
## Platform Services
| Service | Hostname | Namespace | Ingress Type | Auth Method |
|---------|----------|-----------|--------------|-------------|
| Vault | vault.fzymgc.house | vault | Traefik IngressRoute | Certificate |
| Authentik | auth.fzymgc.house | authentik | Traefik IngressRoute | Native |
| Grafana | grafana.fzymgc.house | monitoring | Traefik IngressRoute | OIDC |
| ArgoCD | argocd.fzymgc.house | argocd | Traefik IngressRoute | OIDC |
## Infrastructure Services
| Service | Hostname | Namespace | Ingress Type | Auth Method |
|---------|----------|-----------|--------------|-------------|
| Traefik | traefik.fzymgc.house | traefik | kube-vip VIP | Forward-Auth |
| Longhorn | longhorn.fzymgc.house | longhorn-system | Traefik IngressRoute | Forward-Auth |
## Applications
| Service | Hostname | Namespace | Ingress Type | Auth Method |
|---------|----------|-----------|--------------|-------------|
| Windmill | windmill.fzymgc.house | windmill | Traefik IngressRoute | OIDC |
---
*Update this table when adding or modifying services.*
Step 2: Create technologies.md (from Notion Tech References)
# Technologies
Technology stack with documentation links.
## Kubernetes
| Technology | Category | Docs | Version |
|------------|----------|------|---------|
| k3s | Kubernetes | [k3s.io](https://k3s.io/) | 1.31.x |
| Calico | Networking | [projectcalico.org](https://docs.tigera.io/calico/) | 3.29.x |
| MetalLB | Load Balancer | [metallb.io](https://metallb.io/) | 0.14.x |
| Longhorn | Storage | [longhorn.io](https://longhorn.io/docs/) | 1.7.x |
| Traefik | Ingress | [traefik.io](https://doc.traefik.io/traefik/) | 3.x |
## Security
| Technology | Category | Docs | Version |
|------------|----------|------|---------|
| Vault | Secrets | [vaultproject.io](https://developer.hashicorp.com/vault/docs) | 1.18.x |
| Authentik | SSO | [goauthentik.io](https://docs.goauthentik.io/) | 2024.x |
| cert-manager | Certificates | [cert-manager.io](https://cert-manager.io/docs/) | 1.16.x |
| External Secrets | Secrets Sync | [external-secrets.io](https://external-secrets.io/) | 0.12.x |
## Observability
| Technology | Category | Docs | Version |
|------------|----------|------|---------|
| Grafana | Dashboards | [grafana.com](https://grafana.com/docs/) | 11.x |
| VictoriaMetrics | Metrics | [victoriametrics.com](https://docs.victoriametrics.com/) | 1.x |
| Loki | Logs | [grafana.com/loki](https://grafana.com/docs/loki/) | 3.x |
## GitOps & Automation
| Technology | Category | Docs | Version |
|------------|----------|------|---------|
| ArgoCD | GitOps | [argo-cd.readthedocs.io](https://argo-cd.readthedocs.io/) | 2.13.x |
| Terraform | IaC | [terraform.io](https://developer.hashicorp.com/terraform/docs) | 1.12.x |
| HCP Terraform | Remote Execution | [cloud.hashicorp.com](https://developer.hashicorp.com/terraform/cloud-docs) | - |
| Windmill | Workflows | [windmill.dev](https://www.windmill.dev/docs/) | - |
---
*Update versions when upgrading components.*
Step 3: Create network.md
# Network Reference
IP addresses, CIDRs, and DNS configuration.
## Kubernetes Networking
| Resource | Value |
|----------|-------|
| K8s API VIP | 192.168.20.140 (kube-vip) |
| Cluster CIDR (pods) | 10.42.0.0/16 |
| Service CIDR | 10.43.0.0/16 |
| Cluster DNS | 10.43.0.10 |
## MetalLB Address Pools
| Pool | Range | Purpose |
|------|-------|---------|
| default | 192.168.20.145-149 | General services |
| reserved | 192.168.20.155-159 | Critical infrastructure |
## Node IPs
| Node | IP | Role |
|------|-----|------|
| tpi-alpha-1 | 192.168.20.101 | Control plane |
| tpi-alpha-2 | 192.168.20.102 | Control plane |
| tpi-alpha-3 | 192.168.20.103 | Control plane |
| tpi-alpha-4 | 192.168.20.104 | Worker |
| tpi-beta-1 | 192.168.20.111 | Worker |
| tpi-beta-2 | 192.168.20.112 | Worker |
| tpi-beta-3 | 192.168.20.113 | Worker |
| tpi-beta-4 | 192.168.20.114 | Worker |
## DNS
| Domain | Purpose |
|--------|---------|
| fzymgc.house | Internal services |
| fzymgc.net | External/webhook services |
Step 4: Create secrets.md
# Secrets & Vault Paths
Vault secret locations and policies.
## Secret Structure
## Key Vault Paths
| Path | Purpose | Consumers |
|------|---------|-----------|
| `secret/fzymgc-house/cluster/authentik` | Authentik API token | Terraform |
| `secret/fzymgc-house/cluster/cloudflared/tunnels/*` | Tunnel credentials | cloudflared |
| `secret/fzymgc-house/cluster/grafana` | Grafana secrets | Terraform |
## Kubernetes Auth Roles
| Role | Namespace | Service Account | Policies |
|------|-----------|-----------------|----------|
| external-secrets | external-secrets | external-secrets | external-secrets |
| cert-manager | cert-manager | cert-manager | cert-manager |
---
*See `tf/vault/` for policy definitions.*
Step 5: Verify structure
Run: ls docs/reference/
Expected: index.md, services.md, technologies.md, network.md, secrets.md
Step 6: Commit
git add docs/reference/
git commit -m "feat(docs): add reference documentation"
Task 7: Create Architecture Overview¶
Files:
- Create: docs/architecture/overview.md
Step 1: Create overview.md
# System Overview
High-level architecture of the fzymgc-house cluster.
## Hardware
Two TuringPi 2 boards (alpha/beta) with 8 RK1 compute modules:
## Three-Layer Architecture
| Layer | Tool | Purpose |
|-------|------|---------|
| Infrastructure | Ansible | OS config, k3s install, node setup |
| Platform | Terraform | Vault policies, Authentik config, integrations |
| Applications | ArgoCD | Kubernetes manifests, GitOps deployments |
## Data Flow
## GitOps Workflow
1. Changes committed to `main` branch
2. ArgoCD detects changes
3. Manifests synced to cluster
4. Applications updated automatically
Step 2: Commit
git add docs/architecture/
git commit -m "feat(docs): add architecture overview"
Task 8: Clean Up Old Docs Root¶
Files:
- Delete: docs/README.md (replaced by index.md)
Step 1: Remove old README
rm docs/README.md
Step 2: Test build
Run: mkdocs build --strict
Expected: Build succeeds
Step 3: Commit
git add docs/README.md
git commit -m "chore(docs): remove old README (replaced by index.md)"
Phase 3: CI/CD¶
Task 9: Create GitHub Actions Workflow¶
Files:
- Create: .github/workflows/docs.yml
Step 1: Create workflow file
name: Deploy Docs
on:
push:
branches: [main]
paths:
- 'docs/**'
- 'mkdocs.yml'
- '.github/workflows/docs.yml'
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
steps:
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Install dependencies
run: pip install mkdocs-material
- name: Build docs
run: mkdocs build --strict
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy site --project-name=cluster-docs
Step 2: Verify YAML
Run: python -c "import yaml; yaml.safe_load(open('.github/workflows/docs.yml'))"
Expected: No output (success)
Step 3: Commit
git add .github/workflows/docs.yml
git commit -m "feat(ci): add docs deployment workflow"
Task 10: Create Cloudflare Pages Project¶
Manual Steps (Cloudflare Dashboard or CLI):
- Go to Cloudflare Dashboard → Pages
- Create project named
cluster-docs - Set production branch to
main - No build settings needed (we build in GitHub Actions)
Or via Wrangler CLI:
wrangler pages project create cluster-docs --production-branch main
Step 1: Add GitHub secrets
Add to repository secrets:
- CLOUDFLARE_API_TOKEN — API token with Pages edit permissions
- CLOUDFLARE_ACCOUNT_ID — Your Cloudflare account ID
Step 2: Verify project exists
Run: wrangler pages project list | grep cluster-docs
Expected: Project listed
Phase 4: Authentik Exposure¶
Task 11: Update Tunnel Configuration¶
Files:
- Modify: tf/cloudflare/variables.tf
- Modify: tf/cloudflare/tunnel.tf
Step 1: Add authentik_services variable to variables.tf
Add after webhook_services variable:
variable "authentik_services" {
description = "Authentik endpoints to expose via tunnel"
type = map(object({
service_url = string
}))
default = {
auth = {
service_url = "https://authentik-server.authentik.svc.cluster.local"
}
}
}
Step 2: Update tunnel.tf ingress
Modify the cloudflare_zero_trust_tunnel_cloudflared_config resource, update the ingress concat:
config = {
ingress = concat(
# Webhook services (existing)
[for svc, definition in var.webhook_services : {
hostname = "${svc}${var.webhook_suffix}.${var.webhook_domain}"
service = definition.service_url
origin_request = {
http_host_header = "${svc}${var.webhook_suffix}.${var.webhook_domain}"
origin_server_name = split("//", definition.service_url)[1]
no_tls_verify = false
}
}],
# Authentik services
[for svc, definition in var.authentik_services : {
hostname = "${svc}.fzymgc.house"
service = definition.service_url
origin_request = {
http_host_header = "${svc}.fzymgc.house"
origin_server_name = "authentik-server.authentik.svc.cluster.local"
no_tls_verify = true
}
}],
# Catch-all
[{ service = "http_status:404" }]
)
}
Step 3: Add Authentik DNS record
Add to tunnel.tf:
resource "cloudflare_dns_record" "authentik" {
for_each = var.authentik_services
zone_id = data.cloudflare_zone.fzymgc_house.id
name = each.key
content = "${cloudflare_zero_trust_tunnel_cloudflared.main.id}.cfargotunnel.com"
type = "CNAME"
proxied = true
comment = "Authentik via ${var.tunnel_name} tunnel"
ttl = 1
}
Step 4: Verify Terraform
Run: terraform -chdir=tf/cloudflare validate
Expected: Success
Step 5: Commit
git add tf/cloudflare/
git commit -m "feat(cloudflare): expose Authentik via tunnel"
Task 12: Create Authentik OIDC Application¶
Files:
- Modify: tf/authentik/ — Add Cloudflare Access OIDC provider
Step 1: Add OIDC provider and application
Create or modify appropriate file in tf/authentik/:
# Cloudflare Access OIDC Provider
resource "authentik_provider_oauth2" "cloudflare_access" {
name = "Cloudflare Access"
client_id = random_string.cloudflare_access_client_id.result
authorization_flow = data.authentik_flow.default_authorization.id
invalidation_flow = data.authentik_flow.default_invalidation.id
client_type = "confidential"
signing_key = data.authentik_certificate_key_pair.default.id
access_token_validity = "minutes=5"
property_mappings = [
data.authentik_property_mapping_provider_scope.openid.id,
data.authentik_property_mapping_provider_scope.email.id,
data.authentik_property_mapping_provider_scope.profile.id,
]
redirect_uris = [
"https://*.cloudflareaccess.com/cdn-cgi/access/callback",
]
}
resource "random_string" "cloudflare_access_client_id" {
length = 32
special = false
}
resource "random_password" "cloudflare_access_client_secret" {
length = 64
special = false
}
# Store credentials in Vault
resource "vault_kv_secret_v2" "cloudflare_access_oidc" {
mount = "secret"
name = "fzymgc-house/cluster/cloudflare-access/authentik"
data_json = jsonencode({
client_id = random_string.cloudflare_access_client_id.result
client_secret = random_password.cloudflare_access_client_secret.result
})
}
# Application
resource "authentik_application" "cloudflare_access" {
name = "Cloudflare Access"
slug = "cloudflare-access"
protocol_provider = authentik_provider_oauth2.cloudflare_access.id
meta_launch_url = "https://docs.fzymgc.house/"
}
Step 2: Commit
git add tf/authentik/
git commit -m "feat(authentik): add Cloudflare Access OIDC provider"
Phase 5: Access Protection¶
Task 13: Create Access Configuration¶
Files:
- Create: tf/cloudflare/access.tf
- Modify: tf/cloudflare/variables.tf
Step 1: Add variables for IDPs
Add to tf/cloudflare/variables.tf:
variable "authentik_oidc_client_id" {
description = "Authentik OIDC client ID for Cloudflare Access"
type = string
sensitive = true
}
variable "authentik_oidc_client_secret" {
description = "Authentik OIDC client secret for Cloudflare Access"
type = string
sensitive = true
}
variable "github_oauth_client_id" {
description = "GitHub OAuth App client ID"
type = string
sensitive = true
}
variable "github_oauth_client_secret" {
description = "GitHub OAuth App client secret"
type = string
sensitive = true
}
Step 2: Create access.tf
# ==============================================================================
# Identity Providers
# ==============================================================================
resource "cloudflare_zero_trust_access_identity_provider" "authentik" {
account_id = var.cloudflare_account_id
name = "Authentik"
type = "oidc"
config {
client_id = var.authentik_oidc_client_id
client_secret = var.authentik_oidc_client_secret
auth_url = "https://auth.fzymgc.house/application/o/authorize/"
token_url = "https://auth.fzymgc.house/application/o/token/"
certs_url = "https://auth.fzymgc.house/application/o/cloudflare-access/jwks/"
scopes = ["openid", "email", "profile"]
}
}
resource "cloudflare_zero_trust_access_identity_provider" "github" {
account_id = var.cloudflare_account_id
name = "GitHub"
type = "github"
config {
client_id = var.github_oauth_client_id
client_secret = var.github_oauth_client_secret
}
}
resource "cloudflare_zero_trust_access_identity_provider" "otp" {
account_id = var.cloudflare_account_id
name = "Email One-Time PIN"
type = "otp"
}
# ==============================================================================
# Cluster Docs Application
# ==============================================================================
resource "cloudflare_zero_trust_access_application" "cluster_docs" {
account_id = var.cloudflare_account_id
name = "Cluster Documentation"
domain = "docs.fzymgc.house"
type = "self_hosted"
session_duration = "24h"
allowed_idps = [
cloudflare_zero_trust_access_identity_provider.authentik.id,
cloudflare_zero_trust_access_identity_provider.github.id,
cloudflare_zero_trust_access_identity_provider.otp.id,
]
}
resource "cloudflare_zero_trust_access_policy" "cluster_docs_allow" {
account_id = var.cloudflare_account_id
application_id = cloudflare_zero_trust_access_application.cluster_docs.id
name = "Allow authorized users"
precedence = 1
decision = "allow"
include {
login_method = [
cloudflare_zero_trust_access_identity_provider.authentik.id,
cloudflare_zero_trust_access_identity_provider.github.id,
cloudflare_zero_trust_access_identity_provider.otp.id,
]
}
require {
email_domain = [
"fzymgc.email",
"fzymgc.house",
"fzymgc.dev",
"fuzzymagic.com",
]
}
}
Step 3: Validate
Run: terraform -chdir=tf/cloudflare validate
Expected: Success
Step 4: Commit
git add tf/cloudflare/
git commit -m "feat(cloudflare): add Access protection for docs site"
Task 14: Create GitHub OAuth App¶
Manual Steps:
- Go to GitHub → Settings → Developer settings → OAuth Apps
- Create new OAuth App:
- Name:
fzymgc-house Cloudflare Access - Homepage URL:
https://docs.fzymgc.house - Callback URL:
https://fzymgc-house.cloudflareaccess.com/cdn-cgi/access/callback - Note client ID and generate client secret
Step 1: Store in Vault
vault kv put secret/fzymgc-house/cluster/cloudflare-access/github \
client_id="<github_client_id>" \
client_secret="<github_client_secret>"
Phase 6: Cleanup¶
Task 15: Slim Down CLAUDE.md Files¶
Files:
- Modify: CLAUDE.md (root)
Step 1: Update root CLAUDE.md
Remove duplicated content, add pointers to docs. Example update:
Add to appropriate section:
## Documentation
**Primary documentation:** https://docs.fzymgc.house (or run `mkdocs serve` locally)
See `docs/` for:
- [Operations guides](docs/operations/) — Vault, Authentik, Cloudflare procedures
- [Reference](docs/reference/) — Services catalog, network info, secrets
- [Architecture](docs/architecture/) — System design and decisions
Step 2: Commit
git add CLAUDE.md
git commit -m "docs: update CLAUDE.md to reference docs site"
Task 16: Update Notion References¶
Step 1: Update any files referencing Notion
Search for Notion links and update to point to new docs site or mark as deprecated.
Run: rg -l "notion.so" docs/
Step 2: Commit updates
git add -A
git commit -m "docs: deprecate Notion references in favor of docs site"
Task 17: Final Verification¶
Step 1: Full local build
Run: mkdocs build --strict
Expected: Build succeeds with no errors
Step 2: Local preview
Run: mkdocs serve
Expected: All pages render, navigation works, search works
Step 3: Push to trigger deployment
git push origin main
Step 4: Verify GitHub Action
Check: https://github.com/fzymgc-house/selfhosted-cluster/actions Expected: Workflow runs and deploys successfully
Step 5: Verify Access protection
Open: https://docs.fzymgc.house Expected: Redirected to Cloudflare Access login, shows 3 IDP options
Step 6: Test each IDP
- [ ] Authentik login works
- [ ] GitHub login works
- [ ] OTP login works
- [ ] Unauthorized email domain is rejected
Success Criteria Checklist¶
- [ ] MkDocs builds successfully with
--strict - [ ] Full-text search works across all docs
- [ ] Navigation reflects new structure
- [ ] Authentik exposed via Cloudflare Tunnel
- [ ] GitHub OAuth App configured
- [ ] All 3 IDPs functional in Access
- [ ] Email domain restriction enforced
- [ ] Auto-deploy on push to main works
- [ ] Existing doc content migrated
- [ ] Reference docs created from Notion data
- [ ] CLAUDE.md files updated with pointers