commit 40631fee7ce44ecaac0567d5ef98dc74dc9f945c Author: 0xWheatyz Date: Mon May 18 17:18:47 2026 -0400 chore: bootstrap repo scaffolding Initial scaffolding committed by bootstrap script. See ROADMAP.md for phase plan and SESSION_LOG.md (api-company only) for context. diff --git a/MASTER_BUILD_PROMPT.md b/MASTER_BUILD_PROMPT.md new file mode 100644 index 0000000..37297f6 --- /dev/null +++ b/MASTER_BUILD_PROMPT.md @@ -0,0 +1,958 @@ +# MASTER BUILD PROMPT — Recursive API Company Bootstrap +## For Claude Code | Talos/Kubernetes + RapidAPI + Free Public Data + +--- + +## CONTEXT & CONSTRAINTS + +You are building a production API business from scratch for a developer who: +- Runs a multi-site Talos Kubernetes cluster (homelab + VPS tunnel for static IP) +- Will list APIs on RapidAPI marketplace at age 18 (target launch date ~30 days out) +- Target: $100/mo net profit with <40 hrs/mo maintenance +- Has zero hosting budget (existing infra only) +- PayPal account for RapidAPI payouts goes live on 18th birthday + +Stack: +- Kubernetes with Nginx or Traefik ingress already running +- VPS acts as a WireGuard/tunnel endpoint with static IP +- Node.js or Python (use whichever fits the task better) +- Gitea at `gitea.leeworks.dev` for source control — NOT GitHub +- Gitea Actions for CI (same YAML syntax as GitHub Actions, runs on Gitea runners) +- Flux CD handles ALL Kubernetes deployments — never run `helm upgrade` manually +- Helm charts committed to git; Flux watches and reconciles automatically +- OpenAPI spec (YAML) is the single source of truth for all documentation + +--- + +## PRIME DIRECTIVE + +This is a **recursive build loop**. You will never stop unless explicitly told to. + +The loop is: +``` +RESEARCH → PLAN → SPEC → BUILD → TEST → COMMIT → (Flux deploys) → DOCUMENT → RESEARCH → ... +``` + +When you exhaust your context window on building, switch to research. +When you exhaust research, return to building. +You never idle. You never ask "what should I do next?" — you decide and do it. + +--- + +## PHASE 0 — ORIENTATION (run once at start, then never again) + +Before touching any code, do all of the following: + +1. **Audit existing cluster state** + - `kubectl get nodes` — document node count, roles, resource totals + - `kubectl get namespaces` — identify existing workloads + - `kubectl get ingress -A` — understand current ingress setup + - `kubectl top nodes` — check available headroom + - `flux get kustomizations` — understand what Flux is already managing + - `flux get helmreleases -A` — list Helm releases under Flux control + - Save findings to `./docs/cluster-audit.md` + +2. **Check existing tooling** + - `node --version` + - `python3 --version` + - `helm version` + - `flux version` + - `kubectl config current-context` + - Confirm git remote points to `gitea.leeworks.dev` + - Save to `./docs/tooling.md` + +3. **Create project root and push to Gitea** + ```bash + mkdir -p api-company/{apis,helm,flux,docs,scripts,research,monitoring,openapi} + cd api-company + git init + git remote add origin https://gitea.leeworks.dev/{your-username}/api-company.git + git push -u origin main + ``` + +4. **Bootstrap Flux on this repo** (if not already watching it) + ```bash + flux create source git api-company \ + --url=https://gitea.leeworks.dev/{your-username}/api-company \ + --branch=main \ + --interval=1m \ + --secret-ref=gitea-credentials + + flux create kustomization api-company \ + --source=GitRepository/api-company \ + --path="./flux" \ + --prune=true \ + --interval=5m + ``` + All Flux manifests live in `./flux/`. Flux watches this path and reconciles + HelmRelease, Kustomization, and ConfigMap changes automatically on every commit. + +5. **Internalize the RapidAPI constraints** + - 25% platform fee + ~2% PayPal fee = keep ~73.5 cents per dollar charged + - Payout only via PayPal (18+ required) + - Free tier required on every API + - Recommended 4 tiers: Free / Basic / Pro / Ultra + - Every plan needs a "Requests" object + - Rate limits set per plan, enforced by RapidAPI gateway + - OpenAPI spec upload uses RapidAPI Platform API (Bearer token auth) + +--- + +## REPOSITORY STRUCTURE + +``` +api-company/ +├── apis/ +│ ├── zip-enrichment/ +│ │ ├── src/ ← application code +│ │ ├── scripts/ ← data seeding scripts +│ │ ├── Dockerfile +│ │ └── openapi.yaml ← OpenAPI spec (source of truth) +│ ├── holidays/ +│ │ ├── src/ +│ │ ├── Dockerfile +│ │ └── openapi.yaml +│ └── air-quality/ +│ ├── src/ +│ ├── Dockerfile +│ └── openapi.yaml +├── helm/ +│ ├── zip-enrichment/ ← Helm chart +│ ├── holidays/ +│ ├── air-quality/ +│ └── docs-site/ +├── flux/ +│ ├── sources/ +│ │ └── gitea-api-company.yaml +│ ├── zip-enrichment/ +│ │ ├── helmrelease.yaml ← Flux watches this, deploys automatically +│ │ └── namespace.yaml +│ ├── holidays/ +│ │ ├── helmrelease.yaml +│ │ └── namespace.yaml +│ ├── air-quality/ +│ │ ├── helmrelease.yaml +│ │ └── namespace.yaml +│ ├── docs-site/ +│ │ ├── helmrelease.yaml +│ │ └── namespace.yaml +│ └── monitoring/ +│ ├── helmrelease.yaml +│ └── namespace.yaml +├── docs-site/ ← Astro + Redoc site +├── monitoring/ ← Prometheus, Grafana, Gatus configs +├── docs/ ← Internal docs +├── research/ +├── scripts/ +│ └── publish-openapi.js ← RapidAPI spec upload script +├── .gitea/ +│ └── workflows/ +│ ├── build-and-deploy.yaml +│ ├── publish-openapi.yaml +│ └── rebuild-docs.yaml +├── SESSION_LOG.md +└── STATUS.md +``` + +--- + +## THE OPENAPI-FIRST WORKFLOW + +The OpenAPI spec is written once and drives everything downstream: + +``` +apis/{name}/openapi.yaml + │ + ├──▶ Gitea Actions validates spec on every push (redocly lint) + │ + ├──▶ Gitea Actions uploads to RapidAPI Platform API + │ (updates listing, endpoint docs, example responses automatically) + │ + ├──▶ Redoc container at docs.leeworks.dev/{name} renders it live + │ (same spec, beautiful searchable docs site, self-hosted) + │ + └──▶ Postman collection auto-generated from spec (for testing) +``` + +### OpenAPI spec template + +Create `./apis/{api-name}/openapi.yaml`. Example for ZIP enrichment: + +```yaml +openapi: 3.0.3 +info: + title: ZIP Code Enrichment + # Do NOT write "API" in the title — RapidAPI appends it automatically + description: | + Enrich any US ZIP code with Census demographic data, geographic metadata, + and USPS-verified information. Sourced from the US Census Bureau ACS and + HUD-USPS crosswalk files. Updated monthly. + + ## Data Sources + - US Census Bureau ACS 5-year estimates + - HUD-USPS ZIP crosswalk files + - IANA timezone boundary data + + ## Authentication + All requests must come through the RapidAPI gateway. Direct access is blocked. + version: "1.0.0" + x-rapidapi-category: "Data" + x-rapidapi-subcategory: "Geodata" + +servers: + - url: https://zip.leeworks.dev + +paths: + /lookup: + get: + summary: Enrich a single ZIP code + description: Returns full demographic and geographic data for a US ZIP code. + parameters: + - name: zip + in: query + required: true + schema: + type: string + pattern: '^\d{5}$' + example: "01085" + responses: + "200": + description: Successful enrichment + content: + application/json: + schema: + $ref: '#/components/schemas/ZipResult' + example: + zip: "01085" + city: "Westfield" + state: "MA" + county: "Hampden" + lat: 42.1501 + lon: -72.7495 + timezone: "America/New_York" + population: 41301 + median_income: 58200 + median_age: 40.1 + unemployment_rate: 4.2 + metro_area: "Springfield, MA" + housing_units: 17842 + congressional_district: "MA-01" + data_vintage: "ACS 2023 5-year" + "400": + description: Invalid ZIP code format + "404": + description: ZIP code not found + + /batch: + post: + summary: Enrich up to 100 ZIP codes in one request + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + zips: + type: array + items: + type: string + maxItems: 100 + example: ["01085", "10001", "90210"] + responses: + "200": + description: Array of enriched ZIP results + + /radius: + get: + summary: Find all ZIP codes within N miles of a given ZIP + parameters: + - name: zip + in: query + required: true + schema: + type: string + - name: miles + in: query + required: true + schema: + type: number + minimum: 1 + maximum: 100 + responses: + "200": + description: Array of nearby ZIP codes + + /health: + get: + summary: Health check and data freshness + responses: + "200": + description: Service status + +components: + schemas: + ZipResult: + type: object + properties: + zip: + type: string + city: + type: string + state: + type: string + county: + type: string + lat: + type: number + lon: + type: number + timezone: + type: string + population: + type: integer + median_income: + type: number + median_age: + type: number + unemployment_rate: + type: number + metro_area: + type: string + housing_units: + type: integer + congressional_district: + type: string + data_vintage: + type: string +``` + +--- + +## GITEA ACTIONS CI PIPELINES + +### Pipeline 1 — Build image, update Flux manifest +`.gitea/workflows/build-and-deploy.yaml`: + +```yaml +name: Build and Deploy + +on: + push: + branches: [main] + paths: + - 'apis/**' + - 'helm/**' + +jobs: + detect-changes: + runs-on: ubuntu-latest + outputs: + apis: ${{ steps.filter.outputs.changes }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + zip-enrichment: + - 'apis/zip-enrichment/**' + holidays: + - 'apis/holidays/**' + air-quality: + - 'apis/air-quality/**' + + build: + needs: detect-changes + runs-on: ubuntu-latest + strategy: + matrix: + api: ${{ fromJSON(needs.detect-changes.outputs.apis) }} + steps: + - uses: actions/checkout@v4 + + - name: Build and push Docker image + run: | + IMAGE="registry.leeworks.dev/${{ matrix.api }}:${{ gitea.sha }}" + docker build -t $IMAGE ./apis/${{ matrix.api }} + docker push $IMAGE + + - name: Update Flux HelmRelease image tag + run: | + sed -i "s|tag:.*|tag: ${{ gitea.sha }}|" \ + flux/${{ matrix.api }}/helmrelease.yaml + git config user.email "ci@leeworks.dev" + git config user.name "Gitea Actions" + git add flux/${{ matrix.api }}/helmrelease.yaml + git commit -m "chore: update ${{ matrix.api }} to ${{ gitea.sha }}" + git push + # Flux detects commit → reconciles → rolling update → done + # No helm upgrade needed. Ever. +``` + +### Pipeline 2 — Validate and publish OpenAPI spec to RapidAPI +`.gitea/workflows/publish-openapi.yaml`: + +```yaml +name: Publish OpenAPI to RapidAPI + +on: + push: + branches: [main] + paths: + - 'apis/*/openapi.yaml' + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Validate all OpenAPI specs + run: npx @redocly/cli lint apis/*/openapi.yaml + # Pipeline fails here if spec is invalid. Never publish broken docs. + + - name: Detect changed specs + id: changed + run: | + CHANGED=$(git diff --name-only HEAD~1 HEAD | grep 'openapi.yaml' || true) + echo "CHANGED_FILES=$CHANGED" >> $GITHUB_ENV + + - name: Publish to RapidAPI + env: + RAPIDAPI_KEY: ${{ secrets.RAPIDAPI_PLATFORM_KEY }} + ZIP_API_ID: ${{ secrets.RAPIDAPI_ZIP_API_ID }} + ZIP_VERSION_ID: ${{ secrets.RAPIDAPI_ZIP_VERSION_ID }} + HOLIDAYS_API_ID: ${{ secrets.RAPIDAPI_HOLIDAYS_API_ID }} + HOLIDAYS_VERSION_ID: ${{ secrets.RAPIDAPI_HOLIDAYS_VERSION_ID }} + AQI_API_ID: ${{ secrets.RAPIDAPI_AQI_API_ID }} + AQI_VERSION_ID: ${{ secrets.RAPIDAPI_AQI_VERSION_ID }} + run: node scripts/publish-openapi.js +``` + +### `./scripts/publish-openapi.js` + +```javascript +const fs = require('fs'); +const path = require('path'); +const FormData = require('form-data'); +const fetch = require('node-fetch'); + +const API_MAP = { + 'zip-enrichment': { + apiId: process.env.ZIP_API_ID, + versionId: process.env.ZIP_VERSION_ID, + }, + 'holidays': { + apiId: process.env.HOLIDAYS_API_ID, + versionId: process.env.HOLIDAYS_VERSION_ID, + }, + 'air-quality': { + apiId: process.env.AQI_API_ID, + versionId: process.env.AQI_VERSION_ID, + }, +}; + +async function publishSpec(apiName) { + const { apiId, versionId } = API_MAP[apiName]; + if (!apiId || !versionId) { + console.log(`Skipping ${apiName} — RapidAPI ID not configured yet`); + return; + } + + const specPath = path.join('apis', apiName, 'openapi.yaml'); + const specContent = fs.readFileSync(specPath, 'utf8'); + + const form = new FormData(); + form.append('file', specContent, { + filename: 'openapi.yaml', + contentType: 'application/yaml', + }); + + const url = `https://platformapi1.p.rapidapi.com/v1/apis/${apiId}/versions/${versionId}`; + const response = await fetch(url, { + method: 'PUT', + headers: { + 'x-rapidapi-host': 'platformapi1.rapidapi-x.rapidapi.com', + 'x-rapidapi-key': process.env.RAPIDAPI_KEY, + ...form.getHeaders(), + }, + body: form, + }); + + if (!response.ok) { + throw new Error(`Failed to publish ${apiName}: ${response.status} ${await response.text()}`); + } + console.log(`✓ Published ${apiName} spec to RapidAPI`); +} + +const changedFiles = (process.env.CHANGED_FILES || '').split('\n').filter(Boolean); +const apisToPublish = Object.keys(API_MAP).filter(api => + changedFiles.length === 0 || changedFiles.some(f => f.includes(api)) +); + +Promise.all(apisToPublish.map(publishSpec)) + .then(() => console.log('Done')) + .catch(err => { console.error(err); process.exit(1); }); +``` + +### Pipeline 3 — Rebuild docs site when specs change +`.gitea/workflows/rebuild-docs.yaml`: + +```yaml +name: Rebuild Docs Site + +on: + push: + branches: [main] + paths: + - 'apis/*/openapi.yaml' + - 'docs-site/**' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Copy specs into docs site + run: | + mkdir -p docs-site/public/specs + cp apis/zip-enrichment/openapi.yaml docs-site/public/specs/ + cp apis/holidays/openapi.yaml docs-site/public/specs/ + cp apis/air-quality/openapi.yaml docs-site/public/specs/ + + - name: Build Astro site + run: | + cd docs-site && npm ci && npm run build + + - name: Push docs image and update Flux + run: | + docker build -t registry.leeworks.dev/docs-site:${{ gitea.sha }} ./docs-site + docker push registry.leeworks.dev/docs-site:${{ gitea.sha }} + sed -i "s|tag:.*|tag: ${{ gitea.sha }}|" flux/docs-site/helmrelease.yaml + git config user.email "ci@leeworks.dev" + git config user.name "Gitea Actions" + git add flux/docs-site/helmrelease.yaml + git commit -m "chore: update docs-site to ${{ gitea.sha }}" + git push +``` + +--- + +## FLUX CD MANIFEST TEMPLATES + +### `./flux/zip-enrichment/helmrelease.yaml` + +```yaml +apiVersion: helm.toolkit.fluxcd.io/v2beta1 +kind: HelmRelease +metadata: + name: zip-enrichment + namespace: zip-enrichment +spec: + interval: 5m + chart: + spec: + chart: ./helm/zip-enrichment + sourceRef: + kind: GitRepository + name: api-company + namespace: flux-system + values: + replicaCount: 2 + image: + repository: registry.leeworks.dev/zip-enrichment + tag: latest # Gitea Actions updates this on every push + resources: + requests: + memory: "128Mi" + cpu: "50m" + limits: + memory: "256Mi" + cpu: "200m" + ingress: + enabled: true + host: zip.leeworks.dev + env: + RAPIDAPI_PROXY_SECRET: + valueFrom: + secretKeyRef: + name: rapidapi-secrets + key: proxy-secret +``` + +Repeat for `holidays` (namespace: `holidays`) and `air-quality` (namespace: `air-quality`). + +--- + +## PHASE 1 — FIRST API: US ZIP CODE ENRICHMENT + +### Why this one first +- Data source: US Census Bureau API (free, no key for basic use) +- Secondary: HUD-USPS crosswalk files (free download) +- High demand: shipping, marketing, real estate, fintech +- Low maintenance: Census updates ~annually +- Proven market: competitors charge $49–$299/mo + +### Step 1.1 — Write the OpenAPI spec first +Before any application code, create `./apis/zip-enrichment/openapi.yaml` +using the template above. Validate locally: +```bash +npx @redocly/cli lint apis/zip-enrichment/openapi.yaml +``` +Commit and push. The spec is now the contract — implement to match it. + +### Step 1.2 — Data acquisition & caching + +Build `./apis/zip-enrichment/scripts/seed-data.js`: + +Pull and cache: +- Census Bureau ACS 5-year: `https://api.census.gov/data/2023/acs/acs5` + (population, median income, median age, housing units, unemployment rate) +- HUD-USPS crosswalk: ZIP to county, metro area, census tract +- Timezone data from precomputed IANA/Census boundary CSV + +Store in SQLite at `./apis/zip-enrichment/data/zip.db`, indexed on ZIP for <5ms lookups. +Monthly CronJob re-seeds from source. + +**Failure mode:** Census API down → return stale data with `X-Data-Freshness` header. +Never return a 500 to a paying customer. + +### Step 1.3 — API server + +`./apis/zip-enrichment/src/server.js` — Node.js + Fastify + +Endpoints must match `openapi.yaml` paths exactly: +- `GET /lookup?zip=01085` +- `POST /batch` — body `{ zips: [...] }`, max 100 +- `GET /radius?zip=01085&miles=25` +- `GET /health` + +RapidAPI proxy secret verification on every request: +```javascript +const PROXY_SECRET = process.env.RAPIDAPI_PROXY_SECRET; +fastify.addHook('preHandler', (req, reply, done) => { + if (req.headers['x-rapidapi-proxy-secret'] !== PROXY_SECRET) { + reply.code(403).send({ error: 'Direct access not permitted' }); + return; + } + done(); +}); +``` + +### Step 1.4 — Deploy via Flux + +Create Helm chart at `./helm/zip-enrichment/` and Flux manifest at +`./flux/zip-enrichment/helmrelease.yaml`. Commit and push. +Flux reconciles within 5 minutes. Monitor: +```bash +flux get helmreleases -n zip-enrichment --watch +``` + +Use a PersistentVolumeClaim for the SQLite file so it survives pod restarts. + +### Step 1.5 — Pricing tiers + +| Plan | Price | Requests/mo | Overage | +|-------|---------|-------------|---------------| +| Free | $0 | 100 | Not available | +| Basic | $9/mo | 5,000 | $0.003/req | +| Pro | $19/mo | 25,000 | $0.002/req | +| Ultra | $49/mo | 100,000 | $0.001/req | + +To net $100/mo: need ~$136/mo gross = 15 Basic OR 7 Pro OR 3 Ultra subscribers. + +--- + +## PHASE 2 — SECOND API: PUBLIC HOLIDAYS & BUSINESS DAYS + +### Data sources (free) +- Nager.Date: `https://date.nager.at/api/v3/PublicHolidays/{year}/{countryCode}` +- US Federal Reserve bank holidays (scrape federalreserve.gov) +- NYSE trading days (public calendar) + +### Endpoints +``` +GET /holidays?country=US&year=2025 +GET /is-holiday?country=US&date=2025-12-25 +GET /business-days?country=US&start=2025-01-01&end=2025-01-31 +GET /next-business-day?country=US&date=2025-12-24 +GET /trading-days?exchange=NYSE&year=2025 ← Pro+ only +``` + +### Deployment +Same Gitea Actions + Flux pattern. Namespace: `holidays`. +Data is tiny — rebuild from upstream on pod start, no PVC needed. + +### Pricing +| Free | Basic $5/mo | Pro $15/mo | Ultra $29/mo (unlimited) | + +--- + +## PHASE 3 — THIRD API: AIR QUALITY + POLLEN + +### Data sources (free) +- OpenAQ: `https://api.openaq.org/v3/` +- EPA AirNow: `https://www.airnowapi.org/` +- Open-Meteo: `https://air-quality-api.open-meteo.com/` + +### Endpoints +``` +GET /aqi?lat=42.11&lon=-72.59 +GET /aqi?zip=01085 +GET /forecast?lat=42.11&lon=-72.59&days=3 +GET /pollen?lat=42.11&lon=-72.59 +GET /history?lat=42.11&lon=-72.59&start=2025-01-01&end=2025-01-07 +``` + +--- + +## RECURSIVE RESEARCH PROTOCOL + +When finishing a build phase OR hitting a context limit, run this block: + +``` +SEARCH 1: "RapidAPI most subscribed APIs {current_year}" +SEARCH 2: "developers complaining about {category} API {current_year} reddit" + (rotate: geocoding, business data, weather, finance, legal) +SEARCH 3: "free public dataset API {niche} no existing wrapper" +SEARCH 4: site:rapidapi.com {category} (check subscriber counts) +SEARCH 5: "is there an API for" site:reddit.com +``` + +Append findings to `./research/RESEARCH_LOG.md`: + +```markdown +## Research Session {date} {time} + +### Findings +- [finding]: [URL] [demand: high/medium/low] + +### New API Candidates +1. {name} — {description} — {data source} — {evidence} + +### Decision +Building next: {name} because {reason} +``` + +--- + +## PHASE 4 — MONITORING & OBSERVABILITY + +Deploy via Flux HelmRelease once 2+ APIs are live: + +- Prometheus + Grafana (community Helm charts) +- Instrument with `prom-client` (Node) or `prometheus-client` (Python) +- Metrics per API: `api_requests_total`, `api_response_duration_seconds`, + `api_data_freshness_seconds`, `api_active_subscribers` + +Deploy **Gatus** for public status page at `status.leeworks.dev`: +```yaml +# monitoring/gatus/config.yaml +endpoints: + - name: ZIP Enrichment + url: https://zip.leeworks.dev/health + interval: 30s + conditions: + - "[STATUS] == 200" + - "[RESPONSE_TIME] < 500" + - name: Holidays API + url: https://holidays.leeworks.dev/health + interval: 30s + conditions: + - "[STATUS] == 200" +``` +Gatus is self-hosted and Kubernetes-native — fits the existing stack. +Customers trust APIs with a public status page. This is not optional. + +--- + +## PHASE 5 — DOCUMENTATION SITE + +``` +Stack: Astro + Redoc, self-hosted at docs.leeworks.dev +Deployed via: Flux HelmRelease (same as everything else) +``` + +Redoc renders each `openapi.yaml` as a polished, searchable reference page. +Gitea Actions copies specs into the docs site at build time (Pipeline 3 above). +Docs always match the live API because they come from the same file. + +``` +docs.leeworks.dev/ ← company landing page +docs.leeworks.dev/zip-enrichment ← Redoc renders openapi.yaml +docs.leeworks.dev/holidays +docs.leeworks.dev/air-quality +docs.leeworks.dev/blog/ ← SEO content (not optional) +docs.leeworks.dev/pricing/ ← cross-API comparison +``` + +Blog posts for SEO — write one per API at minimum: +- "How to get Census demographic data by ZIP code in Node.js" +- "Checking if a date is a business day in 30+ countries with one API call" +- "Building an air quality widget for your React app using free EPA data" + +Each post has a working code example using YOUR API and links to the RapidAPI listing. +This is how developers find you on Google before they find you on RapidAPI. + +--- + +## PHASE 6 — COMPANY INFRASTRUCTURE + +### Gitea organization at `gitea.leeworks.dev` +- Create org: `leeworks-apis` (or chosen company name) +- Repos: one per API + `helm-charts` + `docs-site` + `flux-config` +- All CI: Gitea Actions (`.gitea/workflows/*.yaml`) +- All deploys: Flux watching `gitea.leeworks.dev` + +### Gitea Actions runner (deploy in cluster if not running) +```yaml +# flux/gitea-runner/helmrelease.yaml +apiVersion: helm.toolkit.fluxcd.io/v2beta1 +kind: HelmRelease +metadata: + name: gitea-runner + namespace: gitea-runner +spec: + chart: + spec: + chart: gitea-act-runner + sourceRef: + kind: HelmRepository + name: gitea + values: + config: + runner: + labels: + - "ubuntu-latest:docker://node:20" +``` + +### DNS (all routed through VPS tunnel) +``` +zip.leeworks.dev → ZIP enrichment API +holidays.leeworks.dev → Holidays API +aqi.leeworks.dev → Air quality API +docs.leeworks.dev → Documentation + Redoc +status.leeworks.dev → Gatus status page +gitea.leeworks.dev → Already running ✓ +registry.leeworks.dev → Container registry +``` + +### Legal (prepare before 18th birthday) +`./docs/legal/terms-of-service.md`, `privacy-policy.md`, `acceptable-use-policy.md` + +--- + +## PHASE 7 — LAUNCH SEQUENCE (18th birthday) + +```bash +# 1. Verify Flux is healthy +flux get helmreleases -A +# All should show READY=True RECONCILING=False + +# 2. Smoke test each API +curl https://zip.leeworks.dev/health +curl https://holidays.leeworks.dev/health +curl https://aqi.leeworks.dev/health + +# 3. Verify OpenAPI specs are showing on RapidAPI +# (check each listing's Endpoints tab — must match your openapi.yaml) + +# 4. Link PayPal to RapidAPI (manual) +# rapidapi.com → avatar → Personal Payouts → Link PayPal + +# 5. Enable paid tiers on all RapidAPI listings (manual) +# Monetize tab → enable Basic / Pro / Ultra plans + +# 6. Post announcements +# - Hacker News: "Show HN: I built 3 APIs on free Census/EPA data" +# - r/webdev, r/SideProject, r/learnprogramming +# - Indie Hackers +# - Product Hunt (schedule in advance) + +# 7. Email waitlist from 30-day build window +``` + +--- + +## RECURSIVE LOOP RULES — READ EVERY SESSION + +1. **Read `SESSION_LOG.md` and `STATUS.md` first.** Always. No exceptions. + +2. **Read `./research/RESEARCH_LOG.md`** to understand decisions already made. + +3. **Check cluster state before writing any deployment code:** + ```bash + flux get helmreleases -A + kubectl get deployments -A + ``` + +4. **Never run `helm upgrade` manually.** Commit to git, let Flux reconcile. + Manual helm commands cause drift and break the GitOps workflow. + +5. **OpenAPI spec before code.** Write and validate the spec entry for any new + endpoint before implementing the handler. Docs drift is a trust killer. + +6. **Prioritize:** + - Fix broken things first (`flux get all -A` for errors) + - Deploy nearly-finished things second + - Build new things third + - Research when blocked or between phases + +7. **End every session by appending to `SESSION_LOG.md`:** + ```markdown + ## Session {timestamp} + ### Completed + - ... + ### In progress + - ... + ### Next session starts with + - ... + ``` + +8. **The goal is $100/mo net.** If it doesn't move toward revenue, it's optional. + +9. **When truly stuck**: run the research protocol. There is always another API. + +--- + +## STATUS TRACKER — `./STATUS.md` + +```markdown +# Company Status + +## APIs +| API | Spec | Code | Deployed | Listed on RapidAPI | Paying Users | MRR | +|----------------|------|------|----------|--------------------|--------------|-----| +| ZIP Enrichment | [ ] | [ ] | [ ] | [ ] | 0 | $0 | +| Holidays | [ ] | [ ] | [ ] | [ ] | 0 | $0 | +| Air Quality | [ ] | [ ] | [ ] | [ ] | 0 | $0 | + +## Infrastructure +- Cluster nodes: +- Flux healthy: Y/N +- Gitea Actions runner: UP/DOWN +- VPS tunnel: UP/DOWN +- Site 3 expansion: IN PROGRESS / DONE + +## Revenue +- Gross MRR: $0 +- Net MRR (after ~26.5% fees): $0 +- Target: $100/mo net +- Gap: $100 + +## Next action +{one sentence — exactly what to do first next session} +``` + +--- + +*This prompt is intentionally never complete. More APIs, more research, more revenue.* +*The loop ends when you say it ends — not before.* diff --git a/README.md b/README.md new file mode 100644 index 0000000..503e82f --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# api-company + +Meta-repo for the `leeworks-apis` recursive build loop. This repo holds cross-cutting concerns; the actual APIs each live in their own repo. + +## Sibling repos + +| Repo | Purpose | Status | +|---|---|---| +| [leeworks-agents/zip-enrichment](https://gitea.leeworks.dev/leeworks-agents/zip-enrichment) | US ZIP code enrichment (Census ACS + HUD-USPS) | Phase 1 | +| [leeworks-agents/holidays](https://gitea.leeworks.dev/leeworks-agents/holidays) | Public holidays and business days | Phase 1 | +| [leeworks-agents/air-quality](https://gitea.leeworks.dev/leeworks-agents/air-quality) | AQI + pollen + forecast | Phase 1 | + +## What lives here + +- `MASTER_BUILD_PROMPT.md` — the prime directive driving the build loop +- `ROADMAP.md` — cross-repo milestones (Phase 0–6) +- `STATUS.md` — living revenue + infra scorecard +- `SESSION_LOG.md` — append-only log of agent work sessions +- `research/RESEARCH_LOG.md` — market research findings, candidate API ideas +- `flux/` — cluster-level Flux manifests (registry, runner, monitoring, docs-site) +- `monitoring/` — Prometheus, Grafana, Gatus configs +- `docs-site/` — Astro + Redoc site at `docs.leeworks.dev` + +## How this gets worked on + +The `agent-company` Kubernetes deployment in `0xWheatyz/Talos` runs three crons every 5 hours: + +- `/manager` discovers `agent-ready` issues here and in each API repo, fans out `@repo-manager` subagents +- `/sprint` reads `ROADMAP.md` and breaks down the next phase into new issues +- `/ship` merges approved PRs and creates upstream PRs to `0xWheatyz` forks + +Goal: $100/mo net revenue via RapidAPI marketplace listings. diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..c3a991b --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,50 @@ +# api-company Roadmap + +Cross-repo milestones. Per-API phases live in each API repo's ROADMAP.md. + +## Phase 0 — Orientation (one-time) +- [ ] Audit cluster: nodes, namespaces, ingress, Flux state → `docs/cluster-audit.md` +- [ ] Add Flux GitRepository + Kustomization watching `leeworks-agents/api-company` +- [ ] Deploy `gitea-act-runner` in cluster via Flux HelmRelease (`flux/gitea-runner/`) +- [ ] Stand up `registry.leeworks.dev` (container registry) — required before any API CI works + +## Phase 1 — API Contracts +Each API repo writes its `openapi.yaml` first and lints it. Spec is the source of truth. +- See: `leeworks-agents/zip-enrichment`, `holidays`, `air-quality` + +## Phase 2 — Data +Per-API: pull free public data sources, cache locally (SQLite or in-memory), schedule monthly re-seed. + +## Phase 3 — Servers +Per-API: implement Fastify/FastAPI service matching `openapi.yaml` exactly. RapidAPI proxy-secret middleware on every route. + +## Phase 4 — Monitoring +- [ ] Prometheus + Grafana via Flux HelmRelease (`flux/monitoring/`) +- [ ] Gatus public status page at `status.leeworks.dev` +- [ ] Instrument every API with `api_requests_total`, `api_response_duration_seconds`, `api_data_freshness_seconds` + +## Phase 5 — Documentation site +- [ ] `docs-site/` Astro + Redoc skeleton +- [ ] Gitea Actions pipeline that copies per-API `openapi.yaml` files at build time +- [ ] Deploy to `docs.leeworks.dev` via Flux +- [ ] One SEO blog post per API minimum (see master prompt §Phase 5) + +## Phase 6 — Pre-launch +- [ ] `docs/legal/terms-of-service.md` +- [ ] `docs/legal/privacy-policy.md` +- [ ] `docs/legal/acceptable-use-policy.md` +- [ ] DNS: `zip.leeworks.dev`, `holidays.leeworks.dev`, `aqi.leeworks.dev`, `docs.`, `status.`, `registry.` +- [ ] PayPal linked to RapidAPI (manual, on 18th birthday) +- [ ] Paid tiers enabled on RapidAPI listings (manual) + +## Revenue target + +$100/mo **net** ≈ $136/mo gross after RapidAPI 25% + PayPal ~2%. + +| Plan baseline | Subs needed for net $100/mo | +|---|---| +| 15 Basic @ $9 | $122 net | +| 7 Pro @ $19 | $97 net | +| 3 Ultra @ $49 | $107 net | + +(Per-API rough math; mix-and-match across the 3 APIs.) diff --git a/SESSION_LOG.md b/SESSION_LOG.md new file mode 100644 index 0000000..c9d5f40 --- /dev/null +++ b/SESSION_LOG.md @@ -0,0 +1,22 @@ +# Session Log + +Append-only. Every agent session ends by adding an entry. + +--- + +## Session 2026-05-18 — bootstrap + +### Completed +- Created 4 repos in `leeworks-agents` org: `api-company`, `zip-enrichment`, `holidays`, `air-quality` +- Created labels on each: `agent-ready`, `roadmap`, `blocked`, `phase-0` through `phase-6` +- Scaffolded initial commits with README, ROADMAP, STATUS, SESSION_LOG, MASTER_BUILD_PROMPT +- Per-API repos seeded with stub `openapi.yaml`, ROADMAP, README, empty Dockerfile +- Filed 15 seed issues across the 4 repos (5 + 4 + 3 + 3) + +### In progress +- Awaiting first `/manager` cron tick (or manual trigger) to dispatch `@repo-manager` subagents + +### Next session starts with +- Read `STATUS.md` and `ROADMAP.md` +- Run Phase-0 cluster audit issue (#1 in this repo) +- If Phase-0 is blocked, jump to a per-API repo and work the `phase-1` openapi.yaml issue diff --git a/STATUS.md b/STATUS.md new file mode 100644 index 0000000..58a7a5f --- /dev/null +++ b/STATUS.md @@ -0,0 +1,26 @@ +# Company Status + +_Last updated: 2026-05-18 (bootstrap)_ + +## APIs +| API | Spec | Code | Deployed | Listed on RapidAPI | Paying Users | MRR | +|----------------|------|------|----------|--------------------|--------------|-----| +| ZIP Enrichment | [ ] | [ ] | [ ] | [ ] | 0 | $0 | +| Holidays | [ ] | [ ] | [ ] | [ ] | 0 | $0 | +| Air Quality | [ ] | [ ] | [ ] | [ ] | 0 | $0 | + +## Infrastructure +- Cluster nodes: 3 control plane (10.0.1.3, .4, .5) + workers (testing1) +- Flux healthy: TBD — see Phase-0 issue #1 +- Gitea Actions runner: DOWN (Phase-0 issue #3) +- VPS tunnel: TBD +- Container registry: NOT DEPLOYED (Phase-0 issue #4) + +## Revenue +- Gross MRR: $0 +- Net MRR (after ~26.5% fees): $0 +- Target: $100/mo net +- Gap: $100 + +## Next action +Phase-0 cluster audit (issue #1 in this repo). Until Flux is confirmed watching this org and the runner+registry are up, no API CI can land. diff --git a/docs-site/.gitkeep b/docs-site/.gitkeep new file mode 100644 index 0000000..6aa55af --- /dev/null +++ b/docs-site/.gitkeep @@ -0,0 +1 @@ +# placeholder — populated by Phase-4/5 issues diff --git a/flux/.gitkeep b/flux/.gitkeep new file mode 100644 index 0000000..6aa55af --- /dev/null +++ b/flux/.gitkeep @@ -0,0 +1 @@ +# placeholder — populated by Phase-4/5 issues diff --git a/monitoring/.gitkeep b/monitoring/.gitkeep new file mode 100644 index 0000000..6aa55af --- /dev/null +++ b/monitoring/.gitkeep @@ -0,0 +1 @@ +# placeholder — populated by Phase-4/5 issues diff --git a/research/RESEARCH_LOG.md b/research/RESEARCH_LOG.md new file mode 100644 index 0000000..25a4dea --- /dev/null +++ b/research/RESEARCH_LOG.md @@ -0,0 +1,29 @@ +# Research Log + +Append-only. Every research session adds an entry using the template below. + +## Template + +```markdown +## Research Session {YYYY-MM-DD HH:MM} + +### Searches run +1. RapidAPI most subscribed APIs {year} +2. Developers complaining about {category} API {year} reddit +3. Free public dataset API {niche} no existing wrapper +4. site:rapidapi.com {category} +5. "is there an API for" site:reddit.com + +### Findings +- {finding}: {URL} [demand: high/medium/low] + +### New API Candidates +1. {name} — {description} — {data source} — {evidence} + +### Decision +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.)_