Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/wfl-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ jobs:
echo "- Scribe: $(git -C lib/scribe rev-parse HEAD)"
echo '- Runner: blacksmith-2vcpu-ubuntu-2404 (Linux x64)'
echo '- Command: python3 scripts/run_tests.py --include-scribe'
echo '- HTTP checks: python3 -m unittest discover -s tests/integration -v'
} | tee -a "$GITHUB_STEP_SUMMARY"

- name: Run Scriptorium and pinned Scribe suites
- name: Run WFL suites and HTTP configuration checks
shell: bash
env:
RESOLVED_IMAGE: ${{ steps.runtime.outputs.image }}
Expand All @@ -64,6 +65,7 @@ jobs:
python3 --version
wfl --version
python3 scripts/run_tests.py --include-scribe
python3 -m unittest discover -s tests/integration -v
'

- name: Clean up test container
Expand Down
7 changes: 6 additions & 1 deletion .wflcfg
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Scriptorium runtime configuration (WFL settings)
# Scriptorium configuration (WFL runtime and application settings)
timeout_seconds = 60
logging_enabled = false
debug_report_enabled = false
Expand All @@ -9,6 +9,11 @@ log_level = info
# 0.0.0.0 -> listen on all interfaces (use behind a reverse proxy)
web_server_bind_address = 127.0.0.1

# HTTP port, read by Scriptorium at boot rather than by the WFL runtime.
# Use a whole number from 1 to 65535. Missing or invalid values use 8080.
# Restart Scriptorium after changing it.
web_server_port = 8080

# To terminate TLS directly in Scriptorium, point these at your PEM files and
# start with a `listen ... secured` port (see docs/ARCHITECTURE.md).
# web_server_tls_cert_file = /etc/scriptorium/tls/cert.pem
Expand Down
8 changes: 5 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ violate the standard.
its summary records the resolved image digest, runtime version, and source
revisions. See
[testing.md](testing.md) for commands, coverage limits, and merge evidence.
- **`data_dir` is an application convention, not a WFL runtime feature.**
`main.wfl` reads `.wflcfg` itself at boot and parses the key via
`config_value_from` in `app/util.wfl`. The runtime ignores it.
- **`data_dir` and `web_server_port` are application configuration.**
`main.wfl` reads `.wflcfg` itself at boot using helpers in `app/util.wfl`;
the runtime does not apply these keys. The HTTP port accepts whole numbers
from 1 to 65535 and defaults to 8080 for a missing file or setting, or an
empty, malformed, fractional, or out-of-range value. Restart after changing it.

## Known gaps worth knowing before you touch rendering

Expand Down
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ Open <http://127.0.0.1:8080/> — you will be sent to `/install`. Choose a site
title, tagline, admin username, and password. When setup finishes you are
signed in at `/admin`. Add more users (admins or authors) under **Users**.

> Bind address and TLS are set in `.wflcfg`. The server listens on
> `127.0.0.1:8080` by default; set `web_server_bind_address = 0.0.0.0` to expose
> it behind a reverse proxy.
> Network settings are in `.wflcfg`. The server listens on `127.0.0.1:8080`
> by default; set `web_server_bind_address = 0.0.0.0` to expose it behind a
> reverse proxy. Scriptorium reads `web_server_port = 8080` at boot; change it
> to a whole number from 1 to 65535 and restart to use a different HTTP port.
> A missing file or setting, or an empty, malformed, fractional, or out-of-range
> value, uses 8080. TLS settings are also in `.wflcfg`.

### Choosing a theme

Expand All @@ -94,8 +97,9 @@ theme_root = themes
> **A value runs to the end of the line.** `.wflcfg` supports whole-line `#`
> comments only, so `theme = logbie # my theme` sets the theme to
> `logbie # my theme` and every lookup misses. Put comments on their own line.
> This applies to `data_dir` too. Boot warns when the configured theme
> directory does not exist, which is what a trailing comment looks like.
> This applies to `data_dir` and `web_server_port` too. Boot warns when the
> configured theme directory does not exist, which is what a trailing comment
> looks like.

A body template resolves as `<theme_root>/<theme>/body/<name>.html`, then
`<theme_root>/<theme>/templates/<name>.html`, then the base theme — so a theme
Expand Down Expand Up @@ -181,7 +185,7 @@ as a hidden `csrf_token` field) — requests without it get a 403.

```text
main.wfl Boot (open DB, migrate, backfill) + request loop + router + handlers
.wflcfg WFL runtime config (bind address, TLS, body-size cap, data_dir)
.wflcfg Runtime and app config (bind address, HTTP port, TLS, body-size cap, data_dir)
app/
util.wfl slugify, to_int, field_or, truncate, file_ext/stem, config_value_from (parsing is stdlib)
db.wfl SQLite schema + every query/execute helper
Expand Down Expand Up @@ -212,6 +216,7 @@ From the repository root, with Python 3.11+ and WFL on PATH:
```sh
python scripts/run_tests.py # all five Scriptorium suites
python scripts/run_tests.py --include-scribe # also test the pinned Scribe engine
python -m unittest discover -s tests/integration -v # HTTP port configuration
python -m unittest discover -s tests/tooling -v
python scripts/check_repo_hygiene.py
```
Expand Down
31 changes: 31 additions & 0 deletions TestPrograms/util.test.wfl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,37 @@ describe "Scriptorium utilities":
expect config_value_from of "" and "data_dir" and "./data" to equal "./data"
end test

test "config_port_from defaults when absent or commented":
expect config_port_from of "" to equal 8080
expect config_port_from of "web_server_bind_address = 127.0.0.1" to equal 8080
expect config_port_from of "# web_server_port = 9090" to equal 8080
end test

test "config_port_from reads the configured port and trims whitespace":
expect config_port_from of "web_server_port = 9090" to equal 9090
expect config_port_from of " web_server_port = 12345 \r\n" to equal 12345
end test

test "config_port_from accepts the valid port boundaries":
expect config_port_from of "web_server_port = 1" to equal 1
expect config_port_from of "web_server_port = 65535" to equal 65535
end test

test "config_port_from rejects empty and nonnumeric values":
expect config_port_from of "web_server_port = " to equal 8080
expect config_port_from of "web_server_port = nope" to equal 8080
expect config_port_from of "web_server_port = true" to equal 8080
expect config_port_from of "web_server_port = null" to equal 8080
expect config_port_from of "web_server_port = 9090 # inline comment" to equal 8080
end test

test "config_port_from rejects fractional and out-of-range ports":
expect config_port_from of "web_server_port = 9090.5" to equal 8080
expect config_port_from of "web_server_port = 0" to equal 8080
expect config_port_from of "web_server_port = -1" to equal 8080
expect config_port_from of "web_server_port = 65536" to equal 8080
end test

test "install_validate rejects an empty title":
expect install_validate of "" and "ada" and "secret" and "secret" to equal "Site title is required."
end test
Expand Down
14 changes: 14 additions & 0 deletions app/util.wfl
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,20 @@ define action called config_value_from with parameters cfg_text and cfg_key and
return fallback
end action

// config_port_from: resolve Scriptorium's HTTP port from .wflcfg text.
// Missing or invalid values preserve the default listener on port 8080.
define action called config_port_from with parameters cfg_text:
store raw_port as config_value_from of cfg_text and "web_server_port" and "8080"
store configured_port as to_int of raw_port and 8080
check if (configured_port is less than 1) or (configured_port is greater than 65535):
return 8080
end check
check if configured_port is not equal to (floor of configured_port):
return 8080
end check
return configured_port
end action

// install_validate: first-run installer field checks. Pure (no I/O).
// Title and username are trimmed; passwords are compared as submitted.
define action called install_validate with parameters site_title and username and password and password_confirm:
Expand Down
11 changes: 10 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ reads as deliberate rather than accidental.

```
Browser
│ HTTP (127.0.0.1:8080)
│ HTTP (127.0.0.1:8080 by default)
▼
main.wfl ── listen on port ── main loop: wait for request ── route ──┐
│ │
Expand Down Expand Up @@ -61,6 +61,15 @@ main.wfl ── site_ext.wfl (+ defines the router and every request handler)
already exist), the request loop, the `route`-based dispatcher with the
first-run lock, and all handler actions.

## Network configuration

WFL reads `web_server_bind_address` and TLS settings from `.wflcfg`. Scriptorium
reads `web_server_port` itself at boot, like `data_dir`, and passes the resolved
port to `listen`. The default is 8080; the configured value must be a whole
number from 1 to 65535. A missing file or setting, or an empty, malformed,
fractional, or out-of-range value, falls back to 8080. Changes require a restart.
The shared bind address remains `127.0.0.1`.

## Request lifecycle

WFL's web server is **pull-based and single-threaded**: the loop blocks on
Expand Down
8 changes: 5 additions & 3 deletions main.wfl
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,8 @@ store cfg_text as ""
check if file exists at ".wflcfg":
change cfg_text to scribe_read_file of ".wflcfg"
end check
// The HTTP port is an application setting; preserve 8080 for existing installs.
store server_port as config_port_from of cfg_text
store data_dir as config_value_from of cfg_text and "data_dir" and ""
check if data_dir is not equal to "":
check if directory exists at data_dir:
Expand Down Expand Up @@ -1395,12 +1397,12 @@ otherwise:
create directory at uploads_dir
end check

listen on port 8080 as web_server
display "Scriptorium is running at http://127.0.0.1:8080"
listen on port server_port as web_server
display "Scriptorium is running at http://127.0.0.1:" with server_port
check if (install_is_done of db) is equal to yes:
display " admin: /admin"
otherwise:
display " First run — open http://127.0.0.1:8080/install to set up your site"
display " First run — open http://127.0.0.1:" with server_port with "/install to set up your site"
end check

main loop:
Expand Down
25 changes: 20 additions & 5 deletions testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,22 @@ pin changes and changes affecting Scribe integration. CMS-specific Scribe
regressions remain in `TestPrograms/scribe.test.wfl` even when upstream tests
also cover related behavior.

The HTTP port configuration checks start disposable Scriptorium processes and
exercise `/install` using synthetic temporary databases:

```sh
python -m unittest discover -s tests/integration -v
```

These checks require WFL on `PATH` (or `WFL_EXECUTABLE` set to its executable)
and an available loopback port 8080. They check an explicitly configured port,
the default when the setting or file is absent, and startup URLs. They do not
exercise installer submission or other complete CMS journeys. The WFL tests
workflow runs them inside its disposable nightly container after the WFL suites.

| Existing suite | Direct command from the repository root | What it currently exercises |
|---|---|---|
| Utilities | `wfl --test TestPrograms/util.test.wfl` | Slugs, number/field helpers, parsing, config values, installer validation |
| Utilities | `wfl --test TestPrograms/util.test.wfl` | Slugs, number/field helpers, parsing, config values and valid/default ports, installer validation |
| Data | `wfl --test TestPrograms/db.test.wfl` | In-memory SQLite CRUD helpers, sessions, installer state, rate-limit records, legacy CSRF-column migration |
| Auth | `wfl --test TestPrograms/auth.test.wfl` | CSRF helper acceptance/rejection and session token binding |
| Scribe integration | `wfl --test TestPrograms/scribe.test.wfl` | Markdown, safe-marker/filter propagation, escaping, nested blockquotes |
Expand Down Expand Up @@ -180,8 +193,9 @@ keyboard behavior, or data integrity.
concrete restore or forward-repair plan. An in-memory migration test alone
does not prove crash recovery or database/upload consistency.
- **Configuration:** exercise unset/default keys and explicit values for
`data_dir`, `theme`, and `theme_root`, including malformed values. Account for
the current whole-line-comment parsing and out-of-tree theme paths.
`web_server_port`, `data_dir`, `theme`, and `theme_root`, including malformed
values. Account for the current whole-line-comment parsing and out-of-tree
theme paths.
- **Dependencies:** inspect the actual pinned Scribe diff, run both local and
upstream suites, and exercise affected rendering paths. Runtime upgrades also
require the application suites and affected web/SQLite/crypto boundaries.
Expand Down Expand Up @@ -210,7 +224,8 @@ moving: retain the run URL and digest with PR evidence so a later nightly does
not obscure which runtime was tested. The nightly workflow is not a declaration
that every nightly, platform, or production configuration is supported.

There is no automated HTTP/browser journey suite in this repository. The
Automated HTTP coverage is limited to startup port configuration and serving
the installer form. Complete HTTP/browser journeys remain unautomated. The
scheduled Scribe updater only proposes dependency changes; its successful run
alone is not runtime test evidence.

Expand Down Expand Up @@ -248,7 +263,7 @@ remain a separate adoption item below.

| Gap | Required next step and trigger |
|---|---|
| No router/HTTP or browser automation | Add real-boundary regression coverage with each affected behavior change; plan coverage of all critical journeys before the next production release. |
| HTTP coverage is limited to port configuration; no browser automation | Add real-boundary regression coverage with each affected behavior change; plan coverage of all critical journeys before the next production release. |
| No declared compatibility matrix or release-candidate workflow | Define supported runtime/platform/configuration tuples and retain candidate results before the next production release. |
| No coverage measurement, performance budgets, or scheduled extended tests | Establish baselines and risk-based targets before claiming those properties; review at the next profile review. |
| Host protection settings are external | Maintainer verifies required checks and review rules on GitHub at adoption and after workflow changes. |
Expand Down
Loading
Loading