release: v4.0.0-alpha.4 #163

Merged
rajames merged 10 commits from StarForth-v4.0.0 into master 2026-11-10 16:12:05 +00:00
25 changed files with 1199 additions and 128 deletions
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## 2026-11-10 — LithosAnanke v4.0.0-alpha.4: the plug-and-play stick
The plug-and-play stick (#92), the BAM flush (#155), the Book of Fabric (#138), the Dictionary of v4 (#157), and the .com’s Downloads and Documents pages (#126, #161).
## 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
+9 -1
View File
@@ -78,7 +78,7 @@ BOARD_DIR := $(if $(TARGET),$(BOARD_DIR_$(TARGET)))
SUBMAKE := $(MAKE) --no-print-directory
.DEFAULT_GOAL := all
.PHONY: all boot_image boot_images write_stick docs clean clean-docs kernel boards help help-hosted sbom sbom-board \
.PHONY: all boot_image boot_images write_stick docs fabric dictionary clean clean-docs kernel boards help help-hosted sbom sbom-board \
qemu qemu-esp qemu-gdb init
# ARCH given on the command line and no TARGET: `clean` is the kernel's for that
@@ -203,6 +203,14 @@ docs:
fi
@$(SUBMAKE) -f docs/book/Makefile BOARD=$(BOARD_DIR) BOARD_NAME=$(TARGET)
# fabric -- The Book of Fabric (#138): build/docs/BookOfFabric.pdf
fabric:
@$(SUBMAKE) -f docs/book/Makefile fabric
# dictionary -- The Dictionary of v4 (#157): build/docs/Dictionary.pdf
dictionary:
@$(SUBMAKE) -f docs/book/Makefile dictionary
clean-docs:
rm -rf build/docs
+3 -3
View File
@@ -1,4 +1,4 @@
# LithosAnanke v4.0.0-alpha.3
# LithosAnanke v4.0.0-alpha.4
**UEFI-bootable FORTH microkernel.** Boots from firmware, initialises memory and interrupts, then
runs StarForth as its sole userspace runtime. No libc. No OS. Just stone and necessity.
@@ -7,8 +7,8 @@ runs StarForth as its sole userspace runtime. No libc. No OS. Just stone and nec
**Try it in your browser:** [starshipos-f18.com](https://starshipos-f18.com/) — a StarshipOS
machine of your own, booted to `[zuse@Hera] ok>`, thrown away when you leave. **Releases:**
[v4.0.0-alpha.3](https://gitea.starshipos-f18.org/rajames/LithosAnanke/releases) (pre-release),
the kernel `4.0.0-alpha.3` with the StarForth engine `3.2.0`.
[v4.0.0-alpha.4](https://gitea.starshipos-f18.org/rajames/LithosAnanke/releases) (pre-release),
the kernel `4.0.0-alpha.4` with the StarForth engine `3.2.0`.
---
+64 -5
View File
@@ -57,8 +57,24 @@ chapter_name = $(word 1,$(subst :, ,$(1)))
chapter_src = $(word 2,$(subst :, ,$(1)))
FRAGMENTS := $(foreach c,$(CHAPTERS),$(GEN)/$(call chapter_name,$(c)).tex)
.PHONY: all tools FORCE
# The Book of Fabric (#138): the FABRIC design record alone, as one book, from
# the same fragments -- `make fabric` -> build/docs/BookOfFabric.pdf.
FABRIC_DIR := $(OUT)/fabric
FABRIC_PDF := $(OUT)/BookOfFabric.pdf
FABRIC_FRAGS := $(foreach n,fabric-0 fabric-1 fabric-2 fabric-3 fabric-3-5 fabric-3-6 fabric-3-7 fabric-4,$(GEN)/$(n).tex)
# The Dictionary of v4 (#157): every live word, alphabetical, with its stack
# notation and description -- `make dictionary` -> build/docs/Dictionary.pdf.
# Its names are the booted system's (v4/tools/mkdictionary.py); the check
# refuses a table that has fallen behind them.
DICT_DIR := $(OUT)/dictionary
DICT_PDF := $(OUT)/Dictionary.pdf
DICT_TSV := docs/reference/v4-dictionary.tsv
.PHONY: all fabric dictionary tools FORCE
all: $(PDF)
fabric: $(FABRIC_PDF)
dictionary: $(DICT_PDF)
tools:
@missing=""; \
@@ -86,25 +102,34 @@ $(GEN)/pandoc-highlighting.tex: $(BOOK_DIR)/pandoc/highlighting.latex | tools
@mkdir -p $(GEN)
@printf '```c\nx\n```\n' | $(PANDOC) -f gfm -t latex -s --template=$< -o $@
# Per-build facts the book prints: the commit every \datasource refers to,
# Per-build facts the books print: the commit every \datasource refers to,
# the version (git describe: a release's tag, or the tag and the commits since),
# and which board appendix (if any) is included.
$(VAR_DIR)/meta.tex: FORCE
@mkdir -p $(VAR_DIR)
define meta_cmds
@mkdir -p $(dir $@)
@{ \
c=$$(git rev-parse --short=12 HEAD 2>/dev/null || echo unknown); \
git diff --quiet HEAD -- 2>/dev/null || c="$$c (+ uncommitted changes)"; \
printf '\\newcommand{\\bookcommit}{%s}\n' "$$c"; \
printf '\\newcommand{\\bookdate}{%s}\n' "$$(date -u +%Y-%m-%d)"; \
printf '\\newcommand{\\bookversion}{%s}\n' "$$(git describe --tags --always 2>/dev/null || echo unknown)"; \
printf '\\def\\bookboard{%s}\n' "$(BOARD_NAME)"; \
$(if $(BOARD),printf '\\newcommand{\\bookboardappendix}{board-%s}\n' "$(BOARD)";) \
} > $@.tmp
@cmp -s $@.tmp $@ && rm -f $@.tmp || mv $@.tmp $@
endef
$(VAR_DIR)/meta.tex: FORCE
$(meta_cmds)
$(FABRIC_DIR)/meta.tex: FORCE
$(meta_cmds)
$(DICT_DIR)/meta.tex: FORCE
$(meta_cmds)
FORCE:
TEXINPUTS_BOOK := $(abspath $(VAR_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)):
$(PDF): $(BOOK_DIR)/main.tex $(FRAGMENTS) $(GEN)/pandoc-highlighting.tex $(VAR_DIR)/meta.tex | tools
$(PDF): $(BOOK_DIR)/main.tex $(BOOK_DIR)/preamble.tex $(FRAGMENTS) $(GEN)/pandoc-highlighting.tex $(VAR_DIR)/meta.tex | tools
@echo " LATEXMK $(BOOK_DIR)/main.tex -> $@"
@TEXINPUTS=$(TEXINPUTS_BOOK) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \
-file-line-error -outdir=$(VAR_DIR) $(BOOK_DIR)/main.tex > $(VAR_DIR)/latexmk.log 2>&1 || { \
@@ -112,3 +137,37 @@ $(PDF): $(BOOK_DIR)/main.tex $(FRAGMENTS) $(GEN)/pandoc-highlighting.tex $(VAR_D
echo "Error: LaTeX failed; full log: $(VAR_DIR)/main.log"; exit 1; }
@cp $(VAR_DIR)/main.pdf $@
@echo " PDF $@"
TEXINPUTS_FABRIC := $(abspath $(FABRIC_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)):
$(FABRIC_PDF): $(BOOK_DIR)/fabric.tex $(BOOK_DIR)/preamble.tex $(FABRIC_FRAGS) $(GEN)/pandoc-highlighting.tex $(FABRIC_DIR)/meta.tex | tools
@echo " LATEXMK $(BOOK_DIR)/fabric.tex -> $@"
@TEXINPUTS=$(TEXINPUTS_FABRIC) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \
-file-line-error -outdir=$(FABRIC_DIR) $(BOOK_DIR)/fabric.tex > $(FABRIC_DIR)/latexmk.log 2>&1 || { \
grep -A4 -E '^(.*:[0-9]+:|!)' $(FABRIC_DIR)/fabric.log | head -40; \
echo "Error: LaTeX failed; full log: $(FABRIC_DIR)/fabric.log"; exit 1; }
@cp $(FABRIC_DIR)/fabric.pdf $@
@echo " PDF $@"
# the live word list needs the hosted v4; the check fails the build on a word with no entry, an
# unfinished entry, or an entry for a word the system no longer has
$(GEN)/dictionary-words.md: $(DICT_TSV) v4/tools/mkdictionary.py FORCE | tools
@mkdir -p $(GEN)
@$(MAKE) --no-print-directory -C v4 hosted >/dev/null
@python3 v4/tools/mkdictionary.py check $(DICT_TSV)
@python3 v4/tools/mkdictionary.py md $(DICT_TSV) $@
$(GEN)/dictionary-words.tex: $(GEN)/dictionary-words.md $(PANDOC_FILTERS) | tools
@echo " PANDOC $< -> $@"
@$(PANDOC) $(PANDOC_FLAGS) --id-prefix=dictionary- $< -o $@
TEXINPUTS_DICT := $(abspath $(DICT_DIR)):$(abspath $(GEN)):$(abspath $(BOOK_DIR)):
$(DICT_PDF): $(BOOK_DIR)/dictionary.tex $(BOOK_DIR)/preamble.tex $(GEN)/dictionary-words.tex $(GEN)/pandoc-highlighting.tex $(DICT_DIR)/meta.tex | tools
@echo " LATEXMK $(BOOK_DIR)/dictionary.tex -> $@"
@TEXINPUTS=$(TEXINPUTS_DICT) $(LATEXMK) -xelatex -interaction=nonstopmode -halt-on-error \
-file-line-error -outdir=$(DICT_DIR) $(BOOK_DIR)/dictionary.tex > $(DICT_DIR)/latexmk.log 2>&1 || { \
grep -A4 -E '^(.*:[0-9]+:|!)' $(DICT_DIR)/dictionary.log | head -40; \
echo "Error: LaTeX failed; full log: $(DICT_DIR)/dictionary.log"; exit 1; }
@cp $(DICT_DIR)/dictionary.pdf $@
@echo " PDF $@"
+42
View File
@@ -0,0 +1,42 @@
% docs/book/dictionary.tex -- master file of The Dictionary of v4 (#157):
% every word StarForth v4 boots with, alphabetical, with its stack notation,
% a description and where it is defined (Captain Bob, 2026-11-08: "a
% 'dictionary' document with the word, stack notations and descriptions.
% Alphabetical."; v4's words only).
%
% Built by `make dictionary` (docs/book/Makefile): v4/tools/mkdictionary.py
% checks docs/reference/v4-dictionary.tsv against the live system and writes
% the Markdown that pandoc turns into the fragment input below.
\documentclass[11pt,openany]{book}
\input{preamble}
\title{The Dictionary of StarForth v4\\[0.5em]\large Every word, its stack notation, and what it does}
\author{Robert A. James (Captain Bob)}
\date{Version \texttt{\bookversion}\\Built \bookdate{} from commit \texttt{\bookcommit}}
\begin{document}
\frontmatter
\maketitle
\chapter{How to read it}
Every word of the dictionary StarForth v4 boots with is here, in
alphabetical order. The names are not typed by hand: they are the booted
system's own list (\texttt{WORDS}, and the kernel's words of the node), and
the build refuses a dictionary that has fallen behind it.
\textbf{Stack notation.} \texttt{( before -{}- after )}: the values a word
takes from the stack, the top on the right, and what it leaves there.
\texttt{( R: ... )} is the return stack. \texttt{n} a signed number,
\texttt{u} an unsigned one, \texttt{d} and \texttt{ud} double-cell numbers,
\texttt{q} a Q48.16 fixed-point number, \texttt{x} any cell, \texttt{flag}
a truth value (0 false, -1 true), \texttt{c} a character, \texttt{addr} a
cell address, \texttt{baddr} a byte address, \texttt{xt} an execution token.
\textbf{xxx.} Where a word reads a name or text from the input stream, the
description shows it, as \texttt{' xxx} or \texttt{.\textquotedbl{} text\textquotedbl}.
\mainmatter
\input{dictionary-words}
\end{document}
+69
View File
@@ -0,0 +1,69 @@
% docs/book/fabric.tex -- master file of The Book of Fabric (#138): the FABRIC
% design record as one book, published with every release (Captain Bob,
% 2026-11-07: "build a 'The Book of Fabric' and publish it as version
% <version> every release"; 2026-11-08: "Publish. Fabric is all about steady
% state.").
%
% Built by `make fabric` (docs/book/Makefile) with xelatex, from the same
% pandoc fragments as the book (fabric-0 ... fabric-4, from docs/fabric/).
\documentclass[11pt,openany]{book}
\input{preamble}
\title{The Book of Fabric\\[0.5em]\large The StarshipOS design record}
\author{Robert A. James (Captain Bob)}
\date{Version \texttt{\bookversion}\\Built \bookdate{} from commit \texttt{\bookcommit}}
\begin{document}
\frontmatter
\maketitle
\tableofcontents
\chapter{About this book}
FABRIC is the design record of StarshipOS, LithosAnanke and StarForth: every
ruling, the reasoning behind it, what was built, what was found, and what was
abandoned, written as the work happened. It is reproduced here as written --
struck-through corrections, superseded plans and all -- because the record of
what was believed, and when, is part of the record.
One document is living at a time; when its topic closes, it is closed and a
successor opens. Part~I is the living document. Part~II holds the closed
ones, in the order they were written; each states, at its head, when and why
it closed. Part~III holds the forward-looking notes.
Each chapter is the Markdown file named below, converted when this book is
built; the version and commit on the title page say exactly which.
\begin{longtable}{@{}lll@{}}
\toprule
Document & Topic & Status \\
\midrule
\endhead
FABRIC-3 & Bare metal boot & Living \\
FABRIC-0 & The Stadium & Closed 2026-08-12 \\
FABRIC-1 & The Stadium, continued & Closed 2026-08-25 \\
FABRIC-2 & The Stadium, continued again & Closed 2026-09-04 \\
FABRIC-3.5 & The Tripod/kernel reshuffle & Closed 2026-09-22 \\
FABRIC-3.6 & The reshuffle's execution log & Closed 2026-09-22 \\
FABRIC-3.7 & Phase 8 PKI: the elevation entrypoint & Closed 2026-10-14 \\
FABRIC-4 & Forward-looking design notes & Notes \\
\bottomrule
\end{longtable}
\mainmatter
\part{The living record}
\input{fabric-3}
\part{The closed records}
\input{fabric-0}
\input{fabric-1}
\input{fabric-2}
\input{fabric-3-5}
\input{fabric-3-6}
\input{fabric-3-7}
\part{Forward-looking notes}
\input{fabric-4}
\end{document}
+1 -93
View File
@@ -6,99 +6,7 @@
% build/docs/gen/; meta.tex is generated per build. Neither is committed.
\documentclass[11pt,openany]{book}
\usepackage{amsmath,amssymb}
\usepackage{fontspec}
% fonts-dejavu-core has no serif/sans italics (fonts-dejavu-extra does); when a
% shape is missing, slant the upright one rather than silently drop emphasis.
\setmainfont{DejaVu Serif}[AutoFakeSlant=0.2]
\setsansfont{DejaVu Sans}[AutoFakeSlant=0.2]
\setmonofont{DejaVu Sans Mono}[Scale=0.85]
\newfontfamily\symbolfont{DejaVu Sans}
\usepackage[letterpaper,margin=1in]{geometry}
\usepackage{xcolor}
\usepackage{graphicx}
\usepackage{longtable,booktabs,array,calc,multirow}
\usepackage{fancyvrb}
\usepackage[normalem]{ulem}
\usepackage{enumitem}
\usepackage{newunicodechar}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.18}
\usepackage[hidelinks]{hyperref}
\usepackage{bookmark}
% --- what pandoc's LaTeX fragments expect (normally from its own template)
\providecommand{\tightlist}{\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\providecommand{\pandocbounded}[1]{#1}
\providecommand{\st}[1]{\sout{#1}}
\newcounter{none} % pandoc: {\def\LTcaptype{none} ...} marks unnumbered tables
\usepackage{etoolbox}
\makeatletter
\def\fnum@table{\tablename~\thetable}
\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{}
\makeatother
\usepackage{fvextra}
\input{pandoc-highlighting}
% Code and diagrams are wider than the page in places: wrap, never overflow.
\RecustomVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\},breaklines,breakanywhere,fontsize=\small}
\RecustomVerbatimEnvironment{verbatim}{Verbatim}{breaklines,breakanywhere,fontsize=\small}
\setlistdepth{9}
\renewlist{itemize}{itemize}{9}
\setlist[itemize]{label=\textbullet}
\renewlist{enumerate}{enumerate}{9}
\setlist[enumerate]{label=\arabic*.}
% Running heads: chapter on the left page, section on the right, both cut to
% the page width (several design-record titles are a full sentence long).
\usepackage{fancyhdr}
\usepackage[fit]{truncate}
\pagestyle{fancy}
\fancyhf{}
\renewcommand{\chaptermark}[1]{\markboth{#1}{}}
\renewcommand{\sectionmark}[1]{\markright{#1}}
\fancyhead[LE]{\small\truncate{\dimexpr\headwidth-3em}{\leftmark}}
\fancyhead[RO]{\small\truncate{\dimexpr\headwidth-3em}{\rightmark}}
\fancyhead[RE,LO]{\small\thepage}
\renewcommand{\headrulewidth}{0.4pt}
\setlength{\headheight}{14pt}
\fancypagestyle{plain}{\fancyhf{}\fancyfoot[C]{\small\thepage}\renewcommand{\headrulewidth}{0pt}}
\setlength{\emergencystretch}{3em}
\setlength{\parindent}{0pt}
\setlength{\parskip}{0.5em}
% --- glyphs DejaVu Serif lacks: the status marks used across the docs
\newunicodechar{✅}{{\symbolfont ✔}}
\newunicodechar{✓}{{\symbolfont ✓}}
\newunicodechar{❌}{{\symbolfont ✘}}
\newunicodechar{✗}{{\symbolfont ✗}}
\newunicodechar{⬜}{{\symbolfont ☐}}
\newunicodechar{⭐}{{\symbolfont ★}}
\newunicodechar{🔶}{{\symbolfont ◆}}
\newunicodechar{⚠}{{\symbolfont ⚠}}
\newunicodechar{📋}{}
\newunicodechar{🐛}{[bug]}
\newunicodechar{🎯}{[goal]}
\newunicodechar{🟡}{{\symbolfont ●}}
\newunicodechar{📍}{{\symbolfont ▸}}
\newunicodechar{🔓}{[unlocked]}
\newunicodechar{❓}{?}
\newunicodechar{⟺}{\ensuremath{\Longleftrightarrow}}
\newunicodechar{⋯}{\ensuremath{\cdots}}
\newunicodechar{^^^^fe0f}{}
% --- the documents number their own sections (§III.1, D-3, ...)
\setcounter{secnumdepth}{0}
\setcounter{tocdepth}{1}
\input{meta}
% \datasource{path/to/data.csv}: cite the data behind a figure or table.
% Every figure or table of measured numbers reads its CSV at build time
% (\addplot table / \pgfplotstabletypeset) and is followed by this line, so
% the reader can find the exact file at the exact commit.
\newcommand{\datasource}[1]{%
\par{\small Data: \texttt{\detokenize{#1}} at commit \texttt{\bookcommit}.}\par}
\input{preamble}
\title{LithosAnanke and StarForth}
\author{}
+97
View File
@@ -0,0 +1,97 @@
% docs/book/preamble.tex -- what both books share: the packages, the fonts,
% what pandoc's fragments expect, the glyphs, the running heads, and meta.tex
% (the commit, the date, the version). Input by main.tex (the book) and
% fabric.tex (the Book of Fabric, #138).
\usepackage{amsmath,amssymb}
\usepackage{fontspec}
% fonts-dejavu-core has no serif/sans italics (fonts-dejavu-extra does); when a
% shape is missing, slant the upright one rather than silently drop emphasis.
\setmainfont{DejaVu Serif}[AutoFakeSlant=0.2]
\setsansfont{DejaVu Sans}[AutoFakeSlant=0.2]
\setmonofont{DejaVu Sans Mono}[Scale=0.85]
\newfontfamily\symbolfont{DejaVu Sans}
\usepackage[letterpaper,margin=1in]{geometry}
\usepackage{xcolor}
\usepackage{graphicx}
\usepackage{longtable,booktabs,array,calc,multirow}
\usepackage{fancyvrb}
\usepackage[normalem]{ulem}
\usepackage{enumitem}
\usepackage{newunicodechar}
\usepackage{pgfplots}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.18}
\usepackage[hidelinks]{hyperref}
\usepackage{bookmark}
% --- what pandoc's LaTeX fragments expect (normally from its own template)
\providecommand{\tightlist}{\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\providecommand{\pandocbounded}[1]{#1}
\providecommand{\st}[1]{\sout{#1}}
\newcounter{none} % pandoc: {\def\LTcaptype{none} ...} marks unnumbered tables
\usepackage{etoolbox}
\makeatletter
\def\fnum@table{\tablename~\thetable}
\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{}
\makeatother
\usepackage{fvextra}
\input{pandoc-highlighting}
% Code and diagrams are wider than the page in places: wrap, never overflow.
\RecustomVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\},breaklines,breakanywhere,fontsize=\small}
\RecustomVerbatimEnvironment{verbatim}{Verbatim}{breaklines,breakanywhere,fontsize=\small}
\setlistdepth{9}
\renewlist{itemize}{itemize}{9}
\setlist[itemize]{label=\textbullet}
\renewlist{enumerate}{enumerate}{9}
\setlist[enumerate]{label=\arabic*.}
% Running heads: chapter on the left page, section on the right, both cut to
% the page width (several design-record titles are a full sentence long).
\usepackage{fancyhdr}
\usepackage[fit]{truncate}
\pagestyle{fancy}
\fancyhf{}
\renewcommand{\chaptermark}[1]{\markboth{#1}{}}
\renewcommand{\sectionmark}[1]{\markright{#1}}
\fancyhead[LE]{\small\truncate{\dimexpr\headwidth-3em}{\leftmark}}
\fancyhead[RO]{\small\truncate{\dimexpr\headwidth-3em}{\rightmark}}
\fancyhead[RE,LO]{\small\thepage}
\renewcommand{\headrulewidth}{0.4pt}
\setlength{\headheight}{14pt}
\fancypagestyle{plain}{\fancyhf{}\fancyfoot[C]{\small\thepage}\renewcommand{\headrulewidth}{0pt}}
\setlength{\emergencystretch}{3em}
\setlength{\parindent}{0pt}
\setlength{\parskip}{0.5em}
% --- glyphs DejaVu Serif lacks: the status marks used across the docs
\newunicodechar{✅}{{\symbolfont ✔}}
\newunicodechar{✓}{{\symbolfont ✓}}
\newunicodechar{❌}{{\symbolfont ✘}}
\newunicodechar{✗}{{\symbolfont ✗}}
\newunicodechar{⬜}{{\symbolfont ☐}}
\newunicodechar{⭐}{{\symbolfont ★}}
\newunicodechar{🔶}{{\symbolfont ◆}}
\newunicodechar{⚠}{{\symbolfont ⚠}}
\newunicodechar{📋}{}
\newunicodechar{🐛}{[bug]}
\newunicodechar{🎯}{[goal]}
\newunicodechar{🟡}{{\symbolfont ●}}
\newunicodechar{📍}{{\symbolfont ▸}}
\newunicodechar{🔓}{[unlocked]}
\newunicodechar{❓}{?}
\newunicodechar{⟺}{\ensuremath{\Longleftrightarrow}}
\newunicodechar{⋯}{\ensuremath{\cdots}}
\newunicodechar{^^^^fe0f}{}
% --- the documents number their own sections (§III.1, D-3, ...)
\setcounter{secnumdepth}{0}
\setcounter{tocdepth}{1}
\input{meta}
% \datasource{path/to/data.csv}: cite the data behind a figure or table.
% Every figure or table of measured numbers reads its CSV at build time
% (\addplot table / \pgfplotstabletypeset) and is followed by this line, so
% the reader can find the exact file at the exact commit.
\newcommand{\datasource}[1]{%
\par{\small Data: \texttt{\detokenize{#1}} at commit \texttt{\bookcommit}.}\par}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,32 @@
# alpha.4's documents and pages -- progress (kept current, one line per increment)
Captain Bob, 2026-11-10: "Do it" -- the rest of alpha.4 that lives in the tree, after #92 (merged,
#160). #133 and #134 are the server's (Captain Bob's).
## #138 -- The Book of Fabric
- [x] `docs/book/fabric.tex`: the FABRIC record as one book -- Part I the living record (FABRIC-3),
Part II the closed ones in order, Part III the forward-looking notes; title page with the
version (`git describe --tags`) and the commit. The book's preamble moved into
`docs/book/preamble.tex`, shared by both books (the book itself builds unchanged, 821 pages).
- [x] `make fabric` -> `build/docs/BookOfFabric.pdf` (722 pages, 42 s).
- [x] `scripts/nightly.sh docs` builds it (`$DOCS/fabric.pdf`); `publish` sends it as
`org.starship:documentation:<version>`, classifier `fabric`; `gitea-release` attaches every
PDF in `$DOCS`, so the release carries it.
## #157 -- the dictionary of v4, and the .com's Documents page
- [x] the live word list of the built system (318 words): `v4/tools/mkdictionary.py words` -- the hosted v4's `WORDS` (317) plus the kernel's `sk_v4_words[]` (`MINT-ZUSE`)
- [x] the table in the tree, `docs/reference/v4-dictionary.tsv`: all 318 entries, written in three alphabetical batches against v4's own definitions (the source gave 54 stacks and 105 descriptions to start from); `check` passes
- [x] the check: `mkdictionary.py check` -- every live word has a finished entry, every entry a live word; run by `make dictionary`, so a stale table fails the build and the nightly
- [x] the PDF, alphabetical: `make dictionary` -> `build/docs/Dictionary.pdf` (20 pages; `docs/book/dictionary.tex`); the nightly's docs stage builds it, `publish` sends classifier `dictionary`, the release attaches it
- [x] the Documents page: the canvas artboard (`project/Documents.dc.html`, canvas v17: the Book of Fabric and the Dictionary first, then the book, the volumes, the API; each with its SHA-256; the SSRN paper), then `site/com/canvas/Documents.dc.html`
## #126 -- the Downloads page live
- [x] `site/com/build.py --release TAG --sums SHA256SUMS` builds Downloads and Documents: `[TAG]`, `[RELEASE]/<file>` (the
Gitea release's file), `[SHA:<file>]`; a file not in SHA256SUMS fails the build. Without a release it builds the three
pages as before, byte for byte, with no link to the two. `install.sh` installs them when they are built. The release's
SHA256SUMS now covers every file it attaches (the images, the PDFs, the Root CA's certificate): `nightly.sh gitea-release`
- [x] the nav links on Home, Try it, Inquiries (canvas v17; shown only when the two pages are built)
**At the alpha.4 release** (after `gitea-release`): download the release's `SHA256SUMS`, then
`python3 site/com/build.py --release v4.0.0-alpha.4 --sums SHA256SUMS` and
`XWIKI_USER=rajames XWIKI_PASSWORD=... site/com/install.sh` (Captain Bob's; the .com's login).
+1 -1
View File
@@ -112,7 +112,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.3
LITHOS_VERSION ?= 4.0.0-alpha.4
# ==============================================================================
# BUILD PATHS
+22 -6
View File
@@ -42,7 +42,9 @@
# The book (make docs), the formal volumes (make -C docs/formal vols) and the API reference
# (make -C docs/formal doxygen), into build/nightly-docs/.
# scripts/nightly.sh publish-docs <URL>
# org.starship:documentation:<VERSION>: classifier book, vol1, vol2, vol3, api (pdf).
# org.starship:documentation:<VERSION>: classifier book, fabric (The Book of Fabric, #138),
# dictionary (The Dictionary of v4, #157 -- it fails the stage if a live word has no entry),
# vol1, vol2, vol3, api (pdf).
#
# VERSION is <LITHOS_VERSION>-SNAPSHOT, or NIGHTLY_VERSION when set (a release sets it from its
# tag). NEXUS_USER (default ci-publisher) and NEXUS_PASSWORD; publishing needs mvn and a JRE.
@@ -173,8 +175,12 @@ publish)
docs)
rm -rf "$DOCS"; mkdir -p "$DOCS"
make --no-print-directory docs
make --no-print-directory fabric
make --no-print-directory dictionary
make --no-print-directory -C docs/formal vols doxygen
cp build/docs/LithosAnanke.pdf "$DOCS/book.pdf"
cp build/docs/BookOfFabric.pdf "$DOCS/fabric.pdf"
cp build/docs/Dictionary.pdf "$DOCS/dictionary.pdf"
for v in 1 2 3; do cp "docs/formal/build/vol$v/main.pdf" "$DOCS/vol$v.pdf"; done
cp docs/formal/build/doxygen/StarForth-API-Reference.pdf "$DOCS/api.pdf"
(cd "$DOCS" && sha256sum ./*.pdf | tee SHA256SUMS)
@@ -189,15 +195,23 @@ gitea-release)
: "${RELEASE_TOKEN:?RELEASE_TOKEN: a token that may write the repository}"
api=${GITEA_API:-${GITHUB_SERVER_URL:-https://gitea.starshipos-f18.org}/api/v1/repos/${GITHUB_REPOSITORY:-rajames/LithosAnanke}}
need "$OUT/StarshipOS.iso" "$OUT/pc-stick.img.xz" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz" "$OUT/SHA256SUMS"
files=("$OUT/StarshipOS.iso" "$OUT/pc-stick.img.xz" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz" "$OUT/SHA256SUMS")
for f in "$DOCS"/*.pdf ci/pki/starshipos-root-ca.pem; do [ -f "$f" ] && files+=("$f"); done
# The release's SHA256SUMS covers every file it attaches -- the images' sums as the images
# stage wrote them, then the documents and the Root CA's certificate -- so that
# `sha256sum -c SHA256SUMS` checks all of it, and the .com's Documents page (site/com/build.py,
# #157) finds each document's sum. The Nexus artifacts keep their own SHA256SUMS.
rel=$(mktemp -d)
cp "$OUT/SHA256SUMS" "$rel/SHA256SUMS"
extra=("$DOCS"/*.pdf ci/pki/starshipos-root-ca.pem)
for f in "${extra[@]}"; do [ -f "$f" ] && (cd "$(dirname "$f")" && sha256sum "$(basename "$f")") >> "$rel/SHA256SUMS"; done
files=("$OUT/StarshipOS.iso" "$OUT/pc-stick.img.xz" "$OUT/raspi5.img.xz" "$OUT/MilkV.img.xz" "$rel/SHA256SUMS")
for f in "${extra[@]}"; do [ -f "$f" ] && files+=("$f"); done
gitea() { curl -fsS -H "Authorization: token $RELEASE_TOKEN" "$@"; }
body=$(python3 -c '
import json, sys
notes, sums, version = sys.argv[1], open(sys.argv[2]).read(), sys.argv[3]
print(json.dumps(notes.strip() + "\n\n## Checksums (SHA-256)\n\n```\n" + sums.strip() + "\n```\n\n"
"Also in Nexus: `org.starship:kernelImage:" + version + "` and `org.starship:documentation:" + version + "`.\n"))' \
"$(git tag -l --format='%(contents)' "$tag")" "$OUT/SHA256SUMS" "$VERSION")
"$(git tag -l --format='%(contents)' "$tag")" "$rel/SHA256SUMS" "$VERSION")
id=$(gitea "$api/releases/tags/$tag" 2>/dev/null | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' 2>/dev/null || true)
if [ -z "$id" ]; then
pre=false; case "$tag" in *-*) pre=true;; esac
@@ -214,11 +228,13 @@ print(json.dumps(notes.strip() + "\n\n## Checksums (SHA-256)\n\n```\n" + sums.st
case " $have " in *" $n "*) echo " $n: there"; continue;; esac
gitea -o /dev/null -X POST -F "attachment=@$f" "$api/releases/$id/assets?name=$n" && echo " $n: attached"
done
rm -f "$rel/SHA256SUMS" && rmdir "$rel"
;;
publish-docs)
url=${2:-}; [ -n "$url" ] || usage
need "$DOCS/book.pdf" "$DOCS/vol1.pdf" "$DOCS/vol2.pdf" "$DOCS/vol3.pdf" "$DOCS/api.pdf"
deploy "$url" documentation "$DOCS/book.pdf" pdf book \
need "$DOCS/book.pdf" "$DOCS/fabric.pdf" "$DOCS/dictionary.pdf" "$DOCS/vol1.pdf" "$DOCS/vol2.pdf" "$DOCS/vol3.pdf" "$DOCS/api.pdf"
deploy "$url" documentation "$DOCS/book.pdf" pdf book "$DOCS/fabric.pdf" pdf fabric \
"$DOCS/dictionary.pdf" pdf dictionary \
"$DOCS/vol1.pdf" pdf vol1 "$DOCS/vol2.pdf" pdf vol2 "$DOCS/vol3.pdf" pdf vol3 \
"$DOCS/api.pdf" pdf api
;;
+4 -4
View File
@@ -91,19 +91,19 @@ html, body, #xwikimaincontainer, #xwikimaincontainerinner, #contentcontainer, #c
<h2 style="margin: 0; font-family: 'Exo 2', sans-serif; font-size: 24px">Or write directly</h2>
<div style="border-top: 1px solid #22324D !important; padding-top: 16px">
<div style="font-size: 13px; color: #A3AEC2 !important">General</div>
<a href="mailto:info@starshipos.com" style="font-size: 16px">info@starshipos.com</a>
<a href="mailto:rajames440@gmail.com" style="font-size: 16px">rajames440@gmail.com</a>
</div>
<div style="border-top: 1px solid #22324D !important; padding-top: 16px">
<div style="font-size: 13px; color: #A3AEC2 !important">Licensing &amp; partnerships</div>
<a href="mailto:licensing@starshipos.com" style="font-size: 16px">licensing@starshipos.com</a>
<a href="mailto:rajames440@gmail.com" style="font-size: 16px">rajames440@gmail.com</a>
</div>
<div style="border-top: 1px solid #22324D !important; padding-top: 16px">
<div style="font-size: 13px; color: #A3AEC2 !important">Press &amp; media</div>
<a href="mailto:press@starshipos.com" style="font-size: 16px">press@starshipos.com</a>
<a href="mailto:rajames440@gmail.com" style="font-size: 16px">rajames440@gmail.com</a>
</div>
<div style="border-top: 1px solid #22324D !important; padding-top: 16px">
<div style="font-size: 13px; color: #A3AEC2 !important">Security reports</div>
<a href="mailto:security@starshipos.org" style="font-size: 16px">security@starshipos.org</a>
<a href="mailto:rajames440@gmail.com" style="font-size: 16px">rajames440@gmail.com</a>
</div>
<p style="margin: 0; padding-top: 16px; border-top: 1px solid #22324D !important; font-size: 13px; color: #A3AEC2 !important">StarshipOS is developed in Ashland, Ohio.</p>
</aside>
+2 -2
View File
@@ -134,7 +134,7 @@ html, body, #xwikimaincontainer, #xwikimaincontainerinner, #contentcontainer, #c
<h2 id="signup-h" style="margin: 0 0 8px; font-family: 'Exo 2', sans-serif; font-size: 28px">Launch announcements</h2>
<p style="margin: 0; color: #A3AEC2 !important; font-size: 14px">Milestones, releases and papers. Low volume. Unsubscribe anytime.</p>
</div>
<form action="mailto:info@starshipos.com?subject=Subscribe%20to%20launch%20announcements" method="post" enctype="text/plain" style="flex: 1 1 420px; display: flex; flex-wrap: wrap; gap: 12px" aria-label="Subscribe to announcements">
<form action="mailto:rajames440@gmail.com?subject=Subscribe%20to%20launch%20announcements" method="post" enctype="text/plain" style="flex: 1 1 420px; display: flex; flex-wrap: wrap; gap: 12px" aria-label="Subscribe to announcements">
<label for="sub-email" style="position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0)">Email address</label>
<input id="sub-email" name="email" type="email" required placeholder="you@example.com" style="flex: 1 1 240px; min-height: 48px; padding: 0 16px; background: #0A1222 !important; border: 1px solid #34486A !important; border-radius: 4px; color: #EEF2F7 !important; font-family: inherit; font-size: 15px">
<button type="submit" style="min-height: 48px; padding: 0 24px; background: #F5891F !important; color: #0A1222 !important; border: 0 !important; border-radius: 4px; font-family: inherit; font-weight: 700; font-size: 15px; cursor: pointer">Subscribe</button>
@@ -145,7 +145,7 @@ html, body, #xwikimaincontainer, #xwikimaincontainerinner, #contentcontainer, #c
<footer style="border-top: 1px solid #22324D !important">
<div style="max-width: 1120px; margin: 0 auto; padding: 32px 24px; display: flex; flex-wrap: wrap; gap: 16px 32px; justify-content: space-between; font-size: 13px; color: #A3AEC2 !important">
<span>© 2026 Robert Allan James · A Starship Foundation project (in formation)</span>
<span style="display: flex; flex-wrap: wrap; gap: 24px"><a href="/bin/view/Inquiries/" style="color: #C8D1DE !important">Inquiries</a><a href="mailto:info@starshipos.com" style="color: #C8D1DE !important">info@starshipos.com</a><a href="https://starshipos-f18.org/" style="color: #C8D1DE !important">starshipos-f18.org</a></span>
<span style="display: flex; flex-wrap: wrap; gap: 24px"><a href="/bin/view/Inquiries/" style="color: #C8D1DE !important">Inquiries</a><a href="mailto:rajames440@gmail.com" style="color: #C8D1DE !important">rajames440@gmail.com</a><a href="https://starshipos-f18.org/" style="color: #C8D1DE !important">starshipos-f18.org</a></span>
</div>
</footer>
+48 -7
View File
@@ -3,7 +3,9 @@
# Copyright (c) 2023-2026 Robert A. James
"""build.py -- the starshipos-f18.com XWiki pages, made from the "StarshipOS.com" design canvas.
site/com/build.py canvas/{Main,Inquiries,TryIt}.dc.html -> {Main,Inquiries,TryIt}.WebHome.xwiki
site/com/build.py [--release TAG --sums SHA256SUMS]
canvas/{Main,Inquiries,TryIt}.dc.html -> {Main,Inquiries,TryIt}.WebHome.xwiki,
and with a release, canvas/{Downloads,Documents}.dc.html too
The canvas is the source: its artboards are copied into canvas/ as they are, and this turns each into
one {{html}} block (which needs a page author with script rights). What changes on the way:
@@ -15,6 +17,10 @@ one {{html}} block (which needs a page author with script rights). What changes
- Try it: the drawn console becomes the real one, an iframe of try.starshipos-f18.com;
- the inquiry form posts to the .org's PublicInquiry, which files it under Inquiries there
(admin-only, site/org/inquiries); the announcements form, which has no server, sends by e-mail;
- Downloads and Documents (#126, #157) are built from a release: --release TAG and the release's
SHA256SUMS fill in [TAG], [RELEASE]/<file> (the file on the Gitea release) and [SHA:<file>].
Without a release they are not built, and the other pages' links to them are taken out, so
nothing on the site points at a page that is not there (they go live with alpha.4);
install.sh puts the pages and the images on the wiki.
"""
@@ -31,6 +37,8 @@ FONTS = ('<link rel="preconnect" href="https://fonts.googleapis.com">\n'
'&amp;family=Exo+2:wght@600;700&amp;display=swap" rel="stylesheet">')
ORG = "https://starshipos-f18.org/"
PAGES = ["Main", "Inquiries", "TryIt"]
RELEASE_PAGES = ["Downloads", "Documents"]
RELEASES = "https://gitea.starshipos-f18.org/rajames/LithosAnanke/releases/download/"
IMAGES = {"/_blob/5b564d54d98e02ad600c913a9a1888f2": "badge.jpg",
"/_blob/5483451f292782a8b366e437181c4bcf": "captain-bob.jpg"}
@@ -44,11 +52,15 @@ CONSOLE = '''<div style="background: #050A14; border: 1px solid #34486A; border-
<p style="margin: 12px 0 0; font-size: 13px; color: #A3AEC2">If the console does not connect, every machine is in use: try again in a few minutes.</p>'''
def body(src):
def body(src, pages):
m = re.search(r"<x-dc>(.*)</x-dc>", src, re.S)
html = re.sub(r"<helmet>.*?</helmet>", "", m.group(1), flags=re.S).strip()
html = html.replace("{{accent}}", ACCENT)
for p in PAGES:
# a page not built this time is not linked to: its <a> taken out (the nav, the footers)
for p in RELEASE_PAGES:
if p not in pages:
html = re.sub(r'<a href="%s\.dc\.html"[^>]*>[^<]*</a>\n?' % p, "", html)
for p in pages:
html = html.replace('href="%s.dc.html"' % p, 'href="/bin/view/%s/"' % p)
html = html.replace('href="https://starshipos.org"', 'href="%s"' % ORG)
html = html.replace(">starshipos.org</a>", ">starshipos-f18.org</a>")
@@ -68,7 +80,7 @@ def try_it(html):
def forms(html, page):
if page == "Main":
html = html.replace('<form style="flex: 1 1 420px; display: flex; flex-wrap: wrap; gap: 12px" aria-label="Subscribe to announcements">',
'<form action="mailto:info@starshipos.com?subject=Subscribe%20to%20launch%20announcements" method="post" enctype="text/plain" style="flex: 1 1 420px; display: flex; flex-wrap: wrap; gap: 12px" aria-label="Subscribe to announcements">')
'<form action="mailto:rajames440@gmail.com?subject=Subscribe%20to%20launch%20announcements" method="post" enctype="text/plain" style="flex: 1 1 420px; display: flex; flex-wrap: wrap; gap: 12px" aria-label="Subscribe to announcements">')
html = html.replace('<input id="sub-email" type="email"', '<input id="sub-email" name="email" type="email" required')
html = html.replace('<button type="button" style="min-height: 48px; padding: 0 24px',
'<button type="submit" style="min-height: 48px; padding: 0 24px')
@@ -90,14 +102,43 @@ def forms(html, page):
return html
def release(html, tag, sums):
"""[TAG], [RELEASE]/<file> and [SHA:<file>] from the release; a file it has not is an error."""
html = html.replace("[TAG]", tag)
html = html.replace("[RELEASE]/", RELEASES + tag + "/")
def sha(m):
if m.group(1) not in sums:
sys.exit("build: %s is not in the release's SHA256SUMS" % m.group(1))
return sums[m.group(1)]
return re.sub(r"\[SHA:([^\]]+)\]", sha, html)
if __name__ == "__main__":
import argparse
ap = argparse.ArgumentParser()
ap.add_argument("--release", help="the release's tag, e.g. v4.0.0-alpha.4")
ap.add_argument("--sums", help="the release's SHA256SUMS")
a = ap.parse_args()
if bool(a.release) != bool(a.sums):
sys.exit("build: --release and --sums go together")
sums = {}
if a.sums:
for l in open(a.sums).read().split("\n"):
f = l.split()
if len(f) == 2:
sums[f[1].lstrip("*").lstrip("./")] = f[0]
os.chdir(os.path.dirname(os.path.abspath(__file__)))
for p in PAGES:
html = forms(body(open("canvas/%s.dc.html" % p).read()), p)
pages = PAGES + (RELEASE_PAGES if a.release else [])
for p in pages:
html = forms(body(open("canvas/%s.dc.html" % p).read(), pages), p)
if p in RELEASE_PAGES:
html = release(html, a.release, sums)
if p == "TryIt":
html = try_it(html)
html = colorlock.lock(html, '<div style="background: #0A1222')
assert "{{" not in html.replace(colorlock.STYLE, "") and "[SSRN" not in html and ".dc.html" not in html and "_blob" not in html, p
assert "[TAG]" not in html and "[RELEASE]" not in html and "[SHA:" not in html, p
with open("%s.WebHome.xwiki" % p, "w") as f:
f.write(colorlock.GUEST_CHROME + '{{html clean="false"}}\n%s\n%s\n{{/html}}\n' % (FONTS, html))
print("wrote", ", ".join("%s.WebHome.xwiki" % p for p in PAGES))
print("wrote", ", ".join("%s.WebHome.xwiki" % p for p in pages))
+109
View File
@@ -0,0 +1,109 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>StarshipOS — Documents</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&amp;family=Exo+2:wght@600;700&amp;display=swap" rel="stylesheet">
<style>
body{margin:0;background:#0A1222}
a{color:#F5891F}a:hover{color:#FFB061}
</style>
</helmet>
<div style="background: #0A1222; color: #EEF2F7; font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 16px; line-height: 1.6; min-height: 100vh">
<header style="border-bottom: 1px solid #22324D">
<nav aria-label="Main" style="max-width: 1120px; margin: 0 auto; padding: 20px 24px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px">
<a href="Main.dc.html" style="font-family: 'Exo 2', sans-serif; font-weight: 700; font-size: 22px; color: #EEF2F7; text-decoration: none; letter-spacing: 0.02em">starship<span style="color: {{accent}}">OS</span></a>
<div style="display: flex; flex-wrap: wrap; gap: 24px; font-size: 14px">
<a href="Main.dc.html" style="color: #C8D1DE; text-decoration: none">Home</a>
<a href="TryIt.dc.html" style="color: #C8D1DE; text-decoration: none">Try it</a>
<a href="Downloads.dc.html" style="color: #C8D1DE; text-decoration: none">Downloads</a>
<a href="Documents.dc.html" aria-current="page" style="color: #EEF2F7; text-decoration: none; border-bottom: 2px solid {{accent}}">Documents</a>
<a href="Inquiries.dc.html" style="color: #C8D1DE; text-decoration: none">Inquiries</a>
<a href="https://starshipos.org" style="color: {{accent}}; text-decoration: none">Contributors →</a>
</div>
</nav>
</header>
<section style="max-width: 1120px; margin: 0 auto; padding: 80px 24px 32px">
<p style="margin: 0 0 12px; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; color: #4FD1B0">ok&gt; documents</p>
<h1 style="margin: 0 0 16px; font-family: 'Exo 2', sans-serif; font-size: clamp(36px, 5vw, 56px); line-height: 1.1">How it was built, and what it says.</h1>
<p style="margin: 0 0 24px; max-width: 720px; font-size: 18px; color: #C8D1DE">Every release builds its documents from the same tree as its images: the design record, the dictionary of every word, the book, the formal volumes. Each is a PDF on the release, with its checksum.</p>
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px; font-size: 14px">
<span style="display: inline-block; padding: 4px 12px; border-radius: 4px; background: #111C30; border: 1px solid #34486A; color: #EEF2F7">[TAG]</span>
<a href="[RELEASE]/SHA256SUMS" style="color: #C8D1DE">SHA256SUMS →</a>
<a href="https://gitea.starshipos-f18.org/rajames/LithosAnanke/releases" style="color: #C8D1DE">All releases →</a>
</div>
</section>
<section aria-labelledby="new-h" style="max-width: 1120px; margin: 0 auto; padding: 32px 24px 48px">
<p style="margin: 0 0 12px; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; color: {{accent}}">Start here</p>
<h2 id="new-h" style="margin: 0 0 32px; font-family: 'Exo 2', sans-serif; font-size: 36px; line-height: 1.15">Two books, kept with every release.</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 24px">
<article style="background: #111C30; border: 1px solid #34486A; border-radius: 6px; padding: 32px; display: flex; flex-direction: column; gap: 14px">
<div><h3 style="margin: 0 0 4px; font-family: 'Exo 2', sans-serif; font-size: 28px">The Book of Fabric</h3><p style="margin: 0; font-size: 13px; color: {{accent}}">the design record, as it happened</p></div>
<p style="margin: 0; color: #C8D1DE; font-size: 15px">Every ruling, the reasoning behind it, what was built and what was abandoned, written as the work went -- reproduced as written, corrections and all. The living record first, then the closed ones in order. Fabric is about steady state: this is how it was reached.</p>
<div style="font-size: 12px; color: #A3AEC2; word-break: break-all">SHA-256 [SHA:fabric.pdf]</div>
<a href="[RELEASE]/fabric.pdf" style="display: inline-block; align-self: flex-start; padding: 12px 20px; background: {{accent}}; color: #0A1222; font-weight: 700; text-decoration: none; border-radius: 4px">fabric.pdf</a>
</article>
<article style="background: #111C30; border: 1px solid #34486A; border-radius: 6px; padding: 32px; display: flex; flex-direction: column; gap: 14px">
<div><h3 style="margin: 0 0 4px; font-family: 'Exo 2', sans-serif; font-size: 28px">The Dictionary of v4</h3><p style="margin: 0; font-size: 13px; color: {{accent}}">every word, alphabetical</p></div>
<p style="margin: 0; color: #C8D1DE; font-size: 15px">Every word StarForth v4 boots with: its stack notation, what it does, and where it is defined. The names come from the booted system itself, and the build refuses a dictionary that has fallen behind it.</p>
<pre style="margin: 0; padding: 12px 14px; background: #0A1222; border: 1px solid #22324D; border-radius: 4px; font-size: 12px; color: #EEF2F7; white-space: pre-wrap">DUP ( x -- x x ) Copy the top value.
MINT-ZUSE ( -- ) Mint the real Zuse onto a second stick.</pre>
<div style="font-size: 12px; color: #A3AEC2; word-break: break-all">SHA-256 [SHA:dictionary.pdf]</div>
<a href="[RELEASE]/dictionary.pdf" style="display: inline-block; align-self: flex-start; padding: 12px 20px; background: {{accent}}; color: #0A1222; font-weight: 700; text-decoration: none; border-radius: 4px">dictionary.pdf</a>
</article>
</div>
</section>
<section aria-labelledby="more-h" style="max-width: 1120px; margin: 0 auto; padding: 32px 24px 48px">
<p style="margin: 0 0 12px; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; color: {{accent}}">And the rest</p>
<h2 id="more-h" style="margin: 0 0 32px; font-family: 'Exo 2', sans-serif; font-size: 30px; line-height: 1.15">The book, the formal set, the API.</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px">
<a href="[RELEASE]/book.pdf" style="display: block; background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 20px; text-decoration: none"><div style="font-family: 'Exo 2', sans-serif; font-size: 18px; color: #EEF2F7">The book</div><div style="font-size: 13px; color: #A3AEC2">StarshipOS, end to end</div><div style="margin-top: 8px; font-size: 11px; color: #6B7A93; word-break: break-all">[SHA:book.pdf]</div></a>
<a href="[RELEASE]/vol1.pdf" style="display: block; background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 20px; text-decoration: none"><div style="font-family: 'Exo 2', sans-serif; font-size: 18px; color: #EEF2F7">Volume I</div><div style="font-size: 13px; color: #A3AEC2">the formal set</div><div style="margin-top: 8px; font-size: 11px; color: #6B7A93; word-break: break-all">[SHA:vol1.pdf]</div></a>
<a href="[RELEASE]/vol2.pdf" style="display: block; background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 20px; text-decoration: none"><div style="font-family: 'Exo 2', sans-serif; font-size: 18px; color: #EEF2F7">Volume II</div><div style="font-size: 13px; color: #A3AEC2">the formal set</div><div style="margin-top: 8px; font-size: 11px; color: #6B7A93; word-break: break-all">[SHA:vol2.pdf]</div></a>
<a href="[RELEASE]/vol3.pdf" style="display: block; background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 20px; text-decoration: none"><div style="font-family: 'Exo 2', sans-serif; font-size: 18px; color: #EEF2F7">Volume III</div><div style="font-size: 13px; color: #A3AEC2">the formal set</div><div style="margin-top: 8px; font-size: 11px; color: #6B7A93; word-break: break-all">[SHA:vol3.pdf]</div></a>
<a href="[RELEASE]/api.pdf" style="display: block; background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 20px; text-decoration: none"><div style="font-family: 'Exo 2', sans-serif; font-size: 18px; color: #EEF2F7">API reference</div><div style="font-size: 13px; color: #A3AEC2">StarForth's C, generated</div><div style="margin-top: 8px; font-size: 11px; color: #6B7A93; word-break: break-all">[SHA:api.pdf]</div></a>
</div>
</section>
<section aria-labelledby="paper-h" style="max-width: 1120px; margin: 0 auto; padding: 48px 24px 96px">
<div style="background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 32px 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 24px">
<div style="flex: 1 1 420px; min-width: 0">
<p style="margin: 0 0 8px; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; color: {{accent}}">The paper</p>
<h2 id="paper-h" style="margin: 0 0 8px; font-family: 'Exo 2', sans-serif; font-size: 26px">The ideas behind it, on SSRN.</h2>
<p style="margin: 0; color: #C8D1DE; font-size: 15px">The whitepaper the design starts from.</p>
</div>
<a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=5930134" style="display: inline-block; padding: 12px 20px; border: 1px solid #34486A; color: #EEF2F7; text-decoration: none; border-radius: 4px">Read the paper →</a>
</div>
</section>
<footer style="border-top: 1px solid #22324D">
<div style="max-width: 1120px; margin: 0 auto; padding: 32px 24px; display: flex; flex-wrap: wrap; gap: 16px 32px; justify-content: space-between; font-size: 13px; color: #A3AEC2">
<span>© 2026 Robert Allan James · A Starship Foundation project (in formation)</span>
<span style="display: flex; flex-wrap: wrap; gap: 24px"><a href="Main.dc.html" style="color: #C8D1DE">Home</a><a href="Downloads.dc.html" style="color: #C8D1DE">Downloads</a><a href="Inquiries.dc.html" style="color: #C8D1DE">Inquiries</a><a href="https://starshipos.org" style="color: #C8D1DE">starshipos.org</a></span>
</div>
</footer>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"accent":{"editor":"color","default":"#F5891F","options":["#F5891F","#5FD3C4","#8FB8FF"]},"$preview":{"width":1280,"height":2400}}'>
class Component extends DCLogic {
renderVals() {
return { accent: this.props.accent ?? '#F5891F' };
}
}
</script>
</body>
</html>
+145
View File
@@ -0,0 +1,145 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>StarshipOS — Downloads</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&amp;family=Exo+2:wght@600;700&amp;display=swap" rel="stylesheet">
<style>
body{margin:0;background:#0A1222}
a{color:#F5891F}a:hover{color:#FFB061}
</style>
</helmet>
<div style="background: #0A1222; color: #EEF2F7; font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 16px; line-height: 1.6; min-height: 100vh">
<header style="border-bottom: 1px solid #22324D">
<nav aria-label="Main" style="max-width: 1120px; margin: 0 auto; padding: 20px 24px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px">
<a href="Main.dc.html" style="font-family: 'Exo 2', sans-serif; font-weight: 700; font-size: 22px; color: #EEF2F7; text-decoration: none; letter-spacing: 0.02em">starship<span style="color: {{accent}}">OS</span></a>
<div style="display: flex; flex-wrap: wrap; gap: 24px; font-size: 14px">
<a href="Main.dc.html" style="color: #C8D1DE; text-decoration: none">Home</a>
<a href="TryIt.dc.html" style="color: #C8D1DE; text-decoration: none">Try it</a>
<a href="Downloads.dc.html" aria-current="page" style="color: #EEF2F7; text-decoration: none; border-bottom: 2px solid {{accent}}">Downloads</a>
<a href="Documents.dc.html" style="color: #C8D1DE; text-decoration: none">Documents</a>
<a href="Inquiries.dc.html" style="color: #C8D1DE; text-decoration: none">Inquiries</a>
<a href="https://starshipos.org" style="color: {{accent}}; text-decoration: none">Contributors →</a>
</div>
</nav>
</header>
<section style="max-width: 1120px; margin: 0 auto; padding: 80px 24px 32px">
<p style="margin: 0 0 12px; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; color: #4FD1B0">ok&gt; downloads</p>
<h1 style="margin: 0 0 16px; font-family: 'Exo 2', sans-serif; font-size: clamp(36px, 5vw, 56px); line-height: 1.1">StarshipOS, on a machine of yours.</h1>
<p style="margin: 0 0 24px; max-width: 700px; font-size: 18px; color: #C8D1DE">One image per board, straight from the release. Write it to a USB stick or an SD card, boot, and the first boot makes you Zuse, the superuser: your identity lives on the stick.</p>
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 12px; font-size: 14px">
<span style="display: inline-block; padding: 4px 12px; border-radius: 4px; background: #111C30; border: 1px solid #34486A; color: #EEF2F7">[TAG]</span>
<span style="display: inline-block; padding: 4px 12px; border-radius: 4px; border: 1px solid {{accent}}; color: {{accent}}">alpha · pre-release</span>
<a href="https://gitea.starshipos-f18.org/rajames/LithosAnanke/releases" style="color: #C8D1DE">All releases →</a>
</div>
</section>
<section aria-label="What an alpha means" style="max-width: 1120px; margin: 0 auto; padding: 16px 24px 48px">
<div style="background: #111C30; border: 1px solid #34486A; border-left-width: 1px; border-radius: 6px; padding: 24px 28px; display: flex; flex-wrap: wrap; gap: 16px 40px">
<div style="flex: 1 1 300px; min-width: 0">
<h2 style="margin: 0 0 8px; font-family: 'Exo 2', sans-serif; font-size: 22px">An alpha, honestly.</h2>
<p style="margin: 0; color: #C8D1DE; font-size: 15px">Every release is booted in QEMU on amd64, aarch64 and riscv64, passes its 538 power-on checks on each, and its proofs, before it is published. On real hardware it has not been booted yet. If you try it, tell us what happened.</p>
</div>
<div style="flex: 0 1 260px; display: flex; flex-direction: column; justify-content: center; gap: 8px; font-size: 14px">
<div><span style="color: #4FD1B0">✓</span> <span style="color: #C8D1DE">QEMU, three architectures</span></div>
<div><span style="color: #A3AEC2">○</span> <span style="color: #C8D1DE">Real hardware: not yet</span></div>
<a href="Inquiries.dc.html" style="font-size: 14px">Report how it went →</a>
</div>
</div>
</section>
<section aria-labelledby="images-h" style="max-width: 1120px; margin: 0 auto; padding: 32px 24px 48px">
<p style="margin: 0 0 12px; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; color: {{accent}}">The images</p>
<h2 id="images-h" style="margin: 0 0 32px; font-family: 'Exo 2', sans-serif; font-size: 36px; line-height: 1.15">Pick your board.</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 24px">
<article style="background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 28px; display: flex; flex-direction: column; gap: 14px">
<div><h3 style="margin: 0 0 4px; font-family: 'Exo 2', sans-serif; font-size: 24px">PC</h3><p style="margin: 0; font-size: 13px; color: {{accent}}">any x86-64 PC that boots UEFI from USB</p></div>
<p style="margin: 0; color: #C8D1DE; font-size: 15px">A USB stick image. Its first boot makes your Zuse on the stick: no account, no password — the stick is your key.</p>
<div style="font-size: 13px; color: #A3AEC2">Write it (it replaces everything on the stick):</div>
<pre style="margin: 0; padding: 12px 14px; background: #0A1222; border: 1px solid #22324D; border-radius: 4px; font-size: 12px; color: #EEF2F7; white-space: pre-wrap">xz -dc pc-stick.img.xz | sudo dd of=/dev/sdX bs=4M conv=fsync status=progress</pre>
<p style="margin: 0; font-size: 13px; color: #A3AEC2">or balenaEtcher, which reads the .xz as it is.</p>
<a href="[RELEASE]/pc-stick.img.xz" style="display: inline-block; align-self: flex-start; padding: 12px 20px; background: {{accent}}; color: #0A1222; font-weight: 700; text-decoration: none; border-radius: 4px">pc-stick.img.xz</a>
</article>
<article style="background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 28px; display: flex; flex-direction: column; gap: 14px">
<div><h3 style="margin: 0 0 4px; font-family: 'Exo 2', sans-serif; font-size: 24px">Raspberry Pi 5</h3><p style="margin: 0; font-size: 13px; color: {{accent}}">an SD card</p></div>
<p style="margin: 0; color: #C8D1DE; font-size: 15px">Boots straight from the Pi's own firmware. Until first-boot Zuse reaches this board, it needs a Zuse identity drive made on a PC.</p>
<div style="font-size: 13px; color: #A3AEC2">Write it with Raspberry Pi Imager (Use custom), or:</div>
<pre style="margin: 0; padding: 12px 14px; background: #0A1222; border: 1px solid #22324D; border-radius: 4px; font-size: 12px; color: #EEF2F7; white-space: pre-wrap">xz -dc raspi5.img.xz | sudo dd of=/dev/sdX bs=4M conv=fsync status=progress</pre>
<a href="[RELEASE]/raspi5.img.xz" style="display: inline-block; align-self: flex-start; padding: 12px 20px; border: 1px solid #34486A; color: #EEF2F7; text-decoration: none; border-radius: 4px">raspi5.img.xz</a>
</article>
<article style="background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 28px; display: flex; flex-direction: column; gap: 14px">
<div><h3 style="margin: 0 0 4px; font-family: 'Exo 2', sans-serif; font-size: 24px">Milk-V Mars</h3><p style="margin: 0; font-size: 13px; color: {{accent}}">RISC-V, an SD card</p></div>
<p style="margin: 0; color: #C8D1DE; font-size: 15px">Found by the board's own U-Boot. Until first-boot Zuse reaches this board, it needs a Zuse identity drive made on a PC.</p>
<div style="font-size: 13px; color: #A3AEC2">Write it:</div>
<pre style="margin: 0; padding: 12px 14px; background: #0A1222; border: 1px solid #22324D; border-radius: 4px; font-size: 12px; color: #EEF2F7; white-space: pre-wrap">xz -dc MilkV.img.xz | sudo dd of=/dev/sdX bs=4M conv=fsync status=progress</pre>
<a href="[RELEASE]/MilkV.img.xz" style="display: inline-block; align-self: flex-start; padding: 12px 20px; border: 1px solid #34486A; color: #EEF2F7; text-decoration: none; border-radius: 4px">MilkV.img.xz</a>
</article>
<article style="background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 28px; display: flex; flex-direction: column; gap: 14px">
<div><h3 style="margin: 0 0 4px; font-family: 'Exo 2', sans-serif; font-size: 24px">QEMU</h3><p style="margin: 0; font-size: 13px; color: {{accent}}">for developers</p></div>
<p style="margin: 0; color: #C8D1DE; font-size: 15px">The ISO every release's amd64 acceptance boots, byte for byte. To boot it the way we do, see the developer guide in the repository.</p>
<a href="https://gitea.starshipos-f18.org/rajames/LithosAnanke" style="font-size: 14px">The repository →</a>
<a href="[RELEASE]/StarshipOS.iso" style="display: inline-block; align-self: flex-start; padding: 12px 20px; border: 1px solid #34486A; color: #EEF2F7; text-decoration: none; border-radius: 4px">StarshipOS.iso</a>
</article>
</div>
<p style="margin: 24px 0 0; font-size: 14px; color: #A3AEC2">Rather not write anything? <a href="TryIt.dc.html">Try it in your browser</a> — a real machine, at its prompt in seconds.</p>
</section>
<section aria-labelledby="verify-h" style="max-width: 1120px; margin: 0 auto; padding: 48px 24px">
<div style="background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 40px; display: flex; flex-wrap: wrap; gap: 40px">
<div style="flex: 999 1 460px; min-width: 0">
<p style="margin: 0 0 12px; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; color: {{accent}}">Verify</p>
<h2 id="verify-h" style="margin: 0 0 16px; font-family: 'Exo 2', sans-serif; font-size: 30px; line-height: 1.2">Know it's ours.</h2>
<p style="margin: 0 0 16px; color: #C8D1DE">Every file's SHA-256 is in the release's SHA256SUMS. Inside each image, every capsule the system loads is signed by StarshipOS Capsule Signing, issued by the StarshipOS Root CA, and checked at boot.</p>
<pre style="margin: 0; padding: 12px 14px; background: #0A1222; border: 1px solid #22324D; border-radius: 4px; font-size: 13px; color: #EEF2F7">sha256sum -c SHA256SUMS --ignore-missing</pre>
</div>
<div style="flex: 1 1 300px; min-width: 0; display: flex; flex-direction: column; gap: 12px">
<div style="font-size: 13px; color: #A3AEC2">StarshipOS Root CA, SHA-256</div>
<code style="display: block; padding: 12px 14px; background: #0A1222; border: 1px solid #22324D; border-radius: 4px; font-size: 12px; color: #EEF2F7; word-break: break-all">DA:A7:40:56:65:41:1A:FE:E6:08:85:3F:6E:AD:83:3A:38:30:83:E6:F0:6D:69:A6:A8:9E:92:BD:85:93:39:81</code>
<a href="[RELEASE]/SHA256SUMS" style="font-size: 14px">SHA256SUMS →</a>
<a href="[RELEASE]/starshipos-root-ca.pem" style="font-size: 14px">The Root CA's certificate →</a>
</div>
</div>
</section>
<section aria-labelledby="docs-h" style="max-width: 1120px; margin: 0 auto; padding: 48px 24px 96px">
<div style="background: #111C30; border: 1px solid #22324D; border-radius: 6px; padding: 32px 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 24px">
<div style="flex: 1 1 420px; min-width: 0">
<p style="margin: 0 0 8px; font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; color: {{accent}}">Read</p>
<h2 id="docs-h" style="margin: 0 0 8px; font-family: 'Exo 2', sans-serif; font-size: 26px">The documents of this release.</h2>
<p style="margin: 0; color: #C8D1DE; font-size: 15px">The Book of Fabric, the Dictionary of v4, the book and the formal set -- built from the same tree as these images.</p>
</div>
<a href="Documents.dc.html" style="display: inline-block; padding: 12px 20px; border: 1px solid #34486A; color: #EEF2F7; text-decoration: none; border-radius: 4px">Documents →</a>
</div>
</section>
<footer style="border-top: 1px solid #22324D">
<div style="max-width: 1120px; margin: 0 auto; padding: 32px 24px; display: flex; flex-wrap: wrap; gap: 16px 32px; justify-content: space-between; font-size: 13px; color: #A3AEC2">
<span>© 2026 Robert Allan James · A Starship Foundation project (in formation)</span>
<span style="display: flex; flex-wrap: wrap; gap: 24px"><a href="Main.dc.html" style="color: #C8D1DE">Home</a><a href="Inquiries.dc.html" style="color: #C8D1DE">Inquiries</a><a href="https://starshipos.org" style="color: #C8D1DE">starshipos.org</a></span>
</div>
</footer>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"accent":{"editor":"color","default":"#F5891F","options":["#F5891F","#5FD3C4","#8FB8FF"]},"$preview":{"width":1280,"height":2700}}'>
class Component extends DCLogic {
renderVals() {
return { accent: this.props.accent ?? '#F5891F' };
}
}
</script>
</body>
</html>
+6 -4
View File
@@ -23,6 +23,8 @@ a{color:#F5891F}a:hover{color:#FFB061}
<div style="display: flex; flex-wrap: wrap; gap: 24px; font-size: 14px">
<a href="Main.dc.html" style="color: #C8D1DE; text-decoration: none">Home</a>
<a href="TryIt.dc.html" style="color: #C8D1DE; text-decoration: none">Try it</a>
<a href="Downloads.dc.html" style="color: #C8D1DE; text-decoration: none">Downloads</a>
<a href="Documents.dc.html" style="color: #C8D1DE; text-decoration: none">Documents</a>
<a href="Inquiries.dc.html" aria-current="page" style="color: #EEF2F7; text-decoration: none; border-bottom: 2px solid {{accent}}">Inquiries</a>
<a href="https://starshipos.org" style="color: {{accent}}; text-decoration: none">Contributors →</a>
</div>
@@ -80,19 +82,19 @@ a{color:#F5891F}a:hover{color:#FFB061}
<h2 style="margin: 0; font-family: 'Exo 2', sans-serif; font-size: 24px">Or write directly</h2>
<div style="border-top: 1px solid #22324D; padding-top: 16px">
<div style="font-size: 13px; color: #A3AEC2">General</div>
<a href="mailto:info@starshipos.com" style="font-size: 16px">info@starshipos.com</a>
<a href="mailto:rajames440@gmail.com" style="font-size: 16px">rajames440@gmail.com</a>
</div>
<div style="border-top: 1px solid #22324D; padding-top: 16px">
<div style="font-size: 13px; color: #A3AEC2">Licensing &amp; partnerships</div>
<a href="mailto:licensing@starshipos.com" style="font-size: 16px">licensing@starshipos.com</a>
<a href="mailto:rajames440@gmail.com" style="font-size: 16px">rajames440@gmail.com</a>
</div>
<div style="border-top: 1px solid #22324D; padding-top: 16px">
<div style="font-size: 13px; color: #A3AEC2">Press &amp; media</div>
<a href="mailto:press@starshipos.com" style="font-size: 16px">press@starshipos.com</a>
<a href="mailto:rajames440@gmail.com" style="font-size: 16px">rajames440@gmail.com</a>
</div>
<div style="border-top: 1px solid #22324D; padding-top: 16px">
<div style="font-size: 13px; color: #A3AEC2">Security reports</div>
<a href="mailto:security@starshipos.org" style="font-size: 16px">security@starshipos.org</a>
<a href="mailto:rajames440@gmail.com" style="font-size: 16px">rajames440@gmail.com</a>
</div>
<p style="margin: 0; padding-top: 16px; border-top: 1px solid #22324D; font-size: 13px; color: #A3AEC2">StarshipOS is developed in Ashland, Ohio.</p>
</aside>
+3 -1
View File
@@ -25,6 +25,8 @@ a{color:#F5891F}a:hover{color:#FFB061}
<a href="#research" style="color: #C8D1DE; text-decoration: none">Research</a>
<a href="#silicon" style="color: #C8D1DE; text-decoration: none">Silicon</a>
<a href="TryIt.dc.html" style="color: #C8D1DE; text-decoration: none">Try it</a>
<a href="Downloads.dc.html" style="color: #C8D1DE; text-decoration: none">Downloads</a>
<a href="Documents.dc.html" style="color: #C8D1DE; text-decoration: none">Documents</a>
<a href="Inquiries.dc.html" style="color: #C8D1DE; text-decoration: none">Inquiries</a>
<a href="https://starshipos.org" style="color: {{accent}}; text-decoration: none">Contributors →</a>
</div>
@@ -135,7 +137,7 @@ a{color:#F5891F}a:hover{color:#FFB061}
<footer style="border-top: 1px solid #22324D">
<div style="max-width: 1120px; margin: 0 auto; padding: 32px 24px; display: flex; flex-wrap: wrap; gap: 16px 32px; justify-content: space-between; font-size: 13px; color: #A3AEC2">
<span>© 2026 Robert Allan James · A Starship Foundation project (in formation)</span>
<span style="display: flex; flex-wrap: wrap; gap: 24px"><a href="Inquiries.dc.html" style="color: #C8D1DE">Inquiries</a><a href="mailto:info@starshipos.com" style="color: #C8D1DE">info@starshipos.com</a><a href="https://starshipos.org" style="color: #C8D1DE">starshipos.org</a></span>
<span style="display: flex; flex-wrap: wrap; gap: 24px"><a href="Inquiries.dc.html" style="color: #C8D1DE">Inquiries</a><a href="mailto:rajames440@gmail.com" style="color: #C8D1DE">rajames440@gmail.com</a><a href="https://starshipos.org" style="color: #C8D1DE">starshipos.org</a></span>
</div>
</footer>
+2
View File
@@ -23,6 +23,8 @@ a{color:#F5891F}a:hover{color:#FFB061}
<div style="display: flex; flex-wrap: wrap; gap: 24px; font-size: 14px">
<a href="Main.dc.html" style="color: #C8D1DE; text-decoration: none">Home</a>
<a href="TryIt.dc.html" aria-current="page" style="color: #EEF2F7; text-decoration: none; border-bottom: 2px solid {{accent}}">Try it</a>
<a href="Downloads.dc.html" style="color: #C8D1DE; text-decoration: none">Downloads</a>
<a href="Documents.dc.html" style="color: #C8D1DE; text-decoration: none">Documents</a>
<a href="Inquiries.dc.html" style="color: #C8D1DE; text-decoration: none">Inquiries</a>
<a href="https://starshipos.org" style="color: {{accent}}; text-decoration: none">Contributors →</a>
</div>
+8 -1
View File
@@ -39,10 +39,17 @@ attach Main badge.jpg
attach Main captain-bob.jpg
page Inquiries "Inquiries" Inquiries.WebHome.xwiki
page TryIt "Try StarshipOS" TryIt.WebHome.xwiki
# Downloads and Documents exist once build.py has been given a release (#126, #157)
pages="/bin/view/Main/ /bin/view/Inquiries/ /bin/view/TryIt/"
if [ -f Downloads.WebHome.xwiki ] && [ -f Documents.WebHome.xwiki ]; then
page Downloads "Downloads" Downloads.WebHome.xwiki
page Documents "Documents" Documents.WebHome.xwiki
pages="$pages /bin/view/Downloads/ /bin/view/Documents/"
fi
echo "=== as a guest"
bad=
for u in /bin/view/Main/ /bin/view/Inquiries/ /bin/view/TryIt/ \
for u in $pages \
/bin/download/Main/WebHome/badge.jpg /bin/download/Main/WebHome/captain-bob.jpg; do
code=$(curl -s -o /dev/null -w '%{http_code}' "$base$u")
if [ "$code" = 200 ]; then echo " $u: 200"; else echo " $u: $code -- NOT readable"; bad=1; fi
+1
View File
@@ -15,6 +15,7 @@ Needs the programming right.
#if (!$c.get('announcements'))#set ($d = $c.addBooleanField('announcements', 'Wants announcements', 'yesno'))#set ($changed = true)#end
#if (!$c.get('source'))#set ($d = $c.addTextField('source', 'Source', 60))#set ($changed = true)#end
#if (!$c.get('status'))#set ($d = $c.addStaticListField('status', 'Status', 1, false, 'new|read|answered|closed'))#set ($changed = true)#end
#if (!$c.get('mailed'))#set ($d = $c.addBooleanField('mailed', 'E-mailed to Captain Bob', 'yesno'))#set ($changed = true)#end
#if ($changed)
#set ($d = $xdoc.setTitle('Inquiry class'))
#set ($d = $xwiki.getXWiki().saveDocument($xdoc, 'The inquiry fields', $xcontext.getContext()))
+1
View File
@@ -12,6 +12,7 @@ This page holds no inquiry.
|=Organization|$doc.display('organization', $o)
|=Topic|$doc.display('topic', $o)
|=Announcements|$doc.display('announcements', $o)
|=E-mailed|$doc.display('mailed', $o)
|=Source|$doc.display('source', $o)
== Message ==
@@ -4,6 +4,11 @@
## rights (getDocumentAsAuthor, saveAsAuthor): a guest can reach nothing but this. Nothing a visitor
## sends is ever written into this page's output or an entry's title: with this author's rights,
## text that reached the wiki markup could run a macro.
## Each inquiry filed is also e-mailed to Captain Bob (#161; ruled 2026-11-10: "server emails each
## one"), plain text, through the wiki's mail service (Administration > Email > Mail Sending: the
## SMTP account is set there, not here). Reply-To is the visitor's address, which has passed the
## address check above; the subject is fixed but for the topic, its line breaks removed. If the
## mail is not sent the inquiry is filed all the same, and its entry says so (its E-mailed field: no).
#macro (field $n $max)#set ($v = "$!request.getParameter($n)")#set ($v = $v.trim())#if ($v.length() > $max)#set ($v = $v.substring(0, $max))#end#end
#if ($request.method != 'POST')
This page receives the inquiry form on [[starshipos-f18.com>>https://starshipos-f18.com/bin/view/Inquiries/]].
@@ -35,6 +40,33 @@ This page receives the inquiry form on [[starshipos-f18.com>>https://starshipos-
#set ($d = $o.set('source', 'starshipos-f18.com'))
#set ($d = $o.set('status', 'new'))
#set ($d = $entry.saveAsAuthor('An inquiry from the .com form'))
## the e-mail: every visitor-given value is plain text in a text/plain part, never markup
#set ($subjectTopic = $topic.replaceAll('[\r\n]+', ' '))
#set ($body = "A new inquiry from starshipos-f18.com, filed as ${ref}:
${xwiki.getURL($ref, 'view')}
Name: $name
E-mail: $email
Organization: $org
Topic: $topic
Announcements: #if ($wants)yes#{else}no#end
$message
")
#set ($mail = $services.mail.sender.createMessage('rajames440@gmail.com', "Inquiry from starshipos-f18.com: $subjectTopic"))
#if ($mail)
#set ($d = $mail.addPart('text/plain', $body))
#set ($d = $mail.setHeader('Reply-To', $email))
#set ($sent = $services.mail.sender.send($mail))
#set ($ok = $sent && !$services.mail.sender.lastError)
#if ($ok)#set ($ok = $sent.statusResult.waitTillProcessed(30000))#set ($ok = !$sent.statusResult.allErrors.hasNext())#end
#else
#set ($ok = false)
#end
#set ($e2 = $xwiki.getDocumentAsAuthor($ref))
#set ($o2 = $e2.getObject('Inquiries.Code.InquiryClass'))
#if ($o2)#if ($ok)#set ($d = $o2.set('mailed', 1))#else#set ($d = $o2.set('mailed', 0))#end#set ($d = $e2.saveAsAuthor('Whether the inquiry was e-mailed'))#end
{{html}}<p>Thank you. Your inquiry has been received; every message is read by a human, and replies usually come within a few days.</p><p><a href="https://starshipos-f18.com/">Back to starshipos-f18.com</a></p>{{/html}}
#end
#end
+173
View File
@@ -0,0 +1,173 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: LicenseRef-Starship-1.0
# Copyright (c) 2023-2026 Robert A. James
"""mkdictionary.py -- the dictionary of v4 (#157): every word the system boots with, alphabetical,
with its stack notation, a description, and where it is defined.
v4/tools/mkdictionary.py words the live word list, one per line
v4/tools/mkdictionary.py draft TSV a table for every live word not in TSV yet, appended,
its stack and description taken from the source where
the source has them (to be read and finished by hand)
v4/tools/mkdictionary.py check TSV every live word has an entry with a stack and a
description, and every entry is a live word: 0, or 1
with what is wrong
v4/tools/mkdictionary.py md TSV OUT.md the dictionary as Markdown, alphabetical, for the book
The live word list is the system's own: the hosted v4 (`make -C v4 hosted`) booted and asked
`WORDS`, plus the kernel's words of the lone node (`sk_v4_words[]` in kernel/src/v4/sk_v4.c) --
the words a booted StarshipOS has. The table (docs/reference/v4-dictionary.tsv) holds the stack
notation and the description, which only a person can write; the names are never typed by hand.
"""
import os, re, subprocess, sys
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
HOSTED = os.path.join(ROOT, "v4", "build", "starforth4-amd64")
SOURCES = ([os.path.join("capsules/v4/sys", f) for f in sorted(os.listdir(os.path.join(ROOT, "capsules/v4/sys")))
if f.endswith(".f18")] +
[os.path.join("capsules/v4", f) for f in sorted(os.listdir(os.path.join(ROOT, "capsules/v4")))
if f.endswith(".4th")])
KERNEL_WORDS_SRC = "kernel/src/v4/sk_v4.c"
def live_words():
"""The hosted system's WORDS, then the kernel's own words not among them."""
if not os.path.exists(HOSTED):
sys.exit(f"mkdictionary: {HOSTED} is missing: make -C v4 hosted")
out = subprocess.run([HOSTED], input="WORDS\n", capture_output=True, text=True, timeout=120).stdout
lines = out.split("\n")
try:
start = next(k for k, l in enumerate(lines) if l.startswith("ok> ") and l[4:].strip())
except StopIteration:
sys.exit("mkdictionary: the hosted system printed no WORDS")
got = []
for l in lines[start:]:
if l.startswith("ok> ") and got:
break
got.append(l[4:] if l.startswith("ok> ") else l)
text = " ".join(got)
if " ok" in text:
text = text[:text.rfind(" ok")]
words = text.split()
src = open(os.path.join(ROOT, KERNEL_WORDS_SRC)).read()
m = re.search(r"sk_v4_words\[\]\s*=\s*\{([^}]*)\}", src)
for k in re.findall(r'"([^"]+)"', m.group(1) if m else ""):
if k not in words:
words.append(k)
return words
def definitions():
"""Where each name is defined, and what its source says of it: {name: (file, stack, text)}."""
found = {}
for rel in SOURCES:
lines = open(os.path.join(ROOT, rel), errors="replace").read().split("\n")
for i, l in enumerate(lines):
names = []
if rel.endswith(".f18"):
names = re.findall(r'",\s+(\S+)\s+#,', l)
else:
names = re.findall(r"(?:^|\s):\s+(\S+)", l)
names += re.findall(r"\b(?:CONSTANT|VARIABLE|DEFER|CREATE|VOCABULARY)\s+(\S+)", l)
for n in names:
if n in found:
continue
stack, text = "", ""
inline = re.search(r":\s+" + re.escape(n) + r"\s+(\([^)]*\))", l)
if inline:
stack = inline.group(1)
# the comment block just above
j, block = i - 1, []
while j >= 0 and lines[j].startswith("\\"):
block.insert(0, lines[j].lstrip("\\").strip())
j -= 1
comment = " ".join(b for b in block if b)
cm = re.match(r"(\([^)]*--[^)]*\))\s*(.*)", comment)
if cm and not stack:
stack, comment = cm.group(1), cm.group(2)
text = comment
found[n] = (rel, stack, text)
for k in re.findall(r'"([^"]+)"', re.search(r"sk_v4_words\[\]\s*=\s*\{([^}]*)\}",
open(os.path.join(ROOT, KERNEL_WORDS_SRC)).read()).group(1)):
found.setdefault(k, (KERNEL_WORDS_SRC, "", ""))
return found
def read_table(path):
table = {}
if os.path.exists(path):
for l in open(path).read().split("\n"):
# a comment is a line beginning with # that has no tab: a row always has tabs, and
# some words begin with # (#, #>, #S)
if not l.strip() or (l.startswith("#") and "\t" not in l):
continue
f = l.split("\t")
while len(f) < 3:
f.append("")
table[f[0]] = (f[1].strip(), f[2].strip())
return table
def main():
if len(sys.argv) < 2:
sys.exit(__doc__)
mode = sys.argv[1]
if mode == "words":
print("\n".join(live_words()))
return
path = sys.argv[2]
table = read_table(path)
words = live_words()
if mode == "draft":
defs = definitions()
new = [w for w in words if w not in table]
with open(path, "a") as f:
if not table:
f.write("# docs/reference/v4-dictionary.tsv -- the dictionary of v4 (#157): name, stack notation,\n"
"# description, tab-separated. The names are the live system's (v4/tools/mkdictionary.py\n"
"# check refuses a missing or a stale one). A row whose description starts TODO is a draft.\n")
for w in sorted(new):
rel, stack, text = defs.get(w, ("", "", ""))
desc = ("TODO " + text) if text else "TODO"
f.write(f"{w}\t{stack}\t{desc}\n")
print(f"mkdictionary: {len(words)} live words; {len(new)} drafted into {path}")
return
if mode == "check":
bad = []
for w in words:
if w not in table:
bad.append(f"no entry: {w}")
elif not table[w][0] or not table[w][1] or table[w][1].startswith("TODO"):
bad.append(f"unfinished: {w}")
for w in table:
if w not in words:
bad.append(f"not a live word: {w}")
if bad:
print("\n".join(bad))
print(f"mkdictionary: {len(bad)} problem(s) in {path} ({len(words)} live words)")
sys.exit(1)
print(f"mkdictionary: {path} covers all {len(words)} live words")
return
if mode == "md":
defs = definitions()
out = sys.argv[3]
with open(out, "w") as f:
f.write("# The dictionary of v4\n\n")
f.write(f"Every word StarForth v4 boots with -- {len(words)} of them -- in alphabetical order: its "
"stack notation (what it takes and leaves: `( before -- after )`), what it does, and "
"where it is defined.\n\n")
f.write("| Word | Stack | Description | Defined in |\n|---|---|---|---|\n")
for w in sorted(words, key=lambda x: (x.upper(), x)):
stack, desc = table.get(w, ("", ""))
rel = defs.get(w, ("", "", ""))[0]
cell = lambda x: x.replace("|", "\\|")
# a token with a quote in it is FORTH (." text" S" ABORT"): set as code, so its
# quotes print straight, not as typographic ones
desc = re.sub(r'(\S*"\S*)', lambda mm: "`" + mm.group(1) + "`", desc)
f.write(f"| `{cell(w)}` | `{cell(stack)}` | {cell(desc)} | `{rel}` |\n")
print(f"mkdictionary: {out}, {len(words)} words")
return
sys.exit(__doc__)
if __name__ == "__main__":
main()