# Host IFCStudio on a VPS IFCStudio is a static Work in progress release. The browser runs the application and reads local IFC files. The VPS serves files over HTTPS; it needs no GPU, upload endpoint, Python runtime for the application, or database. Use a Linux VPS with DNS for your domain, ports 80 and 443 open, and disk space for three complete releases plus a staging copy. Install rsync and Python 3 for the deployment script. Use a dedicated SSH account with write access to the release directory and grant the web server read and traverse access. Desktop Chrome 113 or later with hardware acceleration provides WebGPU; an up-to-date Chrome is recommended. See [Chrome's WebGPU support notes](https://developer.chrome.com/docs/web-platform/webgpu/overview). ## Build and check the package Prepare the pinned toolchain once as described in the checkout's `docs/Web.md`. Prepare the WASM EnergyPlus engine and catalogue as well: `python3 tools/build_energyplus.py --platform web --emsdk "$EMSDK" --jobs 4`. With `EMSDK` set and `brotli`, Python 3, CMake and Ninja on your build machine: ```sh python3 tools/package_web.py python3 tools/serve_web.py --directory dist/ifcstudio-web ``` This builds the `web` Release preset with EnergyPlus and the example plugins enabled, and writes `dist/ifcstudio-web/`. Check the page in Chrome at `http://localhost:8080/`. Localhost counts as a secure context. Do not open the HTML from disk. Keep the adjacent `.br` and `.gz` variants and the `licenses/` tree when transferring the package. The first Python or EnergyPlus action may fetch a separate content-addressed file. The EnergyPlus catalogue is cached by its full hash. Serve the entire public directory so those features remain available. Keep `dist/ifcstudio-symbols/viewer..symbols` for crash diagnosis; the deployment script uploads only `dist/ifcstudio-web/`. ## Set up HTTPS Use either config in the repository. Replace `ifcstudio.example.com` and `/srv/ifcstudio` everywhere. Both configs serve `/srv/ifcstudio/current`. - **Caddy 2:** install `deploy/caddy/Caddyfile` from the checkout at `/etc/caddy/Caddyfile`. Once DNS points to the VPS, Caddy gets and renews certificates and redirects HTTP to HTTPS. Run `caddy validate --config /etc/caddy/Caddyfile` before reloading the service. - **nginx:** install `deploy/nginx/ifcstudio.conf` from the checkout into the server's `http` context, typically `/etc/nginx/conf.d/ifcstudio.conf`. Use nginx with HTTP/2 and `ngx_http_gzip_static_module` (check `nginx -V`). The template uses nginx 1.25.1 syntax; the listener comment covers older releases. Install and load the matching `ngx_brotli` static module or remove `brotli_static on;` to use the gzip variants. The HTTPS listener needs existing certificates. Initially enable just the HTTP block and create `/var/www/letsencrypt`, then run `certbot certonly --webroot -w /var/www/letsencrypt -d ifcstudio.example.com`. Enable the full config, run `nginx -t`, reload nginx and enable certbot renewal (with a service reload after renewal). ## Deploy and roll back The owner runs this from the checkout with a complete package: ```sh tools/deploy_web.sh deploy@ifcstudio.example.com /srv/ifcstudio ``` The script uploads to a unique staging directory with rsync and only then atomically switches `current` to the new release. It retains the latest three managed releases. It refuses to replace a real directory named `current`. For an offline rehearsal use `tools/deploy_web.sh --local /tmp/ifcstudio-test`. To roll back on the VPS, list `/srv/ifcstudio/releases`, select a previous release and atomically switch back (Linux, from the release root): ```sh cd /srv/ifcstudio ln -s releases/PREVIOUS-RELEASE .rollback mv -Tf .rollback current ``` Use the release's actual directory name and avoid rolling back during a deploy. An already open browser tab can reload if a release changed during its startup. ## Verify the deployment ```sh curl -I https://ifcstudio.example.com/ curl -I https://ifcstudio.example.com/build.json curl -I -H 'Accept-Encoding: br, gzip' https://ifcstudio.example.com/viewer.HASH.wasm ``` Use the actual WASM name from `build.json`. Expect COOP `same-origin`, COEP `require-corp`, CORP `same-origin`, and `X-Content-Type-Options: nosniff`. Expect `Cache-Control: no-cache` on HTML and the manifest, and `public, max-age=31536000, immutable` on hashed files. WASM should report `Content-Type: application/wasm`, and `Content-Encoding: br` (or `gzip` with the nginx fallback). In Chrome DevTools, `crossOriginIsolated` should be `true`. ## Package sizes The table below reports decoded, Brotli 11 and gzip 9 sizes. Packaging regenerates it for each release. The manifest also lists each file with its SHA-256. | File | Raw bytes | Brotli 11 | gzip 9 | | --- | ---: | ---: | ---: | | Inter-Regular.woff2 | 140,944 | 140,949 | 140,914 | | Inter-SemiBold.woff2 | 144,628 | 144,633 | 144,566 | | energyplus.sqlite | 135,782,400 | 9,990,645 | 15,043,989 | | python.pack | 63,365,904 | 8,296,673 | 13,428,136 | | viewer.js | 229,625 | 45,873 | 53,037 | | viewer.wasm | 107,705,398 | 20,520,636 | 30,399,307 | | index.html | 20,612 | 5,717 | 6,834 | | licenses/ (all notices) | 858,896 | 245,016 | 319,131 |