"Project your work." A project-first social network for developers and indie hackers. The atomic unit is the project, not the person — there's no generic feed of posts about nothing. Every update, comment, and piece of media belongs to something you're actually building.
- 📱 iOS app — submitted to the App Store, currently in review
- 🌐 Web — public profile and project pages, live at the deployed site (see Deployment)
- 📄 License — MIT
Most places to show your work are either a code host (GitHub — no audience), a generic feed (LinkedIn/Twitter — no project structure), or a one-time launch (Product Hunt — no ongoing record). ProjectR combines a real dev workspace with a social graph built around what you've shipped, not who you follow:
- Forge — a real dev workspace inside the app. Connect GitHub to browse commits, branches,
and pull requests, share a commit or PR straight into chat as a tappable card, or work against a
cloned local copy of a repo (Rust/
gix-backed) without ever leaving ProjectR. - Levels that mean something — your level (1–10,000, uncapped past 100) is computed entirely from real signals: shipped projects, tech-stack breadth, update cadence, and engagement — never a follower count. Per-skill levels work the same way, scoped to only the projects using that tech.
- Portfolio PDF — generate a real, polished PDF from your best projects: auto-filled bios (on device via Apple Intelligence where available, a server fallback otherwise), real GitHub stats, ready to attach to a job application.
- No login wall for sharing. Every profile and project page is publicly readable and rendered server-side on the web — a shared link works for anyone, not just other ProjectR users.
- Projects — creation, editing, updates, comments, likes, saves (private bookmarks), and collaborators (not just a single owner).
- Social — following, a following/trending feed, Discover with category filters, notifications (in-app + real APNs push), 1:1 direct messages with realtime delivery.
- Stories — 24-hour stories plus permanent Highlights, shown on both your own and visited profiles.
- Forge — GitHub OAuth connect (server-verified — the token is checked against GitHub's own
/userAPI before anything is trusted, so the verified badge can't be spoofed), commit/branch/PR browsing, repo insights (commit activity, code frequency, contributors, punch card), local git via a Rust core, weekly star-history snapshots. - Moderation — blocking, reporting, and an admin review queue, all backed by real RLS policies.
- Accounts — Sign in with Apple/Google/email, Face ID app lock, real account deletion (not a stub — actually removes storage objects and cascades through every table), crash reporting (Sentry), self-hosted feature-usage analytics (never sold, never shared, no ad networks).
supabase/ Postgres schema (migrations), Edge Functions, local dev config
web/ Next.js app — public profile & project pages (the distribution loop)
ios/ SwiftUI app — XcodeGen-generated, project.yml is the source of truth
forge-core/ Rust crate (gix-backed local git) — compiled to an XCFramework for iOS
- Backend: Supabase (Postgres + Auth + Storage + Realtime + Edge Functions), everything gated by RLS — no separate API layer.
- iOS: SwiftUI, Swift 6 strict concurrency, XcodeGen (
project.ymlis the only source of truth for the Xcode project — never edit.xcodeprojdirectly). - Web: Next.js (App Router), server-rendered, deployed on Vercel.
- Forge core: Rust (
gixfor git operations), exposed to Swift viauniffias an XCFramework.
- Xcode 26+, XcodeGen (
brew install xcodegen) - Node 20+
- Supabase CLI, Docker (for local Supabase)
supabase startApplies every migration in supabase/migrations/ automatically and prints an API URL + anon key —
both the web app and the iOS app need these.
cd web
cp .env.local.example .env.local # fill in NEXT_PUBLIC_SUPABASE_URL / ANON_KEY from step 1
npm install
npm run devVisit http://localhost:3000/@someusername or /p/some-project-slug.
cd ios
cp Config.local.xcconfig.example Config.local.xcconfig # fill in SUPABASE_URL / ANON_KEY from step 1
xcodegen generate
open ProjectR.xcodeprojConfig.local.xcconfig and Config.production.xcconfig are both gitignored — every developer
(and CI) fills in their own. The .xcodeproj itself is also gitignored and regenerated from
project.yml by xcodegen generate; re-run it whenever you add or remove source files.
cd ios && xcodebuild test -scheme ProjectRTests
cd forge-core && cargo testBoth are fully wired (supabase/config.toml's [auth.external.apple] / [auth.external.google],
DEVELOPMENT_TEAM set in ios/project.yml). Apple's native flow verifies a real nonce round-trip;
Google's SDK integration keeps skip_nonce_check = true since it doesn't generate its own nonce.
Email/password auth works out of the box against local Supabase.
If you fork this for your own bundle ID/team, update PRODUCT_BUNDLE_IDENTIFIER and
DEVELOPMENT_TEAM in project.yml, the Apple client_id in config.toml (must match your bundle
ID exactly), and your own Google OAuth client in Config.local.xcconfig. Restart the stack
(supabase stop && supabase start) after any config.toml auth change.
- Web is deployed on Vercel, connected to this repo's
mainbranch. Production environment variables (NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_ANON_KEY) are set directly on the Vercel project, not read from.env.local(which stays local-only, pointed at local Supabase). - iOS ships via
Config.production.xcconfig(gitignored, points at the real hosted Supabase project). Universal Links and theSITE_URLused forShareLinkboth point at wherever the web app is actually live — update both together if you move to a custom domain. - Production Supabase secrets (APNs credentials, GitHub OAuth secret, webhook secrets, Sentry DSN,
Anthropic API key for the AI-bio fallback tier) are managed via
supabase secrets set, never committed — seesupabase/.env.examplefor the full list of what's expected.
github_connections(the OAuth token behind the GitHub-verified badge) has no direct client table access at all. Every read/write goes throughsecurity definerRPCs that hard-checkauth.uid()server-side, and the token-storage RPC is further locked toservice_roleonly — a dedicated Edge Function (connect-github) fetches the real GitHub/userrecord itself before ever writing anything, so the verified badge can't be spoofed by calling the RPC directly with a fabricated username.- New tables in the
publicschema are not auto-exposed to theanon/authenticatedData API roles by default. Migrations grant access explicitly alongside RLS policies — if you add a table, you need both. - Postgres grants
EXECUTEtoPUBLICon new functions by default, and this project's hosted Supabase setup separately grantsanon/authenticatedby default too — anysecurity definerfunction that trusts an explicit id parameter instead ofauth.uid()needs an explicitREVOKE, or it's callable by anyone regardless of role-based reasoning that looks safe on paper.
MIT © 2026 Kartik Sanil