Skip to content

pgsdf/UTF

Repository files navigation

Unified Temporal Fabric

UTF is an integration repository for the PGSDF multimedia substrate. It brings together the spatial, semantic, audio, and input layers of the system under a single workspace and provides the cross-cutting infrastructure — shared protocol constants, a unified event schema, session identity, and a clock publication interface — that makes them a coherent fabric rather than four independent daemons.

The temporal coordination layer is chronofs: it aligns audio, visual, and input domains against a single monotonic clock driven by audio hardware, eliminating drift between subsystems as a structural property of the architecture. chronofs is implemented and complete; the remaining work is moving the clock-writer role from semaaud to audiofs (per ADR 0003), which completes when audiofs is built.

UTF targets PGSD, a distribution founded on FreeBSD. Earlier development used GhostBSD as a convenient FreeBSD-derivative test host; that is no longer the case. PGSD-on-FreeBSD is the single supported target going forward. Some verification artefacts in the repository's history were produced on GhostBSD VMs and bare-metal GhostBSD machines while that was the active development venue; the substantive findings transfer to stock FreeBSD because GhostBSD inherits FreeBSD's HID stack and kernel configuration, but the project no longer targets GhostBSD as a deliverable platform. PGSD will ship its own kernel configuration that omits drivers superseded by inputfs (currently hms, hkbd, hgame, hcons, hsctrl, utouch).


Current state (2026-05-19)

What is built and verified:

  • chronofs is complete (C-1 through C-5 closed): clock module, event stream buffers, resolver, audio-driven frame scheduler, chrono_dump. The temporal coordination layer exists.
  • inputfs is the production input substrate. AD-2 closed 2026-05-17: the legacy semainputd daemon is retired, Phase 2.5 verified on bare-metal hardware, semadrawd consumes inputfs directly. The input cutover is done, not pending.
  • drawfs, semadraw, semaaud, shared/ infrastructure (event schema, session identity, clock region, protocol constants) are in place.
  • inputfs gesture library (libsemainput) is the path for reusable input semantics; that move is recorded and in progress.

What is decided but not yet built:

  • audiofs (AD-3) is the current load-bearing work. Architecture decided across ADRs 0002-0008; F.0 closed (ADR 0009 Accepted 2026-05-19). The work is gated: it requires the gap-and-governance audit against FreeBSD's snd(4) (per ADR 0008) to be performed and verdict rendered by an independent reviewer before implementation begins. The audit enumeration draft and contract drafts (AUDIO_STATE, AUDIO_EVENTS) exist as verification-stage artifacts pending fresh-context review. AD-3 is not scheduled and will not be scheduled until the gate clears.
  • The clock writer is transitioning from semaaud to audiofs per ADR 0003. shared/CLOCK.md's prose still attributes the writer to semaaud; that is the legacy state, superseded by ADR 0003. The transition completes when audiofs is built.

What is deliberately not yet started:

  • Desktop environment, session manager, application ecosystem (NDE, GNUstep-style layer). These wait on the substrate being singular and stable, which means AD-3 cleared. Starting upper layers before the audio substrate consolidates would risk rewrite. This is sequencing, not absence of intent.

The remaining substrate work is therefore narrow and specific: clear the AD-3 gate (independent audit verification, then implementation against verified contracts on confirmed-target hardware). No new architectural decisions are open at the substrate level; the open work is execution against decided architecture.


Substrate and distribution

This monorepo contains two architecturally distinct projects that share a development workspace for convenience:

UTF is the substrate. Kernel modules and userland services that provide a unified temporal fabric for input, audio, graphics, and time. UTF has stable contracts (the kernel/userland boundary, the shared-memory regions under /var/run/sema/, the IPC protocols) that a different distribution could in principle adopt and build a different desktop on top of. UTF has no opinion about users, sessions, login, or desktop environment; it deals in uids from getpeereid(3) and surfaces from clients.

UTF userland services follow the sema- prefix convention: semadrawd, semaaud, semainput. Kernel modules follow the *fs convention: drawfs, inputfs, chronofs, and the planned audiofs (architecture decided in ADRs 0002-0008; F.0 closed, closure bookkeeping reconciled by ADR 0009 (Accepted 2026-05-19); AD-3 unscheduled and gated on the owed maintenance model and unperformed audit, see BACKLOG).

PGSD is the distribution. A FreeBSD distribution built on UTF, focused on scientific and METOC visualization. PGSD makes choices that UTF deliberately does not: which desktop environment to ship (NDE), how to manage user sessions and login (pgsd-sessiond), what application layer (LT, GNUstep), what default applications, what kernel configuration. A different distribution built on UTF could make different choices in any of these areas.

PGSD distribution-layer components follow the pgsd- prefix convention to make the layer split visible at the process level: pgsd-sessiond for session management, with future pgsd-*d daemons as the distribution grows. Reading ps output on a PGSD system tells the operator which layer each process belongs to.

PGSD currently has three component tracks, all in BACKLOG:

  • NDE (Native Desktop Environment): surface manager, system bar, launcher, X11 compatibility bridge. PGSD's default desktop. See ## NDE in BACKLOG.md.
  • LT (Layer Tree): animation engine, GNUstep backend, the Quartz-equivalent application layer. See ## Long-term: Quartz Equivalent on UTF in BACKLOG.md.
  • SM (Session Management): graphical login, session lifecycle, screen lock, idle and power management. Opened 2026-05-10 from work originally scoped under NDE. See ## SM: Session Management in BACKLOG.md. Design for the initial daemon (pgsd-sessiond) is at pgsd-sessiond/docs/adr/0001-design.md.

The split is reflected in BACKLOG categorisation. UTF substrate work is filed under ## Architectural Discipline with AD-* identifiers. Distribution-layer work is filed under the relevant track section with track-prefixed identifiers (NDE-*, LT-*, SM-*).

The Architecture diagram below shows the UTF substrate only. Distribution-layer components consume the substrate via its stable contracts and are not depicted there; their architecture lives in the per-track design documents.


Architecture

                    Applications
                         |
                    libsemadraw
                    (SDCS streams)
                         |
                    semadrawd ----+----- semaaud
                    (compositor)  |      (audio)
                         |        |        |
                      drawfs      |    chronofs
                    (/dev/draw)   |    (temporal fabric)
                         |        |        |
                      hardware    |    inputfs <-- semainput
              (EFI framebuffer /  |    (HID kernel  (retired daemon;
               Vulkan / X11)      |     substrate)   libsemainput only)
                                  |
                              hardware
                              (USB HID)

AD-2a Phase 3 (2026-05-08) already deleted the semainputd daemon and promoted its gesture logic into libsemainput, now consumed by the compositor and by clients. What remains is the Stage E cutover: until it lands, semadrawd still reads input via the legacy evdev path; after it, semadrawd reads directly from the inputfs ring at /var/run/sema/input/events and the drawfs_inject adapter is removed. The daemon deletion is done; the runtime-path cutover is not.

Component Role
drawfs Kernel graphics transport. /dev/draw character device, surface lifecycle, mmap-backed pixel buffers, EFI framebuffer blit.
semadraw Semantic rendering. SDCS command streams, semadrawd compositor, software and hardware backends.
semaaud Audio daemon. OSS output, policy-controlled stream routing, preemption, fallback.
semainput Retired daemon directory. The semainputd evdev daemon was retired 2026-05-08 (AD-2a Phase 3); only the libsemainput gesture library remains. The evdev/Stage E cutover in semadrawd is separate and still outstanding; see BACKLOG.
inputfs Kernel input substrate. Attaches at hidbus, parses HID reports, publishes input state and events to userspace via shared memory under /var/run/sema/input/. The future input source after AD-2.
shared/ Protocol constants, event schema, session identity, clock interface.
chronofs Temporal coordination layer. Audio-driven frame scheduler, ring buffers, clock publication.
audiofs Kernel audio substrate. Planned replacement for the OSS dependency, owning audio hardware end to end and writing the audio clock chronofs reads. Architecture decided (ADRs 0002-0008); F.0 closed (closure bookkeeping reconciled by ADR 0009, Accepted); implementation gated, see BACKLOG AD-3.

Repository Layout

UTF/
├── drawfs/          kernel module and protocol (FreeBSD 15)
├── inputfs/         kernel input substrate (FreeBSD 15)
├── audiofs/         kernel audio substrate (architecture decided ADRs 0002-0008; F.0 closed, ADR 0009 Accepted; no code yet)
├── semadraw/        semantic rendering daemon and client library
├── semaaud/         audio routing daemon
├── semainput/       input classification and gesture daemon
├── shared/          cross-cutting constants, schema, and interfaces
├── chronofs/        temporal coordination layer
├── pgsd-sessiond/   PGSD distribution layer: graphical login daemon (design only, see ADR)
├── pgsd-kernel/     PGSD distribution layer: kernel config omitting inputfs-superseded drivers (AD-8)
├── s6/              UTF supervision tree, installed to /var/service/utf/ (AD-20)
├── scripts/         rc.d scripts, devfs rulesets, periodic jobs, stack up/down helpers
├── BACKLOG.md       consolidated project backlog (source of truth)
└── docs/
    ├── Thoughts.md                  chronofs architecture
    ├── PROTOCOL_MISMATCH_FINDINGS.md  integration audit (resolved)
    └── sessions/                    per-session working memos

Subsystems

drawfs

A FreeBSD kernel module that exposes /dev/draw. Clients open the device, negotiate via a binary framed protocol, create surfaces backed by swap memory, map them with mmap(2), render into the pixel buffer, and present. The kernel is not a compositor; policy lives in userspace.

Phase 1 is complete: surface lifecycle, mmap, framed binary protocol, input event injection. Phase 2 adds EFI framebuffer support: the module maps the UEFI GOP framebuffer at load time and exposes DRAWFSGIOC_BLIT_TO_EFIFB and DRAWFSGIOC_GET_EFIFB_INFO ioctls, allowing semadrawd to render directly to the physical display on bare metal FreeBSD without a GPU driver. Verified on Intel Bay Trail (1024x768) and Apple iMac (3840x2160).

DRM/KMS support is a skeleton, gated behind DRAWFS_DRM_ENABLED at build time and strictly optional. The EFI framebuffer path is the default bare metal display path and requires no DRM.

See drawfs/docs/ for protocol specification, architecture, and build instructions.

semadraw

A userspace semantic graphics system. Applications link against libsemadraw and produce SDCS (Semantic Draw Command Streams) — binary sequences of drawing operations that express intent rather than GPU commands. semadrawd owns surface composition and presentation. Backends include software (reference), Vulkan, DRM/KMS, X11, Wayland, and drawfs.

semadraw-term is a native terminal emulator built on libsemadraw. It supports multi-session operation (up to 8 sessions), a session status bar, VT100/xterm-256color emulation, auto-detection of display size via DRAWFSGIOC_GET_EFIFB_INFO, and font scaling for HiDPI displays. It runs on bare-metal FreeBSD via the drawfs EFI framebuffer backend and on FreeBSD with Xorg via the X11 backend.

See semadraw/docs/ for the SDCS specification, architecture, and API overview.

semaaud

An audio routing daemon for FreeBSD. Clients connect via a Unix socket and submit PCM streams with a JSON header. The daemon applies a policy grammar (allow, deny, override, group exclusivity, fallback routing) and writes accepted streams to the OSS device. Two named targets (default and alt) support concurrent routing scenarios.

See semaaud/docs/ for the policy specification and roadmap.

semainput

Historically the legacy userspace input daemon: it read evdev devices, classified them by capability fingerprint, aggregated physical devices into stable logical identities, applied pointer smoothing, and emitted structured JSON-lines events for semantic input (mouse, keyboard, touch) and gestures (two-finger scroll, pinch, three-finger swipe, drag, tap).

That daemon (semainputd) was retired on 2026-05-08 under AD-2a Phase 3: the daemon binary, the semainput/src/ source tree, the rc.d shim, and the s6 service directory were deleted. Its gesture-recognition logic was not lost; it had been migrated into libsemainput (AD-2a Phase 2.3), which is now the only userland surface remaining under semainput/ and is consumed directly by semadrawd. See semainput/README.md and semainput/libsemainput/README.md for current state.

The daemon retirement and the evdev cutover are distinct steps and only the first is done. inputfs (below) is the kernel-side substrate that replaces semainput's role; its substrate landed (Stages A through D). The remaining cutover, which removes semadrawd's evdev reader and the drawfs_inject adapter so UTF's runtime input path no longer touches evdev at all, is Stage E (tracked in BACKLOG.md; also referenced as the inputfs proposal's Stage E and the AD-2a cutover). Stage E is unblocked but not yet executed; it remains the next intentional act for the input stack.

See semainput/docs/ for architecture and system interface documentation of the historical daemon; it is retained for the design record and does not describe anything currently running.

shared/

Protocol constants for all three binary protocols (drawfs, semadraw IPC, SDCS) in a single JSON source of truth. A code generator emits C headers and Zig constant declarations. A unified event schema, session identity module, and clock publication interface serve all four daemons.

chronofs

A temporal coordination layer that makes time a first-class addressable medium across all four subsystems. The audio hardware clock drives a shared monotonic counter. All events carry an audio-sample timestamp. The frame scheduler queries scene state at a target audio position rather than wall time, producing drift-free AV synchronization by construction.

Implementation is complete across all dependency waves: clock publication (/var/run/sema/clock), ring buffers, resolver, audio-driven frame scheduler, and chrono_dump diagnostic tool.

See docs/Thoughts.md for the full design and chronofs/BACKLOG.md for the implementation history.

inputfs

A FreeBSD kernel module that owns the HID input path. inputfs attaches at hidbus, parses HID report descriptors, registers interrupt callbacks, and publishes input state and events to userspace via shared-memory regions under /var/run/sema/input/. The state region carries the materialised view (current pointer position, device inventory, per-device keyboard and touch state) updated under a seqlock; the event ring carries an ordered delta stream consumable via the EventRingReader from shared/src/input.zig.

Stage A delivered the design (proposal, foundations, ADRs 0001 through 0011, four byte-level companion specs). Stage B delivered HID attachment, descriptor parsing, interrupt handler registration, and per-device role classification. Stage C delivered userspace publication of the state region and event ring, along with the inputdump diagnostic CLI and a verification protocol at inputfs/docs/C_VERIFICATION.md that runs 26 automated checks plus a manual mouse-and-button checklist. Stage D (focus routing and coordinate transform) landed across eight sub-stages (D.0a through D.6); the parser was hardened by AD-9's fuzzing work before the cutover proceeds.

inputfs replaces semainput (the userspace evdev daemon) on the PGSD target. The substrate is built; the cutover that retires the evdev reader, the drawfs_inject adapter, and the semainputd daemon is Stage E, tracked as AD-2a in BACKLOG.md and the next intentional act for the input stack. Until AD-2a lands, both paths coexist (hw.inputfs.enable tunable from Stage D); after AD-2a, evdev is no longer present in any UTF code path. UTF runs on inputfs only, with no fallback, by deliberate commitment to the discipline at docs/UTF_ARCHITECTURAL_DISCIPLINE.md.

inputfs also feeds FreeBSD's vt(4) console keyboard input via a bridge driver (the inputfs_kbd kbd-layer driver inside the inputfs module). Each HID keyboard inputfs attaches becomes a slave of kbdmux, which feeds vt(4)'s console keystroke pipeline. This means console login at ttyv0 works on a PGSD kernel that has no legacy hkbd loaded (per AD-8's WITHOUT_MODULES discipline). The bridge is gated by hw.inputfs.kbdmux_bridge (default 1) and documented in ADR 0019 (inputfs/docs/adr/0019-kbdmux-bridge.md).

See inputfs/docs/ for the proposal, foundations, ADRs, byte-level specs, and verification protocols.

audiofs

A planned FreeBSD kernel module that will own audio hardware end to end, replacing the OSS dependency semaaud currently writes to. audiofs is the audio-side counterpart to inputfs: a kernel substrate that owns the device and publishes to userland, rather than a userspace daemon talking to a legacy device node. It also becomes the kernel-side writer of the audio hardware clock that chronofs reads, closing the temporal fabric on the side it originates from.

The work is tracked as AD-3 in BACKLOG.md (Stage F of audiofs/docs/audiofs-proposal.md). The architectural decision phase, F.0, is closed: ADRs 0001 through 0008 record the decisions (OSS coexistence, clock writer, mixer location, userland architecture, the snd(4) full replacement, the physics/semantics boundary, and Stage F scope), and ADR 0009 reconciles the F.0 closure bookkeeping by recording how each of the proposal's six open questions was dispositioned. No kernel code exists yet, by deliberate discipline: ADR before code, without exception.

F.0 being closed does not make AD-3 startable. Per ADR 0008, implementation is gated on two preconditions that remain owed: the long-term per-chipset maintenance model, and the gap-and-governance audit (audiofs/docs/snd4-gap-governance-audit.md), which by ADR 0008's sequencing runs strictly before any audiofs code. AD-3 is architecturally decided, sequenced, and gated; it is not scheduled.

See audiofs/docs/adr/ for the decision record and audiofs/docs/audiofs-proposal.md for the Stage F design.


System Requirements

UTF targets PGSD-on-FreeBSD 15.0-RELEASE. Beyond a working FreeBSD installation, two system-level configuration items are required for the daemons and kernel modules to operate correctly.

For an end-to-end walkthrough of installing UTF on a fresh FreeBSD system, including hazards that have actually broken installs (in particular: do not add inputfs_load="YES" to /boot/loader.conf), see INSTALL.md.

/var/run must be tmpfs

Several UTF components publish state to userland via shared-memory regions under /var/run/sema/: the audio clock at /var/run/sema/clock (semaaud), the session token at /var/run/sema/session, and the inputfs state region at /var/run/sema/input/state (Stage C onward). These files are recreated on every daemon or module load and are meaningful only for the current boot.

FreeBSD convention is that /var/run is volatile. Some installations leave /var/run on the same filesystem as the rest of /var, which makes shared-memory publication writes more expensive and leaves stale region files persisting across reboots until the next module load truncates them. The supported configuration is to mount /var/run as tmpfs by adding the following line to /etc/fstab:

tmpfs /var/run tmpfs rw,mode=755 0 0

After editing fstab, either reboot or run sudo mount /var/run to activate. Confirm with mount | grep /var/run (expect a tmpfs on /var/run line). The inputfs verification protocol assumes this configuration; running on a non-tmpfs /var/run is unsupported.

PGSD kernel configuration

PGSD ships a kernel that omits drivers superseded by inputfs: hms, hkbd, hgame, hcons, hsctrl, utouch, hpen, and the hidmap framework. Stock FreeBSD compiles hms and hkbd statically into GENERIC and produces .ko modules for the rest, which causes the HID transport layer to attach the legacy drivers ahead of inputfs at boot or on USB events. Running the inputfs verification protocols on stock FreeBSD requires either booting the PGSD kernel or moving the competing .ko files out of /boot/kernel/ and regenerating linker.hints (see BACKLOG.md AD-8 for the durable answer via WITHOUT_MODULES in /etc/src.conf).


Multi-user deployment

UTF's substrate publication files default to mode 0600 owned by root:wheel, per ADR 0013 (inputfs/docs/adr/0013-publication-permissions.md). On a single-user dev or bench system, no further configuration is needed: all UTF daemons run as root by default, all consumers run via sudo, and the substrate is uniformly accessible within that root context.

On a multi-user system, operators relax the defaults via the operating system rather than via UTF-specific configuration. Two layers control the result:

Kernel-side (inputfs). Three sysctl tunables apply at module load and at runtime:

sysctl hw.inputfs.dev_uid=0
sysctl hw.inputfs.dev_gid=$(getent group operator | cut -d: -f3)
sysctl hw.inputfs.dev_mode=0640

These can also live in /boot/loader.conf for boot-time defaults:

hw.inputfs.dev_uid=0
hw.inputfs.dev_gid=920
hw.inputfs.dev_mode=0640

The sysctls take effect for files created after the change. Already-open files retain the attributes they were created with. Reload the inputfs module to refresh.

Userspace (semaaud, semadraw, etc.). Daemon process group is set via /etc/rc.conf:

semaaud_user="root"
semaaud_group="operator"
chronofs_user="root"
chronofs_group="operator"
semadrawd_user="root"
semadrawd_group="operator"

Daemon umask is set per-script (see the existing rc.d entries for the pattern). Setting umask 027 together with the explicit 0o600 UTF passes to createFile produces files at mode 0600; setting umask 037 with explicit 0o640 produces group-readable files. UTF cannot expose more permissions than its explicit mode, so the umask only ever restricts further.

Authorized consumers are added to the chosen group via pw groupmod operator -m <user>. After this, the user can run inputdump, chrono_dump, and similar diagnostic tools without sudo.

drawfs's cdev follows the same convention with parallel sysctls (hw.drawfs.dev_uid, hw.drawfs.dev_gid, hw.drawfs.dev_mode).


Build

Each subsystem builds independently. Use start.sh to build and run all daemons together.

The canonical source location on a deployed system is /usr/local/src/UTF/, aligned with hier(7). See INSTALL.md Step 3 for the recommendation; developers cloning to a workspace folder need no special handling because all build scripts resolve paths relative to themselves.

drawfs requires FreeBSD kernel sources:

cd drawfs
./build.sh install
./build.sh build
./build.sh load
./build.sh test

semadraw, semaaud, semainput, chronofs require Zig 0.15 or newer:

cd semadraw && zig build
cd semaaud  && zig build
cd semainput && zig build
cd chronofs && zig build

Start all daemons and terminal (drawfs backend, auto-detects display resolution):

sudo sh start.sh                  # full stack + terminal at scale 2
sudo sh start.sh --scale 4        # full stack + terminal at scale 4
sudo sh start.sh --no-term        # daemons only, no terminal

Stop everything:

sudo sh start.sh --stop

Run the terminal emulator manually:

sudo semadraw/zig-out/bin/semadraw-term            # auto-detects display size
sudo semadraw/zig-out/bin/semadraw-term --scale 2  # HiDPI
sudo semadraw/zig-out/bin/semadraw-term --scale 4  # 4K/5K

Graphics Backends

Backend Use case Requirements
drawfs (EFI) Bare metal FreeBSD console UEFI firmware, drawfs.ko loaded
X11 FreeBSD with Xorg libX11
Vulkan GPU-accelerated rendering Vulkan driver
software Testing and reference None
DRM/KMS Optional GPU modesetting drm-kmod, build with DRAWFS_DRM_ENABLED

The EFI framebuffer path works on any UEFI machine regardless of GPU age or driver availability, including hardware with no Vulkan support and no working drm-kmod port.


Status

Component Status
drawfs Phase 1 complete. Phase 2 (EFI framebuffer) complete. DRM/KMS skeleton, opt-in only.
semadraw drawfs backend operational. semadraw-term functional on bare metal and X11.
semaaud Phase 12 (durable policy) complete.
semainput semainputd daemon retired 2026-05-08 (AD-2a Phase 3). Only libsemainput remains, consumed by semadrawd. The evdev/Stage E cutover in semadrawd is separate and still outstanding (see inputfs row).
inputfs Substrate complete (Stages A, B, C, D landed on PGSD-bare-metal; eight Stage D sub-stages D.0a through D.6; parser hardened by AD-9). Stage E cutover (AD-2a) is the next intentional act and removes evdev from the UTF tree.
audiofs Architecture decided in ADRs 0002-0008. F.0 closed: closure bookkeeping reconciled by ADR 0009 (Accepted 2026-05-19), which resolves a contradiction between ADR 0001's closure criterion and ADR 0008's completion assertion. No kernel code by discipline. AD-3 architecturally decided, sequenced, and gated; not scheduled. Blocked on owed maintenance model and the unperformed gap-and-governance audit.
shared/ Protocol constants, generator, event schema, session identity, clock interface: all complete.
chronofs Complete. Audio-driven frame scheduler operational.

The next intentional act for the input stack is Stage E. inputfs has owned HID at the kernel level since Stage D landed; semainput, the userspace evdev daemon it replaces, is still in the tree because the cutover hasn't been executed yet. AD-2a executes that cutover: switch semadrawd's default backend to inputfs, delete the evdev adapter and the drawfs_inject adapter, retire semainputd, promote gesture.zig into a userland library libsemainput, and remove evdev-loading instructions from user-facing setup docs. After AD-2a, no UTF code path uses evdev. AD-2b (per-user pointer smoothing, design landed in ADR 0015) is independent of AD-2a and may proceed in either order.


License

BSD 2-Clause. See LICENSE.

Copyright (c) 2026 Pacific Geoscience Systems Development Foundation.

About

UTF is an integration repository for the PGSDF multimedia substrate.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors