Deploy LingerCut (public HTTPS / SaaS)
Goal: someone else can open a URL, run Inventory, and get kill receipts. Secrets stay in the host env — never in git.
Founder runtime
LingerCut ships from GitHub main → Vercel (https://lingercut.com). Pull requests get Preview deployments. Need a shell? Use a Cursor or GitHub cloud agent.
The founder's PC is not a runtime. Do not treat npm run dev or docker compose up on that laptop as how LingerCut runs. Sovereign Compose is the buyer's host — [SOVEREIGN_DEPLOY.md](./SOVEREIGN_DEPLOY.md).
Deploy choice
| Path | Doc |
|---|---|
| This page — SaaS on Vercel | Founder ship path + public HTTPS evaluation |
| Sovereign — Docker on the buyer's host | [SOVEREIGN_DEPLOY.md](./SOVEREIGN_DEPLOY.md) — customer-controlled / critical-infra. Not a LingerCut-operated AWS VPC. |
Critical-infrastructure buyers: also read [CRITICAL_INFRA.md](./CRITICAL_INFRA.md).
1. Prerequisites
- GitHub repo pushed (this project)
- Accounts ready to add HTTPS redirect URIs:
- Google OAuth client
- Microsoft Entra app
- GitHub OAuth App
- GitLab OAuth application (gitlab.com or self-managed)
- A host that runs Next.js 15 (Vercel is the default path below)
- Optional: Upstash Redis for durable kill receipts
- Production fail-closed (required on Vercel production):
OPERATOR_PASSWORD,OPERATOR_SESSION_SECRET,RECEIPT_SIGNING_SECRET— see [HARDENING.md](./HARDENING.md)
2. Deploy on Vercel
### CLI (from this repo)
npx vercel
npx vercel --prod
### Dashboard
- vercel.com → Add New → Project → import
lingercut. - Framework: Next.js. Root directory: repo root.
- Environment variables (Production + Preview as needed):
| Name | Example |
|---|---|
NEXT_PUBLIC_SITE_URL | https://lingercut.vercel.app (your real URL) |
OPERATOR_PASSWORD | required in production — long random |
OPERATOR_SESSION_SECRET | required in production — separate from password |
RECEIPT_SIGNING_SECRET | required in production — HMAC kill receipts |
RECEIPT_SIGNING_SECRET_PREVIOUS | optional — verify-only after rotate (30–90 day overlap) |
AUTH_SECRET | people-SSO state HMAC |
AUTH_GOOGLE_ID / AUTH_GOOGLE_SECRET | people-SSO Google client — not connector GOOGLE_CLIENT_* |
AUTH_MICROSOFT_ENTRA_ID_ID / SECRET / ISSUER | optional people-SSO Entra — leave unset for Google-only. Hidden on /login when unset. Not connector MICROSOFT_CLIENT_* |
FOUNDER_EMAIL | invite bypass allowlist |
ALLOW_PASSWORD_LOGIN | default on; password becomes break-glass when SSO env is set |
SESSION_ENCRYPTION_KEY | preferred AEAD key for OAuth cookies (else derived from session secret) |
DUAL_CONTROL / OPERATOR_PASSWORD_APPROVER | defaults on with live connectors in prod; set 0 only for lab |
UPSTASH_REDIS_REST_URL | from Upstash (durable receipts) |
UPSTASH_REDIS_REST_TOKEN | from Upstash |
GOOGLE_CLIENT_ID | from Google Cloud |
GOOGLE_CLIENT_SECRET | from Google Cloud |
GOOGLE_REDIRECT_URI | https://YOUR_DOMAIN/api/google/callback |
MICROSOFT_CLIENT_ID | from Entra |
MICROSOFT_CLIENT_SECRET | from Entra |
MICROSOFT_REDIRECT_URI | https://YOUR_DOMAIN/api/microsoft/callback |
GITHUB_CLIENT_ID | from GitHub OAuth App |
GITHUB_CLIENT_SECRET | from GitHub OAuth App |
GITHUB_REDIRECT_URI | https://YOUR_DOMAIN/api/github/callback |
GITLAB_CLIENT_ID | from GitLab OAuth application |
GITLAB_CLIENT_SECRET | from GitLab OAuth application |
GITLAB_REDIRECT_URI | https://YOUR_DOMAIN/api/gitlab/callback |
GITLAB_BASE_URL | optional self-managed origin (default https://gitlab.com) |
PROBE_BASE_URL / PROBE_SHARED_SECRET | pilots: second origin (project lingercut-probe or Compose probe). Never the same host as this cut plane — see [PROBE_RELIABILITY.md](./PROBE_RELIABILITY.md) |
OPENAI_API_KEY | optional — ranking helper + embedded support intelligence. Without it, support degrades to grounded FAQ (still delayed handoff). Add in Vercel for the smart Residual Trust desk. |
OPENAI_MODEL | optional — default gpt-4o-mini |
SUPPORT_WEBHOOK_URL | optional — POST when a visitor confirms connect to a trained operator |
- Deploy. Copy the production URL into
NEXT_PUBLIC_SITE_URLand redeploy if needed.
Live production alias (this repo): https://lingercut.vercel.app
Guide: https://lingercut.vercel.app/guide
Support: https://lingercut.vercel.app/support
Operator inbox: https://lingercut.vercel.app/support/inbox
Vercel project: lingercut on team LingerCut Security (live: https://lingercut.com). Older notes said lingercut-app on ScriptureIntelligence — do not add new secrets there. Isolated probe is a second Vercel project (lingercut-probe), not this one. Add fail-closed secrets + OAuth + Upstash in the Vercel dashboard (see [HARDENING.md](./HARDENING.md)), then update provider redirect URIs to https://lingercut.com/api/.../callback.
CLI production deploys on team ScriptureIntelligence require the Git author to be a Vercel team member (solomonstatesman@gmail.com). Commits authored as ugochukwu.eneh@outlook.com are blocked (TEAM_ACCESS_REQUIRED) and never build.
Production uses the HTTPS redirects above. A contributor workstation .env.local is optional and is not how LingerCut ships (founder runtime is Vercel).
Full env template: [.env.example](../.env.example) (never commit real secrets).
3. Update each OAuth app
Authorized JavaScript origins: add https://YOUR_DOMAIN
Authorized redirect URIs: add https://YOUR_DOMAIN/api/google/callback
### Microsoft
App registration → Authentication → Web redirect URIs: add
https://YOUR_DOMAIN/api/microsoft/callback
### GitHub
OAuth App → Authorization callback URL: either switch to the HTTPS URL, or create a second OAuth App named LingerCut production with callback
https://YOUR_DOMAIN/api/github/callback
### GitLab
OAuth application → Redirect URI:
https://YOUR_DOMAIN/api/gitlab/callback
Optional GITLAB_BASE_URL for self-managed GitLab (GitHub in this app has no GHES analogue).
4. Durable kill receipts
- Local default:
.data/receipts.json(gitignored). - Production: set
UPSTASH_REDIS_REST_URL+UPSTASH_REDIS_REST_TOKEN. LingerCut stores the receipt list under keylingercut:receipts. - Receipts page shows which backend is active.
- Without Upstash on Vercel, receipts can disappear between serverless instances — fine for a first smoke test only.
5. Operator auth
Operator lock is on when OPERATOR_PASSWORD or people-SSO env is set and LINGERCUT_REQUIRE_OPERATOR_AUTH is not 0. Then /console, cuts, receipts, playbooks, connectors, the support inbox, and APIs require login at /login. People-SSO click list: [PEOPLE_SSO.md](./PEOPLE_SSO.md). /demo stays public.
Lab / beachhead unlock: set LINGERCUT_REQUIRE_OPERATOR_AUTH=0 (and usually DUAL_CONTROL=0) so Inventory and demos work without passwords. Keep OPERATOR_SESSION_SECRET + RECEIPT_SIGNING_SECRET for signed receipts. Re-lock for pilots with LINGERCUT_REQUIRE_OPERATOR_AUTH=1, OPERATOR_PASSWORD, and DUAL_CONTROL=1.
Public /support and /api/support/chat stay open. OAuth provider callbacks stay public so redirects complete. Marketing / stays public. /login shows “Operator lock is off” with a link to Inventory when auth is disabled.
6. Receipt export
From a stored receipt: Export JSON or Export PDF (IR / insurance / board artifact). Also linked from the Receipts table.
7. Smoke test after deploy
- Open
https://YOUR_DOMAIN→ Open the app (login if password set) - Connectors page should show ready for each configured provider
- Sign in → Cut → Receipts list shows a live kill receipt
- Export JSON/PDF
- Confirm the receipt stamp is CUT · BOUNCE only when replay failed
8. Optional custom domain
Vercel → Project → Domains → add app.lingercut.com (or your domain), then update NEXT_PUBLIC_SITE_URL and all redirect URIs to match.
9. Prefer sovereign instead?
Government, air-gapped-friendly, or critical-infrastructure operators: they run [SOVEREIGN_DEPLOY.md](./SOVEREIGN_DEPLOY.md) (Docker Compose + volume) on their host. Same kill-receipt product. LingerCut does not host Compose on a founder laptop.