- Rust 99.4%
- Linker Script 0.6%
| 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-exit.ld | ||
| linker-fault-kill.ld | ||
| linker-irq-client.ld | ||
| linker-isolation-a.ld | ||
| linker-isolation-b.ld | ||
| linker-lookup-client.ld | ||
| linker-probe.ld | ||
| linker-time-client.ld | ||
| linker-write-protect.ld | ||
| linker.ld | ||
| README.md | ||
| ROADMAP.md | ||
| rustfmt.toml | ||
| SECURITY.md | ||
EriX Process Manager (procd)
procd is the user-space process lifecycle manager. It is responsible for
controlled process/task creation and lifecycle policy.
EriX is a clean-room, capability-based microkernel operating system written entirely in Rust.
Technical requirements are tracked in the EriX requirements, conventions, and project documentation.
See:
- docs for design documents, specifications, and development plans.
- Related architecture repositories for kernel, services, libraries, drivers, and integration tooling.
Purpose of This Repository
This repository implements the EriX proc service daemon. Its purpose in EriX is to provide the proc service role through explicit IPC and startup authority.
Functionally, it implements the daemon runtime, state model, IPC handling, and validation tests. The repository keeps the implementation, interface contracts, tests, and documentation for that behavior in one reviewable ownership boundary.
The maintained responsibilities are:
- implement the proc service runtime and state model
- validate startup authority before accepting IPC requests
- handle bounded service operations through the assigned endpoint set
- keep service behavior, tests, and authority invariants documented
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
EriX development is modular, deterministic, reproducible, authority-explicit, security-first, and self-hosting oriented.
This repository follows the project roadmap and the validation rules documented in its own roadmap.
Implementation Status
Runtime baseline:
no_stdexecutable service binary (x86_64-unknown-none) with SysV entry ABI- startup envelope parsing/validation via
lib-bootstrap - role/intake contract validation via
lib-capabi - runtime IPC server loop (
RECV+REPLY) for lifecycle request handling - staged lifecycle path wired through kernel-control IPC:
CREATE_STAGED_PROCESS,START_PROCESS, andABORT_STAGED_PROCESS - extension/test helper binaries now follow the same canonical
.text.service_entrytrampoline convention as ordinary service images so staged child start uses the validated ELF entry surface consistently - extension/test helper binaries that do deeper IPC/interrupt call chains also reserve explicit in-image stack space in their per-bin linker scripts so the staged spawn stack does not collapse back into image data
- typed staged dynamic lifecycle paths:
IPC_PROCD_OP_CREATE_STAGED_DYNAMIC_PROCESS(0x420)IPC_PROCD_OP_MAP_STAGED_DYNAMIC_OBJECT(0x421)IPC_PROCD_OP_APPLY_STAGED_DYNAMIC_RELOCATION(0x422)IPC_PROCD_OP_APPLY_STAGED_DYNAMIC_RELOCATION_BATCH(0x431)IPC_PROCD_OP_SEAL_STAGED_DYNAMIC_OBJECT(0x423)IPC_PROCD_OP_START_STAGED_PROCESS(0x401)IPC_PROCD_OP_ABORT_STAGED_PROCESS(0x402)IPC_PROCD_OP_COMMIT_SUCCESSOR(0x42A)IPC_PROCD_OP_QUERY_PROCESS_TIMES(0x42B)IPC_PROCD_OP_READ_SYSTEM_METRICS(0x42D)
procd-integration-stress-batchadmits two root-endpoint-only operations,IPC_PROCD_OP_INTEGRATION_STRESS_LAUNCH_BATCH(0x42F) andIPC_PROCD_OP_INTEGRATION_STRESS_OBSERVE_BATCH(0x430). They launch and reclaim bounded waves of the exactexithelper entirely inside procd and return only process/generation evidence. Production builds admit neither operation. Each launch uses the kernel's closed full-graph operation to map, relocate, and seal the signedexitexecutable and its shared dependencies. Procd retains its staging endpoint and install grant only through successful process start, revokes both before replying, no caller receives either capability, and every unused fixed-capacity identity row must be zero.- on the private launchd receive endpoint,
IPC_PROCD_OP_PROVISION_TTY_SESSION(0x425) carries one moved install-grant receipt, one provider-pinned working-directory sender, an optional exact job-bound redirection-resolver sender, an optional exact script-launch sender, an optional job-bound CPU-accounting sender represented by an exact all-zero tuple when absent, plus the direct stream endpoints complementary to the nonempty ttyd mask. A zero-source stream is valid only when launchd explicitly marks its fixed child slot deferred to an already installed dynamic description. The script and resolver routes must be exactENDPOINT SEND|GRANT; the resolver grants only the transfer needed to stage the authenticated cwd on each vfsd call. Accounting, cwd, and direct streams remain exactENDPOINT SEND. Procd validates canonical receipt order, exact provenance, fixed destinations, live type, rights, pairwise endpoint separation, and exact absence of every deferred staging slot before contacting ttyd. The authenticated cwd sender remains mandatory for launch resolution and cleanup even when its explicit child destination is zero. - successful tty provisioning accepts the mandatory lifecycle receipt plus exactly the selected send-only ttyd stream receipts into the remaining fixed slots, leaves each explicitly deferred fixed stream absent, proves the complete lifecycle/input/output object relationships, retains cleanup senders for lifecycle and cwd, installs cwd only when its child destination is nonzero, installs the optional resolver, exact-rights script sender, an accounting sender only when present, and all four session endpoints into exact child slots in one grant operation, and deletes every temporary alias and the moved grant before replying. Procd retains no resolver, script, or accounting sender
- any tty refusal, malformed reply, partial installation, or lost success reply hangs up the created session when reachable, aborts the staged process, and exhaustively sweeps known authority; uncertain cleanup fail-stops procd
IPC_PROCD_OP_INSTALL_LAUNCH_DESCRIPTION(0x429) accepts exactly one launchd-originated endpoint-SENDreceipt in the fixed one-use description staging slot. The request must match the canonical description that procd just wrote for the exact active process generation. Procd installs that endpoint only at the declared child slot through its retained endpoint-only grant, deletes the staging alias after every attempt, and refuses the next startup record until installation succeeds. A lost or cleanup-uncertain reply aborts the staged child rather than treating numeric correlation as proof of authority. The same operation installs provider-backed descriptors and launchd-created here-document or pipeline senders: procd receives only the canonical child slot/access row and exactENDPOINT SEND, never private stream identity, retained bytes, or launchd's receive authority- a private powerboxd receive endpoint accepts only
IPC_PROCD_OP_INSTALL_POWERBOX_GRANT; the runtime authenticates the exact transferred send-only object receipt, staged process generation, retained install grant, child slot, and empty revocation destination before invoking the kernel's atomicINSTALL_WITH_REVOCATION_GRANToperation - successful powerbox installation returns exactly one distinct manage-only revocation grant and retires both procd's temporary object alias and its sender-local revocation source after delivery; every failure returns zero child/revocation fields and schedules both temporary slots for deletion, with process teardown on uncertain cleanup
- immediate and static staged process-creation requests are not accepted; the dynamic create/map/relocate/seal path is the only process-construction path
procdreceives no host-report authority; startup, lifecycle, loader, and process-event outcomes remain visible through typed replies and records- startup explicitly registers procd's TCB-bound service endpoint as the kernel
process-event wake destination; canonical empty
NOTIFYmessages trigger an immediate event drain without granting children any delivery authority - typed lifecycle control paths:
IPC_PROCD_OP_STOP_PROCESS(0x400)IPC_PROCD_OP_WAIT_PROCESS_EVENT(0x403)IPC_PROCD_OP_QUERY_PROCESS(0x404)IPC_PROCD_OP_SET_RESTART_POLICY(0x405)IPC_PROCD_OP_GET_RESTART_POLICY(0x406)IPC_PROCD_OP_UPDATE_FOUNDATION_ENDPOINTS(0x407)IPC_PROCD_OP_BEGIN_MATERIALIZATION(0x408),IPC_PROCD_OP_SUBMIT_MATERIALIZED_SEGMENT(0x409),IPC_PROCD_OP_APPLY_MATERIALIZED_RELOCATION(0x40A),IPC_PROCD_OP_COMMIT_MATERIALIZATION(0x40B), andIPC_PROCD_OP_ABORT_MATERIALIZATION(0x40C) for the semantic dynamicloaderdhandoffIPC_PROCD_OP_COMMIT_STAGED_MATERIALIZATION(0x40D) promotes a complete loader transaction to a tracked but unstarted process. Ordinary programs return exactly one install-grant receipt; the exact powerboxd service role instead returns its process endpoint and install grant into two disjoint, fixed loaderd receipt slotsIPC_PROCD_OP_APPLY_MATERIALIZED_RELOCATION_BATCH(0x40E) applies one sequential transport-bounded group of authoritative relocation writes on the same private loader-materialization endpoint
- lifecycle control requests and replies serialize only correlation, operation inputs, and observable results; no zero-only alignment or compatibility words remain in these bodies
- dedicated driver-lifecycle endpoint rooted at
SLOT_PROCD_DRIVER_ENDPOINT (2112), installed into procd-local slot221, and validated during startup - dedicated loader-materialization endpoint installed from
SLOT_PROCD_LOADER_MATERIALIZATION_ENDPOINT (2448)into procd-local slot213with onlyRECV|GRANT;loaderdreceives only aSEND|GRANTalias in its local slot595 - primary kernel-control authority is copied from
SLOT_KERNEL_PROCESS_ENDPOINT (2288)into procd-local slot222, which boundsprocdto the process/capability lifecycle ops it actually uses procdno longer receives localCNODE,TCB, orVSPACEmanagement copies in its startup intake; routine lifecycle work is driven by its explicit process-control endpoint, staged child replies, and install grantsdevicedspawns receive an extended caller-local slot window so the dedicatedprocddriver-lifecycle endpoint at2112anddeviced's dedicated kernel log/query primary control slot atSLOT_KERNEL_LOG_ENDPOINT (2272)stay visible whenrootdassigns them. The same role-specific window follows the shared capability ABI through the architectural boundary so every retained per-driver-instance endpoint slot is addressable. Empty slots convey no authority, and the window also includesdeviced's isolated verifier endpoint aliases at slots624and625.blockdspawns receive the shared exact window ending immediately after its one transaction-local provider endpoint. Empty intervening slots convey no authority, and each traversal step replaces that one explicit capability.timedspawns receive an extended caller-local slot window so the dedicated kernel time-control endpoint atSLOT_KERNEL_TIME_ENDPOINT (2128)stays visible whenrootdassigns it as the primary control slotirqdspawns receive an extended caller-local slot window so the dedicated kernel IRQ-control endpoint atSLOT_KERNEL_IRQ_ENDPOINT (2144)stays visible whenrootdassigns it as the primary control slothotplugdspawns receive an extended caller-local slot window so the dedicated kernel hotplug-control endpoint atSLOT_KERNEL_HOTPLUG_ENDPOINT (2160)stays visible whenrootdassigns it as the primary control slotprobed,drv-acpi,drv-serial, anddrv-i8042spawns receive extended caller-local slot windows so their dedicated narrow platform-discovery-read, ACPI RSDP-read, COM1, and i8042 primary-control endpoints stay visible when the parent assigns themfbcondspawns receive an extended caller-local slot window so the dedicated console/framebuffer endpoint atSLOT_KERNEL_CONSOLE_ENDPOINT (2192)stays visible whenrootdassigns it as the primary control slotmemd,vspaced, andpagerdspawns receive caller-local windows covering their role-local primary-control destinations; root's dedicated source endpoints are copied rather than exposed under root's slot numbers. Memd's window additionally covers its sole transaction-local frame-export slot.- procd-owned managed-process scratch slots are kept in a high local window derived from the deployment's managed-process record count and outside role-local service endpoint ranges. Lib-capabi's shared five-slice multiplier covers child endpoint, working-directory cleanup, install grant, powerbox revocation grant, and child VSpace aliases, so rootd grants the exact window that procd consumes without colliding with explicit peer or driver endpoints. The two immediately preceding, addressable slots are reserved for one-use launch-description and redirection-resolver receipts
- lifecycle records, pending destroys, crash reports, bootstrap events, and
driver events borrow equal-length typed slices from one exact
deployment-patched arena;
procdretains no general allocator authority - managed records retain lifecycle facts only: request identifiers remain
correlation data, while child endpoint, caller install-grant, and VSpace
aliases are dropped after their operation. Procd retains one separate
process-bound install grant attenuated to endpoint
SENDsolely for the private powerbox route, then deletes it before process start. Builds without crash reporting retain no mutable crash-queue slice in live service state - filesystem provider work filesystem provider, key-service, loader-service,
and dynamic-linker spawns receive a caller-local slot window sized through
dynlinkd, soe2fsd,fatd, the privatekeydservice,loaderd, anddynlinkdcan receive only their explicit role-local endpoint slots without inheriting ambient service or driver authority - startup ready-state now uses caller-local
QUERY_LOCAL_CAPon bare metal, withQUERY_CAPretained for host/test scaffolding, to validate the actual local control slot, service endpoint slot, and every declared transferred slot - those local control, driver-lifecycle, and loader-materialization slots are
real copied endpoint aliases materialized by
rootd, not just documented slot conventions - deterministic process-id/endpoint assignment from kernel-control responses
- async probe-completion notify path for bootstrap completion signaling
rootdnow directly launches onlyprocd; onceprocdis ready it stages and starts later foundation and runtime services itself- The pre-
dynlinkdstaged path uses that same lifecycle surface.rootdsupplies only boot-catalog object identities and relocation ordinals, whileprocdasks the kernel process-control endpoint to map, relocate, seal, and start dynamic children without static boot-module frame mapping or ambient catalog/filesystem authority. - Root-only staged relocation batches carry at most 64 ordered target/value pairs for one exact unstarted process generation and mapped object. The complete body is checked before writes; every record keeps its original kernel authority check, including duplicate or backward targets. Failure reports only the successful prefix and requires rootd to abort that process, never retry a typed BUSY response. Ordinals correlate replies without adding replay state, transferable authority, or a new kernel operation.
- Helper executables reuse the staged dynamic path.
procdpreserves pager registration, wait-event, endpoint-transfer, and cleanup semantics forprobe,exit,isolation-a,isolation-b,fault-kill, andwrite-protectwhile keeping helper launch manifest-bound and pre-dynlinkd.write-protectdeliberately writes to a present read-only page after pager registration so integration can prove that protection violations remain kernel-owned and never reach pagerd. - The isolation helpers exercise a shared anonymous page immediately below the boot-artifact aperture. It is outside both the permanent low 1 GiB supervisor identity mapping and linked service images, ensuring the first access is an eligible non-present user fault rather than a supervisor protection fault.
- Pager setup transfers the child VSpace to pagerd over pagerd's transfer-bearing registration endpoint with an explicit read-write, non-executable anonymous stack region. That authority covers exactly the shared 256-KiB reserve below the page-aligned stack top, never executable images or intervening address-space holes. Procd then records paging with the kernel without naming a child endpoint; the child receives no pagerd endpoint capability.
- Migrated drivers enter through the dedicated driver lifecycle endpoint.
devicedsupplies the role-scoped executable identity and explicit child endpoint/install-grant slots;procdmaps the signed dynamic graph before publishing the managed process record and cleans up every staged cap on failure. - The storage, filesystem, VFS, and loader services use the same
pre-
dynlinkdstaged dynamic create path. The deployment selects a managed-process record count that covers its complete dynamic launch wave, soblockd,ramfsd,keyd,e2fsd,fatd,vfsd, andloaderdcan be started from signed boot-catalog artifacts without residual catalog, frame, or process authority on cleanup. - dynamic child stack premap maps only the initial stack page through the
explicit
memdframe-allocation IPC path, treatsBUSY/RETRYas protocol backpressure without an arbitrary retry count, and leaves the remaining declared stack region to pagerd after registration; terminal allocation failures still fail closed and trigger staged-process cleanup. Kernel acceptance transfers exclusive ownership of the request buffer until the authoritative reply arrives, so procd never rebuilds or drops an in-flight frame-allocation request. - one shared retained-call primitive covers pager registration, stack and
materialization frame allocation, probe start, startup logging, and crash
reporting. It keeps accepted message storage live through reply without a
polling-count ceiling; operation-specific code decides whether
pre-acceptance
BUSYis retried immediately or deferred. Kernel-control calls use the same retained-completion rule, and accepted inbound lifecycle calls keep their reply bytes until transient reply backpressure clears. - optional
logdandcrashdwork makes one non-blocking acceptance attempt; once accepted, the call is retained through its authoritative reply. Replies must carry no authority: every unexpected receipt destination is retired, malformed receipt state or uncertain cleanup fail-stopsprocd, and a fully retired unwanted transfer is rejected as invalid input. - pagerd may reject malformed intake with a header-only error reply; procd preserves that explicit result instead of misclassifying the absent typed success body as an internal transport failure
- runtime endpoint policy is now split by local endpoint:
- root orchestration endpoint accepts non-
drv-*, non-launch-child lifecycle and foundation operations but rejects materialization operations - loader-materialization endpoint accepts only begin, segment, relocation, or relocation batch, commit, staged commit, transaction abort, and exact staged-process rollback; it cannot start or observe a process
- dedicated driver endpoint accepts only
drv-*lifecycle requests - the driver endpoint sees only
drv-*wait/query/restart-policy traffic - private launchd endpoint accepts only exact staged-child tty provisioning, canonical launch-startup begin/fragment/commit, ordinary start, successor commit, rollback, exact-generation stop, an authority-free CPU-time snapshot of a loader-materialized launch process, or one indexed read-only processor and allocator snapshot; launchd STOP transactionally destroys the exact unstarted staged generation or terminates the same generation after start, and events remain limited to that canonical process class
- private powerboxd endpoint accepts only one exact revocable child-capability installation and grants no process observation or lifecycle control
- root orchestration endpoint accepts non-
- before foundation publication,
procdwaits on its root orchestration endpoint without a service-local deadline. Afterwards, one ordered non-blocking scan visits root, launchd tty provisioning, loader-materialization, driver, and powerboxd install endpoints. ABUSYresult is the kernel's transfer-rendezvous signal, soprocdblocks on that exact endpoint until it accepts the waiting call; when no call is ready, it performs eligible housekeeping and yields. - process exit, process kill, and staged abort use the retained session-specific lifecycle sender to request orderly or killed teardown, then delete that sender before retiring the managed record; uncertain teardown terminates procd instead of leaving session authority or bookkeeping ambiguous
- launchd may read one running launch child's canonical PTY event cursor through that already retained sender. The private request binds the exact process and generation, failures preserve the cursor and erase event data, and procd transfers no lifecycle or event authority. A retained terminal generation returns retry so launchd's following exact status read wins the exit race
- before a terminal managed process is destroyed, procd queries the kernel with its exact process/generation identity and records saturating user and system CPU nanoseconds. Killed tasks are destroyed before retained TTY and working-directory senders are closed so an in-flight request cannot block cleanup. Orderly exits have already lost execution authority and their kernel endpoint receiver; procd closes their retained TTY and working-directory senders before process destruction, preserving provider completion. Pagerd's delegated VSpace alias is unregistered only after destruction. Launchd can read a live kernel snapshot or that immutable terminal snapshot only for its own launch-child generation. The request and reply carry no capability receipts, and procd delegates neither its process-control endpoint nor any child authority. A terminal launch record is not reusable until launchd has observed the snapshot once.
- system-metrics requests are admitted only on launchd's private endpoint. Procd reads one explicit processor topology/counter pair through its existing process-control endpoint and allocator totals through its existing memd sender, then returns a combined descriptive record with no capability receipts. Root, driver, loader, and powerbox routes cannot invoke it, and failed responses expose no counters or memory totals.
- after a successful reply, housekeeping is deferred for one loop and process creation consumes inactive managed slots before draining pending-destroy records, keeping dynamic launch waves from being stranded behind unrelated cleanup work
- managed-process lookup prefers live staged/running records over older terminal records when the kernel reuses a process id, so a completed child cannot shadow the explicit endpoint and restart-policy authority of a newer dynamic spawn
- terminal-slot reclamation binds visible-event obligations to the complete process ID and staged generation. A queued event for a newer occupant of a reused kernel ID therefore cannot pin an older delivered terminal record or exhaust the bounded managed-process arena.
- wait-event replies return the terminal event before crash-report housekeeping,
so lifecycle observation is not blocked by
crashdorlogd; queued killed-process reports are drained later through the fixed procd-specific crash-report endpoint, including after a successfulQUERY_PROCESSreply used as an explicit verifier synchronization point. Internal service calls advertise the larger of their encoded request and response sizes, so a crash-report reply cannot be stranded behind a request-sized receive buffer. An empty wait performs one direct kernel event call and returnsRETRY; the kernel's immediate transport-levelRETRYis the authoritative empty-queue result, not an accepted retained call. Procd therefore does not consume scheduler time in a service-local spin loop or wait for a reply that cannot arrive. - root and launchd select disjoint process classes from the bounded bootstrap
event queue without consuming each other's records. A successful launchd
STOP response consumes its matching queued event because that response
already carries the terminal outcome; natural exits remain visible only to
launchd's private wait route. Root queries for launch-child records return
DENIEDwith only the caller-supplied process identity echoed; role, state, generation, status, event, and restart-policy fields remain zero. - launchd alone may deliver a receipt-free canonical signal to one exact launch-child process ID and generation. Procd applies the validated inherited ignore set or catalog default, makes ignored outcomes mutation-free, and uses a tagged kernel kill reason for terminate-class defaults. The matching terminal event is consumed by the response, which also returns the immutable user/system CPU snapshot captured before destruction. Launchd therefore does not need a second lifecycle call after termination. Stop/continue fail without mutation until real kernel state transitions exist. Managed cancellation remains a separate operation and reason.
- bootstrap-only foundation endpoint updates are published through
UPDATE_FOUNDATION_ENDPOINTSsoprocdcan start withpagerd = 0and bind those slots later without reviving the old adoption path. The update is one-shot and requires exactly three kernel-normalized endpoint receipts with the request's destinations and exact least-authority rights; rejected calls visit and drop every decodable receipt destination even after an earlier drop fails. Malformed tables or uncertain cleanup terminate procd so process teardown revokes its CSpace. - spawn requests register only the declared anonymous stack region and explicit
non-executable rights with
pagerd; executable mappings are materialized before process start and no generic boot-image frame is delegated toprocd - every lifecycle and failed-create cleanup requires pager unregistration to succeed or report that the binding is already absent; a hard cleanup failure terminates procd instead of continuing with untracked delegated VSpace authority
- failed staged creation attempts visit the process abort plus every local endpoint, install-grant, and VSpace drop. An unknown process identity or any cleanup result other than success/already-absent fail-stops the service after the remaining local cleanup operations have been attempted
- summary-only and whole-file loader handoffs have been removed; filesystem-backed execution uses only the semantic process-materialization protocol
- loader materialization requests from
loaderdare accepted only on the dedicated loader-materialization endpoint and lowered onto the existing staged child path; requests carry canonical mapping semantics, ordered loadable-segment bytes, and sequential transport-bounded authoritative relocation batches, not a source path, whole ELF object, caller-selected frame, VSPACE, memory, install-grant, service, or peer authority - filesystem-byte execution is a
procd-owned transaction:loaderdsupplies ordered loadable-segment fragments and relocation batches, whileprocdowns child creation, fresh zero-initialized frame extents of at most 64 pages within each segment, page-by-page mapping and permission sealing, staging, event tracking, and cleanup. Final RW pages reuse their identical private staging map; RO/RX pages still perform the kernel-checked rights transition. The child is unstarted and its frame remains private until commit, while permission validation, extent accounting, and frame retirement run for every page. Fragmented allocations shrink down to one page; each extent capability retires after its last page is sealed. The private staged commit preserves the entry point, stack pointer, and highest mapped image extent without starting. Launchd may start only after its correlated tty installation succeeds and procd has configured the exact canonical startup-image range for that process generation. - ordinary staged materialization returns no child endpoint and only one
process install grant. Procd derives one endpoint-
SEND-only child-install grant before moving the original grant, uses it only for authenticated launch-description or powerbox installation, and drops it before start - an explicitly declared filesystem-service materialization must be exactly
ttydwith its canonical service endpoint,launchdwith its dedicated child-local request endpoint,powerboxdwith its canonical request endpoint, or the privatedrv-rtc-cmosprovider with its canonical service endpoint. Each receives its complete role-specific CSpace window; the RTC window ends at its role-local resource boundary, while powerboxd's addressable window covers its authenticated extendedPBX1transient pool through the architectural boundary without populating any empty slot. Each returns the process endpoint and original install grant into separate fixed loaderd receipts, derives no user-process powerbox grant, and cannot use immediate commit. Root may start ttyd, launchd, and powerboxd; deviced alone may start the RTC provider through the driver-lifecycle endpoint and waits for its restart-never clean one-shot exit. Loaderd may roll back either class. Launchd's ordinary-child control route cannot control any service-class process, including launchd itself - a lost staged-commit reply aborts the exact process and sweeps every local endpoint, install-grant, powerbox-grant, and VSpace scratch alias
- materialization abort is transaction-exact: a mismatched transaction cannot
disturb the active transfer, successful abort erases it, and a repeated abort
returns authenticated
NOT_FOUNDwithout recreating or retaining authority - image population stages every page touched by the current fragment as writable and non-executable before issuing one complete-range population operation; completed pages are remapped with final RO/RX/RW permissions before their extent capabilities are dropped, and distinct virtual pages never alias one physical page; failures retain whether allocation, mapping, process-memory population, or transaction-local capability retirement rejected the page
- adjacent raw-nonoverlapping ELF segments may share one aligned boundary page; procd retains that page's single staging frame until both segments contribute, seals it with the union of their final permissions, and rejects a writable plus executable union or any raw byte-range overlap
- relocation batches merge only consecutive adjacent eight-byte writes into runs of at most 4 KiB; holes, duplicate targets, and backwards targets start a new run, preserving exact byte coverage and authenticated plan order
- materialization requests exact frame rights up front; kernel retype supplies
zeroed, already-backed pages, and final-cap deletion reclaims their precise
physical ranges after the child mappings are revoked during cleanup. Frame
requests are re-encoded after transient provider
BUSY/RETRY; permanentRESOURCE_EXHAUSTEDfails the transaction without an unbounded retry - the initial stack is an ordinary loader-submitted writable, non-executable ELF segment; commit accepts only a 16-byte-aligned stack pointer covered by that segment and creates no separate policy-sized stack mapping
- cleanup regression coverage injects materialization failures after child creation, frame allocation, partial mapping, partial segment copy, and event registration, then proves process, endpoint, VSPACE, frame, memory-write, and managed-process state is accounted for after cleanup; a failed local frame-cap drop remains tracked as cleanup-only state until a later drop succeeds
- request identifiers are non-zero response-correlation values, not transaction sequence counters; semantic segment and relocation indexes enforce ordering without imposing a 32-bit operation-count ceiling
- Loader closure evidence comes from the focused scenario groups: filesystem discovery, ELF-plan rejection, materialized disk-byte execution, identity rejection, cleanup, and authority denial all run without the older broad VM matrix
- procd accepts one receipt-free launchd-only startup stream at a time. It
places the canonical
LCS1version 1.8 image above the loader graph with one unmapped guard page, derives its actual and optional distinct stable POSIX-visible process identity only from the authenticated BEGIN process/generation summary, copies the optional child launch-envelope cross-check and authenticated command-entry digest only from that same private stream, allocates fresh pages from memd with exactlyREAD|WRITE|MAP, and hashes every generated prefix and supplied byte. Arguments, environment, child-slot-ordered shared open descriptions, and strictly increasing sparse descriptors are validated in canonical order; invalid access, references, duplicates, and reordering fail before commit. A description's numeric slot remains inert until a separate0x429call supplies the matching authenticated endpoint receipt; procd blocks every later record while that installation is pending. Sparse descriptor numbers and child-local description indexes never convey authority. Description IDs zero through two select the already installed fixed stdin/stdout/stderr streams; IDs from three select dynamic description rows and remain gated by their exact endpoint-install obligations. The generation-bound kernel startup-range operation runs only after all four counts, size, ordering, and SHA-256 digest match. Exact begin, last-fragment, and commit retries are idempotent; another stream receivesBUSY, and abort drops any tracked transient frame alias before process rollback. A launch child cannot start until this commit succeeds; launchd drives the stream before TTY installation and process start. Launch children receive complete private-CSpace addressability so intrinsic slots, the bound receiver, fixed streams, and arbitrary sparse descriptor slots can all be queried and used. This window does not populate an empty slot or grant authority: every non-intrinsic capability still requires an authenticated explicit install, and exsh validates the exact resulting inventory before it executes. Service processes retain their narrower role-specific windows. Focused end-to-end coverage remains pending until exsh is packaged on ext4. - ordinary launch start accepts either the actual identity as the visible PID or a distinct nonzero stable visible PID after exact startup commit. The stable number remains descriptive: only launchd's private endpoint plus the exact actual process/generation pair authorizes start. Procd retains the selected visible identity in the managed lifecycle record independently of the recyclable kernel process ID.
- successor commit accepts only a configured staged launch child whose stable startup identity equals the exact running predecessor's retained stable visible identity. The predecessor and successor actual process IDs remain distinct lifecycle selectors bound to exact generations. Procd drops the candidate's one-shot start grant while the predecessor remains live, then stops and consumes the predecessor event before starting the candidate. Any uncertainty after predecessor stop fail-stops the service.
- spawned service images now keep any declared
.stack_reserveregion in a final loaded segment sostack_top = module.vaddr_endlands in explicit reserved stack space after live.data/.bss - the single-threaded runtime keeps transport-sized authority-bearing reply assembly in dedicated static scratch, so wider storage transports cannot consume or spill the pre-pager bootstrap stack
- every staged syscall helper (
probe,lookup-client,time-client, andirq-client) declares a 64 KiB.stack_reserveso the first user-mode syscall frame cannot underflow into adjacent writable image state - service-resolving helpers receive one exact target scope from
procd:lookup-clientcan resolve onlypagerd,time-clientonlytimed, andirq-clientonly IRQD's LAPIC-timer handler object. Their call buffers reserve the complete typed reply plus normalized transfer receipt, and the shared resolver authenticates that exact receipt before the endpoint is used. The IRQ helper can register and acknowledge only the timer line and receives no IRQD administrative or mask-control authority. The helper clears the materialized service endpoint after its operation, and helpers without a discovery need receive no scope. - The lookup, time, and IRQ helpers share the same retained-call implementation and retry only pre-acceptance transport backpressure. Once the kernel accepts a call, each helper retains and polls the same message buffer until the service replies; the IRQ helper therefore never resubmits its authority transfer. No helper applies an arbitrary attempt cap or keeps a second full request-envelope snapshot.
time-clientvalidates monotonic and wall-clock snapshot ordering without invoking a blocking deadline operation- staged extension helper binaries now keep the canonical
.text.service_entrywrapper soprocdlaunches the intended helper entry instead of falling through to a null start address - staged create replies now return a caller-local child endpoint alias plus a
kernel-issued install grant, so callers populate the child's private CSpace
before
procdrequests start - managed stop binds both process id and nonzero staged generation before using
kernel
KILL_PROCESSplus synchronous event retirement; a stale generation cannot stop a reused id. Repeated stop after retirement returns the recorded terminal outcome without issuing another kill. Non-running live records remain denied except for the exact loader-materialized staged generation on launchd's private reconciliation route. A new stop drains available kernel events until its target arrives or the event source is empty, without a fixed polling-attempt cap - managed query binds the same exact process id and nonzero staged generation, so a terminal record cannot be substituted by a newer occupant of a reused kernel id; both scalars remain descriptive and require an already-held, admitted procd route
- wait-event requests use either zero/zero to consume the next event visible to the authenticated endpoint or one exact nonzero process/generation pair. Exact selection removes only that incarnation and preserves every unrelated queued event; partial selectors are rejected without mutation
- successful ordinary materialization returns that exact generation with its materialization identifier, while failures and non-commit replies keep it zero, allowing loaderd to preserve complete lifecycle identity
- foundation-service startup is non-blocking before discovery comes online:
procdboots without requiringnamed/logd/crashdavailability procddoes not self-register. Root publishes it after readiness.- Root delegates fixed send-only log-submit and crash-report clients in the startup bundle. Their endpoint objects may exist before the receiving services are ready, so early calls can retry without naming authority.
- The procd-specific log endpoint authenticates the source role, and the
procd-specific crash endpoint is the only report client accepted by
crashd; neither request body carries a caller-claimed producer identity. - Only staged helper processes receive target-specific naming scopes, and only for pager, time, or interrupt operations declared by their launch role.
Validation Note
The host/test QUERY_CAP fallback remains cfg-scoped so runtime/release builds
stay warning-free.
Dynamic Boot Artifact Evidence
The image build packages procd as an ELF64 x86_64 ET_DYN executable with
.erix_dynlink metadata in the signed dynlink-store and mirrors it under
/lib/erix/dynlink with its required shared objects.
Startup remains rootd/kernel process-control as the final pre-procd substrate
service; authority remains the dedicated process-control endpoint and explicit
install grants. Dynamic packaging and filesystem mirror records are evidence
and launch inputs only; they do not grant filesystem, loader, object-store,
service-discovery, provider-bypass, block-device, or dynlinkd authority.
procd receives only the documented startup endpoints, peers, and
capabilities for its role.
Optional startup logging submits Rust strings through lib-log and checks the
logical LogSubmitResponseV1::is_success() result while delegating chunk
framing, request sequencing, and submit buffer sizing to the logging client
library. The fixed submit endpoint supplies procd's authenticated source role.
Release runtime images treat the informational procd-ready startup record as
handled without submitting it to logd.
Governance Principles
procd governance is scoped to process/service creation and driver lifecycle
execution.
The scoped governance rules are:
- It starts services and drivers only from explicit launch requests and assigned capabilities.
- It must not infer device ownership; driver authority comes from
devicedpolicy. - It preserves process identity, startup metadata, and failure reporting for callers.
- It treats residual launch authority or malformed start envelopes as validation failures.
- It must not acquire filesystem/provider authority to launch disk programs;
file-backed executable bytes arrive only through an explicit
loaderdtransfer contract.
Authority Boundaries
procdoperates only through startup-assigned service capabilities.- New authority must be represented in bootstrap/capability validation and integration tests before use.
- Materialized loader launches must not accept caller-selected frame slots,
VSPACE caps, install grants, service endpoints, or peer endpoints from
loaderd; those remain procd-owned or launch-policy-owned surfaces. - Holding loaderd's send-only materialization endpoint grants no root orchestration, driver lifecycle, query, stop, restart-policy, or foundation update authority.
- Loader cleanup fault hooks are integration/test-only and do not grant
loaderdprovider, block, key, journal, peer-service, scratch, or caller-inherited authority.
Contact
Development occurs in EriX organization and discussions happen in issues and design documents.
No decisions are considered valid without documented rationale.
Maintainers can be reached via email: admin@erikinkinen.fi.