From 68a1524dca7cf97205fba49f6bf6c5ec4b8cc823 Mon Sep 17 00:00:00 2001 From: agent-company Date: Tue, 26 May 2026 20:45:33 +0000 Subject: [PATCH 1/2] =?UTF-8?q?docs:=20add=20operator-runbook.md=20?= =?UTF-8?q?=E2=80=94=20ordered=20manual-action=20guide=20for=20operator=20?= =?UTF-8?q?(closes=20leeworks-agents/api-company#50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/operator-runbook.md | 299 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 docs/operator-runbook.md diff --git a/docs/operator-runbook.md b/docs/operator-runbook.md new file mode 100644 index 0000000..727dc08 --- /dev/null +++ b/docs/operator-runbook.md @@ -0,0 +1,299 @@ +# Operator Runbook + +**Audience:** Human operator (0xWheatyz) +**Purpose:** Ordered, copy-paste-ready guide to bring the full `api-company` stack live. +**Last updated:** 2026-05-26 +**Closes:** leeworks-agents/api-company#50 + +--- + +## Overview + +The agent has committed all Flux manifests and documentation. The only remaining +work is a set of manual steps that require Gitea admin access, `kubectl` access to +the `testing1` cluster, and external service accounts (RapidAPI, Slack, PayPal). + +Work through these phases **in order** — each phase unblocks the next. + +--- + +## Phase 0 — Create upstream repo (unblocks all ship cycles) + +> **Why first?** Every agent deployment cycle fails to open a PR to upstream +> because `0xWheatyz/api-company` does not yet exist. This one step unblocks +> all automated deployments. See issues #41, #47. + +### Step 0-A — Create `0xWheatyz/api-company` on Gitea + +1. Log into Gitea as `0xWheatyz`. +2. **+** → **New Repository**. +3. Owner: `0xWheatyz`, Name: `api-company`. +4. Visibility: Public (or Private — your choice). +5. **Do not** initialise with a README. +6. Click **Create Repository**. + +Once created, the agent's next ship cycle will open a deployment PR automatically. + +--- + +## Phase 1 — Wire Flux to this repo (unblocks all GitOps reconciliation) + +> **Why second?** Until Flux watches `leeworks-agents/api-company`, none of the +> manifests in `flux/` are applied to the cluster. See issue #2. + +### Step 1-A — Create `gitea-leeworks-agents-token` secret in `flux-system` + +```bash +# In Gitea: User Settings → Applications → Generate Token +# Scopes: read:repository (read-only is sufficient for Flux) +# Copy the token, then: + +kubectl create secret generic gitea-leeworks-agents-token \ + -n flux-system \ + --from-literal=username=leeworks-agents \ + --from-literal=password= +``` + +### Step 1-B — Copy Flux source + kustomization into 0xWheatyz/Talos + +Reference manifests are at `flux/api-company-source/` in this repo. +Copy them verbatim to: + +``` +0xWheatyz/Talos:testing1/first-cluster/cluster/flux/api-company-source/ +├── gitrepository.yaml +└── kustomization.yaml +``` + +You can do this via the Gitea web editor or locally: + +```bash +cd /path/to/Talos-checkout +mkdir -p testing1/first-cluster/cluster/flux/api-company-source +# copy the two files from api-company/flux/api-company-source/ +git add . +git commit -m "feat: wire Flux GitRepository + Kustomization for api-company" +git push origin main +``` + +**Verify reconciliation (after ~5 minutes):** +```bash +flux get sources git -n flux-system +flux get kustomizations -n flux-system +``` + +Both `api-company` entries should show `Ready = True`. + +--- + +## Phase 2 — Secrets for already-staged services + +Once Flux is watching the repo, it will attempt to reconcile all `flux/` +sub-directories. The HelmReleases will stall on missing secrets. Create them: + +### Step 2-A — `gitea-runner-token` (unblocks Gitea Actions runner, issue #3) + +```bash +# In Gitea: Admin Panel → Site Administration → Actions → Runners +# → Create new Runner → copy registration token + +kubectl create secret generic gitea-runner-token \ + -n gitea-runner \ + --from-literal=token= +``` + +**Verify:** +```bash +kubectl get pods -n gitea-runner +# Then check Gitea Admin → Actions → Runners — runner should appear Online +``` + +### Step 2-B — `grafana-admin` (unblocks Grafana, issue #7) + +```bash +kubectl create secret generic grafana-admin \ + -n monitoring \ + --from-literal=admin-password= +``` + +Grafana URL: `https://grafana.leeworks.dev` (login: `admin` / ``) + +### Step 2-C — `gatus-slack-webhook` (unblocks Gatus alerts, issue #8) + +```bash +# Create an incoming webhook at: https://api.slack.com/messaging/webhooks + +kubectl create secret generic gatus-slack-webhook \ + -n monitoring \ + --from-literal=url=https://hooks.slack.com/services/YOUR/WEBHOOK/URL +``` + +Gatus URL: `https://status.leeworks.dev` + +### Step 2-D — Enable Gitea packages + registry DNS (issue #4) + +**4a — Enable packages in Gitea `app.ini`:** +```ini +[packages] +ENABLED = true +``` +Restart Gitea after editing `app.ini`. + +**4b — Add DNS A record:** +``` +registry.leeworks.dev → +``` + +Find the ingress IP: +```bash +kubectl get svc -n ingress-nginx +``` + +See `docs/registry.md` for additional context. + +--- + +## Phase 3 — Enable CI image push (unblocks API service deployments) + +Once the runner is online and the registry is reachable, CI pipelines can build +and push container images. + +### Step 3-A — Add `GITEA_TOKEN` Actions Secret to each repo + +Repos to configure: +- `leeworks-agents/api-company` +- `leeworks-agents/zip-enrichment` +- `leeworks-agents/holidays` +- `leeworks-agents/air-quality` + +**For each repo:** Repo → Settings → Actions → Secrets → Add Secret +- **Name:** `GITEA_TOKEN` +- **Value:** Gitea personal access token with `write:packages` scope + +### Step 3-B — Create image-automation token secret (issue #51) + +The agent has added `ImageRepository` + `ImagePolicy` + `ImageUpdateAutomation` +manifests to `flux/image-automation/`. Flux will automatically update image tags +in HelmReleases when CI pushes new images — but it needs write access to commit +back: + +```bash +kubectl create secret generic gitea-image-automation-token \ + -n flux-system \ + --from-literal=username=leeworks-agents \ + --from-literal=password= +``` + +--- + +## Phase 4 — DNS for API services (issue #33) + +Add DNS A records for all six leeworks.dev subdomains (all point to the same +cluster ingress IP): + +| Hostname | Target | +|-------------------------|------------------------| +| `zip.leeworks.dev` | `` | +| `holidays.leeworks.dev` | `` | +| `aqi.leeworks.dev` | `` | +| `docs.leeworks.dev` | `` | +| `grafana.leeworks.dev` | `` | +| `status.leeworks.dev` | `` | + +cert-manager will obtain Let's Encrypt certificates automatically once DNS +propagates (typically minutes, up to 48 h). + +--- + +## Phase 5 — RapidAPI + PayPal (issue #44, #19) + +> **Blocked on operator being 18+ for PayPal.** Complete when eligible. + +1. Create accounts on [rapidapi.com](https://rapidapi.com) and [paypal.com](https://www.paypal.com). +2. Link PayPal to RapidAPI as the payout method. +3. Submit each API to the RapidAPI marketplace using `docs/rapidapi-listings.md`. +4. Configure paid tiers per `ROADMAP.md`. + +After submission, RapidAPI generates a `X-RapidAPI-Proxy-Secret` per API. Create: + +```bash +# zip-enrichment +kubectl create secret generic rapidapi-proxy-secret \ + -n zip-enrichment \ + --from-literal=X-RapidAPI-Proxy-Secret= + +# holidays +kubectl create secret generic rapidapi-proxy-secret \ + -n holidays \ + --from-literal=X-RapidAPI-Proxy-Secret= + +# air-quality +kubectl create secret generic rapidapi-proxy-secret \ + -n air-quality \ + --from-literal=X-RapidAPI-Proxy-Secret= +``` + +--- + +## Quick Verification Checklist + +```bash +# Flux overall health +flux get all -A + +# API service pods +kubectl get pods -n zip-enrichment +kubectl get pods -n holidays +kubectl get pods -n air-quality + +# Ingress + TLS +kubectl get ingress -A +kubectl get certificates -A + +# Gitea runner +kubectl get pods -n gitea-runner + +# Monitoring stack +kubectl get pods -n monitoring + +# Image automation +flux get imagepolicies -A +flux get imagerepositories -A +``` + +--- + +## Dependency Summary + +``` +Phase 0: Create 0xWheatyz/api-company repo + └─► unblocks agent deployment PRs to upstream + +Phase 1: Wire Flux (gitea-token secret + Talos manifests) + └─► all flux/ manifests reconcile + +Phase 2: Service secrets (runner-token, grafana-admin, gatus-webhook, registry) + └─► runner online, monitoring live, registry reachable + +Phase 3: CI secrets + image-automation token + └─► images build, push, and auto-update → API services deploy + +Phase 4: DNS records + └─► HTTPS certs issued → public URLs go live + +Phase 5: RapidAPI + PayPal + └─► revenue enabled +``` + +--- + +## Related Documents + +| Document | Purpose | +|----------|---------| +| `docs/secrets-checklist.md` | Full checklist of all required secrets | +| `docs/registry.md` | Container registry architecture decision | +| `docs/cluster-audit.md` | Node/namespace/ingress inventory | +| `docs/rapidapi-listings.md` | RapidAPI marketplace submission details | +| `ROADMAP.md` | Full project roadmap and milestones | +| `STATUS.md` | Current cycle status and blockers | -- 2.52.0 From 82c9f70a018a8288b5bcb95a5b96eec583f0ada3 Mon Sep 17 00:00:00 2001 From: agent-company Date: Tue, 26 May 2026 20:45:33 +0000 Subject: [PATCH 2/2] feat: add Flux ImageRepository + ImagePolicy + ImageUpdateAutomation for all three API services (closes leeworks-agents/api-company#51) --- flux/air-quality/helmrelease.yaml | 2 +- flux/holidays/helmrelease.yaml | 2 +- flux/image-automation/imagepolicies.yaml | 39 +++++++++++++++++++ flux/image-automation/imagerepositories.yaml | 34 ++++++++++++++++ .../imageupdateautomation.yaml | 37 ++++++++++++++++++ flux/image-automation/kustomization.yaml | 6 +++ flux/kustomization.yaml | 1 + flux/zip-enrichment/helmrelease.yaml | 2 +- 8 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 flux/image-automation/imagepolicies.yaml create mode 100644 flux/image-automation/imagerepositories.yaml create mode 100644 flux/image-automation/imageupdateautomation.yaml create mode 100644 flux/image-automation/kustomization.yaml diff --git a/flux/air-quality/helmrelease.yaml b/flux/air-quality/helmrelease.yaml index 128be9a..a7a6920 100644 --- a/flux/air-quality/helmrelease.yaml +++ b/flux/air-quality/helmrelease.yaml @@ -35,7 +35,7 @@ spec: - name: gitea-registry containers: - name: air-quality - image: registry.leeworks.dev/air-quality/server:latest + image: registry.leeworks.dev/air-quality/server:latest # {"$imagepolicy": "flux-system:air-quality"} ports: - containerPort: 3000 env: diff --git a/flux/holidays/helmrelease.yaml b/flux/holidays/helmrelease.yaml index 35c48ba..40cc750 100644 --- a/flux/holidays/helmrelease.yaml +++ b/flux/holidays/helmrelease.yaml @@ -35,7 +35,7 @@ spec: - name: gitea-registry containers: - name: holidays - image: registry.leeworks.dev/holidays/server:latest + image: registry.leeworks.dev/holidays/server:latest # {"$imagepolicy": "flux-system:holidays"} ports: - containerPort: 3000 env: diff --git a/flux/image-automation/imagepolicies.yaml b/flux/image-automation/imagepolicies.yaml new file mode 100644 index 0000000..f55609c --- /dev/null +++ b/flux/image-automation/imagepolicies.yaml @@ -0,0 +1,39 @@ +# ImagePolicy: select the latest semver tag from each ImageRepository. +# Tags pushed by CI should follow semver (e.g. v1.2.3) or use "latest" — +# the semver policy picks up any vX.Y.Z tag. The "latest" alias keeps +# things working before formal releases are tagged. +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: zip-enrichment + namespace: flux-system +spec: + imageRepositoryRef: + name: zip-enrichment + policy: + semver: + range: ">=0.1.0" +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: holidays + namespace: flux-system +spec: + imageRepositoryRef: + name: holidays + policy: + semver: + range: ">=0.1.0" +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImagePolicy +metadata: + name: air-quality + namespace: flux-system +spec: + imageRepositoryRef: + name: air-quality + policy: + semver: + range: ">=0.1.0" diff --git a/flux/image-automation/imagerepositories.yaml b/flux/image-automation/imagerepositories.yaml new file mode 100644 index 0000000..bbf7214 --- /dev/null +++ b/flux/image-automation/imagerepositories.yaml @@ -0,0 +1,34 @@ +# Flux image-reflector-controller watches these registries for new image tags. +# Requires: flux-system/gitea-image-automation-token secret (see docs/operator-runbook.md) +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: zip-enrichment + namespace: flux-system +spec: + image: registry.leeworks.dev/zip-enrichment/server + interval: 5m + secretRef: + name: gitea-leeworks-agents-token +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: holidays + namespace: flux-system +spec: + image: registry.leeworks.dev/holidays/server + interval: 5m + secretRef: + name: gitea-leeworks-agents-token +--- +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageRepository +metadata: + name: air-quality + namespace: flux-system +spec: + image: registry.leeworks.dev/air-quality/server + interval: 5m + secretRef: + name: gitea-leeworks-agents-token diff --git a/flux/image-automation/imageupdateautomation.yaml b/flux/image-automation/imageupdateautomation.yaml new file mode 100644 index 0000000..332cd2d --- /dev/null +++ b/flux/image-automation/imageupdateautomation.yaml @@ -0,0 +1,37 @@ +# ImageUpdateAutomation: when an ImagePolicy selects a new tag, this object +# instructs Flux to open a commit on the api-company repo updating the +# image reference in the relevant HelmRelease values. +# +# The GitRepository used here is the api-company source (flux-system/api-company). +# Flux needs write access; create the token secret first: +# kubectl create secret generic gitea-image-automation-token \ +# -n flux-system \ +# --from-literal=username=leeworks-agents \ +# --from-literal=password= +# Then patch the api-company GitRepository to reference it (or reuse +# gitea-leeworks-agents-token if that token also has write:repository scope). +apiVersion: image.toolkit.fluxcd.io/v1beta2 +kind: ImageUpdateAutomation +metadata: + name: api-company + namespace: flux-system +spec: + interval: 10m + sourceRef: + kind: GitRepository + name: api-company + git: + checkout: + ref: + branch: main + commit: + author: + email: agent@leeworks.dev + name: Flux Image Automation + messageTemplate: | + chore(image): update {{range .Updated.Images}}{{.Repository}}:{{.NewTag}} {{end}} + push: + branch: main + update: + path: ./flux + strategy: Setters diff --git a/flux/image-automation/kustomization.yaml b/flux/image-automation/kustomization.yaml new file mode 100644 index 0000000..bd70636 --- /dev/null +++ b/flux/image-automation/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - imagerepositories.yaml + - imagepolicies.yaml + - imageupdateautomation.yaml diff --git a/flux/kustomization.yaml b/flux/kustomization.yaml index b5db76d..cef3caf 100644 --- a/flux/kustomization.yaml +++ b/flux/kustomization.yaml @@ -7,3 +7,4 @@ resources: - zip-enrichment - holidays - air-quality + - image-automation diff --git a/flux/zip-enrichment/helmrelease.yaml b/flux/zip-enrichment/helmrelease.yaml index 7bf2698..bb18736 100644 --- a/flux/zip-enrichment/helmrelease.yaml +++ b/flux/zip-enrichment/helmrelease.yaml @@ -35,7 +35,7 @@ spec: - name: gitea-registry containers: - name: zip-enrichment - image: registry.leeworks.dev/zip-enrichment/server:latest + image: registry.leeworks.dev/zip-enrichment/server:latest # {"$imagepolicy": "flux-system:zip-enrichment"} ports: - containerPort: 3000 env: -- 2.52.0