Author SHA1 Message Date
Claude 44264d0781 FABRIC-3 XLVIII.6: #131 closed -- QEMU's capture doubled a byte, not our kernel [skip ci]
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-08 02:25:06 +00:00
Claude 55613350c4 demo: CPUQuota 150%, as set on the server (#116) [skip ci]
Since #151 an idle guest halts (1% of a core at the prompt), so the cap no
longer has to hold back spinning sessions. Captain Bob set 1.5 cores for
all sessions together on the server (2026-11-08): one busy visitor a full
core, half a core always left to the wikis and Gitea. Confirmed on the
server with a live session idle for 3 minutes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-08 02:09:52 +00:00
rajames f747ab78a6 Merge pull request 'kernel: v4 waits for a key in arch_halt(), not a spin (#151); Try it deployed' (#153) from StarForth-v4.0.0 into master 2026-11-08 01:59:29 +00:00
Claude 1928d53b6b kernel: v4 waits for a key in arch_halt(), not a spin (#151)
Build / Image RASPI (push) Successful in 3m9s
Build / Image PC (push) Successful in 4m27s
Build / Image MILKV (push) Successful in 3m18s
Build / v4 tests (32- and 64-bit cells) (push) Successful in 10m27s
Acceptance / Acceptance (pull_request) Successful in 15m15s
An idle StarshipOS kept a CPU core 100% busy: sk_v4_readline(), with
nothing typed, and sk_v4_identity_wait(), headless until Zuse, waited in
arch_relax(). Both now halt until the next interrupt (hlt; wfi on aarch64
and riscv64): the heartbeat's timer, ISR-driven on all three, wakes them at
100 Hz, so a key is still seen within a tick.

Idle at the prompt, QEMU's CPU: 100% -> 1% of a core on amd64, 1% on
aarch64 and riscv64. Acceptance passes: amd64 logs/20261108-005539, aarch64
-005753, riscv64 -010136 (POST 538/538, one dict_hash, 7 ok), the stick
loaded high, test-32 (39 programs, 0 failures). FABRIC-3 XLVIII.5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-08 01:21:36 +00:00
Claude a083c5b834 site/com: Try it in the .com's navigation (#117) [skip ci]
The canvas's Home and Inquiries artboards gain a 'Try it' link (canvas
versions 12, 13); canvas/ copies them, build.py rebuilds the pages, and
install.sh put them on the wiki. Checked as a visitor: the home page, the
link, the Try it page in 1.1 s, its framed console Ready. FABRIC-3 XLVIII.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-08 00:49:46 +00:00
Claude 88b962b29e demo: one core for all sessions, lowest priority, 1 GiB; Try it deployed [skip ci]
An idle StarshipOS guest keeps a host core 100% busy (measured 2026-11-08:
QEMU at 100% of a core, 148 MiB, the guest idle at its prompt; #151).
starshipos-demo.service: CPUQuota=100% and Nice=19 for all sessions
together, MemoryMax=1G -- sized for this server, where the old 300% and 4G
let two visitors starve both wikis and Gitea. DEPLOY.md says how to apply
it. FABRIC-3 XLVIII.4: Try it deployed and checked from outside; the ttyd
package's own login service; #151, #152.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-08 00:38:54 +00:00
Claude 4d0b9ab771 demo/DEPLOY.md: disable the ttyd package's own service first [skip ci]
Ubuntu's ttyd package starts ttyd.service, which runs login on port 7681;
installed by step 1, it took the port, and try.starshipos-f18.com served
the server's login prompt until it was disabled (2026-11-08). Step 4 now
disables it first and checks what listens on 7681. And the check from
outside, done: ready in 1.5 s, 7 ok, 49 ok, the third visitor busy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-08 00:25:26 +00:00
Claude 0dbad0f278 demo: DEPLOY.md, the server checklist for Try it [skip ci]
Five steps on the server after #148: the checkout and amd64 build tools,
the demo's own Zuse, the image and the saved machine at 384 MiB, the
service, TLS and Apache for try.starshipos-f18.com; then the check from
outside and the .com's navigation link (#115, #116, #117).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-08 00:07:58 +00:00
rajames 6bbf4b1afd Merge pull request 'acceptance prints the serial log of a failed check (#136); the demo limits its guests (#116)' (#148) from StarForth-v4.0.0 into master 2026-11-08 00:07:45 +00:00
Claude 1e3f211daa FABRIC-3 XLVIII: the review, the protocol, the SDK, the demo's limits
Build / Image RASPI (push) Canceled after 0s
Build / Image PC (push) Canceled after 0s
Build / Image MILKV (push) Canceled after 0s
Build / v4 tests (32- and 64-bit cells) (push) Canceled after 0s
Acceptance / Acceptance (pull_request) Successful in 16m20s
Today's rulings and work since XLVII.6, kept at the pace of the work
(Captain Bob, 2026-11-07: 'the book of fabric needs to keep pace'): the
alpha.4 list (#147) and what is in it, the developer protocol (#135), the
Book of Fabric (#138), the SDK milestone with the capsule repository (#149)
and the latency DoE (#150), #136 and the demo's guests and busy message,
with what was measured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-07 23:19:28 +00:00
Claude 171b3947dd demo: two guests of 384 MiB, and a busy message for the rest (#116)
Build / Image RASPI (push) Canceled after 0s
Build / Image PC (push) Canceled after 0s
Build / Image MILKV (push) Canceled after 0s
Build / v4 tests (32- and 64-bit cells) (push) Canceled after 0s
Acceptance / Acceptance (pull_request) Canceled after 0s
Captain Bob, 2026-11-07: 'Limit guests and have a Busy, try back in a few
minutes.' start-session.sh takes one of DEMO_MAX_GUESTS slots (default 2),
a lock it holds for the session's life (flock -n: two visitors cannot both
take the last one; QEMU and the bridge are started without its descriptor);
with every slot taken, the visitor's terminal says every machine is busy and
to try back in a few minutes, and the session ends without a machine. The
service: DEMO_MAX_GUESTS=2, RuntimeDirectory for the locks, ttyd's
--max-clients 6 so a visitor over the limit reaches the message. The guest:
384 MiB (v4 boots from 256 MiB, refuses at 128; measured, #116). Tested
here: every slot held -> the busy message; slot 1 held -> slot 2 taken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-07 23:18:40 +00:00
Claude 1d4898496f acceptance: a failed check prints the serial log where it looked (#136)
Build / Image PC (push) Successful in 3m20s
Build / v4 tests (32- and 64-bit cells) (push) Successful in 10m1s
Build / Image MILKV (push) Successful in 2m55s
Build / Image RASPI (push) Successful in 2m42s
Acceptance / Acceptance (pull_request) Successful in 18m44s
Run 197's riscv64 failure (#131) could only be read from the run's logs
artifact, which Gitea's API does not list. qemu_session.py's check() now
prints, on a FAIL, the serial lines after the first match of an anchor the
check names, or the log's last lines; control characters as escapes, so a
stray byte or terminal sequence in a line shows instead of only failing a
match. The boot-disk checks anchor on 'Boot successful!'. A passing run
prints as before (riscv64 here, logs/ committed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-07 22:49:03 +00:00
rajames 777f739708 Merge pull request 'release: v4.0.0-alpha.3; the proofs run capped to fit the server' (#132) from StarForth-v4.0.0 into master
Release / Release ${{ github.ref_name }} (push) Successful in 41m51s
2026-11-07 20:52:52 +00:00
Claude b8951bc26b release v4.0.0-alpha.3: accepted in full, logs
Build / Image PC (push) Successful in 3m56s
Build / v4 tests (32- and 64-bit cells) (push) Successful in 10m7s
Build / Image MILKV (push) Successful in 8m45s
Acceptance / Acceptance (pull_request) Successful in 15m37s
Build / Image RASPI (push) Successful in 4m11s
On 50d2bc98 (LithosAnanke v4.0.0-alpha.3), in this session, one QEMU at a
time in the foreground: amd64 logs/20261107-195526, aarch64 -195725,
riscv64 -200055 (POST 538/538, dict_hash=0xfec8c47cfc6f437f on all three),
the stick loaded high, test-32 (39 programs, 0 failures); the proofs
through the capped stage, logs/20261107-195341 (47 s, ~1.8 GiB).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-07 20:09:33 +00:00
Claude 50d2bc98d9 release: v4.0.0-alpha.3; the proofs run capped to fit the server
v4.0.0-alpha.2's release run (200) was killed in the StarForth session:
Isabelle's defaults (Java 4 GiB, a thread per CPU). scripts/nightly.sh
proofs now runs one thread, Java 512 MiB, Poly/ML 900 MiB, through a
settings file of a home made for the run (heaps linked to the user's):
47 s, ~1.8 GiB, logs/20261107-195341. Nothing was published from alpha.2;
its tag stays. LITHOS_VERSION 4.0.0-alpha.3. FABRIC-3.md XLVII.6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
2026-11-07 19:54:47 +00:00
41 changed files with 356 additions and 21 deletions
+9 -1
View File
@@ -1,6 +1,14 @@
# Changelog
## 2026-11-07 — LithosAnanke v4.0.0-alpha.2: the first published alpha
## 2026-11-07 — LithosAnanke v4.0.0-alpha.3: the first published alpha
`v4.0.0-alpha.2` (`2d43db25`) used the image's HOL-Library as intended, but its release run (200) ran
the proofs with Isabelle's defaults -- Java's heap up to 4 GiB, a thread per CPU -- and they were
killed in the StarForth session. Nothing was published; the tag stays where it is. The proofs now run
capped (`scripts/nightly.sh proofs`: one thread, Java 512 MiB, Poly/ML 900 MiB; 47 s, ~1.8 GiB), and
`v4.0.0-alpha.3` is the first alpha published. `docs/fabric/FABRIC-3.md` XLVII.6.
## 2026-11-07 — LithosAnanke v4.0.0-alpha.2: not published
`v4.0.0-alpha.1` (`233025da`) was accepted and tagged, but its release run (196) built HOL-Library
from scratch in the proofs step and was killed: `release.yml`'s full `make init` replaced the
+96
View File
@@ -0,0 +1,96 @@
# Deploying "Try it" on the server
The public demo: each visitor gets a StarshipOS machine of their own in the browser, resumed at
Zuse's prompt, gone when they leave. Two at a time on today's server (384 MiB each); a third visitor
is told every machine is busy and to try back in a few minutes. Issues #115, #116, #117.
Run as root on the server, **after PR #148 is merged** (it carries the 384 MiB guests and the busy
message), **while CI is idle** (each step that boots QEMU boots one, in the foreground).
## 1. The checkout and what the build needs (~15 min)
```bash
apt-get install -y ttyd qemu-system-x86 ovmf python3 git
git clone https://gitea.starshipos-f18.org/rajames/LithosAnanke.git /opt/starshipos-demo
cd /opt/starshipos-demo
git checkout master # after #148: alpha.3's kernel, and the demo's limits
make ARCH=amd64 init INIT_DOCS=0 INIT_PROOFS=0 # amd64 only: no docs, no Isabelle
```
Master after #148 is the `v4.0.0-alpha.3` kernel unchanged, plus #148's scripts.
## 2. The demo's own Zuse (~3 min, two QEMU boots)
```bash
demo/mint-zuse.sh /var/lib/starshipos-demo-zuse
ls -la /var/lib/starshipos-demo-zuse/zusestick.img # the stick every session attaches
```
Never anyone's real Zuse: minted for the demo (#111), its writes discarded in every session.
## 3. The image and the saved machine (~2 min, one QEMU boot)
```bash
demo/build-iso.sh /tmp/StarshipOS.iso
DEMO_PROMPT='[zuse@Hera]' demo/make-snapshot.sh /tmp/StarshipOS.iso /var/lib/starshipos-demo \
/var/lib/starshipos-demo-zuse/zusestick.img
ls -la /var/lib/starshipos-demo/ # StarshipOS.iso identity.img vars.fd state
```
The saved machine belongs to this image, this QEMU and 384 MiB: re-run step 3 after any of them
changes. Sessions already running keep theirs.
## 4. The service (~5 min)
Ubuntu's `ttyd` package starts its own `ttyd.service`, which runs **`login` on port 7681** -- our
port. Left running, it takes the port, and Apache hands visitors the server's own login prompt
(found 2026-11-08, after step 5). Disable it first:
```bash
systemctl disable --now ttyd # the package's service: a login prompt on 7681
useradd --system --home /var/lib/starshipos-demo --shell /usr/sbin/nologin starshipos-demo
chown -R starshipos-demo: /var/lib/starshipos-demo
cp demo/starshipos-demo.service /etc/systemd/system/
systemctl daemon-reload && systemctl enable --now starshipos-demo
systemctl status starshipos-demo --no-pager | head -5
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:7681/ # 200: ttyd answers
ss -ltnp | grep 7681 # the ttyd of starshipos-demo, nothing else
```
The service expects the checkout's `demo/` at `/opt/starshipos-demo/demo` (step 1 put it there).
## 5. TLS and Apache for try.starshipos-f18.com (~10 min)
DNS: `try.starshipos-f18.com` must point at this server (an A record) before certbot.
```bash
a2enmod proxy proxy_http proxy_wstunnel headers ssl rewrite
certbot certonly --apache -d try.starshipos-f18.com
cp demo/apache-demo.conf /etc/apache2/sites-available/starshipos-demo.conf
a2ensite starshipos-demo && apachectl configtest && systemctl reload apache2
curl -sI https://try.starshipos-f18.com/ | head -3 # 200, its own certificate
```
`apache-demo.conf` lets only the .com frame the terminal (`frame-ancestors`).
## 6. The check (done 2026-11-08)
From outside: a session ready 1.5 s after the page opens; `3 4 + .` answers `7 ok`, `: SQUARE DUP *
; 7 SQUARE .` answers `49 ok`; a third visitor at once is told every machine is busy. The
certificate is try.starshipos-f18.com's own (Let's Encrypt), and only the .com may frame it.
## 7. Tell Claude
Then I check from outside: a session reaches `[zuse@Hera] ok>`, `3 4 + .` answers `7 ok`, a third
visitor gets the busy message, and the .com's Try it page shows the console. Then the "Try it" link
goes into the .com's navigation (#117).
## If something goes wrong
- `journalctl -u starshipos-demo -n 50` -- ttyd and the sessions
- A session that ends at once: the saved machine does not match (re-run step 3)
- Memory: `free -h` -- a session's QEMU holds ~150 MB of its 384 MiB guest; two at most
(`DEMO_MAX_GUESTS` in the service)
- CPU: an idle guest keeps a host core busy (the kernel's idle loop polls -- its own issue), so the
service caps all sessions together at one core (`CPUQuota=100%`, `Nice=19`). After changing the
service file: `systemctl daemon-reload && systemctl restart starshipos-demo`
+2 -1
View File
@@ -20,7 +20,8 @@ XWiki "Try it" page (iframe) -> Apache: TLS, websocket -> ttyd -> start-session.
| `start-session.sh` | One visitor's session: resumes the saved machine, ends it on hang-up, close or time-out (#112) |
| `console.py` | Joins the visitor's terminal to the machine only once it runs, discarding keys typed while it loads |
| `qemu-args.sh` | The machine, shared by the two above: a resumed machine must match the saved one exactly |
| `starshipos-demo.service` | ttyd as a systemd service, on localhost, at most 3 sessions (#114) |
| `starshipos-demo.service` | ttyd as a systemd service, on localhost; two guests at once, a busy message for the rest (#114, #116) |
| `DEPLOY.md` | Putting it on the server, step by step |
| `apache-demo.conf` | Example vhost: TLS, the websocket, framing allowed for the `.com` only (#115) |
| `xwiki-try-it.xwiki` | The "Try it" page, its own page on the `.com` XWiki (`TryIt`), in the landing page's style |
+5 -3
View File
@@ -3,8 +3,10 @@
# A session resumes a machine saved by make-snapshot.sh, and QEMU can resume a saved machine only
# into the same machine: the same board, CPU, memory, firmware and devices. Both scripts build the
# command line here, so they cannot drift apart. It is the machine `make -f kernel/Makefile
# ARCH=amd64 qemu` boots (q35, TCG, 1 GiB, OVMF, the ISO as a CD), less what a visitor must not
# have: no network device, no Artemis disk, no display, and QEMU's seccomp sandbox on.
# ARCH=amd64 qemu` boots (q35, TCG, OVMF, the ISO as a CD) -- in 384 MiB, not its 1 GiB: v4 boots
# to Zuse's prompt from 256 MiB, refuses at 128 ("no memory for the block chain"), measured
# 2026-11-07 (#116) -- less what a visitor must not have: no network device, no Artemis disk, no
# display, and QEMU's seccomp sandbox on.
#
# demo_qemu_args <state-dir> <vars.fd> -> fills the array QEMU_ARGS
#
@@ -18,7 +20,7 @@ demo_qemu_args() {
QEMU_ARGS=(
-machine q35,accel=tcg
-cpu qemu64,+rdrand
-m 1024
-m 384 # v4 boots from 256 MiB; 384 leaves room (#116)
-smp 1
-drive "if=pflash,format=raw,readonly=on,file=$OVMF_CODE"
-drive "if=pflash,format=raw,file=$vars"
+16 -4
View File
@@ -13,8 +13,13 @@ User=starshipos-demo
Group=starshipos-demo
Environment=DEMO_STATE=/var/lib/starshipos-demo
Environment=DEMO_SESSION_LIMIT=1800
# -m: sessions at once -- each a QEMU of about 1 GiB and most of a core (issue #116 measures it)
ExecStart=/usr/bin/ttyd --port 7681 --interface lo --writable --max-clients 3 \
Environment=DEMO_MAX_GUESTS=2
# the guests' slot locks (start-session.sh): /run/starshipos-demo, made for this user, gone at stop
RuntimeDirectory=starshipos-demo
# --max-clients: the guests and a few more, so a visitor over the limit reaches the busy message
# instead of a refused connection; still a cap on open connections
# each guest a QEMU of 384 MiB and most of a core (demo/qemu-args.sh; #116)
ExecStart=/usr/bin/ttyd --port 7681 --interface lo --writable --max-clients 6 \
--client-option titleFixed=StarshipOS --client-option disableLeaveAlert=true \
/opt/starshipos-demo/demo/start-session.sh
Restart=on-failure
@@ -25,8 +30,15 @@ ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ReadOnlyPaths=/var/lib/starshipos-demo
MemoryMax=4G
CPUQuota=300%
# Sized for this server (3.8 GiB, two cores; both wikis, Gitea, Nexus, CI). Since #151 an idle guest
# halts (1% of a core at the prompt, was 100%), so a session costs CPU only while its visitor runs
# something. All sessions together get 1.5 cores at the lowest priority (Captain Bob, 2026-11-08):
# one busy visitor a full core, two busy at once three-quarters each, and half a core always left
# to the wikis and Gitea. 1 GiB holds two sessions (~150 MB resident each) with room. Raise both
# with the server (8 GB: MemoryMax=2G, CPUQuota=200% or more).
MemoryMax=1G
CPUQuota=150%
Nice=19
TasksMax=200
[Install]
+24 -3
View File
@@ -12,8 +12,29 @@ here=$(cd "$(dirname "$0")" && pwd)
. "$here/qemu-args.sh"
dir=${1:-${DEMO_STATE:-/var/lib/starshipos-demo}}
limit=${DEMO_SESSION_LIMIT:-1800} # seconds a session may last
guests=${DEMO_MAX_GUESTS:-2} # machines at once: 384 MiB each (qemu-args.sh, #116)
slots=${DEMO_SLOTS:-/run/starshipos-demo} # a lock file per machine
[ -f "$dir/state" ] || { echo "The demo is not set up yet: no saved machine in $dir." >&2; exit 1; }
# One of $guests slots, or a busy message (Captain Bob, 2026-11-07: "Limit guests and have a Busy,
# try back in a few minutes"). A slot is a lock this script holds for the session's life (flock -n:
# two visitors cannot both take the last one); it goes when the script does, however it ends. The
# machine's processes are started without the lock's descriptor ({slot}>&-), so it is this
# script's alone.
mkdir -p "$slots"
slot=
for i in $(seq 1 "$guests"); do
exec {fd}>"$slots/slot-$i.lock"
if flock -n "$fd"; then slot=$fd; break; fi
exec {fd}>&-
done
if [ -z "$slot" ]; then
printf '\r\nEvery StarshipOS machine is busy right now (%d at once on this small server).\r\n' "$guests"
printf 'Try back in a few minutes.\r\n'
sleep 20
exit 0
fi
tmp=$(mktemp -d "${TMPDIR:-/tmp}/starshipos-session.XXXXXX")
trap 'rm -rf "$tmp"' EXIT
cp "$dir/vars.fd" "$tmp/vars.fd"
@@ -28,13 +49,13 @@ printf 'Try: 3 4 + . : SQUARE DUP * ; 7 SQUARE .\r\n\r\n'
qemu-system-x86_64 "${QEMU_ARGS[@]}" \
-chardev "socket,id=con,path=$tmp/con.sock,server=on,wait=off" -serial chardev:con \
-qmp "unix:$tmp/qmp.sock,server=on,wait=off" -monitor none \
-incoming "file:$dir/state" </dev/null &
-incoming "file:$dir/state" </dev/null {slot}>&- &
qemu=$!
( sleep "$limit"; kill "$qemu" 2>/dev/null ) &
( sleep "$limit"; kill "$qemu" 2>/dev/null ) {slot}>&- &
timer=$!
# The bridge is a tracked child too: bash runs a trap only once its foreground command returns,
# so a hang-up would otherwise wait on the bridge. `wait` returns to the trap at once.
python3 -I "$here/console.py" "$tmp/qmp.sock" "$tmp/con.sock" 0<&0 &
python3 -I "$here/console.py" "$tmp/qmp.sock" "$tmp/con.sock" 0<&0 {slot}>&- &
bridge=$!
trap 'kill "$bridge" "$qemu" "$timer" 2>/dev/null; rm -rf "$tmp"' EXIT
trap 'exit 0' HUP TERM INT
+110
View File
@@ -6178,3 +6178,113 @@ packages, which also fetched Isabelle into the workspace and pointed `/usr/local
2026-11-07): the tag stays where it is; `release.yml` runs `init INIT_PROOFS=0` (as
`nightly-docs.yml` does), `LITHOS_VERSION` becomes `4.0.0-alpha.2`, and `v4.0.0-alpha.2` is the
first published alpha.
### 6. v4.0.0-alpha.2 not published either; v4.0.0-alpha.3
PR #130 merged (`2d43db25`, acceptance run 199; run 197 before it failed one riscv64 check, #131);
`v4.0.0-alpha.2` tagged there. Its release run (200) passed acceptance and used the image's
Isabelle and HOL-Library, as XLVII.5 meant -- then the StarForth session was killed (exit 137) in
the proofs: Isabelle ran with its defaults, Java's heap up to 4 GiB and a thread per CPU, more than
the server (3.8 GiB, ~2.3 GiB free with the wikis stopped; perhaps a per-job cap besides). Capped
-- one thread, Java 512 MiB, Poly/ML 900 MiB, in a settings file of a home made for the run, its
heaps linked to the user's -- the session passes in 47 s at ~1.8 GiB, through
`scripts/nightly.sh proofs` itself (`logs/20261107-195341`). Nothing was published. Ruled (Captain
Bob, 2026-11-07): the tag stays; `LITHOS_VERSION` `4.0.0-alpha.3`, the first published alpha; the
review list begun meanwhile goes to the milestone v4.0.0-alpha.4.
## XLVIII. After the first published alpha: the review, the protocol, the SDK, the demo's limits (2026-11-07)
### 1. Ruled
- **A ranked list for the next release** (Captain Bob: "a list of problems, deficiencies, smells and
so on placed in issues by rank priority"): milestone **v4.0.0-alpha.4**, its index #147. Must:
Gitea's plain-HTTP logins (#133), rotating what was exposed (#134), the riscv64 flake (#131), the
developer protocol (#135), CI's litter (#129). Should: a failed check prints its serial log
(#136), capsule trust against the root and unsigned capsules (#139, a ruling), the queue's
cancelling of waiting runs (#140, a ruling), CLAUDE.md's drift (#137), the Book of Fabric (#138),
downloads (#126). Nice: the empty DoE CSVs (#141 -- no DoE has run since 2026-09-16: noise, not
breakage), DoE columns always 0 (#142), mkcapsule's warnings (#143), a stale header (#144),
tracked files a build rewrites (#145), the sites' small things (#146).
- **The developer protocol** (#135): "We can simply run nightly as developers to further make sure
nothing sneaks in. Just no Nexus push ... a protocol for programmers. Then the protocol evidence
can be submitted in the PR." -- `scripts/nightly.sh protocol`, a `PROTOCOL.md` per run, checked
by CI against the PR's head.
- **The Book of Fabric** (#138): the FABRIC record as one book, published with every release --
and this record kept at the pace of the work, so the book is (Captain Bob: "the book of fabric
needs to keep pace").
- **v4.0.0-alpha.5: the SDK**, for FORTH and capsule developers; a systems SDK later, FPGA when we
get there (milestone 11). In it:
- **A capsule repository in blocks**, like `~/.m2/repository` (#149): on Artemis, indexed by
coordinates and capsule hash, a TTL by default as the rest of the system has, pinning one-way,
verified in and on use, composed in FORTH; filled by hand now, from Nexus once there is
networking. To rule: its block range and size, the default TTL, what is pinned.
- **K under network latency** (#150): "Networking latency will be a super good K test." -- the
repository pulling from Nexus with delay, jitter, loss and bandwidth dialled in (`tc netem`),
K and the DoE's measures per architecture. After networking, #141 and #142.
- **The demo's limits** (#116): "Limit guests and have a Busy, try back in a few minutes."
### 2. Built
- **#136:** `scripts/qemu_session.py`'s `check()` prints, on a FAIL, the serial lines after an anchor
the check names (the boot-disk checks: `Boot successful!`), else the log's last lines, control
characters as escapes -- so #131's next occurrence explains itself in the job's output.
- **The demo:** guests of **384 MiB** (`demo/qemu-args.sh`); `start-session.sh` takes one of
`DEMO_MAX_GUESTS` slots (2), a lock held for the session's life, and with every slot taken tells
the visitor every machine is busy and to try back in a few minutes; the service makes the locks'
directory and lets ttyd take 6 connections so the extra visitors reach the message.
### 3. Verified
- The demo machine, booted here with only `-m` changed: POST 538/538 and Zuse's prompt at 1024, 512,
384 and 256 MiB (82-84 s); at 128 MiB POST passes, then "StarForth v4: no memory for the block
chain". The slots: every slot held -- the busy message; slot 1 held -- slot 2 taken.
- `excerpt()` against a real riscv64 serial log, with an anchor and without; a riscv64 acceptance
with the new script, passing as before.
- With both wikis up again (Shenandoah, the .com's heap at 512 MiB, the .org's given 256 more): the
server at 2.6 of 3.8 GiB used, 1.2 GiB available, 2 GiB of swap used; Build 204 (three images, the
v4 tests) passed alongside them.
### 4. Try it, deployed (2026-11-08)
Captain Bob ran `demo/DEPLOY.md` on the server (#115): try.starshipos-f18.com with its own Let's
Encrypt certificate, ttyd behind Apache, framing allowed to the .com alone. Checked from outside: a
session ready 1.5 s after the page opens, `3 4 + .` -> `7 ok`, `: SQUARE DUP * ; 7 SQUARE .` ->
`49 ok`, a third visitor at once told every machine is busy.
Found on the way:
- Ubuntu's `ttyd` package starts its own `ttyd.service` -- `login` on port 7681, ours -- and
until it was disabled the page served the server's login prompt to anyone. `DEPLOY.md` step 4
now disables it first and checks what listens on 7681.
- **An idle StarshipOS keeps a host core 100% busy** (#151): the demo machine at its prompt, idle
20 s, QEMU at 100% of a core, 148 MiB resident -- the kernel's idle loop polls, never halts. Two
visitors' sessions with Gitea's diff statistics on a burst of pushes (#152, 1.75 GiB) filled the
server's swap and stalled both wikis. The service now caps all sessions together at one core at
the lowest priority (`CPUQuota=100%`, `Nice=19`, `MemoryMax=1G`), until #151 is fixed.
- **Try it in the .com's navigation** (#117): "Try it" added to the canvas's Home and Inquiries
artboards, the .com rebuilt from them (`site/com`) and installed. Checked as a visitor: the home
page, the "Try it" link, the Try it page in 1.1 s, its framed console Ready.
### 5. #151: the idle loop halts (2026-11-08)
Ruled (Captain Bob): "Fix the kernel problem." The v4 system waited for a key -- `sk_v4_readline()`
with nothing typed, and `sk_v4_identity_wait()` headless until Zuse -- in `arch_relax()`, a spin.
Both now wait in `arch_halt()` (`hlt` on amd64, `wfi` on aarch64 and riscv64) until the next
interrupt: the heartbeat's timer, which is ISR-driven on all three (`heartbeat_tick()`), wakes them
at 100 Hz, so a key is seen within a tick, as the cursor's blink and the identity beat already
were. v3's REPL (`repl.c`) still spins; v3 is not in acceptance.
Verified: idle at Zuse's prompt for 20 s, QEMU's CPU **100% -> 1%** of a core on amd64 (the demo
machine), and **1%** on aarch64 and riscv64 (`logs/20261108-011614`, `-011931`); the acceptance
protocol passes -- amd64 `logs/20261108-005539`, aarch64 `-005753`, riscv64 `-010136`, POST
538/538, `dict_hash 0xfec8c47cfc6f437f` on all three, `7 ok` typed and answered; the stick loaded
high; `test-32` (39 programs, 0 failures). The demo's CPU cap (`CPUQuota=100%`) can be relaxed
once this reaches the server.
### 6. #131 closed: QEMU's capture, not our kernel (2026-11-08)
Run 197's riscv64 serial log, read from Gitea's artifact store on the server (`acceptance-logs-185`),
had the line with one character doubled -- "Boot diisk: not a StarshipOS stick" -- and a doubled
byte in OpenSBI's banner too, printed before our kernel runs. So the byte was doubled in QEMU's
serial capture under the server's load, not by the kernel (Captain Bob closed #131). The check that
failed was right to: it refuses a line that is not what was printed. The capture itself (the log is
`logfile=` on the socket chardev the acceptance types through) is what is left to harden.
+5 -1
View File
@@ -98,6 +98,10 @@ endif
# v4.0.0-alpha.2 — the first published alpha: v4.0.0-alpha.1 (233025da) was accepted and tagged,
# but its release run's proofs step built HOL-Library from scratch and was killed, so
# nothing was published; the tag stays where it is (Captain Bob, 2026-11-07).
# v4.0.0-alpha.3 — the first published alpha: v4.0.0-alpha.2 (2d43db25) kept the image's
# HOL-Library, but its proofs ran with Isabelle's defaults (Java 4 GiB, a thread per CPU)
# and were killed in the StarForth session; nothing was published, the tag stays. The
# proofs now run capped (scripts/nightly.sh: 1 thread, Java 512 MiB, ML 900 MiB).
# LITHOS_VERSION (kernel) and VERSION (embedded StarForth engine) are independent
# and do not auto-sync (§XXX.4) — do not infer a relationship if they ever coincide.
# Engine VERSION bumps at tag time by what actually changed (§XXX.6): major = any
@@ -106,7 +110,7 @@ endif
# the messaging layer was replaced by kernel-Hermes, PLOT/FB-* relocated to Hestia —
# all dictionary-visible, no FORTH-79 word semantics changed.
VERSION ?= 3.2.0
LITHOS_VERSION ?= 4.0.0-alpha.2
LITHOS_VERSION ?= 4.0.0-alpha.3
# ==============================================================================
# BUILD PATHS
+5 -1
View File
@@ -113,7 +113,11 @@ static int sk_v4_readline(void)
} else {
heartbeat_service();
}
arch_relax();
/* Nothing typed: wait for the next interrupt, not in a spin (#151 -- the spin kept a
* CPU core 100% busy at the prompt, and the demo's host with it). The heartbeat's
* timer wakes it at 100 Hz, so a key is seen within a tick, as the blink and the
* identity beat are. */
arch_halt();
continue;
}
if (c == '\r' || c == '\n') {
+1 -1
View File
@@ -331,7 +331,7 @@ void sk_v4_identity_wait(void)
while (!g_zuse_session) {
sk_v4_discard_input();
sk_v4_identity_beat();
arch_relax();
arch_halt(); /* headless, until Zuse: wait for the next tick, not a spin (#151) */
}
sk_v4_discard_input();
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More