docs/site-deployment.md

Public site deployment

Vercel public-site deployment, PostHog setup, and runtime separation.

Public Site Deployment

The public marketing and docs site is an Astro hybrid site in packages/site. It is separate from the Randal runtime, dashboard, Railway deployments, and self-hosted randal serve process.

Use this guide when you want a hosted marketing/docs preview or production site. Do not use it to deploy an agent runtime.

What Gets Deployed

  • Source package: packages/site
  • Build command: bun run site:build
  • Output mode: Astro hybrid output with @astrojs/vercel/serverless
  • Static pages: marketing/docs pages are prerendered by Astro
  • Server route: /api/personalize is a Vercel serverless function when classifier personalization is configured
  • Docs content source: README.md and allowlisted docs/*.md files read at build time

The site does not run the Bun/Hono agent runtime in production. Vercel serves prerendered pages from its output and runs only the site API routes that are explicitly marked server-side.

Separation From Runtime Deployments

The public site is only the marketing/docs surface:

  • It does not start randal serve.
  • It does not host the dashboard or gateway runtime.
  • It does not require RANDAL_API_TOKEN, Discord secrets, LiveKit, Twilio, or Railway variables.
  • It only needs a model provider key if generated landing-page classification is intentionally enabled.
  • It does not change self-host or Railway runtime deploy behavior.
  • PostHog, when enabled, is included only in packages/site static pages.

Use docs/deployment-guide.md for local, Railway, Docker, and imported-service runtime deployments.

Use the Vercel GitHub integration for PR previews and production deploys. This keeps site deploys in Vercel and avoids a duplicate GitHub Actions deploy path.

  1. Create or log in to a Vercel account, ideally under the Hassion Studio team or org.
  2. Import the GitHub repository Hassion-Studio/randal.
  3. Configure the Vercel project with these settings:
SettingValue
Framework presetAstro
Install commandbun install
Build commandbun run site:build:vercel
Output modeVercel Build Output API via the Astro Vercel adapter, promoted from packages/site/.vercel/output to .vercel/output
Node runtimeNode 20 (package.json sets engines.node to 20.x)

The repo includes vercel.json with the same settings. The extra Vercel build script exists because the Astro app lives in packages/site, while Vercel's Build Output API expects .vercel/output at the deployed project root. Normal self-host users can ignore this file; it is inert unless the repo is connected to Vercel or a deploy command is run.

For local verification, use Node 20 before running bun run site:build if you need generated Vercel function metadata to match production. The adapter derives the function runtime from the Node process that runs the build.

Environment Variables

Copy packages/site/.env.example for local site development or set the same variables in Vercel.

VariableRequiredDescription
PUBLIC_SITE_URLRecommendedCanonical site origin, for example https://randal.bot or a Vercel preview URL.
PUBLIC_POSTHOG_ENABLEDOptionalSet to true to enable PostHog on the public site. Any other value disables it.
PUBLIC_POSTHOG_KEYOptionalPublic PostHog project key. Analytics stay disabled if this is missing.
PUBLIC_POSTHOG_HOSTOptionalPostHog ingest host. Defaults to https://us.i.posthog.com; use the EU host if needed.
OPENROUTER_API_KEYOptionalServer-only key for landing-page classifier personalization. Leave unset to use static presets only.
RANDAL_PERSONALIZATION_MODELOptionalClassifier model ID. Defaults to openai/gpt-4.1-mini.
PUBLIC_TURNSTILE_SITE_KEYRequired with TurnstilePublic Cloudflare Turnstile site key for protected classifier requests.
TURNSTILE_SECRET_KEYRequired in production if classifier is enabledServer-only Turnstile secret. When set, /api/personalize verifies tokens server-side before any model call.
RANDAL_PERSONALIZE_ALLOW_UNPROTECTEDEmergency/internal onlySet to true only to intentionally allow production classifier requests without Turnstile. Default behavior fails closed.

Do not set runtime secrets such as ANTHROPIC_API_KEY, OPENAI_API_KEY, RANDAL_API_TOKEN, DATABASE_URL, legacy migration keys, or any agent runtime credential on the public site project. If OPENROUTER_API_KEY is set for the landing classifier, keep it server-only and configure Turnstile protection or the explicit unprotected override.

Landing Personalization Deployment

The homepage personalization endpoint is intentionally narrow:

  • It is a classifier/router only. Model output can select a static preset id, but rich homepage copy, CTAs, quote, FAQ, feature rows, and workflow cards all come from static site-owned preset data.
  • The endpoint rejects production requests without an exact allowed Origin header based on PUBLIC_SITE_URL.
  • If TURNSTILE_SECRET_KEY is set, requests must include a valid Cloudflare Turnstile token verified server-side.
  • In production, if no Turnstile secret is configured, the endpoint fails closed unless RANDAL_PERSONALIZE_ALLOW_UNPROTECTED=true is explicitly set.
  • If no model key is configured, the browser uses deterministic static presets and the site remains useful.

Safe rollout order:

  1. Deploy the hybrid site with OPENROUTER_API_KEY unset and verify static preset personalization works.
  2. Set PUBLIC_SITE_URL to the production origin and redeploy.
  3. Add Turnstile keys and verify challenge-protected requests in preview.
  4. Add OPENROUTER_API_KEY only after the protection path is verified.
  5. If anything fails, remove OPENROUTER_API_KEY; the site falls back to static presets.

PostHog Setup

PostHog is opt-in and public-site-only.

  1. Create a PostHog project.
  2. Copy the public project key.
  3. In Vercel, set PUBLIC_POSTHOG_ENABLED=true.
  4. Set PUBLIC_POSTHOG_KEY=phc_....
  5. Set PUBLIC_POSTHOG_HOST=https://us.i.posthog.com or the EU ingest host.
  6. Redeploy the site.

If PUBLIC_POSTHOG_ENABLED is absent, not true, or PUBLIC_POSTHOG_KEY is empty, the built site does not include the PostHog snippet.

Domain Setup

  1. In Vercel, open the site project settings.
  2. Add the production domain, for example randal.bot.
  3. Follow Vercel's DNS instructions for your registrar.
  4. Set PUBLIC_SITE_URL to the final production origin.
  5. Trigger a production redeploy so canonical URLs use the production domain.

Preview deployments can use Vercel's generated preview URLs. If you need exact canonical preview URLs, set PUBLIC_SITE_URL in the Vercel preview environment.

GitHub Actions Deployment

Do not configure a GitHub Actions Vercel deploy workflow by default. The current deployment path is the Vercel GitHub app, which owns PR previews and production deploys without repo-managed Vercel tokens.

A GitHub Actions deploy could be added later for a specific need, but it should remain an explicit alternative to the Vercel integration, not a second active deploy path for the same site.

Local Verification

Before pushing deployment changes, run:

bun run site:build
bun run test:site
bun run lint

To verify PostHog is disabled by default, build without public PostHog env vars and confirm the generated HTML has no PostHog snippet. To verify opt-in behavior, run a build with PUBLIC_POSTHOG_ENABLED=true and PUBLIC_POSTHOG_KEY=phc_test and confirm the static pages include the public-site snippet.