Autonomous 24/7 opportunity mapper. Discord bot identity: Hop.
Autonomous 24/7 job / internship / fellowship / freelance pipeline. Runs on a Raspberry Pi 5 (DietPi, ARM64, 8GB). Crawls 28+ sources, ranks against the user profile, delivers a daily Discord digest, supports apply / skip / snooze via buttons + slash commands, tracks outcomes via Gmail IMAP, learns from feedback.
Success metric: first paid internship OR first paid freelance gig within 30 days of go-live.
Authoritative documents:
CLAUDE.md— working summary every dev session should read first./home/lakshit_gupta/.claude/plans/virtual-splashing-pine.md— full plan with rationale.
cartograph/
├── pyproject.toml uv-managed deps
├── compose.yaml Docker Compose stack
├── secrets.yaml.example SOPS template (encrypt to secrets.yaml on the Pi)
├── Makefile make help
├── docker/ Dockerfiles + postgres config + init SQL
├── migrations/ V001..V005 SQL migrations
├── config/ profile, sources YAMLs, LLM prompts, routing rules
├── src/
│ ├── common/ db / queue / types / identity_vault / llm / metrics / logger / secrets
│ ├── fetchers/ http (T0) / flaresolverr (T1) / browser (T2 = camoufox)
│ ├── extractors/ T0 regex / T1 selectors / T2 LLM / dedup
│ ├── sources/ ats / rss / github_markdown / hn / reddit / fellowship / india / freelance
│ ├── ranker/ formula + embeddings + llm_rerank + feedback
│ ├── notifiers/ discord (23 slash commands, embeds, handlers) / obsidian / email
│ ├── gmail_watcher/ IMAP IDLE + LLM classifier + state writer + Upwork parser
│ ├── application/ resume_tailor / cover_letter / sender
│ ├── workers/ scheduler / crawler / extractor / ranker / notifier / gmail / identity_warmup
│ ├── api/ FastAPI (/health, /metrics, /admin)
│ └── cli/ `mp` admin command (migrate, seed-sources, sources, identity, opps)
├── scripts/ bootstrap.sh / backup.sh / restore_drill.sh / pretest_cf.sh
├── docs/runbooks/ pi_recovery.md / source_quarantine.md / identity_ban.md
└── grafana/dashboards/ agent_jobs.json
Two non-negotiables:
- No cross-subsystem imports. Subsystems talk via Redis Streams.
src/common/is the only universal import target. - Power-fail safety. Postgres
synchronous_commit=on,full_page_writes=on, WAL archive every 5 min. Redisappendfsync everysec. No UPS — durability config replaces it. Do not weaken.
Important: do not run these on a non-Pi machine. Several steps (swap, fsck,
/mnt/storagepaths, port reservations) assume the deployment host.
# 1. Clone
git clone <repo> /home/$USER/coding/cartograph
cd /home/$USER/coding/cartograph
# 2. Host prep — swap, fonts, WAL dirs, fsck flag
CARTOGRAPH_PI_CONFIRM=1 sudo bash scripts/bootstrap.sh
# 3. SOPS + secrets
age-keygen -o ~/.config/sops/age/keys.txt
# Get pubkey (last line of file output above) → use it below
cp secrets.yaml.example secrets.yaml
$EDITOR secrets.yaml # fill in all REPLACE values
sops --encrypt --age <pubkey> --in-place secrets.yaml
# 4. Profile assets (user-owned)
$EDITOR config/profile/resume.json
$EDITOR config/profile/skills.yaml
$EDITOR config/profile/comp_floors.yaml
$EDITOR config/profile/prefs.yaml
# 5. Build + start
make up # sops exec-env + docker compose up -d
# 6. DB schema + seed
make migrate
make seed
# 7. Verify
docker compose ps # all services Up
curl -s http://localhost:9090/health
curl -s http://localhost:9090/metrics | head- Read the daily digest in
#📰-daily-digest(or run/digest now). - Click Apply / Skip / Snooze / Pin on each opportunity.
- Watch freelance speed lane in
#⚡-priority-push. Edit the LLM-drafted proposal in the modal, then Send. - Glance at
#📬-responsesand#🎤-interviewsdaily. - 9pm IST: if
applied_today < 5, you'll be @mentioned in#🔔-alerts.
Useful CLI:
docker compose run --rm tools python -m src.cli.main opps recent --limit 30
docker compose run --rm tools python -m src.cli.main sources list
docker compose run --rm tools python -m src.cli.main identity status
docker compose run --rm tools python -m src.cli.main identity gen-master-key/budget set|today|status
/digest now|preview|schedule
/apply <opp_id>
/skip <opp_id>
/snooze <opp_id> <days>
/pin <opp_id>
/status
/source list|pause|resume|add
/identity status|update
/cost today|cap
/followup <opp_id>
/explain <opp_id>
/export <range>
/review # v2 — dark-source candidates
See CLAUDE.md § Verification. Hard pass criteria:
- Power-cut → Postgres
pg_amcheckclean. - Redis AOF replay clean.
bash scripts/restore_drill.shpasses.cf_clearance_solve_rate > 0.7after 10 hits to a CF source.- End-to-end happy path: Greenhouse → opp row → score row → embed posted → Apply click → application row + applied thread.
- Reaction handler matches button handler.
/status,/budget today 30,/source listwork.- Gmail watcher transitions opp state on rejection email.
- 9pm nudge fires when applied_today < target.
- Existing Prometheus scrapes our
/metrics; Grafana panels render. - Cost cap refuses next LLM call when inflated past $3/day.
- Banning one identity cascades sibling quarantine within 5min.
| # | Item | Window |
|---|---|---|
| 1 | Done | |
| 2 | OpenRouter API key | Day -1 |
| 3 | New Discord application + bot + invite to JOBS category only | Day -3 |
| 4 | Cloudflare Email Routing catch-all + aliases (jobs+wellfound@, etc.) | Day -2 |
| 5 | Master resume → JSON at config/profile/resume.json |
Day -3 → 0 |
| 6 | Skill matrix YAML at config/profile/skills.yaml |
Day -3 → 0 |
| 7 | Comp floors at config/profile/comp_floors.yaml |
Day -3 → 0 |
| 8 | Identity warmup on platforms (10 min/day, real clicks) | Day -3 → 0 |
| 9 | Resend signup + verify sender domain | Day -2 |
| 10 | Google Cloud project + Gmail OAuth client (or app passwords) | Day -2 |
| 11 | Telegram api_id / api_hash |
Before Day 13 |
| 12 | Worker Gmail inbox (e.g. upwork-worker@yourdomain.tld) + app password |
Before Day 13 |
Until first paid earnings: one new feature per week max. Only features that demonstrably improve apply rate or response rate are allowed. Phase 2 / 3 / 4 / 5 roadmaps live in CLAUDE.md.