Internal briefing
You are helping me build a Python web app that will be deployed with iVistaar.
## What iVistaar is
iVistaar is an internal deployment control plane for Windows Server/IIS. It gives Flask, Dash, FastAPI, Streamlit, and Gradio apps an organization URL without Git, Docker, Linux, or any public cloud service. I connect a folder on the server, click Deploy, and the app appears at:
http://<our-server>:8080/apps/<slug>/
The app never runs at the site root — it is always served under the /apps/<slug>/ path prefix by iVistaar's gateway.
## Runtime environment (assume all of this)
- Windows Server, Python 3.10+ (prefer 3.11–3.12 so pip wheels exist; do not require compiling pandas/numpy).
- The server is on our intranet. Do not rely on public internet at runtime: no CDN-hosted JS/CSS/fonts, no external API calls unless I explicitly allow them.
- Each app runs as its own Waitress process on a localhost port, behind iVistaar's reverse proxy. WebSockets work (Streamlit/Gradio) in service mode.
- No Git, no Docker, no systemd, no Linux-only commands or paths. Use pathlib / os.path, never hardcoded "/" or "\" separators.
## Entrypoint contract (iVistaar connects by module:variable)
- Flask: expose a WSGI object named `app` in app.py → entrypoint `app:app`.
- Dash: create `app = Dash(__name__)` and also set `server = app.server` at module scope → entrypoint `app:server` (or `run:server` if the file is run.py).
- NEVER set requests_pathname_prefix, routes_pathname_prefix, or serve_locally yourself — iVistaar injects the /apps/<slug>/ prefix and forces serve_locally=True. Setting them breaks asset URLs.
- FastAPI: `app = FastAPI()` → entrypoint `app:app` (served via a2wsgi).
- Streamlit: the entrypoint is `streamlit:app.py` where app.py is the script path.
- Gradio: expose the interface as a module-level variable named `demo` → entrypoint `app:demo`.
## Code rules so the app works behind the /apps/<slug>/ prefix
1. Never hardcode absolute URL paths. No href="/...", src="/...", fetch("/..."), url_for with a leading assumption of root. Use relative URLs ("./...", "../...") or the framework's URL helpers.
2. All frontend→backend requests must be relative so they stay under the prefix.
3. Do not call app.run()/uvicorn.run()/demo.launch() at module import time. Guard dev servers under `if __name__ == "__main__":`. iVistaar imports the module; it must not block or bind a port on import.
4. Ship a requirements.txt. Prefer versions with prebuilt Windows wheels. If you use pandas/numpy, target Python 3.11–3.12.
5. Write files only inside the app folder. Log to stdout/stderr, not to fixed log paths.
6. Answer GET / quickly with HTTP 200 — iVistaar health-checks the app after deploy.
7. Keep secrets out of code; read them from environment variables.
## What happens when I click Deploy
iVistaar creates a virtualenv, installs requirements.txt, generates a wrapper (ivistaar_serve.py), starts a scheduled task named iVistaarApp-<slug>, and reverse-proxies /apps/<slug>/ to it. Redeploy restarts that task.
## Failure modes to avoid (these are the ones that actually happen)
- Dash stuck on "Loading…" → wrong entrypoint or missing `server = app.server`, or someone set the pathname prefix manually.
- "DashRenderer is not defined" → component JS requested from the wrong path; caused by hardcoded absolute asset URLs or manual prefix settings.
- pip tries to compile pandas / asks for Visual Studio → wrong Python version; use 3.11–3.12 and wheels.
- Streamlit loads but widgets freeze → needs WebSocket support; fine on iVistaar service mode, broken under plain IIS HttpPlatformHandler.
- WinError 32 on redeploy → the app kept a file open (often a log); close file handles, log to stdout.
When you generate code, follow the entrypoint contract and the prefix rules above. If a choice would only work at the site root, change it to work under /apps/<slug>/ instead.