Skip to content

R2Kernel™ 1.0.0rc9

RC9 is a prerelease for evaluation, not general operating or hard-real-time approval. It adds explicit installer options for every choice plus --non-interactive; a release that requires license acceptance, including the published runtime, still needs interactive review. It also adds subsystem self-binding, launch-time bindings, per-instance CPU claims and brokered sockets for multi-process packages, and it defines federation readiness by peer acknowledgement. RC8 remains an immutable historical prerelease; its artifacts and tags are not replaced. Upgrade the runtime and SDKs together, and upgrade the hosts of a federation together. Local Control remains protocol v5 without mixed-version fallback or automatic replay.

Upgrade notes

  • Real-time priority of managed packages. The launcher bounds each managed package's RLIMIT_RTPRIO to its admitted CPU claim (0 for policy: other or no CPU claim). A limit inherited from the service unit or a container does not let a package raise its own real-time priority. Declare the priority in the package's resources.cpu, or per instance with r2kernel launch run --resources or the REST start resources field. See Real-Time Scheduling.
  • Federation. A peer is listed in subscriptions_synced only after it acknowledges this host's current subscription request. Peers without acknowledgement support stay connected but never satisfy readiness, so upgrade all hosts of a federation together.
  • REST package routes. POST /api/v1/control/packages/{id}/start and restart reject unknown fields; stop accepts only instance_id. Other fields return 400.
  • Strict resource claims. resources in package.yaml and every claim under it reject unknown keys. Examples of such keys are a misspelt key such as sandox or netwrok, a capabilities list outside grant, or an extra cpu key. RC8 ignored them, so a misspelt sandbox ran the package without its brokered sandbox. A package that RC8 accepted with such a key is now refused when it is validated, installed or launched, with unknown field at line <N> column <M>. Correct the key at that line and install the package again.
  • Strict package manifest. Every top-level key of package.yaml must be one of schema_version, name, version, description, subsystem_manifest, entrypoint, parameters, resources, frontend, contracts, lifecycle and metadata. RC8 ignored other keys, so a misspelt resources section (for example resouces:) ran the package without its claims and sandbox. Such a package is now refused when it is validated, installed or launched, with unknown field at line <N> column <M>. Keep custom data under metadata.
  • Package parse errors. A parse error of package.yaml, of a subsystem manifest or of a Gateway role check names the file, the kind of error and its line and column, for example invalid type at line 3 column 13; it no longer repeats a value, key or key path of the file. Tools that matched the old message text must match the location instead.
  • Subsystem name length. A subsystem name has at most 128 UTF-8 bytes and no NUL character, the limit of a Control source identity. RC8 registered a longer name, which could never use managed Control; Control now refuses it at registration, and its refusal does not repeat the name.
  • Link-layer capability grants. A resources.link_layer capability (CAP_NET_RAW, CAP_NET_ADMIN) is granted only when the resource authority policy approves it for the claimed interface in link_layer_grants. A package that relied on its manifest alone is refused at launch until the operator adds the entry, for example {"interface": "enp5s0", "capabilities": ["CAP_NET_RAW"]}, to link_layer_grants in /etc/r2kernel/resource-authority.json and runs sudo systemctl restart r2kernel-resourced. A policy without the field approves nothing; a policy the RC9 installer creates has an empty link_layer_grants. The capability applies to the whole host. See Resource Authority.
  • Resource service mode. resource_service.mode: auto and required both require a running r2kernel-resourced; Control's startup error names the mode and the remedy. Use disabled only for standalone Core development.
  • Managed launch. The resource service creates each package directly inside its lease scope with clone3 (Linux 5.7+). Where clone3 is unavailable, for example under a container system-call filter that blocks it, managed launches are refused.
  • Installer. --install-token is deprecated; use --install-token-file PATH or R2KERNEL_INSTALL_TOKEN.
  • SDK. EventTransport.apply_state rejects a non-empty remote_subscriptions; send-only users keep calling recv_event(peer, timeout_ms=0) for every connected peer; timeout_ms=0 does not wait on the network (it can wait for the transport lock behind a concurrent flush_n or blocking recv_event); one call applies at most 64 setup frames and returns None (C: NotFound) when no Event is available, and the next call continues. JSON produced by the C++ SDK escapes every control character (U+0000–U+001F).
  • Timed-request late policy. late_policy is exactly drop, reject or execute_immediately (codes 0, 1 and 2); any other value is refused on send and on receive and never falls back to execute_immediately. C++: TimedExchangeTimingOptions::late_policy set to an empty string is refused instead of meaning execute_immediately. Python: a late_policy with surrounding spaces is refused instead of being trimmed.
  • C ABI Event receive. r2kernel_event_client_recv/_recv_ex keep an Event whose topic, publisher or payload does not fit the caller's buffers at the head of the queue and return BufferTooSmall (-4) with every required length in the length outputs; retry with buffers of at least those sizes to receive it. A caller that retries with the same buffers receives -4 again without waiting, so a receive loop built against RC8 or earlier, which skipped any non-zero result, spins on that Event and receives no later Event. Upgrade SDK builds and C ABI callers together with the runtime library.
  • C ABI federation transport results. r2kernel_event_transport_accept_peer, _apply_state_json and the _recv_event calls no longer consume their item when an output buffer is null or too small for a non-empty value: they return BufferTooSmall (-4) with every required length and keep the item for the next call (one kept Event per peer; a kept apply_state report's connected/disconnected transitions are reported again, ahead of its own, by the next apply_state_json). A size query with a null buffer no longer discards the item; call again with buffers of the reported sizes. (r2kernel_event_client_recv with a null buffer still skips that field.) r2kernel_event_transport_accept_peer can return R2KERNEL_EVENT_TRANSPORT_ALREADY_CONNECTED (1). This is not an error: the duplicate connection was closed, the peer stays connected on its existing session, and out_peer_id is not written. A caller that treats every non-zero result as a failure must handle 1. r2kernel_event_transport_flush_n returns SystemError (-3) instead of -2 when a send expires, and closes that peer's connection. Federation and Timed Exchange sends never raise SIGPIPE: a send to a closed peer fails with SystemError (-3), so a C or C++ host does not need to ignore SIGPIPE.
  • C ABI federation transport functions. New r2kernel_event_transport_recv_event_timeout takes an explicit wait: timeout_ms 0 never blocks (it processes frames already delivered, at most 64 setup frames per call, and returns NotFound (-2) when no Event is available), a positive value waits at most that long, a handled signal (EINTR) during that wait neither ends it early nor closes the session, and a negative value returns InvalidArgument (-1). recv_event and recv_event_ex still wait up to the transport's io_timeout_ms. Ten new functions split accept and apply into steps that wait on the network without holding the transport: r2kernel_event_transport_session_setup, r2kernel_event_session_setup_accept, r2kernel_event_session_peer_id, r2kernel_event_transport_install_session, r2kernel_event_session_close, r2kernel_event_session_setup_close, r2kernel_event_transport_apply_begin_json, r2kernel_event_apply_plan_connect, r2kernel_event_transport_apply_finish and r2kernel_event_apply_plan_close. A transport handle has at most one open apply plan: while one is open, apply_begin_json and apply_state_json return InvalidArgument (-1). A plan closed with r2kernel_event_apply_plan_close instead of r2kernel_event_transport_apply_finish leaves the apply incomplete; the peers its begin already disconnected are listed in disconnected by the next apply. The signatures of existing functions are unchanged.
  • Signals during federation waits. A signal handled while recv_event, recv_event(timeout_ms>0), accept_peer or apply_state of an EventTransport, or the matching C ABI function, waits on the network no longer fails the call or closes the session: the wait continues with the time left. In Python a Ctrl+C during such a wait therefore raises KeyboardInterrupt only after the wait returns, at the latest when its timeout (io_timeout_ms for timeout_ms=None) has passed.
  • C ABI timed requests. r2kernel_codec_encode_timed_exchange_request, r2kernel_codec_decode_timed_exchange_request and their _v2 forms return InvalidArgument (-1) for a late-policy code other than 0 (drop), 1 (reject) or 2 (execute_immediately), on encode and on decode, in both envelope versions. A caller that passed another code, or received a request carrying one, now gets -1 instead of a request.

Installation

The installer uses every choice given as an option or R2KERNEL_* environment value and asks only the remaining questions. Every value option accepts --option VALUE and --option=VALUE. --non-interactive (alias --yes) never prompts; a missing decision, such as --existing-prefix overwrite|abort for a non-empty prefix, is an error that names the option. License acceptance remains manual and interactive only: --non-interactive refuses a release that requires license acceptance, which includes the published runtime release.

install.sh parses every argument and checks the prefix location with the verified installer before any sudo prompt; checks that need the release, the prefix contents or root run after elevation, before anything is downloaded or installed. It elevates only for a protected prefix (root-owned, not a symbolic link, not group/other writable), forwards exactly the installer's documented R2KERNEL_* values through sudo and never the install token, uses sudo -n with --non-interactive, removes its temporary directory on exit or interruption, and exits with the installer's status. An installer running as root refuses a prefix that is not protected; re-run install.sh with --prefix PATH for a user prefix. The prefix must be a dedicated directory, and an existing prefix is replaced only where an earlier R2Kernel install created it. install.sh pins the SHA256 of the installer and of release-manifest.json and the release version.

r2kernel-uninstall removes a no-service prefix that the calling user owns without sudo, after checking that nobody else can change the path to it; root refuses a prefix another user owns. Entry-point links written through another spelling of the prefix, or for a relative --prefix, are removed with the prefix. Installing, upgrading, uninstalling and running r2kernel-doctor no longer write __pycache__ directories into <prefix>/runtime/tools. The installer runs its service-upgrade coordinator, often as root, with Python in isolated mode (python3 -I -B): a module in the directory the installer was started from is never imported, PYTHON* environment variables and the user site directory are ignored, and no bytecode is written. r2kernel-uninstall refuses a user-owned prefix when it or a directory on the path to it has a POSIX access ACL.

See Installation.

Bindings

A registered App or Component subsystem may bind its own declared requirement when control.self_binding.mode: local (the default). Self-bindings target only local providers, never replace an operator binding, explicit unbind or live launch binding intent (an intent whose lease is still carried by a current registration), are refused for Gateway or exclusive-arbitration targets already bound by another source, and are removed when the source registration ends or re-registers. An operator binding takes precedence, and an applied launch binding intent displaces an exclusive self-binding, as an operator binding does. By default the source must be a member of a managed lease. Bindings are a registry and routing contract; local Control access is bounded by caller authentication and the runtime IPC group, not by bindings.

Setting a binding for a source that is not registered is refused, and the response carries binding.state: "source_missing" so deployment tools can retry. To declare bindings before a node starts, pass them with the managed start (r2kernel launch run --bind, or bindings in the REST package start). They are validated against the package manifest, applied right after that instance's own process has registered (the registration reply does not wait for them), reused by restart and runtime apply of the running instance, and discarded when the instance's lease ends. Stopping an instance removes its record, so a later start must send its bindings again. Binding rows report their origin.

See Subsystem Binding Model and Subsystem SDK Guide.

Resource control

  • Per-instance CPU claims. A managed start can replace the package's resources.cpu for one instance. The claim is validated and admitted like the manifest claim, kept with the running instance's record for restart, and reported as cpu_origin. resources.cpu_instance_override: forbidden refuses it.
  • Brokered sockets for multi-process packages. A package leader can pass brokered sockets to its child processes through R2KERNEL_INHERITED_FDS; the Python and C++ SDKs validate the inherited descriptor without any broker fallback. resources.sandbox.fd_delegation: lease_scope lets every process of the lease scope request network and CAN sockets directly (Linux 6.5+); device descriptors stay leader-only.
  • Brokered sandbox diagnostics. Packages see R2KERNEL_SANDBOX_NETWORK=brokered (and R2KERNEL_SANDBOX_CAN=brokered); the kernel logs denied socket calls subject to kernel.seccomp.actions_logged. A brokered sandbox also denies the socket families that reach the network without AF_INET/AF_INET6 and the io_uring system calls.
  • Stops and cleanup. The resource service keeps serving other admitted requests while a stop runs. A stop takes at most about 9 s (3 s graceful, 2 s forced, 2 s exact-child reap, 2 s scope retirement); clients wait up to 10 s and then report the outcome as unknown while the operation continues. A failed cleanup is retried automatically with back-off (1–30 s) and does not consume launch capacity; at 64 retained reservations new launches are refused explicitly. Exhaustion of the request queue, reply capacity or incomplete-request capacity (including rotation of a peer's oldest incomplete request) is an explicit refusal to a connected requester.
  • Manifest reads. package.yaml and the subsystem manifest are regular files of at most 1 MiB; a file's type is checked before it is opened, so a FIFO never blocks and a device node is never opened. A parse error names the file, the kind of error and its line and column and never repeats text of the file. These limits do not cover the spec and role documents named under Known limitations.
  • Package reads with the manager's identity. The resource service reads a package with the user, group and supplementary groups of the manager that asked for the launch, never with more access than the requesting manager has: the package directory, package.yaml, the subsystem manifest, the Gateway role check and every path that validation checks. A launch of a package the manager cannot read is refused, even when the package account could read it; only the instance's runtime directory and log files are created as root, for the package account. When the resource service prepares a launch, the Gateway role check runs in a separate process with the requesting manager's identity, a 256 MiB data limit and the 5 s launch preparation deadline, so a FIFO or an endless file there fails the launch and no longer blocks or grows the resource service.
  • Package runtime state. On an installed runtime an instance's runtime directory is <prefix>/var/run/packages/<uid>/<instance-id>, owned by the package user. Stopping the instance keeps it with its logs. GET /api/v1/control/packages answers 500 with "code": "package_store_unavailable" when Control cannot open, lock or read the package store.
  • Install root. The symlinked package install-root refusal names packages.install_dir, the active configuration file and the resolved path to configure. Symlinked install roots remain unsupported.
  • Containers. Container Deployment describes the supported arrangement: Control, the resource service and the packages in one PID namespace with a writable, delegated cgroup v2 subtree. Container runtimes are not part of release qualification.

See Resource Control.

Federation readiness

subscriptions_synced lists the peers that acknowledged, on the current native connection, this host's latest subscription request with a matching digest. A peer leaves the list on reconnect or after a local subscription change until it acknowledges again. Outstanding and unsupported acknowledgements are reported as subscription_ack_pending and subscription_ack_unsupported. A subscription request is sent only when the set changes, on a new connection, or as a retry after two control status intervals.

Security-mode hints only choose which host initiates first. A hint change never closes an established connection, except that an authenticated optional/required record closes a plaintext session that peer id initiated. Compatible pairs connect even with a missing or wrong hint within two control status intervals plus the connect time. A disabled host and a required host never connect; required never accepts plaintext. While a peer record is authenticated and fresh, an unauthenticated snapshot for that peer is ignored. Failed outbound connects back off from 1 s, doubling to 30 s per peer (connect_backoff). When two hosts dial each other at once, both keep the connection initiated by the lexically smaller host id; the losing connection is counted in duplicate_sessions_closed. Federation connection setup (accept, handshake, connect) runs outside the Event data-path lock, so a silent TCP client no longer delays Event forwarding or receive. A peer that closes its connection in the middle of a frame is disconnected at once; the complete frames it sent before are still delivered. Each Event send ends within the federation I/O timeout, also for an Event written in several parts; a peer that has not taken a whole Event by then is disconnected and counted in send_deadline_disconnects, and its queued Events are discarded (counted in dropped). With federation security optional or required, the backend raises federation.io_timeout_ms and federation.connect_timeout_ms to at least 3000 ms (default 200 ms and 250 ms), so the Event send deadline and the pre-authentication accept budget cap are then at least 3 s. One flush batch starts no send after one I/O timeout. Per control tick, pre-authentication accepts take at most half the control status interval (capped by the I/O timeout); the pending connections' handshakes progress together, so a connection that stalls at any handshake step (silent, or a ClientHello and then nothing), or that its peer reset before the accept, cannot hold back a peer queued behind it within the tick's accept limit (16 connections, or one per configured peer if there are more); a connection without a first frame within half that budget, or without a later handshake frame within the budget, is closed. These connection rules are verified with two controllers on one host.

See Deployment Checks.

Time sync, Terminal and HTTPS settings

Time-sync probes have a 2 s deadline, the supervisor stops the processes it started when it exits normally, on SIGTERM or SIGINT, when its parent exits, and on an error return (on a signal or parent exit, before it writes its final status; the kernel also ends them when the supervisor is killed with SIGKILL), and a managed companion that exits is restarted with back-off from 1 s to 30 s. Control and r2kernel timesync doctor accept the time-sync service status as clock evidence only when the running service wrote it recently; otherwise they report why it was rejected. A Terminal login at the session limit is reported in the Console. The Console waits up to 30 s in total for the HTTPS settings service, refuses a request that cannot start within that time as busy, and reports an unanswered save as possibly saved. Control's HTTPS listener runs at most 32 TLS handshakes at once and at most 4 from one source address. All Control HTTP listeners together keep at most 256 connections open and at most 64 from one source address, and close a connection that sends no complete request head within 10 s of being established or of its last response. Request bodies are limited to 2 MiB except for package upload; the package API proxy also bounds body arrival time. The package API proxy now refuses upstream responses over 2 MiB with 502, the package WebSocket proxy and the Control event/control WebSockets refuse messages over 2 MiB, and a proxied WebSocket is closed, whatever the package sends, when for 60 s its browser sends no frame (including the answer to the proxy's 30 s ping) and takes no message the proxy had to wait to send; a page keeps its session while the proxy's messages to it have to wait for its connection to accept more data and each then gets through within 60 s, and a page that has gone away is closed at the latest about 120 s after its connection stopped accepting data. One source address holds at most a quarter of the package API and WebSocket proxy slots. The local Unix control socket gives one local user at most 16 MiB of its 64 MiB request-byte budget. An accept error such as too many open files on the listeners named in Runtime Configuration pauses that listener for 1 s instead of stopping Control. A federation accept failure of one connection (for example a peer that reset first) does not end that control tick's accepts; only a listener-level error such as too many open files does. Both are logged at most once per 5 s. On the local Unix control socket, requests that fail on the socket itself, failed Event resource operations and failed notification deliveries are each logged at most once per 5 s with the count since the previous line.

See Time Sync Operations and Web and API Security.

SDK

Unprepared codec calls (before initialize() or without a managed Subsystem) perform a single conversion per call and still re-read the selected spec roots on every call, with a one-time warning. For repeated use, initialize the Subsystem or create a SchemaContext and PreparedCodec. A lifecycle hook that returns false is logged with the phase and the last warning or error logged during the hook by that hook's own thread. C++ frame-transform notifications, and heartbeat status sent through a CoreClient other than the native R2KernelClient, are bounded at 100 ms in total per notification (each socket call gets only the time left).

A running Python subsystem, or a C++ subsystem with the native R2KernelClient, stays registered across a Control restart: when Control does not acknowledge a heartbeat status, the subsystem registers again with its unchanged session, process ID, lease and notify endpoint and restores its status, Event publications, Event subscriptions and Control handler; self-bindings are not requested again. After a failed attempt, the next one is due 1 s, 2 s and then every 4 s later and runs in the first heartbeat after that time. Control waits at most 750 ms for the resource broker to prove the lease of a registering process and asks again every 100 ms while the broker is absent, restarting or busy; any broker answer decides at once. A first registration does not wait for the Event resource work of other subsystems; a registration that replaces an expired one is answered within 1.5 s, if necessary with a refusal to retry that carries reason registration_gate_busy or registration_cleanup_pending. The Python and C++ SDKs retry these refusals: at startup within R2KERNEL_STARTUP_RETRY_COUNT attempts (default 20) R2KERNEL_STARTUP_RETRY_DELAY_MS apart (default 100 ms), and later from the heartbeat. When Control kept a C++ publication's shared-memory slots, the publisher continues its Event generation after the re-registration, so subscribers receive exactly the Events published after it. A lifecycle change (fault(), degrade(), pause(), a transition that Control requests) sends one status update and never waits for or starts a re-registration (Python, and C++ with the native R2KernelClient); while a heartbeat runs or the registration is being restored, the update is left to the next heartbeat with an info line, and an update Control does not accept is logged as an error and the next heartbeat registers again. In C++, a lifecycle status left to the heartbeat when terminate() or reset() stops it is not sent (see Known limitations). The Python SDK removes its Control notification socket when the process exits normally or is terminated by SIGTERM with its default action. Only the process that created a notification socket removes it: a child forked from that process (os.fork, or multiprocessing with the fork start method, including Pool workers) removes none of its parent's sockets when it exits, is terminated or closes an inherited client, and it gets back the default SIGTERM action. Such a child also leaves its parent's Control connections open and never waits for a lock another thread of the parent held at the fork; it cannot use the parent's prepared Control lanes and needs its own client. At start, Control removes the notification sockets of its own user that killed SDK clients left behind, and the resource broker removes those of every client identity it serves, in each case only when the socket's process no longer exists and nothing listens on it.

When an exchange on a prepared Control lane misses its deadline after it reached Control, or Control closes or resets the lane's connection (end of stream before a frame, or a send that fails with EPIPE or a receive that fails with ECONNRESET), the Python and C++ SDKs fail that exchange without replay and re-prepare the lane once, within its preparation timeout, with its original capacities and completion mode. A transaction failure that Control reports in its reply is not re-prepared, although Control then closes the connection. The late exchange is still a failed exchange. Re-preparations are counted as prepared_lane_recoveries and prepared_lane_recovery_failures.

The C++ SDK refuses an Operation start received in JSON form whose timed_late_policy is not drop, reject or execute_immediately (invalid_argument) instead of passing it to the handler.

See Subsystem SDK Reference.

See Typed Payload and Codec Guide.

Startup deadline and cancellation

Managed startup applies the original transaction deadline to preparation, process startup, gate delivery and response delivery. Preparation errors do not receive a fresh response budget. A caller disconnect, authorized cancellation, expiry or shutdown prevents an unfinished startup from being committed, while other admitted operations continue to progress. Capacity exhaustion is an explicit refusal, not an unbounded accumulation of launch work.

Reply expiry and process cleanup are different contracts. Cleanup has a separate bounded teardown phase; an expired reply does not prove that every resource is already retired. Cleanup failure retains exact process/resource recovery identity and reports failure rather than silently releasing authority. A late process-start result must still be owned and cleaned up. An incomplete startup frame does not authorize application execution; a partial reply does not permit a second response or automatic replay.

These are transaction and ownership guarantees, not a universal filesystem, kernel scheduling or host WCET bound. Broker-owned foreground success still requires whole-scope completion, exact child reap and resource release.

Current-run Event readiness

Gated fixtures require this attempt's publisher startup acknowledgement before opening its gate. Use a fresh private 0700 run directory, an absent gate and a unique topic/publisher identity for every attempt. The helper must observe the current publisher registration; an already-ready topic owned by another publisher or an unobserved name is insufficient. An absent, stale or mismatched acknowledgement leaves the gate closed.

Existing-path refusal occurs before SDK registration. Neither participant deletes a caller's old gate to make it fresh, and the helper creates only an exclusive empty 0600 gate after all required checks. Follow the shipped fixture README for the complete invocation and identity inputs.

Explicit cross-host readiness requires both sender and receiver status, actual native peers and acknowledged subscriptions on both hosts. Requested clock checks must pass on both hosts. Diagnostic topology fallback never approves one-way latency evidence. Gate success is neither receiver callback evidence nor a performance qualification. The native connected list reports new connection transitions, not current membership.

Event buffering remains bounded and may drop data under overload. Use supported queue-depth and payload-capacity settings for the workload, and inspect sequence gaps.

Mixed-mode Event connections

For a Disabled/Optional native Event pair, the Disabled host initiates the plaintext connection, independent of host-name ordering or publication direction. Optional keeps its secure outbound policy and accepts this permitted plaintext inbound connection. The advertised transport mode is only a connection-planning hint. It does not authenticate a registration or grant remote Control authority. Required remains secure-only; a failed secure handshake never authorizes a plaintext retry.

External HTTPS configuration

Protected external configuration remains readable through the HTTPS settings service. Successful inspection does not imply permission to save. The response separately reports write capability and a read-only reason; the Console shows saved/running values while disabling connection changes, PEM inputs and Save when writing is unavailable. External settings writes are refused before configuration or TLS-material mutation.

Known limitations

These defects exist in RC9 and are not fixed in it.

  • A package holding any brokered CAN socket is not confined to its claimed interfaces: it can bind the socket again or name another interface in each send and so reach every CAN interface of the host, including interfaces another package claims exclusively, and a package without resources.sandbox.can: brokered can open raw CAN sockets itself, because Linux requires no capability for them.
  • A brokered UDP socket is bound to its claimed adapter, but its holder can name another interface for each datagram (IP_PKTINFO) and so send through every adapter of the host, including adapters another package claims exclusively.
  • An approved CAP_NET_RAW or CAP_NET_ADMIN link-layer grant is ambient and host-wide in the package process: it applies to every interface, CAP_NET_ADMIN amounts to host network administration, and the declared protocols are not enforced.
  • Core reads the spec files a package supplies without a size limit or a file-type check (the spec documents it resolves from the spec roots, such as message sets, API specs, defaults.yaml and prepared-context sources, the prepared codec's defaults.yaml, and the Gateway contract documents and subsystem manifest read to check a gateway's role), so a FIFO in place of such a file blocks the reading thread and a device or very large file is read without limit.
  • r2kernel package validate, r2kernel launch run and package installation in Control run the Gateway role check in their own process without a deadline or size limit, so a FIFO or an endless file among the Gateway files of a local package directory or in a spec root blocks or grows that process (archives installed through Control cannot contain FIFOs or symbolic links).
  • A launch preparation helper thread of the resource service (it has 4) can be held by a metadata call such as stat on a filesystem whose operations block, for example a FUSE mount that the manager controls inside the package directory, because the 5 s preparation deadline frees the reply but not the thread.
  • When Control cannot open, lock or read the package store, package install, start and stop answer 400 with the error text, and only GET /api/v1/control/packages answers 500 with "code": "package_store_unavailable".
  • Uninstalling a package does not remove its instances' runtime directories under <prefix>/var/run/packages/<uid>/, so remove them as root when the instance ID is not used again.
  • Control writes each failed transport-resource cleanup step, and the UDP management listener each notification it sends, as its own log line without a rate limit, and Control sets no length limit of its own on Event topic names.
  • Heartbeat status updates, Event publish and subscribe requests and Timed Exchange handler registration still wait without a bound behind operations that hold Control's Event resource gate across resource broker requests, so a stalled resource broker makes the SDKs' restores time out and retry.
  • The Control WebSockets /api/v1/ws/events and /api/v1/ws/control have no ping, idle limit or send timeout.
  • A C++ child process created with fork() that destroys, or exits with, an inherited R2KernelClient ends the parent's Control lanes: the parent's next call on such a lane fails and re-prepares it.
  • A C++ lifecycle status left to the heartbeat when terminate() or reset() stops the heartbeat is lost, because no next heartbeat runs, so Control shows the previous lifecycle state and reports the session as inactive after control.subsystem_timeout.
  • Control also runs the in-process Gateway role check on every read of its package store for each package under packages.additional_paths, not only at installation.
  • A control tick attempts at most 16 federation accepts, or one per configured peer if there are more, failed attempts included, so reset or silent connections that fill this limit delay the peers queued behind them to the next tick.

Qualification boundaries

The declared local C++ acceptance profile is Required-authority raw-eight-byte echo, period and caller/scheduled-completion deadlines of 1 ms, Core FIFO99 on CPUs 0 and 1, and caller/handler FIFO80. Every scheduled slot, skip, failure, caller deadline, scheduled-completion deadline and after-window completion belongs to the result; no catch-up, replay or deadline relaxation converts a failed run into acceptance. Measured results accompany the RC9 release page. On a virtual machine without CPU isolation, strict 1 ms results depend on the guest kernel and hypervisor; see the host conditions in Real-Time Scheduling.

Python is outside strict 1 ms periodic acceptance. Guest affinity does not prove physical CPU/hypervisor isolation. Finite synthetic results do not establish general WCET/hard-real-time, cross-host 1 kHz Control, physical actuator completion, hardware PTP accuracy or native ARM execution. Container runtimes are not part of release qualification. Benchmark demos remain withdrawn.