procd is the user-space process lifecycle manager. It is responsible for controlled process/task creation and lifecycle policy.
  • Rust 99.4%
  • Linker Script 0.6%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Erik Inkinen 1ede86c276
All checks were successful
CI / markdown (push) Successful in 18s
CI / test (push) Successful in 1m37s
chore: Merge native CLI development into main
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: 72c621aa7d
Selected feature: c7607afcbe

Publish this integration point following the requested cross-repository merge. Preserve the feature branch and immutable dependency objects for reproducibility; do not squash, rebase or rewrite existing commits.
2026-09-12 08:28:51 +03:00
.github build: Pin dependency and CI helper source commits 2026-09-12 07:46:45 +03:00
src perf: Reduce bounded materialization allocation round trips 2026-09-07 22:25:23 +03:00
.editorconfig Initial commit 2026-02-26 17:48:05 +01:00
.gitignore Ignore local .ci workspace 2026-04-15 21:53:01 +03:00
.markdownlint-cli2.yaml Tighten CI markdown policy 2026-05-22 15:10:37 +03:00
ARCHITECTURE.md perf: Reduce bounded materialization allocation round trips 2026-09-07 22:25:23 +03:00
Cargo.toml build: Pin dependency and CI helper source commits 2026-09-12 07:46:45 +03:00
CODE_OF_CONDUCT.md docs: Synchronize shared contribution and review policies 2026-09-12 06:38:21 +03:00
CONTRIBUTING.md build: Pin dependency and CI helper source commits 2026-09-12 07:46:45 +03:00
LICENSE Initial commit 2026-02-26 17:48:05 +01:00
linker-exit.ld refactor: document bootstrap status and use semantic terminology 2026-07-25 18:19:18 +03:00
linker-fault-kill.ld refactor: document bootstrap status and use semantic terminology 2026-07-25 18:19:18 +03:00
linker-irq-client.ld refactor: document bootstrap status and use semantic terminology 2026-07-25 18:19:18 +03:00
linker-isolation-a.ld refactor: document bootstrap status and use semantic terminology 2026-07-25 18:19:18 +03:00
linker-isolation-b.ld refactor: document bootstrap status and use semantic terminology 2026-07-25 18:19:18 +03:00
linker-lookup-client.ld refactor: document bootstrap status and use semantic terminology 2026-07-25 18:19:18 +03:00
linker-probe.ld refactor: document bootstrap status and use semantic terminology 2026-07-25 18:19:18 +03:00
linker-time-client.ld refactor: document bootstrap status and use semantic terminology 2026-07-25 18:19:18 +03:00
linker-write-protect.ld Add pager protection policy proof helper 2026-07-28 16:15:48 +03:00
linker.ld fix: keep lifecycle state below the process stack 2026-07-29 08:39:19 +03:00
README.md perf: Reduce bounded materialization allocation round trips 2026-09-07 22:25:23 +03:00
ROADMAP.md perf: Reduce bounded materialization allocation round trips 2026-09-07 22:25:23 +03:00
rustfmt.toml procd: drop unstable rustfmt options for stable toolchains 2026-03-23 10:19:56 +02:00
SECURITY.md docs: Synchronize shared contribution and review policies 2026-09-12 06:38:21 +03:00

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_std executable 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, and ABORT_STAGED_PROCESS
  • extension/test helper binaries now follow the same canonical .text.service_entry trampoline 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-batch admits two root-endpoint-only operations, IPC_PROCD_OP_INTEGRATION_STRESS_LAUNCH_BATCH (0x42F) and IPC_PROCD_OP_INTEGRATION_STRESS_OBSERVE_BATCH (0x430). They launch and reclaim bounded waves of the exact exit helper 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 signed exit executable 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 exact ENDPOINT SEND|GRANT; the resolver grants only the transfer needed to stage the authenticated cwd on each vfsd call. Accounting, cwd, and direct streams remain exact ENDPOINT 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-SEND receipt 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 exact ENDPOINT 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 atomic INSTALL_WITH_REVOCATION_GRANT operation
  • 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
  • procd receives 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 NOTIFY messages 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), and IPC_PROCD_OP_ABORT_MATERIALIZATION (0x40C) for the semantic dynamic loaderd handoff
    • IPC_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 slots
    • IPC_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 slot 221, and validated during startup
  • dedicated loader-materialization endpoint installed from SLOT_PROCD_LOADER_MATERIALIZATION_ENDPOINT (2448) into procd-local slot 213 with only RECV|GRANT; loaderd receives only a SEND|GRANT alias in its local slot 595
  • primary kernel-control authority is copied from SLOT_KERNEL_PROCESS_ENDPOINT (2288) into procd-local slot 222, which bounds procd to the process/capability lifecycle ops it actually uses
  • procd no longer receives local CNODE, TCB, or VSPACE management copies in its startup intake; routine lifecycle work is driven by its explicit process-control endpoint, staged child replies, and install grants
  • deviced spawns receive an extended caller-local slot window so the dedicated procd driver-lifecycle endpoint at 2112 and deviced's dedicated kernel log/query primary control slot at SLOT_KERNEL_LOG_ENDPOINT (2272) stay visible when rootd assigns 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 includes deviced's isolated verifier endpoint aliases at slots 624 and 625.
  • blockd spawns 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.
  • timed spawns receive an extended caller-local slot window so the dedicated kernel time-control endpoint at SLOT_KERNEL_TIME_ENDPOINT (2128) stays visible when rootd assigns it as the primary control slot
  • irqd spawns receive an extended caller-local slot window so the dedicated kernel IRQ-control endpoint at SLOT_KERNEL_IRQ_ENDPOINT (2144) stays visible when rootd assigns it as the primary control slot
  • hotplugd spawns receive an extended caller-local slot window so the dedicated kernel hotplug-control endpoint at SLOT_KERNEL_HOTPLUG_ENDPOINT (2160) stays visible when rootd assigns it as the primary control slot
  • probed, drv-acpi, drv-serial, and drv-i8042 spawns 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 them
  • fbcond spawns receive an extended caller-local slot window so the dedicated console/framebuffer endpoint at SLOT_KERNEL_CONSOLE_ENDPOINT (2192) stays visible when rootd assigns it as the primary control slot
  • memd, vspaced, and pagerd spawns 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; procd retains 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 SEND solely 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, so e2fsd, fatd, the private keyd service, loaderd, and dynlinkd can receive only their explicit role-local endpoint slots without inheriting ambient service or driver authority
  • startup ready-state now uses caller-local QUERY_LOCAL_CAP on bare metal, with QUERY_CAP retained 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
  • rootd now directly launches only procd; once procd is ready it stages and starts later foundation and runtime services itself
  • The pre-dynlinkd staged path uses that same lifecycle surface. rootd supplies only boot-catalog object identities and relocation ordinals, while procd asks 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. procd preserves pager registration, wait-event, endpoint-transfer, and cleanup semantics for probe, exit, isolation-a, isolation-b, fault-kill, and write-protect while keeping helper launch manifest-bound and pre-dynlinkd. write-protect deliberately 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. deviced supplies the role-scoped executable identity and explicit child endpoint/install-grant slots; procd maps 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-dynlinkd staged dynamic create path. The deployment selects a managed-process record count that covers its complete dynamic launch wave, so blockd, ramfsd, keyd, e2fsd, fatd, vfsd, and loaderd can 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 memd frame-allocation IPC path, treats BUSY/RETRY as 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 BUSY is 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 logd and crashd work 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-stops procd, 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
  • before foundation publication, procd waits 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. A BUSY result is the kernel's transfer-rendezvous signal, so procd blocks 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 crashd or logd; queued killed-process reports are drained later through the fixed procd-specific crash-report endpoint, including after a successful QUERY_PROCESS reply 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 returns RETRY; the kernel's immediate transport-level RETRY is 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 DENIED with 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_ENDPOINTS so procd can start with pagerd = 0 and 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 to procd
  • 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 loaderd are 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: loaderd supplies ordered loadable-segment fragments and relocation batches, while procd owns 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 ttyd with its canonical service endpoint, launchd with its dedicated child-local request endpoint, powerboxd with its canonical request endpoint, or the private drv-rtc-cmos provider 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 extended PBX1 transient 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_FOUND without 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; permanent RESOURCE_EXHAUSTED fails 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 LCS1 version 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 exactly READ|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 separate 0x429 call 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 receives BUSY, 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_reserve region in a final loaded segment so stack_top = module.vaddr_end lands 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, and irq-client) declares a 64 KiB .stack_reserve so the first user-mode syscall frame cannot underflow into adjacent writable image state
  • service-resolving helpers receive one exact target scope from procd: lookup-client can resolve only pagerd, time-client only timed, and irq-client only 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-client validates monotonic and wall-clock snapshot ordering without invoking a blocking deadline operation
  • staged extension helper binaries now keep the canonical .text.service_entry wrapper so procd launches 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 procd requests start
  • managed stop binds both process id and nonzero staged generation before using kernel KILL_PROCESS plus 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: procd boots without requiring named/logd/crashd availability
  • procd does 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 deviced policy.
  • 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 loaderd transfer contract.

Authority Boundaries

  • procd operates 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 loaderd provider, 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.