# OPREG/PEL1-KERNEL — Kernel Operation Registry for PEL/1 Status: Approved Owner: Niklas Rydberg Version: 0.1.1 SoT: Yes Last Updated: 2025-11-16 Linked Phase Pack: N/A Tags: [registry, execution] **Document ID:** `OPREG/PEL1-KERNEL` **Layer:** L1 Profile (Operation Registry for `PEL/1-CORE` + `PEL/PROGRAM-DAG/1`) **Depends on (normative):** * `ASL/1-CORE v0.3.x` — `Artifact`, `TypeTag`, `Reference`, `HashId` * `PEL/1-CORE v0.1.x` — primitive execution layer core * `PEL/PROGRAM-DAG/1 v0.2.x` — DAG scheme for PEL * `HASH/ASL1 v0.2.x` — ASL1 hash family (for `HASH-ASL1-256`) **Integrates with (informative):** * `SUBSTRATE/STACK-OVERVIEW v0.1.x` * `ENC/PEL-PROGRAM-DAG/1` (canonical encoding of Program) * `PEL/TRACE-DAG/1` (optional trace profile) * Higher-level operation registries (domain-specific ops) © 2025 Niklas Rydberg. ## License Except where otherwise noted, this document (text and diagrams) is licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0). The identifier registries and mapping tables (e.g. TypeTag IDs, HashId assignments, EdgeTypeId tables) are additionally made available under CC0 1.0 Universal (CC0) to enable unrestricted reuse in implementations and derivative specifications. Code examples in this document are provided under the Apache License 2.0 unless explicitly stated otherwise. Test vectors, where present, are dedicated to the public domain under CC0 1.0. --- ## 0. Purpose and Non-Goals ### 0.1 Purpose `OPREG/PEL1-KERNEL` defines a **minimal, globally stable set of PEL operations** that every “kernel-capable” PEL engine is expected to implement: * They operate on **ASL/1 Artifacts** (bytes + optional type tag). * They are used in **`PEL/PROGRAM-DAG/1`** programs as `OperationId { name, version }`. * They are **pure and deterministic**: same inputs → same outputs, independent of engine or environment. * They explicitly define: * **Arity** (number of inputs), * **Parameter model** (logical Params value), * **Output shape** (number and form of outputs), * **Runtime error conditions** and associated `status_code` values for `PEL/PROGRAM-DAG/1`. These operations are intentionally **low-level** and **byte-centric**; richer semantics (JSON, typed records, domain-specific logic) belong in separate registries. ### 0.2 Non-goals This registry does **not** define: * Any storage or transport API (`ASL/1-STORE`). * Any encoding of Programs or Params into bytes (`ENC/PEL-PROGRAM-DAG/1`, param-encoding profiles). * Any certification or fact semantics (`CIL/1`, `FER/1`, `FCT/1`). * Provenance graph edges (`TGK/1`). * Human-readable diagnostics payloads (see §2.4). --- ## 1. Conventions and Context ### 1.1 Base types From `ASL/1-CORE`: ```text Artifact { bytes: OctetString type_tag: optional TypeTag } TypeTag { tag_id: uint32 } Reference { hash_id: HashId digest: OctetString } HashId = uint16 ``` From `PEL/1-CORE` and `PEL/PROGRAM-DAG/1` (simplified): ```text OperationId { name: string version: uint32 } ExecutionStatus = uint8 // e.g. OK, INVALID_PROGRAM, INVALID_INPUTS, RUNTIME_FAILED ExecutionErrorKind = uint8 // e.g. NONE, PROGRAM, INPUTS, RUNTIME ExecutionErrorSummary { kind: ExecutionErrorKind status_code: uint32 } DiagnosticEntry { code: uint32 message: OctetString } ExecutionResultValue { pel1_version : uint16 status : ExecutionStatus scheme_ref : SchemeRef summary : ExecutionErrorSummary diagnostics : list } ``` `PEL/PROGRAM-DAG/1` defines `Exec_DAG` as: ```text Exec_DAG( program: Artifact, inputs: list, params: optional Artifact ) -> (outputs: list, result: ExecutionResultValue) ``` and defines that each Node evaluates an `OperationId` with a logical interface: ```text Op(name, version)( inputs: list, params: ParamsValue ) -> Ok(list) | Err(status_code: uint32) ``` The overall `ExecutionResultValue.summary.status_code` for `RUNTIME_FAILED` is taken from the `status_code` returned by the failing operation. ### 1.2 Status and error mapping This registry only defines **runtime error codes** (used when `Exec_DAG` sets `status = RUNTIME_FAILED`). Global outcome statuses: ```text ExecutionStatus { OK = 0 INVALID_PROGRAM = 2 INVALID_INPUTS = 3 RUNTIME_FAILED = 4 } ``` Error summary kind: ```text ExecutionErrorKind { NONE = 0 PROGRAM = 1 INPUTS = 2 RUNTIME = 3 } ``` Mapping (from `PEL/PROGRAM-DAG/1`): * `status = OK` ⇒ `kind = NONE`, `status_code = 0` * `status = INVALID_PROGRAM`⇒ `kind = PROGRAM`, `status_code = 2` * `status = INVALID_INPUTS` ⇒ `kind = INPUTS`, `status_code = 3` * `status = RUNTIME_FAILED` ⇒ `kind = RUNTIME`, `status_code = op-specific (> 0)` This registry **only** defines the operation-specific `status_code` values that may appear when `status = RUNTIME_FAILED`. ### 1.3 Kernel status_code layout For kernel ops we reserve a simple scheme for `status_code` on runtime failure: ```text status_code = (kernel_op_code << 16) | error_index ``` Where: * `kernel_op_code` is a 16-bit numeric code assigned per operation in this registry. * `error_index` is a small (non-zero) 16-bit integer enumerating distinct error causes per op. This ensures: * No collision between error codes of different operations. * Easy offline decoding of `status_code` into `(op, reason)`. Concrete `kernel_op_code` assignments are given in §3. ### 1.4 Params and encodings Each operation defines a **logical Params type** (e.g. `SliceParams { offset: u64; length: u64 }`). This registry does **not** define byte-level encodings of Params; those are defined in a companion profile (e.g. `OPREG/PEL1-KERNEL-PARAMS/1`). This document is the **semantic** registry. Conformance requirements: * For each operation, there MUST exist exactly one canonical encoding and decoding for its Params type. * All engines claiming to implement the operation MUST use that same encoding and decoding. * If Params decoding fails, the operation MUST treat the Node as either: * `INVALID_PROGRAM` (preferred for static malformations), or * `RUNTIME_FAILED` with a specific `status_code` (if the registry so specifies). For this initial kernel set, we treat **Param decoding errors as INVALID_PROGRAM**, not as runtime failures. ### 1.5 Diagnostics To keep `ExecutionResultValue` stable and simple, kernel operations: For kernel operations, the operation semantics MUST always return an empty diagnostics list, and the scheme’s Exec_DAG implementation MUST NOT add additional diagnostics when a failing Node is a kernel op. Human-readable error information is expected to be carried in: * separate trace artifacts (`PEL/TRACE-DAG/1`), or * external logs and observability systems, not in `ExecutionResultValue.diagnostics`. --- ## 2. Common Kernel Operation Conventions All kernel operations in this registry share these properties: 1. **Purity and determinism** * They operate only on: * the input `Artifact.bytes` and `Artifact.type_tag`, * their decoded Params, * standard pure functions (e.g. integer arithmetic, hashing as per `HASH/ASL1`). * They MUST NOT: * read clocks or random sources, * perform network or filesystem I/O, * depend on global mutable state. 2. **Type tags** * Unless otherwise stated, operations **preserve the input type tag** when transforming a single input. * For operations with multiple inputs, if they require consistent type tags, this is checked at runtime and may yield a runtime error. * Operations MAY produce Artifacts with `type_tag = None` for “raw bytes” outputs. 3. **Arity and static vs dynamic errors** * Each operation specifies `min_inputs` and `max_inputs`. * Violations of these arity constraints are **static** (depend only on the Program) and MUST be treated as `INVALID_PROGRAM`, not `RUNTIME_FAILED`. * Runtime errors are reserved for **data-dependent** conditions (e.g. out-of-bounds slice based on actual input length). 4. **Success vs failure** * On success: operation returns `Ok(list)`, and `Exec_DAG` keeps `status = OK` (unless a different Node fails later). * On failure: operation returns `Err(status_code)`, and `Exec_DAG` stops evaluation and sets: ```text status = RUNTIME_FAILED summary.kind = RUNTIME summary.status_code = status_code diagnostics = [] ``` --- ## 3. Kernel Operation Index We define four kernel operations: | Kernel Op Code | OperationId.name | version | Summary | | -------------: | ----------------------- | :------ | --------------------------------------- | | `0x0001` | `"pel.bytes.concat"` | `1` | Concatenate N artifacts | | `0x0002` | `"pel.bytes.slice"` | `1` | Take a byte slice of one artifact | | `0x0003` | `"pel.bytes.const"` | `1` | Produce a constant artifact from params | | `0x0004` | `"pel.bytes.hash.asl1"` | `1` | Hash an artifact’s bytes with ASL1 | All operation names are case-sensitive UTF-8 strings. Each operation’s `OperationId` is: ```text OperationId { name: version: 1 } ``` `kernel_op_code` in the `status_code` formula (§1.3) is the hex code in the first column. --- ## 4. Operation Specifications ### 4.1 `pel.bytes.concat` v1 (code 0x0001) **OperationId** ```text name = "pel.bytes.concat" version = 1 kernel_op_code = 0x0001 ``` **Intent** Concatenate the byte payloads of N input Artifacts (N ≥ 1) into a single output Artifact. All input type tags MUST be identical (including “no type tag”). #### 4.1.1 Arity and Params * `min_inputs = 1` * `max_inputs = unbounded` (any positive number) * Params: **none** (`Unit`) Static errors (handled as `INVALID_PROGRAM`): * `inputs.length == 0`. * Params not decodable as `Unit` (i.e. any non-empty Params according to the canonical encoding). #### 4.1.2 Semantics Given: ```text inputs = [A0, A1, ..., A_{n-1}], n >= 1 params = () ``` Let: ```text Ti = Ai.type_tag Bi = Ai.bytes ``` 1. **Type tag consistency check (runtime)** * If there exist `i, j` such that `Ti` and `Tj` are not equal in the `ASL/1-CORE` sense (i.e. one is absent and the other present, or both present but with different `tag_id`): * Operation returns `Err(status_code = 0x0001_0001)`. 2. **Concatenation** * Define: ```text B_out = B0 || B1 || ... || B_{n-1} // byte-wise concatenation T_out = T0 // they are all equal by step 1 ``` * Output list is a single Artifact `C`: ```text C.bytes = B_out C.type_tag = T_out ``` * Operation returns `Ok([C])`. This operation does not impose any explicit limit on the concatenated length; overflow or resource exhaustion is outside the PEL semantic layer. #### 4.1.3 Runtime error codes For `pel.bytes.concat` v1, runtime errors (producing `RUNTIME_FAILED`) are: | Name | Condition | `status_code` | | ------------------- | ---------------------------------- | ------------- | | `TYPE_TAG_MISMATCH` | Any pair of input type tags differ | `0x0001_0001` | On any such error: * Operation returns `Err(status_code)` as above. * `Exec_DAG` sets `status = RUNTIME_FAILED`, `summary.status_code = status_code`, `diagnostics = []`. --- ### 4.2 `pel.bytes.slice` v1 (code 0x0002) **OperationId** ```text name = "pel.bytes.slice" version = 1 kernel_op_code = 0x0002 ``` **Intent** Take a contiguous slice from a single input Artifact’s bytes. #### 4.2.1 Params: `SliceParams` Logical Params: ```text SliceParams { offset: uint64 // byte offset, 0-based length: uint64 // number of bytes to include } ``` * `offset` and `length` are non-negative. * Their canonical encoding/decoding is defined in a param-encoding profile; invalid encodings MUST result in `INVALID_PROGRAM`. #### 4.2.2 Arity * `min_inputs = 1` * `max_inputs = 1` Arity violations → `INVALID_PROGRAM`. #### 4.2.3 Semantics Given: ```text inputs = [A] params = SliceParams { offset, length } ``` Let: ```text B = A.bytes // length = L T = A.type_tag L = |B| o = offset ℓ = length ``` 1. **Range check (runtime)** * If `o > L` or `o + ℓ > L` (with arithmetic in unbounded integers): * Operation returns `Err(status_code = 0x0002_0001)`. 2. **Slicing** * Define: ```text B_out = B[o .. o+ℓ] // ℓ bytes starting at index o ``` * Output Artifact `C`: ```text C.bytes = B_out C.type_tag = T ``` * Operation returns `Ok([C])`. Note: `o == L` and `ℓ == 0` is allowed and yields an empty-byte output. #### 4.2.4 Runtime error codes For `pel.bytes.slice` v1: | Name | Condition | `status_code` | | --------------------- | ------------------------------------------------------- | ------------- | | `RANGE_OUT_OF_BOUNDS` | `offset > len(bytes)` or `offset + length > len(bytes)` | `0x0002_0001` | On such error, `Exec_DAG` sets `status = RUNTIME_FAILED`, `summary.status_code = 0x0002_0001`, `diagnostics = []`. --- ### 4.3 `pel.bytes.const` v1 (code 0x0003) **OperationId** ```text name = "pel.bytes.const" version = 1 kernel_op_code = 0x0003 ``` **Intent** Produce a constant Artifact specified entirely by Params, with no data dependencies. This is a way to embed small literal values directly in a Program. #### 4.3.1 Params: `ConstParams` Logical Params: ```text ConstParams { bytes: OctetString // payload bytes has_tag: bool // whether a type tag is present tag_id: uint32 optional // only meaningful if has_tag = true } ``` Semantics: * If `has_tag == false`: * Output Artifact has `type_tag = None`. * If `has_tag == true`: * Output Artifact has `type_tag = Some(TypeTag{ tag_id })`. Param encoding/decoding is defined in a param-encoding profile; malformed encodings ⇒ `INVALID_PROGRAM`. #### 4.3.2 Arity * `min_inputs = 0` * `max_inputs = 0` Any non-empty `inputs` list is a static error (`INVALID_PROGRAM`). #### 4.3.3 Semantics Given: ```text inputs = [] params = ConstParams { bytes = B, has_tag, tag_id? } ``` Then: * If `has_tag` is false: ```text C.bytes = B C.type_tag = None ``` * If `has_tag` is true: ```text C.bytes = B C.type_tag = Some(TypeTag{ tag_id }) ``` * Output list is `[C]`. * Operation returns `Ok([C])`. There are no data-dependent runtime errors: this operation **always succeeds** given valid Params. #### 4.3.4 Runtime error codes *None.* All failures are static (bad Params encoding, wrong arity) and must be treated as `INVALID_PROGRAM`. --- ### 4.4 `pel.bytes.hash.asl1` v1 (code 0x0004) **OperationId** ```text name = "pel.bytes.hash.asl1" version = 1 kernel_op_code = 0x0004 ``` **Intent** Compute an ASL1-family hash (`HASH/ASL1`) over the raw bytes of a single input Artifact. This operation is **not** about ASL/1 identity (which uses `ArtifactBytes` via `ENC/ASL1-CORE`), but about hashing arbitrary byte payloads for protocol or application use. #### 4.4.1 Params: `HashParams` Logical Params: ```text HashParams { hash_id: HashId // must be a valid ASL1 HashId } ``` For this version: * `hash_id` MUST be `0x0001` (i.e. `HASH-ASL1-256`). * Any other `hash_id` MUST be treated as a **static error** ⇒ `INVALID_PROGRAM`. Rationale: this ensures all conformant engines agree on the algorithm set for this op. Future versions (e.g. `pel.bytes.hash.asl1` v2) MAY support additional `HashId`s. Param encoding/decoding is defined elsewhere; malformed encodings ⇒ `INVALID_PROGRAM`. #### 4.4.2 Arity * `min_inputs = 1` * `max_inputs = 1` Arity violations ⇒ `INVALID_PROGRAM`. #### 4.4.3 Semantics Given: ```text inputs = [A] params = HashParams { hash_id = 0x0001 } ``` Let: ```text B = A.bytes H = HASH-ASL1-256 // SHA-256 as defined in HASH/ASL1 for HashId 0x0001 ``` Compute: ```text digest = H(B) // 32-byte digest ``` Then: * Output Artifact `C`: ```text C.bytes = digest // exactly 32 bytes C.type_tag = None // raw bytes digest, no type tag ``` * Output list is `[C]`. * Operation returns `Ok([C])`. There are no data-dependent runtime errors; hashing is assumed total. Any internal errors (e.g. memory failure) are outside PEL semantics. #### 4.4.4 Runtime error codes *None.* All failures (unsupported `hash_id`, bad Params, wrong arity) are static and must be treated as `INVALID_PROGRAM`. --- ## 5. Conformance An engine is **OPREG/PEL1-KERNEL–conformant** if and only if: 1. **Operation availability** * It exposes the four operations defined in §3 with exactly the specified `OperationId { name, version }`. 2. **Arity and Params** * For each operation, it enforces `min_inputs`/`max_inputs` as specified. * It implements the defined logical Params types and uses the canonical param encoding/decoding for each. * It treats: * arity violations, and * invalid or undecodable Params as `INVALID_PROGRAM` per `PEL/PROGRAM-DAG/1` (i.e. `Exec_DAG` produces `status = INVALID_PROGRAM`, `summary.status_code = 2`). 3. **Runtime semantics** * For all supported operations: * Given the same input Artifacts and Params, all conformant engines produce identical output Artifacts (same `bytes`, same `type_tag`) and identical `status_code` on failure. * Runtime failure conditions (e.g. slice out-of-bounds, type tag mismatch) are detected exactly as specified and mapped to the correct `status_code` using the `kernel_op_code`/`error_index` scheme. 4. **Status and diagnostics mapping** * When a kernel operation returns `Ok`, `Exec_DAG` MUST NOT change `status` or `summary` (beyond normal success semantics). * When a kernel operation returns `Err(status_code)`: * `Exec_DAG` MUST set: ```text status = RUNTIME_FAILED summary.kind = RUNTIME summary.status_code = status_code diagnostics = [] ``` * `Exec_DAG` MUST NOT mutate other fields of `ExecutionResultValue` except as defined in `PEL/PROGRAM-DAG/1` (e.g. to capture which Node failed, via trace profiles). 5. **Purity** * Kernel operations MUST not perform external I/O or observe environment state; they must behave as pure functions of their inputs and Params. * Any caching or performance optimizations MUST NOT change observable behavior at the level of `Artifact` values and `status_code`. 6. **Layering** * The engine does not depend on `ASL/1-STORE`, `TGK/1`, `CIL/1`, `FER/1`, `FCT/1`, or `OI/1` on the PEL core hot path. It may use those layers around PEL, but not as part of the operation semantics. --- ## 6. Change Log (informative) **v0.1.1 (2025-11-15)** * Removed `pel.bytes.clone/1` from the kernel operation index. * Reassigned `kernel_op_code` values to remove the `0x01` gap: * `pel.bytes.concat/1`: `0x0002` → `0x0001` (and `TYPE_TAG_MISMATCH` from `0x0002_0001` → `0x0001_0001`). * `pel.bytes.slice/1`: `0x0003` → `0x0002` (and `RANGE_OUT_OF_BOUNDS` from `0x0003_0001` → `0x0002_0001`). * `pel.bytes.const/1`: `0x0004` → `0x0003`. * `pel.bytes.hash.asl1/1`: `0x0005` → `0x0004`. * Updated §3, §4, and §5 to reflect the new kernel op set and codes. **v0.1.0 (2025-11-15)** * Initial definition of `OPREG/PEL1-KERNEL` with five kernel operations: * `pel.bytes.clone/1` * `pel.bytes.concat/1` * `pel.bytes.slice/1` * `pel.bytes.const/1` * `pel.bytes.hash.asl1/1` * Established `status_code = (kernel_op_code << 16) | error_index` convention. * Restricted `pel.bytes.hash.asl1/1` to `HashId = 0x0001` (HASH-ASL1-256) for cross-implementation determinism. * Required kernel operations to leave `ExecutionResultValue.diagnostics` empty; richer diagnostics to be handled by trace/overlay profiles. --- ## Document History * **0.1.1 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline.