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.
This commit is contained in:
@@ -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.*
|
||||||
@@ -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.
|
||||||
+50
@@ -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.)
|
||||||
@@ -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
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# placeholder — populated by Phase-4/5 issues
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# placeholder — populated by Phase-4/5 issues
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# placeholder — populated by Phase-4/5 issues
|
||||||
@@ -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.)_
|
||||||
Reference in New Issue
Block a user