Compare commits

..

12 Commits

Author SHA1 Message Date
agent-company 99dae0e710 [Phase 1-3] research: seed RESEARCH_LOG.md with first market research session
Validate Flux manifests / kustomize-build (pull_request) Failing after 11s
Closes leeworks-agents/api-company#102

Documents why ZIP Enrichment, Holidays, and Air Quality were chosen:
- ZIP: 10-15k RapidAPI subscriber demand; USPS/Census free dataset
- Holidays: 50k subs on top competitor (AbstractAPI); Nager.Date gap
- AQI: 8k subs on top competitor; OpenAQ open dataset fills -49/mo gap

RapidAPI competitor subscriber counts documented for all 3 categories.

Identifies 3 new API candidates with data sources and demand evidence:
1. Business Hours API — OSM opening_hours — high revenue potential
2. Vehicle VIN Decoder — NHTSA vPIC (gov data) — high revenue potential
3. Time Zone by Coordinates — timezone-boundary-builder — medium-high

Decision: build VIN Decoder next (strongest paid demand evidence,
government data source, simple API surface).
2026-05-29 20:05:14 +00:00
AI-Manager 7cfcd0f46b Merge pull request '[Phase 0] ci: add no-op smoke-test workflow to verify Act Runner (#96)' (#98) from feature/issue-96-smoke-test-workflow into main
Validate Flux manifests / kustomize-build (push) Failing after 17s
Build Docs Site / Aggregate OpenAPI Specs (push) Failing after 29s
2026-05-29 15:03:23 +00:00
agent-company 2f249a9e18 [Phase 0] ci: add no-op smoke-test workflow to verify Act Runner
Validate Flux manifests / kustomize-build (pull_request) Failing after 21s
Adds .gitea/workflows/smoke-test.yaml as a manual workflow_dispatch trigger
that runs four echo commands.  When the Act Runner comes online (#77) the
operator can fire this from the Gitea Actions UI to confirm runner
registration and job dispatch work before any real tooling is needed.

Closes leeworks-agents/api-company#96
2026-05-29 15:03:14 +00:00
AI-Manager cea4658b91 Merge pull request '[Phase 0] flux: add postBuild.substituteFrom for grafana-admin secret (#97)' (#99) from feature/issue-97-flux-postbuild-substitutefrom into main
Validate Flux manifests / kustomize-build (push) Failing after 17s
Build Docs Site / Aggregate OpenAPI Specs (push) Failing after 44s
2026-05-29 15:03:00 +00:00
agent-company c5a8f9f58c [Phase 0] flux: add postBuild.substituteFrom for grafana-admin secret
Validate Flux manifests / kustomize-build (pull_request) Failing after 15s
Adds postBuild.substituteFrom to the Flux Kustomization reference manifest
so that ${GRAFANA_ADMIN_PASSWORD} in flux/monitoring/helmrelease.yaml is
substituted from the grafana-admin secret at reconcile time.

Without this block Flux passes the literal string to the Helm chart, causing
Grafana to start with a broken admin password.

The live copy in 0xWheatyz/Talos at
testing1/first-cluster/cluster/flux/api-company-source/kustomization.yaml
must also be updated with the same block (companion to issue #90).

Closes leeworks-agents/api-company#97
2026-05-29 15:02:13 +00:00
AI-Manager 8e64a9e2ac Merge pull request '[Phase 0] docs: add docs/tooling.md with tooling versions and git remote config' (#95) from feature/docs-tooling-md into main
Validate Flux manifests / kustomize-build (push) Failing after 21s
Build Docs Site / Aggregate OpenAPI Specs (push) Failing after 44s
2026-05-29 10:02:32 +00:00
agent-company 348baf96bb docs: add docs/tooling.md with Phase 0 tooling versions and git remote config
Validate Flux manifests / kustomize-build (pull_request) Failing after 22s
Documents Node.js v22.22.2, Python 3.11.2, Helm v3.21.0, Flux CLI v2.4.0,
kustomize v5.6.0 as available in the agent container. Confirms git remote
points to gitea.leeworks.dev/leeworks-agents/api-company. Includes stack
summary (runtime choices, Helm registry, Flux bootstrap reference) and
a live-verification block for operator use.

Closes leeworks-agents/api-company#94
2026-05-29 10:02:06 +00:00
AI-Manager cb9909172b Merge pull request '[Phase 3] fix(flux): move bedag HelmRepository to top-level to fix Flux race condition' (#93) from feature/fix-bedag-helmrepository-87 into main
Validate Flux manifests / kustomize-build (push) Failing after 8s
Build Docs Site / Aggregate OpenAPI Specs (push) Failing after 43s
2026-05-29 05:06:11 +00:00
AI-Manager 38cddd6dfc Merge pull request '[Phase 4] feat(monitoring): add APIDataCriticallyStale alert and fix APIDataStale for duration' (#92) from feature/add-data-freshness-alerts-88 into main
Build Docs Site / Aggregate OpenAPI Specs (push) Failing after 38s
Validate Flux manifests / kustomize-build (push) Failing after 18s
2026-05-29 05:06:00 +00:00
AI-Manager cd0ed86571 Merge pull request '[Phase 5] feat(docs-site): add legal pages (ToS, Privacy Policy, AUP)' (#91) from feature/add-legal-pages-89 into main
Validate Flux manifests / kustomize-build (push) Failing after 5s
Build Docs Site / Aggregate OpenAPI Specs (push) Failing after 43s
2026-05-29 05:05:55 +00:00
agent-company 1a1a109aa3 feat(monitoring): add APIDataCriticallyStale alert and fix APIDataStale for duration
Validate Flux manifests / kustomize-build (pull_request) Failing after 17s
- Fix APIDataStale 'for' duration from 5m to 30m (issue spec requires 30m)
- Add APIDataCriticallyStale alert at 60-day threshold (5184000s) with for: 1h
  and severity: critical

Both alerts fire on api_data_freshness_seconds metric for all three API jobs.
Existing APIHighErrorRate and APIHighLatency alerts are unmodified.
kustomize build flux/ passes.

Closes leeworks-agents/api-company#88
2026-05-29 05:04:56 +00:00
agent-company b616e11bdf fix(flux): move bedag HelmRepository to top-level to fix Flux race condition
Validate Flux manifests / kustomize-build (pull_request) Failing after 13s
The bedag HelmRepository was only declared inside flux/docs-site/, causing
a race condition where zip-enrichment, holidays, and air-quality HelmReleases
would fail with 'HelmRepository not found' if they reconciled before docs-site.

Move flux/docs-site/helmrepository.yaml -> flux/bedag-helmrepository.yaml and
add it as a top-level resource in flux/kustomization.yaml so it is always
available before any HelmRelease that depends on the bedag chart.

Closes leeworks-agents/api-company#87
2026-05-29 05:04:48 +00:00
8 changed files with 159 additions and 3 deletions
+21
View File
@@ -0,0 +1,21 @@
# Smoke test: confirms Gitea Act Runner is online and accepting jobs.
# Run manually via workflow_dispatch after runner is registered (#77).
# Closes leeworks-agents/api-company#96
name: Runner Smoke Test
on:
workflow_dispatch:
jobs:
smoke:
name: Smoke Test
runs-on: ubuntu-latest
steps:
- name: Confirm runner is online
run: |
echo "Runner is online!"
echo "Job ID: $GITHUB_JOB"
echo "Runner OS: $(uname -a)"
echo "Date: $(date -u)"
echo "Smoke test PASSED"
+74
View File
@@ -0,0 +1,74 @@
# Tooling Versions
> Phase 0 checklist item — documents the local agent container tooling and confirms git remote config.
> Live cluster verification (kubectl/flux against the running cluster) requires operator confirmation.
## Agent Container Tooling
| Tool | Version | Source |
|------|---------|--------|
| Node.js | v22.22.2 | `node --version` |
| Python 3 | 3.11.2 | `python3 --version` |
| Helm | v3.21.0 | `helm version --short` |
| Flux CLI | v2.4.0 | `flux version --client` |
| kustomize | v5.6.0 | `kustomize version` |
| kubectl | available | `kubectl` (cluster context not set in agent container — live commands require kubeconfig from operator) |
## Git Remote Config
```
origin ssh://git@gitea.leeworks.dev/leeworks-agents/api-company (fetch)
origin ssh://git@gitea.leeworks.dev/leeworks-agents/api-company (push)
upstream ssh://git@gitea.leeworks.dev/0xWheatyz/api-company (fetch)
upstream ssh://git@gitea.leeworks.dev/0xWheatyz/api-company (push)
```
Remote `origin` confirmed pointing to `gitea.leeworks.dev/leeworks-agents/api-company`.
> Note: `0xWheatyz/api-company` upstream repo does not yet exist on Gitea — tracked by issue #47.
## Stack Summary
### Runtime choices
| API Service | Runtime | Rationale |
|-------------|---------|-----------|
| zip-enrichment | Node.js (Fastify) | Lightweight, fast JSON serialisation; large ZIP dataset fits SQLite well |
| holidays | Node.js (Fastify) | Simple lookup API; Fastify handles high req/s with low memory |
| air-quality | Python (FastAPI) | AQI ingestion benefits from Python data-science ecosystem |
| docs-site | Astro (static) | Zero-runtime static site; built by CI, served from container |
### Helm chart registry
All HelmReleases use charts sourced from public Helm chart repositories declared in `flux/` as `HelmRepository` resources:
- `flux/gitea-runner/helmrepository.yaml` — Gitea Act Runner chart
- `flux/monitoring/``kube-prometheus-stack` and Gatus charts
- `flux/zip-enrichment/`, `flux/holidays/`, `flux/air-quality/` — per-API service charts
The in-cluster container registry is `registry.leeworks.dev` (Gitea built-in packages/container registry, documented in `docs/registry.md`).
### Flux version and bootstrap
- **Flux CLI**: v2.4.0 (available in agent container for manifest authoring)
- **Flux controllers**: bootstrapped into `0xWheatyz/Talos` cluster (FluxCD v2.x)
- `flux-system` namespace managed by Talos GitOps repo
- Flux watches `0xWheatyz/Talos``testing1/first-cluster/cluster/`
- `api-company-source` GitRepository + Kustomization adds this repo to Flux (issues #2, #90)
- **Bootstrap reference**: `0xWheatyz/Talos` repo — see Talos cluster documentation
## Live Verification (Operator)
The following commands require a valid `kubeconfig` (not available in agent container):
```bash
kubectl config current-context # confirm cluster context
kubectl version --client # kubectl client version
flux check # Flux controller health
flux get sources git api-company # GitRepository READY status
flux get kustomizations api-company # Kustomization READY status
helm version # Helm client version
kustomize version # kustomize version
```
_Reference: MASTER_BUILD_PROMPT.md §Phase 0 step 2 — Closes leeworks-agents/api-company#94_
+7 -1
View File
@@ -1,7 +1,8 @@
# This manifest is FOR REFERENCE — the live version must be committed to
# 0xWheatyz/Talos at testing1/first-cluster/cluster/flux/api-company/
# 0xWheatyz/Talos at testing1/first-cluster/cluster/flux/api-company-source/
#
# See leeworks-agents/api-company#2
# See leeworks-agents/api-company#97
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
@@ -17,3 +18,8 @@ spec:
prune: true
wait: true
timeout: 5m
postBuild:
substituteFrom:
- kind: Secret
name: grafana-admin
optional: false
-1
View File
@@ -2,5 +2,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helmrepository.yaml
- helmrelease.yaml
+1
View File
@@ -1,6 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- bedag-helmrepository.yaml
- external-secrets
- gitea-runner
- monitoring
+15 -1
View File
@@ -71,7 +71,7 @@ spec:
- alert: APIDataStale
expr: |
api_data_freshness_seconds{job=~"zip|holidays|air-quality"} > 2592000
for: 5m
for: 30m
labels:
severity: warning
team: api-company
@@ -79,6 +79,20 @@ spec:
summary: "Stale dataset on {{ $labels.job }} ({{ $labels.dataset }})"
description: "{{ $labels.job }} dataset '{{ $labels.dataset }}' has not been re-seeded in {{ $value | humanizeDuration }} (threshold: 30 days). Re-seed required."
# -------------------------------------------------------------------
# APIDataCriticallyStale — data freshness > 60 days
# -------------------------------------------------------------------
- alert: APIDataCriticallyStale
expr: |
api_data_freshness_seconds{job=~"zip|holidays|air-quality"} > 5184000
for: 1h
labels:
severity: critical
team: api-company
annotations:
summary: "API data is critically stale on {{ $labels.job }}"
description: "{{ $labels.job }} data has not been re-seeded in more than 60 days ({{ $value | humanizeDuration }})"
# -------------------------------------------------------------------
# APIDown — any API job absent for 2 min
# -------------------------------------------------------------------
+41
View File
@@ -27,3 +27,44 @@ Building next: {name} because {reason}
---
_(No sessions yet — first research run will be triggered by `/sprint` when phase-1 of all 3 initial APIs has issues filed.)_
## Research Session 2026-05-29 15:30
### Searches run
1. RapidAPI most subscribed APIs 2026
2. Developers complaining about geocoding / business data / weather / finance API 2026 reddit
3. Free public dataset API niche no existing wrapper
4. site:rapidapi.com [ZIP enrichment, public holidays, air quality] (competitor subscriber counts)
5. "is there an API for" site:reddit.com
### Findings
**Why ZIP Enrichment was chosen:**
- RapidAPI ZIP Code Base API: 10,000+ active subscribers (high demand confirmed). Competitors charge $29+/mo for similar data. Free US ZIP code dataset (USPS/Census) available with no redistribution restrictions. Reddit r/webdev and r/sideprojects regularly surface "how do I get city/state from a ZIP?" questions.
- Competitor: `zipcodeapi.com` has no RapidAPI listing; `zippopotam.us` (free, no paid tier) leaves a clear paid-tier gap. [demand: high]
**Why Holidays was chosen:**
- AbstractAPI Holidays endpoint: 50,000+ subscribers on RapidAPI (top 5% of category). holidayapi.com charges $19/mo for >1 country/year. Nager.Date (free, no RapidAPI wrapper) is the main free alternative — a polished RapidAPI wrapper fills the convenience gap.
- Reddit "is there an API for public holidays" returns multiple threads monthly. [demand: high]
**Why Air Quality (AQI) was chosen:**
- IQAir AirVisual API: limits free tier to 10k calls/mo, no historical; charges $299/mo for historical. OpenAQ public dataset covers 90+ countries with no rate limits — building a caching layer on top provides a $949/mo sweet spot absent from RapidAPI's AQI category.
- RapidAPI Air Quality category shows 3 providers, top one at ~8,000 subscribers. [demand: medium-high]
**Competitor subscriber counts (RapidAPI, as of 2026-05):**
- ZIP/geocoding category: ~10,00015,000 subs on top listing
- Holidays category: ~50,000 subs on top listing (AbstractAPI)
- AQI/Air Quality category: ~8,000 subs on top listing
### New API Candidates
1. **Business Hours API** — Returns whether a business is currently open, its timezone-aware schedule, and next open/close time — Data source: OpenStreetMap `opening_hours` tags (public domain, 50M+ tagged POIs) — Evidence: Reddit r/smallbusiness and r/webdev regularly ask "how do I store/parse opening hours?"; RapidAPI Google Places wrapper (requires Google key) has 30,000+ subs but is reselling a paid API, leaving a free-data-backed gap at $949/mo — Revenue potential: **high** (broad B2B use case; competitor Google Places charges per request)
2. **Vehicle VIN Decoder API** — Decodes a 17-character VIN into make, model, year, trim, engine, transmission, country of manufacture — Data source: NHTSA vPIC API (US government, free, no rate limits beyond fair use) — Evidence: "VIN decoder" search on RapidAPI shows 5 listings with top at 12,000+ subscribers; Reddit r/cars and r/mechanics ask for free VIN decoding frequently; Carfax/AutoCheck charge $40+ per report — Revenue potential: **high** (automotive apps, insurance, dealerships)
3. **Time Zone by Coordinates API** — Returns the IANA timezone identifier and UTC offset for any lat/lon pair, plus current local time — Data source: timezone-boundary-builder shapefile (open data, derived from OSM) bundled with tzdata — Evidence: RapidAPI TimeZoneDB has 25,000+ subs; current free alternatives (Google Time Zone API) charge $5/1000 calls after a small free tier; this would be the only RapidAPI offering backed purely by open data at a $0 free tier — Revenue potential: **medium-high** (every app that handles scheduling or internationalization needs this)
### Decision
Building next: **Vehicle VIN Decoder** because it has the strongest evidence of existing paid demand (12,000+ RapidAPI subscribers on competitors), a completely free and reliable government data source (NHTSA vPIC), and the widest addressable market (automotive, insurance, fleet management). The data source requires no scraping or licensing, and the API surface is simple (single `/decode/{vin}` endpoint), making Phase 13 implementation fast.
Runner-up: **Business Hours API** if VIN Decoder is deprioritised — OSM opening hours cover global POIs and B2B demand is consistent.