What it does
Veyra Radar is an AI evidence engine for identifying high-quality, legitimate remote job opportunities. It ingests postings from public APIs (Remotive, RemoteOK, Jobicy) and ATS pages (AshbyHQ, Greenhouse, Lever, Workable), then scores each posting in real time across six transparent trust dimensions — reality, recruiter activity, company credibility, content quality, global remote-fit, and evidence strength — multiplied by a deterministic safety gate that locks known scam patterns at zero. Every score is paired with a per-posting evidence ledger showing exactly which signals fired and why.
Two optional second-opinion layers can be activated when the user supplies their own API keys: a multi-agent dialectic debate (Advocate / Skeptic / Judge, powered by Google Gemini) and a Google Safe Browsing v4 URL reputation check. Both are bring-your-own-key: keys live only in the browser session and are never persisted server-side.
Built for global candidates — especially those applying from outside the US and EU — who need verified roles, not misleading or low-quality postings.
How we built it
The engine is deterministic at its core and AI-augmented at the edges.
The trust score is a transparent linear combination of six dimensions, gated by a safety multiplier:
$$ \text{TrustScore} = 100 \times \left( \sum_{i=1}^{6} w_i \cdot d_i \right) \times \text{SafetyGate} $$
- dᵢ are the six dimensions
- wᵢ are weights declared in
weights.yamlas the single source of truth - SafetyGate ∈ {0.0, 0.5, 0.8, 1.0}, set to 0.0 whenever a kill-switch pattern matches (upfront fee, banking-before-offer, identity-document harvesting, reshipping, crypto deposit)
Ingestion parses JSON-LD JobPosting schemas from public job APIs
and ATS pages, falling back to DOM heuristics. Cloudflare-protected
platforms (Indeed, LinkedIn, Glassdoor, ZipRecruiter) gracefully fall
back to manual paste with reference archetypes.
Company-domain enrichment is a three-tier cascade: curated map →
Wikidata SPARQL → ATS-slug probing (a slug like acme-ai is probed
against acme.ai before falling back to .com / .io).
The optional Dialectic Multi-Agent Debate (Google Gemini) layers
an Extractor → Advocate → Skeptic → Judge pipeline under strict
Python guardrails. The LLM can adjust the score within ±15 points and
raise a human_review_needed flag, but it can never override the
safety gate or silently downgrade a deterministic verdict.
Security is bring-your-own-key by design. Keys live in
st.session_state (per-browser-session isolation), never in the global
settings singleton (which is shared across sessions on a Streamlit
worker). Safe Browsing requests send the key in the X-Goog-Api-Key
header — never in the URL query string — so it never lands in proxy
logs or SDK exception traces. Any free-text error message is
regex-redacted before reaching the UI.
Stack: Python 3.11+ · Streamlit · Pydantic v2 · Google Gemini SDK · Google Safe Browsing v4 · pytest · Apache 2.0.
Challenges we ran into
The hardest bugs were the ones where a single transient external failure created permanent corrupted state:
- Cache poisoning by transient network errors. When a slug-probe
or Wikidata SPARQL call timed out, the result
Nonewas being cached for the rest of the process — silently breaking company-domain resolution. Fixed by caching only authoritative answers; transient failures now retry on the next call. - LLM verdict drift. The Judge agent could return
UNKNOWN_INSUFFICIENT_EVIDENCEeven when the deterministic engine had emittedLEGIT_BUT_REMOTE_RESTRICTED, producing a contradictory "score 73 + UNKNOWN" UI. Fixed with a Python guardrail that preserves the deterministic label and raiseshuman_review_neededinstead. - Transient timeouts becoming SCAM_RISK. A one-minute Cloudflare
hiccup would persist a
SCAM_RISKlabel in batch outputs for the rest of the day. Fixed by making the URL probe three-state (alive/verified-dead/probe-failed) and only firing the kill-switch onverified-dead. - Cross-session key leakage. The Streamlit
settingsmodule is a singleton shared across browser sessions on a worker. Writing user-pasted keys tosettings.gemini_api_keywould leak them to every other session's call. Fixed by removing everysettings.*write and threadingapi_keyas an explicit per-call argument through the agents and the Safe Browsing client.
Each of these became a permanent regression test in
tests/test_regression_guardrails.py, so the same class of bug
can't recur.
Accomplishments that we're proud of
- A working live deployment, not a slide-deck prototype: the deterministic engine scores every posting without any external key, and the optional AI layers activate with the user's own.
- A zero-leak credential architecture: keys never reach disk, never the global singleton, never logs, never URL query strings — verified by an automated CI job that scans every commit for leaked API-key shapes.
- Hard guardrails on LLM authority: no agent, however persuasive, can override a deterministic kill-switch or silently downgrade a verified posting — every guardrail is a Python invariant with a regression test, not a prompt instruction the model could ignore.
- Honest UX about uncertainty: when evidence is ambiguous the engine surfaces "human review needed" instead of fabricating false confidence — judges and candidates alike see exactly what the system does and does not know.
What we learned
- A pure-LLM trust score is dangerous. A model can be manipulated, hallucinate, or contradict its own logic. The right architecture is a deterministic engine as the source of truth, with the LLM as a constrained second opinion under hard guardrails — never the other way around.
- Transparency builds trust faster than accuracy. Showing every signal that fired — even when the signals contradict each other — is what makes a candidate confident enough to act. A black-box "73/100" without receipts is worse than useless.
- Security has to be architected in, not bolted on. A late-stage attempt to add BYOK to a system that mutates global state would have leaked keys across sessions. We designed the credential flow from day one to thread per-call, with the global singleton intentionally inaccessible from the agents.
What's next for veyra-radar
- Browser extension that scores postings inline on Indeed, LinkedIn, Glassdoor, and ZipRecruiter — so users see Veyra's verdict in the same tab where they're already job-hunting.
- Embeddable Veyra Score badge that any ethical job board can
drop into their listings via a single
<script>tag — turning trust transparency into a competitive feature for the aggregators themselves. - Recruiter-side verification: employers can sign their postings with a Veyra-issued attestation, giving legitimate roles a cryptographic provenance signal that scammers can't fake.
- Expanded language coverage: French, German, Italian, Japanese, and Mandarin quality heuristics, so the engine works for candidates across every major remote-job market.
- Community labeling loop: candidates who actually applied to a posting can confirm or contest the outcome — feeding back into a growing gold set that the engine continuously calibrates against.
Built With
- docker
- github-actions
- google-gemini
- google-safe-browsing
- json-ld
- markdown
- playwright
- plotly
- pydantic
- pytest
- python
- streamlit
- streamlit-community-cloud
- wikidata
Log in or sign up for Devpost to join the conversation.