Skip to content

Repository files navigation

SL Theory Website

Small Flask app for exploring the drop-impact scaling theory described in the paper linked below. The browser collects Weber number We and Ohnesorge number Oh, then calls JSON endpoints to compute Reynolds number, classify the impact regime, and predict BetaMax. The homepage also supports batch CSV uploads for beta predictions.

What's in the repo

SLtheoryWebsite/
  app.py
  batchProcess.py
  calculateReynoldsNumber.py
  deploy.sh
  regimeDecide.py
  phase_diagram_svg.py
  SLtheory_prediction.py
  SLtheory_model.json
  static/
    tokens.css
    site.css
    site.js
    comphy-lab-mark.png
    favicon-96x96.png
  templates/index.html
  requirements.txt
  runtime.txt
  vercel.json

app.py creates the Flask app, registers the blueprints, caps request bodies at 1 MB, and runs the server locally. calculateReynoldsNumber.py serves the homepage and computes Reynolds number. regimeDecide.py classifies the regime from We and Oh and returns the model-based predBeta prediction using SLtheory_prediction.py and SLtheory_model.json. batchProcess.py accepts CSV uploads with We and Oh columns, reuses the same theory-range validation as /regime, and returns a CSV with beta filled in or error for invalid rows. The phase diagram is rendered in Python by phase_diagram_svg.py. The frontend is split between templates/index.html, static/site.css, and static/site.js.

Design system

The page follows the CoMPhy Lab design system. static/tokens.css is a verbatim copy of the upstream tokens.css, with a provenance header naming the upstream commit; do not edit it here. Change a core colour in the design-system repository, then re-vendor the file. static/site.css composes the token primitives (.panel, .card, .eyebrow, .btn, .field, .input, .chip, .hero-title) into the calculator layout and never redefines a token. Brand webfonts are self-hosted under static/fonts/ (Cormorant Garamond for the hero wordmark, Fraunces for headings, IBM Plex Sans and Mono for body and code), loaded via static/fonts/fonts.css before tokens.css. No Google Fonts CDN. phase_diagram_svg.py draws the server-side SVG with the same paper, ink and brand hues, so the figure matches the page in both themes. The theme choice is stored under the shared comphy-theme key and falls back to the OS preference.

Local run

pip install -r requirements.txt
python app.py

Or use the helper script to create a virtualenv, install dependencies, and start the local server in one step:

chmod +x deploy.sh
./deploy.sh

By default the script serves the app at http://127.0.0.1:5000 with Flask debug mode disabled. You can override the port from the CLI with ./deploy.sh 8000 or ./deploy.sh --port 8000. You can still override the bind address or default port with environment variables such as HOST=0.0.0.0 PORT=8000 ./deploy.sh.

If you need the Werkzeug debugger for local development, enable it explicitly on a loopback bind:

FLASK_DEBUG=1 ./deploy.sh

FLASK_DEBUG=1 is intentionally rejected for non-loopback HOST values to avoid exposing the debugger or debug tracebacks over the network.

The app is configured for Python 3.9 in runtime.txt.

Endpoints

  • GET / renders the single-page UI from templates/index.html.
  • POST /add expects JSON like {"weberNumber": 10, "ohnesorgeNumber": 0.1} with 1 <= We <= 10^3 and 10^-3 <= Oh <= 10^2, and returns {"result": <Re>} rounded to two decimals.
  • POST /regime expects the same JSON and range bounds, and returns {"regime": "I" | "II" | "III" | "IV", "predBeta": <BetaMax>} with predBeta rounded to two decimals.
  • POST /batch expects multipart/form-data with a .csv file field named file. The CSV must contain We and Oh headers, uses the same 1 <= We <= 10^3 and 10^-3 <= Oh <= 10^2 limits as /regime, and returns a CSV download with a beta column. Invalid rows are marked as error and summarized in X-Row-Errors. Uploads larger than 1 MB are rejected with HTTP 413.
  • GET /regime-diagram.svg returns a server-rendered SVG of the Weber-Ohnesorge phase diagram. Optional weberNumber, ohnesorgeNumber, and theme query parameters add the current input marker and match the active light or dark theme.

The frontend calls /add and /regime separately after the user submits the input form, then renders Re, Regime, and predBeta together. Numeric results are rounded in the Python endpoints and displayed to two decimal places in the UI. Inputs outside the theory range are rejected in both the browser and the Flask routes. The regime figure is generated by Python and returned as SVG.

Deployment

vercel.json routes all origin traffic to app.py using @vercel/python. Cloudflare's sl2-proxy Worker provides the canonical public hostname and proxies accepted requests to that Vercel origin.

The canonical public site is sl25.comphy-lab.org. Legacy GET and HEAD requests under comphy-lab.org/sl25 and comphy-lab.org/sl2 redirect permanently with HTTP 308 while preserving the path suffix and query string. Existing root API and asset routes on comphy-lab.org remain available for compatibility.

The Worker's two rate-limit bindings apply separate per-IP, per-route limits to calculation POSTs: 120 requests per minute for each of /add and /regime, and 20 requests per minute for /batch. Canonical and legacy forms share counters. Static assets, the phase-diagram GET, and other GET requests are not counted. Limited requests receive HTTP 429 with Retry-After: 60. Wrangler disables both workers.dev and preview URLs.

Run the Worker tests and validate its deployment bundle from cloudflare/:

node --test sl2-proxy.test.mjs
npx --yes wrangler@4.78.0 deploy --dry-run

The rate-limit binding requires Wrangler 4.36.0 or later.

Origin isolation

The imported WSGI application requires X-SL25-Origin-Token on every request, including static files and Socket.IO transport paths. The public Worker supplies this header from its SL25_ORIGIN_TOKEN secret after checking the public route and method. Client-supplied credentials are discarded. The hosted application stores only the SHA-256 verifier in origin-auth.json; the random 256-bit bearer must never be committed or exposed to the browser. Missing or malformed origin configuration returns a non-cacheable 503; missing or incorrect credentials return a non-cacheable 403 before application dispatch.

Vercel Authentication must use Standard Protection for this project. It protects older, generated and preview deployment URLs; the WSGI verifier protects the current production alias that Standard Protection leaves public. Verify both boundaries after deployment. Standard Protection is available on the existing Hobby plan; the paid All Deployments setting is not required.

For initial delivery, provision the Worker secret and deploy the reviewed proxy before deploying the guarded Python application. Verify ordinary canonical and legacy requests before moving to the second stage. Upstream redirects are rejected, and legacy HTML assets use the canonical public hostname, so the credential cannot escape through redirected or browser-issued origin requests. Treat token rotation as a coordinated release; zero-downtime rotation needs a staged verifier change that temporarily accepts both token digests.

Run the origin and compatibility tests with the repository's Python dependencies:

python -m unittest discover -s tests -p 'test_*.py'

For anonymous local development, use python app.py or ./deploy.sh. Both use the explicit local entry point and preserve HOST, PORT and loopback-only debug behaviour. Importing app through a WSGI server enables the origin guard even when Vercel's environment markers are absent.

Production release and rollback receipt

This site has two production releases: the Vercel Python origin and the Cloudflare sl2-proxy Worker. Record them as one compatible pair after a change. The release receipt must contain:

  1. The Git main commit, passing Python origin tests and Worker tests, and the Vercel production deployment ID, source commit and production alias read back from Vercel. A successful build alone does not prove which origin the alias serves.
  2. The active Cloudflare Worker version ID, the custom-domain and legacy route assignments read back from Cloudflare, and confirmation that the SL25_ORIGIN_TOKEN secret binding exists. Record presence only, never its value or the verifier digest.
  3. Live checks through https://sl25.comphy-lab.org/: the page, a local static asset, the phase diagram and representative /add, /regime and /batch requests. Check the legacy /sl25 and /sl2 GET/HEAD redirects and the retained root API paths. Confirm the rate-limit bindings and unrelated-path denial from the deployed Worker configuration and its automated tests; do not exhaust the production rate limits for a receipt.
  4. Separate origin-isolation checks: an unauthenticated request to the Vercel production alias is denied by the WSGI guard, and older/generated/preview deployment URLs remain under Vercel Standard Protection. Confirm workers.dev and Worker preview URLs are still disabled.

For a failed release, restore the last verified pair. Promote the previous known-good Vercel deployment if the origin changed; roll back to the previous known-good Worker version if the proxy changed. Keep the Worker secret and Vercel verifier compatible throughout. Re-run the public route and origin isolation checks, and record the restored deployment and Worker version IDs. Credential rotation needs its staged verifier procedure above; rolling back only one side of a rotation can make the public site fail closed.

Known limitations

  • The canonical public calculator remains callable within its generous limits; GET requests, including phase diagrams, remain outside the calculation counters.
  • Existing root API routes on comphy-lab.org remain part of the compatibility surface; a later DNS-only transition for the main site needs separate route planning.
  • Cloudflare's browser-integrity control returns error 1010 for Python's default user agent on both canonical and legacy hosts; browsers and curl succeed.

Notes

  • The code validates that inputs are present, JSON object shaped, numeric, finite, and positive before doing the Reynolds, regime, or predBeta calculations, and batch CSV rows reuse the same theory-range validation.
  • Flask-SocketIO is used to run the app locally, but there are no socket event handlers in the current app. The helper keeps debug mode off by default and only allows FLASK_DEBUG=1 on loopback hosts.
  • The page loads MathJax from a CDN and embeds a YouTube iframe, so full rendering depends on external network access. The obsolete polyfill has been removed.

About

This is a website to render the results of SL theory, https://arxiv.org/abs/2408.12714

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages