Part 1 established the thesis: trust is the product. This paper describes how that thesis is implemented — an async FastAPI backend, a Next.js frontend, an SQLAlchemy data model built around a natural bounty key, and a pure-function trust engine that is the moat. We cover the layered scraping pipeline, the security posture, and how each trust rule is validated by unit tests and by a live re-probe against a real farm.
01System architecture
GitScout is a two-tier application deployed across Render (backend) and Vercel (frontend):
Next.js (App Router, TS) ──HTTP──▶ FastAPI (async) ──▶ SQLAlchemy async ──▶ Postgres (Neon) / SQLite
Vercel Render (SSL: require)
Backend layout (backend/app/):
api/v1/— versioned REST surface:bounties,issues,triage,notifications,billing,health, wired throughrouter.py.scrapers/— the ingestion pipeline:github_client,orchestrator,bounty_extractor,classifier,domain_registry, and the trust enginebounty_trust.py.triage/— issue intelligence:ast_localizer(free, deterministic AST triage),llm_engine/enhancer(optional LLM enrichment),repro_generator,fix_planner.security/—headers.pyandrate_limiter.py.dispatcher/— multi-channel notification (Discord, email, Telegram, WhatsApp).models/,schemas/— ORM models and Pydantic schemas kept deliberately separate.
02Data model
The model is designed around a natural, collision-proof key rather than a surrogate id:
class Issue(Base):
__tablename__ = "issues"
# Natural PK: "repo_owner/repo_name#issue_number"
id: Mapped[str] = mapped_column(String(255), primary_key=True, index=True)
...
has_bounty: Mapped[bool] = mapped_column(Boolean, default=False, index=True)
bounty_amount_usd: Mapped[Optional[float]] = mapped_column(Float, nullable=True, index=True)
bounty_source: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
bounty_url: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
hourly_roi: Mapped[Optional[float]] = mapped_column(Float, nullable=True, index=True)
id = "owner/name#number"makes re-ingestion idempotent — the same GitHub issue always maps to the same row, so scrapes can run repeatedly without duplicating data.- Bounty fields are nullable and indexed. An issue is an issue first; a bounty is an optional overlay.
bounty_amount_usdandhourly_roiare indexed because they are the primary sort keys of the product ("show me the highest-ROI real bounties"). bounty_sourceandbounty_urlpreserve provenance — every bounty links back to its source of truth, a trust requirement, not a nicety.- Async everywhere.
create_async_enginewithpool_pre_pingandpool_recycle=300; the connection layer switchesssl: requirefor Postgres/Neon andcheck_same_thread: Falsefor SQLite so the same code runs in tests and in production.
03The trust engine (bounty_trust.py)
This is the moat. It is a pure module — no network, no I/O — so it is fast, free, and exhaustively unit-testable. It runs before any reputation call. Three layers of defense, in order:
bounty-plaza and its owner account are permanently blocked. No heuristics, no appeal.MIN_BOUNTY_REPO_STARS = 50).tscircuit, activepieces, onyx-dot-app, …).The engagement / vanity-token gate
The subtle case is a repo that clears the star gate but still pays nothing real — it rewards social engagement with vanity tokens. The gate encodes exactly that shape:
_SOCIAL_TASK_RE— "star the repo," "join our Discord," "follow on X," "post a video."_REAL_CURRENCIESvs_NON_TOKEN_UNITS/_REWARD_TOKEN_RE— distinguishes$,USD,€from made-up point tokens ("RTC", "points")._BOUNTY_TAG_RE— crucially, the token check only fires inside a bounty/pool context, so a normal repo that merely mentions a token name is not misclassified.
04Scraping pipeline correctness
Two correctness fixes shaped the pipeline:
- Real cash lives behind platform markers, not the raw label. The orchestrator queries
algora.io in:body,Funding on Polar in:body, and💎 Bounty— not barelabel:bounty, which is ~40% farm noise. - Separate GitHub-valid passes, then merge (
d51ea96). A single over-combined search query can be silently rejected by the GitHub Search API and return zero results. Running each qualifier as its own valid pass and merging makes discovery robust.
05Validation
The trust engine ships with 11 passing unit tests (test_bounty_trust.py), including the three that pin the engagement gate's contract:
test_engagement_bounty_detection— a star-for-tokens farm is rejected.test_engagement_filter_spares_real_cash_bounties— a genuine$-denominated bounty is not rejected (the safety property).test_prefilter_rejects_engagement_farms— end-to-end throughprefilter_farm.
Scottcjn/rustchain-bounties, paying "RTC" tokens for stars) admitted 20 candidates before the gate — and 0 after.Beyond unit tests, the gate was validated by that live re-probe against a real farm — the difference between a rule that passes a synthetic test and one that works on the live web. The wider backend carries a full suite (test_api_*, test_scrapers, test_security, test_ast_localizer, test_dispatcher, test_health), so the trust engine is validated in the context of the running API, not in isolation.
06Security posture
- Dedicated
security/layer — security headers (headers.py) and a rate limiter (rate_limiter.py), tested bytest_security.py. - Transport security —
ssl: requireenforced for Postgres/Neon connections. - Secret hygiene — a prior incident (real credentials pasted in plaintext into hand-off docs, not committed to git) drove a redaction-and-logging fix and a rotation task. The lesson is documented so it is not repeated: secrets never live in docs, and key material is redacted in logs.
- Anti-abuse as a first-class concern — the trust engine itself is a security control: it stops GitScout from becoming a distribution channel for a crypto-deposit scam. That is a security property, not merely a data-quality one.
07What this buys the product
The architecture makes the thesis operational: a small, trustworthy feed produced by a cheap, testable, layered filter, with provenance on every row. Part 3 → shows what happens when that engine meets the real bounty market — and why the honest outcome was to not ship a fabricated win.
Code references are to the live backend/app/ tree.