739 lines
24 KiB
Markdown
739 lines
24 KiB
Markdown
|
|
# ENC/TGK1-EDGE/1 — Canonical Encoding for TGK EdgeArtifacts
|
|||
|
|
|
|||
|
|
Status: Approved
|
|||
|
|
Owner: Niklas Rydberg
|
|||
|
|
Version: 0.1.0
|
|||
|
|
SoT: Yes
|
|||
|
|
Last Updated: 2025-11-16
|
|||
|
|
Linked Phase Pack: N/A
|
|||
|
|
Tags: [binary-minimalism, traceability]
|
|||
|
|
|
|||
|
|
<!-- Source: /amduat/docs/new/enc-tgk1-edge1.md | Canonical: /amduat/tier1/enc-tgk1-edge-1.md -->
|
|||
|
|
|
|||
|
|
**Document ID:** `ENC/TGK1-EDGE/1`
|
|||
|
|
**Profile ID:** `TGK1_EDGE_ENC_V1 = 0x0201` (symbolic; concrete assignment lives in encoding-profile registry)
|
|||
|
|
**Layer:** Edge Encoding Profile (on top of ASL/1-CORE + TGK/1-CORE)
|
|||
|
|
|
|||
|
|
**Depends on (normative):**
|
|||
|
|
|
|||
|
|
* `ASL/1-CORE v0.4.x` — value model (`Artifact`, `TypeTag`, `Reference`, `HashId`, identity model)
|
|||
|
|
* `ENC/ASL1-CORE v1.x` — canonical encodings for `Artifact` and `Reference`
|
|||
|
|
* `TGK/1-CORE v0.7.x` — trace graph kernel (`Node`, `EdgeBody`, `EdgeTypeId`, edgehood invariants)
|
|||
|
|
|
|||
|
|
**Integrates with (informative):**
|
|||
|
|
|
|||
|
|
* `HASH/ASL1 v0.2.x` — ASL1 hash family for `EdgeRef` identity
|
|||
|
|
* `ASL/1-STORE v0.4.x` — content-addressable store holding EdgeArtifacts
|
|||
|
|
* `SUBSTRATE/STACK-OVERVIEW v0.2.x` — stack layering discipline
|
|||
|
|
* TGK type catalogs (e.g. `TGK/TYPES-CORE`) — `EdgeTypeId` semantics
|
|||
|
|
* Future TGK profiles (`TGK/STORE/1`, `TGK/PROV/1`) that interpret edges
|
|||
|
|
|
|||
|
|
> The Profile ID `TGK1_EDGE_ENC_V1` is a configuration label.
|
|||
|
|
> It is **not** embedded into edge payloads. Encoders and decoders select this encoding by context (type tag + profile configuration), not per value.
|
|||
|
|
|
|||
|
|
© 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. Overview
|
|||
|
|
|
|||
|
|
`ENC/TGK1-EDGE/1` defines the **canonical, streaming-friendly, injective binary encoding** of the `EdgeBody` structure from `TGK/1-CORE`:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
EdgeBody {
|
|||
|
|
type: EdgeTypeId // uint32
|
|||
|
|
from: Node[] // Node = Reference
|
|||
|
|
to: Node[]
|
|||
|
|
payload: Reference
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
and its embedding as TGK **EdgeArtifacts**:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Artifact {
|
|||
|
|
bytes = EdgeBytes // this profile
|
|||
|
|
type_tag = TYPE_TAG_TGK1_EDGE_V1
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
where `EdgeBytes` is a single `OctetString` (sequence of bytes) used as `Artifact.bytes`.
|
|||
|
|
|
|||
|
|
Under this profile:
|
|||
|
|
|
|||
|
|
* `EdgeBytes` is the canonical representation of an `EdgeBody`.
|
|||
|
|
* Edge identity is the ASL/1 `Reference` over the EdgeArtifact (`EdgeRef`), derived via `ENC/ASL1-CORE` + `HASH/ASL1`.
|
|||
|
|
* The encoding is:
|
|||
|
|
|
|||
|
|
* **Injective** — distinct `EdgeBody` values → distinct `EdgeBytes`.
|
|||
|
|
* **Deterministic & stable** — same `EdgeBody` → same `EdgeBytes` across implementations and time.
|
|||
|
|
* **Streaming-friendly** — encoders, decoders, and hashers can operate in a single forward-only pass.
|
|||
|
|
|
|||
|
|
In line with `TGK/1-CORE`:
|
|||
|
|
|
|||
|
|
* Each EdgeArtifact encodes **exactly one** logical edge (one `EdgeBody`).
|
|||
|
|
* All TGK edges are represented as ordinary ASL/1 Artifacts plus their ASL `Reference` identities; this profile introduces no additional identity or node/edge ID layer.
|
|||
|
|
|
|||
|
|
> **Non-goal:** This profile does **not** define what any particular `EdgeTypeId` “means”, nor how graphs are stored, indexed, or traversed. Those behaviors are defined by `TGK/1-CORE`, TGK type catalogs, and higher-layer profiles.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Scope & Layering
|
|||
|
|
|
|||
|
|
### 1.1 Purpose
|
|||
|
|
|
|||
|
|
This specification defines:
|
|||
|
|
|
|||
|
|
* The **binary layout** of:
|
|||
|
|
|
|||
|
|
* `EdgeBytes` — canonical encoding for `EdgeBody`.
|
|||
|
|
* `EncodedRef` — an internal wrapper for embedding ASL `Reference`s.
|
|||
|
|
|
|||
|
|
* Canonical field ordering and integer widths.
|
|||
|
|
|
|||
|
|
* How `EdgeBytes` are bound into EdgeArtifacts and converted into `EdgeRef` identity.
|
|||
|
|
|
|||
|
|
It does **not** define:
|
|||
|
|
|
|||
|
|
* TGK graph semantics or provenance algorithms (`TGK/1-CORE`, `TGK/PROV/1`).
|
|||
|
|
* Store or transport APIs (`ASL/1-STORE`, deployment profiles).
|
|||
|
|
* Edge-type catalogs (`TGK/TYPES-*`) or policy.
|
|||
|
|
|
|||
|
|
### 1.2 Layering constraints
|
|||
|
|
|
|||
|
|
In line with `SUBSTRATE/STACK-OVERVIEW` and `TGK/1-CORE`:
|
|||
|
|
|
|||
|
|
* `ENC/TGK1-EDGE/1` is a **TGK edge-encoding profile**, not a kernel primitive.
|
|||
|
|
|
|||
|
|
* It MUST NOT:
|
|||
|
|
|
|||
|
|
* redefine `Artifact`, `Reference`, `HashId`, or `TypeTag` (from `ASL/1-CORE`);
|
|||
|
|
* redefine `Node`, `EdgeBody`, or `EdgeTypeId` (from `TGK/1-CORE`);
|
|||
|
|
* embed store, provenance, or policy semantics into its layout.
|
|||
|
|
|
|||
|
|
* It defines exactly one canonical encoding for `EdgeBody` values under the profile ID `TGK1_EDGE_ENC_V1`.
|
|||
|
|
|
|||
|
|
TGK/1-CORE sees this profile as providing a partial function:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
decode_edge_payload_TGK1_EDGE :
|
|||
|
|
OctetString -> EdgeBody | error
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
that is:
|
|||
|
|
|
|||
|
|
* **partial** — may fail with an error for some inputs;
|
|||
|
|
* **deterministic** — a pure function of its input bytes, with no dependence on environment or mutable state;
|
|||
|
|
* **side-effect free** — decoding does not consult stores, catalogs, or policy.
|
|||
|
|
|
|||
|
|
Artifacts whose `type_tag` selects this profile use `decode_edge_payload_TGK1_EDGE` as their TGK edge decoder in the sense of `TGK/1-CORE §3.2`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Conventions
|
|||
|
|
|
|||
|
|
### 2.1 RFC 2119 terms
|
|||
|
|
|
|||
|
|
The key words **MUST**, **MUST NOT**, **SHOULD**, **MAY**, etc. are to be interpreted as described in RFC 2119.
|
|||
|
|
|
|||
|
|
### 2.2 Integer encodings
|
|||
|
|
|
|||
|
|
All multi-byte integers are encoded as **big-endian** (network byte order), as in `ENC/ASL1-CORE`:
|
|||
|
|
|
|||
|
|
* `u8` — 1 byte
|
|||
|
|
* `u16` — 2 bytes
|
|||
|
|
* `u32` — 4 bytes
|
|||
|
|
* `u64` — 8 bytes
|
|||
|
|
|
|||
|
|
Only fixed-width integers are used.
|
|||
|
|
|
|||
|
|
### 2.3 Lists
|
|||
|
|
|
|||
|
|
A list of values of some type `T` is encoded as:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
List<T> ::
|
|||
|
|
count (u32)
|
|||
|
|
element_0
|
|||
|
|
element_1
|
|||
|
|
...
|
|||
|
|
element_{count-1}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
* `count` is the number of elements (MAY be zero).
|
|||
|
|
* Elements are encoded in order using the canonical encoding of `T`.
|
|||
|
|
|
|||
|
|
### 2.4 Embedded Reference (`EncodedRef`)
|
|||
|
|
|
|||
|
|
Within `EdgeBytes`, ASL/1 `Reference` values are embedded using a length-prefixed wrapper over canonical `ReferenceBytes` from `ENC/ASL1-CORE`:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
EncodedRef ::
|
|||
|
|
ref_len (u32)
|
|||
|
|
ref_bytes (byte[0..ref_len-1]) // canonical ReferenceBytes
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Where:
|
|||
|
|
|
|||
|
|
* `ref_bytes` MUST be the canonical `ReferenceBytes` encoding of some `Reference` value under `ENC/ASL1-CORE v1.x`:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ReferenceBytes ::
|
|||
|
|
hash_id (u16)
|
|||
|
|
digest (byte[...]) // remaining bytes in the frame
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
* `ref_len` MUST be the exact byte length of `ref_bytes` and MUST be ≥ 2.
|
|||
|
|
|
|||
|
|
Decoders MUST:
|
|||
|
|
|
|||
|
|
1. Read `ref_len (u32)`.
|
|||
|
|
2. Read exactly `ref_len` bytes as `ref_bytes`.
|
|||
|
|
3. Decode `ref_bytes` as `ReferenceBytes` per `ENC/ASL1-CORE v1.x`.
|
|||
|
|
4. Reject encodings where:
|
|||
|
|
|
|||
|
|
* `ref_len < 2`, or
|
|||
|
|
* `ref_bytes` is not a valid `ReferenceBytes` sequence (e.g. truncated or improperly framed in its context).
|
|||
|
|
|
|||
|
|
If the implementation also implements `HASH/ASL1` and recognizes the decoded `hash_id`, it MUST apply any length checks required by `ENC/ASL1-CORE` / `HASH/ASL1` for that `HashId` (e.g. fixed digest length). Failures MUST be treated as encoding/integrity errors.
|
|||
|
|
|
|||
|
|
`EncodedRef` is purely an internal framing wrapper for this profile; it introduces no additional semantics beyond “a `Reference` encoded canonically and length-prefixed so it can be embedded in larger structures”.
|
|||
|
|
|
|||
|
|
This pattern mirrors `EncodedRef` from `ENC/PEL-TRACE-DAG/1` for cross-profile consistency.
|
|||
|
|
|
|||
|
|
### 2.5 Encoding version field (`edge_version`)
|
|||
|
|
|
|||
|
|
`EdgeBytes` includes an `edge_version (u16)` field:
|
|||
|
|
|
|||
|
|
* For `TGK1_EDGE_ENC_V1`, encoders **MUST** always write `edge_version = 1`.
|
|||
|
|
* Decoders for this profile:
|
|||
|
|
|
|||
|
|
* **MUST** accept `edge_version = 1`; and
|
|||
|
|
* **MUST** treat any other value as “**not this encoding**” and fail decoding.
|
|||
|
|
|
|||
|
|
Within this profile, `edge_version` is a **guard word**, not an evolution mechanism:
|
|||
|
|
|
|||
|
|
* This document will never assign any other meaning than “constant value 1” to `edge_version` for `TGK1_EDGE_ENC_V1`.
|
|||
|
|
* Values other than `1` simply indicate that the bytes are not an `EdgeBytes` value for this profile.
|
|||
|
|
|
|||
|
|
Any incompatible change to the `EdgeBytes` layout MUST be expressed as a **new encoding profile** (e.g. `TGK1_EDGE_ENC_V2` with its own Profile ID, and almost certainly a new `TypeTag`), not by reusing this profile with `edge_version = 2`.
|
|||
|
|
|
|||
|
|
Append-only extensions that would change the canonical mapping from `EdgeBody` to bytes are also out of scope for this profile; they belong in new profiles. Canonical `EdgeBody → EdgeBytes` mapping for `TGK1_EDGE_ENC_V1` is fixed and permanently tied to `edge_version = 1`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Logical Model Reference (from TGK/1-CORE)
|
|||
|
|
|
|||
|
|
> **Source of truth:** `TGK/1-CORE`.
|
|||
|
|
> This section is an informative restatement; in any conflict, `TGK/1-CORE` governs.
|
|||
|
|
|
|||
|
|
### 3.1 Node
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Node := Reference // ASL/1 Reference
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Nodes are graph vertices identified solely by their `Reference` value.
|
|||
|
|
|
|||
|
|
### 3.2 EdgeTypeId
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
EdgeTypeId = uint32
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Semantics of particular `EdgeTypeId` values are defined by TGK type catalogs and profiles, not by this document.
|
|||
|
|
|
|||
|
|
### 3.3 EdgeBody
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
EdgeBody {
|
|||
|
|
type: EdgeTypeId
|
|||
|
|
from: Node[] // ordered, MAY be empty
|
|||
|
|
to: Node[] // ordered, MAY be empty
|
|||
|
|
payload: Reference // always present
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Relevant invariant from `TGK/1-CORE`:
|
|||
|
|
|
|||
|
|
> **TGK/EDGE-NONEMPTY-ENDPOINT/CORE/1**
|
|||
|
|
> For a well-formed `EdgeBody`, at least one of `from` or `to` **MUST** be non-empty.
|
|||
|
|
> An `EdgeBody` with both `from = []` and `to = []` is invalid and MUST NOT be produced or accepted as a TGK edge.
|
|||
|
|
|
|||
|
|
Other notes from `TGK/1-CORE`:
|
|||
|
|
|
|||
|
|
* Duplicates within `from` or `to` are allowed.
|
|||
|
|
* `payload` may also appear in `from` or `to`.
|
|||
|
|
* Semantics of such patterns, if any, are profile-specific.
|
|||
|
|
|
|||
|
|
`ENC/TGK1-EDGE/1` encodes exactly these fields and MUST NOT introduce additional logical data at the `EdgeBody` level.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. EdgeBody Encoding
|
|||
|
|
|
|||
|
|
### 4.1 Overall layout: `EdgeBytes`
|
|||
|
|
|
|||
|
|
The canonical encoding of an `EdgeBody` under `TGK1_EDGE_ENC_V1` is a single self-contained byte sequence:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
EdgeBytes ::
|
|||
|
|
edge_version (u16)
|
|||
|
|
type_id (u32) // EdgeTypeId
|
|||
|
|
from_count (u32)
|
|||
|
|
from_nodes (EncodedRef[0..from_count-1])
|
|||
|
|
to_count (u32)
|
|||
|
|
to_nodes (EncodedRef[0..to_count-1])
|
|||
|
|
payload_ref (EncodedRef)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`EdgeBytes` is treated as an indivisible frame. When embedded in larger structures or protocols, the enclosing layer is responsible for providing the frame boundaries (e.g. via a length-prefix or message framing).
|
|||
|
|
|
|||
|
|
Field roles:
|
|||
|
|
|
|||
|
|
1. **edge_version (u16)**
|
|||
|
|
|
|||
|
|
* Guard word for this encoding profile.
|
|||
|
|
* For `TGK1_EDGE_ENC_V1`, encoders **MUST** set `edge_version = 1` for all values.
|
|||
|
|
* Decoders for this profile:
|
|||
|
|
|
|||
|
|
* **MUST** accept `edge_version = 1`; and
|
|||
|
|
* **MUST** treat any other value as “not a `TGK1_EDGE_ENC_V1` edge payload” and fail decoding.
|
|||
|
|
|
|||
|
|
`edge_version` is not a version knob for evolving `TGK1_EDGE_ENC_V1`; it is a constant sanity check to quickly reject mismatched bytes.
|
|||
|
|
|
|||
|
|
2. **type_id (u32)**
|
|||
|
|
|
|||
|
|
* Encodes `EdgeBody.type : EdgeTypeId`.
|
|||
|
|
* The meaning of each `EdgeTypeId` value is external to this spec.
|
|||
|
|
|
|||
|
|
3. **from_count (u32)** and **from_nodes**
|
|||
|
|
|
|||
|
|
* `from_count` is the length of `EdgeBody.from`.
|
|||
|
|
* `from_nodes` is a list of `from_count` `EncodedRef` entries, each encoding a `Node` (i.e. a `Reference`).
|
|||
|
|
* Order MUST match the logical `from` list; duplicates are allowed; MAY be zero-length.
|
|||
|
|
|
|||
|
|
4. **to_count (u32)** and **to_nodes**
|
|||
|
|
|
|||
|
|
* `to_count` is the length of `EdgeBody.to`.
|
|||
|
|
* `to_nodes` is a list of `to_count` `EncodedRef` entries.
|
|||
|
|
* Order MUST match the logical `to` list; duplicates are allowed; MAY be zero-length.
|
|||
|
|
|
|||
|
|
5. **payload_ref (EncodedRef)**
|
|||
|
|
|
|||
|
|
* Encodes `EdgeBody.payload : Reference`.
|
|||
|
|
* Always present and encoded as a single `EncodedRef`.
|
|||
|
|
|
|||
|
|
### 4.2 Encoding procedure (normative)
|
|||
|
|
|
|||
|
|
Let `E` be a logical `EdgeBody` value. The canonical encoding function:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
encode_edgebody_tgk1_v1 : EdgeBody -> EdgeBytes
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
is defined as:
|
|||
|
|
|
|||
|
|
1. Set `edge_version = 1`.
|
|||
|
|
|
|||
|
|
2. Emit `edge_version` as `u16`.
|
|||
|
|
|
|||
|
|
3. Emit `E.type` as `type_id (u32)`.
|
|||
|
|
|
|||
|
|
4. Let `from_count = len(E.from)`; emit `from_count (u32)`.
|
|||
|
|
|
|||
|
|
5. For each `Node` in `E.from` in order:
|
|||
|
|
|
|||
|
|
* Let `R` be that `Node` (an ASL `Reference` value).
|
|||
|
|
* Encode `R` as canonical `ReferenceBytes` using `ENC/ASL1-CORE v1.x`.
|
|||
|
|
* Wrap as `EncodedRef` (see §2.4) and append.
|
|||
|
|
|
|||
|
|
6. Let `to_count = len(E.to)`; emit `to_count (u32)`.
|
|||
|
|
|
|||
|
|
7. For each `Node` in `E.to` in order:
|
|||
|
|
|
|||
|
|
* Encode as `EncodedRef` as above and append.
|
|||
|
|
|
|||
|
|
8. Encode `E.payload` as canonical `ReferenceBytes`, wrap as `EncodedRef`, and append as `payload_ref`.
|
|||
|
|
|
|||
|
|
9. Enforce the TGK non-empty endpoint invariant at encoding time:
|
|||
|
|
|
|||
|
|
* If `from_count == 0` **and** `to_count == 0`, the encoder MUST fail and MUST NOT produce `EdgeBytes` for this `EdgeBody` under this profile.
|
|||
|
|
|
|||
|
|
> **TGK1-EDGE-NONEMPTY/ENC/1**
|
|||
|
|
> Encoders for `TGK1_EDGE_ENC_V1` **MUST** reject any attempt to encode an `EdgeBody` with `from = []` and `to = []`.
|
|||
|
|
> Such a value is not a well-formed TGK edge per `TGK/1-CORE` and MUST NOT be emitted as an EdgeArtifact payload.
|
|||
|
|
|
|||
|
|
### 4.3 Decoding procedure (normative)
|
|||
|
|
|
|||
|
|
Given a byte slice known to contain exactly one `EdgeBytes` frame under this profile, the canonical decoding function:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
decode_edgebody_tgk1_v1 : EdgeBytes -> EdgeBody | error
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
is defined as:
|
|||
|
|
|
|||
|
|
1. Read `edge_version (u16)`.
|
|||
|
|
|
|||
|
|
* If `edge_version != 1`, fail with an encoding error (e.g. “not `TGK1_EDGE_ENC_V1`”).
|
|||
|
|
|
|||
|
|
2. Read `type_id (u32)`.
|
|||
|
|
|
|||
|
|
3. Read `from_count (u32)`.
|
|||
|
|
|
|||
|
|
* For `i = 0 .. from_count-1`, read and decode one `EncodedRef` as a `Reference` and append to `from_nodes`.
|
|||
|
|
|
|||
|
|
4. Read `to_count (u32)`.
|
|||
|
|
|
|||
|
|
* For `j = 0 .. to_count-1`, read and decode one `EncodedRef` and append to `to_nodes`.
|
|||
|
|
|
|||
|
|
5. Read `payload_ref` as a single `EncodedRef` and decode to `payload : Reference`.
|
|||
|
|
|
|||
|
|
6. If `from_count == 0` **and** `to_count == 0`, fail with an encoding error:
|
|||
|
|
|
|||
|
|
* This violates `TGK/EDGE-NONEMPTY-ENDPOINT/CORE/1` and `TGK1-EDGE-NONEMPTY/ENC/1`.
|
|||
|
|
|
|||
|
|
7. If the decoding context expects an isolated `EdgeBytes` value:
|
|||
|
|
|
|||
|
|
* After step 5 (or 6), if any unread bytes remain in the slice, the decoder MUST treat this as an encoding error (trailing data).
|
|||
|
|
|
|||
|
|
8. Construct and return:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
EdgeBody {
|
|||
|
|
type = EdgeTypeId(type_id)
|
|||
|
|
from = from_nodes
|
|||
|
|
to = to_nodes
|
|||
|
|
payload = payload
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Decoders MUST additionally treat as encoding errors:
|
|||
|
|
|
|||
|
|
* truncated sequences (insufficient bytes for any declared field or `EncodedRef`);
|
|||
|
|
* invalid `EncodedRef` encodings (see §2.4);
|
|||
|
|
* any integer reads that cannot be completed because the input ends early.
|
|||
|
|
|
|||
|
|
`decode_edgebody_tgk1_v1` MUST be deterministic and MUST NOT depend on any external configuration beyond:
|
|||
|
|
|
|||
|
|
* the bytes in the `EdgeBytes` frame; and
|
|||
|
|
* the static definition of `ENC/ASL1-CORE v1.x` used to decode embedded `ReferenceBytes`.
|
|||
|
|
|
|||
|
|
Recognition of `type_id` values (as supported or not in a given ExecutionEnvironment) is handled by `TGK/1-CORE` and the local catalog. This profile always decodes the raw `EdgeBody` structure, regardless of whether the environment later chooses to treat it as an EdgeArtifact.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. EdgeArtifact Binding & Profile Selection
|
|||
|
|
|
|||
|
|
### 5.1 EdgeArtifact shape
|
|||
|
|
|
|||
|
|
Under this profile, EdgeArtifacts MUST be ASL/1 Artifacts of the form:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Artifact {
|
|||
|
|
bytes = EdgeBytes
|
|||
|
|
type_tag = TYPE_TAG_TGK1_EDGE_V1
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Where:
|
|||
|
|
|
|||
|
|
* `TYPE_TAG_TGK1_EDGE_V1` is a `TypeTag` whose concrete `tag_id`:
|
|||
|
|
|
|||
|
|
* is assigned in the global TypeTag registry, and
|
|||
|
|
* is included in the environment’s `EDGE_TAG_SET` when this profile is active.
|
|||
|
|
|
|||
|
|
ExecutionEnvironments that wish to treat such Artifacts as TGK edges MUST:
|
|||
|
|
|
|||
|
|
* include `TYPE_TAG_TGK1_EDGE_V1.tag_id` in their configured `EDGE_TAG_SET`; and
|
|||
|
|
* register `TGK1_EDGE_ENC_V1` as the edge-encoding profile for that tag, so that `decode_edge_payload_TGK1_EDGE` is used for those Artifacts’ `bytes`.
|
|||
|
|
|
|||
|
|
This document treats `TYPE_TAG_TGK1_EDGE_V1` symbolically and does not assign a numeric `tag_id`.
|
|||
|
|
|
|||
|
|
### 5.2 Integration with TGK/1-CORE’s `decode_edge_payload_P`
|
|||
|
|
|
|||
|
|
For ExecutionEnvironments that activate `TGK1_EDGE_ENC_V1` for `TYPE_TAG_TGK1_EDGE_V1`, the corresponding `decode_edge_payload_P` function from `TGK/1-CORE §3.2` is:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
decode_edge_payload_TGK1_EDGE(bytes: OctetString) -> EdgeBody | error
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
defined by:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
decode_edgebody_tgk1_v1(bytes)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
from §4.3.
|
|||
|
|
|
|||
|
|
Conformant implementations MUST:
|
|||
|
|
|
|||
|
|
* apply `decode_edge_payload_TGK1_EDGE` only to Artifacts whose `type_tag.tag_id` is configured to use this profile; and
|
|||
|
|
* treat any decoding failure as “not a valid edge payload for this profile”.
|
|||
|
|
|
|||
|
|
Multi-profile behavior (e.g., co-existence with other edge encodings) is governed by `TGK/1-CORE §3.2`. In particular:
|
|||
|
|
|
|||
|
|
* If more than one active profile successfully decodes the same `Artifact.bytes`, all such profiles MUST decode to the same logical `EdgeBody` value.
|
|||
|
|
* If two active profiles decode the same bytes to different `EdgeBody` values, the ExecutionEnvironment MUST NOT treat that Artifact as an EdgeArtifact until the conflict is resolved.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. EdgeRef Identity via ASL/1-CORE
|
|||
|
|
|
|||
|
|
Given:
|
|||
|
|
|
|||
|
|
* `EdgeBytes` from §4;
|
|||
|
|
|
|||
|
|
* an `EdgeArtifact`:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
A_edge = Artifact {
|
|||
|
|
bytes = EdgeBytes
|
|||
|
|
type_tag = TYPE_TAG_TGK1_EDGE_V1
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
* `ENC/ASL1-CORE v1.x` for canonical `ArtifactBytes`;
|
|||
|
|
|
|||
|
|
* a hash algorithm `H` with `HashId = HID` from `HASH/ASL1`,
|
|||
|
|
|
|||
|
|
the canonical `EdgeRef : Reference` (the edge identity) is:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ArtifactBytes = encode_artifact_core_v1(A_edge)
|
|||
|
|
digest = H(ArtifactBytes)
|
|||
|
|
EdgeRef = Reference { hash_id = HID, digest = digest }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
This profile does not introduce any new identity scheme. Edge identity is entirely determined by:
|
|||
|
|
|
|||
|
|
* the ASL/1 Artifact identity model,
|
|||
|
|
* the selected encoding profile (typically `ASL_ENC_CORE_V1`), and
|
|||
|
|
* the selected hash algorithm (`HASH/ASL1`).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Canonicality & Injectivity
|
|||
|
|
|
|||
|
|
### 7.1 Injectivity
|
|||
|
|
|
|||
|
|
> **TGK1-EDGE-INJECTIVE/ENC/1**
|
|||
|
|
> Under `TGK1_EDGE_ENC_V1`, the mapping:
|
|||
|
|
>
|
|||
|
|
> ```text
|
|||
|
|
> EdgeBody -> EdgeBytes
|
|||
|
|
> ```
|
|||
|
|
>
|
|||
|
|
> MUST be injective. That is, for any two `EdgeBody` values `E1` and `E2`:
|
|||
|
|
>
|
|||
|
|
> ```text
|
|||
|
|
> E1 != E2 ⇒ encode_edgebody_tgk1_v1(E1) != encode_edgebody_tgk1_v1(E2)
|
|||
|
|
> ```
|
|||
|
|
|
|||
|
|
This is ensured by:
|
|||
|
|
|
|||
|
|
* encoding all logical fields (`type`, `from`, `to`, `payload`);
|
|||
|
|
* preserving list order exactly;
|
|||
|
|
* using a fixed, explicit binary layout.
|
|||
|
|
|
|||
|
|
### 7.2 Stability
|
|||
|
|
|
|||
|
|
For the fixed profile `TGK1_EDGE_ENC_V1` (with the guard word `edge_version = 1`):
|
|||
|
|
|
|||
|
|
* The same logical `EdgeBody` MUST always encode to the same `EdgeBytes` across:
|
|||
|
|
|
|||
|
|
* implementations,
|
|||
|
|
* platforms,
|
|||
|
|
* executions,
|
|||
|
|
* and time.
|
|||
|
|
|
|||
|
|
Encoders MUST NOT:
|
|||
|
|
|
|||
|
|
* reorder elements of `from` or `to`;
|
|||
|
|
* alter integer widths or endianness;
|
|||
|
|
* introduce alternative layouts for any field;
|
|||
|
|
* use any `edge_version` other than `1`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. Error Handling (Encoding Layer)
|
|||
|
|
|
|||
|
|
Decoders for this profile MUST treat as **encoding errors** (to be surfaced as some error category at the API boundary):
|
|||
|
|
|
|||
|
|
1. **Guard word mismatch**
|
|||
|
|
|
|||
|
|
* `edge_version != 1`.
|
|||
|
|
|
|||
|
|
2. **Truncated fields**
|
|||
|
|
|
|||
|
|
* Not enough bytes to read any declared field (`u16`, `u32`, `EncodedRef`, list elements).
|
|||
|
|
|
|||
|
|
3. **Invalid `EncodedRef`**
|
|||
|
|
|
|||
|
|
* `ref_len < 2`; or
|
|||
|
|
* `ref_bytes` is not a valid `ReferenceBytes` sequence per `ENC/ASL1-CORE v1.x`; or
|
|||
|
|
* (when `HASH/ASL1` is implemented and `hash_id` is known) the digest length implied by `ref_bytes` does not match the canonical length for that `HashId`.
|
|||
|
|
|
|||
|
|
4. **Empty endpoints**
|
|||
|
|
|
|||
|
|
* `from_count == 0` **and** `to_count == 0` (violation of `TGK/EDGE-NONEMPTY-ENDPOINT/CORE/1`).
|
|||
|
|
|
|||
|
|
5. **Inconsistent list lengths**
|
|||
|
|
|
|||
|
|
* Fewer actual `EncodedRef` entries than indicated by `from_count` or `to_count`.
|
|||
|
|
|
|||
|
|
6. **Trailing data in isolated contexts**
|
|||
|
|
|
|||
|
|
* Additional bytes remaining after a full `EdgeBytes` value has been decoded, when the decoding context expects exactly one `EdgeBytes` frame.
|
|||
|
|
|
|||
|
|
Translating these into concrete error codes (e.g. `ERR_TGK1_EDGE_ENC_INVALID`) is implementation-specific, but MUST result in rejection of the payload as an `EdgeBytes` value under this profile.
|
|||
|
|
|
|||
|
|
Semantic errors about `EdgeTypeId` recognition or edge-type-specific constraints are handled by TGK catalogs and higher profiles, not at the encoding layer.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Streaming & Implementation Notes
|
|||
|
|
|
|||
|
|
Implementations MUST be able to encode and decode `EdgeBytes` in a **single forward-only pass**:
|
|||
|
|
|
|||
|
|
* All length prefixes (`from_count`, `to_count`, `ref_len`) precede their content.
|
|||
|
|
* Decoders MUST NOT require backtracking to interpret the structure.
|
|||
|
|
|
|||
|
|
For large edges (many endpoints):
|
|||
|
|
|
|||
|
|
* Encoders MAY stream `EncodedRef` entries as they are generated.
|
|||
|
|
* Decoders MAY stream `EncodedRef` entries to consumers or hashers as they are read.
|
|||
|
|
|
|||
|
|
Any such streaming strategy MUST be observationally equivalent to decoding the entire `EdgeBytes` into an `EdgeBody` in memory and MUST respect the canonical layout.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. Conformance
|
|||
|
|
|
|||
|
|
An implementation is **ENC/TGK1-EDGE/1–conformant** if, for `TGK1_EDGE_ENC_V1`, it:
|
|||
|
|
|
|||
|
|
1. **Implements canonical EdgeBody encoding/decoding**
|
|||
|
|
|
|||
|
|
* Implements `encode_edgebody_tgk1_v1` and `decode_edgebody_tgk1_v1` exactly as specified in §4.
|
|||
|
|
* Always writes `edge_version = 1` when encoding.
|
|||
|
|
* Accepts only `edge_version = 1` and treats any other value as “not this encoding”.
|
|||
|
|
|
|||
|
|
2. **Uses `EncodedRef` correctly**
|
|||
|
|
|
|||
|
|
* Embeds `Reference` values via `EncodedRef` as in §2.4.
|
|||
|
|
* Uses canonical `ReferenceBytes` from `ENC/ASL1-CORE v1.x` when forming `ref_bytes`.
|
|||
|
|
* Applies `HASH/ASL1` length checks for known `HashId`s when available.
|
|||
|
|
|
|||
|
|
3. **Enforces TGK invariants at the encoding layer**
|
|||
|
|
|
|||
|
|
* Rejects encodings with both `from` and `to` empty (`TGK1-EDGE-NONEMPTY/ENC/1`).
|
|||
|
|
* Treats malformed payloads as encoding errors as per §8.
|
|||
|
|
|
|||
|
|
4. **Binds EdgeBytes into EdgeArtifacts correctly**
|
|||
|
|
|
|||
|
|
* When forming EdgeArtifacts, sets:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Artifact.bytes = EdgeBytes
|
|||
|
|
Artifact.type_tag = TYPE_TAG_TGK1_EDGE_V1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
* Does not embed additional logical data into the Artifact beyond `EdgeBody` and `type_tag`.
|
|||
|
|
|
|||
|
|
5. **Derives EdgeRef identity via ASL/1-CORE**
|
|||
|
|
|
|||
|
|
* Uses `ENC/ASL1-CORE v1` and `HASH/ASL1` for identity, as in §6.
|
|||
|
|
* Does not introduce alternative edge identity mechanisms at this layer.
|
|||
|
|
|
|||
|
|
6. **Integrates with TGK/1-CORE profile selection**
|
|||
|
|
|
|||
|
|
* Applies `decode_edge_payload_TGK1_EDGE` only to Artifacts whose `type_tag.tag_id` is configured for this profile.
|
|||
|
|
* Respects multi-profile behavior rules from `TGK/1-CORE §3.2` when other edge encodings are also active.
|
|||
|
|
|
|||
|
|
7. **Preserves injectivity and stability**
|
|||
|
|
|
|||
|
|
* Distinct `EdgeBody` values always produce distinct `EdgeBytes`.
|
|||
|
|
* The same `EdgeBody` always produces the same `EdgeBytes` under this profile.
|
|||
|
|
|
|||
|
|
Everything else — storage layout, access protocols, graph indexes, provenance algorithms, and edge-type semantics — is defined by `ASL/1-STORE`, `TGK/1-CORE`, TGK catalogs, and higher-layer profiles.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. Informative Example (Sketch)
|
|||
|
|
|
|||
|
|
> Non-normative; values and hex are illustrative only.
|
|||
|
|
|
|||
|
|
Consider an edge:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
EdgeBody {
|
|||
|
|
type = 0x00000010 // EDGE_EXECUTION (for example)
|
|||
|
|
from = [N_prog, N_input]
|
|||
|
|
to = [N_output]
|
|||
|
|
payload = R_receipt
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Where `N_prog`, `N_input`, `N_output`, and `R_receipt` are `Reference` values with canonical `ReferenceBytes`:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Ref(N_prog) = ReferenceBytes(N_prog) // length = len_pg, bytes = bytes_pg
|
|||
|
|
Ref(N_input) = ReferenceBytes(N_input) // length = len_in, bytes = bytes_in
|
|||
|
|
Ref(N_output) = ReferenceBytes(N_output) // length = len_out, bytes = bytes_out
|
|||
|
|
Ref(R_receipt) = ReferenceBytes(R_receipt) // length = len_rc, bytes = bytes_rc
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Then `EdgeBytes` under this profile are:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
edge_version = 0001 ; u16 (guard word)
|
|||
|
|
|
|||
|
|
type_id = 00000010 ; u32
|
|||
|
|
|
|||
|
|
from_count = 00000002 ; 2 sources
|
|||
|
|
from_nodes =
|
|||
|
|
000000?? bytes_pg ... ; EncodedRef(N_prog)
|
|||
|
|
000000?? bytes_in ... ; EncodedRef(N_input)
|
|||
|
|
|
|||
|
|
to_count = 00000001 ; 1 target
|
|||
|
|
to_nodes =
|
|||
|
|
000000?? bytes_out ... ; EncodedRef(N_output)
|
|||
|
|
|
|||
|
|
payload_ref =
|
|||
|
|
000000?? bytes_rc ... ; EncodedRef(R_receipt)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Where each `EncodedRef(X)` is:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ref_len(X) (u32) || ReferenceBytes(X)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
These `EdgeBytes` become `Artifact.bytes` for an EdgeArtifact with `type_tag = TYPE_TAG_TGK1_EDGE_V1`. All conformant encoders MUST produce the same bytes for the same logical `EdgeBody`; all conformant decoders MUST reconstruct the same `EdgeBody` from those bytes.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**End of `ENC/TGK1-EDGE/1 v0.1.0 — Canonical Encoding for TGK EdgeArtifacts` (draft).**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Document History
|
|||
|
|
|
|||
|
|
* **0.1.0 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline.
|