TempleOS Desktop Docs

Review of the HolyC-native apps plan

Edited 2026-10-27docs/plan-review.md

Date 2026-10-27. Read-only review of docs/holyc-native-apps-plan.md (the plan), docs/core-apps-plan.md, docs/os-foundation-plan.md, the two research reports, desk3/01-core.HC to desk3/05-run.HC, spike/KsSpike.HC and scripts/. Nothing was built or run. Line numbers are of the files as of today.

Evidence labels: [V] verified by reading a local file or a primary source fetched today (URL given), [D] reported by documentation or a project README fetched today, [U] unverified (reasoning, memory or arithmetic; treat as a hypothesis). Every speed estimate is [U] unless it cites a measurement.

0. Verdict#

The plan is a good list of apps and an honest list of gaps, but it is sequenced as an application project when the real risks are in the platform underneath it. Four things are wrong or missing at the root:

  1. It solves persistence and file exchange with a bridge that does not need to exist. TempleOS installs itself to a hard drive (scripts/boot-vm.sh:17 answers "n" to "Install onto hard drive?" [V]). Attach a disk and say yes: notes, settings, bookmarks and the whole dev loop survive reboots, with no channel, no daemon and no protocol. The channel is still wanted for fetch and host services, but it drops from "gate 1, everything depends on it" to "one service among several".
  2. It builds the app framework and widget kit before fixing the renderer's cost structure, and the renderer is where every later gate ("render legibly at 2x", editors, a browser page) will hit the wall. The measured 23 to 25 ms per dragged window at 2880x1800 is not the emulator's floor; it is the sum of four fixable things inside 01-core.HC (a bytewise MemCpy, a per-pixel shadow, per-pixel function calls, x87 maths in pixel loops). The cheapest single win in the whole project is replacing TempleOS's MemCpy, which is REP_MOVSB [V], with an 8 or 16 byte per iteration copy: under TCG every rep movsb iteration is one byte.
  3. It writes "native" apps against a shell that is a demo, not a framework: fixed NA=9 windows, one window per app, if (a==...) chains in three places, a 500-entry hit list that silently drops entries, no clip stack, no key-up or modifier handling, Esc reboots the desktop, and every dirty rectangle re-executes every app's draw code. The plan knows some of this ("App framework", "Widget kit v2") but underestimates it as M and does not list the event model, clipping, Unicode, threading or memory items at all.
  4. It does not say what "a complete operating system that runs and looks like a modern OS" means for TempleOS V5.03, which cannot display this desktop on real hardware at all (the mode set uses QEMU's Bochs VBE ports 0x1CE/0x1CF, desk3/04-main.HC:9-13, spike/KsSpike.HC:9-13 [V]; V5.03 itself is 640x480 16-colour VGA). If the Temple family stays the base, the base should be ZealOS (variable-resolution 32-bit VBE, AHCI, a networking stack in development, UEFI via Limine, public domain [D]), not V5.03.

Recommendation on the base: keep the HolyC desktop as the showpiece and personality, do the renderer pass and the five native apps that TempleOS does well (editor, code editor with Run, terminal, notes, reader-mode browser over a host fetch service), and treat the "complete modern OS" goal as the Linux-plus-own-Wayland-shell track already written up in os-foundation-plan.md, with the HolyC desktop running inside it as one window. The conditions that would change that are in section 3.12.

1. Critique of the plan#

1.1 Wrong or shaky assumptions#

# Plan statement What the evidence says Consequence
A1 "Persistence: host file service over the channel" (plan line 23); README: "the live CD has no writable disk" The ISO offers an install to disk at boot and the script declines it (scripts/boot-vm.sh:17 [V]). TempleOS has an ATA driver and the RedSea file system; an installed system keeps C:/D: across reboots. Persistence is a QEMU flag (-drive file=temple.qcow2,if=ide) and a "y", not a channel gate. A host-side RedSea reader (the format is flat: a free bitmap and 64-byte directory entries, Kernel/BlkDev/FileSysRedSea.HC [U, from memory of the source]) also gives file exchange both ways without a protocol.
A2 "the lockstep serial loader moves about 1.4 to 2.7 KB/s" is treated as a property of serial The loader acks every 32 bytes over a TCP round trip and sleeps 3 to 4 s per file (scripts/serial-send.py:13-18, scripts/send-to-vm.sh:27 [V]). It is a protocol artifact. The 50 KB/s gate is probably easy; it is still the wrong channel for anything but control text (section 3.2).
A3 Channel option (b) "a second IDE disk used as a mailbox ... verify raw block access" Right instinct, under-weighted. TempleOS already drives ATA (Kernel/BlkDev/ listing [V]: it exists; raw BlkRead/BlkWrite on a mounted drive [U]). Both QEMU and a host writer go through the same macOS page cache, so host writes are visible to the next guest read without special cache modes [U]. This is the best zero-driver bulk channel on this QEMU: a few MB/s with PIO, no shared-memory unknowns. Put it ahead of serial streaming for bulk data.
A4 "TempleOS has none of it [networking]" True for V5.03. But Shrine has a native TCP/IP stack with a PCnet-PCI II driver, Wget and a package manager (experimental) [D]; ZealOS lists "network card drivers and a networking stack" as in development [D]. All public domain. The "native network stack" track (plan step 8) should start from that code, not from zero. TLS remains nowhere.
A5 "the mouse, keyboard and clipboard paths are still hacks ... no key-up, no non-ASCII" The code masks the scan code to 8 bits (04-main.HC:267), discarding the flag bits TempleOS puts above bit 8 (SCF_SHIFT, SCF_CTRL, SCF_ALT; key-up is SCF_KEY_UP, and kbd.down_bitmap holds held keys) [U, from memory of Kernel/KeyDev.HC and KernelA.HH; verify in the VM]. Modifiers and key-up are probably available today without a kernel change; add them to the framework spike. Non-ASCII stays a real gap (8-bit kernel, Txt handles 32 to 126 only, 01-core.HC:399,420 [V]).
A6 "Paint: already largely exists" (browser table) Paint exists for ASCII text in four sizes of one face at two scales, with no clipping other than the dirty rectangle, no scrolling and no images. Paint for a browser is M to L, not "exists": clip stack, scroll offset, image blit with scaling, underline, selection highlight, many fonts.
A7 Browser B1 "3 to 5 weeks ... Wikipedia, MDN, a blog, HN, a GitHub README lay out without overlap" MDN and GitHub use CSS grid and flexbox throughout, web fonts, SVG icons and Unicode; Wikipedia needs Unicode and tables. The plan's CSS list has "simple flexbox" only and the font plan is ASCII. B1 is 2 to 3 times the estimate for one person, and the 20-page gate will drive weeks of CSS. Keep the gate but choose pages by what the engine will support (HN, plain blogs, docs sites, our own docs) and make MDN/GitHub stretch goals.
A8 "JavaScript: later a small ES5-class interpreter" Even ES5 plus a DOM would not render modern sites; the plan says so. Say "no JavaScript, ever, in the guest" and keep the "display list from a host browser" as the only escape. It avoids an XL item nobody will finish.
A9 "A full-screen copy is about 7 ms" is read as the bus limit MemCpy is REP_MOVSB (Kernel/KUtils.HC [V], fetched today); QEMU's TCG translates a rep movs as one element per loop iteration [U]. 0.5 to 0.75 GB/s is what a byte-at-a-time loop costs under TCG, not what the emulated memory can do. Expect 3 to 8x on every copy from a 30-line assembly routine (section 2).
A10 Frame cap 60 (ks_fcap=60, 01-core.HC:58) QEMU's GUI refresh interval is 30 ms by default (GUI_REFRESH_INTERVAL_DEFAULT 30, include/ui/console.h [V]). Nothing above about 33 fps is visible in the QEMU window. Cap at 30 and spend the budget on quality, or on sleeping.

1.2 Missing work#

  • Unicode and text. The kernel is 8-bit, the atlas is ASCII, TW and Txt skip anything outside 32 to 126. A browser, an email client and a messenger are mostly non-ASCII text in practice (names, quotes, dashes, accents, emoji). The plan's font row says "non-Latin" once and gives no path. See 3.3.
  • Clipping and scrolling. The only clip is the dirty rectangle (ks_c0x..ks_c1y, 01-core.HC:36). Apps keep content inside windows by arithmetic (rows in Notes, FROWS in Files). A scroll view, a text field, a list and a browser viewport all need a push/pop clip stack intersected with the dirty rectangle. Not listed.
  • Event model. Clicks are dispatched by numeric id ranges (04-main.HC:148-204,218-261), keys by FrontOpen index (:315-361), hover by a global ks_hot. There is no event struct, no capture/bubble, no focus object, no double-click, no right button (only ms.lb is read, :378), no key repeat policy. The plan's "app table with draw, click, key, close" is the right shape but is listed as M; with text fields, drag and drop, menus and modal sheets it is L.
  • Threading. Everything runs in one task; Draw blocks input; the idle loop sleeps 5 ms (05-run.HC:123). A fetch, a page parse or a file copy will freeze the desktop. TempleOS Spawn takes a target_cpu (Kernel/KTask.HC [V]) and tasks share one address space, so a worker per core is cheap. Not mentioned anywhere in the plan.
  • Memory. At 2x the desktop holds ten wallpapers of 20.7 MB plus bb and prev (about 250 MB of the 1 GB in boot-vm.sh:14) [V, arithmetic from 01-core.HC:35,655-656 and 04-main.HC:103-104], icon masks cached per size forever (ks_icm, 01-core.HC:514,571), and no budget for window layers, glyph caches, images or page DOMs. No -m increase, no eviction policy.
  • Failure. TempleOS is ring 0 with one address space: a stray pointer in any app takes the desktop and the OS with it. HolyC has try/catch (used in 03-apps.HC:271-275), which is the only tool. The plan has no crash policy (auto-restart the desktop task? reboot to a snapshot?). For an OS that runs an AI agent and a browser against untrusted input this is the first security fact, and it is absent.
  • Testing. scripts/snap.sh and the monitor socket exist [V]; there is no golden-screenshot test, no frame-time regression check, no per-commit boot. The browser gate ("20 pages lay out") needs exactly this harness.
  • Real hardware. Nothing in the plan says where the OS eventually runs. V5.03 plus this desktop cannot display outside QEMU (A4, verdict point 4).

1.3 Sequencing#

The plan's order is channel, framework, services, browser B0, B1, editors, mail, network. Better:

  1. Install to disk (hours). Removes the persistence gate.
  2. Measurement harness and renderer performance pass (1 week, section 2). Every later item is cheaper to build against a renderer whose costs are known and whose primitives will not change under it.
  3. Framework, event model, clip stack, input fix (modifiers, key-up, Esc no longer quits), widget kit v2 (3 weeks, not 2).
  4. Text: host-built atlases for more ranges, sizes and weights; glyph cache by (face, size, codepoint); UTF-8 strings throughout (1 week). This is the first thing that makes the editor, the browser and mail possible.
  5. Channel: raw-disk mailbox for bulk, serial for control (3 days), host service v0 (file exchange, fetch).
  6. Browser B0 (reader mode) and the editor family.
  7. B1 only after a B0 user test, with the page set chosen to fit.

1.4 Is desk3 a sound base? Code audit#

Keep: the logical/native unit split (01-core.HC:31-36,331-367), clipped spans and the corner coverage table (:130-176), the dirty-rectangle loop (02-ui.HC:24-71, 05-run.HC:28-54), immediate-mode widgets with hit registration (02-ui.HC:117-177), the Lucide stroke rasteriser with a per-size cache (01-core.HC:530-597), the per-phase timers for Monitor (05-run.HC:5-25). These are good ideas done simply.

Redesign now (cheaper now than after six apps are written against them):

Item Where Problem Change
App = window = array slot 02-ui.HC:4-15, NA at 01-core.HC:26 One window per app, fixed size, nine of them; ToFront copies arrays (04-main.HC:28-41) A window table (id, app, rect, z, flags, layer bitmap) and an app table of function pointers; HolyC has function pointers (Spawn takes one).
Dispatch by if-chain 03-apps.HC:585-597, 04-main.HC:148-204, :315-361 Three places to edit per app; keyboard rules differ per app app->draw/click/key/wheel/close through the table; one focus rule.
Draw re-runs per dirty rect 05-run.HC:33-36 Ten rects means ten full passes over every window's draw code; text and icon loops run and get clipped per glyph Per-window retained layers (section 2.4): a dirty rect becomes a composite of overlapping layers, and only the window whose content changed re-executes its draw.
Hit list 02-ui.HC:74,94 MAXH 500, silently dropped when full; linear scan per hover; rebuilt only inside the first dirty rect's draw (ks_reg set at 05-run.HC:32, cleared at :50) by accident of ordering Per-window hit lists owned by the layer, invalidated with the layer; Hit reports overflow in Monitor.
Clip 01-core.HC:36 One rectangle, equals the dirty rect ClipPush(x,y,w,h)/ClipPop intersecting with the current clip; scroll offset in the window's draw context.
Text 01-core.HC:369-489 ASCII only; Wrap is O(n^2) (calls TW on the growing buffer per character, :475) with a 200-byte buffer; Notes re-lays out the whole buffer every frame (03-apps.HC:422-449) and every 500 ms for the caret blink (05-run.HC:109-113) UTF-8 decode; glyph cache keyed by face, size, codepoint; a line-layout cache per paragraph; the caret blink dirties a 2 by 17 rectangle, not the body.
Keyboard 04-main.HC:319-321,267 Esc and Shift-Esc reboot; flag bits masked off; no key-up; no repeat policy Esc is an app key; a system chord (Ctrl-Alt-Q) quits; keep sc whole and expose modifiers; read kbd.down_bitmap for key-up if it exists [U].
Mouse 04-main.HC:367-378 Relative deltas times 3 times scale, re-centred with MsSet; no right or middle button; wheel from ms.pos.z Keep the relative scheme (it works) but add acceleration curves and read ms.rb/ms.mb [U, field names from memory]. Absolute positioning needs a tablet device, which needs USB, which TempleOS lacks.
Shadow 01-core.HC:287-329 Per-pixel Px call with a table lookup over the whole blur band, per window, per dirty rect 9-slice cache keyed by (w,h,r,blur) and span blends with constant alpha on the straight edges (section 2.3).
Blend 01-core.HC:60-65,85-92 Mix is a function call per pixel from Px, RRTexN, Txt, LIcon, Up4, BuildWall Inline the two-multiply blend; precompute src*a per span; a 16-entry table per glyph colour for the 4-bit alpha.
Floating point in pixel loops DiskN :235-246 (a Sqrt per pixel), LineN :248-272, SegMask :530-552 HolyC F64 is x87 (Compiler/OpCodes.DD has FLD/FADD/FSQRT and no SSE [V]); TCG emulates x87 in software Integer coverage tables for disks (like CornerTab), DDA in fixed point for lines; icons are already cached so SegMask only pays once per size.
Memory 01-core.HC:649-717, 04-main.HC:90-121 Wallpapers never evicted; no budget Keep one style's wallpaper, rebuild on switch (0.3 s today, less after the pass); raise -m.

The verdict on the base: the primitives are fine, the shell structure is a demo. Do the redesign in the framework step, before the editor, because the editor is the first app that needs clipping, focus, modifiers, UTF-8 and a line cache, and it will otherwise be written around the demo's limits and then rewritten.

2. Performance#

2.1 Where the time goes today (reasoning from the code and the measured numbers)#

  • 64000 pixel loop in 1 ms (spike/KsSpike.HC:67-71, README) is 15.6 ns per back[j]=back[j]+0x010101. HolyC keeps j in memory and emits about ten instructions for that statement [U]; TCG on Apple Silicon runs a translated guest instruction in roughly 1 to 2 ns [U]. So the cost is HolyC's code generation times TCG, not TCG alone: a register-resident assembly loop should do the same in 3 to 5 ns per pixel, and a 16-byte SSE2 move in about 1 ns per pixel if the assembler had SSE (it does not [V]; 64-bit MOV pairs then, two pixels per instruction).
  • A full 1280x800x4 copy in 7 ms is 0.59 GB/s, consistent with one translated loop iteration per byte (REP_MOVSB [V]).
  • A dragged window at 2x (23 ms): per dirty rect (two per move, each about the window plus 88x90 logical of shadow margin, 02-ui.HC:68-71), Draw copies the wallpaper span by span (05-run.HC:7-8, MemCpy per row), then for every open window draws the shadow band (a 560x400 window at 2x with blur 68 native is about 280k Px calls [U, arithmetic]), the rounded fill, the ring, the app body, then the menubar backdrop and the dock. Estimate for one window: shadow 8 to 12 ms, body and fill 4 to 6 ms, copies 4 to 6 ms, the rest 2 ms. The shadow and the byte copies are the two biggest items.

2.2 Ranked wins#

Rank Change Estimated gain Reasoning Cheapest experiment
1 Run on x86 with hardware virtualisation (a Linux PC or server with KVM, an Intel Mac with HVF, or a bare-metal cloud instance). Apple Silicon cannot accelerate an x86 guest (HVF is same-architecture only; Rosetta does not apply to system emulation). 10 to 20x on HolyC code, 10 to 30x on copies; a 2x frame drag from 23 ms to 1 to 2 ms; the 20.7 MB full-frame copy from about 35 ms [U, extrapolated from 0.59 GB/s] to 2 ms TCG costs 10 to 30 host instructions per guest instruction and software MMU on every load and store; KVM runs the guest natively Copy spike/KsSpike.HC to any x86 Linux box, qemu-system-x86_64 -accel kvm, read the three numbers. One hour plus access to a machine. A cheap mini PC or a used Intel Mac is the development-host answer if the HolyC route continues.
2 Replace MemCpy/MemSetU32 with 64-bit, unrolled assembly (REP_MOVSQ at least; an 8-way unrolled MOV loop better). Kernel/KUtils.HC uses REP_MOVSB and REP_STOSD [V]. 3 to 8x on every copy and fill: card copy, wallpaper spans, layer composites, prev snapshot in SetStyle TCG executes string instructions one element per iteration [U]; 8 bytes per iteration is 8x fewer iterations; an unrolled plain loop avoids the per-iteration flag and counter work 30 lines of HolyC asm in the spike; time 100 copies of 4 MB with each variant. Half a day. Caveat: QEMU may have grown a bulk fast path for rep movs in recent versions [U]; the measurement settles it.
3 9-slice shadow cache. Alpha of the shadow depends only on (w,h,r,blur), so cache four corner tiles and two edge profiles; blend straight edges with a per-row or per-column constant alpha span. Shadow cost from about 10 ms to 2 ms per 2x window Replaces 280k Px calls (bounds check, function call, Mix call) with table reads and span loops Set ks_focus shadow strength to 0 and Shadow to a no-op, read ks_pt[1] in Monitor before and after; that is the upper bound. Then implement. One day.
4 Retained window layers and tile damage. Each window draws into its own bitmap (stride-aware), re-executed only when its content changes; a frame is composite-by-copy of the layers that touch each dirty tile (32x32 tiles with per-tile dirty bits instead of 10 merged rects, 02-ui.HC:25-55). Drag, raise, focus animation and overlaps become copies: with win 2, about 3 to 5 ms for a 2x window; text-heavy windows stop re-rasterising their text on every neighbour's change Separates "content changed" from "position changed"; the browser's scroll becomes a memmove plus one strip Prototype on one window (Notes): draw it to a side bitmap, composite with a stride-aware SpanCopy. Two days.
5 No function calls or x87 in pixel loops. Inline blend in Px-callers, premultiplied source per span, 16-entry alpha tables for glyph blending, integer disks and lines. 1.5 to 2x on corners, glyphs, icons, disks; the dock's disks and radios lose their per-pixel Sqrt HolyC function calls push a frame and pass arguments on the stack; x87 under TCG is software floating point Time DiskN with r=20 at 2x 1000 times, then with a coverage table. Half a day.
6 Multicore rasterisation. -smp 4; Spawn(fn,data,name,core) three workers; split the dirty tiles by row bands; the main task composites. MTTCG is supported and on by default for the i386 target in current QEMU (target/i386/tcg/tcg-cpu.c: .mttcg_supported = true; accel/tcg/tcg-all.c enables it when supported and not icount [V, master source; assume the same in 11.0.2 [U]]). 2 to 3x on the draw phase on a 4-core slice of this Mac; nothing on the single card copy unless split too Pixels are embarrassingly parallel; TempleOS tasks share memory so no marshalling -smp 4, spawn three tasks that each fill a quarter of bb, time against one task. Half a day. Risk: TempleOS's own SMP code paths under MTTCG are lightly tested; a wrong result means stay single-core.
7 Frame pacing. Cap at 30 (QEMU refreshes the window every 30 ms [V]); only sleep when nothing is dirty; measure input-to-pixel latency not frame time. No speedup; removes wasted frames and makes animations even Frames that the display never shows cost CPU and heat Set ks_fcap=30, watch Monitor. Minutes.
8 Cursor and drag latency. With layers, the two cursor rectangles are composites, not draws; keep the cursor in its own tiny layer; read the mouse at the top of the loop and composite last. Hover and drag feel: from "one full Draw of everything under 16x21 px" to two tile copies Latency is dominated by the frame, which win 4 shrinks Comes with win 4.
9 Memory bandwidth. 2x doubles pixel count and quadruples bytes per logical area; keep blurred backdrops at quarter resolution (already), evict unused wallpapers, keep layers only for open windows, and consider 1.5x logical scales for large displays. Frees about 200 MB; avoids swapping to a bigger -m for layers Arithmetic from 01-core.HC:655-656 -m 2048M and one wallpaper at a time. Minutes.
10 GPU path. This QEMU has virtio-gpu-pci without GL [V, task statement]. A virtio-gpu 2D driver in HolyC (virtqueue setup, RESOURCE_CREATE_2D, TRANSFER_TO_HOST_2D, SET_SCANOUT, RESOURCE_FLUSH) is 500 to 1000 lines and gives a second way to put a guest-rasterised frame on screen, with explicit flush rectangles instead of dirty-page scanning. No drawing acceleration. virgl would require emitting a Gallium command stream from HolyC: not a project. About zero for rendering; maybe saves the host some dirty-page scanning All the cost is in the guest's rasterisation, which no 2D scanout device helps Do not do it. If the display path ever matters, measure the host-side cost of vga_update_display with perf on Linux first.

Combined, wins 2 to 5 should bring a 2x window drag on this Mac from 23 ms to roughly 4 to 6 ms and a full-screen style crossfade from about 60 ms to about 15 ms [U]; win 1 makes the whole question go away and is the only one that gives headroom for a browser layout engine in HolyC. Win 6 is a multiplier on whatever remains.

2.3 Emulator configuration, briefly#

  • -accel tcg,thread=multi is the default when supported; no flag needed. -accel tcg,tb-size= larger translation cache helps only if the working set thrashes (unlikely at this code size) [U].
  • -cpu choice: keep a plain x86-64 model; AVX emulation exists but is slow and the assembler cannot emit it anyway.
  • -display cocoa,zoom-to-fit=on scales 2880x1800 on the host each refresh; that is host time, not guest time. For measurement use -display none and screendump.
  • QEMU 7.2 and later show TCG regressions for some x86 guests on Apple Silicon (a Windows 3.11 hang report, QEMU issue 2996 [D]); not our symptom, but pin the QEMU version used for measurements.

3. Better solutions by component#

3.1 Display and input path#

Plan: keep the Bochs VBE framebuffer, fix input "in the framework step". Right for QEMU. Add: the layer compositor (2.2 win 4), a clip stack, a proper cursor layer, modifiers and key-up from the scan code flags and kbd.down_bitmap [U], right and middle buttons, and a system chord instead of Esc. For real hardware the display path must be a boot-time VBE mode set in the boot loader, which is what ZealOS did [D]; V5.03 cannot do it.

3.2 Host channel#

Plan: serial streaming spike, else an IDE mailbox, else a NIC. Better order:

  1. Installed disk for persistence (not a channel at all).
  2. Raw second disk as a mailbox for bulk bytes both ways: guest BlkRead/BlkWrite through the existing ATA driver on a -drive if=ide,format=raw file the host also opens. A 4 KB header with sequence numbers, then a ring of blocks. Expected several MB/s with PIO under TCG [U]; no driver, no shared-memory ordering questions, works on any QEMU and on real hardware with a USB-free ATA disk. Cheapest experiment: write 1 MB from the host, read it in the guest, compare hashes, time it. One day.
  3. Serial for control lines and events (it is duplex and already wired): stream with the FIFO, no acks, measure drops.
  4. Shared RAM (memory-backend-file, share=on) only if latency below 1 ms matters (a live viewport). Unproven under TCG on macOS, as the earlier report says.
  5. A virtio driver (serial or blk) later for interrupts and DMA; not first.

3.3 Fonts and text shaping#

Plan: port stb_truetype (about 5k lines) or keep host-built atlases. Better: host-built atlases now, a small rasteriser later, shaping never in the guest.

  • Now: extend scripts/gen-font.py to emit a glyph index keyed by codepoint and more ranges (Latin-1, Latin Extended, Greek, Cyrillic, general punctuation, arrows, a monospace face, bold and italic), at the sizes a browser and editor use, plus a Noto Sans CJK subset if CJK is wanted (about 20k glyphs at two sizes is a few MB in 4-bit alpha). Add the kern pairs for the main face. This is days, not weeks, and covers email, chat and most of the document web.
  • Later: a quadratic-outline scanline rasteriser in HolyC (TrueType glyf only, nonzero winding, 4x vertical oversampling, about 1500 lines) with an LRU glyph cache, for arbitrary sizes and web fonts. Under TCG a 24 px glyph rasterises in well under a millisecond [U]; cached, it does not matter.
  • Shaping (ligatures, Arabic joining, Indic reordering), bidi and IME: do not write these in HolyC. If the OS ever needs them, the host service returns positioned glyph runs for a string (a "shape" request), which the guest paints with its cache. That keeps the guest honest about what it can do and matches the plan's own service model.

3.4 Web browser engine scope#

Plan: reader mode first, then HTML/CSS subset, then maybe JavaScript. Agree with reader mode first and with "document web" as the ceiling. Changes:

  • Make B0 the product: reader mode plus a host-side HTML-to-display-list service for pages that reader mode mangles. A display list (text runs with font and colour, boxes, images, links with rectangles) painted natively is not screen capture; it is what the plan already names as the "clean fallback". With it, B1's layout engine becomes an optimisation, not a prerequisite for usefulness.
  • B1 native layout: scope it as "block, inline, lists, tables, images, simple flex", with a conformance set chosen from pages that use only that. Drop "position basics" and "simple flexbox" from the first cut; add them after the page set passes.
  • JavaScript: never in the guest (A8).
  • Images: a host decode-and-resize service (the plan's option two) is right; PNG and JPEG decoders in HolyC are a later nicety.

3.5 Network and TLS#

Plan: host fetch service first, native stack optional. Right. For the native track, start from Shrine's stack (PCnet driver, TCP/IP, Wget [D]) or ZealOS's work, both public domain; write an E1000 or virtio-net driver against QEMU. TLS in HolyC (X25519, AES-GCM, SHA-256, certificate parsing, a root store) is months and will be slow under TCG; it is justified only on real hardware or KVM, and only if the OS is meant to be self-contained. Keep credentials on the host regardless.

3.6 Persistence and file systems#

Plan: host file service. Better: installed RedSea disk (3.2.1), plus a host RedSea tool to read and write the image when the VM is stopped and a mailbox file for live exchange. RedSea has no journaling, no permissions and no long-name hashing; fine for a single-user OS, not for a "complete OS" (section 4).

3.7 Audio#

Plan: host plays, guest controls (mpd/mpv). Right. A guest HDA or AC97 driver for QEMU (-device intel-hda plus hda-duplex, or AC97) is a few hundred lines of ring-buffer DMA and would let the music player be genuinely native for decoded PCM; decoding MP3 or AAC in HolyC under TCG is too slow to be worth it (an MP3 frame decoder is around 10 million operations per second of audio [U]). Order: host playback now, an AC97 driver as a weekend spike later, decoding on the host always.

3.8 Apps SDK#

Plan: app table and widget kit. Add: a written contract (draw context with clip and scroll, event struct, focus, invalidation API, timers, a worker-task helper), a component gallery that doubles as the golden test, UTF-8 everywhere, and a size limit per app file so the single concatenated build (scripts/send-to-vm.sh:14 [V]) stays fast to compile. HolyC's #include at run time already gives hot reload; use it: each app a file, reloadable without a desktop restart.

3.9 Packaging and updates#

Not in the plan. For the HolyC desktop: a manifest per app (name, icon, files, version), apps stored on the installed disk under one directory, an updater that pulls a package through the fetch service and verifies a hash. Shrine's Pkg is a public-domain reference [D]. Signatures need a hash and a signature verifier in HolyC (Ed25519 is about 1000 lines of C and runs fast enough once per package [U]).

3.10 AI agent integration#

Plan: host agent, approval for every write and command, native chat and diff panels. Right, and the only safe shape given ring 0. Add: the agent must never drive guest input directly without the "agent is driving" banner the earlier report proposed; all tool calls and their approvals logged on the host; the guest shows the exact command before approval. The engine's editor.* protocol and kosmos-mcp are the obvious transport once the channel exists.

3.11 Security and permissions#

In TempleOS there are none: no user separation, no memory protection, no sandbox, any app can read all memory and the whole disk. The plan's rule (nothing sent, deleted or purchased without a UI prompt) is therefore only as strong as the host daemon's enforcement of it. Write the permission broker on the host with per-app manifests, make the guest unable to reach the network or credentials by construction (it cannot), and treat anything the guest sends as untrusted input. Say this in the plan, because "security" for this OS is a host property.

3.12 The base OS question#

Option Can look modern Can run a modern browser, TLS, drivers, GPU Keeps the HolyC work Runs on real hardware Years to "complete OS"
TempleOS V5.03 (today) yes, in QEMU no, by design (ring 0, no libc, no network, 8-bit text, BIOS only) all of it no (VGA only) not reachable
ZealOS (fork; variable-resolution 32-bit VBE, AHCI, networking in development, UEFI via Limine, public domain [D]) yes, with a port of the renderer to its VBE path partly: network yes (later), TLS no, GPU no, protection no most (HolyC dialect is close [U]) yes [D] the "hobby OS" end state: a modern-looking single-address-space OS with native apps and a document web browser; 2 to 3 person-years for that, never a daily driver for web apps
Linux base plus own Wayland shell (os-foundation-plan.md) yes yes, all of it exists the design, tokens, assets, app list; the HolyC desktop as a VM window or personality yes 1 to 2 person-years to a usable shell and store on an existing distribution; the OS underneath is already complete
Modern host app embedding a HolyC runtime (a Rust or Swift app that runs TempleOS in QEMU or a HolyC interpreter as a "personality" with our shell around it) yes yes (host) all, as content it is an app, not an OS months; useful as the development vehicle and demo, not the goal
Bare-metal Rust unikernel reusing the renderer design yes no, you write drivers, a network stack, TLS, a browser, a GPU stack from scratch (or pull in Rust crates like smoltcp, rustls, embedded-graphics) the design yes 3 to 5 person-years for parity with the ZealOS end state plus TLS; still no web platform
Hybrid: Linux base, our shell, TempleOS VM as a first-class app with shared channel yes yes all yes as Linux base; the HolyC desktop keeps its showcase role

Recommendation: hybrid. Keep the HolyC desktop as the showpiece on the Temple family (move to ZealOS when the showpiece should leave QEMU), finish the renderer pass and the native apps that fit (editor, code editor, terminal, notes, reader browser, calculator, files, settings, music control), and put the "complete OS" ambition on the Linux-base track, where the same shell design becomes a Wayland compositor and the HolyC desktop runs inside it as one window, with the host services the plan already needs. That gives the user the two things they asked for without pretending one base can do both.

Conditions that change it:

  • If the goal is really "a TempleOS that looks modern and runs on a PC" (an art and hobby OS, accepting no web apps, no TLS in the guest and no protection): go ZealOS, develop on x86 with KVM, skip the Linux track.
  • If the user gets an x86 development host with KVM: the TCG penalty disappears and the HolyC native-app scope (including B1 layout) becomes comfortable; the base question is unchanged.
  • If the user will accept a host app as the "OS" (a kiosk-style shell on macOS or Linux running the HolyC desktop and web apps side by side): the embedding option is the fastest path to a daily-driver feel, at the cost of not being an OS.

4. Long-term roadmap to a complete operating system#

4.1 Capability map#

Capability TempleOS V5.03 Built here Plan covers Missing Viable on TempleOS?
Kernel, memory protection ring 0 only, identity-mapped single address space, no protection by design n/a no everything No. Adding paging-based isolation to TempleOS is a different kernel.
Processes, permissions, users preemptive tasks, no users, no permissions one task no all No.
File systems, storage RedSea, ISO9660 read, ATA; no journaling, no USB, no NVMe, no AHCI (ZealOS adds AHCI [D]) RAM drive use host file service install to disk; FAT or exFAT for exchange; integrity Partly (ZealOS).
Drivers, hardware PS/2, ATA, VGA, PC speaker, PIT/HPET, APIC; no USB, no ACPI power, no PCIe MSI PCI scan, Bochs VBE NIC "later" USB (keyboards and mice on modern PCs are USB), VBE boot mode set, audio, power Partly (VBE and AHCI in ZealOS); USB stack is months.
Networking none (Shrine and ZealOS: experimental stacks [D]) none host fetch; native optional driver, stack, TLS, DNS over anything, proxies Stack yes (borrow); TLS costly; keep on host.
Graphics, compositor 640x480x16 software renderer, dirty rects, AA shapes, blur, shadows, 2x layers implied layer compositor, clip stack, images, GPU never Yes for software 2D; no GPU ever.
Text: Unicode, bidi, IME, accessibility 8-bit, one bitmap font Inter ASCII atlases "non-Latin" one line UTF-8 strings, multi-range atlases, kerning, shaping (host), bidi (host), IME (host), screen reader hooks (the hit list is a start) Rendering yes; shaping and IME via host.
Audio PC speaker none host playback AC97 or HDA driver, mixer, decoding (host) Driver yes; decoding no.
Power none none no ACPI, suspend, battery No.
Installer, updates ISO installer to disk scripts no image build, update channel, rollback Partly.
App model, SDK HolyC #include, tasks, DolDoc immediate-mode widgets app table, widget kit contract, events, UTF-8, worker tasks, gallery tests Yes.
Store, package manager none (Shrine Pkg [D]) none no manifests, hashes, signatures, fetch Yes (borrow).
Security none none UI prompts host broker, manifests, audit log; guest isolation impossible Host only.
Localisation English, 8-bit English no UTF-8, string tables, locale formats Rendering yes.
Developer tools the compiler, debugger, DolDoc, grep send-to-vm, snapshot code editor with Run goldens, frame-time CI, crash capture Yes.
Testing and CI none snap.sh, monitor screendump no per-commit boot, goldens, latency probe Yes.

4.2 Phases#

Phase Content Exit criteria Effort [U] Base
P0 Foundation install to disk; measurement harness (goldens, per-phase timers, latency probe); renderer pass (copy routine, shadow cache, inline blend, integer disks); -m and wallpaper eviction 2x window drag at or under 8 ms on this Mac; goldens for every style; no change to the look 1.5 weeks TempleOS in QEMU
P1 Platform window and app tables, event struct, focus, clip stack, modifiers and key-up, system chord, widget kit v2 (text field, list, tabs, scroll view, menu, dialog), UTF-8 strings, multi-range atlases, glyph cache, worker tasks, component gallery gallery passes goldens in five styles at 1x and 2x; a text field with selection, clipboard (guest-local) and UTF-8 used by two apps; Esc no longer reboots 4 weeks same
P2 Services raw-disk mailbox and serial control; host daemon (file exchange, fetch with cache, image decode and resize, shape, clipboard); permission broker and audit log on the host guest fetches a 500 KB page in under a second and shows a decoded image; every host action is logged 2 weeks same
P3 Apps A editor (line cache, undo, find), code editor with Run and error panel, terminal, notes, files completion, settings saved to disk each app at 2x stays under 10 ms per keystroke frame 3 weeks same
P4 Browser B0 chrome, history, bookmarks, reader mode, host display-list fallback 20 chosen pages legible at 2x; links, scroll, find, selection work 2 weeks same
P5 Apps B email and messenger over host services with approvals; music control; AI IDE panels account review with the user first; nothing outbound without a prompt 3 weeks same
P6 Browser B1 native HTML and CSS subset, tables, images, forms the conformance set lays out without overlap; MDN and GitHub as stretch 6 to 10 weeks same
P7 Hardware (optional) move the desktop to ZealOS: port the renderer to its VBE framebuffer, AHCI disk, its network stack; boot on a PC or under KVM boots on one real PC at native resolution; same goldens 4 to 6 weeks ZealOS
P8 Modern OS track os-foundation-plan.md M0 to M5: Linux base, Smithay shell with our chrome, portals, PipeWire, Flatpak store; the HolyC desktop as a VM window an Electron and a Tauri app run unmodified under our shell; the HolyC desktop opens as a window with shared clipboard and files 3 to 6 months to M4 Linux

Where TempleOS stops being viable: at memory protection, users and processes (never), USB and modern hardware (P7 at best with months of driver work), TLS and a web platform (never in the guest), audio decoding and video (never), IME and complex text (host only). Everything from P0 to P6 is viable and worth doing as the showcase; P7 is the decision point about real hardware; P8 is the only route to the stated long-term goal.

5. Ten changes to the plan, in priority order#

# Change Reason Cheapest first step
1 Install TempleOS to a disk image; delete the persistence gate. The installer exists (boot-vm.sh:17 declines it [V]); a file service for notes and settings is a detour. qemu-img create -f qcow2 temple.qcow2 2G, add -drive file=temple.qcow2,if=ide,index=0, answer "y" once, snapshot. One hour.
2 Replace MemCpy and MemSetU32 with 64-bit unrolled assembly for the desktop's copies. REP_MOVSB [V] is one byte per TCG iteration; copies are in every frame. Time three variants in spike/KsSpike.HC. Half a day.
3 Insert a renderer performance phase (P0) before the framework. The 23 ms drag is four fixable costs (2.2 wins 2 to 5); fixing them after apps exist means touching every app. Disable Shadow and read ks_pt[1] to measure the shadow's share. One hour.
4 Grow the framework item into a platform phase: window table, event struct, clip stack, modifiers and key-up, UTF-8. The demo's structure (fixed NA, if-chains, 500 hits, dirty-rect-only clip, Esc reboots) will be baked into six apps otherwise. Write the one-page SDK contract (draw context, event struct, invalidation) and convert Notes to it. Two days.
5 Host-built multi-range atlases with kerning now; a HolyC TrueType rasteriser later; shaping, bidi and IME on the host. ASCII-only text (01-core.HC:399,420) blocks email, chat and the browser more than any missing CSS property. Extend scripts/gen-font.py to Latin-1 plus Greek and Cyrillic and a monospace face; codepoint-indexed glyph table. Two days.
6 Make the raw-disk mailbox the bulk channel and serial the control channel; shared RAM only for a live viewport. No driver, no memory-ordering unknowns, works on real hardware; serial's number was a protocol artifact. Host writes 1 MB to a raw image; guest BlkReads and hashes it. One day.
7 Ship the browser as reader mode plus a host display-list fallback; defer native layout (B1) and drop JavaScript for good. Coverage comes from the fallback, crispness from native paint; B1 at 3 to 5 weeks is optimistic by 2 to 3x. Pick the 20 pages and classify each as reader-mode, display-list or native-subset. One hour.
8 Use the cores: -smp 4, worker tasks for parse, fetch and tile rasterisation. MTTCG is on by default for x86 guests in current QEMU [V]; the desktop blocks on every long operation. Spawn three fill tasks and time them. Half a day.
9 Decide the base for real hardware: ZealOS for the Temple showcase, Linux plus our shell for the modern OS; write both into the plan with the hybrid as the stated end state. V5.03 cannot display this desktop outside QEMU; the user's long-term goal needs a kernel TempleOS will never be. Boot the ZealOS ISO in QEMU, check its VBE mode and that Spawn, MAlloc and port I/O match. Half a day.
10 Add a test and crash policy: goldens per style and scale, a frame-time budget check, a latency probe, and a documented recovery (snapshot restore) for the ring-0 crash case. Every gate in the plan is a visual or timing claim with no harness; one bad pointer takes the OS. A script that boots the snapshot, opens each app, runs screendump, compares hashes. One day.

6. Evidence index#

Local files read today (all [V]): README.md; docs/holyc-native-apps-plan.md; docs/core-apps-plan.md; docs/os-foundation-plan.md; docs/kosmos-app-port-research.md; docs/electron-tauri-support-research.md; desk3/01-core.HC to 05-run.HC; spike/KsSpike.HC; scripts/boot-vm.sh, send-to-vm.sh, serial-send.py, snap.sh, run.sh.

Web sources, accessed 2026-10-27:

  • TempleOS Kernel/KUtils.HC (archive branch): MemCpy uses REP_MOVSB, MemSetU32 uses REP_STOSD, no SSE or MMX in the file. https://raw.githubusercontent.com/cia-foundation/TempleOS/archive/Kernel/KUtils.HC [V]
  • TempleOS Compiler/OpCodes.DD: no SSE or SSE2 opcodes (no MOVDQU, MOVAPS, PADDB, PXOR and so on); XMM registers declared but unused; F64 is x87 (FLD, FADD, FMUL, FSQRT). https://raw.githubusercontent.com/cia-foundation/TempleOS/archive/Compiler/OpCodes.DD [V]
  • TempleOS Kernel/KTask.HC: Spawn(..., I64 target_cpu=-1 ...), "-1 for current CPU", SpawnQue for other cores; mp_cnt. https://raw.githubusercontent.com/cia-foundation/TempleOS/archive/Kernel/KTask.HC [V]
  • TempleOS Kernel/ directory listing (BlkDev, SerialDev, MultiProc.HC, PCIBIOS.HC present). https://api.github.com/repos/cia-foundation/TempleOS/contents/Kernel?ref=archive [V]
  • QEMU include/ui/console.h: GUI_REFRESH_INTERVAL_DEFAULT 30, GUI_REFRESH_INTERVAL_IDLE 3000. https://raw.githubusercontent.com/qemu/qemu/master/include/ui/console.h [V]
  • QEMU accel/tcg/tcg-all.c: MTTCG enabled by default when cc->tcg_ops->mttcg_supported and not icount; thread=multi forces it with a warning if unsupported. https://raw.githubusercontent.com/qemu/qemu/master/accel/tcg/tcg-all.c [V]
  • QEMU target/i386/tcg/tcg-cpu.c: .mttcg_supported = true, .guest_default_memory_order = TCG_MO_ALL & ~TCG_MO_ST_LD. https://raw.githubusercontent.com/qemu/qemu/master/target/i386/tcg/tcg-cpu.c [V] (master; the 11.0.2 build on this Mac assumed the same [U])
  • QEMU MTTCG design document. https://www.qemu.org/docs/master/devel/multi-thread-tcg.html [D]
  • QEMU issue 2996 (x86 TCG regression on Apple M1 from 7.2 onward for one guest). https://gitlab.com/qemu-project/qemu/-/issues/2996 [D]
  • ZealOS README: 32-bit colour VBE with variable resolutions, AHCI, network drivers and stack in development, Limine UEFI boot, public domain. https://raw.githubusercontent.com/Zeal-Operating-System/ZealOS/master/README.md [D]
  • Shrine README: native TCP/IP stack (experimental) with PCnet-PCI II, serial tunnel, Wget, Pkg package manager, Mfa file transfer. https://raw.githubusercontent.com/minexew/Shrine/master/README.md [D]
  • Search results on TCG x86 performance on Apple Silicon: qualitative only ("too slow to be usable" for full x86 VMs), no measured factor. https://www.cl.cam.ac.uk/teaching/2425/ECAD+Arch/aarch64-vm.html [D]

Unverified claims that matter most, in the order they should be tested: the TCG cost of rep movsb versus an unrolled loop (one measurement decides change 2); that TempleOS exposes modifier flags and key-up in ScanKey's scan code and kbd.down_bitmap; raw BlkRead/BlkWrite on a second unformatted ATA drive; that -smp 4 under MTTCG runs TempleOS's SMP code correctly on this QEMU; the RedSea on-disk layout for a host tool; ZealOS API compatibility with this desktop's HolyC.

TempleOS Desktop Docs. Built from the Markdown sources; offline, no external requests. Back to top