lumen is the compositor and display server for LoricaOS, a capability-based, no-ambient-authority x86-64 operating system built on the from-scratch Aegis kernel.
lumen owns the screen. It maps the framebuffer, draws the desktop, and is the
single process every graphical application talks to in order to get a window.
A GUI app does not touch the framebuffer or the input devices itself — it
connects to lumen over an AF_UNIX socket, asks for a window, and receives a
shared-memory buffer it draws into plus a stream of input events. lumen
composites every client's surface, its own built-in surfaces (the top bar, the
"Aegis" menu, the dropdown terminal, the About window), and the cursor into the
framebuffer.
This repository is a standalone LoricaOS component. It does not vendor the
GUI toolkit; it fetches a pinned build artifact of the
glyph toolkit (GLYPH_VERSION), compiles
against it, and packs a signed herald system
package, lumen.hpkg.
LoricaOS is decomposed into independent repositories:
| Repo | Role |
|---|---|
LoricaOS/Aegis |
The kernel. Provides the framebuffer mapping, PTYs, AF_UNIX sockets, memfd, the capability model, and the syscalls lumen uses. |
LoricaOS/LoricaOS |
The OS: userland, rootfs, ISO/installer. Assembles the components into a bootable system. |
LoricaOS/glyph |
The GUI toolkit. Provides window/surface primitives, the software renderer (draw_*, font_*), the terminal emulator core (glyph_term), theme/preferences, the /apps bundle registry, and the client side of lumen's window protocol (lumen_proto.h, lumen_client.h). |
LoricaOS/lumen |
This repo. The compositor/display server. The foundational graphical component. |
Everything graphical depends on lumen. The desktop session manager (Bastion)
launches lumen; the dock, application launcher, file manager, editor,
calculator, settings, the standalone terminal, and the GUI installer are all
lumen clients that connect to /run/lumen.sock. In herald packaging terms,
those components declare depends=lumen.
Because lumen ships the desktop fonts (Inter, JetBrains Mono) and logo
assets inside its package, every component that depends on lumen gets the fonts
transitively — there is no separate font package to install.
lumen is a single-threaded, poll-driven compositor. main()
(src/main.c) maps the framebuffer, sets up the compositor, opens the input
devices and the client socket, then runs one event loop at roughly 60 fps:
service clients, read keyboard and mouse, read terminal PTYs, update the clock,
composite the dirty regions, present.
The source is organized into focused modules:
| File | Responsibility |
|---|---|
src/main.c |
Entry point and event loop. Framebuffer mapping (sys_fb_map), backbuffer allocation, raw-mode keyboard, mouse batching, the top-bar/clock/volume UI, the "Aegis" context menu, keyboard shortcuts, the boot crossfade, wallpaper/logo loading, client process spawning, and zombie reaping. |
src/compositor.c / .h |
Window management and dirty-rect compositing: the window stack, focus/raise, drag, drag-and-drop brokering, the desktop background/gradient, frosted-glass window chrome, soft shadows, rounded corners, the per-window blur cache, and the backbuffer→framebuffer flip. |
src/lumen_server.c / .h |
The AF_UNIX window-protocol server: accept/handshake, per-client state, CREATE_WINDOW/CREATE_PANEL/DAMAGE/DESTROY/INVOKE/DRAG_START/SET_ADMIN, the memfd shared-buffer handoff via SCM_RIGHTS, and input/focus/DnD event delivery back to clients. |
src/cursor.c / .h |
The software cursor: a procedurally built 16×20 ARGB arrow sprite with outline and drop shadow, drawn with save-under so the compositor can move it without recompositing. |
src/terminal.c / .h |
The in-process quake-style dropdown terminal (Ctrl+Alt+T). A thin wrapper over glyph's shared terminal-emulator core (glyph_term) plus a PTY. Regular terminals are a separate /bin/terminal client. |
src/about.c / .h |
The "About Aegis" window: version string, CPU/memory/display info read from /proc, logo, and the v1 software disclaimer (shown once at every startup). |
The compositor keeps a Z-ordered array of up to MAX_WINDOWS (16) windows
(compositor_t.windows, top of stack is last). Each window is a glyph
glyph_window_t. Windows come in two flavours:
- In-process windows owned by lumen itself: the dropdown terminal and the About window. lumen renders their content directly.
- Proxy windows owned by external clients. The client renders into a shared
memfdbuffer; the compositor reads that buffer when it composites. Aproxy_window_t(src/lumen_server.c) ties the glyph window to the client connection and the shared mapping.
Rendering is damage-driven. Event handlers accumulate dirty rectangles
(comp_add_dirty), and comp_composite repaints only those regions — redrawing
the desktop background under each rect, re-blitting any window that overlaps it,
then copying just those rects to the framebuffer (partial_flip). A
full_redraw flag forces a whole-frame composite for events that change
everything (window raise, theme change, wallpaper change, the boot fade). See
docs/ARCHITECTURE.md for the full compositing pipeline,
including the frosted-glass panel cache and rounded-corner handling.
lumen listens on /run/lumen.sock (AF_UNIX, SOCK_STREAM). The wire protocol
is defined in the glyph toolkit (lumen_proto.h / lumen_client.h), so clients
and the server share one definition. The exchange is:
- Handshake. On connect the client sends a
lumen_hello_t(magic+version); the server validates it againstLUMEN_MAGIC/LUMEN_VERSIONand replies with alumen_hello_reply_tstatus. - Create a window. The client sends
LUMEN_OP_CREATE_WINDOW(orLUMEN_OP_CREATE_PANEL). The server allocates amemfd,ftruncates it towidth*height*4, maps it, and replies with alumen_window_created_tcarrying the geometry — and thememfditself passed as anSCM_RIGHTSancillary message. The client maps the samememfdand draws into it. - Damage. When the client has drawn a frame it sends
LUMEN_OP_DAMAGE; the compositor marks the window dirty and presents it. The firstDAMAGEis also what reveals the window (it is created unpresented so its uninitialized buffer never flashes) and triggers its open animation. - Events. The compositor delivers
LUMEN_EV_KEY,LUMEN_EV_MOUSE(down/up/move/wheel),LUMEN_EV_FOCUS, the drag-and-drop events (LUMEN_EV_DRAG_OVER/_LEAVE/LUMEN_EV_DROP), andLUMEN_EV_CLOSE_REQUESTback over the same socket.
Two additional opcodes are notable. LUMEN_OP_INVOKE lets a client (the dock)
ask the compositor to launch a named application — resolved through glyph's
/apps bundle registry and spawned as a new client process. LUMEN_OP_SET_ADMIN
lets a client flag a window as an elevated/admin session, which the compositor
renders with an unmistakable red titlebar tint.
The server is hardened against malformed clients: every fixed-size opcode
requires the client-declared header length to match the expected struct size
exactly, and the connection is dropped on any mismatch rather than guessing
(lumen_server_read). Client and per-client window counts are bounded
(LUMEN_MAX_CLIENTS = 8, LUMEN_MAX_WINDOWS_PER_CLIENT = 8).
lumen reads the raw keyboard from stdin (put into raw, non-canonical mode) and
the mouse from /dev/mouse (non-blocking). Mouse events are batched per frame —
all pending deltas are drained and coalesced before hit-testing — to keep the
pointer responsive. Keyboard handling decodes escape sequences inline (bare ESC,
CSI arrow keys, and the compositor's global chords) and otherwise forwards keys
to the focused window's on_key callback or to its PTY.
The cursor (src/cursor.c) is drawn in software with save-under: before the
sprite is blitted, the framebuffer pixels beneath it are copied aside, so
cursor_hide() can restore them without a recomposite. Pure pointer motion (no
content change) takes a fast path that only hides and re-shows the cursor.
A single quake-style dropdown terminal lives in-process (src/terminal.c),
toggled with Ctrl+Alt+T. It is a thin wrapper over glyph's shared
glyph_term emulator core plus a PTY opened by glyph's
glyph_pty_open_and_spawn. The main loop polls every window that carries a PTY
(win->tag ≥ 0) and feeds output into the emulator. Regular terminal windows
are a separate standalone client (/bin/terminal); only the dropdown remains
embedded in the compositor.
lumen maps the linear framebuffer once via sys_fb_map (syscall 513), which
returns its address, dimensions, pitch, and bpp. It composites into a
heap-allocated backbuffer and flips finished regions to the real framebuffer,
so clients never see partial frames. A sys_present syscall (515) is issued
after each flip (a no-op on a direct framebuffer, a hook for buffered display
paths). Night Light, when enabled, is applied as a warm tint during the flip.
LoricaOS has no ambient authority. A process can do nothing to the system
except through capabilities granted to it by kernel policy at exec time; there is
no implicit "root can do anything". lumen's capability set is declared in
pkg/caps.d/lumen and installed to /etc/aegis/caps.d/lumen:
service FB THREAD_CREATE PROC_READ POWER
| Capability | Why lumen needs it |
|---|---|
FB |
Direct framebuffer access. lumen maps the linear framebuffer with sys_fb_map and writes pixels to it — it is the display server, so this is its defining capability. |
THREAD_CREATE |
Used to spawn client processes. lumen launches GUI applications on demand (sys_spawn, e.g. from the "Aegis" menu and LUMEN_OP_INVOKE); without authority to create new execution contexts it could not start the desktop's apps. |
PROC_READ |
Read process/system introspection. The About window reads /proc/meminfo for CPU and memory info; lumen also reaps the child clients it spawns. |
POWER |
Power control. The menu's "Restart" and "Power Off" items signal init (PID 1) for a graceful shutdown, with a raw sys_reboot as fallback — which requires power authority. This is why the package is class=system. |
service marks this as a system service profile. lumen runs with only these
baseline capabilities; it does not hold elevated privilege. Capability
delegation for a session is handled by lumen's parent (Bastion), not by lumen
itself.
lumen builds with a musl cross-compiler against the prebuilt glyph toolkit.
Requirements:
MUSL_CC— a muslgcc(defaults tomusl-gcconPATH). LoricaOS userland is built against musl.HERALD_KEY— the package signing key (ECDSA P-256 private key). Required to packlumen.hpkg.- Network access (or a pre-populated cache) to fetch the glyph toolkit artifact.
make # fetch toolkit + build lumen.elf + pack lumen.hpkg
HERALD_KEY=/path/to/key make
MUSL_CC=/opt/musl/bin/musl-gcc make
make clean # remove build outputs + the fetched toolkitThe build (Makefile) does three things:
- Fetch the toolkit.
tools/fetch-glyph.sh $(GLYPH_VERSION) toolkitdownloadsglyph-<ver>.tar.gzfrom the glyph repo's releases (or uses the cached copy undervendor/) and unpacks itsinclude/andlib/intotoolkit/. This mirrors how LoricaOS fetches the kernel artifact. - Compile.
$(MUSL_CC)builds thesrc/*.csources againsttoolkit/includeand linkstoolkit/lib's-lcitadel -lglyphintolumen.elf. The version string is injected via-DAEGIS_VERSION. - Pack.
tools/pack.shstages the payload, buildslumen.hpkg, and signs it.
The pinned toolkit version lives in GLYPH_VERSION; lumen's own version lives in
VERSION.
lumen.hpkg is a herald system package: a manifest-first, uncompressed POSIX
ustar archive accompanied by a detached lumen.hpkg.sig ECDSA-P256/SHA-256
signature (tools/pack.sh). Because lumen is class=system (it holds the
POWER capability), herald installs its whole payload tree verbatim:
manifest id, name, version, class=system
bin/lumen the compositor (stripped)
etc/aegis/caps.d/lumen its capability policy
usr/share/fonts/Inter-Regular.ttf
usr/share/fonts/JetBrainsMono-Regular.ttf
usr/share/logo.raw LoricaOS desktop logo
usr/share/wallpaper.raw default desktop wallpaper
The fonts ship here so that every component depending on lumen inherits them. The logo assets back the desktop centerpiece and the About window.
Install it directly:
herald install lumenor pull it in as part of the desktop meta-package. lumen is the base of the
graphical stack — everything else in the desktop declares depends=lumen.
.
├── Makefile fetch toolkit → build lumen.elf → pack lumen.hpkg
├── VERSION lumen's version
├── GLYPH_VERSION pinned glyph toolkit artifact version
├── src/
│ ├── main.c entry point + event loop + top bar/menu/shortcuts
│ ├── compositor.c/.h window management + dirty-rect compositing
│ ├── lumen_server.c/.h AF_UNIX window-protocol server
│ ├── cursor.c/.h software cursor with save-under
│ ├── terminal.c/.h in-process dropdown terminal (glyph_term wrapper)
│ └── about.c/.h "About Aegis" window
├── pkg/
│ └── caps.d/lumen capability policy (installed to /etc/aegis/caps.d)
├── assets/
│ ├── Inter-Regular.ttf desktop UI font
│ ├── JetBrainsMono-Regular.ttf monospace/terminal font
│ ├── logo.raw LoricaOS logo (width/height header + RGBA)
│ └── wallpaper.raw default desktop wallpaper
├── tools/
│ ├── fetch-glyph.sh fetch + unpack the pinned glyph toolkit artifact
│ └── pack.sh build + sign lumen.hpkg
└── docs/
└── ARCHITECTURE.md compositor internals in depth
Build outputs (lumen.elf, lumen.hpkg, lumen.hpkg.sig), the fetched
toolkit/, and the vendor/ cache are git-ignored.
docs/ARCHITECTURE.md— the compositing pipeline, the protocol exchange, input/cursor handling, the dropdown terminal, and the About window, in detail.LoricaOS/glyph— the toolkit lumen builds against and the home of the window protocol.LoricaOS/Aegis— the kernel.