[App Lifecycle]

Flask application setup: MAX_CONTENT_LENGTH, CORS policy, blueprints, error handlers, and process entry.

App Lifecycle

Abstract

backend/app.py constructs a single global Flask app, applies security-minded defaults (body size, CORS allowlist), registers free and Pro routes, and exposes a module __main__ for local development. There is no multi-app factory pattern today — import app for WSGI (gunicorn) or run the module for the built-in server.

Conceptual model

Interface surface

Process entry

make run
# equivalent
python -m backend.app

Environment:

VariableDefaultRole
HOST127.0.0.1Bind address (localhost-first for dev)
PORT5002Listen port
ALLOWED_ORIGINShttps://pynescript.ai, https://app.pynescript.ai, localhost regexCORS origins (comma-separated; regex allowed)
API_KEY_STORE/root/pynescript/data/api_keys.jsonJSON key store path (file-backed default)
ADMIN_TOKENunsetDocumented for admin create_key (see auth)

debug=False always on the dev runner.

Health

GET / → JSON:

{
  "status": "healthy",
  "service": "pynescript-pro-api",
  "version": "1.0.0",
  "timestamp": 0,
  "endpoints": { "…": "…" }
}

Hard limits

  • MAX_CONTENT_LENGTH = 5 * 1024 * 1024 — reject oversized bodies before JSON parse fills memory (audit 2026-07-05 / S1).
  • CORS methods: GET, POST only.
  • Allow headers: Content-Type, Authorization, X-Admin-Token.
  • supports_credentials=False.

Localhost regex used in the default origin list:

^https?://(?:localhost|127\.0\.0\.1)(?::\d+)?$

Same-origin / no Origin header (curl, server-to-server) remains usable under flask-cors behavior.

Blueprints

app.register_blueprint(preview_bp)   # url_prefix=/preview
app.register_blueprint(backtest_bp)  # url_prefix=/backtest

Defined in backend/api/preview.py.

Error handlers

CodeBody codeMessage
404NOT_FOUNDEndpoint {path} not found.
500INTERNAL_ERRORInternal server error.

Both return JSON with status: "error".

Internals

PathRole
backend/app.pyApp object, free routes, auth routes
backend/api/preview.pyPro blueprints
backend/middleware/schemas.pyRequest validation for /run* and auth
backend/requirements.txtFlask, flask-cors, numpy, matplotlib, …

Recommended production: gunicorn/uvicorn-class WSGI with multiple workers; for multi-worker key consistency prefer SQLite/Redis stores over the default in-memory/file singleton assumptions — see auth.

Invariants and edge cases

  1. Import side effects: creating app configures CORS immediately from env.
  2. Body too large → Flask 413 before route logic (not custom JSON).
  3. Unknown fields on schema-validated routes → UNKNOWN_FIELDS 400 (/run, auth); preview routes currently use looser get_json() (schemas exist but are not always applied in handlers).
  4. Dev bind default is loopback — set HOST=0.0.0.0 only when intentional.

Worked example — smoke test

curl -s http://127.0.0.1:5002/ | jq .status
# "healthy"

Failure modes

SymptomCause
Browser CORS errorsOrigin not in ALLOWED_ORIGINS
413 on large OHLCVExceeded 5 MB — downsample or paginate
Import errors for matplotlibMissing backend deps — pip install -r backend/requirements.txt

See also