Initial scaffolding committed by bootstrap script. See ROADMAP.md for phase plan and SESSION_LOG.md (api-company only) for context.
28 KiB
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.devfor 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 upgrademanually - 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:
-
Audit existing cluster state
kubectl get nodes— document node count, roles, resource totalskubectl get namespaces— identify existing workloadskubectl get ingress -A— understand current ingress setupkubectl top nodes— check available headroomflux get kustomizations— understand what Flux is already managingflux get helmreleases -A— list Helm releases under Flux control- Save findings to
./docs/cluster-audit.md
-
Check existing tooling
node --versionpython3 --versionhelm versionflux versionkubectl config current-context- Confirm git remote points to
gitea.leeworks.dev - Save to
./docs/tooling.md
-
Create project root and push to Gitea
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 -
Bootstrap Flux on this repo (if not already watching it)
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=5mAll Flux manifests live in
./flux/. Flux watches this path and reconciles HelmRelease, Kustomization, and ConfigMap changes automatically on every commit. -
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:
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:
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:
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
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:
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
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:
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=01085POST /batch— body{ zips: [...] }, max 100GET /radius?zip=01085&miles=25GET /health
RapidAPI proxy secret verification on every request:
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:
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:
## 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) orprometheus-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:
# 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)
# 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)
# 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
-
Read
SESSION_LOG.mdandSTATUS.mdfirst. Always. No exceptions. -
Read
./research/RESEARCH_LOG.mdto understand decisions already made. -
Check cluster state before writing any deployment code:
flux get helmreleases -A kubectl get deployments -A -
Never run
helm upgrademanually. Commit to git, let Flux reconcile. Manual helm commands cause drift and break the GitOps workflow. -
OpenAPI spec before code. Write and validate the spec entry for any new endpoint before implementing the handler. Docs drift is a trust killer.
-
Prioritize:
- Fix broken things first (
flux get all -Afor errors) - Deploy nearly-finished things second
- Build new things third
- Research when blocked or between phases
- Fix broken things first (
-
End every session by appending to
SESSION_LOG.md:## Session {timestamp} ### Completed - ... ### In progress - ... ### Next session starts with - ... -
The goal is $100/mo net. If it doesn't move toward revenue, it's optional.
-
When truly stuck: run the research protocol. There is always another API.
STATUS TRACKER — ./STATUS.md
# 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.