@@ -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. *