audit: kernel params bound and docgraph removal

This commit is contained in:
Carl Niklas Rydberg 2025-12-22 12:40:06 +01:00
parent a932363ad0
commit b3d776adb4
6 changed files with 104 additions and 312 deletions

View file

@ -43,7 +43,7 @@ Status legend: ✅ completed, ⬜ pending.
15. ✅ `tier1/tgk-store-1.md` 15. ✅ `tier1/tgk-store-1.md`
16. ✅ `tier1/tgk-prov-1.md` 16. ✅ `tier1/tgk-prov-1.md`
17. ✅ `tier1/opreg-pel1-kernel.md` 17. ✅ `tier1/opreg-pel1-kernel.md`
18. `tier1/opreg-pel1-kernel-params-1.md` 18. `tier1/opreg-pel1-kernel-params-1.md`
19. ⬜ `tier1/opreg-tgk-docgraph-1.md` 19. ⬜ `tier1/opreg-tgk-docgraph-1.md`
20. ⬜ `tier1/amduat20-stack-overview.md` 20. ⬜ `tier1/amduat20-stack-overview.md`
@ -246,8 +246,22 @@ Status legend: ✅ completed, ⬜ pending.
`AMDUAT_PEL_KERNEL_STATUS_INTERNAL`/`AMDUAT_PEL_KERNEL_STATUS_OOM`, which do `AMDUAT_PEL_KERNEL_STATUS_INTERNAL`/`AMDUAT_PEL_KERNEL_STATUS_OOM`, which do
not follow the `kernel_op_code << 16 | error_index` scheme and are not not follow the `kernel_op_code << 16 | error_index` scheme and are not
specified as kernel runtime error codes. specified as kernel runtime error codes.
- Resolution: not addressed (needs spec clarification or implementation changes - Resolution: documented `pel.bytes.params/1` in OPREG/PEL1-KERNEL and params
to either remove/relocate `pel.bytes.params`, suppress diagnostics for kernel profile; missing global params now yields `INVALID_INPUTS`; Exec_DAG no longer
runtime failures, and/or treat internal/OOM as out-of-model rather than emits diagnostics for kernel op runtime failures; internal/OOM paths now
kernel runtime errors). return out-of-model (no `ExecutionResultValue`), avoiding non-registry status
- Tests: not run. codes.
- Tests: `ctest --test-dir /home/niklas/build/amduat` (pass, 14 tests).
## 2025-12-22 — OPREG/PEL1-KERNEL-PARAMS/1 (`tier1/opreg-pel1-kernel-params-1.md`)
- Scope: kernel params canonical encodings/decoding, size bounds, and
`INVALID_PROGRAM` mapping for param decode errors.
- Findings: `amduat_decode_const` accepts `params_bytes` longer than
`0xFFFF_FFFF` as long as `bytes.len` fits in `size_t`, but the spec requires
any kernel params payload length exceeding `u32::MAX` to be treated as a
decode error (even for non-`ENC/PEL-PROGRAM-DAG` inputs).
- Resolution: added a `params_bytes.len <= UINT32_MAX` guard in
`amduat_pel_kernel_params_decode` so all kernel param decodes enforce the
u32 bound; added a regression test that feeds an oversized `params_bytes`
length and expects `INVALID_PROGRAM`.
- Tests: user reported “100% tests passed, 0 tests failed out of 14”.

View file

@ -115,6 +115,9 @@ bool amduat_pel_kernel_params_decode(
if (desc == NULL || out_params == NULL) { if (desc == NULL || out_params == NULL) {
return false; return false;
} }
if (params_bytes.len > UINT32_MAX) {
return false;
}
out_params->kind = desc->kind; out_params->kind = desc->kind;
switch (desc->kind) { switch (desc->kind) {

View file

@ -823,13 +823,8 @@ static bool amduat_pel_program_dag_exec_internal(
amduat_prepared_reset(&prepared); amduat_prepared_reset(&prepared);
prep_result = amduat_program_prepare(program, &prepared, &prep_error); prep_result = amduat_program_prepare(program, &prepared, &prep_error);
if (prep_result == AMDUAT_PEL_PROGRAM_PREP_OOM) { if (prep_result == AMDUAT_PEL_PROGRAM_PREP_OOM) {
amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED, amduat_prepared_free(&prepared);
AMDUAT_PEL_EXEC_ERROR_RUNTIME, return false;
AMDUAT_PEL_KERNEL_STATUS_OOM);
amduat_diag_setf(out_result,
AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED,
"runtime failed: out of memory");
return true;
} }
if (prep_result != AMDUAT_PEL_PROGRAM_PREP_OK) { if (prep_result != AMDUAT_PEL_PROGRAM_PREP_OK) {
amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_INVALID_PROGRAM, amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_INVALID_PROGRAM,
@ -988,13 +983,7 @@ static bool amduat_pel_program_dag_exec_internal(
program->nodes_len, sizeof(*node_results)); program->nodes_len, sizeof(*node_results));
if (node_results == NULL) { if (node_results == NULL) {
amduat_prepared_free(&prepared); amduat_prepared_free(&prepared);
amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED, return false;
AMDUAT_PEL_EXEC_ERROR_RUNTIME,
AMDUAT_PEL_KERNEL_STATUS_OOM);
amduat_diag_setf(out_result,
AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED,
"runtime failed: out of memory");
return true;
} }
for (i = 0; i < program->nodes_len; ++i) { for (i = 0; i < program->nodes_len; ++i) {
node_results[i].status = AMDUAT_PEL_NODE_TRACE_SKIPPED; node_results[i].status = AMDUAT_PEL_NODE_TRACE_SKIPPED;
@ -1015,13 +1004,7 @@ static bool amduat_pel_program_dag_exec_internal(
if (resolved_inputs == NULL) { if (resolved_inputs == NULL) {
amduat_node_results_free(node_results, program->nodes_len); amduat_node_results_free(node_results, program->nodes_len);
amduat_prepared_free(&prepared); amduat_prepared_free(&prepared);
amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED, return false;
AMDUAT_PEL_EXEC_ERROR_RUNTIME,
AMDUAT_PEL_KERNEL_STATUS_OOM);
amduat_diag_setf(out_result,
AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED,
"runtime failed: out of memory");
return true;
} }
} }
@ -1091,33 +1074,18 @@ static bool amduat_pel_program_dag_exec_internal(
&prepared.params[node_index], params, &prepared.params[node_index], params,
&node_results[node_index].outputs, &node_results[node_index].outputs,
&node_results[node_index].outputs_len, &status_code)) { &node_results[node_index].outputs_len, &status_code)) {
if (status_code == AMDUAT_PEL_KERNEL_STATUS_OOM) { if (status_code == AMDUAT_PEL_KERNEL_STATUS_OOM ||
status_code == AMDUAT_PEL_KERNEL_STATUS_INTERNAL ||
status_code == 0) {
free(resolved_inputs); free(resolved_inputs);
resolved_inputs = NULL; resolved_inputs = NULL;
goto oom_finish; goto env_fail;
}
if (status_code == 2 || status_code == 3 || status_code == 0) {
status_code = 1;
} }
node_results[node_index].status = AMDUAT_PEL_NODE_TRACE_FAILED; node_results[node_index].status = AMDUAT_PEL_NODE_TRACE_FAILED;
node_results[node_index].status_code = status_code; node_results[node_index].status_code = status_code;
(void)amduat_node_diag_setf(
&node_results[node_index],
AMDUAT_PEL_DAG_DIAG_NODE_RUNTIME_FAILED,
"runtime failed: node %u (%.*s@%u) status_code %u",
(unsigned int)node->id,
(int)node->op.name.len,
(const char *)node->op.name.data,
(unsigned int)node->op.version,
(unsigned int)status_code);
any_node_executed = true; any_node_executed = true;
amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED, amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED,
AMDUAT_PEL_EXEC_ERROR_RUNTIME, status_code); AMDUAT_PEL_EXEC_ERROR_RUNTIME, status_code);
amduat_diag_setf(out_result,
AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED,
"runtime failed: node %u status_code %u",
(unsigned int)node->id,
(unsigned int)status_code);
free(resolved_inputs); free(resolved_inputs);
goto finish; goto finish;
} }
@ -1132,7 +1100,7 @@ static bool amduat_pel_program_dag_exec_internal(
if (*out_outputs == NULL) { if (*out_outputs == NULL) {
free(resolved_inputs); free(resolved_inputs);
resolved_inputs = NULL; resolved_inputs = NULL;
goto oom_finish; goto env_fail;
} }
} }
@ -1166,7 +1134,7 @@ static bool amduat_pel_program_dag_exec_internal(
*out_outputs_len = 0; *out_outputs_len = 0;
free(resolved_inputs); free(resolved_inputs);
resolved_inputs = NULL; resolved_inputs = NULL;
goto oom_finish; goto env_fail;
} }
} }
@ -1195,13 +1163,7 @@ finish:
amduat_prepared_free(&prepared); amduat_prepared_free(&prepared);
return true; return true;
oom_finish: env_fail:
amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED,
AMDUAT_PEL_EXEC_ERROR_RUNTIME,
AMDUAT_PEL_KERNEL_STATUS_OOM);
amduat_diag_setf(out_result,
AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED,
"runtime failed: out of memory");
if (out_outputs != NULL && *out_outputs != NULL) { if (out_outputs != NULL && *out_outputs != NULL) {
amduat_pel_program_dag_free_outputs(*out_outputs, *out_outputs_len); amduat_pel_program_dag_free_outputs(*out_outputs, *out_outputs_len);
*out_outputs = NULL; *out_outputs = NULL;
@ -1223,7 +1185,7 @@ oom_finish:
node_results = NULL; node_results = NULL;
} }
amduat_prepared_free(&prepared); amduat_prepared_free(&prepared);
return true; return false;
} }
bool amduat_pel_program_dag_exec( bool amduat_pel_program_dag_exec(

View file

@ -2,6 +2,7 @@
#include "amduat/hash/asl1.h" #include "amduat/hash/asl1.h"
#include "amduat/pel/run.h" #include "amduat/pel/run.h"
#include <stdbool.h> #include <stdbool.h>
#include <limits.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -247,6 +248,67 @@ static int test_invalid_params(void) {
return 0; return 0;
} }
static int test_oversized_params_len(void) {
amduat_pel_dag_input_t node_inputs[1];
amduat_pel_node_t nodes[1];
amduat_pel_root_ref_t roots[1];
amduat_pel_program_t program;
amduat_artifact_t inputs[1];
amduat_artifact_t *outputs = NULL;
size_t outputs_len = 0;
amduat_pel_execution_result_value_t result;
const char op_concat[] = "pel.bytes.concat";
uint8_t params_byte = 0x00u;
size_t oversized_len;
if (SIZE_MAX <= UINT32_MAX) {
return 0;
}
oversized_len = (size_t)UINT32_MAX + 1u;
memset(&result, 0, sizeof(result));
node_inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL;
node_inputs[0].value.external.input_index = 0;
nodes[0].id = 1;
nodes[0].op.name = amduat_octets(op_concat, strlen(op_concat));
nodes[0].op.version = 1;
nodes[0].inputs = node_inputs;
nodes[0].inputs_len = 1;
nodes[0].params = amduat_octets(&params_byte, oversized_len);
roots[0].node_id = 1;
roots[0].output_index = 0;
program.nodes = nodes;
program.nodes_len = 1;
program.roots = roots;
program.roots_len = 1;
inputs[0] = amduat_artifact(amduat_octets("x", 1));
if (!amduat_pel_program_dag_exec(&program, inputs, 1, NULL, &outputs,
&outputs_len, &result)) {
fprintf(stderr, "exec failed\n");
amduat_pel_execution_result_free(&result);
return 1;
}
if (result.status != AMDUAT_PEL_EXEC_STATUS_INVALID_PROGRAM ||
result.summary.kind != AMDUAT_PEL_EXEC_ERROR_PROGRAM ||
result.summary.status_code != 2) {
fprintf(stderr, "unexpected oversized params status\n");
amduat_pel_program_dag_free_outputs(outputs, outputs_len);
amduat_pel_execution_result_free(&result);
return 1;
}
amduat_pel_program_dag_free_outputs(outputs, outputs_len);
amduat_pel_execution_result_free(&result);
return 0;
}
static int test_invalid_input_index(void) { static int test_invalid_input_index(void) {
amduat_pel_dag_input_t node_inputs[1]; amduat_pel_dag_input_t node_inputs[1];
amduat_pel_node_t nodes[1]; amduat_pel_node_t nodes[1];
@ -363,6 +425,9 @@ int main(void) {
if (test_invalid_params() != 0) { if (test_invalid_params() != 0) {
return 1; return 1;
} }
if (test_oversized_params_len() != 0) {
return 1;
}
if (test_invalid_input_index() != 0) { if (test_invalid_input_index() != 0) {
return 1; return 1;
} }

View file

@ -770,14 +770,11 @@ static int test_surf_runtime_failure_trace_diag(void) {
amduat_pel_surface_execution_result_t decoded; amduat_pel_surface_execution_result_t decoded;
amduat_artifact_t trace_artifact; amduat_artifact_t trace_artifact;
amduat_pel_trace_dag_value_t trace; amduat_pel_trace_dag_value_t trace;
char expected_msg[128];
int expected_len;
int exit_code = 1; int exit_code = 1;
uint8_t payload_a[] = {'a'}; uint8_t payload_a[] = {'a'};
uint8_t payload_b[] = {'b'}; uint8_t payload_b[] = {'b'};
amduat_artifact_t input_a; amduat_artifact_t input_a;
amduat_artifact_t input_b; amduat_artifact_t input_b;
const uint32_t diag_code = 0x00030002u;
cfg.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1; cfg.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
cfg.hash_id = AMDUAT_HASH_ASL1_ID_SHA256; cfg.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
@ -861,20 +858,11 @@ static int test_surf_runtime_failure_trace_diag(void) {
} }
artifact_free(&trace_artifact); artifact_free(&trace_artifact);
expected_len = snprintf(expected_msg, sizeof(expected_msg), if (trace.node_traces_len != 1 ||
"runtime failed: node 1 (pel.bytes.concat@1) "
"status_code %u",
(unsigned int)
AMDUAT_PEL_KERNEL_STATUS_CONCAT_TYPE_TAG_MISMATCH);
if (expected_len < 0 ||
trace.node_traces_len != 1 ||
trace.node_traces[0].status != AMDUAT_PEL_NODE_TRACE_FAILED || trace.node_traces[0].status != AMDUAT_PEL_NODE_TRACE_FAILED ||
trace.node_traces[0].diagnostics_len < 1 || trace.node_traces[0].status_code !=
trace.node_traces[0].diagnostics[0].code != diag_code || AMDUAT_PEL_KERNEL_STATUS_CONCAT_TYPE_TAG_MISMATCH ||
trace.node_traces[0].diagnostics[0].message.len != trace.node_traces[0].diagnostics_len != 0) {
(size_t)expected_len ||
memcmp(trace.node_traces[0].diagnostics[0].message.data,
expected_msg, (size_t)expected_len) != 0) {
fprintf(stderr, "trace runtime diagnostics mismatch\n"); fprintf(stderr, "trace runtime diagnostics mismatch\n");
amduat_enc_pel_trace_dag_free(&trace); amduat_enc_pel_trace_dag_free(&trace);
goto cleanup_decoded; goto cleanup_decoded;

View file

@ -1,240 +0,0 @@
# OPREG/TGK-DOCGRAPH/1 — Document Graph Registry
Status: Draft
Owner: Architecture
Version: 0.1.0
SoT: Plan
Last Updated: 2025-12-01
Linked Phase Pack: PH12
Tags: [registry, tgk, docgraph]
<!-- Source: /amduat/logs/ph12/evidence/import/PH12-EV-IMPORT-001/opreg-tgk-docgraph-design-20251201.md | Canonical: /amduat/tier1/opreg-tgk-docgraph-1.md -->
**Document ID:** `OPREG/TGK-DOCGRAPH/1`
**Layer:** L1 Profile (TGK Doc Graph Registry over `TGK/1-CORE` + `ENC/TGK1-EDGE/1`)
**Depends on (normative):**
* `ASL/1-CORE v0.4.x``Artifact`, `Reference`, `TypeTag`, `HashId`
* `ENC/ASL1-CORE v1.x` — canonical encodings for Artifacts and References
* `HASH/ASL1 v0.2.x` — ASL1 hash family (`HASH-ASL1-256`)
* `TGK/1-CORE v0.7.x` — trace graph kernel: `Node`, `EdgeBody`, `EdgeTypeId`
* `ENC/TGK1-EDGE/1 v0.1.x` — canonical encoding for `EdgeBody` / EdgeArtifacts
* `AMDUAT-DOCID` (Tier-0) — document identity and SoT/surface model
**Integrates with (informative):**
* `TGK/STORE/1` — graph store/query profile over ASL/1-STORE + TGK
* ADR-032 and PH10/PH12 import designs (RΩ / export)
* Future doc graph consumers (assistant overlays, IDX, provenance views)
© 2025 Amduat Programme.
## 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/TGK-DOCGRAPH/1` defines a **doc/import/navigation graph registry** for Amduat:
* It names **node concepts** (as ASL/1 Artifacts) for:
* conceptual documents (DOCID lineages),
* document versions at a given snapshot (e.g. RΩ),
* Git commits and blobs,
* Amduat SoT instances.
* It names **edge types** (`EdgeTypeId`s) that connect those concepts:
* document ↔ version, surface, SoT state,
* version ↔ Git blob/commit,
* document ↔ Amduat instance.
* It constrains how those edges are represented as EdgeArtifacts under
`ENC/TGK1-EDGE/1` and consumed via `TGK/STORE/1`.
This registry is intentionally **doc/import scoped**. Execution, fact, and
certificate edges live in their own TGK/OPREG registries and MUST NOT reuse
`EdgeTypeId` assignments from this doc graph registry.
This Tier-1 stub is the **canonical registry companion** to the PH12 design
note `PH12-EV-IMPORT-001 — Doc Graph OPREG Profile Design
(/logs/ph12/evidence/import/PH12-EV-IMPORT-001/opreg-tgk-docgraph-design-20251201.md)`,
which records design intent and sandbox experience; this document is the SoT
for the node and edge vocabulary.
### 0.2 Non-goals
This registry does **not** define:
* any storage API (`ASL/1-STORE`, `TGK/STORE/1` already cover that),
* any provenance algorithms or queries (`TGK/PROV/1` and higher layers),
* any assistant or overlay behavior (those consume this registry),
* concrete import/export profiles (ADR-032 handles those).
It only defines **concepts and edge types**; encoding and storage use existing
Tier-1 profiles.
---
## 1. Node Concepts (Informative overview)
This section summarizes node concepts; canonical encodings and type_tags are
defined in companion encoding profiles (TBD).
### 1.1 DOC_CONCEPT
Conceptual governed document identity per `AMDUAT-DOCID`:
* `identity_authority` (string),
* `lineage_id` (string),
* optional `doc_code` (string),
* optional `code_status` (e.g. `tentative`, `stable`).
There is exactly one `DOC_CONCEPT` node per `(identity_authority, lineage_id)`.
### 1.2 DOC_VERSION
Versioned SoT slice of a governed document at a snapshot commit:
* `identity_authority`, `lineage_id`, `doc_code`, `code_status`,
* `g_commit` (Git commit id),
* `sha256` (content hash of the doc bytes at `g_commit`),
* `path` (repository path at `g_commit`, e.g. `/amduat/tier0/docid.md`),
* `surface`, `sot` (SoT state) per DOCID header.
Multiple `DOC_VERSION` nodes may exist for a `DOC_CONCEPT` across commits.
### 1.3 GIT_COMMIT
Git commit metadata:
* `commit` (sha1),
* `parents` (list of parent commit ids),
* `tree` (tree id),
* `author_name`, `author_email`, `authored_at`,
* `committer_name`, `committer_email`, `committed_at`,
* summary or truncated message.
### 1.4 GIT_BLOB
Content snapshot for a single blob at `g_commit`:
* `blob_sha` (sha1),
* `sha256` (content hash),
* `size_bytes`,
* `mode` (tree mode, including exec/symlink bits),
* `path` at `g_commit`.
### 1.5 AMDUAT_INSTANCE
Descriptor for an Amduat SoT instance:
* `g_commit` (RΩ commit),
* `store_root` (SoT store root),
* `store_backend_id`,
* references to RΩ FER/1 receipts and manifests,
* optional labels (environment, hostname, etc.).
### 1.6 Helper nodes
* `SURFACE` — surface classification nodes (e.g. `tier0`, `tier1`, `phase`, `evidence`).
* `SOT_STATE` — SoT state nodes (`Yes`, `Plan`, `Ref`).
---
## 2. Edge Types (Doc Graph Domain)
`EdgeTypeId` values in this registry are reserved for doc/import/navigation
edges. Concrete numeric assignments live in the encoding/catalogue layer.
Implementations and other OPREG registries MUST treat these `EdgeTypeId`s as
belonging exclusively to the **Amduat doc graph domain**:
* the eventual allocation for this registry is expected to reserve a contiguous
`EdgeTypeId` band (informally: an `AMDUAT-DOCGRAPH` band),
* only doc/import/navigation semantics (edges in §§2.12.4) may occupy that
band,
* PEL execution, FER/1, CIL, FCT, and other TGK domains MUST use their own
registries and bands.
### 2.1 Identity & version edges
* `EDGE_DOC_HAS_VERSION`
`DOC_CONCEPT → DOC_VERSION` — this version belongs to this conceptual document.
* `EDGE_VERSION_OF`
`DOC_VERSION → DOC_CONCEPT` — reverse link; derivable from `EDGE_DOC_HAS_VERSION`.
* `EDGE_DOC_HAS_IDENTITY`
`DOC_VERSION → DOC_CONCEPT` — DOCID identity is attached to this version.
### 2.2 Surface & SoT edges
* `EDGE_DOC_ON_SURFACE`
`DOC_VERSION → SURFACE` — surface classification (governance/spec/phase/evidence).
* `EDGE_DOC_SOT`
`DOC_VERSION → SOT_STATE` — SoT status (`Yes`, `Plan`, `Ref`) for this version.
### 2.3 Git provenance edges
* `EDGE_VERSION_HAS_BLOB`
`DOC_VERSION → GIT_BLOB` — ties a document version to the blob at `g_commit`.
* `EDGE_VERSION_FROM_COMMIT`
`DOC_VERSION → GIT_COMMIT` — last commit that touched this path at/before the snapshot.
### 2.4 SoT instance edges
* `EDGE_DOC_MEMBER_OF_AMDUAT`
`DOC_CONCEPT → AMDUAT_INSTANCE` — this document is part of a particular Amduat instance.
---
## 3. Encoding & Store Integration (Summary)
All doc-graph edges:
* are represented as TGK `EdgeBody` values with `EdgeTypeId` from this registry,
* are encoded as EdgeArtifacts via `ENC/TGK1-EDGE/1` using `TYPE_TAG_TGK1_EDGE_V1`,
* derive `EdgeRef` identities via `HASH/ASL1` over `EdgeBytes`,
* live in ASL/1-STORE instances alongside other Artifacts.
Nodes (`DOC_CONCEPT`, `DOC_VERSION`, `GIT_COMMIT`, `GIT_BLOB`, `AMDUAT_INSTANCE`, etc.) are ordinary
ASL/1 Artifacts; their `Reference`s are the TGK nodes.
`TGK/STORE/1` provides query semantics over the resulting graph.
JSON overlays or other projected views (for example, PH12 doc graph sandboxes)
MAY be emitted for human navigation and experiments, but they are always
derived from the underlying node Artifacts and EdgeArtifacts governed by this
registry and `ENC/TGK1-EDGE/1`; overlays are never the source of truth for
doc graph semantics.
---
## 4. Ingest & Encoder Interaction (Informative)
Implementations are expected to:
* materialise node Artifacts per this registry (and companion encoding profiles),
* emit FER/1 receipts for ingest pipelines,
* emit an idempotent edge worklist (doc-edge queue) that references `EdgeTypeId`s
from this registry and node `Reference`s,
* use a separate encoder to turn worklist items into EdgeArtifacts using `ENC/TGK1-EDGE/1`,
writing them into ASL/1-STORE for consumption via `TGK/STORE/1`.
Details of worklist format and encoder scheduling are left to PH12/PHB01
implementation notes; this registry only fixes the conceptual node/edge space.