- Rust 99.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Merge the selected feature/native-cli history with an explicit two-parent commit so main retains the development lineage and the validated source snapshot. The resulting tree is identical to the selected feature commit; no dependency pins or runtime behavior are changed by this merge. Previous main: |
||
| .github | ||
| src | ||
| .editorconfig | ||
| .gitignore | ||
| .markdownlint-cli2.yaml | ||
| ARCHITECTURE.md | ||
| Cargo.toml | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| LICENSE | ||
| linker.ld | ||
| README.md | ||
| ROADMAP.md | ||
| rustfmt.toml | ||
| SECURITY.md | ||
ttyd
ttyd owns trusted virtual-terminal session state, PTY master endpoints, input
focus, and ordinary versus trusted display routing for native EriX sessions.
EriX is a clean-room, capability-based microkernel operating system written entirely in Rust.
See:
- docs for system architecture and the technical manual
- lib-tty for PTY state contracts
- lib-capabi for exact service routes and startup capability roles
Purpose of This Repository
This repository provides an allocation-free semantic core and freestanding
syscall executable for trusted terminal sessions. The runtime authenticates
every startup endpoint before calling the core and constructs its typed state
from one deployment-patched arena sized for the authenticated
TtydStartupConfigV2 record (legacy V1 selects both mirrored paths).
Authenticated tty_fbcon and tty_serial flags select terminal routes, never
logd output. Both paths with tty_mirror share one session; without mirroring,
the private root bootstrap route binds distinct realms to framebuffer and serial
seats. Each seat owns its focus, input, ordinary output and trusted review.
Disabled routes must be absent at startup and are never polled afterward. No
enabled path means no session admission. Matched-image VM validation of these
new combinations is pending Integration; startup performance remains separate.
The maintained responsibilities are:
- assign generational session and PTY identities
- bind authenticated realm identity to session records as data, not authority
- own PTY master-side lifecycle and resize state
- permit mode changes only through the active seat-focused owner's lifecycle endpoint, never an inherited input reader; exact input context is read-only
- atomically expose canonical type-ahead when entering raw mode, without duplicate echo; queued input survives restoration and capacity failure retries without mutation; raw physical arrow/delete keys retain complete CSI sequences
- own a typed lossless input stream and output stream per PTY, with canonical line-editing storage in a third physically distinct deployment pool
- serve slave input only through its authenticated read receiver and shared stdout/stderr only through its authenticated write receiver, with exact stream status, orderly direction close, and forward-only resynchronization for sequential PTY readers and shared PTY writers
- keep shared session routes, policy and trusted state live when one inherited stream caller disappears during its reply; kernel retirement of that exact pending CALL does not prove session-owner death or authorize receiver removal; already committed reads/writes are not rolled back or replayed
- capture the focused session when each input event is accepted
- retain ordinary input and per-session descriptive PTY events in one authenticated caller-sized ordinary arena, physically separate from secure attention
- validate normalized ordinary input, apply the signed deployment's set-1 key map and PTY terminal mode, and commit canonical editing plus echo only after every affected destination can accept the complete transaction
- convert signal-enabled Control-C input in place into canonical
INTevents, never PTY text, and coalesce only an already-pending identical standard condition - preview PTY output without consuming it, reserve ordinary dual-renderer storage, apply output newline mapping, and only then advance the owner stream
- retain ordinary output until every selected renderer acknowledges it; framebuffer delivery is one exact light-gray terminal stream and never promotes an internal echo/output queue record into an implicit display line
- on orderly process exit, deny new session calls while draining every already accepted PTY byte through selected ordinary renderers before authority cleanup;
- retain the existing orderly HANGUP reply until that session's accepted output drains and its selected framebuffer acknowledges an empty ordinary FLUSH; serial acknowledgement already follows the exact synchronous driver write; explicit session-owner failure still cancels immediately
- retain trusted chrome separately from ordinary output; powerbox reviews have private framebuffer and serial delivery, while grant inspection remains framebuffer-only
- prepare immutable, route-bound renderer calls and advance only the exact opaque output record named by a correlated service reply
- retain at most one accepted renderer call in the sole private client buffer, yield one cooperative scheduler turn after acceptance and each pending poll, poll that original frame without resubmission, and continue lifecycle and other endpoint intake between polls; no renderer timeout, capability discovery, or new authority is introduced
- keep each trusted display active until its renderer accepts a canonical empty release; cancellation drains accepted calls and releases even zero-progress producer retries without advancing a successor's output
- bind an explicit operation/lifetime policy and deny-or-prompt exceptional default to each authenticated session
- answer private launchd and powerboxd policy requests only after exact session, realm, subject, selector, and object correlation
- enter prompt-scoped secure attention automatically for the focused requesting session, after previously accepted physical input drains; show the resolved executable, exact requested operation, escaped scope-bound object selector, lifetime and identity evidence through private framebuffer and serial review
- capture authenticated physical or serial
yorn, followed by Enter on the same input transport, only after that renderer acknowledges the complete exact review; show both bindings and never replay captured answers into the shell - require a post-visibility empty authenticated serial read before arming consent; discard older in-flight replies, buffered batches and post-answer tails across prompt/focus changes. Trusted serial bytes bypass public log history and exclude ordinary writers while modal. This trusts the console operator, not arbitrary PTY text or a cryptographically authenticated remote user
- mirror receipt-free descriptive grant lifecycle rows from powerboxd on its existing private route, and render only the focused session's canonical active-grant snapshot after exact-generation secure attention
- accept receipt-free ordinary and trusted inputd pushes without accepting a caller-supplied session or prompt correlation; bind both from ttyd-owned focus and pending-prompt state
- poll UART input only through the authenticated seriald session route and feed it through the same focused canonical line discipline outside modal prompts; serial bytes cannot create generic trusted-control events. Accepted calls keep their own exact buffer and are polled once per service turn, so a delayed serial reply leaves rendering, lifecycle cleanup, and physical input live. Empty or busy reads skip seven turns before another input submission; pending replies and buffered bytes remain eligible every turn
- accept next/previous focus switching and emergency session kill only after secure attention under the exact active input-policy generation; derive the affected session from ttyd-owned focus and never from inputd data; when a trusted switch is followed by killing that focus, restore the exact prior live focus retained by ttyd rather than selecting from caller input
- create each session lifecycle, PTY input, and PTY output endpoint through one narrow endpoint factory; retain only exact receive aliases and delegate the lifecycle alias plus only the request-selected stdin, stdout, and stderr send aliases to authenticated procd
- bind those private retained receivers with one zero-capacity nonblocking
receive before session commit or sender export, requiring exactly empty
RETRY; this also covers root bootstrap sessions. No message or buffer is retained, failed priming rolls back the existing exact ownership ledger, and dropping temporary sources leaves the retained receiver binding live - accept one root-authenticated bootstrap-owner request containing an explicit subset of native-command filesystem operations, read-only object metadata, exact-entry metadata-read and touch, plus session inspection; exclude generic metadata mutation and process control, bind deny-all to one-shot and nonempty subsets to job lifetime, authenticate a deny-or-prompt exceptional default, and return only a lifecycle sender plus descriptive policy identity
- accept only context read, primary-PTY state read, exact-cursor PTY event read, or hangup on an exact retained session-lifecycle receiver; descriptive reads return only route-selected state, while hangup replies before retiring the receiver and advances all three retained authorities through retryable cleanup without polling empty capability slots
- preflight resize and hangup event retention before semantic mutation, emit
canonical
WINCHandHUPidentities, and erase every queued PTY event before lifecycle authority bookkeeping leaves the session record - track exact runtime capability slots through retryable teardown cleanup
- return explicit backpressure without dropping events, output, or authority
Clean-Room Policy
EriX follows a strict clean-room philosophy:
- No external source code may be copied.
- No external Rust crates are allowed.
- No code generation tools that embed third-party code.
- All code must be authored within the project.
Violations will result in rejection of the contribution.
License
All EriX repositories are licensed under the ISC License.
Development Model
The semantic core is no_std, forbids unsafe code, and denies missing public
documentation. The freestanding executable confines unsafe code to exclusive
views of its linker-provided message buffers and state arena. Every mutable
table and queue is caller-owned. Capacities come from authenticated deployment
configuration and are never private protocol ceilings.
Real capabilities remain owned by runtime code. Copyable slot values in the core are cleanup bookkeeping only: teardown moves one slot at a time into a visible FIFO, and the runtime acknowledges the exact row only after deleting that capability.
Status
Implemented:
- deployment-sized session, PTY, input, output, and cleanup storage validation
- exact per-PTY stream records over separately authenticated input, canonical-line, and output byte pools, with remainder bytes partitioned deterministically and no private per-PTY capacity
- exact startup transfer-policy, live-inventory, and
TTY1tail validation - generational identities and multi-session focus switching
- authenticated process-session provisioning transfers focus to the new PTY only when exporting new stdin and after committing its complete retained endpoint tuple; inherited-input, lifecycle-only, and output-only children preserve existing ordinary and trusted focus during execution, retirement, and publication rollback, so subsequent prompted commands retain shell input
- cross-role and live authority-slot alias rejection
- one caller-sized ordinary input/control arena with independent global-input and per-session PTY cursors, plus a physically distinct secure-attention queue
- lossless dual-renderer ordinary output and isolated trusted prompt output
- terminal hangup, orderly exit-time output drain, abnormal cancellation, retryable cleanup, and stale-ID tests
- endpoint-selected receipt-free session-context reads that expose only the current active route's non-authoritative session and realm values
- endpoint-selected receipt-free PTY event reads with exact cursor rejection,
typed retry, canonical
INT/WINCH/HUPvalues, permitted standard-signal coalescing, and response-storage preflight before consumption - build-gated integration fixtures that seed one service-global canonical
INT: trap dispatch uses the first authenticated event read, whilewaitfirst observes one exact empty retry and injects on the next read of that same lifecycle-selected session; both consume the one-shot event through the existing bounded cursor transaction, and ordinaryttyd-runtimebuilds seed nothing and gain no route or authority - canonical nonzero
u64realm identity shared with native launch and powerbox IPC contracts - authority-free, peer-distinct policy routes: launchd receives the complete active-session ceiling while powerboxd receives one exact routine or exceptional object decision
- transactional prompt admission, exact physical/serial answers, prompt-spam denial, and cancellation on session teardown or powerboxd peer death
- exact receipt-free IPC framing for the complete launchd ceiling, powerboxd object decisions, deferred prompts, and later stored prompt replies
- exact inputd call/reply framing, monotonic event rejection, focus capture, complete-review visibility gating, and internally correlated prompt approval or denial; automatic attention grants no unrelated trusted-control privilege
- secure-attention-bound next/previous focus traversal over live sessions and emergency focused-session kill with exact prompt cancellation before normal retryable authority cleanup
- caller-sized active-grant mirror storage, exact generational active/remove reconciliation, focused canonical grant review on framebuffer-only trusted chrome, prompt exclusion, toggle dismissal, and session/peer-death erasure
- exact fbcond ordinary/trusted and seriald ordinary-output/input adapters with monotonic identity, partial progress, immutable retry, stale-reply rejection, trusted-display serialization, and retained trusted release
- a
ttyd_entryexecutable path that completes the startup call only after live kernel inventory validates all ten exact endpoint transfers and the endpoint-factory primary control capability - a deployment-patched, page-aligned state arena that initializes every typed region before constructing the semantic state and rejects undersized or mismatched storage
- post-mount execution from the authenticated ext4 runtime graph through
loaderd, dynlinkd, and procd, with the binary and package-local library absent
from
boot.img - fair nonblocking intake across lifecycle, per-session PTY input/output, ordinary/trusted input, launchd and powerboxd policy, and deferred powerbox prompt routes
- immutable renderer-call retry through framebuffer and serial pre-acceptance backpressure, plus a one-accepted-call submit/poll state machine that keeps lifecycle intake live without resubmission, and exact FIFO capability deletion before cleanup records are consumed
- authenticated procd session provisioning that preflights one complete retained slot tuple, creates three endpoints, attenuates ttyd's aliases to receive-only, commits semantic state only after every alias exists, and returns lifecycle plus selected send-only aliases with selected stdout and stderr naming one output object
- complete transaction cleanup before a typed failure reply, unpublished-state rollback when procd dies during reply, and fail-stop behavior whenever exact capability retirement cannot be proved
- fair dynamic intake for every retained session-lifecycle receiver and every active-session input/output receiver; role-restricted, receipt-free ordered-stream framing; shared stdout/stderr cursor resynchronization; caller-death-safe peer-failure commitment; non-callable orderly draining through both renderers; and automatic one-step-at-a-time teardown cleanup
- stream-byte erasure before unpublished transaction capability retirement, hangup, peer-death cleanup, or final receiver staging, while retaining exact semantic cleanup evidence whenever capability deletion is uncertain
- fair bounded ordinary terminal I/O: normalized set-1 key translation, canonical append/erase/submit, raw input, terminal-mode newline mapping and echo, transactional dual-renderer output draining, orderly EOF closure, and erased caller-owned scratch without a trusted-route API
- signal-enabled Control-C conversion that reuses the retained input slot, preserves losslessness even when the shared arena is full, and inserts no control byte into the PTY stream
Powerboxd-side active-grant publication, procd-side exact user-child
installation, and lifecycle-focused VM validation remain in progress.
The executable is installed and launched from ext4 and is not added to
boot.img.
Governance Principles
ttyd governs terminal sessions and trusted UI state, not process launch,
approval lifetime, executable loading, device input policy, or rendering.
- Endpoint possession is authenticated before a semantic route method is used.
- No caller gains authority from a session, realm, PTY, or numeric slot value.
- Ordinary PTY and public serial writer bytes cannot construct attention or consent. Only authenticated physical inputd and private seriald RX reach their separate modal capture adapters; each uses its own complete-review visibility.
- Inputd cannot assert a ttyd session or powerbox correlation. Manual trusted controls require the exact secure-attention policy generation; automatic prompt attention does not establish that generation or authorize those controls.
- Prompt keys accepted before complete review delivery, held-key repeats and stale event IDs cannot answer. This is an acceptance-order guarantee, not a hardware-generation timestamp guarantee for input still upstream of ttyd.
- Prompt retirement discards its answer, suppresses matching held-key tails, and preserves ordered modifier state. Explicit trusted focus changes cancel the exact deferred decision; ordinary provisioning cannot steal a modal prompt.
- Trusted switching accepts only a direction and emergency kill accepts no target; ttyd derives both from its current authenticated focus. Switching transfers the exact secure-attention generation, while kill clears it and begins the existing retryable teardown path.
- Grant-view updates carry no receipts and are descriptive only. Ttyd accepts them solely from powerboxd's authenticated private receiver, reconciles the full generational row, and never treats a grant ID or installed slot as authority.
- Trusted chrome has no serial route.
- A visible trusted record prevents another trusted record from rendering until its owning prompt is cancelled or answered and fbcond acknowledges release.
- Session policy can permit routine launch authority, but an exceptional powerbox request can only default to denial or one trusted prompt.
- Ordinary PTY data has no API that enters secure attention, answers a prompt, or writes to the trusted renderer queue.
- Authority cleanup remains visible and retryable until exact deletion succeeds.
- A lifecycle request is authenticated by the retained receiver that delivered it. Context and event-read bodies supply only correlation and an exact descriptive cursor; hangup bodies supply only a validated reason. None carries a session selector or capability receipt.
- Git dependencies use full 40-character commit
revpins. Integration source revisions and CI caches preserve those identities, with no branch-head or synthetic-commit substitution.
Library Boundaries
lib-bootstrapowns the authenticated deployment storage record.lib-stdioowns detached byte-stream state and exact storage identity checks.lib-ttyowns PTY state-transition semantics.lib-ipcowns terminal, session-policy, and trusted-prompt wire values.inputd,fbcond, andserialdown their device-facing authority.- Runtime code owns actual endpoints and syscalls; the semantic core discovers, mints, copies, and deletes no capability.
- The primary control slot is an endpoint factory only. It may create endpoint objects and attenuated local aliases; it cannot populate a child process or install authority outside ttyd's CSpace.
- The procd provisioning receiver authenticates its sole caller. Request slot numbers select fixed destinations in procd's CSpace only because the reply transfer executes on that authenticated call. The echoed nonempty stream mask decides which standard-stream destinations receive an alias; lifecycle is always transferred and unselected stream destinations receive nothing.
- Private policy, prompt, and input adapters reject every transfer-bearing call and delete every parseable unwanted receipt before returning the error.
- Renderer adapters emit typed bodies for exact startup-authenticated slots; runtime transport must reject response receipts before passing typed bodies back for correlation and acknowledgement.
- The runtime service endpoint exposes only the typed root bootstrap-session operation after startup. Every other call is denied, and every parseable unwanted receipt is retired rather than retained. Bootstrap replies may target exactly either of the two canonical rootd lifecycle receipts; no range, neighboring destination, or rootd PTY stream is admitted.
- Per-session lifecycle receivers accept only typed authority-free context, primary-PTY state, PTY event, or hangup contracts. Their exact retained receiver remains in the dynamic scan through draining and termination except while it owns a deferred orderly reply. Already-terminal hangup remains idempotent; PTY input/output routes remain active-only. Queued descriptive events are erased before the lifecycle receiver is staged and deleted through the cleanup FIFO.
- Deferred orderly completion retains the exact session generation, held receiver, kernel-attested caller and correlated prompt cancellation in deployment-sized session storage. No second receive or authority cleanup can replace them. The final empty framebuffer FLUSH shares the existing fair renderer transport and checked request sequence; no new opcode, authority, or global output drain is introduced. Generic query and retirement uncertainty fail closed. If the caller disappears, ttyd explicitly retires only this lifecycle receiver and proves its absence, records that absence, drains the accepted output, and releases the exact cancellation without an OK reply. A reused slot is never queried or dropped through that retired record; unrelated sessions remain live. Forced teardown of an already-pending orderly session, including an ancestor's console-family retirement, marks that reply cancelled and explicitly retires its receiver. Only its pre-admitted output continues draining so accepted renderer tickets remain valid; completing those bytes never produces an OK. Other forced teardowns keep immediate output-discard behavior.
- Per-session input receivers accept only read, status, and orderly read-close; per-session output receivers accept only write, status, and orderly write-close. Neither receiver admits transfer receipts, a caller-selected session, or the opposite direction's operation surface.
Contact
Development occurs in the EriX organization. Decisions and rationale are recorded in issues and design documents.
Maintainers can be reached at admin@erikinkinen.fi.