Setup
This guide takes you from a fresh clone to a working local development server. It covers the runtime requirements, the full env variable list, the Postgres install on Windows / macOS / Linux, the Prisma schema push and seed, and the production build commands. Read it once in order; the next time you onboard a machine you can skim straight to the commands.
Requirements
You need three things on the machine: Node 20 or newer, Bun for installs and the dev server, and Postgres 14 or newer for storage. Bun replaces npm install and npm run dev because it's substantially faster at both and the rest of the app's tooling (Prisma, Next, scripts) works transparently with it. If you prefer npm or pnpm they work fine too — just substitute the package manager in every command below.
Node ships with corepack for managing package managers, but the simplest install on macOS / Linux is via nvm and on Windows via nvm-windows. Bun comes from bun.sh with a one-liner installer. Postgres has its own section below.
Clone and install
# Use the repository URL you received from Rohan Sharma
# (https://rohansrma.vercel.app) when you got access.
git clone <repo-url> rsopshub
cd rsopshub
bun install
The install pulls Prisma's binaries, the Next.js framework, and every UI library the app needs. Expect a couple of hundred megabytes in node_modules; that's normal for a Next 15 app.
Postgres on Windows
If you're on Windows and don't already have Postgres running, the most reliable path is the official EDB installer. Download Postgres 16 (or 14+ if you have a reason) from postgresql.org/download/windows/. Run the installer with these choices: install location default, data directory default, set a password for the postgres superuser (write it down — you'll put it in .env.local), port 5432 (the default), locale Default locale. After install, open pgAdmin (it ships with the installer) and connect to the local server. Right-click Databases → Create → Database and create one called rsopshub. That's the entire UI setup; everything else happens via Prisma.
If you'd rather create the database from the command line, open a terminal after install and run:
"C:\Program Files\PostgreSQL\16\bin\psql.exe" -U postgres -c "CREATE DATABASE rsopshub;"
Your DATABASE_URL in .env.local will then be postgresql://postgres:<your-password>@localhost:5432/rsopshub. If your password contains special characters, URL-encode them — @ becomes %40, # becomes %23, and so on.
Postgres on macOS
The fastest path is Homebrew:
brew install postgresql@16
brew services start postgresql@16
createdb rsopshub
Brew sets up your user account as a superuser-equivalent role so you don't need a password locally. Your DATABASE_URL becomes postgresql://<yourmac-username>@localhost:5432/rsopshub. If you want a password-based setup for parity with production, run psql postgres and then ALTER USER <yourmac-username> WITH PASSWORD '<pw>'; and use the same form as the Windows example.
Postgres on Linux
On Debian / Ubuntu:
sudo apt update
sudo apt install postgresql postgresql-contrib
sudo systemctl enable --now postgresql
sudo -u postgres psql -c "CREATE USER rsopshub WITH PASSWORD 'changeme';"
sudo -u postgres psql -c "CREATE DATABASE rsopshub OWNER rsopshub;"
Your URL is then postgresql://rsopshub:changeme@localhost:5432/rsopshub.
On Arch: pacman -S postgresql followed by the standard initdb, systemctl enable --now postgresql, then the same psql -c commands as Debian.
On Fedora: dnf install postgresql-server, then postgresql-setup --initdb, then systemctl enable --now postgresql.
Environment variables
Copy the example file:
cp .env.example .env.local # macOS / Linux
Copy-Item .env.example .env.local # PowerShell on Windows
Open .env.local and fill in the variables. There are six required values, and a handful of optional ones that unlock specific integrations. The application refuses to boot in production when any required value is missing; in development it falls back to placeholder strings and logs a warning so you can iterate quickly.
Required variables
| Variable | Purpose | Generate / source |
|---|---|---|
DATABASE_URL | Postgres connection string | Your Postgres host + the database you created |
OWNER_EMAIL | The only email allowed to sign in as owner | Pick any email |
OWNER_PASSWORD | Owner password (≥ 12 chars) | Pick a strong passphrase |
SESSION_SECRET | HMAC key for signing session cookies (≥ 32 chars) | openssl rand -base64 48 |
TOKEN_ENC_KEY | AES-256-GCM key for encrypted secrets at rest (≥ 32 chars) | openssl rand -base64 48 |
APP_URL | Canonical URL of the running app | http://localhost:3000 for dev, real domain in prod |
Detail on each below.
DATABASE_URL is the Postgres connection string. It must be reachable from the machine running the dev server; for a local Postgres install on the same machine the host is localhost. Special characters in the password must be percent-encoded.
OWNER_EMAIL is the only email address that can sign in as the owner. The app compares the typed email in constant time against this value, so you can use any email format you want — it never has to match an external service. Lowercase it for consistency.
OWNER_PASSWORD is the owner's password. It must be at least 12 characters; longer is better. Treat it like the root password to the whole deployment because that's what it is. The app hashes it with scrypt before comparing, so it never touches the database in plaintext.
SESSION_SECRET is the HMAC key used to sign session cookies. Generate a fresh value with openssl rand -base64 48 and paste at least 32 characters of it. Rotating this value invalidates every existing session on every device.
TOKEN_ENC_KEY is the AES-256-GCM key used to encrypt third-party API tokens (Linear, dev.to, Google refresh tokens) at rest in your database, and the same key encrypts the session cookie payload. Generate it with openssl rand -base64 48 and use at least 32 characters. Rotating it makes every previously-encrypted token unreadable — you'll have to re-connect every integration — so write it down somewhere safe.
APP_URL is the canonical URL of the running app. Use http://localhost:3000 for local development; in production this is your real domain. Google OAuth uses this value to construct the redirect URI, so the value you set here must match the redirect URI you register in the Google Cloud console exactly.
Optional variables
| Variable | Unlocks |
|---|---|
NOTION_TOKEN | Notion document mirroring inside /documents |
GOOGLE_OAUTH_CLIENT_ID + GOOGLE_OAUTH_CLIENT_SECRET | Google Docs / Sheets / Drive / Calendar / Meet |
OPENAI_API_KEY | AI-generated daily work-log summaries (falls back to template) |
NOTION_TOKEN is a Notion integration token. Create one at notion.so/profile/integrations; pick "Internal integration", give it read access to the pages you want mirrored, then copy the secret. Set it and Documents will gain a Notion provider.
GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET together unlock the Google integrations (Docs, Sheets, Drive, Calendar / Meet). Create an OAuth 2.0 client of type "Web application" in Google Cloud Console, authorise the redirect URI <APP_URL>/api/integrations/google/callback, enable the Drive API, Docs API, Sheets API, and Calendar API in the same project, and paste the client ID and secret here.
OPENAI_API_KEY is used for the AI-generated daily work-log summaries. Without it the worklog falls back to a deterministic template summary; setting it switches to gpt-4o-mini for richer prose.
There is no DEV_TO_API_KEY or LINEAR_API_KEY in the env. Both of those integrations are per-workspace and the credential lives encrypted in the database. The workspace's integrations page lets each owner connect their own — every integration (Notion, Google, Hacker News, Reddit, dev.to, Linear, PostHog) is managed from that single page.
Apply the schema and seed
Prisma reads prisma/schema.prisma and creates the matching tables in Postgres:
bunx prisma db push
This is the right command for first-time setup and for iterating in development. In production you should use proper migrations (bunx prisma migrate dev to create one, bunx prisma migrate deploy to apply on the production database) so you have a versioned migration history; db push skips that step.
Once the schema is in place, seed it with a default workspace:
bunx prisma db seed
The seed script creates one workspace called "Personal" with a generated access code so the owner can sign in immediately and start populating data. If you want a different starting state, edit prisma/seed.ts before running the seed.
Run the dev server
bun run dev
The first request triggers a Next.js compile (about ten seconds the first time, sub-second after that) and starts the in-process cron schedules. Open http://localhost:3000, click Owner sign in, and use the email + password you put in .env.local. You're in.
Production build
When you're ready to deploy:
bun run build
bun run start
build produces an optimised Next standalone build in .next/. start runs it on port 3000 by default — set PORT to override. In production, set NODE_ENV=production, set every required env variable for real (not the dev placeholders), and run behind a reverse proxy (nginx, Caddy, Vercel, Fly, Railway — see the Deploy doc) that terminates TLS and forwards to the Next server.
Troubleshooting
If Prisma complains it cannot connect, double-check the DATABASE_URL host, port, user, and password. The most common Windows mistake is forgetting to URL-encode @ in the password.
If the dev server boots but the integrations don't work, look at the warning the env parser printed on boot; one of your required values is probably wrong. The app logs which variable failed validation and why.
If you uploaded an OWNER_PASSWORD that was less than 12 characters and the app is refusing to boot in production, change it in env, restart, and the next sign-in will work. Already-issued sessions die when OWNER_PASSWORD changes because the session fingerprint stops matching.
If Google Docs / Sheets embeds show "Allow Google Docs access to your necessary cookies" or "Can't access your Google Account", Chrome's third-party cookie block is hiding the docs.google.com session from the iframe. Open Chrome → ⋮ → Settings → Privacy and security → Third-party cookies and add [*.]google.com to Sites allowed to use third-party cookies. During local development also add [*.]localhost (or whatever host you run the dev server on) so the parent page's cookies aren't blocked either. See docs/06-integrations.md → Google Docs and Sheets for the full rationale.