deviced is the device manager daemon for EriX.
  • Rust 99.7%
  • Linker Script 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Erik Inkinen 5824765c11
All checks were successful
CI / markdown (push) Successful in 10s
CI / test (push) Successful in 1m4s
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: 39664914fa
Selected feature: dd6287e653

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:40 +03:00
.github build: Pin dependency and CI helper source commits 2026-09-12 07:45:54 +03:00
src diag: Classify RTC loader startup failure 2026-09-04 03:28:25 +03:00
.editorconfig Initial commit 2026-03-07 11:22:31 +01:00
.gitignore Ignore local .ci workspace 2026-04-15 21:52:35 +03:00
.markdownlint-cli2.yaml Tighten CI markdown policy 2026-05-22 15:09:54 +03:00
ARCHITECTURE.md feat: own the RTC one-shot lifecycle 2026-09-02 21:32:28 +03:00
Cargo.toml build: Pin dependency and CI helper source commits 2026-09-12 07:45:54 +03:00
CODE_OF_CONDUCT.md docs: Synchronize shared contribution and review policies 2026-09-12 06:38:08 +03:00
CONTRIBUTING.md build: Pin dependency and CI helper source commits 2026-09-12 07:45:54 +03:00
LICENSE Initial commit 2026-03-07 11:22:31 +01:00
linker-driver-capcheck.ld refactor: split oversized modules and use semantic terminology 2026-07-25 18:33:01 +03:00
linker-driver-hold.ld refactor: split oversized modules and use semantic terminology 2026-07-25 18:33:01 +03:00
linker.ld Replace fixed deviced state tables 2026-07-29 10:51:49 +03:00
README.md feat: own the RTC one-shot lifecycle 2026-09-02 21:32:28 +03:00
ROADMAP.md feat: own the RTC one-shot lifecycle 2026-09-02 21:32:28 +03:00
rustfmt.toml deviced: drop unstable rustfmt options for stable toolchains 2026-03-23 10:19:51 +02:00
SECURITY.md docs: Synchronize shared contribution and review policies 2026-09-12 06:38:08 +03:00

deviced

deviced is the device manager daemon for EriX.

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 device service daemon. Its purpose in EriX is to provide the device 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 device service runtime and state model
  • validate startup authority before accepting IPC requests
  • handle validated service operations through the assigned endpoint set
  • materialize and run the private one-shot CMOS RTC driver through an exact RTC-only loaderd route and the ordinary driver-lifecycle procd route
  • 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.

Runtime model

  • Registry state: unsealed -> sealed.
  • Binding records, their reconcile/log state, and HAL registrations are carved from one deployment-sized arena authenticated in the packaged executable. Runtime configuration selects both record counts; page padding remains unusable and deviced receives no allocator authority.
  • BootConfig is parsed by rootd; deviced remains BootConfig-agnostic and receives lifecycle intent only through RPCs.
  • Seed operation is idempotent for exact tuple matches.
  • Drivers are not auto-started at seal/startup.
  • The signed RTC binding is explicit-start and one-shot. It carries no generic executable bounds: deviced asks its dedicated RTC-only loader endpoint for the canonical ext4 image, installs only RTC I/O and timed-provider send authority, starts it through procd, waits for clean exit, and returns zero persistent process and endpoint identity.
  • Seeded bindings for non-service-managed driver roles are rejected. Current supported drv-* roles are all expected to be procd-managed.
  • The internal lifecycle fixture drivers used by the device-manager-only scenario are seeded as already discovered so that start/stop verification can run without bringing up the separate probe/hotplug stack. Those fixture bindings do not inherit auto-start activation flags, so runtime reconciliation cannot spawn them behind rootd's explicit lifecycle checks. Their live endpoint aliases use dedicated slots after the deployed role windows and therefore cannot replace deviced's ACPI or i8042 authority sources.
  • Both lifecycle fixture binaries share the same retained-call runtime. It accepts the complete IPC transport shape, rejects and retires unwanted authority, keeps each accepted caller through its reply, mirrors the result in the envelope and typed body, and requests process teardown when receive, live-caller reply, or cleanup state becomes terminal. An already absent caller needs no reply retention; a valid stop reply precedes fixture process exit.
  • Activation is reconciled from:
    • discovery state (REPORT_DEVICE, REPORT_HOTPLUG)
    • HAL readiness (REGISTER_HAL)
    • provider visibility (REGISTER_PROVIDER for registration-based providers, or an authenticated successful driver start for per-device block providers)
  • drv-serial keeps SERIALD as the consumer-scoping role for provider resolution, but driver process startup itself no longer waits for the REGISTER_HAL round trip; that avoids a circular boot dependency while preserving the explicit consumer contract.
  • Storage mediation uses dedicated runtime peers rather than broad device authority. drv-virtio-block receives a transferred DEVICE_FRAME plus explicit memd and vspaced peers, while blockd receives separate REGISTER_HAL and RESOLVE_PROVIDER surfaces.
  • Virtio PCI capability traversal has no independent record cap: it follows the format-defined list while rejecting misaligned, out-of-range, overflowing, or cyclic records before deriving device authority.
  • HAL/provider registration and resolution derive caller identity from the dedicated endpoint used for the call; their request bodies carry no caller-selected role identifier.
  • The BAR-derivation path uses a scratch local DEVICE_FRAME slot inside deviced: the service validates the derived frame before startup-cap installation and drops that scratch slot on both success and failure paths so residual MMIO authority is not left behind locally.
  • The serial launch path creates a fresh private IRQ event endpoint in a transaction-local scratch slot. It installs separate receive-only and registration-only aliases into drv-serial, then drops the launcher alias on both success and failure paths; the public provider endpoint is never reused as an event channel.
  • Driver startup-transfer descriptors are encoded directly into the canonical transport-sized envelope and validated from those bytes. Deviced's client and server buffers expose the shared transport maximum, so neither the producer nor the caller imposes an independent descriptor or message-size ceiling.
  • Each discovered virtio-block PCI function clones the sealed activation policy into a binding keyed by its location-derived physical identity. Deviced starts one process per physical disk; an authenticated successful DRIVER_START reply is the readiness proof. Deviced retains that instance's service endpoint in its per-device alias interval, and blockd walks providers through the dedicated cursor-based resolution endpoint. Rootd never receives or retains a live driver endpoint.
  • Driver-start and provider-triggered ready logs stay queued until the outer service loop reaches its normal flush point, so synchronous log mirroring does not block concurrent control RPCs from rootd. Each housekeeping pass tries each pending role once; a failed append stays pending for the next pass rather than entering a service-local immediate-retry batch.
  • The queued logd submit path uses lib-log Rust-string submission helpers and logical LogSubmitResponseV1 results rather than deviced-local LogdSubmitStringRequestV1 frame construction, buffer sizing, or NUL scratch storage. Runtime log helpers no longer carry the primary-control slot as a hidden logging authority or enforce the retired fixed 96-byte kernel log-message cap before lib-log chunking.
  • Packaged release runtime builds select deviced-runtime-release-image, which suppresses ERIX_DEVICED:READY:* console stamps while still treating those internal lifecycle notifications as completed.
  • The runtime service loop rotates fairly across the split admin and public endpoint families. Multiplexed receives remain nonblocking, idle passes yield, and a deployment with only one live receive endpoint waits without a service-local deadline. Startup and validation fixtures likewise wait for their required messages without fixed tick ceilings. Each completed request advances the deployment-owned reconciliation batch. There is no report-stream follow-on polling budget or unreachable zero-length burst loop. Successful provider registration still publishes readiness and gives one waiting admin request priority before more driver work.
  • Every typed deviced response mirrors its operation result in the outer IPC envelope. Clients validate request correlation and envelope/body agreement before applying operation-specific policy; a contradiction is a contract failure.
  • READ_DRIVER reconciles pending startup work before forming its reply and only re-delegates a runtime endpoint once provider visibility is established for roles such as drv-serial. RESOLVE_PROVIDER uses the same targeted reconciliation for the requested provider binding before it scans unrelated pending work, letting blockd receive the block provider endpoint without routing that driver authority through rootd. Post-reply readiness publication and admin draining are registration completion work only; a provider lookup returns directly to the receive loop and cannot recursively generate registration diagnostics.
  • The dynamic-link VM profile can enable deviced-integration-defer-background-reconcile, which lets public discovery, HAL, and provider reports update state without arming autonomous driver activation. In that profile, driver launch remains tied to rootd's explicit admin READ_DRIVER/START_DRIVER verification path.
  • Dynamically packaged drivers are created with procd's staged-create ABI. deviced derives the executable identity from the driver role, sends only the explicit child endpoint and install grant, and does not reuse boot-module-table authority for those dynamic driver launches.
  • Procd calls expose deviced's complete writable IPC buffer, then enforce the exact typed response size. Ordinary start, abort, stop, and logging replies must be authority-free. A successful staged create must carry exactly two kernel-normalized receipts, in order: the request-selected generic child endpoint with exact SEND | RECV | GRANT rights and the request-selected install grant with exact GRANT | MINT rights. Deviced queries both installed aliases before retaining them and drops every unexpected, mismatched, negative-reply, or parse-failure receipt.
  • Required procd and driver calls retain the one caller-owned buffer after kernel acceptance and wait for the authoritative reply without duplicate submission or a service-local timeout ceiling. Reply delivery follows the same retained-buffer rule through transient kernel backpressure.
  • Startup and runtime callers must send authority-free request envelopes. Deviced retires every unexpected receipt with the shared complete-drop policy before rejecting the request, and process teardown closes the whole CSpace whenever receipt cleanup or accepted reply delivery is uncertain.
  • Runtime request preservation and capability-bearing replies use separate full-transport static buffers. Their capacities therefore follow the IPC transport contract instead of imposing private 128-byte request or 512-byte transfer-reply ceilings.
  • Informational logd work makes one nonblocking pre-acceptance attempt. A busy endpoint defers the log to existing housekeeping policy; an accepted request retains its buffer until completion and is never abandoned.
  • The storage/filesystem dynamic-port profile extends that driver path to drv-virtio-block. The virtio-block driver executable identity is derived from the sealed driver role, and the staged child receives only its explicit primary-control endpoint, physical device authority, required memory peers, and install grant. The block provider has no deviced registration peer; successful authenticated driver start is the readiness and visibility transition for that physical instance.
  • Start is denied for unknown/unsealed/running bindings.
  • Stop is denied for unknown/non-running bindings.
  • Responses are deterministic and panic-free.
  • Driver start/stop goes through procd in the current slice:
    • start uses CREATE_STAGED_DYNAMIC_PROCESS, install-grant materialization, and START_STAGED_PROCESS
    • stop uses STOP_PROCESS with the exact nonzero staged generation returned by create, so delayed cleanup cannot stop a reused process id
  • deviced now reaches procd for driver lifecycle through the dedicated local endpoint slot SLOT_PROCD_DRIVER_ENDPOINT (2112), validated via local capability introspection, instead of resolving the generic procd service endpoint through named.
  • deviced's primary local control slot is now the dedicated kernel query-only endpoint SLOT_KERNEL_QUERY_ENDPOINT, which is limited to startup validation. deviced no longer receives kernel log output authority, and durable driver-ready and integration-proof records are submitted to logd.
  • deviced now stages probed from the explicit kernel platform-discovery-read endpoint and stages drv-acpi, drv-serial, and drv-i8042 from dedicated local AcpiRsdpRead, Com1Io, and I8042Io source endpoints.
  • Shared platform/device-I/O startup source slots are no longer part of the runtime driver contract.
  • deviced receives only the grant-bearing IRQD COM1 handler endpoint so drv-serial can receive that line-bound registration/ACK object through the staged child install-grant path. Deviced receives no IRQD registry, seal, or mask-control authority.
  • Driver startup peers are validated from delegated fixed slots, not from ambient slot occupancy or synthetic named identity. drv-serial receives only its authenticated log producer client; each PS/2 driver receives only its event-kind-specific input producer client and matching i8042 stream-read client.
  • The i8042 keyboard and mouse endpoint sources are retained solely for managed child creation and restart. deviced installs receive-only aliases in drv-i8042 and send-only aliases in the matching translator, never includes those source slots in its RPC receive scan, and no longer serves PS/2 READ_DRIVER discovery.
  • The staged-driver constructor compares selected peer destinations and primary-control sources with their declared role routes before using the install grant. Omitted or substituted routes are rejected before unintended authority can enter the child.
  • Optional logd use validates the delegated slot through an explicit caller-local query operation before every submission. Deviced validates this fixed client directly and has no service-directory dependency.
  • deviced no longer advertises or receives delegated CNode/TCB/VSpace management caps in the current runtime slice; driver lifecycle authority is reduced to the dedicated procd driver endpoint plus device-side authority.
  • Seeded driver metadata no longer includes a scalar device-cap source slot. Driver hardware authority is expressed only through explicit transferred startup caps; deviced does not rely on unrelated boot metadata as a placeholder device cap.
  • Driver spawn requests now carry procd's explicit local image-frame slot instead of reusing the canonical boot-module-table slot value.
  • 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.
  • The dedicated local driver-lifecycle and primary-control slots are now real copied endpoint aliases materialized by rootd, not implicit conventions.
  • Legacy synthetic direct-process-management fallbacks have been removed from deviced; unsupported driver roles are denied at seed/start/stop time.
  • Startup-failure cleanup for supported service-managed drivers now also goes back through procd STOP_PROCESS, not a direct kernel destroy path.
  • Service-driver startup uses one synchronous RPC per process instance. Transport failure and terminal endpoint retirement proceed directly to cleanup instead of retrying the same request an arbitrary number of times.
  • Every driver-start failure runs all independent cleanup operations. A failed staged abort, process stop, grant revocation, endpoint revocation, or scratch device-frame revocation quarantines deviced; the service exits before accepting another request so kernel process teardown removes authority whose lifetime can no longer be proven.
  • Deviced drops its own grants, endpoint aliases, and scratch capabilities with the local CSpace syscall. The child-population control endpoint remains necessary only for capability copy and staged-child installation.
  • The helper driver binaries used by integration/validation also fail closed if bootstrap omits the startup endpoint argument; they no longer bind to conventional driver-window slots or abandon a required startup receive after a fixed tick budget.

Build

cargo build --all-targets --all-features

Test

cargo test --all-targets --all-features
cargo clippy --all-targets --all-features -- -D warnings

Build and Test Modes

This repository supports additive runtime and integration feature gates.

  • deviced-runtime and deviced-runtime-release-image select production bootstrap/runtime behavior.
  • *-integration-smoke remains available for synthetic end-to-end validation paths.
  • *-integration-force-fail remains test-only and MUST NOT be enabled for runtime appliance builds.
  • explicit driver primary-control omission/misroute features remain integration-only; they exist solely to prove fail-closed startup when staged driver authority is the wrong shape
  • the drv-serial log-peer misroute feature remains integration-only and proves that a generic log slot cannot replace the authenticated fixed client
  • integration evidence uses deviced's ordinary authenticated logd route and requests no explicit sink-routing authority; production logging policy remains unchanged

The smoke-only READ_DEVICE reply markers remain cfg-scoped to deviced-integration-smoke, so runtime/release appliance builds keep the provider-registration reply path warning-free. The reply-path request-op decode is now shared by runtime and smoke builds so the provider-registration fast path compiles identically in appliance and integration configurations. Stale smoke-only debug shim functions for legacy serial/logd marker emission have been removed; remaining lifecycle diagnostics use the real queued logd submission path.

Dynamic Boot Artifact Evidence

The image build packages deviced as a dynamic boot artifact: 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 through procd staged dynamic creation before dynlinkd; authority remains rootd-admin RPC, the dedicated query-only endpoint, and explicit child-populate/lifecycle peers. 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. deviced receives only the documented startup endpoints, peers, and capabilities for its role.

Governance Principles

deviced governance is scoped to device discovery, driver matching, and provider publication.

The scoped governance rules are:

  • It owns device-to-driver matching policy while drivers own hardware-specific behavior.
  • It must start driver services only through procd and transfer only the matched device authority.
  • It keeps provider publication explicit: registration-based providers must authenticate a registration call, while per-device block providers must complete authenticated startup and can be resolved only through the caller-scoped cursor API.
  • It rejects malformed manifests, duplicate providers, and unauthorized driver starts.

Authority Boundaries

  • deviced operates only through startup-assigned service capabilities.
  • New authority must be represented in bootstrap/capability validation and integration tests before use.

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.