Premium Next.js + Python evolution of the Grounded Synthetic Respondent Lab.
This repository contains:
- a new Next.js frontend for the cinematic, one-page product experience
- a new FastAPI backend that wraps and preserves the working Python simulation logic
- the original Streamlit prototype kept as a reference implementation
The product helps a user:
- define a study mode
- define audience, product, market, and survey context
- configure an experiment
- run grounded synthetic respondent simulations
- inspect analysis, trust framing, and executive insights
SyntheticResponderLab/
├── apps/
│ ├── api/ # FastAPI backend for the new product
│ └── web/ # Next.js frontend for the new product
├── Documentation/ # migration docs, specs, and implementation notes
├── UI Prototype/ # visual reference files
└── NeoSmart-Hackathon-App/ # legacy Streamlit app kept as reference
- Next.js 14
- Tailwind CSS
- Framer Motion
- premium one-page workflow UI
- FastAPI
- SQLAlchemy + Alembic
- SQLite for local development
- wraps legacy Python logic instead of rewriting it in JavaScript
- the original multipage Streamlit prototype
- kept in the repo as the reference logic source
- not the primary app to run for the new product
The current Next.js app includes:
- Main
- Study Mode
- Audience
- Product
- Market
- Survey
- Experiment
- Run Simulation
- Analysis
- Insights
- Node.js 18+
- npm
- Python 3.9+
Create a local env file from the example:
cd apps/api
cp .env.example .envCreate the virtual environment and install dependencies:
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"Run the migration:
alembic upgrade headStart the API:
uvicorn src.main:app --reload --port 8000Create a local frontend env file:
cd ../web
cp .env.example .env.localInstall dependencies and start the app:
npm install
npm run devOpen:
- Frontend: http://localhost:3000
- Backend health: http://localhost:8000/api/v1/health
Use apps/api/.env.example as the template.
Important variables:
APP_ENVAPP_DEBUGDATABASE_URLARTIFACTS_ROOTLEGACY_APP_ROOTDEPLOYMENT_SHARED_SECRETCORS_ALLOW_ORIGINSMAX_SURVEY_UPLOAD_BYTESMAX_PRODUCT_IMAGE_UPLOAD_BYTESDAILY_STUDY_CREATE_LIMITDAILY_UPLOAD_LIMITDAILY_PROVIDER_RUN_LIMIT
Production-like startup rules:
APP_ENVmust not bedevelopment,dev,test, orlocalAPP_DEBUGmust befalseDEPLOYMENT_SHARED_SECRETmust be configured and at least 16 charactersARTIFACTS_ROOTmust exist and be writableLEGACY_APP_ROOTmust exist and containbackend/DATABASE_URLmust be a valid SQLAlchemy database URL
Optional provider credentials:
OPENROUTER_API_KEYOPENROUTER_BASE_URLGOOGLE_CLOUD_API_KEYGOOGLE_CLOUD_SERVICE_ACCOUNT_JSONGOOGLE_CLOUD_SERVICE_ACCOUNT_PATHHUD_API_TOKENANTHROPIC_API_KEY
API_BASE_URL=http://localhost:8000
DEPLOYMENT_SHARED_SECRET=
APP_ACCESS_PASSWORD=
# Clerk (real user auth). Required in production unless APP_ACCESS_PASSWORD
# is being used as the only gate.
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/Production note:
- the web app now proxies browser API requests through Next.js server routes
API_BASE_URLmust point to the backend origin reachable from the web serverDEPLOYMENT_SHARED_SECRETmust match the backend value so the server-side proxy can reach protected API routes- Clerk (
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY+CLERK_SECRET_KEY) is the primary authentication method; users sign in via Clerk and the proxy forwards trusted identity headers to the backend APP_ACCESS_PASSWORDis now optional. It remains available as an emergency / maintenance pre-gate that can be enabled in front of Clerk; if set in production it must be at least 12 characters. Leave empty for normal Clerk-authenticated operation- in local development only, the web app can fall back to
http://127.0.0.1:8000whenAPI_BASE_URLis unset - in production-like environments, missing or invalid backend env will fail during startup/build instead of silently falling back
- production builds must configure either Clerk or
APP_ACCESS_PASSWORD; configuring neither fails fast at startup
The app uses Clerk as its external authentication provider.
Configure your Clerk instance:
- Create an application in the Clerk dashboard.
- Turn on Restricted mode so random visitors cannot sign up on their own.
- Disable public sign-ups for every enabled authentication factor.
- Add the application's publishable key and secret key to the frontend env (
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,CLERK_SECRET_KEY). - Admins issue invitations from the Clerk dashboard ("Users" → "Invite"); invitees receive an email link that lands on
/sign-inand completes the Clerk ticket flow.
Backend side:
ADMIN_CLERK_USER_IDSis a comma-separated list of Clerk user ids (for example,user_2abc123,user_2def456) that bypass study ownership checks. Keep this list short; ideally only bootstrap admins.REQUIRE_AUTHENTICATED_IDENTITY=trueforces the backend to reject any request without anX-Authenticated-User-Idheader (even outside production). Leave unset for local development so the dev-fallback synthetic user continues to work.DEV_FALLBACK_USER_IDcontrols the synthetic user id used for local / test environments when identity headers are not present (defaults todev-local-user). Production ignores this fallback.- Daily quota env vars are enforced server-side per authenticated user:
DAILY_STUDY_CREATE_LIMITDAILY_UPLOAD_LIMIT(shared by survey upload and product image analysis)DAILY_PROVIDER_RUN_LIMIT(shared by simulation runs, stability checks, and interview runs)
Identity trust boundary:
- Browsers never call the FastAPI backend directly.
- The Next.js server proxy at
apps/web/src/app/api/backend/[...path]/route.tsresolves the current Clerk session viaauth(), strips any client-suppliedX-Authenticated-*headers, and injects trustedX-Authenticated-User-Id,X-Authenticated-User-Email, andX-Authenticated-Auth-Modeheaders alongside the existingX-Deployment-Secret. - FastAPI's
get_current_userdependency reads those proxy-injected headers. Requests that reach the backend without the deployment shared secret are rejected before identity is considered.
Run these steps once when switching a deployment from the shared-password gate to Clerk:
- Configure Clerk (keys, Restricted mode, invitees) as described above.
- Deploy the new frontend and backend with Clerk env vars set.
- Decide on a bootstrap admin Clerk user id (one of the admins you invited, typically the first user listed in
ADMIN_CLERK_USER_IDS). - Reassign pre-existing studies that have
owner_user_id = NULL(or alegacy:placeholder owner) to the bootstrap admin so they remain accessible:.venv/bin/python apps/api/scripts/backfill_study_owners.py \ --owner user_2abcBootstrap \ --match-legacy \ --dry-run # inspect the output, then rerun without --dry-run to apply - Leave
APP_ACCESS_PASSWORDunset in prod under normal operation. It is only needed if you intentionally want an emergency maintenance pre-gate in front of Clerk.
The app now enforces these protections in code:
- per-user daily study creation limits
- per-user daily upload limits
- per-user daily provider-run limits
- one provider-backed run in flight per user at a time
API behavior:
429 quota_exceededmeans the authenticated user has reached today’s limit for that action409 provider_run_in_flightmeans the authenticated user already has a provider-backed run in progress
Recommended edge/platform protections:
- Clerk owns password security, sign-in, and invite acceptance. Do not rebuild local login throttling in the app.
- Configure rate limits at the hosting edge for:
/sign-in/sign-up- Clerk callback routes used by your deployment
/api/backend/*as defense-in-depth
- Conservative starting points:
- unauthenticated auth-entry routes:
10 requests/min/IP - authenticated
/api/backend/*:60 requests/min/IP, then tune based on real traffic
- unauthenticated auth-entry routes:
Suggested Clerk dashboard settings:
- Restricted mode enabled
- public sign-ups disabled
- invitations issued by admins only
- bot protection / abuse safeguards enabled where available
This app is not fully self-contained yet. A real deployment must provide:
- the
apps/apiservice - the
apps/webservice - a writable artifacts directory for the API
- the legacy runtime tree referenced by
LEGACY_APP_ROOT
If any of those assumptions are missing, backend startup or health checks will fail loudly.
The production-minded access-control stack is layered:
- the backend API is protected by
DEPLOYMENT_SHARED_SECRET(shared between the frontend server and the backend) - real user identity is provided by Clerk on the frontend; users must sign in to render the authenticated app shell
- browser requests hit the Next.js app first and are routed through the Next.js server proxy for every backend call
- the proxy resolves the Clerk session server-side, strips any client-supplied identity headers, and injects trusted
X-Authenticated-User-Id/X-Authenticated-User-Email/X-Authenticated-Auth-Modeheaders alongsideX-Deployment-Secret - FastAPI enforces per-user study ownership: studies are owned by the Clerk user id that created them, and non-owners get a 403 unless they are listed in
ADMIN_CLERK_USER_IDS APP_ACCESS_PASSWORDis an optional emergency pre-gate that can be turned on in front of Clerk if needed; normal operation leaves it empty
This means anonymous users cannot:
- see the authenticated product shell (only the public landing page)
- call the protected frontend proxy routes (proxy returns 401)
- directly call backend study/upload/simulation routes without the shared secret
- impersonate another user by setting
X-Authenticated-*headers manually (the proxy always overwrites them)
Design notes:
- the backend does not verify Clerk JWTs directly today; trust flows through the proxy +
DEPLOYMENT_SHARED_SECRET. Adding JWT verification on the FastAPI side is a future defense-in-depth step. - if Clerk is not configured, the app falls back to the legacy shared-password gate; this is intended only for maintenance or emergency operation.
cd apps/api
source .venv/bin/activate
pytest -qcd apps/web
npm run test:unit
npm run build- Do not commit local
.envfiles. - Do not commit local database files like
local-dev.db. - Do not commit generated artifacts under
apps/api/artifacts/. - Do not commit
node_modules/or virtual environments. - The root
.gitignorein this repo is set up to ignore those. - Local credential-like JSON files are also ignored now, including common service-account filename patterns.
One important security note:
- if you previously stored a real provider key in a local
.env, rotate that key before publishing the repository if there is any chance it was ever exposed
Optional local commit guard:
pip install pre-commit
pre-commit installThis repo now includes a lightweight staged-secret check that blocks obvious private keys and Google service-account JSON from being committed.
The old Streamlit app lives in NeoSmart-Hackathon-App/.
It remains useful for:
- validating behavior against the original prototype
- tracing legacy grounding, survey parsing, simulation, analysis, and insights logic
- understanding the migration history
The new product work should happen in:
This workspace is not currently initialized as a git repository from the root.
If you want to publish from this root folder:
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin <your-github-repo-url>
git push -u origin mainProject planning and migration notes are in Documentation/.
Deployment operators should start with:
Key docs include:
- frontend migration review
- Next.js + Python migration plan
- Phase 0 backend spec
- Phase 1 backend implementation notes
- Phase 2 setup flow hardening notes
- Phase 3 chart system plan