OpenPosition is a trusted academic opportunity discovery platform. It helps researchers, students, professors, and research organizations discover, evaluate, publish, and act on academic positions and collaboration requests that are otherwise scattered across social platforms.
The product source of truth lives in docs/product.
Key documents:
The engineering source of truth lives in docs/engineering.
Key documents:
This codebase is an MVP prototype. It has the basic product surface for browsing, submitting, and viewing academic positions and collaboration requests, but still needs the trust, moderation, action, and measurement loops required for production operation.
- React + TypeScript + Vite
- Tailwind CSS + shadcn/ui components
- Hono API server
- tRPC
- Drizzle ORM
- Supabase Postgres
- Google auth and session cookies
Install dependencies:
npm installRun the development server:
npm run devRun type checks:
npm run checkRun tests:
npm run testBuild:
npm run buildRun database migrations against Supabase:
DATABASE_URL="<supabase pooled postgres url>" npm run db:migrateProduction pages do not fall back to mock posts. If the API cannot reach Supabase, users see a temporary availability message explaining that the free database may be waking up after inactivity.
Vercel Cron calls a protected keepalive endpoint once per day:
CRON_SECRET=<long random secret>Add the same CRON_SECRET to Vercel environment variables. The cron is defined
in vercel.json and calls /api/cron/keepalive; /api/health remains public
for quick manual checks.
Mock fallback is still available for local development. In production, only enable it intentionally:
VITE_ENABLE_MOCK_FALLBACK=trueOpenPosition exposes a protected HTTP endpoint for agents or external collectors to submit academic opportunity candidates. Agents should call this API instead of writing directly to Supabase.
Set a shared secret in local and production environments:
INGESTION_ADMIN_SECRET=<long random secret>Do not commit the real secret to README or other tracked files. Put it in .env
locally and in your deployment provider's environment variables.
Submit a candidate:
curl -X POST "https://your-domain.example/api/ingestion/posts" \
-H "Authorization: Bearer $INGESTION_ADMIN_SECRET" \
-H "Content-Type: application/json" \
-d '{
"source": "X",
"externalId": "tweet_or_page_id",
"originalUrl": "https://x.com/example/status/123",
"originalText": "I am recruiting a PhD student and research intern for LLM systems research.",
"title": "Recruiting PhD student and research intern for LLM systems",
"authorName": "Prof. Example",
"authorAffiliation": "Example University",
"institution": "Example University",
"location": "Remote",
"type": "position",
"positionType": "Research Intern",
"deadline": "2026-06-01",
"tags": ["LLM", "PhD", "Research Intern"]
}'Supported values:
source:X,LinkedIn,RedBook, orOthertype:positionorcollaboratorpositionType:PhD Student,Research Intern,PostDoc, orResearch Assistantdomain: for collaborator posts,Long-term Research,Short-term Project, orCo-author Needed
The API validates the payload, deduplicates by originalUrl and source + externalId, runs the existing moderation path, inserts a moderation review record, and returns:
{
"status": "created",
"postId": 123,
"moderationStatus": "approved",
"visibilityStatus": "active"
}Duplicates return status: "duplicate" with the existing postId.
Collectors should only submit public, source-linked opportunities and should keep platform credentials outside OpenPosition. The API owns validation, moderation, dedupe, and database writes.
The first release should focus on:
- moderation workflow
- admin review queue
- reliable detail/contact/source actions
- post freshness and reporting
- search and filtering improvements
- product event tracking
Do not prioritize automated scraping, recommendations, or complex social features before the trusted content loop works.