Experimental X-inspired audience and distribution simulator. Comparative, not predictive. Not X production. No live X feed. No virality guarantee.
Repo: github.com/jagadeepmamidi/X-impact-simulator
Fifteen curated behavior archetypes emit Phoenix-style affinities. A seeded, weighted population layer maps sanitized display personas onto those explicit behavior profiles. Python maps affinities onto assumed impression-level priors, scores them with public RankingScorer defaults from xai-org/x-algorithm, and runs a Monte Carlo full-cascade spread with synthetic candidate-slate competition. Groq never emits impact_score; the headline score blends 35% persona prior with 65% sampled population/ranking outcome. The shown graph is the run nearest median exposure/score; p10–p90 describe variation across all runs.
- Score a hook against tech, fitness, finance, or comedy packs (15 curated behavior profiles each), sampled into 40 / 100 / 320 / 500 coherent audience members
- Optional Hook B compare on the same media and seed
- Verdict with full-cascade p10–p90, Niche Index, audience fit, negative risk, stability (not statistical confidence), and rewrite suggestions
- Save every run with verified hashes/config provenance and replay from stored probabilities by id; per-owner keys isolate saved runs
- Browse recent runs, restore their population/boost settings, and record observed counts with observation time and window
- Optional BluePrint heads infer the content cluster and apply content-level log-odds lifts to favorite (40%) and retweet (25%), preserving persona differences
- Compatible actions are sampled independently (for example dwell + click + like + repost), rather than forced into one category
- LLM/heuristic 0–1 heads are affinities; Python maps them onto assumed impression-level priors before RankingScorer weights run
Needs Python 3.11+, Node 22+, and a Groq key for LLM + vision + Whisper. Without a key, heuristic scoring still runs.
cp .env.example .env # backend settings; set GROQ_API_KEY if wanted
cp .env.example frontend/.env.local # Next.js settings (server-only names stay private)
cd backend
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt # Windows
# source .venv/bin/activate && pip install -r requirements.txt # macOS/Linux
.venv\Scripts\python -m uvicorn app.main:app --host 127.0.0.1 --port 8000In a second terminal, start from the repository root:
cd frontend
npm install
npm run devOpen http://localhost:3000. API health: http://127.0.0.1:8000/api/health.
Local and hosted frontend requests use the same /api/* handler. Set BACKEND_API_URL in frontend/.env.local; leave NEXT_PUBLIC_API_URL empty. The obsolete INTERNAL_API_URL/BACKEND_PORT rewrite is removed. Public demo mode requires no operator key. The optional personal Groq key uses the visitor's provider quota; private deployments still accept an operator key under advanced access settings. Selecting a recent run fills its ID; Load opens it, Re-run snapshot creates a new run from stored probabilities, and Replay animation only plays the graph.
Selected pictures now show local previews and individual remove controls. Image-only runs require successful vision extraction; provider failures return an actionable error rather than a fabricated text-only result. When a caption is available, a failed image analysis is explicitly labeled as caption-only. Qwen vision responses are capped at 512 tokens; four or five images/frames are combined into a numbered contact sheet to fit the provider's three-image limit, with reduced detail noted in the report. Very short text such as hey is marked as insufficient context; its simulated reach reflects scenario assumptions.
Graph playback supports pause/resume, replay, round scrubbing, and speed selection. Reduced-motion preferences disable autoplay. Detailed model diagnostics and observed outcomes remain available in expandable sections.
The pilot accepts at most 3.5 MB of combined media and a 4 MB complete request. Images are also bounded by decoded pixels; videos are bounded by dimensions, frame count, and duration before frame extraction. Larger or complex media returns a clear rejection. Larger videos need a separate upload path and are deferred. Stop waiting stops the browser request; synchronous server work may still finish and appear after Refresh runs. The proxy stops waiting after 90 seconds by default.
Run these from the repository root in a separate environment (Colab is fine for the larger download):
pip install -r training/requirements.txt
python training/prepare_nemotron.py --per-niche 24 --revision <PINNED_NEMOTRON_COMMIT_SHA>
python training/prepare_blueprint.py --revision <PINNED_BLUEPRINT_COMMIT_SHA>
python training/train_heads.py --split grouped --dataset-revision <PINNED_BLUEPRINT_COMMIT_SHA>Both dataset revisions are REQUIRED 40-character commit SHAs. Mutable refs such as main are rejected. The generated model is promotion-eligible only when grouped leakage-safe evaluation passes and the runtime favorite/retweet heads beat the majority-class AP baseline. The app rejects unapproved artifacts and falls back to deterministic behavior.
Review the generated Nemotron files before promoting them into backend/data/overlays/{niche}.json; runtime validation will still reject minor, sensitive, malformed, and off-niche records. The BluePrint artifact is written to training/artifacts/phoenix_heads.joblib; the app only loads the new validated artifact schema. Do not claim empirical calibration until you have collected a sufficiently large set of real impression-denominator outcomes through the outcome endpoint and evaluated calibration on a held-out time period.
| Variable | Where | Purpose |
|---|---|---|
APP_ENV |
backend | development, test, or fail-closed production validation |
GROQ_API_KEY |
backend | Optional text, vision, Whisper; deterministic fallback works without it |
CORS_ORIGINS |
backend | Comma-separated frontend origins |
SIM_PUBLIC_DEMO |
backend | Opt-in public runs using the server Groq quota; defaults to false. Visitors receive separate browser-session ownership and remain rate limited. |
SIM_PUBLIC_RUNS_PER_HOUR |
backend | Server-key provider budget for public requests; personal keys do not bypass compute quotas |
SIM_PUBLIC_COMPUTE_RUNS_PER_HOUR |
backend | Global public compute budget across server-key and personal-key requests |
SIM_PUBLIC_SESSION_RUNS_PER_HOUR |
backend | Per-browser-session public compute budget |
SIM_API_KEY |
backend | Optional administrator key with access to every owner; never configure it on the frontend server |
SIM_ACCESS_KEYS_JSON |
backend | JSON map of owner IDs to strong keys; production UI users enter their own key and can access only their runs |
TRUSTED_PROXY_CIDRS |
backend | Proxy ranges allowed to supply forwarding headers for rate limiting |
RUN_RETENTION_DAYS |
backend | Required and positive in production |
ALLOW_SQLITE_IN_PRODUCTION |
backend | Explicit acknowledgement for single-node SQLite. Hosted free Render uses ephemeral /tmp. Paid disk is optional later. |
SQLITE_PATH |
backend | Database file; absolute path or relative to repository root. Local default backend/data/runs.sqlite. Hosted free Render: /tmp/runs.sqlite. |
SIM_MAX_CONCURRENT_RUNS |
backend | Maximum active analyses per process, default 2; excess requests receive 503 with Retry-After |
NEXT_PUBLIC_API_URL |
frontend | Optional direct API override; leave empty to use the same-origin proxy |
NEXT_PUBLIC_SITE_URL |
frontend | Canonical site URL |
BACKEND_API_URL |
frontend server | Private upstream URL used by the bounded, streaming same-origin /api/* proxy |
MAX_REQUEST_BYTES |
backend + frontend server | Matching request limit enforced while streaming at both boundaries |
API_PROXY_TIMEOUT_SECONDS |
frontend server | Time to wait for backend headers, default 90 seconds (1–300) |
NEXT_PUBLIC_SIM_DEV_TOKEN |
frontend | Optional browser-visible local gate only; never use as a production secret |
Copy .env.example. Never commit .env.
- API — Render web service, root
backend, startuvicorn app.main:app --host 0.0.0.0 --port $PORT, health/api/health. Production startup requiresAPP_ENV=production,SIM_PUBLIC_DEMO=trueand/or a strong credential inSIM_ACCESS_KEYS_JSONorSIM_API_KEY, positive retention, and an explicit single-node SQLite acknowledgement. Public demo visitors use the server Groq key; reserveSIM_API_KEYfor administration. - UI — Vercel project, root
frontend; set server-onlyBACKEND_API_URLand the sameMAX_REQUEST_BYTESas FastAPI. Do not setSIM_API_KEYorGROQ_API_KEYon Vercel. LeaveNEXT_PUBLIC_API_URLandNEXT_PUBLIC_SIM_DEV_TOKENunset; public demo visitors need no operator key whenSIM_PUBLIC_DEMOis enabled on the backend. Private operator keys stay under Advanced access and are forwarded only to the backend.
The checked-in Render blueprint is the free ephemeral demo: plan: free, SQLITE_PATH=/tmp/runs.sqlite, no persistent disk, production mode, 30-day retention, public demo on, credential sync: false. Free instances cannot attach a disk and spin down after inactivity; /tmp is wiped on restart, redeploy, and spin-down. GET /api/health reports storage.production_ready: false on this path, so the UI loss banner stays. Details: research/RENDER_OWNER_RUNBOOK.md.
Optional later (not the default): paid Starter (or higher) + disk at /var/data + SQLITE_PATH=/var/data/runs.sqlite. Disks are single-instance and skip zero-downtime deploys. Existing /tmp data is not migrated automatically. Keep that path out of render.yaml while staying on free — Blueprint sync would push Starter/disk.
For Vercel, keep the 4 MB request limit; the proxy route requests a 120-second function duration to cover its default 90-second upstream timeout. Use a plan/settings that support that duration (or lower API_PROXY_TIMEOUT_SECONDS if Fluid Compute is disabled). Provider timeout/retry settings bound individual calls; stopping a browser request does not cancel those calls. Real provider latency, budgets, live media, backup/restore and hosted smoke checks remain deployment acceptance work.
SOP deltas and Gate B→C scope: research/SOP_PILOT_ADDENDUM.md. Ranking weight pin: research/x-scoring-notes.md. Synthetic Gate C harness (not empirical calibration): research/GATE_C_SYNTHETIC.md.
With public demo mode enabled, the browser sends a random tab-scoped X-Demo-Session identifier so visitors can access only their own saved runs. The default analysis uses the server's Groq key. Visitors can optionally provide a personal Groq key through X-Groq-API-Key; it is used only for their request and is never stored in a report. Closing the tab clears the browser's session credentials.
Private operators can still send their key as X-API-Key. A key from SIM_ACCESS_KEYS_JSON owns every run it creates and receives 404 for another owner's run IDs; the optional SIM_API_KEY is an administrator override. Public mode is disabled unless explicitly enabled on the backend. Public requests always consume the compute budget, including requests carrying a personal Groq key; a personal key only changes which provider quota pays for the call. Limits are in memory on this single-process demo and reset when it restarts.
| Method | Path | Notes |
|---|---|---|
GET |
/api/health |
Liveness; includes storage.production_ready, storage.ephemeral, storage.path |
POST |
/api/simulate |
Multipart: niche, text, optional media |
POST |
/api/compare |
text_a + text_b |
GET |
/api/simulations?limit=20 |
Recent owner-visible run summaries; limit 1–100 |
GET |
/api/simulations/{id} |
Load a saved run owned by the authenticated key |
POST |
/api/simulations/{id}/replay |
Replay stored probabilities/config with compatibility checks |
GET/POST |
/api/simulations/{id}/outcome |
Read or record observed outcomes for future calibration |
GET |
/api/simulations/{id}/evaluation |
Compare recorded outcomes with stored priors; does not recalibrate |
GET |
/api/simulations/{id}/snapshot |
Read the immutable provenance/config manifest |
DELETE |
/api/simulations/{id} |
Delete a run and its outcome |
From backend, run python -m pytest -q. The suite includes an offline synthetic Gate C backtest (python -m app.gate_c_synthetic) that needs no Groq key; it does not complete empirical Gate C. From frontend, run npm run lint, npm run build, then npm run test:proxy. The proxy test starts only local fixture servers and checks the actual built handler, caller credentials, request size, timeouts and unavailable-backend errors. GitHub Actions runs these same checks. Browser regression steps and accepted SOP scope are recorded in SOP_REVIEW_PLAN.md and research/SOP_PILOT_ADDENDUM.md.
Prior-mapped research prototype, not empirically calibrated. Ranking weights follow public X defaults, not runtime experiments or the full production stack (no live Phoenix model/user history, Thunder, SimClusters retrieval, VMRanker, visibility filtering, or live traffic). Monte Carlo bands measure simulator randomness only; treat results as comparative scenarios, not forecasts.