OpenRung
A free, privacy-first relay network that helps you reach the open internet. Open source and nonprofit-run.
Check a job posting or recruiter message for ghost-job risk and scam risk. Every score shows the facts and sources behind it.
SecurityTypeScript · CSSGitHub @ZynzTehrOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
JobSeance checks job postings and recruiter messages for ghost-job and scam risk. It verifies roles on employers' own job boards, matches messages against FTC and FBI warning signs, and shows the evidence behind every score. Sign in with Google or GitHub to track applications and report scams.
Loading the full README.
Read the full README ↗JobSeance checks a job posting or recruiter message for two risks: ghost-job risk (is the role really open?) and scam risk (is the recruiter real?). Every score shows the facts and sources behind it. Every check needs an account (username + password, Google or GitHub): the landing page is the login form and the scanner lives at /scan.
Built with Next.js 16.4 (App Router), Prisma 7.10, Neon Postgres, Passport.js and Tailwind CSS. Hosted on Vercel.
main (production) and dev (local and previews)Install dependencies. This also generates the Prisma client.
npm install
Copy the example environment file and fill it in. Use .env, not .env.local, because the Prisma CLI reads only .env.
cp .env.example .env
| Variable | Local value |
|---|---|
DATABASE_URL | Neon dev branch, pooled URL (host contains -pooler) |
DATABASE_URL_UNPOOLED | Neon dev branch, direct URL |
JWT_SECRET, OAUTH_COOKIE_SECRET | Run openssl rand -base64 32 once for each |
APP_URL | http://localhost:3000 |
GOOGLE_*, GITHUB_* | From your OAuth apps; leave empty to hide that sign-in button |
CRON_SECRET, USE_FIXTURES | Optional |
AUTOMATION_BYPASS_SECRET | Optional, 32+ bytes. Postman and newman send it as x-automation-key to skip the form token on log-in, sign-up and problem reports |
EMAIL_ENABLED, POSTMARK_SERVER_TOKEN, EMAIL_FROM | Optional; leave empty. Email stays off unless all three are set and EMAIL_ENABLED is 1. See Turning on email |
DEMO_USER_EMAIL, DEMO_USER_PASSWORD | Optional; used by the database seed |
ADMIN_USER_EMAIL, ADMIN_USER_PASSWORD | Optional, seed only: the only way to get an admin account (username admin). demo is always seeded as a plain user, since its password is typed on stage |
ANTHROPIC_API_KEY | Optional; turns on AI reading, board searches and page reads. Use a dedicated key with a monthly spend limit set in the Anthropic console |
AI_MODE, AI_MODEL, AI_MODEL_LOCATE, AI_DAILY_USD | Optional: off disables AI without removing the key; model for reading and board guesses (default claude-haiku-5-5); model for web search and page reads (default claude-sonnet-5-5); global daily spend cap in USD (default 2) |
Start the development server and open http://localhost:3000.
npm run dev
| Command | What it does |
|---|---|
npm run dev | Start the development server |
npm run build | Build for production |
npm test | Run the unit tests (Vitest); they never call the network or the database |
npm run lint | Run ESLint |
npm run test:e2e | Run the Playwright end-to-end tests (see below) |
Anthropic's Claude (claude-haiku-5-5, effort low) reads the company, role and location from a pasted posting. It never scores: the directory and the employer's own job board verify what it read, and the versioned rubric scores as for any check. Board points from an AI reading count only for an exact title match, a job link found in the text, or a company and role the user confirms. What Claude read is shown on the result and never stored.
Text is masked before it is sent (lib/ai/sanitize.ts): email addresses, phone and ID numbers, long numbers and links. Names are not masked.
Spend guardrails, outermost first: the console spend limit on the key, AI_MODE=off, the admin pause row (Setting key aiPaused = 1), a global daily cap (AI_DAILY_USD) and 20 AI checks per account per day, both reserved atomically in Postgres (AiUsage, AiUserUsage). Each call reserves its worst case for the model used (output limit, input ceiling, search fees), so the cap can't be overshot. Web searches and page reads may use at most 60% of the cap. A call refused before anything was billed gives the account its call back. Any failure or cap shows "couldn't verify" and scores 0.
Logs carry numbers only: ai extract model=… in=… out=… ms=… stop=… outcome=….
USE_FIXTURES=1 replays the answers in fixtures/ai/: synthetic ones written for the two demo postings and the demo message in lib/demo-examples.ts. Record real ones with a key:
ANTHROPIC_API_KEY=... npx tsx --tsconfig tsconfig.json scripts/record-ai-fixtures.mts
npm test never calls an AI API: tests/setup.ts turns AI off and blocks Anthropic hosts.
Recruiter message tab: pasted email headers are parsed in memory (lib/lookups/mail-auth.ts) for DKIM/SPF/DMARC (S-AUTH-FAIL) and Reply-To (S-REPLY-TO); the optional "Let Claude read this message" box only suggests the sender and company fields.
Checks read the employer's own system, never a job site. Greenhouse, Lever and Ashby are read in lib/lookups/boards.ts; Workday, SmartRecruiters, Workable, Recruitee and amazon.jobs in lib/lookups/adapters.ts.
A Workday company stores atsBoard as tenant/site and atsHost as the career-site host (for example nvidia.wd5.myworkdayjobs.com), both copied from a real job link.
Workday, SmartRecruiters and amazon.jobs are searched for the role, not read in full, so a role missing from the search shows "couldn't verify" and is never marked removed.
The Workday and amazon.jobs endpoints are public but undocumented. Every block, redirect or error shows "couldn't verify".
USE_FIXTURES=1 reads fixtures/<system>-<board>.json. Re-record the phase 10 systems without touching the demo boards:
node scripts/make-fixtures.mjs --adapters
This is the beginning of the README. Read the rest at the original. Read the full README ↗