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
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<Artifact>)`, 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:
All operation names are case-sensitive UTF-8 strings.
Each operation’s `OperationId` is:
```text
OperationId {
name: <asintable>
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`):
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`).
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`.
* 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.