Thanks for looking. TOS is a Terminal Operating System for the OpenComputers Minecraft mod, written in Lua 5.3. Every byte it loads comes out of the machine's memory, and TOS already needs 1 MB to start (see the README's requirements) — that constraint shapes most of the rules below.
Work on dev. Never commit to main.
| Branch | What it is | Edit it? |
|---|---|---|
main |
The release build — what installers download. Comments stripped, dev tests and build tooling removed, blank-line runs collapsed. | No. Generated. |
dev |
The source tree. Full --! security/invariant comments, usr/lib/tests/, build/, and the add-on source in TOS-Extras/. One clone, suite green. |
Yes — all work happens here. |
optional-utilities |
The add-on pack, laid out as a pkg repository so a machine with an internet card installs from it directly. |
No. Generated from TOS-Extras/ on dev. |
master |
A stub carrying only an OPPM index, so oppm can find TOS. OPPM hardcodes the branch name. |
No. Two files. |
main is produced from dev by build/strip.lua. A commit to main is not "a fix that skipped review" — it is a change that the next release build silently overwrites. If you have already done it, cherry-pick onto dev and open the PR there.
The split exists because comments cost real memory on a machine where every kilobyte counts, and the BIOS is fighting a hard 4 KiB EEPROM budget — roughly a third of bios.lua is comments that must not ship, and must not be lost either. strip.lua keeps every --!-marked comment (security notes, cross-file invariants, license headers) and drops the rest.
Installing from either branch works, since both carry the same tree shape and the same tos/system_manifest.lua:
bootstrap.lua # main (release)
bootstrap.lua Evan450/TOS-Terminal-Operating-System- dev # dev (source)
ROADMAP.md is the open queue, grouped by status. Two kinds of entry are especially good to pick up:
- Open bugs — known broken, and the entry usually says what was already ruled out.
- Emulator checklist items — these need a real OpenComputers install to verify. The off-box suite runs on stock Lua, so it structurally cannot see that class of bug. If you play the mod, this is where you can do something the maintainer's test suite cannot.
Entries marked idea / far future are not commitments — discuss before building one.
The roadmap also records work deliberately not done, with reasons. Reading the relevant entry before proposing a change will usually tell you whether it has already been considered and rejected, and why.
You need a lua interpreter (5.3 or 5.4) and Python 3 for the test runner. The Python build tests (build/test_*.py) need pytest — pip install pytest. Without it those fail; everything else runs.
run_tests.py is the runner to use. run_tests.sh beside it is a serial fallback for a machine with no Python: it runs the same Lua tests and skips the Python ones.
The individual tools live in different directories and different runtimes. tos.py finds them, runs them from the right place, and prints exactly what it ran — so you can learn the underlying commands, and reproduce a failure without it:
python tos.py test [--serial] the whole suite
python tos.py build strip the source into ../TOS-Release
python tos.py pack [--sign] build the add-on disks + repo index
python tos.py sign <dir>|--all sign package manifests in place
python tos.py key print the public key you sign as
python tos.py check fast drift checks, without the full suite
python tos.py selftest build, then run the boot battery on a real machine
python tos.py roadmap regenerate the queue and ROADMAP.md
Every path resolves from the script, not from your shell, so it works from any directory. check is the one worth knowing: it runs only the checks that catch a generated file drifting from its source — release digests, manifest completeness, release excludes, the add-on repo index — which is the mistake this project makes most often.
roadmap is maintainer-only and says so; the notes it reads are not published.
git clone --branch dev https://github.com/Evan450/TOS-Terminal-Operating-System-.git
cd TOS-Terminal-Operating-System-
python run_tests.pyThe suite is a couple of hundred files of pure Lua plus a few Python build tests. It touches no GPU and needs no Minecraft — everything runs off-box against fakes. It should be green before you start and green when you finish.
One clone gives you everything: the OS in tos/, and the add-on source in TOS-Extras/. FAIL=0 is the contract, not any particular pass count — the runner prints its own totals, and a number written down here only tells you how long ago someone edited this file. If it is red before you have changed anything, that is a bug in the tree and worth reporting on its own.
The suite runs against fakes. Two tools boot TOS on a real OpenComputers machine instead: Ocelot Brain, the machine inside Ocelot Desktop's jar, with no window.
python tos.py selftestbuilds, boots a fresh machine and runs the boot self-test battery. Exit 0 means every check passed.python build/headless-session.py SCRIPTtypes at the keyboard and reads the screen back. A script is lines likefirstboot,type ls\n,wait 30 root@andsnap after-ls;--helplists them all.
Both need Ocelot Desktop's jar and a JDK (version 9 or later).
To try your change in the game instead, install it over the network onto a bare OpenOS machine with an internet card, pointing the bootstrap at your branch:
bootstrap.lua <your-fork> dev
Windows note. Clone somewhere short, like
C:\src\tos. Some package paths run to ~255 characters, and Windows' 260-characterMAX_PATHwill make the disk builder fail on a write with a path that looks fine.
| Path | What is there |
|---|---|
bios.lua, init.lua |
The BIOS (4 KiB EEPROM) and the boot loader that builds require |
install.lua, bootstrap.lua |
The installer, and the network bootstrap that fetches a release and hands off to it |
tos/kernel/ |
The kernel: users, securefs, the scheduler, packages, crypto, display. docs/API.md lists every module and public function |
tos/kernel/net/ |
Networking: trust, the mesh, file transfer, remote execution |
tos/shell/ |
The two shells: the panels interface (panels/) and the command line (cli.lua), plus login, the tour and the package picker |
tos/shell/panels/commands/ |
The commands, in three groups: core, admin, extras |
tos/compat/ |
The OpenOS library shims |
tos/peripheral/ |
Redstone, robots, inventories |
tos/system_manifest.lua |
Every file a release installs: the list deploy, verify and the installer read |
etc/rc.d/, usr/bin/, usr/man/, usr/lang/ |
Startup services, user programs, man pages, language catalogues |
usr/lib/tests/ |
The test suite (not shipped) |
build/ |
Release tooling, generators and the headless machine (not shipped) |
TOS-Extras/ |
The add-ons, and the tools that build their disks |
Every source file starts with a comment saying what it is for.
Add-ons live under TOS-Extras/, install through pkg, and run in the capability sandbox. A package is a directory with a package.lua manifest and the files it installs:
TOS-Extras/modules/mything/
package.lua the manifest: name, version, kind, files, capabilities
(and lua = "5.4" / tos = ">=1.5.0" if it needs them: MANUAL 7.3)
init.lua your code
test_mything.lua picked up automatically by run_tests.py
Build the pick-and-choose disks, which also computes each file's SHA-256 into the manifest:
cd TOS-Extras
lua build/build-disk.lua
lua build/test_manifests.lua # manifest lint: capability and shape errorstest_manifests.lua catches the mistakes that are otherwise silent: commands declared as an array instead of a name→path map (which pkg.commands drops without a word), sandboxed code using crypto or vault without declaring the capability, declaring a capability the sandbox will not grant, a kind = "service" package with no /etc/rc.d/<name>.lua, and a manifest pkg cannot read.
Three rules the tooling enforces rather than trusts:
-
A manifest is data, not code.
pkgnever runspackage.lua: it decodes it with the kernel's data-only reader, so a manifest may hold literals and nothing else. No..to split a long description, no variables, no function calls. Lua itself runs such a file without complaint, which is whybuild-disk.luachecks with the kernel's reader too and refuses the manifest, naming the line. -
Below 1.0.0 does not ship.
build-disk.lua'sSKIPtable holds pre-1.0 packages off the public pack, and the tests fail if one rejoins. An unfinished add-on that installs cleanly is worse than one nobody can reach. -
Hashes are not optional.
pkg.installrefuses a package whose manifest does not declare a SHA-256 for every file, unless the operator explicitly passes--allow-unverified. The builder writes them; you should never have to.
pkg verifies Ed25519 publisher signatures, and an operator can refuse anything unsigned. Most contributors never sign anything: the maintainer signs the Optional Utilities pack. If you publish packages of your own, read docs/SIGNING.md first. Your passphrase is your private key, and losing it means starting again with a new identity.
- Read before you edit. Grep for every caller of a function you are changing. Kernel modules are wired together in
tos/kernel/init.lua, and the panels shell dispatches through a command registry — changing a signature in one place usually means several. - Add a test.
usr/lib/tests/test_*.luais auto-discovered byrun_tests.py. Follow the existing shape: atest(name, cond)helper, printsPASS/FAIL, returns false if anything failed. If the thing you fixed had no coverage, a regression test for it is the most valuable part of the PR. - Run the suite.
python run_tests.py. Report the result in the PR — do not claim a fix works if you have not run it. If the change is about what a real machine does (boot, drawing, the keyboard, rebooting), also runpython tos.py selftestor a headless session. - Keep the manifest honest. If you add a runtime file under
/tos,/etc/rc.d,/usr/binor/usr/modules, add it totos/system_manifest.luaas well.test_manifest_completeness.luaenforces this — a file missing from the manifest is silently absent from every fresh install and invisible toverify. - Update the docs in the same commit. Version bumps touch
README.md,CHANGELOG.md, and any version constant together. Documentation that contradicts the code is worse than none. If you add or rename a heading inMANUAL.md, runpython build/make_manual_toc.py; the suite fails while its contents list is stale. InCHANGELOG.md, a change goes under## Unreleased, in the newest round (a###section); a new round also gets a line in the list at the top ofUnreleased. Releases before v1.5.0 live indocs/CHANGELOG-ARCHIVE.md. - Regenerate the API reference with
python build/make_apiref.pywhen you add or change a public kernel function (function mod.name(at the top level of a file intos/kernel/) or the---comment above it. It rewritesdocs/API.md, and the suite fails while that is stale.- A function that refuses callers below ADMIN (one that calls an admin gate such as
adminGate) carries--- @tier adminin that comment.build/test_apiref.pyfails on a gate without the mark, or a mark without the gate.
- A function that refuses callers below ADMIN (one that calls an admin gate such as
The comment markers are load-bearing — strip.lua reads them:
| Marker | Meaning | Survives into main? |
|---|---|---|
--! |
Security note, cross-file invariant, license header | Yes |
-- |
Ordinary explanation, rationale, dev notes | No |
Use --! for anything a future reader must not lose: why a check exists, what attack it stops, what invariant another file depends on. Use plain -- for everything else. Block comments follow the same rule (--[[! keeps, --[[ drops).
Mark security-relevant code with a #SEC tag and the finding ID where one exists, matching the existing style.
- Memory is the budget. Prefer iteration over building intermediate tables. A response held as a Lua string is real RAM. Reading a file with
*awhen you could stream it in 4 KB chunks is how an install OOMs. - Yield in long loops. OpenComputers gives the whole machine one CPU shared across every seat, and a loop that never yields triggers the "too long without yielding" watchdog. Use
proc.yieldCooperative(). - Never truncate a file you might fail to finish writing. Write to
path.tmp, verify, then rename. There are atomic helpers (fs.writeFileAtomic) for the state files that must survive a power cut. - Lua 5.3 syntax is required — kernel modules use bitwise operators and
string.pack. Do not add anything that needs 5.4-only features. - No spaces in file or directory names.
Two things that break on GitHub and are easy to do by accident:
- Angle brackets outside backticks disappear.
<topic>in prose is parsed as an HTML tag and silently deleted. Always write`<topic>`. - A wrapped line starting with
-,+or*becomes a bullet. Sentences that wrap onto a line beginning with one of those turn into a spurious list item mid-paragraph. Rewrap, or reword.
Keep them scoped to one thing. In the description, say what changed, why, and what you ran to verify it. If a change is a design decision rather than a fix, say what you considered and rejected — that is the part review actually needs.
Security findings that should not be public first: say so in the PR description without the details, or open an issue asking for a private channel.
TOS is GPL v3.0. Contributions are accepted under the same license, and existing license headers must be preserved.