677 lines
24 KiB
Markdown
677 lines
24 KiB
Markdown
# UCE Runtime Setup
|
|
|
|
This guide describes how to run UCE behind nginx or Apache. UCE is a FastCGI application server for `.uce` units; the web server should serve static files directly and forward dynamic `.uce` requests to the UCE runtime.
|
|
|
|
## Deployment shape
|
|
|
|
A typical deployment has four pieces:
|
|
|
|
1. A checked-out or packaged UCE runtime tree.
|
|
2. `/etc/uce/settings.cfg`, read by the UCE runtime at startup.
|
|
3. `uce.service`, a systemd service that builds/starts/restarts the runtime.
|
|
4. nginx or Apache as the public HTTP server.
|
|
|
|
Recommended filesystem layout for a source checkout:
|
|
|
|
```text
|
|
/opt/uce/ UCE repository/runtime root
|
|
/var/www/html/ public web root served by nginx/Apache
|
|
/etc/uce/settings.cfg runtime configuration
|
|
/run/uce/fastcgi.sock FastCGI socket used by nginx/Apache
|
|
/run/uce/cli.sock local CLI/admin/test socket
|
|
/var/cache/uce/work generated source, wasm modules, caches
|
|
/var/lib/uce/uploads multipart upload scratch space
|
|
/var/lib/uce/sessions session files
|
|
```
|
|
|
|
For packaged installs, the runtime may live under `/usr/lib/uce` instead of `/opt/uce`. Keep the public web root at `/var/www/html` or another normal web-root path, not under the runtime source tree.
|
|
|
|
## Build requirements
|
|
|
|
On Debian/Ubuntu-like systems, install the distro packages first:
|
|
|
|
```bash
|
|
apt update
|
|
apt install -y clang build-essential libpcre2-dev mariadb-client libmariadb-dev curl rsync ca-certificates
|
|
```
|
|
|
|
UCE also requires two non-vendored dependencies. WASI SDK is load-bearing at runtime because UCE compiles units on demand during requests and during proactive startup scans. The `curl` binary is also a pinned runtime package dependency: `http_request()` and `http_request_async()` execute it directly with an explicit argument vector for TLS-capable outbound HTTP.
|
|
|
|
- **Wasmtime C API / C++ headers** at `/opt/wasmtime` by default. `scripts/build_linux.sh` expects:
|
|
- `/opt/wasmtime/include/wasmtime.hh`
|
|
- `/opt/wasmtime/include/wasmtime/*.h`
|
|
- `/opt/wasmtime/lib/libwasmtime.so`
|
|
|
|
- **Pinned WASI SDK** at `/opt/wasi-sdk` by default. `scripts/build_core_wasm.sh` and request-time `scripts/compile_wasm_unit` expect:
|
|
- `/opt/wasi-sdk/bin/clang++`
|
|
- `/opt/wasi-sdk/bin/wasm-ld`
|
|
- `/opt/wasi-sdk/bin/llvm-objcopy`
|
|
|
|
You can use different install locations by setting environment variables before building and in the systemd service environment:
|
|
|
|
```bash
|
|
export WASMTIME_HOME=/path/to/wasmtime
|
|
export WASI_SDK=/path/to/wasi-sdk
|
|
```
|
|
|
|
Install one web server:
|
|
|
|
```bash
|
|
apt install -y nginx
|
|
# or
|
|
apt install -y apache2
|
|
```
|
|
|
|
Build UCE from the repository root:
|
|
|
|
```bash
|
|
cd /opt/uce
|
|
bash scripts/build_core_wasm.sh
|
|
bash scripts/build_linux.sh
|
|
```
|
|
|
|
Publish the starter site or your application files into the web root:
|
|
|
|
```bash
|
|
mkdir -p /var/www/html
|
|
rsync -a site/ /var/www/html/
|
|
```
|
|
|
|
The main binary is written to:
|
|
|
|
```text
|
|
bin/uce_fastcgi.linux.bin
|
|
```
|
|
|
|
### Installing Wasmtime and WASI SDK
|
|
|
|
The UCE build does not download these dependencies for you. Install Wasmtime through a compatible distro package or a pinned upstream C API archive. Install WASI SDK with UCE's pinned installer, or unpack the same pinned archive under `/opt/wasi-sdk`.
|
|
|
|
Do not use `curl | sh` installers in production setup scripts. Download archives from the upstream release pages, verify checksums/signatures when available, and record the exact versions in your deployment notes. Avoid installing a release published in the last few days unless you have reviewed it separately.
|
|
|
|
The expected directories are:
|
|
|
|
```text
|
|
/opt/wasmtime/include/wasmtime.hh
|
|
/opt/wasmtime/include/wasmtime/store.h
|
|
/opt/wasmtime/lib/libwasmtime.so
|
|
|
|
/opt/wasi-sdk/bin/clang++
|
|
/opt/wasi-sdk/bin/wasm-ld
|
|
/opt/wasi-sdk/bin/llvm-objcopy
|
|
```
|
|
|
|
Install the WASI SDK:
|
|
|
|
```bash
|
|
cd /opt/uce
|
|
scripts/install_wasi_sdk.sh
|
|
scripts/install_wasi_sdk.sh --check-only
|
|
```
|
|
|
|
The current pin is documented in `docs/wasi-sdk-toolchain.md`. The script verifies the archive SHA256 before installing and updates `/opt/wasi-sdk` to point at the pinned versioned directory.
|
|
|
|
For Wasmtime, use a compatible distro package or an upstream C API archive. Example flow using an archive you have already chosen and verified:
|
|
|
|
```bash
|
|
mkdir -p /opt /tmp/uce-deps
|
|
cd /tmp/uce-deps
|
|
|
|
# Download the Wasmtime C API archive for your architecture from the upstream
|
|
# release page, then verify its checksum before unpacking. The archive name
|
|
# normally contains "c-api".
|
|
sha256sum -c wasmtime-c-api.sha256
|
|
mkdir -p /opt/wasmtime
|
|
tar -xf wasmtime-*-c-api*.tar.* -C /opt/wasmtime --strip-components=1
|
|
```
|
|
|
|
After unpacking, verify the tools UCE needs. Also record the exact Wasmtime and WASI SDK versions used. The native build embeds an rpath for `$WASMTIME_HOME/lib`, so the service environment should use the same `WASMTIME_HOME` value used during build.
|
|
|
|
```bash
|
|
test -f /opt/wasmtime/include/wasmtime.hh
|
|
test -f /opt/wasmtime/lib/libwasmtime.so
|
|
/opt/wasi-sdk/bin/clang++ --version
|
|
/opt/wasi-sdk/bin/wasm-ld --version
|
|
/opt/wasi-sdk/bin/llvm-objcopy --version
|
|
```
|
|
|
|
If your paths differ, export the variables for manual builds:
|
|
|
|
```bash
|
|
WASMTIME_HOME=/usr/local/wasmtime WASI_SDK=/usr/local/wasi-sdk bash scripts/build_core_wasm.sh
|
|
WASMTIME_HOME=/usr/local/wasmtime WASI_SDK=/usr/local/wasi-sdk bash scripts/build_linux.sh
|
|
```
|
|
|
|
For systemd, add an override:
|
|
|
|
```bash
|
|
systemctl edit uce.service
|
|
```
|
|
|
|
```ini
|
|
[Service]
|
|
Environment=WASMTIME_HOME=/usr/local/wasmtime
|
|
Environment=WASI_SDK=/usr/local/wasi-sdk
|
|
```
|
|
|
|
Then reload and restart:
|
|
|
|
```bash
|
|
systemctl daemon-reload
|
|
systemctl restart uce.service
|
|
```
|
|
|
|
## Runtime configuration
|
|
|
|
Create `/etc/uce/settings.cfg` from `etc/uce/settings.cfg` and adjust paths if your runtime is not under `/opt/uce`. Replace any checkout-specific paths such as `WASM_CORE_PATH` with `/opt/uce/bin/wasm/core.wasm` or your actual runtime path.
|
|
|
|
Minimum useful settings:
|
|
|
|
```ini
|
|
BIN_DIRECTORY=/var/cache/uce/work
|
|
TMP_UPLOAD_PATH=/var/lib/uce/uploads
|
|
SESSION_PATH=/var/lib/uce/sessions
|
|
|
|
FCGI_SOCKET_PATH=/run/uce/fastcgi.sock
|
|
CLI_SOCKET_PATH=/run/uce/cli.sock
|
|
|
|
SITE_DIRECTORY=/var/www/html
|
|
HTTP_DOCUMENT_ROOT=/var/www/html
|
|
JIT_COMPILE_ON_REQUEST=1
|
|
PROACTIVE_COMPILE_ENABLED=1
|
|
PROACTIVE_COMPILE_CHECK_INTERVAL=60
|
|
|
|
WASM_COMPILE_SCRIPT=scripts/compile_wasm_unit
|
|
WASM_BACKEND_VERBOSE=0
|
|
WASM_CORE_PATH=/opt/uce/bin/wasm/core.wasm
|
|
WASM_MEMORY_LIMIT_BYTES=536870912
|
|
WASM_EPOCH_DEADLINE_TICKS=200
|
|
WASM_EPOCH_PERIOD_MS=50
|
|
|
|
WORKER_COUNT=4
|
|
MAX_MEMORY=16777216
|
|
SESSION_TIME=2592000
|
|
|
|
HTTP_PORT=8080
|
|
WS_BROKER_OUTBOUND_TIMEOUT_SECONDS=30
|
|
```
|
|
|
|
Important settings:
|
|
|
|
- `FCGI_SOCKET_PATH` is the Unix socket used for normal `.uce` requests. Set it explicitly and keep this value and the web-server `fastcgi_pass` path identical. The reference config uses `/run/uce/fastcgi.sock`; if you choose `/run/uce.sock`, use it in both places.
|
|
- `CLI_SOCKET_PATH` is a local HTTP-over-Unix socket used by `scripts/uce-cli` and test/admin units.
|
|
- `SITE_DIRECTORY` is the public site tree to scan for `.uce` files. Use `/var/www/html` when the web root is outside the runtime tree; relative paths are resolved from the runtime working directory.
|
|
- `HTTP_DOCUMENT_ROOT` is the root used by the built-in HTTP/WebSocket listener when it resolves upgrade requests. Set it to the same web root as nginx/Apache.
|
|
- `BIN_DIRECTORY` stores generated C++, wasm artifacts, compile output, and runtime caches.
|
|
- `TMP_UPLOAD_PATH` and `SESSION_PATH` must be writable by the runtime.
|
|
- `HTTP_PORT` is the built-in HTTP/WebSocket listener used for WebSocket upgrade traffic and direct local probes. Bind/firewall it for local access only; nginx/Apache should be the public entry point.
|
|
- `WS_BROKER_OUTBOUND_TIMEOUT_SECONDS` controls how long a forwarded WS message can remain queued in the broker before being dropped (default `30`). Set to `0` to disable the timeout.
|
|
- `WASM_COMPILE_SCRIPT` must point to `scripts/compile_wasm_unit` unless you provide an equivalent compiler. Relative paths are resolved from the runtime root/`COMPILER_SYS_PATH`. That script calls `scripts/check_unit_wasm.py` after linking each unit and uses the pinned WASI SDK on every deployment host.
|
|
- `WASM_CORE_PATH` must point at the built `core.wasm` file.
|
|
|
|
After editing settings, restart UCE:
|
|
|
|
```bash
|
|
systemctl restart uce.service
|
|
```
|
|
|
|
## systemd service
|
|
|
|
For source-checkout deployments, install the provided service helper:
|
|
|
|
```bash
|
|
cd /opt/uce
|
|
scripts/systemd/manage-uce-service.sh setup
|
|
```
|
|
|
|
That helper installs `scripts/systemd/uce.service`, creates runtime directories, enables the service, and starts it.
|
|
|
|
Useful commands:
|
|
|
|
```bash
|
|
scripts/systemd/manage-uce-service.sh status
|
|
scripts/systemd/manage-uce-service.sh restart
|
|
scripts/systemd/manage-uce-service.sh logs 200
|
|
```
|
|
|
|
Equivalent manual systemd service for a source checkout:
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=UCE FastCGI Runtime
|
|
After=network-online.target mariadb.service memcached.service
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
WorkingDirectory=/opt/uce
|
|
RuntimeDirectory=uce
|
|
StateDirectory=uce
|
|
CacheDirectory=uce
|
|
ExecStartPre=/usr/bin/mkdir -p /var/cache/uce/work /var/lib/uce/uploads /var/lib/uce/sessions
|
|
ExecStartPre=/usr/bin/rm -f /run/uce/fastcgi.sock
|
|
ExecStartPre=/usr/bin/bash /opt/uce/scripts/build_linux.sh
|
|
ExecStart=/opt/uce/bin/uce_fastcgi.linux.bin
|
|
ExecStopPost=/usr/bin/rm -f /run/uce/fastcgi.sock
|
|
Restart=always
|
|
RestartSec=2
|
|
TimeoutStopSec=15
|
|
KillMode=mixed
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
Install it as `/etc/systemd/system/uce.service` and run:
|
|
|
|
```bash
|
|
systemctl daemon-reload
|
|
systemctl enable --now uce.service
|
|
```
|
|
|
|
### Debian package build
|
|
|
|
To build a Debian package from the repository root:
|
|
|
|
```bash
|
|
bash scripts/make_deb.sh 0.1.2
|
|
```
|
|
|
|
The Debian package creator bundles WASI SDK and Wasmtime by default when `/opt/wasi-sdk` and `/opt/wasmtime` are present. Verify the pinned SDK before building:
|
|
|
|
```bash
|
|
scripts/install_wasi_sdk.sh --check-only
|
|
bash scripts/make_deb.sh 0.1.2
|
|
```
|
|
|
|
This includes the resolved `/opt/wasi-sdk-...` tree, `/opt/wasi-sdk` symlink, resolved `/opt/wasmtime-...` tree, and `/opt/wasmtime` symlink in the package. It makes the package large, but keeps request-time unit compilation and runtime linking tied to the toolchain versions that passed the test suite. Set `UCE_DEB_BUNDLE_WASI_SDK=0` or `UCE_DEB_BUNDLE_WASMTIME=0` only if your deployment provides those exact dependencies separately.
|
|
|
|
### RPM package build
|
|
|
|
To build an RPM package from the repository root, install `rpmbuild` on the packaging host, verify the pinned SDK, then run:
|
|
|
|
```bash
|
|
scripts/install_wasi_sdk.sh --check-only
|
|
bash scripts/make_rpm.sh 0.1.2
|
|
```
|
|
|
|
The RPM creator mirrors the Debian package layout: runtime files under `/usr/lib/uce`, public files under `/var/www/html`, config under `/etc/uce/settings.cfg`, systemd unit under `/usr/lib/systemd/system/uce.service`, and bundled `/opt/wasi-sdk` plus `/opt/wasmtime` trees by default. Set `UCE_RPM_BUNDLE_WASI_SDK=0` or `UCE_RPM_BUNDLE_WASMTIME=0` only if your deployment provides those exact dependencies separately.
|
|
|
|
## How request routing works
|
|
|
|
### Static files
|
|
|
|
The web server should serve ordinary static files directly from the public web root, for example `/var/www/html`.
|
|
|
|
Examples:
|
|
|
|
```text
|
|
/style.css
|
|
/images/logo.png
|
|
/examples/uce-starter/js/site.js
|
|
```
|
|
|
|
These should not touch the UCE runtime.
|
|
|
|
### Normal `.uce` page requests
|
|
|
|
For a request such as:
|
|
|
|
```text
|
|
GET /doc/index.uce?p=component
|
|
```
|
|
|
|
nginx/Apache forwards the request to `FCGI_SOCKET_PATH` as FastCGI. The web server must provide CGI/FastCGI variables including:
|
|
|
|
- `SCRIPT_FILENAME` — full filesystem path to the `.uce` file.
|
|
- `DOCUMENT_ROOT` — public web root, normally `/var/www/html` or whatever nginx/Apache uses as `root`/`DocumentRoot`.
|
|
- `SCRIPT_NAME` — URL path to the script, such as `/doc/index.uce`.
|
|
- `DOCUMENT_URI` — normalized URI path without query string.
|
|
- `REQUEST_URI` — original request URI including query string.
|
|
- standard request variables such as method, query string, content type, body length, cookies, and headers.
|
|
|
|
UCE resolves the unit, compiles it to wasm if needed, creates a request workspace, and calls:
|
|
|
|
```cpp
|
|
RENDER(Request& context)
|
|
```
|
|
|
|
The unit writes output with template literals or `print()`. Response headers and status are set through `context.header` and `context.set_status()`.
|
|
|
|
### Component and sub-render calls
|
|
|
|
Inside a request, UCE code can call other units:
|
|
|
|
```cpp
|
|
component("components/card", props, context);
|
|
unit_render("other-page.uce", context);
|
|
```
|
|
|
|
These calls stay inside the UCE runtime. They are not new HTTP requests and do not go back through nginx or Apache.
|
|
|
|
### WebSocket pages
|
|
|
|
Any `.uce` unit can provide both an ordinary page render and WebSocket message handling:
|
|
|
|
```cpp
|
|
RENDER(Request& context) { ... } // normal page load
|
|
WS(Request& context) { ... } // later WebSocket messages
|
|
```
|
|
|
|
The nginx and Apache examples below split traffic by checking for a WebSocket upgrade request on `.uce` paths. A file such as `chat.uce` or `events.uce` can expose `WS(Request& context)`.
|
|
|
|
Routing split:
|
|
|
|
- Plain `GET /demo/chat.uce` should use FastCGI, just like any other page render.
|
|
- WebSocket upgrade requests for `/demo/chat.uce` should proxy to the UCE built-in HTTP/WebSocket listener at `HTTP_PORT`.
|
|
|
|
The built-in listener owns the socket lifecycle. When a message arrives, the broker forwards a render-style invocation back to the worker pool so `WS(Request& context)` runs inside the same wasm runtime model as normal pages.
|
|
|
|
### CLI requests
|
|
|
|
`CLI(Request& context)` handlers are not public web endpoints. They are invoked over `CLI_SOCKET_PATH`:
|
|
|
|
```bash
|
|
scripts/uce-cli /tests/cli.uce action=echo message=hello
|
|
curl --unix-socket /run/uce/cli.sock http://localhost/tests/cli.uce
|
|
```
|
|
|
|
Use CLI units for local tests, admin commands, and maintenance tools. Do not expose the CLI socket through nginx or Apache.
|
|
|
|
### Custom runtime HTTP servers
|
|
|
|
UCE code can start local custom HTTP listeners with `server_start_http()`. Those are runtime-managed listeners for app-specific local services. They are separate from the public nginx/Apache entry point and should be firewalled or bound locally unless you explicitly want them reachable.
|
|
|
|
## nginx configuration
|
|
|
|
### Required modules
|
|
|
|
A normal nginx build includes the needed FastCGI and proxy modules. Confirm nginx is installed and can load your config:
|
|
|
|
```bash
|
|
nginx -t
|
|
```
|
|
|
|
### WebSocket upgrade map
|
|
|
|
Put this in the nginx `http` block if using WebSockets:
|
|
|
|
```nginx
|
|
map $http_upgrade $connection_upgrade {
|
|
default upgrade;
|
|
'' close;
|
|
}
|
|
```
|
|
|
|
### Server block
|
|
|
|
Example site config:
|
|
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name example.com;
|
|
|
|
root /var/www/html;
|
|
index index.uce index.html;
|
|
|
|
# Serve static files directly. Directory requests use index.uce when present.
|
|
location / {
|
|
try_files $uri $uri/ =404;
|
|
}
|
|
|
|
# UCE page requests use FastCGI. If the client asks to upgrade a .uce
|
|
# request to WebSocket, send that connection to the built-in listener.
|
|
location ~ \.uce$ {
|
|
error_page 418 = @uce_websocket;
|
|
if ($http_upgrade = "websocket") {
|
|
return 418;
|
|
}
|
|
|
|
include fastcgi_params;
|
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
fastcgi_param DOCUMENT_ROOT $document_root;
|
|
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
|
fastcgi_param DOCUMENT_URI $uri;
|
|
fastcgi_param REQUEST_URI $request_uri;
|
|
fastcgi_pass unix:/run/uce/fastcgi.sock;
|
|
}
|
|
|
|
location @uce_websocket {
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection $connection_upgrade;
|
|
proxy_pass http://127.0.0.1:8080;
|
|
}
|
|
|
|
# Defense in depth if the root is changed later.
|
|
location ~ ^/(src|scripts|etc|bin|work|dist|pkg|docs|changelog)/ {
|
|
return 404;
|
|
}
|
|
}
|
|
```
|
|
|
|
Notes:
|
|
|
|
- `fastcgi_pass` must match `FCGI_SOCKET_PATH`.
|
|
- `proxy_pass` must match `HTTP_PORT`.
|
|
- The example routes WebSocket upgrades for `.uce` paths to the HTTP/WebSocket listener.
|
|
- The built-in HTTP/WebSocket listener resolves scripts from `HTTP_DOCUMENT_ROOT`; do not depend on client-supplied or proxied `Script-Filename` headers for routing.
|
|
- Ordinary `.uce` page loads continue to use FastCGI.
|
|
- Keep `root` pointed at `/var/www/html`, not the runtime repository root.
|
|
- If your app uses a front controller, replace `location /` with a `try_files` rule that ends at `/index.uce`.
|
|
|
|
Front-controller variant:
|
|
|
|
```nginx
|
|
location / {
|
|
try_files $uri $uri/ /index.uce?$query_string;
|
|
}
|
|
```
|
|
|
|
Reload nginx:
|
|
|
|
```bash
|
|
nginx -t
|
|
systemctl reload nginx
|
|
```
|
|
|
|
## Apache configuration
|
|
|
|
Apache can run UCE through `mod_proxy_fcgi` for FastCGI and `mod_proxy_wstunnel` or `mod_proxy_http` for WebSocket upgrades.
|
|
|
|
### Enable modules
|
|
|
|
On Debian/Ubuntu:
|
|
|
|
```bash
|
|
a2enmod proxy proxy_fcgi proxy_http proxy_wstunnel rewrite headers setenvif
|
|
systemctl restart apache2
|
|
```
|
|
|
|
### VirtualHost example
|
|
|
|
```apache
|
|
<VirtualHost *:80>
|
|
ServerName example.com
|
|
DocumentRoot /var/www/html
|
|
|
|
<Directory /var/www/html>
|
|
Require all granted
|
|
Options FollowSymLinks
|
|
AllowOverride None
|
|
DirectoryIndex index.uce index.html
|
|
</Directory>
|
|
|
|
# Do not expose repository internals if DocumentRoot changes later.
|
|
<LocationMatch "^/(src|scripts|etc|bin|work|dist|pkg|docs|changelog)/">
|
|
Require all denied
|
|
</LocationMatch>
|
|
|
|
RewriteEngine On
|
|
|
|
# WebSocket upgrade traffic for any .uce unit goes to UCE's built-in HTTP listener.
|
|
RewriteCond %{HTTP:Upgrade} =websocket [NC]
|
|
RewriteCond %{REQUEST_URI} \.uce(?:\?|$) [NC]
|
|
RewriteRule ^/(.*)$ ws://127.0.0.1:8080/$1 [P,L]
|
|
|
|
# Normal .uce page loads go to FastCGI.
|
|
<FilesMatch "\.uce$">
|
|
SetHandler "proxy:unix:/run/uce/fastcgi.sock|fcgi://localhost/"
|
|
</FilesMatch>
|
|
|
|
# Optional: make the key CGI variables explicit for UCE.
|
|
ProxyFCGISetEnvIf "true" DOCUMENT_ROOT "/var/www/html"
|
|
ProxyFCGISetEnvIf "true" DOCUMENT_URI "%{REQUEST_URI}"
|
|
</VirtualHost>
|
|
```
|
|
|
|
Apache notes:
|
|
|
|
- `SetHandler "proxy:unix:/run/uce/fastcgi.sock|fcgi://localhost/"` must use the same socket path as `FCGI_SOCKET_PATH`.
|
|
- The WebSocket rewrite rule must run before the FastCGI handler.
|
|
- Plain `.uce` page loads should not be proxied as WebSockets unless the client sends `Upgrade: websocket`.
|
|
- Apache's FastCGI environment differs by version and module configuration. If UCE cannot resolve a page, inspect the request environment and make sure `SCRIPT_FILENAME` points to the target file under the web root.
|
|
|
|
If your Apache version does not populate `SCRIPT_FILENAME` correctly through `SetHandler`, use `ProxyPassMatch` for `.uce` files instead:
|
|
|
|
```apache
|
|
ProxyPassMatch ^/(.*\.uce)$ unix:/run/uce/fastcgi.sock|fcgi://localhost/var/www/html/$1
|
|
```
|
|
|
|
Use only one FastCGI mapping style at a time (`SetHandler` or `ProxyPassMatch`) to avoid duplicate routing.
|
|
|
|
## Permissions
|
|
|
|
The web server needs permission to connect to `/run/uce/fastcgi.sock`. Common approaches:
|
|
|
|
- run UCE and the web server under compatible groups;
|
|
- add the web server user (`www-data` on Debian/Ubuntu) to the socket's group;
|
|
- adjust the service or runtime socket mode if needed.
|
|
|
|
The runtime creates the FastCGI socket and CLI socket under `/run/uce`. The CLI socket should remain local-only and should not be reachable from the public web server.
|
|
|
|
Writable paths for the runtime:
|
|
|
|
```text
|
|
/var/cache/uce/work
|
|
/var/lib/uce/uploads
|
|
/var/lib/uce/sessions
|
|
/run/uce
|
|
```
|
|
|
|
## Verification
|
|
|
|
Check service state:
|
|
|
|
```bash
|
|
systemctl status uce.service
|
|
journalctl -u uce.service -n 100 --no-pager
|
|
```
|
|
|
|
Check the FastCGI/web-server path:
|
|
|
|
```bash
|
|
curl -i http://127.0.0.1/doc/index.uce -H 'Host: example.com'
|
|
curl -i http://127.0.0.1/examples/uce-starter/ -H 'Host: example.com'
|
|
```
|
|
|
|
Check the local CLI path:
|
|
|
|
```bash
|
|
cd /opt/uce
|
|
scripts/uce-cli /tests/cli.uce action=echo message=hello
|
|
scripts/run_cli_tests.sh
|
|
```
|
|
|
|
Check WebSocket routing with a WebSocket client against a `.uce` endpoint that defines `WS(Request& context)` through nginx/Apache, not directly against `HTTP_PORT`:
|
|
|
|
```bash
|
|
python3 - <<'PY'
|
|
import base64, os, socket
|
|
host = "example.com"
|
|
path = "/chat.uce"
|
|
key = base64.b64encode(os.urandom(16)).decode()
|
|
request = (
|
|
f"GET {path} HTTP/1.1\r\n"
|
|
f"Host: {host}\r\n"
|
|
"Upgrade: websocket\r\n"
|
|
"Connection: Upgrade\r\n"
|
|
f"Sec-WebSocket-Key: {key}\r\n"
|
|
"Sec-WebSocket-Version: 13\r\n\r\n"
|
|
).encode()
|
|
sock = socket.create_connection(("127.0.0.1", 80), timeout=5)
|
|
sock.sendall(request)
|
|
print(sock.recv(4096).decode("latin1", "replace").split("\r\n", 1)[0])
|
|
sock.close()
|
|
PY
|
|
```
|
|
|
|
## Operational footguns
|
|
|
|
- Keep the FastCGI socket path consistent: `FCGI_SOCKET_PATH` and the web-server `fastcgi_pass` must match exactly. The reference config uses `/run/uce/fastcgi.sock`; if you choose `/run/uce.sock`, use it in both places.
|
|
- Keep the public web root separate from the runtime source tree. The examples use `/opt/uce` for runtime files and `/var/www/html` for public files.
|
|
- Set `HTTP_DOCUMENT_ROOT` when the web root is outside the runtime working directory. The built-in HTTP/WebSocket listener resolves upgrade paths from this setting.
|
|
- Do not expose `CLI_SOCKET_PATH` or `HTTP_PORT` as public entry points. The public path should be nginx/Apache.
|
|
- Do not trust `Script-Filename` request headers from direct HTTP clients. The built-in HTTP listener resolves from `HTTP_DOCUMENT_ROOT` and rejects `..` path segments.
|
|
- WASI SDK is a deployment/runtime dependency, not just a developer build tool. UCE compiles units to wasm on demand during requests and during proactive startup scans, so each host must use the pinned SDK version documented in `docs/wasi-sdk-toolchain.md`.
|
|
- After toolchain or compile-script fixes, clear stale failed artifacts under `BIN_DIRECTORY`; otherwise a later request may report an old compile failure.
|
|
|
|
## Troubleshooting
|
|
|
|
### 502 Bad Gateway
|
|
|
|
Check:
|
|
|
|
- `systemctl status uce.service`
|
|
- `journalctl -u uce.service -n 200 --no-pager`
|
|
- socket path in web server config equals `FCGI_SOCKET_PATH`
|
|
- web server user can connect to the Unix socket
|
|
- `SCRIPT_FILENAME` resolves to an existing `.uce` file
|
|
|
|
### Raw `.uce` source is downloaded or displayed
|
|
|
|
The `.uce` request did not match the FastCGI rule. Check location/order rules and confirm the public root is `/var/www/html` or your chosen web-root path.
|
|
|
|
### Static files 404
|
|
|
|
Confirm the web server `root`/`DocumentRoot` is `/var/www/html` or your chosen web-root path and that `location /` or Apache directory rules allow static file reads.
|
|
|
|
### WebSocket page renders but upgrade fails
|
|
|
|
Check:
|
|
|
|
- the client sends `Upgrade: websocket`
|
|
- `.uce` upgrade traffic reaches `HTTP_PORT`
|
|
- nginx/Apache preserves `Upgrade` and `Connection` headers
|
|
- `HTTP_DOCUMENT_ROOT` matches the web-server root; if it points at the runtime tree while files live in `/var/www/html`, the built-in listener will return `404 script not found`
|
|
- firewall/network policy allows localhost access to `HTTP_PORT`
|
|
|
|
### Page compiles fail
|
|
|
|
Check the compile artifact paths shown in the UCE error response and service logs. Generated files and compile output live under `BIN_DIRECTORY`.
|
|
|
|
Common compile footguns:
|
|
|
|
- `WASM_COMPILE_SCRIPT` is unset or points at a removed script such as `scripts/compile`; set it to `scripts/compile_wasm_unit`.
|
|
- `scripts/check_unit_wasm.py` is missing or not executable; `scripts/compile_wasm_unit` calls it after linking each unit.
|
|
- `WASI_SDK` does not point at the pinned tree with `clang++`, `wasm-ld`, `llvm-objcopy`, and `llvm-nm`; run `scripts/install_wasi_sdk.sh --check-only`.
|
|
- `WASMTIME_HOME` does not point at a tree with Wasmtime headers and `libwasmtime.so`.
|
|
- A previous failed compile left stale `.compile.txt`, `.wasm-check.txt`, or partial `.wasm` files under `BIN_DIRECTORY`.
|
|
|
|
Failed compile output is persisted under the unit's generated path in `BIN_DIRECTORY` and may be reused until the source or compiler inputs change. First fix the source/toolchain issue and reload the page. If the cache itself is suspect, stop UCE, move only the affected unit artifact files or directory aside, and restart so the runtime recompiles from source. Avoid deleting the whole `BIN_DIRECTORY` unless you intentionally want a full rebuild.
|
|
|
|
### CLI commands fail
|
|
|
|
Check:
|
|
|
|
- `CLI_SOCKET_PATH` in `/etc/uce/settings.cfg`
|
|
- `/run/uce/cli.sock` exists
|
|
- `scripts/uce-cli --socket /run/uce/cli.sock /ping` works
|
|
- the target unit defines `CLI(Request& context)`
|