From discovery to draft — on your terms.
Free personal-brand workflow for founders and creators.
Live app · Report bug · Request feature
What it is: A personal content operating system — discover high-signal topics, rank them against your knowledge, draft in your voice, publish manually.
What it is not: An AI writing tool, auto-poster, or subscription SaaS.
Primary interface: https://content-os.stamped.work (web app, Google sign-in or guest preview).
- Free & BYOK — no subscription; users bring OpenRouter, NVIDIA, OpenAI, Tavily, Firecrawl keys (encrypted at rest)
- Signal over noise — topics scored 0–10 against your knowledge embeddings (pgvector), not generic trending lists
- Manual discovery — HN, Reddit, RSS, GitHub, optional web search; no cron, user clicks Run discovery
- Knowledge workspace — markdown context files, AI builder, chunk + embed on save
- Drafts — generate from topics; version history with restore; X thread from LinkedIn draft; delete + published archive
- Research history — every authenticated discovery run persisted at
/research - Guest mode — try dashboard without sign-in (3 discoveries/day, no DB persistence)
- No auto-posting — you approve every word on your own channels
- Stack: Next.js 14 App Router · Prisma · PostgreSQL + pgvector · NextAuth (Google)
- API surface: 20 route files · Data model: 10 Prisma models · App pages: 11 routes
- Deploy: Vercel (Pro recommended for 300s API timeout) · Supabase Postgres with
vectorextension - Tests: no automated suite — manual smoke via
npm run buildand route checks
- Vision
- Screenshots
- Architecture
- Quick start
- Configuration
- App routes & navigation
- Features by area
- API catalog
- Data model
- Project structure
- Scripts & CLI
- Deployment
- Testing & quality
- Security
- Roadmap & changelog
- Contributing & license
- FAQ & glossary
Building a personal brand should not start with endless scrolling. Most founders and creators hit the same wall: hours in HN/Reddit feeds, blank-page paralysis, and AI drafts that sound nothing like them.
Content OS compresses discovery → ranking → drafting into one loop grounded in your knowledge and voice:
flowchart LR
K[Knowledge files] --> D[Discovery]
D --> R[Ranking vs embeddings]
R --> T[Topic board]
T --> G[Generate draft]
G --> E[Edit & revise]
E --> P[Publish manually]
| It is | It is not |
|---|---|
| Personal content OS (discovery → draft → you publish) | Generic ChatGPT wrapper |
| Ranked signal against your expertise | Trend spam / volume play |
| Free app with BYOK API keys | Subscription product |
| Manual publishing on your channels | Auto-poster to LinkedIn or X |
| Guest preview for evaluation | Full anonymous persistence |
| Principle | Meaning |
|---|---|
| Signal over noise | Topics scored against your knowledge, not loudest feed |
| Your voice | Writing style, soul, interests files ground every draft |
| Your control | No auto-posting — ever |
| Free & BYOK | App is free; reasonable usage fits free API tiers |
One-liner: Find what's worth saying, draft it in your voice, publish on your terms.
Landing — discovery to draft on your terms
flowchart TD
subgraph Client["Next.js App Router"]
LP[Landing / Guest]
DASH[Dashboard]
RES[Research]
KNOW[Knowledge]
DRFT[Draft workspace]
end
subgraph API["Route Handlers — app/api/"]
DISC["POST /api/discover"]
GEN["POST /api/generate"]
KAPI["/api/knowledge/*"]
DAPI["/api/draft/*"]
end
subgraph Core["lib/"]
ORCH[discovery/orchestrator]
RANK[ranking + pgvector]
GENLIB[generation/*]
end
subgraph External
HN[HN · Reddit · RSS · GitHub]
WEB[Tavily / Firecrawl BYOK]
LLM[Draft provider BYOK]
OAI[OpenAI embeddings]
end
PG[(PostgreSQL + pgvector)]
LP --> DASH
DASH --> DISC
RES --> DISC
KNOW --> KAPI
DRFT --> GEN
DRFT --> DAPI
DISC --> ORCH --> HN & WEB
ORCH --> RANK --> PG
KAPI --> PG
KAPI --> OAI
GEN --> GENLIB --> LLM
GEN --> PG
DAPI --> PG
sequenceDiagram
participant U as User
participant API as POST /api/discover
participant O as orchestrator.ts
participant A as Adapters
participant R as Ranker
participant DB as PostgreSQL
U->>API: Run discovery
API->>O: Merge + dedupe signals
O->>A: HN, Reddit, RSS, GitHub, Web
A-->>O: Raw topics
O->>R: Embed + score vs KnowledgeChunk
R-->>O: finalScore 0–10
O->>DB: Upsert Trend rows
O->>DB: DiscoveryRun + DiscoveryRunTopic snapshot
API-->>U: Updated topic board
| Mode | Entry | DB writes | Discovery |
|---|---|---|---|
| Google user | /login OAuth |
Full | POST /api/discover |
| Guest | GET /api/guest/enter |
None | POST /api/discover/guest — 3/day, sessionStorage |
| Sign-in | Clears guest cookie | Migrates to user session | — |
Middleware protects /dashboard, /drafts, /draft/, /knowledge, /settings, /onboarding, /analytics, /activity. Pages enforce access via getAppAccess().
| Layer | Technology |
|---|---|
| Framework | Next.js 14.2 App Router, React 18, TypeScript 5 |
| UI | Tailwind CSS, Stamped design tokens (app/globals.css), GSAP (landing) |
| Auth | NextAuth.js 4 — Google OAuth |
| ORM | Prisma 5.22 |
| Database | PostgreSQL 15+ with pgvector |
| Embeddings | OpenAI (server-side, ranking + knowledge) |
| Drafts | User BYOK — OpenRouter, NVIDIA, or OpenAI |
- Node.js 18+
- PostgreSQL 15+ with
vectorextension (Supabase recommended) - Google OAuth Web application credentials
git clone https://github.com/Vinayak-RZ/Content-OS.git
cd Content-OS
npm install
cp .env.example .env.localGenerate secrets:
openssl rand -base64 32 # NEXTAUTH_SECRET
openssl rand -hex 32 # ENCRYPTION_KEY — exactly 64 hex charsFill .env.local — see §5 Configuration. Keep DATABASE_URL and DIRECT_URL in .env too if you use Prisma CLI directly.
npm run db:migrate
npm run devOpen http://localhost:3000.
| Setting | Value |
|---|---|
| Authorized redirect URI | http://localhost:3000/api/auth/callback/google |
| Env vars | GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
- Enable
vectorextension (Database → Extensions). - Pooler URL port 6543 with
?pgbouncer=true→DATABASE_URL - Direct URL port 5432 →
DIRECT_URL - Run
npm run db:migrate
Never rotate
ENCRYPTION_KEYafter users save API keys — existing keys cannot be decrypted.
All keys from .env.example (17 variables):
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | Pooled Postgres (Supabase port 6543) |
DIRECT_URL |
Yes | Direct Postgres for migrations (port 5432) |
NEXTAUTH_SECRET |
Yes | Session signing secret |
NEXTAUTH_URL |
Yes | App URL (http://localhost:3000 or production) |
GOOGLE_CLIENT_ID |
Yes | Google OAuth |
GOOGLE_CLIENT_SECRET |
Yes | Google OAuth |
ENCRYPTION_KEY |
Yes | 64-char hex AES-256-GCM for user API keys |
OPENAI_API_KEY |
Recommended | Server embeddings (knowledge + ranking) |
NEXT_PUBLIC_APP_URL |
Recommended | Public URL for links and SEO |
GOOGLE_SITE_VERIFICATION |
Optional | Search Console HTML token |
GITHUB_TOKEN |
Optional | Higher GitHub API rate limits |
REDDIT_CLIENT_ID / REDDIT_CLIENT_SECRET |
Optional | Reddit adapter |
RESEND_API_KEY / RESEND_FROM_EMAIL |
Optional | Future email digest |
ADMIN_SECRET |
Optional | Org admin BYOK export (min 32 chars) |
User-managed keys (Settings UI, encrypted): Tavily, Firecrawl, OpenRouter, NVIDIA, OpenAI draft keys.
| Route | File | Purpose |
|---|---|---|
/ |
app/page.tsx |
Marketing landing |
/login |
app/(auth)/login/page.tsx |
Google sign-in |
/onboarding |
app/(auth)/onboarding/page.tsx |
Persona + first-run setup |
/dashboard |
app/(dashboard)/dashboard/page.tsx |
Topic board, run discovery |
/research |
app/(dashboard)/research/page.tsx |
Discovery run history |
/drafts |
app/(dashboard)/drafts/page.tsx |
Draft library + published archive |
/draft/[id] |
app/(dashboard)/draft/[id]/page.tsx |
Draft workspace |
/knowledge |
app/(dashboard)/knowledge/page.tsx |
Knowledge files |
/analytics |
app/(dashboard)/analytics/page.tsx |
Published posts, activity |
/settings |
app/(dashboard)/settings/page.tsx |
BYOK keys, preferences |
/activity |
app/(dashboard)/activity/page.tsx |
Legacy activity (not in sidebar) |
Dashboard · Research · Drafts · Knowledge · Analytics · Settings
- Manual runs only — no cron
- Adapters: Hacker News, Reddit, RSS, GitHub; Tavily/Firecrawl when user keys set
- Knowledge-aware ranking via
KnowledgeChunkembeddings + pgvector similarity - Top picks (3 cards), expandable topic pool, save/dismiss feedback
- 10-day backlog expiry; saved topics carry over to next run
- Each run creates
DiscoveryRun+DiscoveryRunTopicsnapshot
GET /api/guest/entersets HttpOnly cookie → demo dashboardPOST /api/discover/guest— 3 runs/day, results insessionStorageonly- Preview overlays on knowledge, drafts, analytics
- Sign in to persist data; guest cookie cleared on OAuth
- Markdown files: writing style, soul, interests, custom files
- Roles:
style|narrative|technical|brand|general - Chunk + embed on save →
KnowledgeChunkwith vector column - AI knowledge builder (
POST /api/knowledge/build) - Starter seeds (
POST /api/knowledge/seed), URL scrape helper
- Generate from trend or custom topic (
POST /api/generate) - Hook + CTA variants; inline edit
- Revision history — kinds:
initial,manual,ai_edit,restore,x_thread; max 30 entries; restore viaPATCH /api/draft/[id]?restoreRevisionId= - X thread —
POST /api/draft/[id]/x-threadgenerates 2–3 tweets from LinkedIn draft - Delete —
DELETE /api/draft/[id] - Published archive — collapsible section in drafts library
- Status:
draft|published;publishedAtfor analytics
- Google OAuth only — no password store
- User API keys AES-256-GCM encrypted (
lib/crypto.ts) - Dashboard routes
noindex; public landing indexed - JSON-LD, sitemap,
llms.txtinlib/seo/
20 route files under app/api/. Authenticated unless noted.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/health |
Health check (public) |
* |
/api/auth/[...nextauth] |
NextAuth handlers |
GET |
/api/guest/enter |
Start guest session → redirect /dashboard |
POST |
/api/discover |
Run discovery (authenticated) |
POST |
/api/discover/guest |
Guest discovery (rate-limited) |
POST |
/api/generate |
Generate draft |
GET |
/api/trends |
List user trends |
PATCH |
/api/trends/[id]/feedback |
Save or dismiss topic |
DELETE |
/api/trends/[id] |
Remove topic from pool |
GET |
/api/topic-engagements |
Topic engagement records |
GET |
/api/settings |
Read settings |
PATCH |
/api/settings |
Update settings + encrypted keys |
GET |
/api/knowledge |
List knowledge files |
POST |
/api/knowledge |
Create knowledge file |
GET / PUT / DELETE |
/api/knowledge/[slug] |
Single file CRUD |
POST |
/api/knowledge/build |
AI knowledge builder |
POST |
/api/knowledge/seed |
Seed starter templates |
POST |
/api/scrape-url |
Scrape URL for import |
GET / PATCH / DELETE |
/api/draft/[id] |
Draft CRUD, restore revision |
POST |
/api/draft/[id]/edit |
AI revision (saves snapshot) |
POST |
/api/draft/[id]/x-thread |
Generate X thread |
GET |
/api/admin/export-keys |
Admin BYOK export (ADMIN_SECRET) |
Long-running: /api/discover and /api/generate use maxDuration = 300 (see vercel.json).
10 Prisma models — prisma/schema.prisma
| Model | Purpose |
|---|---|
User |
Account, persona, encrypted BYOK keys, onboarding |
UsageCounter |
Rate limits (generate_hour, discover_day) |
Trend |
Discovered topic in user pool (scores, feedback, expiry) |
Draft |
Post content, hooks, CTAs, revisionHistory JSON, xThreadParts |
KnowledgeFile |
Markdown context file metadata + content |
KnowledgeChunk |
Chunked text + embedding vector |
TopicEngagement |
Save/dismiss/publish engagement |
CronLog |
Legacy discovery log rows |
DiscoveryRun |
Persisted discovery run metadata |
DiscoveryRunTopic |
Topic snapshot per run |
7 migrations in prisma/migrations/ — apply with npm run db:migrate.
Key Draft.revisionHistory entry shape:
{ "id": "uuid", "kind": "manual|ai_edit|restore|initial|x_thread", "contentBefore": "...", "createdAt": "ISO8601" }content-os/
├── app/
│ ├── (auth)/ # login, onboarding
│ ├── (dashboard)/ # dashboard, research, drafts, draft/[id], knowledge, analytics, settings, activity
│ ├── api/ # 20 route handlers
│ ├── globals.css # Design tokens (source of truth)
│ ├── layout.tsx
│ └── page.tsx # Landing
├── components/
│ ├── dashboard/ # Topic board, pool table, discovery controls
│ ├── draft/ # Workspace, revision panel, X thread panel
│ ├── drafts/ # Library, published archive
│ ├── research/ # Discovery run history
│ ├── knowledge/ # File editor, AI builder
│ ├── landing/ # Marketing page (GSAP)
│ ├── guest/ # Guest preview overlays
│ └── seo/ # JSON-LD, metadata helpers
├── lib/
│ ├── discovery/ # orchestrator.ts, adapters, persist-run.ts, ranking
│ ├── generation/ # Draft prompts, x-thread prompts/schema
│ ├── knowledge/ # Chunking, embeddings, builder
│ ├── drafts/ # revision.ts
│ ├── guest/ # demo-data.ts, guest access
│ ├── seo/ # site-config, sitemap, llms files
│ └── crypto.ts # AES-256-GCM for user keys
├── prisma/
│ ├── schema.prisma
│ └── migrations/
├── scripts/
│ ├── export-user-keys.mjs # Admin CLI
│ └── clean-prisma-artifacts.mjs
├── docs/images/ # README screenshots
├── seeds/founder/ # Example knowledge templates
├── middleware.ts # Auth + guest cookie handling
├── vercel.json # maxDuration for API routes
└── package.json
| Command | Description |
|---|---|
npm run dev |
Development server (port 3000) |
npm run build |
prisma generate + Next.js production build |
npm run start |
Run production build locally |
npm run lint |
ESLint (Next.js config) |
npm run db:migrate |
prisma migrate deploy |
npm run db:push |
prisma db push (dev only) |
npm run db:studio |
Prisma Studio GUI |
npm run db:generate |
Regenerate Prisma client |
npm run admin:export-keys |
Decrypt user BYOK keys (requires ADMIN_SECRET) |
npm run admin:export-keys:csv |
Same, CSV output |
-
Import Content-OS on Vercel.
-
Add all env vars from
.env.example. -
Production URLs:
NEXTAUTH_URL=https://content-os.stamped.work NEXT_PUBLIC_APP_URL=https://content-os.stamped.work
-
Run migrations once against production:
DATABASE_URL="..." DIRECT_URL="..." npm run db:migrate
-
Google OAuth redirect:
https://content-os.stamped.work/api/auth/callback/google
Vercel Pro (or higher) recommended — discovery and generation run 30–120+ seconds; maxDuration = 300 in vercel.json.
- Landing loads at production URL
- Google sign-in completes
- Onboarding → knowledge seed → discovery → draft → publish flow
-
/researchshows run after discovery - Guest enter works without auth
| Check | Command |
|---|---|
| Lint | npm run lint |
| Production build | npm run build |
| Manual E2E | Dev server → full user flow |
No automated test suite (Jest/Vitest/Playwright) is configured. Validate changes with build + manual route checks before merging.
- User API keys encrypted at rest with
ENCRYPTION_KEY(AES-256-GCM) —lib/crypto.ts - NextAuth session on all dashboard/API routes (guest cookie for preview mode)
- Never commit
.envor.env.local - Admin key export gated by
ADMIN_SECRET— usenpm run admin:export-keysorGET /api/admin/export-keys - Dashboard
robots: noindex— only marketing pages indexed
Report security issues via GitHub Issues (mark sensitive).
| Date | Change |
|---|---|
| 2026-06-12 | Draft version history + restore; X thread from LinkedIn draft |
| 2026-06-12 | Draft delete; published archive in drafts library |
| 2026-06-12 | Research history page; DiscoveryRun persistence |
| 2026-06-03 | SEO/AEO improvements; footer social links; UI fixes |
| 2026-06-02 | Guest mode — try dashboard without sign-in |
- Email digest via Resend (env vars already in
.env.example) - Additional discovery adapters
- Automated test suite for API and critical flows
- Embedded launch video on landing hero
- Fork and branch from
main. - Run
npm run lintandnpm run build. - Open a PR with description and screenshots for UI changes.
MIT — Copyright 2026 Content OS contributors.
Is discovery automatic?
No. Click Run discovery on the dashboard. Cron was removed.
Do I need paid APIs?
The app is free. Embeddings use server OPENAI_API_KEY; drafts and optional web search use your BYOK keys on free tiers for reasonable usage.
Can guests save drafts?
No. Guest mode is preview-only. Sign in to persist.
How long are topics kept?
Unsaved topics expire after ~10 days. Saved topics carry over.
| Term | Definition |
|---|---|
| BYOK | Bring Your Own Keys — user API keys stored encrypted in User row |
| Trend | A discovered topic in the ranked pool |
| finalScore | 0–10 relevance score vs your knowledge embeddings |
| DiscoveryRun | One persisted execution of the discovery orchestrator |
| Signal | Topic that fits your expertise vs generic trending noise |




