feat: implement docs-site, legal docs, metrics standard, flux manifests

Closes leeworks-agents/api-company#5  (docs-site Astro scaffold)
Closes leeworks-agents/api-company#9  (metrics instrumentation standard)
Closes leeworks-agents/api-company#10 (Gitea Actions openapi aggregation pipeline)
Closes leeworks-agents/api-company#11 (docs-site Flux HelmRelease)
Closes leeworks-agents/api-company#12 (SEO blog posts x3)
Closes leeworks-agents/api-company#13 (legal docs ToS/Privacy/AUP)
Closes leeworks-agents/api-company#14 (DNS documentation)

## Changes

### docs/legal/
- terms-of-service.md — API usage, liability, account termination, governing law
- privacy-policy.md — request log retention (90d), no PII sold, data sharing
- acceptable-use-policy.md — rate limit abuse, scraping prohibition, resale ban

### docs/metrics-standard.md
- Defines api_requests_total, api_response_duration_seconds, api_data_freshness_seconds
- Fastify (TypeScript) and FastAPI (Python) reference middleware implementations
- Prometheus scrape config and Grafana dashboard guidance

### docs/registry.md
- Decision: use Gitea built-in container registry (no new infra)
- Image naming convention, auth, Kubernetes imagePullSecrets, ingress config

### docs/dns.md
- Required A records for all 6 subdomains
- cert-manager ClusterIssuer and Ingress TLS examples
- Verification commands and human-operator action items

### docs-site/
- Astro 4 + MDX + sitemap scaffold
- Base layout with nav linking all APIs, blog, RapidAPI, status
- Landing page with API cards
- Per-API Redoc viewer pages (zip-enrichment, holidays, air-quality)
- Blog index + 3 SEO blog posts (~1000 words each with JSON-LD)
- Dockerfile (multi-stage: node build + nginx serve)
- nginx.conf with gzip, caching, health endpoint

### flux/
- gitea-runner/: gitea-act-runner HelmRelease (org-scope, dind)
- monitoring/: kube-prometheus-stack + Gatus HelmReleases
  - Prometheus with pod annotation scraping
  - Grafana at grafana.leeworks.dev with persistence
  - Gatus status page at status.leeworks.dev, 90-day retention
- docs-site/: Deployment + Service + Ingress via raw chart
- api-company-source/: GitRepository + Kustomization reference manifests
- kustomization.yaml: root kustomize entry point (build validated)

### .gitea/workflows/build-docs.yaml
- Aggregates openapi.yaml from zip-enrichment, holidays, air-quality repos
- Builds Astro docs-site
- Pushes image to registry.leeworks.dev/leeworks-agents/docs-site
- Triggered on push to main, schedule daily 02:00 UTC, workflow_dispatch
This commit is contained in:
agent-company
2026-05-24 23:20:33 +00:00
parent 40631fee7c
commit a615b7ebfd
41 changed files with 2201 additions and 3 deletions
-1
View File
@@ -1 +0,0 @@
# placeholder — populated by Phase-4/5 issues
@@ -0,0 +1,17 @@
# This manifest is FOR REFERENCE — the live version must be committed to
# 0xWheatyz/Talos at testing1/first-cluster/cluster/flux/api-company/
#
# See leeworks-agents/api-company#2
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: api-company
namespace: flux-system
spec:
interval: 5m
url: https://gitea.leeworks.dev/leeworks-agents/api-company
ref:
branch: main
secretRef:
name: gitea-leeworks-agents-token # must pre-exist in flux-system ns
@@ -0,0 +1,19 @@
# This manifest is FOR REFERENCE — the live version must be committed to
# 0xWheatyz/Talos at testing1/first-cluster/cluster/flux/api-company/
#
# See leeworks-agents/api-company#2
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: api-company
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: api-company
path: ./flux
prune: true
wait: true
timeout: 5m
+89
View File
@@ -0,0 +1,89 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: docs-site
namespace: docs-site
spec:
interval: 10m
chart:
spec:
chart: raw
version: ">=0.2.0"
sourceRef:
kind: HelmRepository
name: bedag
namespace: flux-system
interval: 60m
values:
resources:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: docs-site
namespace: docs-site
spec:
replicas: 1
selector:
matchLabels:
app: docs-site
template:
metadata:
labels:
app: docs-site
spec:
imagePullSecrets:
- name: gitea-registry
containers:
- name: docs-site
image: registry.leeworks.dev/leeworks-agents/docs-site:latest
ports:
- containerPort: 80
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 30
- apiVersion: v1
kind: Service
metadata:
name: docs-site
namespace: docs-site
spec:
selector:
app: docs-site
ports:
- port: 80
targetPort: 80
- apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: docs-site
namespace: docs-site
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- docs.leeworks.dev
secretName: docs-site-tls
rules:
- host: docs.leeworks.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: docs-site
port:
number: 80
+8
View File
@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: bedag
namespace: flux-system
spec:
interval: 60m
url: https://bedag.github.io/helm-charts/
+6
View File
@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helmrepository.yaml
- helmrelease.yaml
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: docs-site
+42
View File
@@ -0,0 +1,42 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: gitea-act-runner
namespace: gitea-runner
spec:
interval: 10m
chart:
spec:
chart: gitea-act-runner
version: ">=0.1.0"
sourceRef:
kind: HelmRepository
name: gitea-charts
namespace: flux-system
interval: 60m
values:
replicaCount: 1
config:
registration:
# Gitea instance URL
instanceUrl: "https://gitea.leeworks.dev"
# Token from Gitea admin → Actions → Runners → New Runner
# Store in a Kubernetes Secret named gitea-runner-token
tokenFromSecret:
secretName: gitea-runner-token
secretKey: token
runner:
# Register at org scope so all leeworks-agents repos can use it
labels:
- "ubuntu-latest:docker://node:20-bookworm"
- "ubuntu-22.04:docker://node:20-bookworm"
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 2000m
memory: 2Gi
# Runner needs Docker socket or dind
dind:
enabled: true
+8
View File
@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: gitea-charts
namespace: flux-system
spec:
interval: 60m
url: https://dl.gitea.com/charts/
+6
View File
@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helmrepository.yaml
- helmrelease.yaml
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: gitea-runner
+6
View File
@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gitea-runner
- monitoring
- docs-site
+94
View File
@@ -0,0 +1,94 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: gatus
namespace: monitoring
spec:
interval: 15m
chart:
spec:
chart: gatus
version: ">=1.0.0"
sourceRef:
kind: HelmRepository
name: minicloudlabs
namespace: flux-system
interval: 60m
values:
ingress:
enabled: true
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
hosts:
- host: status.leeworks.dev
paths:
- path: /
pathType: Prefix
tls:
- secretName: gatus-tls
hosts:
- status.leeworks.dev
config:
storage:
type: sqlite
path: /data/gatus.db
endpoints:
- name: ZIP Enrichment API
url: https://zip.leeworks.dev/health
interval: 1m
conditions:
- "[STATUS] == 200"
- "[RESPONSE_TIME] < 1000"
alerts:
- type: slack
description: "ZIP Enrichment API is down"
send-on-resolved: true
- name: Holidays API
url: https://holidays.leeworks.dev/health
interval: 1m
conditions:
- "[STATUS] == 200"
- "[RESPONSE_TIME] < 1000"
alerts:
- type: slack
description: "Holidays API is down"
send-on-resolved: true
- name: Air Quality API
url: https://aqi.leeworks.dev/health
interval: 1m
conditions:
- "[STATUS] == 200"
- "[RESPONSE_TIME] < 1000"
alerts:
- type: slack
description: "Air Quality API is down"
send-on-resolved: true
- name: Docs Site
url: https://docs.leeworks.dev
interval: 5m
conditions:
- "[STATUS] == 200"
- name: Container Registry
url: https://registry.leeworks.dev/v2/
interval: 5m
conditions:
- "[STATUS] == 200"
ui:
title: "leeworks.dev API Status"
description: "Real-time status for all leeworks.dev APIs"
logo: ""
# Retention: 90 days
retention:
days: 90
persistence:
enabled: true
size: 1Gi
mountPath: /data
@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: minicloudlabs
namespace: flux-system
spec:
interval: 60m
url: https://minicloudlabs.github.io/helm-charts
+92
View File
@@ -0,0 +1,92 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kube-prometheus-stack
namespace: monitoring
spec:
interval: 15m
chart:
spec:
chart: kube-prometheus-stack
version: ">=58.0.0 <60.0.0"
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
interval: 60m
install:
crds: CreateReplace
remediation:
retries: 3
upgrade:
crds: CreateReplace
remediation:
retries: 3
values:
grafana:
enabled: true
adminPassword: "${GRAFANA_ADMIN_PASSWORD}" # inject via Secret/substitution
ingress:
enabled: true
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
hosts:
- grafana.leeworks.dev
tls:
- secretName: grafana-tls
hosts:
- grafana.leeworks.dev
persistence:
enabled: true
size: 5Gi
sidecar:
dashboards:
enabled: true
prometheus:
prometheusSpec:
retention: 30d
storageSpec:
volumeClaimTemplate:
spec:
resources:
requests:
storage: 20Gi
# Scrape pods with prometheus.io/scrape=true annotations
podMonitorNamespaceSelector: {}
podMonitorSelector: {}
serviceMonitorNamespaceSelector: {}
serviceMonitorSelector: {}
# Additional scrape configs for annotation-based discovery
additionalScrapeConfigs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: "true"
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
alertmanager:
enabled: false # Enable when alert routing is configured
kubeStateMetrics:
enabled: true
nodeExporter:
enabled: true
+8
View File
@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: prometheus-community
namespace: flux-system
spec:
interval: 60m
url: https://prometheus-community.github.io/helm-charts
+8
View File
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helmrepository.yaml
- helmrelease.yaml
- gatus-helmrepository.yaml
- gatus-helmrelease.yaml
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: monitoring