The share portal is a static single-page application. It has no backend, no database, and makes no network requests. All plan data is encoded in the URL hash.

Build

bun install
bun run build:portal

Output: apps/portal/dist/

Deploy

Upload the dist/ folder to any static hosting provider.

Nginx

server {
    listen 80;
    server_name plannotator.internal.example.com;
    root /var/www/plannotator;
    try_files $uri /index.html;
}

AWS S3 + CloudFront

aws s3 sync apps/portal/dist/ s3://your-bucket/ --delete

Configure the CloudFront distribution to return /index.html for 404s (SPA routing).

Vercel / Netlify / Cloudflare Pages

Point to the repository root:

  • Build command: bun run build:portal
  • Output directory: apps/portal/dist

Configure Plannotator

Set the PLANNOTATOR_SHARE_URL environment variable to your portal’s URL:

export PLANNOTATOR_SHARE_URL=https://plannotator.internal.example.com

All share links generated by Plannotator will now point to your self-hosted portal. The import dialog placeholder updates automatically.

When PLANNOTATOR_SHARE_URL is not set, the default https://share.plannotator.ai is used.

Verify

  1. Start a plan review in Claude Code or OpenCode
  2. Add an annotation, click Export → Share
  3. Confirm the share URL starts with your configured domain
  4. Open the link — the plan and annotations should render correctly