Portfolio Reporting Deployment
Step-by-step guide to deploy the Portfolio Reporting Stack on your own infrastructure.
Overview
The Portfolio Reporting Stack is designed as a single-tenant deployment per fund. You control your own data, your own API keys, your own domain, and your own infrastructure. Contact Taylor for managed setup, onboarding, and ongoing support.
Required services
| Service | What it does | Free tier |
|---|---|---|
| Hosting (Netlify or Vercel) | Runs the Next.js app | Yes |
| Supabase | Database, authentication, file storage | 500 MB database, 1 GB storage |
| Inbound email (Postmark or Mailgun) | Receives portfolio company emails | Postmark: 100/mo, Mailgun: 1,000/mo |
| AI provider (at least one) | Email processing, metric extraction, summaries | See below |
AI providers
| Provider | Default Model | Notes |
|---|---|---|
| Anthropic | claude-sonnet-4-5 | Best overall quality |
| OpenAI | gpt-4o | Strong alternative |
| Google Gemini | gemini-2.0-flash | Free tier available |
| Ollama | llama3.2 | Free, runs locally |
Optional services
| Service | When you need it |
|---|---|
| Outbound email (Resend, Postmark, Mailgun, or Gmail) | To email portfolio companies from the app |
| Google Cloud OAuth | Google Drive archiving + Gmail sending |
| Dropbox | Alternative file archiving |
Step-by-step setup
Step 1: Clone the repository
git clone https://github.com/tdavidson/reporting.git
cd reporting
npm install
Step 2: Create the Supabase project
- Create a new project at supabase.com
- Go to Project Settings > API and copy:
- Project URL (
NEXT_PUBLIC_SUPABASE_URL) - Anon public key (
NEXT_PUBLIC_SUPABASE_ANON_KEY) - Service role key (
SUPABASE_SERVICE_ROLE_KEY)
- Project URL (
- Run SQL migrations: use
supabase db pushor paste each file insupabase/migrations/into the SQL Editor in order - In Authentication > Providers, confirm Email is enabled
Step 3: Generate an encryption key
All secrets are encrypted at rest using AES-256-GCM:
openssl rand -hex 32
Save this as ENCRYPTION_KEY. If you lose it, encrypted credentials become unrecoverable.
Step 4: Deploy the app
Netlify:
Vercel:
Add these environment variables in your hosting platform:
NEXT_PUBLIC_SUPABASE_URL= # From Step 2
NEXT_PUBLIC_SUPABASE_ANON_KEY= # From Step 2
SUPABASE_SERVICE_ROLE_KEY= # From Step 2
ENCRYPTION_KEY= # From Step 3
NEXT_PUBLIC_APP_URL= # Your deployed URL
Trigger a redeploy after adding variables, NEXT_PUBLIC_* variables require a rebuild.
Step 5: Configure authentication
- Authentication > URL Configuration: set Site URL to your deployed URL, add
https://your-app.com/**to Redirect URLs - Authentication > Hooks: enable the Before User Created hook, select
hook_before_user_created
Step 6: Allow your first user
- In Supabase, go to Table Editor > allowed_signups
- Insert a row with your email address (or
*@yourfund.comfor a domain) - Go to
/auth/signupon your deployed app and create your account - Confirm via email, the first signup is the admin
Step 7: Complete the onboarding wizard
After signing in, the wizard walks you through:
- Fund name
- AI API key, at least one provider. Keys are encrypted and stored in your database.
- Inbound email address, see next step
Step 8: Set up inbound email
Postmark:
- Create a Postmark account
- Set the inbound webhook to:
https://your-app.com/api/inbound-email?token=YOUR_TOKEN - Enter the Postmark inbound address in Settings
Mailgun:
- Create a Mailgun account
- Set up an inbound route forwarding to:
https://your-app.com/api/inbound-email/mailgun - Enter your Mailgun API key and signing key in Settings
Step 9: Add authorized senders
In Settings > Authorized Senders, add email addresses your portfolio companies send reports from. Only emails from authorized senders are processed.
Step 10: Add companies and metrics
- Go to Portfolio and add your companies
- Configure the metrics you want to track per company
- Optionally use Import to bulk-create companies and metrics from a spreadsheet
Step 11: Test it
Forward a portfolio company report to your inbound address. Within a minute:
- The email appears in Inbound
- Metrics are extracted and visible on the company profile
- Low-confidence extractions are flagged in Review
Step 12: Invite your team
Team members can sign up if their email matches the whitelist or your fund's email domain. Admins approve requests in Settings.
Verify your setup
Add ENABLE_SETUP_PAGE=true as an environment variable, then visit /setup to run a built-in checklist. It checks infrastructure, authentication, fund configuration, AI providers, email setup, file storage, and authorized senders. Disable it when done by removing the variable.
Optional: Google Drive / Dropbox
Google Drive:
- Create a Google Cloud project, enable Google Drive API and Gmail API
- Create OAuth 2.0 credentials (Web application)
- Add
https://your-app.com/api/auth/google/callbackas a redirect URI - In Settings, enter your Client ID and Secret and connect
Dropbox:
- Create an app at dropbox.com/developers
- Add
https://your-app.com/api/auth/dropbox/callbackas a redirect URI - Connect in Settings
Optional: Two-factor authentication
Enable TOTP-based MFA from the Settings page. Works with any authenticator app.
Feature visibility
Admins can control which features appear in the sidebar:
| Level | Behavior |
|---|---|
| Everyone | Visible to all team members |
| Admin only | Only visible to admins |
| Hidden | Removed from sidebar, accessible via URL |
| Off | Completely disabled |
Configurable features: Interactions, Investments, Funds, Notes, Letters, LPs, Compliance, Imports, and Asks.
Local development
npm install
cp .env.example .env.local
# Fill in Supabase URL, keys, and encryption key
npx supabase db push
npm run dev
For webhook testing locally, use a tunnel:
ngrok http 3000
# or
cloudflared tunnel --url http://localhost:3000
Updates
The app checks for new versions against GitHub Releases. Admins see an Updates link in the sidebar when a newer version is available.
Contact
For setup assistance, managed deployments, or questions: contact Taylor.
For bug reports and feature requests: GitHub Issues.