trow AI at it.

This commit is contained in:
Carl Niklas Rydberg 2026-02-22 00:25:56 +01:00
parent 7b3ef3eab4
commit 3473b19425
57 changed files with 4476 additions and 64 deletions

View file

@ -66,6 +66,7 @@ set(AMDUAT_ASL_SRCS
src/near_core/asl/collection_view.c src/near_core/asl/collection_view.c
src/near_core/asl/none.c src/near_core/asl/none.c
src/near_core/asl/artifact_io.c src/near_core/asl/artifact_io.c
src/near_core/asl/identity.c
src/near_core/asl/io.c src/near_core/asl/io.c
src/near_core/asl/index_accel.c src/near_core/asl/index_accel.c
src/near_core/asl/index_bloom.c src/near_core/asl/index_bloom.c
@ -78,6 +79,8 @@ set(AMDUAT_ASL_SRCS
src/near_core/asl/store.c src/near_core/asl/store.c
src/near_core/asl/ref_text.c src/near_core/asl/ref_text.c
src/near_core/asl/registry.c src/near_core/asl/registry.c
src/near_core/bytestore.c
src/near_core/fps.c
) )
set(AMDUAT_ASL_LOG_STORE_SRCS set(AMDUAT_ASL_LOG_STORE_SRCS
@ -106,6 +109,8 @@ set(AMDUAT_ENC_SRCS
src/near_core/enc/asl_log.c src/near_core/enc/asl_log.c
src/near_core/enc/asl_core_index.c src/near_core/enc/asl_core_index.c
src/near_core/enc/asl_tgk_exec_plan.c src/near_core/enc/asl_tgk_exec_plan.c
src/near_core/enc/pcb1.c
src/near_core/enc/fcs1.c
src/near_core/enc/pel_program_dag.c src/near_core/enc/pel_program_dag.c
src/near_core/enc/pel_program_dag_desc.c src/near_core/enc/pel_program_dag_desc.c
src/near_core/enc/pel_trace_dag.c src/near_core/enc/pel_trace_dag.c
@ -441,6 +446,26 @@ target_link_libraries(amduat_test_asl_tgk_exec_plan
) )
add_test(NAME asl_tgk_exec_plan COMMAND amduat_test_asl_tgk_exec_plan) add_test(NAME asl_tgk_exec_plan COMMAND amduat_test_asl_tgk_exec_plan)
add_executable(amduat_test_fps tests/enc/test_fps.c)
target_include_directories(amduat_test_fps
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_fps
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME fps COMMAND amduat_test_fps)
add_executable(amduat_test_fcs_pcb1 tests/enc/test_fcs_pcb1.c)
target_include_directories(amduat_test_fcs_pcb1
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_fcs_pcb1
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME fcs_pcb1 COMMAND amduat_test_fcs_pcb1)
add_executable(amduat_test_tgk1_edge tests/enc/test_tgk1_edge.c) add_executable(amduat_test_tgk1_edge tests/enc/test_tgk1_edge.c)
target_include_directories(amduat_test_tgk1_edge target_include_directories(amduat_test_tgk1_edge
PRIVATE ${AMDUAT_INTERNAL_DIR} PRIVATE ${AMDUAT_INTERNAL_DIR}
@ -471,6 +496,17 @@ target_link_libraries(amduat_test_tgk_store_mem
) )
add_test(NAME tgk_store_mem COMMAND amduat_test_tgk_store_mem) add_test(NAME tgk_store_mem COMMAND amduat_test_tgk_store_mem)
add_executable(amduat_test_tgk_error_taxonomy
tests/tgk/test_tgk_error_taxonomy.c)
target_include_directories(amduat_test_tgk_error_taxonomy
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_tgk_error_taxonomy
PRIVATE amduat_tgk
)
add_test(NAME tgk_error_taxonomy COMMAND amduat_test_tgk_error_taxonomy)
add_executable(amduat_test_tgk_store_fs tests/tgk/test_tgk_store_fs.c) add_executable(amduat_test_tgk_store_fs tests/tgk/test_tgk_store_fs.c)
target_include_directories(amduat_test_tgk_store_fs target_include_directories(amduat_test_tgk_store_fs
PRIVATE ${AMDUAT_INTERNAL_DIR} PRIVATE ${AMDUAT_INTERNAL_DIR}
@ -598,6 +634,28 @@ target_link_libraries(amduat_test_asl_index_accel
) )
add_test(NAME asl_index_accel COMMAND amduat_test_asl_index_accel) add_test(NAME asl_index_accel COMMAND amduat_test_asl_index_accel)
add_executable(amduat_test_asl_log_encoding
tests/asl/test_asl_log_encoding.c)
target_include_directories(amduat_test_asl_log_encoding
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_asl_log_encoding
PRIVATE amduat_asl
)
add_test(NAME asl_log_encoding COMMAND amduat_test_asl_log_encoding)
add_executable(amduat_test_asl_error_taxonomy
tests/asl/test_asl_error_taxonomy.c)
target_include_directories(amduat_test_asl_error_taxonomy
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_asl_error_taxonomy
PRIVATE amduat_asl
)
add_test(NAME asl_error_taxonomy COMMAND amduat_test_asl_error_taxonomy)
add_executable(amduat_test_asl_derivation_index_fs add_executable(amduat_test_asl_derivation_index_fs
tests/asl/test_asl_derivation_index_fs.c) tests/asl/test_asl_derivation_index_fs.c)
target_include_directories(amduat_test_asl_derivation_index_fs target_include_directories(amduat_test_asl_derivation_index_fs

View file

@ -17,11 +17,14 @@ it is introduced in this change and implemented in code (referenced below).
2. **Reference** = `(hash_id, digest)`; equality is hash-id + digest equality. 2. **Reference** = `(hash_id, digest)`; equality is hash-id + digest equality.
Sources: `tier1/asl-1-core.md`, `include/amduat/asl/core.h`. Sources: `tier1/asl-1-core.md`, `include/amduat/asl/core.h`.
3. **Reference derivation** is based on canonical encoding + hash family; the API 3. **Reference derivation** uses canonical encoding plus the ASL1 domain
exposes this via `amduat_asl_ref_derive`. separator (`"AMDUAT:ASL1:ART\0"`) and hash family; the API exposes this via
Sources: `tier1/asl-1-core.md`, `include/amduat/asl/ref_derive.h`. `amduat_asl_ref_derive`.
Sources: `tier1/asl-1-core.md`, `include/amduat/asl/ref_derive.h`,
`include/amduat/asl/identity.h`.
4. **Hash family** in use for the stack is HASH/ASL1 (e.g., SHA-256). 4. **Hash family** in use for the stack is HASH/ASL1 (e.g., SHA-256), with
registry/version metadata exposed in the core API.
Sources: `tier1/hash-asl1.md`, `include/amduat/hash/asl1.h`. Sources: `tier1/hash-asl1.md`, `include/amduat/hash/asl1.h`.
--- ---

View file

@ -0,0 +1,50 @@
# ADR-031 — ASL + TGK Index Separation
Status: Accepted
Date: 2026-02-21
Decision Owner: Architecture
## Context
ASL index/log provide artifact visibility, replay, and tombstone semantics.
TGK store provides graph query semantics over TGK edge artifacts. We must decide
whether to **merge** ASL and TGK indexes into one unified index structure, or
keep them separated with explicit cross-layer invariants.
## Decision
We **reject** a merged ASL+TGK index.
ASL index/log and TGK index remain **separate layers** with strict invariants
that keep them consistent.
## Rationale
* ASL index is about **artifact visibility and replay**; TGK index is about
**graph query acceleration**. Their update streams and invariants differ.
* Keeping them separate preserves clean layering and deterministic replay.
* TGK indexes can be rebuilt or swapped without changing ASL semantics.
* A merged index risks coupling graph query policies with storage visibility,
complicating federation and policy enforcement.
## Invariants (Normative)
1. **Visibility alignment:** TGK indexes MUST only index artifacts visible under
ASL log replay at the same `(snapshot_id, log_position)`.
2. **Tombstone enforcement:** TGK indexes MUST respect ASL tombstones and
tombstone lifts at their `logseq`.
3. **No new identity:** TGK nodes/edges MUST use ASL `Reference` identities;
TGK MUST NOT introduce a distinct object identity scheme.
4. **Deterministic derivation:** TGK index updates MUST be deterministically
derived from ASL-admitted records (segments/log). No side-channel writes.
5. **Replay equivalence:** Replaying ASL log + sealed segments MUST yield the
same TGK index state (up to encoding) as an online build.
6. **Snapshot consistency:** TGK queries at a snapshot MUST be consistent with
ASL `IndexState` for that snapshot.
## Consequences
* ASL and TGK index encodings remain separate specs.
* Conformance tests for ASL and TGK can evolve independently, with explicit
cross-layer checks for the invariants above.
* TGK index rebuilds remain possible without touching ASL log/segment formats.

View file

@ -0,0 +1,28 @@
#ifndef AMDUAT_ASL_IDENTITY_H
#define AMDUAT_ASL_IDENTITY_H
#include "amduat/asl/core.h"
#include "amduat/hash/asl1.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
const char *hash_registry;
uint16_t hash_registry_version;
amduat_octets_t domain_separator;
} amduat_asl_identity_rules_t;
/* Domain separator for ASL/1 artifact identity hashing. */
amduat_octets_t amduat_asl_identity_domain_separator(void);
const amduat_asl_identity_rules_t *amduat_asl_identity_rules(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ASL_IDENTITY_H */

137
include/amduat/bytestore.h Normal file
View file

@ -0,0 +1,137 @@
#ifndef AMDUAT_BYTESTORE_H
#define AMDUAT_BYTESTORE_H
#include "amduat/asl/core.h"
#include "amduat/hash/asl1.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
AMDUAT_BYTESTORE_OK = 0,
AMDUAT_BYTESTORE_ERR_POLICY_SIZE = 1,
AMDUAT_BYTESTORE_ERR_IDENTITY_MISMATCH = 2,
AMDUAT_BYTESTORE_ERR_STREAM_ORDER = 3,
AMDUAT_BYTESTORE_ERR_STREAM_TRUNCATED = 4,
AMDUAT_BYTESTORE_ERR_AREA_VIOLATION = 5,
AMDUAT_BYTESTORE_ERR_CRASH_SIMULATION = 6,
AMDUAT_BYTESTORE_ERR_STORE_MISSING = 7,
AMDUAT_BYTESTORE_ERR_UNSUPPORTED = 8,
AMDUAT_BYTESTORE_ERR_IO = 9,
AMDUAT_BYTESTORE_ERR_INVALID = 10
} amduat_bytestore_error_t;
typedef struct {
amduat_hash_id_t hash_id;
size_t max_object_size;
const char *public_root;
const char *secure_root;
} amduat_bytestore_config_t;
typedef struct {
bool present;
size_t size;
} amduat_bytestore_stat_t;
typedef struct {
/* Return 1 for a chunk, 0 for end-of-stream, -1 on error. */
int (*next)(void *ctx, amduat_octets_t *out_chunk);
void *ctx;
} amduat_bytestore_stream_t;
typedef struct {
amduat_bytestore_error_t (*put)(void *ctx,
amduat_octets_t payload,
amduat_reference_t *out_cid);
amduat_bytestore_error_t (*put_stream)(void *ctx,
amduat_bytestore_stream_t stream,
amduat_reference_t *out_cid);
amduat_bytestore_error_t (*import_cor)(void *ctx,
amduat_octets_t envelope,
amduat_reference_t *out_cid);
amduat_bytestore_error_t (*export_cor)(void *ctx,
amduat_reference_t cid,
amduat_octets_t *out_envelope);
amduat_bytestore_error_t (*get)(void *ctx,
amduat_reference_t cid,
amduat_octets_t *out_bytes);
amduat_bytestore_error_t (*stat)(void *ctx,
amduat_reference_t cid,
amduat_bytestore_stat_t *out_stat);
amduat_bytestore_error_t (*assert_area_isolation)(void *ctx,
const char *public_root,
const char *secure_root);
amduat_bytestore_error_t (*validate_config)(void *ctx,
amduat_bytestore_config_t config);
} amduat_bytestore_ops_t;
static inline void amduat_bytestore_ops_init(amduat_bytestore_ops_t *ops) {
if (ops == NULL) {
return;
}
ops->put = NULL;
ops->put_stream = NULL;
ops->import_cor = NULL;
ops->export_cor = NULL;
ops->get = NULL;
ops->stat = NULL;
ops->assert_area_isolation = NULL;
ops->validate_config = NULL;
}
typedef struct {
amduat_bytestore_config_t config;
amduat_bytestore_ops_t ops;
void *ctx;
} amduat_bytestore_t;
void amduat_bytestore_init(amduat_bytestore_t *store,
amduat_bytestore_config_t config,
amduat_bytestore_ops_t ops,
void *ctx);
amduat_bytestore_error_t amduat_bytestore_put(amduat_bytestore_t *store,
amduat_octets_t payload,
amduat_reference_t *out_cid);
amduat_bytestore_error_t amduat_bytestore_put_stream(
amduat_bytestore_t *store,
amduat_bytestore_stream_t stream,
amduat_reference_t *out_cid);
amduat_bytestore_error_t amduat_bytestore_import_cor(
amduat_bytestore_t *store,
amduat_octets_t envelope,
amduat_reference_t *out_cid);
amduat_bytestore_error_t amduat_bytestore_export_cor(
amduat_bytestore_t *store,
amduat_reference_t cid,
amduat_octets_t *out_envelope);
amduat_bytestore_error_t amduat_bytestore_get(amduat_bytestore_t *store,
amduat_reference_t cid,
amduat_octets_t *out_bytes);
amduat_bytestore_error_t amduat_bytestore_stat(
amduat_bytestore_t *store,
amduat_reference_t cid,
amduat_bytestore_stat_t *out_stat);
amduat_bytestore_error_t amduat_bytestore_assert_area_isolation(
amduat_bytestore_t *store,
const char *public_root,
const char *secure_root);
bool amduat_bytestore_cid_for_payload(amduat_hash_id_t hash_id,
amduat_octets_t payload,
amduat_reference_t *out_cid);
bool amduat_bytestore_cid_for_stream(amduat_hash_id_t hash_id,
amduat_bytestore_stream_t stream,
amduat_reference_t *out_cid);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_BYTESTORE_H */

52
include/amduat/enc/fcs1.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef AMDUAT_ENC_FCS1_H
#define AMDUAT_ENC_FCS1_H
#include "amduat/asl/core.h"
#include "amduat/enc/pcb1.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
AMDUAT_FCS1_OK = 0,
AMDUAT_FCS1_ERR_HEADER_INVALID = 1,
AMDUAT_FCS1_ERR_TAG_ORDER = 2,
AMDUAT_FCS1_ERR_UNKNOWN_TAG = 3,
AMDUAT_FCS1_ERR_PARAMETER_FORMAT = 4,
AMDUAT_FCS1_ERR_PCB_ARITY_MISMATCH = 5,
AMDUAT_FCS1_ERR_CYCLE_DETECTED = 6,
AMDUAT_FCS1_ERR_VARINT_NON_MINIMAL = 7,
AMDUAT_FCS1_ERR_TRAILING_BYTES = 8,
AMDUAT_FCS1_ERR_INVALID = 9
} amduat_fcs1_error_t;
typedef struct {
amduat_reference_t function_ptr;
amduat_reference_t parameter_block;
uint32_t arity;
} amduat_fcs1_descriptor_t;
typedef bool (*amduat_fcs1_resolve_fn)(void *ctx,
amduat_reference_t ref,
amduat_octets_t *out_bytes);
amduat_fcs1_error_t amduat_fcs1_decode_v1(amduat_octets_t bytes,
amduat_fcs1_descriptor_t *out_desc);
amduat_fcs1_error_t amduat_fcs1_validate_v1(
amduat_octets_t bytes,
amduat_fcs1_resolve_fn resolver,
void *resolver_ctx);
void amduat_fcs1_descriptor_free(amduat_fcs1_descriptor_t *desc);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ENC_FCS1_H */

38
include/amduat/enc/pcb1.h Normal file
View file

@ -0,0 +1,38 @@
#ifndef AMDUAT_ENC_PCB1_H
#define AMDUAT_ENC_PCB1_H
#include "amduat/asl/core.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
AMDUAT_PCB1_OK = 0,
AMDUAT_PCB1_ERR_HEADER_INVALID = 1,
AMDUAT_PCB1_ERR_TAG_ORDER = 2,
AMDUAT_PCB1_ERR_UNKNOWN_TAG = 3,
AMDUAT_PCB1_ERR_MANIFEST_ORDER = 4,
AMDUAT_PCB1_ERR_DIGEST_MISMATCH = 5,
AMDUAT_PCB1_ERR_VARINT_NON_MINIMAL = 6,
AMDUAT_PCB1_ERR_TRAILING_BYTES = 7,
AMDUAT_PCB1_ERR_INVALID = 8
} amduat_pcb1_error_t;
typedef struct {
uint32_t slot_count;
} amduat_pcb1_manifest_t;
amduat_pcb1_error_t amduat_pcb1_decode_v1(amduat_octets_t bytes,
amduat_pcb1_manifest_t *out_manifest,
amduat_octets_t *out_slot_data);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ENC_PCB1_H */

61
include/amduat/fps.h Normal file
View file

@ -0,0 +1,61 @@
#ifndef AMDUAT_FPS_H
#define AMDUAT_FPS_H
#include "amduat/asl/core.h"
#include "amduat/hash/asl1.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
AMDUAT_FPS_OK = 0,
AMDUAT_FPS_ERR_IO_FAILURE = 1,
AMDUAT_FPS_ERR_NORMALIZATION = 2,
AMDUAT_FPS_ERR_CID_NOT_FOUND = 3,
AMDUAT_FPS_ERR_SLICE_RANGE = 4,
AMDUAT_FPS_ERR_EMPTY_INPUTS = 5,
AMDUAT_FPS_ERR_REV_ALIGNMENT = 6,
AMDUAT_FPS_ERR_INVALID_LEVEL = 7,
AMDUAT_FPS_ERR_SPLICE_RANGE = 8,
AMDUAT_FPS_ERR_INVALID = 9
} amduat_fps_error_t;
typedef enum {
AMDUAT_FPS_REVERSE_BITS = 0,
AMDUAT_FPS_REVERSE_BYTES = 1,
AMDUAT_FPS_REVERSE_WORDS = 2,
AMDUAT_FPS_REVERSE_LONGS = 3
} amduat_fps_reverse_level_t;
amduat_fps_error_t amduat_fps_slice(amduat_octets_t input,
size_t offset,
size_t length,
amduat_octets_t *out);
amduat_fps_error_t amduat_fps_concat(const amduat_octets_t *inputs,
size_t input_count,
amduat_octets_t *out);
amduat_fps_error_t amduat_fps_reverse(amduat_octets_t input,
amduat_fps_reverse_level_t level,
amduat_octets_t *out);
amduat_fps_error_t amduat_fps_splice(amduat_octets_t a,
size_t offset,
amduat_octets_t b,
amduat_octets_t *out);
bool amduat_fps_cid_for_payload(amduat_hash_id_t hash_id,
amduat_octets_t payload,
amduat_reference_t *out_cid);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_FPS_H */

View file

@ -34,6 +34,15 @@ typedef struct {
} amduat_hash_asl1_stream_t; } amduat_hash_asl1_stream_t;
enum { AMDUAT_HASH_ASL1_ID_SHA256 = 0x0001 }; enum { AMDUAT_HASH_ASL1_ID_SHA256 = 0x0001 };
enum {
AMDUAT_HASH_ASL1_VERSION_MAJOR = 0,
AMDUAT_HASH_ASL1_VERSION_MINOR = 2,
AMDUAT_HASH_ASL1_VERSION_PATCH = 5,
AMDUAT_HASH_ASL1_VERSION = 0x0205
};
#define AMDUAT_HASH_ASL1_FAMILY "ASL1"
#define AMDUAT_HASH_ASL1_REGISTRY "HASH/ASL1"
amduat_octets_t amduat_hash_asl1_key(amduat_hash_id_t hash_id, uint8_t out[2]); amduat_octets_t amduat_hash_asl1_key(amduat_hash_id_t hash_id, uint8_t out[2]);
@ -49,6 +58,11 @@ bool amduat_hash_asl1_digest(amduat_hash_id_t hash_id,
amduat_octets_t input, amduat_octets_t input,
uint8_t *out, uint8_t *out,
size_t out_len); size_t out_len);
bool amduat_hash_asl1_digest_domain(amduat_hash_id_t hash_id,
amduat_octets_t domain_separator,
amduat_octets_t input,
uint8_t *out,
size_t out_len);
bool amduat_hash_asl1_stream_init(amduat_hash_id_t hash_id, bool amduat_hash_asl1_stream_init(amduat_hash_id_t hash_id,
amduat_hash_asl1_stream_t *out); amduat_hash_asl1_stream_t *out);

View file

@ -24,6 +24,9 @@ are data nodes that can be hashed and referenced.
## JSONL manifests ## JSONL manifests
Common conventions for all registry manifests are captured in
`registry/manifest.schema.md`.
Each registry has a `registry/<name>.jsonl` manifest. Each line is one entry. Each registry has a `registry/<name>.jsonl` manifest. Each line is one entry.
The manifest is the source of truth for codegen and documentation tables. The manifest is the source of truth for codegen and documentation tables.

View file

@ -1 +1 @@
{"registry":"ENC/ASL1-CORE","profile_id":"0x0001","handle":"amduat.enc.asl1.core.profile.0001@1","name":"ASL_ENC_CORE_V1","version":"0x0105","status":"mandatory","spec_ref":"ENC/ASL1-CORE v1.0.5","notes":"Canonical encoding profile for ASL/1-CORE artifacts and references.","descriptor_sha256":"ed4ce5f50d4cb62d5e420da377ffcc0a767939f795b2c9ea00e57d79e12d93c2"} {"registry":"ENC/ASL1-CORE","profile_id":"0x0001","handle":"amduat.enc.asl1.core.profile.0001@1","name":"ASL_ENC_CORE_V1","version":"0x0106","status":"mandatory","spec_ref":"ENC/ASL1-CORE v1.0.6","notes":"Canonical encoding profile for ASL/1-CORE artifacts and references.","descriptor_sha256":"2d86c1367149e37d076a63fc91673dbc56fe08df83c94a11e323a0185948a87b"}

View file

@ -0,0 +1,43 @@
# Registry Manifest Schema (core)
This file defines the common conventions for registry JSONL manifests in
`vendor/amduat/registry/`. Each registry then supplies a registry-specific
schema that fixes the key field(s) and the descriptor field ordering used for
`descriptor_sha256`.
## JSONL rules
- Each line is one JSON object (JSONL).
- Fields must match the registry-specific schema.
- Required fields must be present even if the value is `null`.
## Handle naming
Handles are stable identifiers in the `amduat` namespace:
```
amduat.<domain>.<registry>.<kind>.<id>@<version>
```
`id` is the canonical key for the registry (usually lowercase, zero-padded hex).
`version` increments only if the meaning of the key changes.
## Canonical descriptor digest
Each entry includes `descriptor_sha256`, the SHA-256 digest (lowercase hex) of
its canonical descriptor JSON object. The canonical descriptor object:
- Includes the descriptor fields defined by the registry-specific schema.
- Uses the exact field ordering specified by the registry-specific schema.
- Excludes the `descriptor_sha256` field itself.
- Uses standard JSON encoding with no extra whitespace.
- Is hashed over its UTF-8 byte sequence.
Registry-specific schema files define the precise field ordering and any
additional constraints on descriptor fields.
## Canonical key encoding
Each registry defines the byte encoding of its key. The encoding is used when
constructing ASL registry artifacts. See the registry-specific schema for the
exact key encoding (e.g., big-endian `u16` for `HASH/ASL1`).

View file

@ -0,0 +1,18 @@
#include "amduat/asl/identity.h"
static const uint8_t k_asl_identity_domain_sep[] = {
'A', 'M', 'D', 'U', 'A', 'T', ':', 'A',
'S', 'L', '1', ':', 'A', 'R', 'T', 0x00};
amduat_octets_t amduat_asl_identity_domain_separator(void) {
return amduat_octets(k_asl_identity_domain_sep,
sizeof(k_asl_identity_domain_sep));
}
const amduat_asl_identity_rules_t *amduat_asl_identity_rules(void) {
static const amduat_asl_identity_rules_t k_rules = {
AMDUAT_HASH_ASL1_REGISTRY,
AMDUAT_HASH_ASL1_VERSION,
{k_asl_identity_domain_sep, sizeof(k_asl_identity_domain_sep)}};
return &k_rules;
}

View file

@ -1,5 +1,6 @@
#include "amduat/asl/ref_derive.h" #include "amduat/asl/ref_derive.h"
#include "amduat/asl/identity.h"
#include "amduat/enc/asl1_core.h" #include "amduat/enc/asl1_core.h"
#include "amduat/enc/asl1_core_codec.h" #include "amduat/enc/asl1_core_codec.h"
#include "amduat/hash/asl1.h" #include "amduat/hash/asl1.h"
@ -53,10 +54,11 @@ bool amduat_asl_ref_derive(amduat_artifact_t artifact,
return false; return false;
} }
if (!amduat_hash_asl1_digest(hash_id, if (!amduat_hash_asl1_digest_domain(hash_id,
artifact_bytes, amduat_asl_identity_domain_separator(),
digest, artifact_bytes,
hash_desc->digest_len)) { digest,
hash_desc->digest_len)) {
amduat_octets_free(&artifact_bytes); amduat_octets_free(&artifact_bytes);
free(digest); free(digest);
return false; return false;

235
src/near_core/bytestore.c Normal file
View file

@ -0,0 +1,235 @@
#include "amduat/bytestore.h"
#include <stdlib.h>
#include <string.h>
static const char k_amduat_bytestore_domain_sep[] = "CAS:OBJ\0";
static bool amduat_bytestore_trim_trailing_slash(const char *input,
size_t *out_len) {
size_t len;
if (input == NULL || out_len == NULL) {
return false;
}
len = strlen(input);
while (len > 1u && input[len - 1u] == '/') {
len--;
}
*out_len = len;
return true;
}
static bool amduat_bytestore_path_is_prefix(const char *parent,
size_t parent_len,
const char *child,
size_t child_len) {
if (parent_len == 0u || child_len == 0u) {
return false;
}
if (child_len < parent_len) {
return false;
}
if (memcmp(parent, child, parent_len) != 0) {
return false;
}
if (child_len == parent_len) {
return true;
}
return child[parent_len] == '/';
}
void amduat_bytestore_init(amduat_bytestore_t *store,
amduat_bytestore_config_t config,
amduat_bytestore_ops_t ops,
void *ctx) {
if (store == NULL) {
return;
}
store->config = config;
store->ops = ops;
store->ctx = ctx;
}
amduat_bytestore_error_t amduat_bytestore_put(amduat_bytestore_t *store,
amduat_octets_t payload,
amduat_reference_t *out_cid) {
if (store == NULL || store->ops.put == NULL) {
return AMDUAT_BYTESTORE_ERR_UNSUPPORTED;
}
return store->ops.put(store->ctx, payload, out_cid);
}
amduat_bytestore_error_t amduat_bytestore_put_stream(
amduat_bytestore_t *store,
amduat_bytestore_stream_t stream,
amduat_reference_t *out_cid) {
if (store == NULL || store->ops.put_stream == NULL) {
return AMDUAT_BYTESTORE_ERR_UNSUPPORTED;
}
return store->ops.put_stream(store->ctx, stream, out_cid);
}
amduat_bytestore_error_t amduat_bytestore_import_cor(
amduat_bytestore_t *store,
amduat_octets_t envelope,
amduat_reference_t *out_cid) {
if (store == NULL || store->ops.import_cor == NULL) {
return AMDUAT_BYTESTORE_ERR_UNSUPPORTED;
}
return store->ops.import_cor(store->ctx, envelope, out_cid);
}
amduat_bytestore_error_t amduat_bytestore_export_cor(
amduat_bytestore_t *store,
amduat_reference_t cid,
amduat_octets_t *out_envelope) {
if (store == NULL || store->ops.export_cor == NULL) {
return AMDUAT_BYTESTORE_ERR_UNSUPPORTED;
}
return store->ops.export_cor(store->ctx, cid, out_envelope);
}
amduat_bytestore_error_t amduat_bytestore_get(amduat_bytestore_t *store,
amduat_reference_t cid,
amduat_octets_t *out_bytes) {
if (store == NULL || store->ops.get == NULL) {
return AMDUAT_BYTESTORE_ERR_UNSUPPORTED;
}
return store->ops.get(store->ctx, cid, out_bytes);
}
amduat_bytestore_error_t amduat_bytestore_stat(
amduat_bytestore_t *store,
amduat_reference_t cid,
amduat_bytestore_stat_t *out_stat) {
if (store == NULL || store->ops.stat == NULL) {
return AMDUAT_BYTESTORE_ERR_UNSUPPORTED;
}
return store->ops.stat(store->ctx, cid, out_stat);
}
amduat_bytestore_error_t amduat_bytestore_assert_area_isolation(
amduat_bytestore_t *store,
const char *public_root,
const char *secure_root) {
size_t public_len;
size_t secure_len;
if (store != NULL && store->ops.assert_area_isolation != NULL) {
return store->ops.assert_area_isolation(store->ctx, public_root,
secure_root);
}
if (!amduat_bytestore_trim_trailing_slash(public_root, &public_len) ||
!amduat_bytestore_trim_trailing_slash(secure_root, &secure_len)) {
return AMDUAT_BYTESTORE_ERR_INVALID;
}
if (public_len == 0u || secure_len == 0u) {
return AMDUAT_BYTESTORE_ERR_INVALID;
}
if (public_len == secure_len &&
memcmp(public_root, secure_root, public_len) == 0) {
return AMDUAT_BYTESTORE_ERR_AREA_VIOLATION;
}
if (amduat_bytestore_path_is_prefix(public_root, public_len, secure_root,
secure_len)) {
return AMDUAT_BYTESTORE_ERR_AREA_VIOLATION;
}
if (amduat_bytestore_path_is_prefix(secure_root, secure_len, public_root,
public_len)) {
return AMDUAT_BYTESTORE_ERR_AREA_VIOLATION;
}
return AMDUAT_BYTESTORE_OK;
}
bool amduat_bytestore_cid_for_payload(amduat_hash_id_t hash_id,
amduat_octets_t payload,
amduat_reference_t *out_cid) {
const amduat_hash_asl1_desc_t *desc;
uint8_t *digest;
amduat_octets_t domain_sep;
if (out_cid == NULL) {
return false;
}
*out_cid = amduat_reference(0, amduat_octets(NULL, 0u));
desc = amduat_hash_asl1_desc_lookup(hash_id);
if (desc == NULL || desc->digest_len == 0u) {
return false;
}
digest = (uint8_t *)malloc(desc->digest_len);
if (digest == NULL) {
return false;
}
domain_sep = amduat_octets(k_amduat_bytestore_domain_sep,
sizeof(k_amduat_bytestore_domain_sep) - 1u);
if (!amduat_hash_asl1_digest_domain(hash_id, domain_sep, payload, digest,
desc->digest_len)) {
free(digest);
return false;
}
*out_cid = amduat_reference(hash_id, amduat_octets(digest, desc->digest_len));
return true;
}
bool amduat_bytestore_cid_for_stream(amduat_hash_id_t hash_id,
amduat_bytestore_stream_t stream,
amduat_reference_t *out_cid) {
amduat_hash_asl1_stream_t hasher;
const amduat_hash_asl1_desc_t *desc;
uint8_t *digest;
amduat_octets_t domain_sep;
amduat_octets_t chunk;
int next_status;
if (out_cid == NULL || stream.next == NULL) {
return false;
}
*out_cid = amduat_reference(0, amduat_octets(NULL, 0u));
desc = amduat_hash_asl1_desc_lookup(hash_id);
if (desc == NULL || desc->digest_len == 0u) {
return false;
}
if (!amduat_hash_asl1_stream_init(hash_id, &hasher)) {
return false;
}
domain_sep = amduat_octets(k_amduat_bytestore_domain_sep,
sizeof(k_amduat_bytestore_domain_sep) - 1u);
if (!amduat_hash_asl1_stream_update(&hasher, domain_sep)) {
amduat_hash_asl1_stream_destroy(&hasher);
return false;
}
while (true) {
chunk = amduat_octets(NULL, 0u);
next_status = stream.next(stream.ctx, &chunk);
if (next_status == 0) {
break;
}
if (next_status < 0) {
amduat_hash_asl1_stream_destroy(&hasher);
return false;
}
if (chunk.len != 0u && chunk.data == NULL) {
amduat_hash_asl1_stream_destroy(&hasher);
return false;
}
if (chunk.len != 0u &&
!amduat_hash_asl1_stream_update(&hasher, chunk)) {
amduat_hash_asl1_stream_destroy(&hasher);
return false;
}
}
digest = (uint8_t *)malloc(desc->digest_len);
if (digest == NULL) {
amduat_hash_asl1_stream_destroy(&hasher);
return false;
}
if (!amduat_hash_asl1_stream_final(&hasher, digest, desc->digest_len)) {
free(digest);
amduat_hash_asl1_stream_destroy(&hasher);
return false;
}
amduat_hash_asl1_stream_destroy(&hasher);
*out_cid = amduat_reference(hash_id, amduat_octets(digest, desc->digest_len));
return true;
}

View file

@ -1,7 +1,7 @@
#include "amduat/enc/asl1_core.h" #include "amduat/enc/asl1_core.h"
static amduat_enc_asl1_core_profile_desc_t g_enc_asl1_core_profiles[] = { static amduat_enc_asl1_core_profile_desc_t g_enc_asl1_core_profiles[] = {
{AMDUAT_ENC_ASL1_CORE_V1, "ASL_ENC_CORE_V1", 0x0105, NULL}}; {AMDUAT_ENC_ASL1_CORE_V1, "ASL_ENC_CORE_V1", 0x0106, NULL}};
amduat_octets_t amduat_enc_asl1_core_key( amduat_octets_t amduat_enc_asl1_core_key(
amduat_asl_encoding_profile_id_t profile_id, amduat_asl_encoding_profile_id_t profile_id,

View file

@ -166,6 +166,9 @@ static bool amduat_asl_tgk_exec_plan_validate_operator(
if (((uint32_t)op->flags & ~AMDUAT_ASL_TGK_EXEC_PLAN_OP_FLAG_MASK) != 0u) { if (((uint32_t)op->flags & ~AMDUAT_ASL_TGK_EXEC_PLAN_OP_FLAG_MASK) != 0u) {
return false; return false;
} }
if (op->snapshot.logseq_min > op->snapshot.logseq_max) {
return false;
}
if (op->input_count > AMDUAT_ASL_TGK_EXEC_PLAN_INPUT_CAP) { if (op->input_count > AMDUAT_ASL_TGK_EXEC_PLAN_INPUT_CAP) {
return false; return false;
} }

294
src/near_core/enc/fcs1.c Normal file
View file

@ -0,0 +1,294 @@
#include "amduat/enc/fcs1.h"
#include "amduat/enc/asl1_core_codec.h"
#include <string.h>
enum {
AMDUAT_FCS1_HEADER_LEN = 7,
AMDUAT_FCS1_TAG_FUNCTION = 0x30,
AMDUAT_FCS1_TAG_PARAMS = 0x31,
AMDUAT_FCS1_TAG_ARITY = 0x32,
AMDUAT_FCS1_TAG_LEGACY_MIN = 0x33,
AMDUAT_FCS1_TAG_LEGACY_MAX = 0x36
};
typedef struct {
const uint8_t *data;
size_t len;
size_t offset;
} amduat_fcs1_cursor_t;
static bool amduat_fcs1_read_varint(amduat_fcs1_cursor_t *cur,
uint64_t *out,
bool *out_minimal) {
uint64_t value = 0u;
uint32_t shift = 0u;
size_t bytes = 0u;
if (cur == NULL || out == NULL || out_minimal == NULL) {
return false;
}
while (cur->offset < cur->len) {
uint8_t byte = cur->data[cur->offset++];
value |= ((uint64_t)(byte & 0x7fu)) << shift;
bytes++;
if ((byte & 0x80u) == 0u) {
break;
}
shift += 7u;
if (shift >= 64u) {
return false;
}
}
if (bytes == 0u || cur->offset > cur->len) {
return false;
}
{
size_t minimal_len = 1u;
uint64_t tmp = value;
while (tmp >= 0x80u) {
tmp >>= 7u;
minimal_len++;
}
*out_minimal = (minimal_len == bytes);
}
*out = value;
return true;
}
static bool amduat_fcs1_read_bytes(amduat_fcs1_cursor_t *cur,
amduat_octets_t *out,
bool *out_minimal) {
uint64_t len;
bool minimal;
if (!amduat_fcs1_read_varint(cur, &len, &minimal)) {
return false;
}
if (len > SIZE_MAX || cur->len - cur->offset < (size_t)len) {
return false;
}
*out = amduat_octets(cur->data + cur->offset, (size_t)len);
cur->offset += (size_t)len;
*out_minimal = minimal;
return true;
}
static bool amduat_fcs1_has_header(amduat_octets_t bytes) {
static const uint8_t magic[4] = {'F', 'C', 'S', '1'};
if (bytes.len < AMDUAT_FCS1_HEADER_LEN || bytes.data == NULL) {
return false;
}
if (memcmp(bytes.data, magic, sizeof(magic)) != 0) {
return false;
}
if (bytes.data[4] != 0x01u || bytes.data[5] != 0x00u ||
bytes.data[6] != 0x00u) {
return false;
}
return true;
}
void amduat_fcs1_descriptor_free(amduat_fcs1_descriptor_t *desc) {
if (desc == NULL) {
return;
}
amduat_reference_free(&desc->function_ptr);
amduat_reference_free(&desc->parameter_block);
desc->arity = 0u;
}
amduat_fcs1_error_t amduat_fcs1_decode_v1(amduat_octets_t bytes,
amduat_fcs1_descriptor_t *out_desc) {
amduat_fcs1_cursor_t cur;
uint8_t last_tag = 0u;
bool seen_function = false;
bool seen_params = false;
bool seen_arity = false;
if (out_desc == NULL) {
return AMDUAT_FCS1_ERR_INVALID;
}
out_desc->function_ptr = amduat_reference(0, amduat_octets(NULL, 0u));
out_desc->parameter_block = amduat_reference(0, amduat_octets(NULL, 0u));
out_desc->arity = 0u;
if (!amduat_fcs1_has_header(bytes)) {
return AMDUAT_FCS1_ERR_HEADER_INVALID;
}
cur.data = bytes.data;
cur.len = bytes.len;
cur.offset = AMDUAT_FCS1_HEADER_LEN;
while (cur.offset < cur.len) {
uint8_t tag = cur.data[cur.offset++];
bool minimal = true;
if (tag <= last_tag) {
return AMDUAT_FCS1_ERR_TAG_ORDER;
}
last_tag = tag;
if (tag == AMDUAT_FCS1_TAG_FUNCTION ||
tag == AMDUAT_FCS1_TAG_PARAMS) {
amduat_octets_t cid_bytes;
if (!amduat_fcs1_read_bytes(&cur, &cid_bytes, &minimal)) {
return AMDUAT_FCS1_ERR_INVALID;
}
if (!minimal) {
return AMDUAT_FCS1_ERR_VARINT_NON_MINIMAL;
}
if (tag == AMDUAT_FCS1_TAG_FUNCTION) {
if (seen_function) {
return AMDUAT_FCS1_ERR_TAG_ORDER;
}
if (!amduat_enc_asl1_core_decode_reference_v1(cid_bytes,
&out_desc->function_ptr)) {
return AMDUAT_FCS1_ERR_INVALID;
}
seen_function = true;
} else {
if (seen_params) {
return AMDUAT_FCS1_ERR_TAG_ORDER;
}
if (!amduat_enc_asl1_core_decode_reference_v1(cid_bytes,
&out_desc->parameter_block)) {
return AMDUAT_FCS1_ERR_INVALID;
}
seen_params = true;
}
} else if (tag == AMDUAT_FCS1_TAG_ARITY) {
uint64_t arity_raw = 0u;
if (seen_arity) {
return AMDUAT_FCS1_ERR_TAG_ORDER;
}
if (!amduat_fcs1_read_varint(&cur, &arity_raw, &minimal)) {
return AMDUAT_FCS1_ERR_INVALID;
}
if (!minimal) {
return AMDUAT_FCS1_ERR_VARINT_NON_MINIMAL;
}
if (arity_raw > UINT32_MAX) {
return AMDUAT_FCS1_ERR_INVALID;
}
out_desc->arity = (uint32_t)arity_raw;
seen_arity = true;
} else if (tag >= AMDUAT_FCS1_TAG_LEGACY_MIN &&
tag <= AMDUAT_FCS1_TAG_LEGACY_MAX) {
return AMDUAT_FCS1_ERR_UNKNOWN_TAG;
} else {
return AMDUAT_FCS1_ERR_UNKNOWN_TAG;
}
}
if (!seen_function || !seen_params || !seen_arity) {
return AMDUAT_FCS1_ERR_TAG_ORDER;
}
if (cur.offset != cur.len) {
return AMDUAT_FCS1_ERR_TRAILING_BYTES;
}
return AMDUAT_FCS1_OK;
}
static amduat_fcs1_error_t amduat_fcs1_validate_cycle(
amduat_reference_t ref,
amduat_fcs1_resolve_fn resolver,
void *resolver_ctx,
amduat_reference_t *stack,
size_t stack_len,
size_t stack_cap) {
amduat_octets_t bytes = amduat_octets(NULL, 0u);
amduat_fcs1_descriptor_t desc;
amduat_fcs1_error_t err;
if (resolver == NULL) {
return AMDUAT_FCS1_ERR_INVALID;
}
if (!resolver(resolver_ctx, ref, &bytes)) {
return AMDUAT_FCS1_OK;
}
if (!amduat_fcs1_has_header(bytes)) {
amduat_octets_free(&bytes);
return AMDUAT_FCS1_OK;
}
for (size_t i = 0u; i < stack_len; ++i) {
if (amduat_reference_eq(stack[i], ref)) {
amduat_octets_free(&bytes);
return AMDUAT_FCS1_ERR_CYCLE_DETECTED;
}
}
memset(&desc, 0, sizeof(desc));
err = amduat_fcs1_decode_v1(bytes, &desc);
amduat_octets_free(&bytes);
if (err != AMDUAT_FCS1_OK) {
amduat_fcs1_descriptor_free(&desc);
return err;
}
if (stack_len >= stack_cap) {
amduat_fcs1_descriptor_free(&desc);
return AMDUAT_FCS1_ERR_INVALID;
}
stack[stack_len] = desc.function_ptr;
err = amduat_fcs1_validate_cycle(desc.function_ptr,
resolver,
resolver_ctx,
stack,
stack_len + 1u,
stack_cap);
amduat_fcs1_descriptor_free(&desc);
return err;
}
amduat_fcs1_error_t amduat_fcs1_validate_v1(
amduat_octets_t bytes,
amduat_fcs1_resolve_fn resolver,
void *resolver_ctx) {
amduat_fcs1_descriptor_t desc;
amduat_octets_t pcb_bytes = amduat_octets(NULL, 0u);
amduat_pcb1_manifest_t manifest;
amduat_octets_t slot_data;
amduat_fcs1_error_t err;
amduat_pcb1_error_t pcb_err;
amduat_reference_t stack[16];
memset(&desc, 0, sizeof(desc));
err = amduat_fcs1_decode_v1(bytes, &desc);
if (err != AMDUAT_FCS1_OK) {
return err;
}
if (resolver == NULL ||
!resolver(resolver_ctx, desc.parameter_block, &pcb_bytes)) {
amduat_fcs1_descriptor_free(&desc);
return AMDUAT_FCS1_ERR_PARAMETER_FORMAT;
}
pcb_err = amduat_pcb1_decode_v1(pcb_bytes, &manifest, &slot_data);
amduat_octets_free(&pcb_bytes);
if (pcb_err != AMDUAT_PCB1_OK) {
amduat_fcs1_descriptor_free(&desc);
return AMDUAT_FCS1_ERR_PARAMETER_FORMAT;
}
(void)slot_data;
if (desc.arity != manifest.slot_count) {
amduat_fcs1_descriptor_free(&desc);
return AMDUAT_FCS1_ERR_PCB_ARITY_MISMATCH;
}
stack[0] = desc.function_ptr;
err = amduat_fcs1_validate_cycle(desc.function_ptr,
resolver,
resolver_ctx,
stack,
1u,
sizeof(stack) / sizeof(stack[0]));
amduat_fcs1_descriptor_free(&desc);
return err;
}

256
src/near_core/enc/pcb1.c Normal file
View file

@ -0,0 +1,256 @@
#include "amduat/enc/pcb1.h"
#include <string.h>
enum {
AMDUAT_PCB1_HEADER_LEN = 7,
AMDUAT_PCB1_TAG_MANIFEST = 0x50,
AMDUAT_PCB1_TAG_DATA = 0x51,
AMDUAT_PCB1_DIGEST_LEN = 32
};
typedef struct {
const uint8_t *data;
size_t len;
size_t offset;
} amduat_pcb1_cursor_t;
static bool amduat_pcb1_read_varint(amduat_pcb1_cursor_t *cur,
uint64_t *out,
bool *out_minimal) {
uint64_t value = 0u;
uint32_t shift = 0u;
size_t start;
size_t bytes = 0u;
if (cur == NULL || out == NULL || out_minimal == NULL) {
return false;
}
start = cur->offset;
while (cur->offset < cur->len) {
uint8_t byte = cur->data[cur->offset++];
value |= ((uint64_t)(byte & 0x7fu)) << shift;
bytes++;
if ((byte & 0x80u) == 0u) {
break;
}
shift += 7u;
if (shift >= 64u) {
return false;
}
}
if (bytes == 0u || cur->offset > cur->len) {
return false;
}
{
size_t minimal_len = 1u;
uint64_t tmp = value;
while (tmp >= 0x80u) {
tmp >>= 7u;
minimal_len++;
}
*out_minimal = (minimal_len == bytes);
}
*out = value;
return true;
}
static bool amduat_pcb1_read_bytes(amduat_pcb1_cursor_t *cur,
amduat_octets_t *out,
bool *out_minimal) {
uint64_t len;
bool minimal;
if (!amduat_pcb1_read_varint(cur, &len, &minimal)) {
return false;
}
if (len > SIZE_MAX || cur->len - cur->offset < (size_t)len) {
return false;
}
*out = amduat_octets(cur->data + cur->offset, (size_t)len);
cur->offset += (size_t)len;
*out_minimal = minimal;
return true;
}
static amduat_pcb1_error_t amduat_pcb1_parse_manifest(
amduat_octets_t bytes,
amduat_pcb1_manifest_t *out_manifest) {
amduat_pcb1_cursor_t cur;
uint64_t slot_count = 0u;
uint64_t last_index = 0u;
bool minimal;
if (out_manifest == NULL) {
return AMDUAT_PCB1_ERR_INVALID;
}
out_manifest->slot_count = 0u;
cur.data = bytes.data;
cur.len = bytes.len;
cur.offset = 0u;
if (!amduat_pcb1_read_varint(&cur, &slot_count, &minimal)) {
return AMDUAT_PCB1_ERR_INVALID;
}
if (!minimal) {
return AMDUAT_PCB1_ERR_VARINT_NON_MINIMAL;
}
if (slot_count > UINT32_MAX) {
return AMDUAT_PCB1_ERR_INVALID;
}
for (uint64_t i = 0u; i < slot_count; ++i) {
uint64_t index;
uint64_t name_len;
uint64_t type;
uint64_t digest_len;
amduat_octets_t name;
amduat_octets_t digest;
if (!amduat_pcb1_read_varint(&cur, &index, &minimal)) {
return AMDUAT_PCB1_ERR_INVALID;
}
if (!minimal) {
return AMDUAT_PCB1_ERR_VARINT_NON_MINIMAL;
}
if (i > 0u && index <= last_index) {
return AMDUAT_PCB1_ERR_MANIFEST_ORDER;
}
last_index = index;
if (!amduat_pcb1_read_varint(&cur, &name_len, &minimal)) {
return AMDUAT_PCB1_ERR_INVALID;
}
if (!minimal) {
return AMDUAT_PCB1_ERR_VARINT_NON_MINIMAL;
}
if (name_len > SIZE_MAX || cur.len - cur.offset < (size_t)name_len) {
return AMDUAT_PCB1_ERR_INVALID;
}
name = amduat_octets(cur.data + cur.offset, (size_t)name_len);
(void)name;
cur.offset += (size_t)name_len;
if (!amduat_pcb1_read_varint(&cur, &type, &minimal)) {
return AMDUAT_PCB1_ERR_INVALID;
}
if (!minimal) {
return AMDUAT_PCB1_ERR_VARINT_NON_MINIMAL;
}
(void)type;
if (!amduat_pcb1_read_varint(&cur, &digest_len, &minimal)) {
return AMDUAT_PCB1_ERR_INVALID;
}
if (!minimal) {
return AMDUAT_PCB1_ERR_VARINT_NON_MINIMAL;
}
if (digest_len != AMDUAT_PCB1_DIGEST_LEN) {
return AMDUAT_PCB1_ERR_DIGEST_MISMATCH;
}
if (cur.len - cur.offset < AMDUAT_PCB1_DIGEST_LEN) {
return AMDUAT_PCB1_ERR_INVALID;
}
digest = amduat_octets(cur.data + cur.offset, AMDUAT_PCB1_DIGEST_LEN);
(void)digest;
cur.offset += AMDUAT_PCB1_DIGEST_LEN;
}
if (cur.offset != cur.len) {
return AMDUAT_PCB1_ERR_TRAILING_BYTES;
}
out_manifest->slot_count = (uint32_t)slot_count;
return AMDUAT_PCB1_OK;
}
amduat_pcb1_error_t amduat_pcb1_decode_v1(amduat_octets_t bytes,
amduat_pcb1_manifest_t *out_manifest,
amduat_octets_t *out_slot_data) {
static const uint8_t magic[4] = {'P', 'C', 'B', '1'};
amduat_pcb1_cursor_t cur;
uint8_t version;
uint8_t flags;
uint8_t rsv;
uint8_t tag;
uint8_t last_tag = 0u;
bool minimal;
bool seen_manifest = false;
bool seen_data = false;
amduat_octets_t manifest_bytes = amduat_octets(NULL, 0u);
amduat_octets_t slot_data = amduat_octets(NULL, 0u);
if (out_manifest == NULL || out_slot_data == NULL) {
return AMDUAT_PCB1_ERR_INVALID;
}
out_manifest->slot_count = 0u;
*out_slot_data = amduat_octets(NULL, 0u);
if (bytes.len < AMDUAT_PCB1_HEADER_LEN || bytes.data == NULL) {
return AMDUAT_PCB1_ERR_HEADER_INVALID;
}
if (memcmp(bytes.data, magic, sizeof(magic)) != 0) {
return AMDUAT_PCB1_ERR_HEADER_INVALID;
}
version = bytes.data[4];
flags = bytes.data[5];
rsv = bytes.data[6];
if (version != 0x01u || flags != 0x00u || rsv != 0x00u) {
return AMDUAT_PCB1_ERR_HEADER_INVALID;
}
cur.data = bytes.data;
cur.len = bytes.len;
cur.offset = AMDUAT_PCB1_HEADER_LEN;
while (cur.offset < cur.len) {
tag = cur.data[cur.offset++];
if (tag <= last_tag) {
return AMDUAT_PCB1_ERR_TAG_ORDER;
}
last_tag = tag;
if (tag == AMDUAT_PCB1_TAG_MANIFEST) {
if (seen_manifest) {
return AMDUAT_PCB1_ERR_TAG_ORDER;
}
if (!amduat_pcb1_read_bytes(&cur, &manifest_bytes, &minimal)) {
return AMDUAT_PCB1_ERR_INVALID;
}
if (!minimal) {
return AMDUAT_PCB1_ERR_VARINT_NON_MINIMAL;
}
seen_manifest = true;
} else if (tag == AMDUAT_PCB1_TAG_DATA) {
if (seen_data) {
return AMDUAT_PCB1_ERR_TAG_ORDER;
}
if (!amduat_pcb1_read_bytes(&cur, &slot_data, &minimal)) {
return AMDUAT_PCB1_ERR_INVALID;
}
if (!minimal) {
return AMDUAT_PCB1_ERR_VARINT_NON_MINIMAL;
}
seen_data = true;
} else {
return AMDUAT_PCB1_ERR_UNKNOWN_TAG;
}
}
if (!seen_manifest || !seen_data) {
return AMDUAT_PCB1_ERR_TAG_ORDER;
}
if (cur.offset != cur.len) {
return AMDUAT_PCB1_ERR_TRAILING_BYTES;
}
{
amduat_pcb1_error_t err =
amduat_pcb1_parse_manifest(manifest_bytes, out_manifest);
if (err != AMDUAT_PCB1_OK) {
return err;
}
}
*out_slot_data = slot_data;
return AMDUAT_PCB1_OK;
}

202
src/near_core/fps.c Normal file
View file

@ -0,0 +1,202 @@
#include "amduat/fps.h"
#include <stdlib.h>
#include <string.h>
static const char k_amduat_fps_domain_sep[] = "CAS:OBJ\0";
static uint8_t amduat_fps_reverse_bits8(uint8_t value) {
value = (uint8_t)(((value & 0xf0u) >> 4) | ((value & 0x0fu) << 4));
value = (uint8_t)(((value & 0xccu) >> 2) | ((value & 0x33u) << 2));
value = (uint8_t)(((value & 0xaau) >> 1) | ((value & 0x55u) << 1));
return value;
}
static bool amduat_fps_alloc_output(size_t len, amduat_octets_t *out) {
uint8_t *buf;
if (out == NULL) {
return false;
}
*out = amduat_octets(NULL, 0u);
if (len == 0u) {
return true;
}
buf = (uint8_t *)malloc(len);
if (buf == NULL) {
return false;
}
*out = amduat_octets(buf, len);
return true;
}
amduat_fps_error_t amduat_fps_slice(amduat_octets_t input,
size_t offset,
size_t length,
amduat_octets_t *out) {
if (out == NULL || (input.len != 0u && input.data == NULL)) {
return AMDUAT_FPS_ERR_INVALID;
}
if (offset > input.len || length > input.len - offset) {
return AMDUAT_FPS_ERR_SLICE_RANGE;
}
if (!amduat_fps_alloc_output(length, out)) {
return AMDUAT_FPS_ERR_IO_FAILURE;
}
if (length != 0u) {
memcpy((void *)out->data, input.data + offset, length);
}
return AMDUAT_FPS_OK;
}
amduat_fps_error_t amduat_fps_concat(const amduat_octets_t *inputs,
size_t input_count,
amduat_octets_t *out) {
size_t total = 0u;
size_t i;
uint8_t *cursor;
if (out == NULL) {
return AMDUAT_FPS_ERR_INVALID;
}
if (input_count == 0u) {
return AMDUAT_FPS_ERR_EMPTY_INPUTS;
}
if (inputs == NULL) {
return AMDUAT_FPS_ERR_INVALID;
}
for (i = 0u; i < input_count; ++i) {
if (inputs[i].len != 0u && inputs[i].data == NULL) {
return AMDUAT_FPS_ERR_INVALID;
}
if (SIZE_MAX - total < inputs[i].len) {
return AMDUAT_FPS_ERR_IO_FAILURE;
}
total += inputs[i].len;
}
if (!amduat_fps_alloc_output(total, out)) {
return AMDUAT_FPS_ERR_IO_FAILURE;
}
cursor = (uint8_t *)out->data;
for (i = 0u; i < input_count; ++i) {
if (inputs[i].len != 0u) {
memcpy(cursor, inputs[i].data, inputs[i].len);
cursor += inputs[i].len;
}
}
return AMDUAT_FPS_OK;
}
amduat_fps_error_t amduat_fps_reverse(amduat_octets_t input,
amduat_fps_reverse_level_t level,
amduat_octets_t *out) {
size_t unit_size;
size_t i;
size_t unit_count;
if (out == NULL || (input.len != 0u && input.data == NULL)) {
return AMDUAT_FPS_ERR_INVALID;
}
if (level == AMDUAT_FPS_REVERSE_BITS) {
if (!amduat_fps_alloc_output(input.len, out)) {
return AMDUAT_FPS_ERR_IO_FAILURE;
}
for (i = 0u; i < input.len; ++i) {
size_t dst = input.len - 1u - i;
((uint8_t *)out->data)[dst] = amduat_fps_reverse_bits8(input.data[i]);
}
return AMDUAT_FPS_OK;
}
if (level == AMDUAT_FPS_REVERSE_BYTES) {
unit_size = 1u;
} else if (level == AMDUAT_FPS_REVERSE_WORDS) {
unit_size = 2u;
} else if (level == AMDUAT_FPS_REVERSE_LONGS) {
unit_size = 4u;
} else {
return AMDUAT_FPS_ERR_INVALID_LEVEL;
}
if (unit_size != 0u && input.len % unit_size != 0u) {
return AMDUAT_FPS_ERR_REV_ALIGNMENT;
}
if (!amduat_fps_alloc_output(input.len, out)) {
return AMDUAT_FPS_ERR_IO_FAILURE;
}
unit_count = unit_size == 0u ? 0u : input.len / unit_size;
for (i = 0u; i < unit_count; ++i) {
size_t src_offset = i * unit_size;
size_t dst_offset = (unit_count - 1u - i) * unit_size;
memcpy((uint8_t *)out->data + dst_offset,
input.data + src_offset,
unit_size);
}
return AMDUAT_FPS_OK;
}
amduat_fps_error_t amduat_fps_splice(amduat_octets_t a,
size_t offset,
amduat_octets_t b,
amduat_octets_t *out) {
size_t total;
if (out == NULL ||
(a.len != 0u && a.data == NULL) ||
(b.len != 0u && b.data == NULL)) {
return AMDUAT_FPS_ERR_INVALID;
}
if (offset > a.len) {
return AMDUAT_FPS_ERR_SPLICE_RANGE;
}
if (SIZE_MAX - a.len < b.len) {
return AMDUAT_FPS_ERR_IO_FAILURE;
}
total = a.len + b.len;
if (!amduat_fps_alloc_output(total, out)) {
return AMDUAT_FPS_ERR_IO_FAILURE;
}
if (offset != 0u) {
memcpy((uint8_t *)out->data, a.data, offset);
}
if (b.len != 0u) {
memcpy((uint8_t *)out->data + offset, b.data, b.len);
}
if (a.len > offset) {
memcpy((uint8_t *)out->data + offset + b.len,
a.data + offset,
a.len - offset);
}
return AMDUAT_FPS_OK;
}
bool amduat_fps_cid_for_payload(amduat_hash_id_t hash_id,
amduat_octets_t payload,
amduat_reference_t *out_cid) {
const amduat_hash_asl1_desc_t *desc;
amduat_octets_t domain_sep;
uint8_t *digest;
if (out_cid == NULL) {
return false;
}
*out_cid = amduat_reference(0, amduat_octets(NULL, 0u));
desc = amduat_hash_asl1_desc_lookup(hash_id);
if (desc == NULL || desc->digest_len == 0u) {
return false;
}
digest = (uint8_t *)malloc(desc->digest_len);
if (digest == NULL) {
return false;
}
domain_sep = amduat_octets(k_amduat_fps_domain_sep,
sizeof(k_amduat_fps_domain_sep) - 1u);
if (!amduat_hash_asl1_digest_domain(hash_id, domain_sep, payload, digest,
desc->digest_len)) {
free(digest);
return false;
}
*out_cid = amduat_reference(hash_id, amduat_octets(digest, desc->digest_len));
return true;
}

View file

@ -125,6 +125,58 @@ bool amduat_hash_asl1_digest(amduat_hash_id_t hash_id,
return desc->impl.digest(desc->impl.ctx, input, out, desc->digest_len); return desc->impl.digest(desc->impl.ctx, input, out, desc->digest_len);
} }
bool amduat_hash_asl1_digest_domain(amduat_hash_id_t hash_id,
amduat_octets_t domain_separator,
amduat_octets_t input,
uint8_t *out,
size_t out_len) {
amduat_hash_asl1_stream_t stream;
uint8_t *buffer;
size_t total_len;
if (out == NULL) {
return false;
}
if ((domain_separator.len != 0 && domain_separator.data == NULL) ||
(input.len != 0 && input.data == NULL)) {
return false;
}
if (amduat_hash_asl1_stream_init(hash_id, &stream)) {
bool ok = amduat_hash_asl1_stream_update(&stream, domain_separator) &&
amduat_hash_asl1_stream_update(&stream, input) &&
amduat_hash_asl1_stream_final(&stream, out, out_len);
amduat_hash_asl1_stream_destroy(&stream);
return ok;
}
if (domain_separator.len > SIZE_MAX - input.len) {
return false;
}
total_len = domain_separator.len + input.len;
buffer = (uint8_t *)malloc(total_len);
if (buffer == NULL) {
return false;
}
if (domain_separator.len != 0) {
memcpy(buffer, domain_separator.data, domain_separator.len);
}
if (input.len != 0) {
memcpy(buffer + domain_separator.len, input.data, input.len);
}
if (!amduat_hash_asl1_digest(hash_id,
amduat_octets(buffer, total_len),
out,
out_len)) {
free(buffer);
return false;
}
free(buffer);
return true;
}
static bool amduat_hash_asl1_sha256_stream_update(void *ctx, static bool amduat_hash_asl1_sha256_stream_update(void *ctx,
amduat_octets_t input) { amduat_octets_t input) {
if (input.len != 0 && input.data == NULL) { if (input.len != 0 && input.data == NULL) {

View file

@ -17,6 +17,8 @@ static void usage(FILE *out) {
" --backend <name|all> Backend to run (repeatable)\n" " --backend <name|all> Backend to run (repeatable)\n"
" --workdir <path> Workdir for backend state\n" " --workdir <path> Workdir for backend state\n"
" --fixtures <path> Fixtures root (default: tests/fixtures/conformance)\n" " --fixtures <path> Fixtures root (default: tests/fixtures/conformance)\n"
" --json Emit machine-readable JSON summary\n"
" --json-out <path> Write JSON summary to a file\n"
" --help Show this help\n"); " --help Show this help\n");
} }
@ -85,6 +87,68 @@ static int print_results(const char *suite,
return rc; return rc;
} }
static void json_escape(FILE *out, const char *value) {
const unsigned char *p = (const unsigned char *)value;
fputc('"', out);
while (*p != '\0') {
switch (*p) {
case '\\':
case '"':
fputc('\\', out);
fputc(*p, out);
break;
case '\n':
fputs("\\n", out);
break;
case '\r':
fputs("\\r", out);
break;
case '\t':
fputs("\\t", out);
break;
default:
if (*p < 0x20) {
fprintf(out, "\\u%04x", (unsigned int)*p);
} else {
fputc(*p, out);
}
break;
}
p++;
}
fputc('"', out);
}
static const char *status_string(amduat_conformance_status_t status) {
switch (status) {
case AMDUAT_CONFORMANCE_STATUS_PASS:
return "PASS";
case AMDUAT_CONFORMANCE_STATUS_SKIP:
return "SKIPPED";
case AMDUAT_CONFORMANCE_STATUS_FAIL:
default:
return "FAIL";
}
}
static void json_write_results(FILE *out,
const amduat_conformance_backend_result_t *results,
size_t results_len) {
size_t i;
fputc('[', out);
for (i = 0; i < results_len; ++i) {
if (i != 0) {
fputc(',', out);
}
fputs("{\"backend\":", out);
json_escape(out, results[i].backend);
fputs(",\"status\":", out);
json_escape(out, status_string(results[i].status));
fputc('}', out);
}
fputc(']', out);
}
static const char *fallback_fixtures_root(const char *path) { static const char *fallback_fixtures_root(const char *path) {
struct stat st; struct stat st;
@ -106,10 +170,16 @@ int main(int argc, char **argv) {
const char *suite = "all"; const char *suite = "all";
const char *fixtures = "tests/fixtures/conformance"; const char *fixtures = "tests/fixtures/conformance";
const char *workdir = "/tmp/amduat_conformance"; const char *workdir = "/tmp/amduat_conformance";
const char *json_out_path = NULL;
char *backend_selection = NULL; char *backend_selection = NULL;
bool include_optional = env_truthy(getenv("AMDUAT_CONFORMANCE_INCLUDE_OPTIONAL")); bool include_optional = env_truthy(getenv("AMDUAT_CONFORMANCE_INCLUDE_OPTIONAL"));
bool json_out = false;
int i; int i;
int rc = 0; int rc = 0;
amduat_conformance_backend_result_t *asl_results = NULL;
size_t asl_results_len = 0u;
amduat_conformance_backend_result_t *tgk_results = NULL;
size_t tgk_results_len = 0u;
for (i = 1; i < argc; ++i) { for (i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--help") == 0) { if (strcmp(argv[i], "--help") == 0) {
@ -149,6 +219,15 @@ int main(int argc, char **argv) {
fixtures = argv[++i]; fixtures = argv[++i];
continue; continue;
} }
if (strcmp(argv[i], "--json") == 0) {
json_out = true;
continue;
}
if (strcmp(argv[i], "--json-out") == 0 && i + 1 < argc) {
json_out = true;
json_out_path = argv[++i];
continue;
}
fprintf(stderr, "unknown argument: %s\n", argv[i]); fprintf(stderr, "unknown argument: %s\n", argv[i]);
usage(stderr); usage(stderr);
@ -167,33 +246,31 @@ int main(int argc, char **argv) {
} }
if (strcmp(suite, "asl") == 0 || strcmp(suite, "all") == 0) { if (strcmp(suite, "asl") == 0 || strcmp(suite, "all") == 0) {
amduat_conformance_backend_result_t *results = NULL;
size_t results_len = 0u;
int suite_rc = amduat_asl_store_conformance_run( int suite_rc = amduat_asl_store_conformance_run(
backend_selection, fixtures, workdir, include_optional, true, backend_selection, fixtures, workdir, include_optional, true,
&results, &results_len); &asl_results, &asl_results_len);
if (suite_rc != 0) { if (suite_rc != 0) {
rc = 1; rc = 1;
} }
if (print_results("asl", results, results_len) != 0) { if (!json_out) {
rc = 1; if (print_results("asl", asl_results, asl_results_len) != 0) {
rc = 1;
}
} }
free(results);
} }
if (strcmp(suite, "tgk") == 0 || strcmp(suite, "all") == 0) { if (strcmp(suite, "tgk") == 0 || strcmp(suite, "all") == 0) {
amduat_conformance_backend_result_t *results = NULL;
size_t results_len = 0u;
int suite_rc = amduat_tgk_store_conformance_run( int suite_rc = amduat_tgk_store_conformance_run(
backend_selection, fixtures, workdir, include_optional, true, backend_selection, fixtures, workdir, include_optional, true,
&results, &results_len); &tgk_results, &tgk_results_len);
if (suite_rc != 0) { if (suite_rc != 0) {
rc = 1; rc = 1;
} }
if (print_results("tgk", results, results_len) != 0) { if (!json_out) {
rc = 1; if (print_results("tgk", tgk_results, tgk_results_len) != 0) {
rc = 1;
}
} }
free(results);
} }
if (strcmp(suite, "asl") != 0 && strcmp(suite, "tgk") != 0 && if (strcmp(suite, "asl") != 0 && strcmp(suite, "tgk") != 0 &&
@ -203,6 +280,40 @@ int main(int argc, char **argv) {
rc = 1; rc = 1;
} }
if (json_out) {
FILE *out = stdout;
if (json_out_path != NULL) {
out = fopen(json_out_path, "w");
if (out == NULL) {
fprintf(stderr, "failed to open json output: %s\n", json_out_path);
rc = 1;
}
}
if (out != NULL) {
fputs("{\"suite\":", out);
json_escape(out, suite);
fputs(",\"fixtures\":", out);
json_escape(out, fixtures);
fputs(",\"workdir\":", out);
json_escape(out, workdir);
fputs(",\"include_optional\":", out);
fputs(include_optional ? "true" : "false", out);
fputs(",\"results\":{", out);
fputs("\"asl\":", out);
json_write_results(out, asl_results, asl_results_len);
fputs(",\"tgk\":", out);
json_write_results(out, tgk_results, tgk_results_len);
fputs("},\"status\":", out);
json_escape(out, rc == 0 ? "PASS" : "FAIL");
fputs("}\n", out);
if (out != stdout) {
fclose(out);
}
}
}
free(asl_results);
free(tgk_results);
free(backend_selection); free(backend_selection);
return rc; return rc;
} }

View file

@ -0,0 +1,139 @@
#include "amduat/asl/store.h"
#include <stdio.h>
#include <string.h>
static amduat_asl_store_error_t stub_put(void *ctx,
amduat_artifact_t artifact,
amduat_reference_t *out_ref) {
amduat_hash_id_t *hash_id = (amduat_hash_id_t *)ctx;
if (out_ref != NULL) {
uint8_t digest = 0xaa;
*out_ref = amduat_reference(*hash_id,
amduat_octets(&digest, sizeof(digest)));
}
(void)artifact;
return AMDUAT_ASL_STORE_OK;
}
static amduat_asl_store_error_t stub_get_not_found(void *ctx,
amduat_reference_t ref,
amduat_artifact_t *out_artifact) {
(void)ctx;
(void)ref;
(void)out_artifact;
return AMDUAT_ASL_STORE_ERR_NOT_FOUND;
}
static amduat_asl_store_error_t stub_get_io(void *ctx,
amduat_reference_t ref,
amduat_artifact_t *out_artifact) {
(void)ctx;
(void)ref;
(void)out_artifact;
return AMDUAT_ASL_STORE_ERR_IO;
}
static int test_not_found_mapping(void) {
amduat_asl_store_ops_t ops;
amduat_asl_store_t store;
amduat_asl_store_config_t cfg = {0};
amduat_reference_t ref;
amduat_artifact_t artifact;
amduat_asl_store_error_t err;
amduat_asl_store_ops_init(&ops);
ops.get = stub_get_not_found;
cfg.hash_id = 0x0001u;
amduat_asl_store_init(&store, cfg, ops, NULL);
ref = amduat_reference(cfg.hash_id, amduat_octets(NULL, 0u));
memset(&artifact, 0, sizeof(artifact));
err = amduat_asl_store_get(&store, ref, &artifact);
return err == AMDUAT_ASL_STORE_ERR_NOT_FOUND ? 0 : 1;
}
static int test_io_mapping(void) {
amduat_asl_store_ops_t ops;
amduat_asl_store_t store;
amduat_asl_store_config_t cfg = {0};
amduat_reference_t ref;
amduat_artifact_t artifact;
amduat_asl_store_error_t err;
amduat_asl_store_ops_init(&ops);
ops.get = stub_get_io;
cfg.hash_id = 0x0001u;
amduat_asl_store_init(&store, cfg, ops, NULL);
ref = amduat_reference(cfg.hash_id, amduat_octets(NULL, 0u));
memset(&artifact, 0, sizeof(artifact));
err = amduat_asl_store_get(&store, ref, &artifact);
return err == AMDUAT_ASL_STORE_ERR_IO ? 0 : 1;
}
static int test_integrity_mapping(void) {
amduat_asl_store_ops_t ops;
amduat_asl_store_t store;
amduat_asl_store_config_t cfg = {0};
amduat_artifact_t artifact;
amduat_reference_t ref;
amduat_hash_id_t wrong_hash = 0x00ffu;
amduat_asl_store_error_t err;
amduat_asl_store_ops_init(&ops);
ops.put = stub_put;
cfg.hash_id = 0x0001u;
amduat_asl_store_init(&store, cfg, ops, &wrong_hash);
memset(&artifact, 0, sizeof(artifact));
err = amduat_asl_store_put(&store, artifact, &ref);
if (err != AMDUAT_ASL_STORE_ERR_INTEGRITY) {
return 1;
}
amduat_reference_free(&ref);
return 0;
}
static int test_unsupported_mapping(void) {
amduat_asl_store_ops_t ops;
amduat_asl_store_t store;
amduat_asl_store_config_t cfg = {0};
amduat_reference_t ref;
amduat_artifact_t artifact;
amduat_asl_store_error_t err;
amduat_asl_store_ops_init(&ops);
cfg.hash_id = 0x0001u;
amduat_asl_store_init(&store, cfg, ops, NULL);
ref = amduat_reference(cfg.hash_id, amduat_octets(NULL, 0u));
memset(&artifact, 0, sizeof(artifact));
err = amduat_asl_store_get(&store, ref, &artifact);
if (err != AMDUAT_ASL_STORE_ERR_UNSUPPORTED) {
return 1;
}
err = amduat_asl_store_put(&store, artifact, &ref);
return err == AMDUAT_ASL_STORE_ERR_UNSUPPORTED ? 0 : 1;
}
int main(void) {
if (test_not_found_mapping() != 0) {
fprintf(stderr, "not_found mapping failed\n");
return 1;
}
if (test_io_mapping() != 0) {
fprintf(stderr, "io mapping failed\n");
return 1;
}
if (test_integrity_mapping() != 0) {
fprintf(stderr, "integrity mapping failed\n");
return 1;
}
if (test_unsupported_mapping() != 0) {
fprintf(stderr, "unsupported mapping failed\n");
return 1;
}
return 0;
}

View file

@ -0,0 +1,86 @@
#include "amduat/enc/asl_log.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
static void fill_hash(uint8_t *out, uint8_t seed) {
for (size_t i = 0; i < 32u; ++i) {
out[i] = (uint8_t)(seed + i);
}
}
static bool records_equal(const amduat_asl_log_record_t *lhs,
const amduat_asl_log_record_t *rhs) {
if (lhs->logseq != rhs->logseq || lhs->record_type != rhs->record_type ||
lhs->payload.len != rhs->payload.len) {
return false;
}
if (lhs->payload.len != 0 &&
memcmp(lhs->payload.data, rhs->payload.data, lhs->payload.len) != 0) {
return false;
}
if (memcmp(lhs->record_hash, rhs->record_hash, sizeof(lhs->record_hash)) != 0) {
return false;
}
return true;
}
int main(void) {
uint8_t payload_a[4] = {0x01, 0x02, 0x03, 0x04};
uint8_t payload_b[8] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
uint8_t payload_c[2] = {0xaa, 0xbb};
amduat_asl_log_record_t records[3];
amduat_asl_log_record_t *decoded = NULL;
size_t decoded_len = 0u;
amduat_octets_t bytes;
memset(records, 0, sizeof(records));
records[0].logseq = 1u;
records[0].record_type = AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL;
records[0].payload = amduat_octets(payload_a, sizeof(payload_a));
fill_hash(records[0].record_hash, 0x10);
records[1].logseq = 2u;
records[1].record_type = AMDUAT_ASL_LOG_RECORD_TOMBSTONE;
records[1].payload = amduat_octets(payload_b, sizeof(payload_b));
fill_hash(records[1].record_hash, 0x20);
records[2].logseq = 3u;
records[2].record_type = AMDUAT_ASL_LOG_RECORD_SNAPSHOT_ANCHOR;
records[2].payload = amduat_octets(payload_c, sizeof(payload_c));
fill_hash(records[2].record_hash, 0x30);
if (!amduat_enc_asl_log_encode_v1(records, 3u, &bytes)) {
fprintf(stderr, "encode failed\n");
return 1;
}
if (!amduat_enc_asl_log_decode_v1(bytes, &decoded, &decoded_len)) {
fprintf(stderr, "decode failed\n");
amduat_octets_free(&bytes);
return 1;
}
if (decoded_len != 3u) {
fprintf(stderr, "decoded length mismatch\n");
amduat_octets_free(&bytes);
amduat_enc_asl_log_free(decoded, decoded_len);
return 1;
}
for (size_t i = 0; i < decoded_len; ++i) {
if (!records_equal(&records[i], &decoded[i])) {
fprintf(stderr, "record mismatch at %zu\n", i);
amduat_octets_free(&bytes);
amduat_enc_asl_log_free(decoded, decoded_len);
return 1;
}
}
amduat_octets_free(&bytes);
amduat_enc_asl_log_free(decoded, decoded_len);
return 0;
}

View file

@ -1,3 +1,4 @@
#include "amduat/asl/identity.h"
#include "amduat/asl/ref_derive.h" #include "amduat/asl/ref_derive.h"
#include "amduat/enc/asl1_core.h" #include "amduat/enc/asl1_core.h"
#include "amduat/enc/asl1_core_codec.h" #include "amduat/enc/asl1_core_codec.h"
@ -39,10 +40,11 @@ static int test_ref_derive_roundtrip(void) {
fprintf(stderr, "digest buffer too small\n"); fprintf(stderr, "digest buffer too small\n");
goto cleanup; goto cleanup;
} }
if (!amduat_hash_asl1_digest(ref.hash_id, if (!amduat_hash_asl1_digest_domain(ref.hash_id,
artifact_bytes, amduat_asl_identity_domain_separator(),
digest, artifact_bytes,
hash_desc->digest_len)) { digest,
hash_desc->digest_len)) {
fprintf(stderr, "hash digest failed\n"); fprintf(stderr, "hash digest failed\n");
goto cleanup; goto cleanup;
} }

View file

@ -13,6 +13,77 @@ enum {
k_input_offset = 97 k_input_offset = 97
}; };
static bool read_file(const char *path, uint8_t **out_bytes, size_t *out_len) {
FILE *fp;
long size;
uint8_t *buffer;
size_t read_len;
if (path == NULL || out_bytes == NULL || out_len == NULL) {
return false;
}
*out_bytes = NULL;
*out_len = 0u;
fp = fopen(path, "rb");
if (fp == NULL) {
return false;
}
if (fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
return false;
}
size = ftell(fp);
if (size < 0) {
fclose(fp);
return false;
}
if (fseek(fp, 0, SEEK_SET) != 0) {
fclose(fp);
return false;
}
buffer = (uint8_t *)malloc((size_t)size + 1u);
if (buffer == NULL) {
fclose(fp);
return false;
}
read_len = fread(buffer, 1u, (size_t)size, fp);
fclose(fp);
if (read_len != (size_t)size) {
free(buffer);
return false;
}
buffer[size] = '\0';
*out_bytes = buffer;
*out_len = (size_t)size;
return true;
}
static bool build_fixture_path(char *out, size_t out_len) {
const char *needle = "/tests/enc/";
const char *path = __FILE__;
const char *pos = strstr(path, needle);
size_t prefix_len;
if (pos == NULL) {
return false;
}
prefix_len = (size_t)(pos - path);
if (prefix_len + strlen("/tests/fixtures/conformance/"
"asl_tgk_exec_plan/artifacts/plan0.bin") + 1u >
out_len) {
return false;
}
memcpy(out, path, prefix_len);
out[prefix_len] = '\0';
strcat(out, "/tests/fixtures/conformance/"
"asl_tgk_exec_plan/artifacts/plan0.bin");
return true;
}
static void store_u32_le(uint8_t *out, uint32_t value) { static void store_u32_le(uint8_t *out, uint32_t value) {
out[0] = (uint8_t)(value & 0xffu); out[0] = (uint8_t)(value & 0xffu);
out[1] = (uint8_t)((value >> 8) & 0xffu); out[1] = (uint8_t)((value >> 8) & 0xffu);
@ -217,6 +288,68 @@ cleanup:
return exit_code; return exit_code;
} }
static int test_invalid_snapshot_range(void) {
amduat_asl_tgk_exec_operator_def_t operators[1];
amduat_asl_tgk_exec_plan_t plan;
memset(operators, 0, sizeof(operators));
fill_default_params(&operators[0].params);
operators[0].op_id = 1u;
operators[0].op_type = AMDUAT_ASL_TGK_EXEC_OP_SEGMENT_SCAN;
operators[0].snapshot.logseq_min = 10u;
operators[0].snapshot.logseq_max = 1u;
plan.plan_version = AMDUAT_ASL_TGK_EXEC_PLAN_VERSION;
plan.operator_count = 1u;
plan.operators = operators;
if (amduat_enc_asl_tgk_exec_plan_encode_v1(&plan,
&(amduat_octets_t){0})) {
fprintf(stderr, "encode unexpectedly succeeded\n");
return 1;
}
return 0;
}
static int test_fixture_plan0(void) {
char path[512];
uint8_t *bytes = NULL;
size_t len = 0u;
amduat_asl_tgk_exec_plan_t plan;
int exit_code = 1;
if (!build_fixture_path(path, sizeof(path))) {
fprintf(stderr, "fixture path build failed\n");
return exit_code;
}
if (!read_file(path, &bytes, &len)) {
fprintf(stderr, "fixture read failed: %s\n", path);
return exit_code;
}
if (!amduat_enc_asl_tgk_exec_plan_decode_v1(amduat_octets(bytes, len),
&plan)) {
fprintf(stderr, "fixture decode failed\n");
goto cleanup;
}
if (plan.plan_version != AMDUAT_ASL_TGK_EXEC_PLAN_VERSION ||
plan.operator_count != 1u ||
plan.operators[0].op_type != AMDUAT_ASL_TGK_EXEC_OP_SEGMENT_SCAN ||
plan.operators[0].snapshot.logseq_max != 10u) {
fprintf(stderr, "fixture contents mismatch\n");
goto cleanup_plan;
}
exit_code = 0;
cleanup_plan:
amduat_enc_asl_tgk_exec_plan_free(&plan);
cleanup:
free(bytes);
return exit_code;
}
int main(void) { int main(void) {
if (test_round_trip() != 0) { if (test_round_trip() != 0) {
return 1; return 1;
@ -227,5 +360,11 @@ int main(void) {
if (test_truncated_decode() != 0) { if (test_truncated_decode() != 0) {
return 1; return 1;
} }
return test_invalid_input_count(); if (test_invalid_input_count() != 0) {
return 1;
}
if (test_invalid_snapshot_range() != 0) {
return 1;
}
return test_fixture_plan0();
} }

236
tests/enc/test_fcs_pcb1.c Normal file
View file

@ -0,0 +1,236 @@
#include "amduat/enc/fcs1.h"
#include "amduat/enc/asl1_core_codec.h"
#include "amduat/fps.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static bool encode_varint(uint64_t value, uint8_t **out, size_t *out_len) {
uint8_t buf[10];
size_t len = 0u;
do {
uint8_t byte = (uint8_t)(value & 0x7fu);
value >>= 7u;
if (value != 0u) {
byte |= 0x80u;
}
buf[len++] = byte;
} while (value != 0u);
*out = (uint8_t *)malloc(len);
if (*out == NULL) {
return false;
}
memcpy(*out, buf, len);
*out_len = len;
return true;
}
static bool append_bytes(uint8_t **buf, size_t *len, size_t *cap,
const void *data, size_t data_len) {
if (*len + data_len > *cap) {
size_t next = *cap == 0u ? 64u : *cap * 2u;
while (next < *len + data_len) {
next *= 2u;
}
uint8_t *next_buf = (uint8_t *)realloc(*buf, next);
if (next_buf == NULL) {
return false;
}
*buf = next_buf;
*cap = next;
}
memcpy(*buf + *len, data, data_len);
*len += data_len;
return true;
}
static bool append_varint(uint8_t **buf, size_t *len, size_t *cap,
uint64_t value) {
uint8_t *enc = NULL;
size_t enc_len = 0u;
bool ok = encode_varint(value, &enc, &enc_len) &&
append_bytes(buf, len, cap, enc, enc_len);
free(enc);
return ok;
}
static bool build_pcb1_bytes(amduat_octets_t *out_bytes,
amduat_reference_t *out_cid) {
uint8_t *buf = NULL;
size_t len = 0u;
size_t cap = 0u;
uint8_t manifest[1 + 1 + 1 + 1 + 32];
size_t manifest_len = 0u;
uint8_t slot_data[] = {0x01, 0x02, 0x03};
amduat_octets_t payload;
amduat_octets_t cid_bytes;
manifest[manifest_len++] = 0x01; /* slot count */
manifest[manifest_len++] = 0x01; /* index */
manifest[manifest_len++] = 0x00; /* name len */
manifest[manifest_len++] = 0x00; /* type */
manifest[manifest_len++] = 0x20; /* digest len */
memset(manifest + manifest_len, 0, 32);
manifest_len += 32;
if (!append_bytes(&buf, &len, &cap, "PCB1", 4u)) {
return false;
}
{
uint8_t header[3] = {0x01, 0x00, 0x00};
if (!append_bytes(&buf, &len, &cap, header, sizeof(header))) {
free(buf);
return false;
}
}
if (!append_bytes(&buf, &len, &cap, (uint8_t[]){0x50}, 1u) ||
!append_varint(&buf, &len, &cap, manifest_len) ||
!append_bytes(&buf, &len, &cap, manifest, manifest_len)) {
free(buf);
return false;
}
if (!append_bytes(&buf, &len, &cap, (uint8_t[]){0x51}, 1u) ||
!append_varint(&buf, &len, &cap, sizeof(slot_data)) ||
!append_bytes(&buf, &len, &cap, slot_data, sizeof(slot_data))) {
free(buf);
return false;
}
payload = amduat_octets(buf, len);
if (!amduat_fps_cid_for_payload(AMDUAT_HASH_ASL1_ID_SHA256,
payload,
out_cid)) {
free(buf);
return false;
}
if (!amduat_enc_asl1_core_encode_reference_v1(*out_cid, &cid_bytes)) {
amduat_reference_free(out_cid);
free(buf);
return false;
}
amduat_octets_free(&cid_bytes);
*out_bytes = payload;
return true;
}
static bool build_fcs1_bytes(amduat_octets_t *out_bytes,
amduat_reference_t function_ref,
amduat_reference_t pcb_ref,
uint32_t arity) {
uint8_t *buf = NULL;
size_t len = 0u;
size_t cap = 0u;
amduat_octets_t func_bytes;
amduat_octets_t pcb_bytes;
if (!amduat_enc_asl1_core_encode_reference_v1(function_ref, &func_bytes) ||
!amduat_enc_asl1_core_encode_reference_v1(pcb_ref, &pcb_bytes)) {
return false;
}
if (!append_bytes(&buf, &len, &cap, "FCS1", 4u)) {
return false;
}
{
uint8_t header[3] = {0x01, 0x00, 0x00};
if (!append_bytes(&buf, &len, &cap, header, sizeof(header))) {
free(buf);
return false;
}
}
if (!append_bytes(&buf, &len, &cap, (uint8_t[]){0x30}, 1u) ||
!append_varint(&buf, &len, &cap, func_bytes.len) ||
!append_bytes(&buf, &len, &cap, func_bytes.data, func_bytes.len)) {
free(buf);
amduat_octets_free(&func_bytes);
amduat_octets_free(&pcb_bytes);
return false;
}
if (!append_bytes(&buf, &len, &cap, (uint8_t[]){0x31}, 1u) ||
!append_varint(&buf, &len, &cap, pcb_bytes.len) ||
!append_bytes(&buf, &len, &cap, pcb_bytes.data, pcb_bytes.len)) {
free(buf);
amduat_octets_free(&func_bytes);
amduat_octets_free(&pcb_bytes);
return false;
}
if (!append_bytes(&buf, &len, &cap, (uint8_t[]){0x32}, 1u) ||
!append_varint(&buf, &len, &cap, arity)) {
free(buf);
amduat_octets_free(&func_bytes);
amduat_octets_free(&pcb_bytes);
return false;
}
amduat_octets_free(&func_bytes);
amduat_octets_free(&pcb_bytes);
*out_bytes = amduat_octets(buf, len);
return true;
}
typedef struct {
amduat_reference_t pcb_ref;
amduat_octets_t pcb_bytes;
} resolver_ctx_t;
static bool resolve_ref(void *ctx,
amduat_reference_t ref,
amduat_octets_t *out_bytes) {
resolver_ctx_t *state = (resolver_ctx_t *)ctx;
if (state == NULL || out_bytes == NULL) {
return false;
}
if (!amduat_reference_eq(ref, state->pcb_ref)) {
return false;
}
return amduat_octets_clone(state->pcb_bytes, out_bytes);
}
int main(void) {
amduat_octets_t pcb_bytes;
amduat_reference_t pcb_ref;
amduat_octets_t fcs_bytes;
amduat_reference_t func_ref;
uint8_t func_digest[32];
resolver_ctx_t ctx;
amduat_fcs1_error_t err;
if (!build_pcb1_bytes(&pcb_bytes, &pcb_ref)) {
return 1;
}
memset(func_digest, 0x11, sizeof(func_digest));
func_ref = amduat_reference(AMDUAT_HASH_ASL1_ID_SHA256,
amduat_octets(func_digest,
sizeof(func_digest)));
if (!build_fcs1_bytes(&fcs_bytes, func_ref, pcb_ref, 1u)) {
amduat_reference_free(&pcb_ref);
amduat_octets_free(&pcb_bytes);
return 1;
}
ctx.pcb_ref = pcb_ref;
ctx.pcb_bytes = pcb_bytes;
err = amduat_fcs1_validate_v1(fcs_bytes, resolve_ref, &ctx);
if (err != AMDUAT_FCS1_OK) {
amduat_reference_free(&pcb_ref);
amduat_octets_free(&pcb_bytes);
amduat_octets_free(&fcs_bytes);
return 1;
}
amduat_reference_free(&pcb_ref);
amduat_octets_free(&pcb_bytes);
amduat_octets_free(&fcs_bytes);
return 0;
}

626
tests/enc/test_fps.c Normal file
View file

@ -0,0 +1,626 @@
#include "amduat/fps.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
const uint8_t *data;
size_t len;
size_t offset;
} cursor_t;
static bool read_file(const char *path, uint8_t **out_bytes, size_t *out_len) {
FILE *fp;
long size;
uint8_t *buffer;
size_t read_len;
if (path == NULL || out_bytes == NULL || out_len == NULL) {
return false;
}
*out_bytes = NULL;
*out_len = 0u;
fp = fopen(path, "rb");
if (fp == NULL) {
return false;
}
if (fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
return false;
}
size = ftell(fp);
if (size < 0) {
fclose(fp);
return false;
}
if (fseek(fp, 0, SEEK_SET) != 0) {
fclose(fp);
return false;
}
buffer = (uint8_t *)malloc((size_t)size + 1u);
if (buffer == NULL) {
fclose(fp);
return false;
}
read_len = fread(buffer, 1u, (size_t)size, fp);
fclose(fp);
if (read_len != (size_t)size) {
free(buffer);
return false;
}
buffer[size] = '\0';
*out_bytes = buffer;
*out_len = (size_t)size;
return true;
}
static bool build_fixture_path(char *out, size_t out_len) {
const char *needle = "/tests/enc/";
const char *path = __FILE__;
const char *pos = strstr(path, needle);
size_t prefix_len;
if (pos == NULL) {
return false;
}
prefix_len = (size_t)(pos - path);
if (prefix_len + strlen("/tests/fixtures/conformance/"
"fps_1/manifest.json") + 1u >
out_len) {
return false;
}
memcpy(out, path, prefix_len);
out[prefix_len] = '\0';
strcat(out, "/tests/fixtures/conformance/fps_1/manifest.json");
return true;
}
static bool cursor_expect(cursor_t *cur, const char *literal) {
size_t len = strlen(literal);
if (cur->len - cur->offset < len) {
return false;
}
if (memcmp(cur->data + cur->offset, literal, len) != 0) {
return false;
}
cur->offset += len;
return true;
}
static bool cursor_skip_ws(cursor_t *cur) {
while (cur->offset < cur->len) {
char c = (char)cur->data[cur->offset];
if (c != ' ' && c != '\n' && c != '\r' && c != '\t') {
break;
}
cur->offset++;
}
return true;
}
static bool cursor_read_string(cursor_t *cur, char **out) {
size_t start;
size_t end;
char *buf;
if (!cursor_skip_ws(cur) || cur->offset >= cur->len ||
cur->data[cur->offset] != '"') {
return false;
}
cur->offset++;
start = cur->offset;
while (cur->offset < cur->len && cur->data[cur->offset] != '"') {
cur->offset++;
}
if (cur->offset >= cur->len) {
return false;
}
end = cur->offset;
cur->offset++;
buf = (char *)malloc(end - start + 1u);
if (buf == NULL) {
return false;
}
memcpy(buf, cur->data + start, end - start);
buf[end - start] = '\0';
*out = buf;
return true;
}
static bool cursor_read_number(cursor_t *cur, size_t *out) {
size_t start;
size_t value = 0u;
bool has_digit = false;
if (!cursor_skip_ws(cur)) {
return false;
}
start = cur->offset;
while (cur->offset < cur->len) {
char c = (char)cur->data[cur->offset];
if (c < '0' || c > '9') {
break;
}
has_digit = true;
value = value * 10u + (size_t)(c - '0');
cur->offset++;
}
if (!has_digit || start == cur->offset) {
return false;
}
*out = value;
return true;
}
static bool hex_decode(const char *hex, uint8_t **out, size_t *out_len) {
size_t len;
size_t i;
uint8_t *buf;
if (hex == NULL || out == NULL || out_len == NULL) {
return false;
}
len = strlen(hex);
if (len % 2u != 0u) {
return false;
}
buf = (uint8_t *)malloc(len / 2u);
if (buf == NULL) {
return false;
}
for (i = 0u; i < len; i += 2u) {
unsigned int byte;
if (sscanf(hex + i, "%2x", &byte) != 1) {
free(buf);
return false;
}
buf[i / 2u] = (uint8_t)byte;
}
*out = buf;
*out_len = len / 2u;
return true;
}
static bool hex_eq(const char *hex, amduat_octets_t bytes) {
uint8_t *decoded = NULL;
size_t decoded_len = 0u;
bool ok = false;
if (!hex_decode(hex, &decoded, &decoded_len)) {
return false;
}
ok = decoded_len == bytes.len &&
(decoded_len == 0u ||
memcmp(decoded, bytes.data, decoded_len) == 0);
free(decoded);
return ok;
}
static bool cid_matches(const char *hex, amduat_octets_t output) {
amduat_reference_t cid;
size_t hex_len = strlen(hex);
char *lower = NULL;
char *scratch = NULL;
bool ok = false;
if (!amduat_fps_cid_for_payload(AMDUAT_HASH_ASL1_ID_SHA256, output, &cid)) {
return false;
}
lower = (char *)malloc(hex_len + 1u);
if (lower == NULL) {
amduat_reference_free(&cid);
return false;
}
scratch = (char *)malloc(cid.digest.len * 2u + 1u);
if (scratch == NULL) {
free(lower);
amduat_reference_free(&cid);
return false;
}
for (size_t i = 0u; i < hex_len; ++i) {
char c = hex[i];
if (c >= 'A' && c <= 'F') {
c = (char)(c - 'A' + 'a');
}
lower[i] = c;
}
lower[hex_len] = '\0';
for (size_t i = 0u; i < cid.digest.len; ++i) {
sprintf(scratch + i * 2u, "%02x", cid.digest.data[i]);
}
ok = strcmp(lower, scratch) == 0;
free(lower);
free(scratch);
amduat_reference_free(&cid);
return ok;
}
static bool parse_manifest_cases(cursor_t *cur) {
if (!cursor_skip_ws(cur) || cur->offset >= cur->len ||
cur->data[cur->offset] != '[') {
return false;
}
cur->offset++;
while (true) {
char *field = NULL;
char *value = NULL;
char *op = NULL;
char *input = NULL;
char *insert = NULL;
char *output = NULL;
char *cid_hex = NULL;
char *level = NULL;
char *inputs[4] = {0};
size_t inputs_len = 0u;
size_t offset = 0u;
size_t length = 0u;
bool has_offset = false;
bool has_length = false;
bool has_inputs = false;
bool has_input = false;
bool has_insert = false;
bool has_level = false;
bool has_output = false;
bool has_cid = false;
bool ok = true;
if (!cursor_skip_ws(cur)) {
return false;
}
if (cur->offset < cur->len && cur->data[cur->offset] == ']') {
cur->offset++;
return true;
}
if (cur->offset >= cur->len || cur->data[cur->offset] != '{') {
return false;
}
cur->offset++;
while (true) {
if (!cursor_read_string(cur, &field)) {
ok = false;
break;
}
if (!cursor_skip_ws(cur) || cur->offset >= cur->len ||
cur->data[cur->offset] != ':') {
ok = false;
break;
}
cur->offset++;
if (strcmp(field, "id") == 0 ||
strcmp(field, "op") == 0 || strcmp(field, "input") == 0 ||
strcmp(field, "insert") == 0 || strcmp(field, "output") == 0 ||
strcmp(field, "cid_sha256") == 0 || strcmp(field, "level") == 0) {
if (!cursor_read_string(cur, &value)) {
ok = false;
break;
}
if (strcmp(field, "id") == 0) {
free(value);
value = NULL;
} else if (strcmp(field, "op") == 0) {
op = value;
} else if (strcmp(field, "input") == 0) {
input = value;
has_input = true;
} else if (strcmp(field, "insert") == 0) {
insert = value;
has_insert = true;
} else if (strcmp(field, "output") == 0) {
output = value;
has_output = true;
} else if (strcmp(field, "cid_sha256") == 0) {
cid_hex = value;
has_cid = true;
} else {
level = value;
has_level = true;
}
} else if (strcmp(field, "offset") == 0 ||
strcmp(field, "length") == 0) {
size_t num = 0u;
if (!cursor_read_number(cur, &num)) {
ok = false;
break;
}
if (strcmp(field, "offset") == 0) {
offset = num;
has_offset = true;
} else {
length = num;
has_length = true;
}
} else if (strcmp(field, "inputs") == 0) {
if (!cursor_skip_ws(cur) || cur->offset >= cur->len ||
cur->data[cur->offset] != '[') {
ok = false;
break;
}
cur->offset++;
while (true) {
if (!cursor_skip_ws(cur)) {
ok = false;
break;
}
if (cur->offset < cur->len && cur->data[cur->offset] == ']') {
cur->offset++;
break;
}
if (inputs_len >= 4u) {
ok = false;
break;
}
if (!cursor_read_string(cur, &inputs[inputs_len++])) {
ok = false;
break;
}
if (!cursor_skip_ws(cur)) {
ok = false;
break;
}
if (cur->offset < cur->len && cur->data[cur->offset] == ',') {
cur->offset++;
continue;
}
}
has_inputs = true;
} else {
ok = false;
break;
}
free(field);
field = NULL;
if (!cursor_skip_ws(cur)) {
ok = false;
break;
}
if (cur->offset < cur->len && cur->data[cur->offset] == ',') {
cur->offset++;
continue;
}
if (cur->offset < cur->len && cur->data[cur->offset] == '}') {
cur->offset++;
break;
}
}
if (!ok || op == NULL || !has_output || !has_cid) {
ok = false;
}
if (ok) {
amduat_octets_t output_bytes = amduat_octets(NULL, 0u);
amduat_octets_t out = amduat_octets(NULL, 0u);
amduat_octets_t input_bytes = amduat_octets(NULL, 0u);
amduat_octets_t insert_bytes = amduat_octets(NULL, 0u);
uint8_t *decoded = NULL;
size_t decoded_len = 0u;
uint8_t *insert_decoded = NULL;
size_t insert_len = 0u;
if (!hex_decode(output, &decoded, &decoded_len)) {
ok = false;
} else {
output_bytes = amduat_octets(decoded, decoded_len);
}
if (ok && !cid_matches(cid_hex, output_bytes)) {
ok = false;
}
if (ok && strcmp(op, "slice") == 0) {
if (!has_input || !has_offset || !has_length) {
ok = false;
} else if (!hex_decode(input, &decoded, &decoded_len)) {
ok = false;
} else {
input_bytes = amduat_octets(decoded, decoded_len);
if (amduat_fps_slice(input_bytes, offset, length, &out) !=
AMDUAT_FPS_OK) {
ok = false;
} else if (!hex_eq(output, out)) {
ok = false;
}
}
} else if (ok && strcmp(op, "concat") == 0) {
if (!has_inputs) {
ok = false;
} else {
amduat_octets_t in_octets[4];
for (size_t i = 0u; i < inputs_len; ++i) {
if (!hex_decode(inputs[i], &decoded, &decoded_len)) {
ok = false;
break;
}
in_octets[i] = amduat_octets(decoded, decoded_len);
}
if (ok) {
if (amduat_fps_concat(in_octets, inputs_len, &out) !=
AMDUAT_FPS_OK) {
ok = false;
} else if (!hex_eq(output, out)) {
ok = false;
}
}
for (size_t i = 0u; i < inputs_len; ++i) {
free((void *)in_octets[i].data);
}
}
} else if (ok && strcmp(op, "reverse") == 0) {
amduat_fps_reverse_level_t lvl = AMDUAT_FPS_REVERSE_BYTES;
if (!has_input || !has_level) {
ok = false;
} else if (strcmp(level, "bit") == 0) {
lvl = AMDUAT_FPS_REVERSE_BITS;
} else if (strcmp(level, "byte") == 0) {
lvl = AMDUAT_FPS_REVERSE_BYTES;
} else if (strcmp(level, "word") == 0) {
lvl = AMDUAT_FPS_REVERSE_WORDS;
} else if (strcmp(level, "long") == 0) {
lvl = AMDUAT_FPS_REVERSE_LONGS;
} else {
ok = false;
}
if (ok && !hex_decode(input, &decoded, &decoded_len)) {
ok = false;
}
if (ok) {
input_bytes = amduat_octets(decoded, decoded_len);
if (amduat_fps_reverse(input_bytes, lvl, &out) != AMDUAT_FPS_OK) {
ok = false;
} else if (!hex_eq(output, out)) {
ok = false;
}
}
} else if (ok && strcmp(op, "splice") == 0) {
if (!has_input || !has_insert || !has_offset) {
ok = false;
} else if (!hex_decode(input, &decoded, &decoded_len) ||
!hex_decode(insert, &insert_decoded, &insert_len)) {
ok = false;
} else {
input_bytes = amduat_octets(decoded, decoded_len);
insert_bytes = amduat_octets(insert_decoded, insert_len);
if (amduat_fps_splice(input_bytes, offset, insert_bytes, &out) !=
AMDUAT_FPS_OK) {
ok = false;
} else if (!hex_eq(output, out)) {
ok = false;
}
}
} else if (ok) {
ok = false;
}
amduat_octets_free(&out);
free((void *)output_bytes.data);
if (input_bytes.data != NULL) {
free((void *)input_bytes.data);
}
if (insert_bytes.data != NULL) {
free((void *)insert_bytes.data);
}
}
free(op);
free(input);
free(insert);
free(output);
free(cid_hex);
free(level);
for (size_t i = 0u; i < inputs_len; ++i) {
free(inputs[i]);
}
if (!ok) {
return false;
}
if (!cursor_skip_ws(cur)) {
return false;
}
if (cur->offset < cur->len && cur->data[cur->offset] == ',') {
cur->offset++;
continue;
}
if (cur->offset < cur->len && cur->data[cur->offset] == ']') {
cur->offset++;
return true;
}
}
}
static bool parse_manifest(uint8_t *bytes, size_t len) {
cursor_t cur;
char *field = NULL;
char *schema = NULL;
bool ok = false;
cur.data = bytes;
cur.len = len;
cur.offset = 0u;
if (!cursor_skip_ws(&cur) || cur.offset >= cur.len ||
cur.data[cur.offset] != '{') {
return false;
}
cur.offset++;
while (true) {
if (!cursor_read_string(&cur, &field)) {
break;
}
if (!cursor_skip_ws(&cur) || cur.offset >= cur.len ||
cur.data[cur.offset] != ':') {
break;
}
cur.offset++;
if (strcmp(field, "schema") == 0) {
if (!cursor_read_string(&cur, &schema)) {
break;
}
} else if (strcmp(field, "cases") == 0) {
if (!parse_manifest_cases(&cur)) {
break;
}
} else {
break;
}
free(field);
field = NULL;
if (!cursor_skip_ws(&cur)) {
break;
}
if (cur.offset < cur.len && cur.data[cur.offset] == ',') {
cur.offset++;
continue;
}
if (cur.offset < cur.len && cur.data[cur.offset] == '}') {
cur.offset++;
break;
}
}
if (schema != NULL &&
strcmp(schema, "amduat.conformance.fps.v0") == 0) {
ok = true;
}
free(field);
free(schema);
return ok;
}
int main(void) {
char path[512];
uint8_t *bytes = NULL;
size_t len = 0u;
int exit_code = 1;
if (!build_fixture_path(path, sizeof(path))) {
fprintf(stderr, "fixture path build failed\n");
return exit_code;
}
if (!read_file(path, &bytes, &len)) {
fprintf(stderr, "fixture read failed: %s\n", path);
return exit_code;
}
if (!parse_manifest(bytes, len)) {
fprintf(stderr, "fixture parse failed\n");
goto cleanup;
}
exit_code = 0;
cleanup:
free(bytes);
return exit_code;
}

22
tests/fixtures/conformance/README.md vendored Normal file
View file

@ -0,0 +1,22 @@
# Conformance Fixtures
This directory holds small, canonical fixture sets and manifests used by the
core conformance harness.
## ASL/1-STORE fixtures
- `asl_put_get/manifest.json` — byte fixtures for PUT/GET idempotency.
## TGK/STORE/1 fixtures
- `tgk_edges/manifest.json` — canonical edge payload fixtures.
## PEL trace TGK vectors
- `../../vectors/pel_trace_tgk_test_vectors_v0.json`
- `../../vectors/pel_trace_tgk_test_vectors_v1.json`
## Generator
`generate_manifest.py` can (re)generate the ASL/1-STORE fixture manifest with
canonical ordering and file digests.

View file

@ -1,8 +1,23 @@
{ {
"schema": "amduat.conformance.fixtures.v0", "schema": "amduat.conformance.fixtures.v0",
"fixtures": [ "fixtures": [
{"id": "empty", "artifact": "a0.bin", "notes": "empty payload"}, {
{"id": "hello", "artifact": "a1.bin", "notes": "ascii payload"}, "id": "a0",
{"id": "binary", "artifact": "a2.bin", "notes": "0..255"} "artifact": "artifacts/a0.bin",
"size": "0",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
{
"id": "a1",
"artifact": "artifacts/a1.bin",
"size": "12",
"sha256": "29e86b2096b77c51796e607cbac976bf507c443c2efd14ab343a5c07f9978644"
},
{
"id": "a2",
"artifact": "artifacts/a2.bin",
"size": "256",
"sha256": "40aff2e9d2d8922e47afd4648e6967497158785fbd1da870e7110266bf944880"
}
] ]
} }

Binary file not shown.

View file

@ -0,0 +1,12 @@
{
"schema": "amduat.conformance.fixtures.v0",
"fixtures": [
{
"id": "plan0",
"artifact": "artifacts/plan0.bin",
"size": "137",
"sha256": "3633b7a6ca915bb4be8837648890ed91a2a67781b837edd6c00a09a7a2d3cc7b",
"notes": "ASL/TGK exec plan v1 fixture"
}
]
}

View file

@ -0,0 +1,65 @@
{
"schema": "amduat.conformance.fps.v0",
"cases": [
{
"id": "concat0",
"op": "concat",
"inputs": [
"dead",
"beef"
],
"output": "deadbeef",
"cid_sha256": "42f90312198cb86ae022fd58e8dd825cc0fea31bf17685040f40a6a49bf55330"
},
{
"id": "reverse_bits0",
"op": "reverse",
"level": "bit",
"input": "01f0",
"output": "0f80",
"cid_sha256": "3a26c2b229a79ed7337cacc1d68e5aa34c3cb12c6027a5bdfb6ae87c9433390e"
},
{
"id": "reverse_bytes0",
"op": "reverse",
"level": "byte",
"input": "01020304",
"output": "04030201",
"cid_sha256": "56f83c32ac7262d4aff0e766e8f3ddbaa325f928ddfb7764aeb1143ed3417d4e"
},
{
"id": "reverse_longs0",
"op": "reverse",
"level": "long",
"input": "0102030405060708",
"output": "0506070801020304",
"cid_sha256": "f720714eb46590c97470c8970768c0162322feb7cfd85a3e3f9af85653729eca"
},
{
"id": "reverse_words0",
"op": "reverse",
"level": "word",
"input": "0102030405060708",
"output": "0708050603040102",
"cid_sha256": "ae08a370a31a4dccd5ce7f86629e0823087f1ac8f29aa52c7fc9a0b515e38389"
},
{
"id": "slice0",
"op": "slice",
"input": "000102030405",
"offset": 2,
"length": 3,
"output": "020304",
"cid_sha256": "8d81247326859e1abafaf9cbf25cdb8c088c778b3ec109cdf9085bd8f2787fac"
},
{
"id": "splice0",
"op": "splice",
"input": "010203",
"offset": 1,
"insert": "ff",
"output": "01ff0203",
"cid_sha256": "a82fb8838858d19c3c258205d6de59dcb8d0f97d3898cfbbe848098aaedec6a7"
}
]
}

View file

@ -0,0 +1,62 @@
#!/usr/bin/env python3
import argparse
import hashlib
import json
import os
from pathlib import Path
def sha256_file(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(1024 * 1024), b""):
h.update(chunk)
return h.hexdigest()
def build_asl_manifest(root: Path) -> dict:
artifacts_dir = root / "artifacts"
fixtures = []
for entry in sorted(artifacts_dir.iterdir()):
if not entry.is_file() or entry.suffix != ".bin":
continue
artifact_rel = f"artifacts/{entry.name}"
fixtures.append(
{
"id": entry.stem,
"artifact": artifact_rel,
"size": str(entry.stat().st_size),
"sha256": sha256_file(entry),
}
)
return {"schema": "amduat.conformance.fixtures.v0", "fixtures": fixtures}
def main() -> int:
parser = argparse.ArgumentParser(
description="Generate conformance fixture manifests."
)
parser.add_argument(
"root",
type=Path,
help="Fixture directory containing artifacts/",
)
parser.add_argument(
"--out",
type=Path,
default=None,
help="Output manifest path (defaults to <root>/manifest.json)",
)
args = parser.parse_args()
manifest = build_asl_manifest(args.root)
out_path = args.out or (args.root / "manifest.json")
out_path.parent.mkdir(parents=True, exist_ok=True)
with out_path.open("w", encoding="utf-8") as f:
json.dump(manifest, f, indent=2, sort_keys=False)
f.write("\n")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,99 @@
# Conformance Fixture Manifests
This directory contains small, canonical fixture sets used by conformance tests.
Each fixture set publishes a JSON manifest with deterministic ordering.
## Common rules
- JSON must be UTF-8, no comments.
- Arrays are ordered and MUST be in canonical order as specified by the schema.
- Artifact references use the canonical `ReferenceBytes` hex encoding when
present (lowercase hex, no `0x` prefix).
- `sha256` fields are lowercase hex of the raw bytes.
## Schema: `amduat.conformance.fixtures.v0`
Used by simple ASL/1-STORE byte fixtures (e.g. `asl_put_get`).
```
{
"schema": "amduat.conformance.fixtures.v0",
"fixtures": [
{
"id": "string",
"artifact": "relative/path.bin",
"size": "0",
"sha256": "hex",
"notes": "optional string"
}
]
}
```
Rules:
- `fixtures` MUST be sorted by ascending `id`.
- `artifact` is a path relative to the manifest file.
- `size` is the byte length of the artifact file encoded as a decimal string.
- `sha256` is the digest of the artifact file contents.
## Schema: `amduat.conformance.tgk_edges.v0`
Used by TGK edge fixture sets.
```
{
"schema": "amduat.conformance.tgk_edges.v0",
"edges": [
{
"id": "string",
"type": 0,
"from": ["refhex"],
"to": ["refhex"],
"payload": "hex",
"notes": "optional string"
}
]
}
```
Rules:
- `edges` MUST be sorted by ascending `id`.
- `from`/`to` are lists of `ReferenceBytes` hex (lowercase).
- `payload` is hex-encoded edge payload bytes (lowercase).
## Schema: `amduat.conformance.fps.v0`
Used by FPS/1 primitive conformance vectors.
```
{
"schema": "amduat.conformance.fps.v0",
"cases": [
{
"id": "string",
"op": "slice|concat|reverse|splice",
"input": "hex",
"inputs": ["hex"],
"insert": "hex",
"offset": 0,
"length": 0,
"level": "bit|byte|word|long",
"output": "hex",
"cid_sha256": "hex"
}
]
}
```
Rules:
- `cases` MUST be sorted by ascending `id`.
- `input` is used for unary operations.
- `inputs` is used for `concat`.
- `insert` is used for `splice`.
- `offset` and `length` are required for `slice` and `splice`.
- `level` is required for `reverse`.
- `output` is the expected output payload bytes.
- `cid_sha256` is `SHA-256("CAS:OBJ\\0" || output)` in lowercase hex.

View file

@ -0,0 +1,103 @@
#include "amduat/tgk/store.h"
#include <stdio.h>
#include <string.h>
static amduat_tgk_graph_error_t stub_resolve_edge(void *ctx,
amduat_reference_t ref,
amduat_tgk_edge_body_t *out_body) {
amduat_tgk_graph_error_t *err = (amduat_tgk_graph_error_t *)ctx;
(void)ref;
(void)out_body;
return *err;
}
static int test_not_edge_mapping(void) {
amduat_tgk_store_ops_t ops;
amduat_tgk_store_t store;
amduat_tgk_store_config_t cfg;
amduat_tgk_graph_error_t err = GS_ERR_NOT_EDGE;
memset(&cfg, 0, sizeof(cfg));
memset(&ops, 0, sizeof(ops));
ops.resolve_edge = stub_resolve_edge;
amduat_tgk_store_init(&store, cfg, ops, &err);
if (amduat_tgk_store_resolve_edge(&store, amduat_reference(0, amduat_octets(NULL, 0u)), NULL) !=
GS_ERR_NOT_EDGE) {
return 1;
}
return 0;
}
static int test_artifact_error_mapping(void) {
amduat_tgk_store_ops_t ops;
amduat_tgk_store_t store;
amduat_tgk_store_config_t cfg;
amduat_tgk_graph_error_t err = GS_ERR_ARTIFACT_ERROR;
memset(&cfg, 0, sizeof(cfg));
memset(&ops, 0, sizeof(ops));
ops.resolve_edge = stub_resolve_edge;
amduat_tgk_store_init(&store, cfg, ops, &err);
if (amduat_tgk_store_resolve_edge(&store, amduat_reference(0, amduat_octets(NULL, 0u)), NULL) !=
GS_ERR_ARTIFACT_ERROR) {
return 1;
}
return 0;
}
static int test_unsupported_mapping(void) {
amduat_tgk_store_ops_t ops;
amduat_tgk_store_t store;
amduat_tgk_store_config_t cfg;
memset(&cfg, 0, sizeof(cfg));
memset(&ops, 0, sizeof(ops));
amduat_tgk_store_init(&store, cfg, ops, NULL);
if (amduat_tgk_store_resolve_edge(&store, amduat_reference(0, amduat_octets(NULL, 0u)), NULL) !=
GS_ERR_UNSUPPORTED) {
return 1;
}
return 0;
}
static int test_integrity_mapping(void) {
amduat_tgk_store_ops_t ops;
amduat_tgk_store_t store;
amduat_tgk_store_config_t cfg;
amduat_tgk_graph_error_t err = GS_ERR_INTEGRITY;
memset(&cfg, 0, sizeof(cfg));
memset(&ops, 0, sizeof(ops));
ops.resolve_edge = stub_resolve_edge;
amduat_tgk_store_init(&store, cfg, ops, &err);
if (amduat_tgk_store_resolve_edge(&store, amduat_reference(0, amduat_octets(NULL, 0u)), NULL) !=
GS_ERR_INTEGRITY) {
return 1;
}
return 0;
}
int main(void) {
if (test_not_edge_mapping() != 0) {
fprintf(stderr, "not edge mapping failed\n");
return 1;
}
if (test_artifact_error_mapping() != 0) {
fprintf(stderr, "artifact error mapping failed\n");
return 1;
}
if (test_unsupported_mapping() != 0) {
fprintf(stderr, "unsupported mapping failed\n");
return 1;
}
if (test_integrity_mapping() != 0) {
fprintf(stderr, "integrity mapping failed\n");
return 1;
}
return 0;
}

View file

@ -2,9 +2,9 @@
Status: Approved Status: Approved
Owner: Niklas Rydberg Owner: Niklas Rydberg
Version: 0.4.1 Version: 0.4.2
SoT: Yes SoT: Yes
Last Updated: 2025-11-16 Last Updated: 2026-02-21
Linked Phase Pack: N/A Linked Phase Pack: N/A
Tags: [deterministic, binary-minimalism] Tags: [deterministic, binary-minimalism]
@ -20,7 +20,7 @@ Tags: [deterministic, binary-minimalism]
**Informative references:** **Informative references:**
* `ENC/ASL1-CORE v1.x` — canonical encoding profile (`ASL_ENC_CORE_V1`) * `ENC/ASL1-CORE v1.x` — canonical encoding profile (`ASL_ENC_CORE_V1`)
* `HASH/ASL1 v0.2.2` — ASL1 hash family and `HashId` assignments * `HASH/ASL1 v0.2.5` — ASL1 hash family and `HashId` assignments
* `ASL/1-STORE v0.4.0` — content-addressable store over ASL/1-CORE * `ASL/1-STORE v0.4.0` — content-addressable store over ASL/1-CORE
* `TGK/1-CORE v0.7.0` — trace graph kernel over `Reference` * `TGK/1-CORE v0.7.0` — trace graph kernel over `Reference`
* `PEL/1` — execution substrate * `PEL/1` — execution substrate
@ -289,12 +289,16 @@ then the Reference `R` for `A` under `(P, H)` is:
```text ```text
ArtifactBytes = encode_P(A) ArtifactBytes = encode_P(A)
digest = H(ArtifactBytes) digest = H("AMDUAT:ASL1:ART\0" || ArtifactBytes)
Reference = { hash_id = HID, digest = digest } Reference = { hash_id = HID, digest = digest }
``` ```
The domain separator is the ASCII byte sequence `AMDUAT:ASL1:ART` followed by a
single `0x00` terminator (16 bytes total). The separator MUST be present
verbatim and MUST NOT be modified or omitted.
> **ASL/CORE-REF-DERIVE/1** > **ASL/CORE-REF-DERIVE/1**
> Any component that claims to derive References from Artifacts for a given `(EncodingProfileId, HashId)` **MUST** use this exact procedure. > Any component that claims to derive References from Artifacts for a given `(EncodingProfileId, HashId)` **MUST** use this exact procedure, including the domain separator.
### 4.3 Deterministic agreement lemma (informative) ### 4.3 Deterministic agreement lemma (informative)
@ -306,7 +310,7 @@ For any two conformant implementations that share:
then for any Artifact `A`: then for any Artifact `A`:
* both will compute identical `ArtifactBytes`, * both will compute identical `ArtifactBytes`,
* both will compute identical `digest = H(ArtifactBytes)`, * both will compute identical `digest = H("AMDUAT:ASL1:ART\0" || ArtifactBytes)`,
* both will form identical `Reference {hash_id = HID, digest = digest}`. * both will form identical `Reference {hash_id = HID, digest = digest}`.
This is the basis for cross-Store and cross-domain determinism in Amduat. This is the basis for cross-Store and cross-domain determinism in Amduat.
@ -316,6 +320,7 @@ This is the basis for cross-Store and cross-domain determinism in Amduat.
While ASL/1-CORE is conceptually family-agnostic, the **Amduat 2.0 substrate** standardizes: While ASL/1-CORE is conceptually family-agnostic, the **Amduat 2.0 substrate** standardizes:
* `ASL_ENC_CORE_V1` as the canonical Artifact encoding profile; * `ASL_ENC_CORE_V1` as the canonical Artifact encoding profile;
* `HASH/ASL1` as the canonical hash namespace for `Reference.hash_id` values;
* `HASH-ASL1-256` (`HashId = 0x0001`) as the canonical default hash algorithm for identity-critical surfaces. * `HASH-ASL1-256` (`HashId = 0x0001`) as the canonical default hash algorithm for identity-critical surfaces.
Other `(EncodingProfileId, HashId)` pairs are allowed but must be explicitly declared by the consuming profile or implementation. Other `(EncodingProfileId, HashId)` pairs are allowed but must be explicitly declared by the consuming profile or implementation.
@ -495,4 +500,5 @@ Everything else — API design, transport formats, performance characteristics,
## Document History ## Document History
* **0.4.2 (2026-02-21):** Added ASL/1 identity domain separation and clarified hash namespace usage.
* **0.4.1 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline. * **0.4.1 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline.

View file

@ -1,10 +1,10 @@
# ASL/1-CORE-INDEX — Semantic Index Model # ASL/1-CORE-INDEX — Semantic Index Model
Status: Draft Status: Approved
Owner: Niklas Rydberg Owner: Niklas Rydberg
Version: 0.1.0 Version: 1.0.0
SoT: No SoT: Yes
Last Updated: 2025-11-16 Last Updated: 2026-02-21
Linked Phase Pack: N/A Linked Phase Pack: N/A
Tags: [deterministic, index, semantics] Tags: [deterministic, index, semantics]

View file

@ -1,10 +1,10 @@
# ASL/INDEXES/1 -- Index Taxonomy and Relationships # ASL/INDEXES/1 -- Index Taxonomy and Relationships
Status: Draft Status: Approved
Owner: Architecture Owner: Architecture
Version: 0.1.0 Version: 1.0.0
SoT: No SoT: Yes
Last Updated: 2025-01-17 Last Updated: 2026-02-21
Linked Phase Pack: N/A Linked Phase Pack: N/A
Tags: [indexes, content, structural, materialization] Tags: [indexes, content, structural, materialization]

View file

@ -115,6 +115,22 @@ in `ENC-ASL-LOG`. The envelope MUST include:
* `payload` (type-specific bytes) * `payload` (type-specific bytes)
* `record_hash` (hash-chained integrity) * `record_hash` (hash-chained integrity)
### 3.4 Log Segment Identity (Optional)
Implementations MAY store the log as **multiple segments** (e.g., rotated files).
If segmented storage is used, each segment MUST be assigned a **store-local**
`segment_id` (opaque `uint64_t`) and MUST carry a **segment hash** computed over
the exact bytes of the segment as defined in `ENC/ASL-LOG/1`.
A log segment identity is the tuple:
```
(segment_id, logseq_min, logseq_max, segment_hash)
```
This identity is used only for storage metadata and audit; it does not change
the ASL/LOG/1 semantics or record ordering.
--- ---
## 4. Record Types (Normative) ## 4. Record Types (Normative)
@ -152,6 +168,8 @@ Semantics:
* Segment MUST be immutable. * Segment MUST be immutable.
* All referenced blocks MUST already be sealed. * All referenced blocks MUST already be sealed.
* Segment contents are not re-logged. * Segment contents are not re-logged.
* `segment_hash` MUST be the SHA-256 hash of the exact segment bytes as encoded
by `ENC/ASL-CORE-INDEX/1`, including the finalized footer.
### 4.2 TOMBSTONE ### 4.2 TOMBSTONE
@ -211,6 +229,9 @@ typedef struct {
Semantics: Semantics:
* `snapshot_id` is an opaque, store-assigned `uint64_t` identifier persisted in
the anchor payload.
* The anchor's `logseq` is the snapshot's log position.
* Defines a replay checkpoint. * Defines a replay checkpoint.
* Enables log truncation below anchor with care. * Enables log truncation below anchor with care.
@ -249,7 +270,8 @@ typedef struct {
To reconstruct CURRENT: To reconstruct CURRENT:
1. Load latest snapshot anchor (if any). 1. Load latest snapshot anchor (if any). If absent, treat
`snapshot_id = 0` and `snapshot.logseq = 0`.
2. Initialize visible segments from that snapshot. 2. Initialize visible segments from that snapshot.
3. Replay all log records with `logseq > snapshot.logseq`. 3. Replay all log records with `logseq > snapshot.logseq`.
4. Apply records in order: 4. Apply records in order:

View file

@ -87,6 +87,10 @@ Excludes:
* Memory residency or caching * Memory residency or caching
* Federation, PEL, or TGK semantics (see `TGK/1` and `TGK/1-CORE`) * Federation, PEL, or TGK semantics (see `TGK/1` and `TGK/1-CORE`)
**ByteStore boundary:** raw byte persistence, COR/1 import/export, and SA/PA
isolation are defined in `BYTESTORE/1` (DDS §11). ASL/STORE-INDEX/1 assumes a
conformant ByteStore beneath the index layer.
--- ---
## 3. Core Concepts ## 3. Core Concepts
@ -144,6 +148,10 @@ as the inclusive `logseq` upper bound defined by `ASL/LOG/1`, not a byte offset
into the log file. Snapshot anchors use their record `logseq` as the snapshot's into the log file. Snapshot anchors use their record `logseq` as the snapshot's
log position. log position.
**Implementation note (identity):** SnapshotID values are store-assigned
`uint64_t` identifiers persisted in `SNAPSHOT_ANCHOR` records. If no snapshot
anchor exists, `SnapshotID` is treated as `0`.
### 3.5 Artifact Location ### 3.5 Artifact Location
* **ArtifactExtent**: `(BlockID, offset, length)` identifying a byte slice within a block. * **ArtifactExtent**: `(BlockID, offset, length)` identifying a byte slice within a block.

View file

@ -1,10 +1,10 @@
# ASL/TGK-EXEC-PLAN/1 -- Unified Execution Plan Semantics # ASL/TGK-EXEC-PLAN/1 -- Unified Execution Plan Semantics
Status: Draft Status: Approved
Owner: Architecture Owner: Architecture
Version: 0.1.0 Version: 1.0.0
SoT: No SoT: Yes
Last Updated: 2025-01-17 Last Updated: 2026-02-21
Linked Phase Pack: N/A Linked Phase Pack: N/A
Tags: [execution, query, tgk, determinism] Tags: [execution, query, tgk, determinism]
@ -82,6 +82,10 @@ Each operator includes:
* `traversal`: optional traversal parameters * `traversal`: optional traversal parameters
* `aggregation`: optional aggregation parameters * `aggregation`: optional aggregation parameters
Snapshot bounds MUST satisfy:
* `logseq_min <= logseq_max`
--- ---
## 2.1 Query Abstraction (Informative) ## 2.1 Query Abstraction (Informative)

83
tier1/bytestore-1.md Normal file
View file

@ -0,0 +1,83 @@
# BYTESTORE/1 — ByteStore API and Persistence Boundary
Status: Draft
Owner: Architecture
Version: 0.1.0
SoT: No
Last Updated: 2026-02-21
Tags: [bytestore, persistence, determinism]
**Document ID:** `BYTESTORE/1`
**Layer:** L0 — Persistence boundary (bytes only)
**Depends on (normative):**
* `DDS` §11 — ByteStore API & Persistence Discipline
* `SRS` FR-BS-001…005 — determinism + durability requirements
* `COR/1` — canonical envelope bytes
* `ICD/1``max_object_size` policy
**Informative references:**
* `ADR-030` — ByteStore Persistence Contract
* `AMDUAT_BYTESTORE` header — `vendor/amduat/include/amduat/bytestore.h`
---
## 1. Purpose
BYTESTORE/1 defines the **code-level API surface** for ByteStore and maps it to
the behavioural requirements already specified in DDS and SRS.
It does not define new semantics; DDS §11 remains the source of truth.
---
## 2. API Surface (Normative)
The following API signatures are required (see `amduat/bytestore.h`):
* `put(payload) -> cid`
* `put_stream(stream) -> cid`
* `import_cor(envelope) -> cid`
* `export_cor(cid) -> envelope`
* `get(cid) -> bytes`
* `stat(cid) -> {present, size}`
* `assert_area_isolation(public_root, secure_root)`
The error codes exposed by the header MUST map to ADR-006 lanes as specified in
DDS §11.7.
---
## 3. Deterministic Identity (Normative)
The CID derivation is fixed:
```
cid = algo_id || H("CAS:OBJ\0" || payload)
```
Implementations MUST use the exact domain separator and hash rules defined in
DDS §11.2 and SRS FR-BS-001.
---
## 4. Persistence Discipline (Normative)
All ByteStore writes MUST follow the fsync ladder from DDS §11.4 and enforce:
* `ICD/1 max_object_size`
* COR/1 import/export byte identity
* SA/PA isolation
* Streaming determinism
---
## 5. Non-Goals
BYTESTORE/1 does not define:
* Physical storage layout
* Replication protocols
* Transport or API gateways

View file

@ -453,6 +453,104 @@ Phase 04 establishes deterministic linkage between FLS/1 envelopes and CRS/1 con
* CRS payload headers MUST match `CRS1` (concept) or `CRR1` (relation) when linkage metadata declares the type ⇒ `ERR_FLS_PAYLOAD_KIND`. * CRS payload headers MUST match `CRS1` (concept) or `CRR1` (relation) when linkage metadata declares the type ⇒ `ERR_FLS_PAYLOAD_KIND`.
* Payloads MAY be CRS/1 concepts or CRR/1 relations; FLS/1 envelopes never mutate CRS graphs. * Payloads MAY be CRS/1 concepts or CRR/1 relations; FLS/1 envelopes never mutate CRS graphs.
##### 7.8.1.1 Session Envelope (Normative)
Session envelopes bind a linkage session to its channel catalog and termination ledger.
**Header:** `MAGIC="FLS1"`, `VERSION=0x01`, `FLAGS=0x00`, `RSV=0x00`.
**TLVs (strict order; no padding):**
| Tag | Field | Type | Card. | Notes |
| ------ | ---------------------- | ------ | ----- | ----- |
| `0x60` | `source_cid` | CID | 1 | Sender surface identity. |
| `0x61` | `target_cid` | CID | 1 | Recipient surface identity. |
| `0x66` | `session_id` | BYTES | 1 | 16-byte opaque session ID. |
| `0x67` | `channel_catalog_cid` | CID | 1 | CID of `FLSC` channel catalog. |
| `0x68` | `termination_ledger_cid` | CID | 0-1 | CID of `FLSL` termination ledger. |
| `0x64` | `timestamp` | UINT64 | 0-1 | Optional bounded timing evidence. |
| `0x65` | `signature` | BYTES | 0-1 | Ed25519 signature over `"AMDUAT:FLS\0" || canonical bytes`. |
**Validation:**
* `session_id` MUST be exactly 16 bytes ⇒ `ERR_FLS_SESSION_ID_LENGTH`.
* `channel_catalog_cid` MUST resolve to an `FLSC` header ⇒ `ERR_FLS_CATALOG_KIND`.
* `termination_ledger_cid` (if present) MUST resolve to an `FLSL` header ⇒ `ERR_FLS_LEDGER_KIND`.
##### 7.8.1.2 Channel Catalog (Normative)
Channel catalogs enumerate message channels and their ordering bounds.
**Header:** `MAGIC="FLSC"`, `VERSION=0x01`, `FLAGS=0x00`, `RSV=0x00`.
**Payload layout:**
```
channel_count : U32
entries : ChannelEntry[channel_count]
ChannelEntry =
channel_id : U32
kind : U16
flags : U16
first_seq : U64
last_seq : U64
```
**Rules:**
* `entries` MUST be sorted by ascending `channel_id``ERR_FLS_CHANNEL_ORDER`.
* `first_seq <= last_seq` for each entry ⇒ `ERR_FLS_CHANNEL_RANGE`.
* `channel_id` MUST be unique ⇒ `ERR_FLS_CHANNEL_DUPLICATE`.
##### 7.8.1.3 Message Envelope (Normative)
Message envelopes bind a payload to a channel and sequence position.
**Header:** `MAGIC="FLSM"`, `VERSION=0x01`, `FLAGS=0x00`, `RSV=0x00`.
**TLVs (strict order; no padding):**
| Tag | Field | Type | Card. | Notes |
| ------ | ------------ | ------ | ----- | ----- |
| `0x66` | `session_id` | BYTES | 1 | 16-byte session ID. |
| `0x69` | `channel_id` | U32 | 1 | Channel identifier. |
| `0x6A` | `seq` | U64 | 1 | Monotonic sequence number within channel. |
| `0x62` | `payload_cid`| CID | 1 | Content payload CID. |
| `0x64` | `timestamp` | UINT64 | 0-1 | Optional bounded timing evidence. |
| `0x65` | `signature` | BYTES | 0-1 | Optional Ed25519 signature. |
**Rules:**
* `session_id` MUST be exactly 16 bytes ⇒ `ERR_FLS_SESSION_ID_LENGTH`.
* `seq` MUST be within the bounds declared in the channel catalog ⇒ `ERR_FLS_SEQ_RANGE`.
##### 7.8.1.4 Termination Ledger (Normative)
Termination ledgers declare channel completion bounds.
**Header:** `MAGIC="FLSL"`, `VERSION=0x01`, `FLAGS=0x00`, `RSV=0x00`.
**Payload layout:**
```
channel_count : U32
entries : TerminationEntry[channel_count]
TerminationEntry =
channel_id : U32
final_seq : U64
reason_code : U16
rsv : U16
closed_at : U64
```
**Rules:**
* `entries` MUST be sorted by ascending `channel_id``ERR_FLS_CHANNEL_ORDER`.
* `final_seq` MUST be ≥ the channels `last_seq` from the catalog ⇒ `ERR_FLS_SEQ_RANGE`.
* `closed_at` MUST be non-zero when a termination entry is present.
#### 7.8.2 CRS/1 Concept & Relation TLVs (Normative) #### 7.8.2 CRS/1 Concept & Relation TLVs (Normative)
> **Scope:** Deterministic CRS/1 byte layout as ratified by ADR-020 v1.1.0. All TLVs > **Scope:** Deterministic CRS/1 byte layout as ratified by ADR-020 v1.1.0. All TLVs

View file

@ -1,10 +1,10 @@
# ENC/ASL-CORE-INDEX/1 — Encoding Specification for ASL Core Index # ENC/ASL-CORE-INDEX/1 — Encoding Specification for ASL Core Index
Status: Draft Status: Approved
Owner: Niklas Rydberg Owner: Niklas Rydberg
Version: 0.1.0 Version: 1.0.0
SoT: No SoT: Yes
Last Updated: 2025-11-16 Last Updated: 2026-02-21
Linked Phase Pack: N/A Linked Phase Pack: N/A
Tags: [encoding, index, deterministic] Tags: [encoding, index, deterministic]

View file

@ -141,6 +141,21 @@ continue replay without failure.
* Unknown `record_type` values are the only exception: they MUST be skipped * Unknown `record_type` values are the only exception: they MUST be skipped
using `payload_len` and MUST NOT break replay determinism. using `payload_len` and MUST NOT break replay determinism.
### 5.1 Log Segment Hashing (Optional)
If a store persists the log in **segments** (multiple files), the following
rules apply:
* **Segment hash:** `segment_hash = SHA-256(segment_bytes)` where
`segment_bytes` are the exact on-disk bytes of that segment, starting at the
`LogHeader` and ending at the final `record_hash`.
* **Hash-chain continuity:** the `prev_record_hash` for the first record in a
segment MUST be:
* all-zeroes for the first segment in a domain, or
* the last record hash of the immediately preceding segment.
* **Segment identity:** `(segment_id, logseq_min, logseq_max, segment_hash)`
is the canonical identity tuple for storage metadata and audit.
--- ---
## 6. Record Type IDs (v1) ## 6. Record Type IDs (v1)

View file

@ -2,9 +2,9 @@
Status: Approved Status: Approved
Owner: Niklas Rydberg Owner: Niklas Rydberg
Version: 1.0.5 Version: 1.0.6
SoT: Yes SoT: Yes
Last Updated: 2025-11-16 Last Updated: 2026-02-21
Linked Phase Pack: N/A Linked Phase Pack: N/A
Tags: [deterministic, binary-minimalism] Tags: [deterministic, binary-minimalism]
@ -16,11 +16,11 @@ Tags: [deterministic, binary-minimalism]
**Depends on (normative):** **Depends on (normative):**
* **ASL/1-CORE v0.4.1** (value model: `Artifact`, `TypeTag`, `Reference`, `HashId`) * **ASL/1-CORE v0.4.2** (value model: `Artifact`, `TypeTag`, `Reference`, `HashId`)
**Integrates with (cross-profile rules):** **Integrates with (cross-profile rules):**
* **HASH/ASL1 v0.2.4** (ASL1 hash family: registry of `HashId → algorithm, digest length`) * **HASH/ASL1 v0.2.5** (ASL1 hash family: registry of `HashId → algorithm, digest length`)
* This profile does **not** depend on HASH/ASL1 to define its layouts. * This profile does **not** depend on HASH/ASL1 to define its layouts.
* When both profiles are implemented, additional cross-checks apply (see §4.4, §5). * When both profiles are implemented, additional cross-checks apply (see §4.4, §5).
@ -424,10 +424,14 @@ For encoding profile `ASL_ENC_CORE_V1`, the canonical rule for constructing `Ref
```text ```text
ArtifactBytes = encode_artifact_core_v1(A) ArtifactBytes = encode_artifact_core_v1(A)
digest = H(ArtifactBytes) digest = H("AMDUAT:ASL1:ART\0" || ArtifactBytes)
Reference = { hash_id = HID, digest = digest } Reference = { hash_id = HID, digest = digest }
``` ```
The domain separator is the ASCII byte sequence `AMDUAT:ASL1:ART` followed by a
single `0x00` terminator (16 bytes total). The separator MUST be present
verbatim and MUST NOT be modified or omitted.
where: where:
* `A` is an `Artifact` (ASL/1-CORE), * `A` is an `Artifact` (ASL/1-CORE),
@ -461,7 +465,7 @@ If two implementations:
then for any `Artifact A` they MUST: then for any `Artifact A` they MUST:
* produce identical `ArtifactBytes = encode_artifact_core_v1(A)`, * produce identical `ArtifactBytes = encode_artifact_core_v1(A)`,
* produce identical `digest = H(ArtifactBytes)`, * produce identical `digest = H("AMDUAT:ASL1:ART\0" || ArtifactBytes)`,
* produce identical `Reference` and `ReferenceBytes = encode_reference_core_v1(Reference)`. * produce identical `Reference` and `ReferenceBytes = encode_reference_core_v1(Reference)`.
This is the determinism foundation used by ASL/1-STORE, PEL/1, FER/1, and FCT/1. This is the determinism foundation used by ASL/1-STORE, PEL/1, FER/1, and FCT/1.
@ -506,10 +510,10 @@ Canonical `ArtifactBytes`:
00 0000000000000002 DEAD 00 0000000000000002 DEAD
``` ```
Digest with `HASH-ASL1-256` (SHA-256): Digest with `HASH-ASL1-256` (SHA-256) and domain separation:
```text ```text
digest = SHA-256(00 0000000000000002 DEAD) digest = SHA-256("AMDUAT:ASL1:ART\0" || 00 0000000000000002 DEAD)
``` ```
Assuming `HashId = 0001` for `HASH-ASL1-256`, the `ReferenceBytes` are: Assuming `HashId = 0001` for `HASH-ASL1-256`, the `ReferenceBytes` are:
@ -555,7 +559,7 @@ Hashing and `ReferenceBytes` proceed as in §6.1.
## 7. Conformance ## 7. Conformance
An implementation conforms to `ENC/ASL1-CORE v1.0.5` if and only if it: An implementation conforms to `ENC/ASL1-CORE v1.0.6` if and only if it:
1. **Correctly encodes and decodes Artifacts** 1. **Correctly encodes and decodes Artifacts**
@ -573,7 +577,7 @@ An implementation conforms to `ENC/ASL1-CORE v1.0.5` if and only if it:
3. **Implements canonical hashing correctly** 3. **Implements canonical hashing correctly**
* Uses `ArtifactBytes` from `encode_artifact_core_v1` as the **only** input to ASL1 hash functions when deriving `Reference`s under this profile. * Uses the ASL/1-CORE domain separator (`"AMDUAT:ASL1:ART\0"`) plus `ArtifactBytes` from `encode_artifact_core_v1` as the **only** input to ASL1 hash functions when deriving `Reference`s under this profile.
* Computes `Reference` via the canonical rule in §5.1. * Computes `Reference` via the canonical rule in §5.1.
* Does not derive `Reference`s from non-canonical or alternative encodings in contexts that claim to use `ASL_ENC_CORE_V1`. * Does not derive `Reference`s from non-canonical or alternative encodings in contexts that claim to use `ASL_ENC_CORE_V1`.
@ -595,4 +599,5 @@ Everything else — transport, storage layout, replication, indexing, overlays,
## Document History ## Document History
* **1.0.6 (2026-02-21):** Added ASL/1-CORE identity domain separation to the reference derivation rule and updated registry metadata.
* **1.0.5 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline. * **1.0.5 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline.

95
tier1/fcs-1.md Normal file
View file

@ -0,0 +1,95 @@
# FCS/1 — Function Composition Schema (Execution-Only)
Status: Approved
Owner: Architecture
Version: 1.0.0
SoT: Yes
Last Updated: 2026-02-21
Linked Phase Pack: PH07
Tags: [execution, descriptor, deterministic]
**Document ID:** `FCS/1`
**Layer:** L2 — Function descriptor schema for deterministic execution
**Depends on (normative):**
* `DDS` — canonical FCS/1 v1-min TLV layout and validation rules
* `PCB1` — parameter block encoding rules (DDS §7.3)
* `ASL/1-CORE v0.4.x` — logical value model (`Artifact`, `Reference`)
---
## 0. Purpose & Scope
`FCS/1` defines the **execution-only** descriptor schema for composite and
custom functions. It explicitly excludes policy, intent, scope, authority, and
other governance metadata (those belong in `FCT/1`).
`FCS/1` descriptors are immutable and content-addressed; identical descriptors
MUST hash to identical CIDs.
---
## 1. Descriptor Model (logical)
An `FCS/1` descriptor captures the deterministic execution recipe:
```
FCSDescriptor {
function_ptr : CID // FPS/1 primitive or nested FCS/1 descriptor
parameter_block : CID // PCB1 parameter block
arity : uint32
}
```
* `function_ptr` MAY reference another `FCS/1` descriptor (composition), or a
primitive function descriptor (e.g., `FPS/1`).
* `parameter_block` MUST reference a valid PCB1 payload.
* `arity` defines the expected number of input slots (must match PCB1).
---
## 2. Canonical Encoding (v1-min)
The canonical byte encoding for `FCS/1` descriptors is defined by DDS §7.2:
* COR/1 framing with strict TLV order.
* Required TLVs only: `function_ptr (0x30)`, `parameter_block (0x31)`,
`arity (0x32)`.
No other TLVs are permitted in v1-min.
---
## 3. Validation Rules
Implementations MUST reject descriptors that violate any of the following:
1. TLVs are not in strict ascending order, or appear more than once.
2. `parameter_block` does not decode as a valid PCB1 envelope.
3. `arity` does not match PCB1 slot manifest length.
4. The descriptor graph (following nested `function_ptr` references) is cyclic.
5. Any legacy or unknown governance TLV appears.
Error mapping follows DDS (e.g., `ERR_FCS_TAG_ORDER`, `ERR_FCS_PARAMETER_FORMAT`,
`ERR_PCB_ARITY_MISMATCH`, `ERR_FCS_CYCLE_DETECTED`, `ERR_FCS_UNKNOWN_TAG`).
---
## 4. Composition Semantics
* Composition is **pure**: a descriptor that references another `FCS/1`
descriptor inherits its deterministic execution recipe.
* The composed descriptor graph MUST be acyclic.
* Registries MUST admit only canonical, validated `FCS/1` descriptors.
---
## 5. Conformance
An implementation is **FCS/1conformant** if it:
* Accepts only v1-min canonical encodings.
* Enforces the validation rules in §3.
* Treats descriptors as immutable and content-addressed.
* Rejects any descriptor with policy/intent metadata embedded.

183
tier1/fer-1.md Normal file
View file

@ -0,0 +1,183 @@
# FER/1 — Execution Receipts and Evidence (Core Semantics)
Status: Draft
Owner: Architecture
Version: 0.1.0
SoT: Yes
Last Updated: 2026-02-21
Linked Phase Pack: PH07
Tags: [evidence, receipt, deterministic, execution]
**Document ID:** `FER/1`
**Layer:** L2 — Evidence receipts over PEL/1 execution
**Depends on (normative):**
* `ASL/1-CORE v0.4.x` — value model (`Artifact`, `Reference`)
* `PEL/1-SURF v0.2.x` — surface execution results
* `TGK/1-CORE v0.7.x` — trace graph kernel
* `ENC/FER1-RECEIPT/1` — canonical receipt encoding
* `DDS` (FER/1 layout and validation rules)
---
## 0. Purpose & Scope
`FER/1` defines **receipt semantics** for deterministic execution evidence. It
standardizes the logical fields of execution receipts and the minimal execution
artifacts that higher layers can rely on, while delegating concrete encodings to
`ENC/FER1-RECEIPT/1` and DDS.
`FER/1` does **not** define policy, intent, authority, or certification rules
(those belong to `FCT/1` and higher layers).
---
## 1. Core Concepts
### 1.1 Execution Artifacts (logical schemas)
`FER/1` uses four logical artifact shapes. Each is an ASL/1 `Artifact` whose
encoding is profile-defined (not specified here).
**ExecutionIntent** — records the inputs and environment for a run:
```
ExecutionIntent {
function_ref : Reference // FCS/1 descriptor or equivalent
program_ref : Reference // program artifact (scheme-specific)
input_manifest_ref : Reference // GS/1 manifest (ordered, deduped)
params_ref : optional Reference
snapshot_id : uint64 // snapshot identity (0 if absent)
execution_config_ref : optional Reference // snapshot-scoped config
environment_ref : Reference // environment capsule (ICD/1 / PH03)
}
```
**ExecutionOutput** — binds outputs to a run (used when outputs are multiple or
need explicit naming):
```
ExecutionOutput {
output_refs : list<Reference> // ordered
result_ref : Reference // surface ExecutionResult artifact
trace_ref : optional Reference
}
```
**ExecutionReceipt** — evidence of a run (canonical encoding in
`ENC/FER1-RECEIPT/1`):
```
ExecutionReceipt {
function_ref : Reference
input_manifest_ref : Reference
environment_ref : Reference
evaluator_id : bytes
output_ref : Reference
executor_refs : list<Reference>
parity_entries : list<ParityEntry>
started_at : uint64
completed_at : uint64
// optional fields per FER/1 v1.1 (DDS): executor_fingerprint, logs,
// determinism_level, rng_seed, limits, parent, context, witnesses, run_id,
// signature, signature_ext
}
```
**ExecutionObservation** — optional, typed evidence produced alongside a run:
```
ExecutionObservation {
kind : enum // e.g. stderr, stdout, metrics, trace
payload_ref : Reference // artifact containing the observation payload
payload_sha256 : bytes[32]
}
```
### 1.2 Program and input conventions
* `function_ref` MUST reference the **function descriptor** used for evaluation
(e.g., FCS/1 descriptor).
* `program_ref` MUST reference the program artifact used by that descriptor.
* `input_manifest_ref` MUST reference a **canonical manifest artifact** that
enumerates inputs in deterministic order.
* `params_ref`, when present, MUST be an immutable artifact included in the
input manifest.
### 1.3 Snapshot binding
All receipts are **snapshot-bound**:
* `snapshot_id` in the intent MUST be the snapshot identity used for input
resolution (0 when absent).
* Receipts and outputs are interpreted relative to that snapshot.
---
## 2. Output Reference Fallback
`ExecutionReceipt.output_ref` is a single reference. The value MUST resolve as:
1. The sole output artifact, **if the run produces exactly one output**; or
2. An `ExecutionOutput` artifact when there are multiple outputs; or
3. The surface `ExecutionResult` artifact, **only** if outputs are empty and
the result is the only durable artifact.
This rule keeps receipts stable while supporting multi-output runs and
zero-output executions.
---
## 3. TGK Traceability Edges (FER/1 profile)
When ingesting receipts, a `TGK/FER/1` profile SHOULD emit traceability edges
using TGK edge artifacts. Edge type assignments are defined by the profile; the
semantics are:
* `EDGE_EXECUTION`: `program_ref -> output_ref`
* `EDGE_CONSUMED_BY`: `input_ref -> output_ref`
* `EDGE_PRODUCED_BY`: `output_ref -> receipt_ref`
* `EDGE_REALIZES`: `receipt_ref -> function_ref`
* `EDGE_OBSERVED_BY`: `output_ref -> observation_ref` (optional)
Edges MUST be derivable solely from receipt content and referenced artifacts.
---
## 4. Determinism & Validation (FER/1 v1.1)
`FER/1` v1.1 receipts are **parity-first** and deterministic. Implementations
MUST enforce the DDS validation rules, including:
* Strict TLV order and no unknown tags.
* `executor_set` and `parity_vector` ordered byte-lexicographically and aligned.
* `parity_entries[i].output_ref` equals the top-level `output_ref`.
* `started_at <= completed_at` and monotonic time envelope.
* Determinism level defaults to `D1_bit_exact`; any other level requires an
explicit `rng_seed`.
* `run_id`, when present, MUST equal the deterministic hash over
`{function_ref, input_manifest_ref, environment_ref, executor_fingerprint}`.
* Receipts MUST NOT encode policy, intent, or authority metadata.
The canonical binary layout for receipts is defined in
`ENC/FER1-RECEIPT/1` and DDS.
---
## 5. Interactions
* `PEL/1-SURF` provides the surface `ExecutionResult` artifact referenced by
`ExecutionOutput.result_ref`.
* `FCT/1` binds policy, authority, and certification to one or more FER/1
receipts.
---
## 6. Conformance
An implementation is **FER/1conformant** if it:
* Produces receipts that validate under `ENC/FER1-RECEIPT/1` and DDS rules.
* Uses the output reference fallback rules in §2.
* Emits TGK edges (when enabled) that are derivable from receipt content.

137
tier1/fps-1.md Normal file
View file

@ -0,0 +1,137 @@
# FPS/1 — Functional Primitive Surface
Status: Approved
Owner: Architecture
Version: 1.0.0
SoT: Yes
Last Updated: 2026-02-21
Tags: [primitives, determinism, bytes]
**Document ID:** `FPS/1`
**Layer:** L1 — Deterministic functional primitives over payload bytes
**Depends on (normative):**
* `ASL/1-CORE``Artifact`, `Reference`, hash identity
* `HASH/ASL1` — hash family registry
* `ADR-006` — deterministic error semantics
* `ADR-015` — CAS rejection governance (alignment)
**Informative references:**
* `SRS` §12 — Functional Primitive Surface summary
---
## 0. Conventions
The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHOULD**, and **MAY** are
to be interpreted as in RFC 2119.
FPS/1 defines **pure, deterministic** operations over canonical byte payloads.
The primitives are functional: identical inputs MUST produce identical outputs.
---
## 1. CID Derivation
All primitives produce exactly one output payload and one CID.
The CID for a payload is derived as:
```
cid = algo_id || H("CAS:OBJ\0" || payload)
```
* `algo_id` is an `ASL/1-CORE` hash identifier.
* For v1, `algo_id` defaults to `HASH/ASL1` SHA-256.
* The domain separator **MUST** be exactly `"CAS:OBJ\0"`.
---
## 2. Primitives (Normative)
### 2.1 put
```
put(payload_bytes) -> CID
```
Canonical write; identity only. Storage durability is out of scope here.
Errors: `ERR_IO_FAILURE`, `ERR_NORMALIZATION`
### 2.2 get
```
get(CID) -> payload_bytes
```
Fetch canonical bytes from a conformant store.
Errors: `ERR_CID_NOT_FOUND`
### 2.3 slice
```
slice(CID, offset, length) -> CID
```
Extract a contiguous byte range.
Errors: `ERR_SLICE_RANGE`
### 2.4 concatenate
```
concatenate([CID1, ..., CIDn]) -> CID
```
Join payloads in order.
Errors: `ERR_EMPTY_INPUTS`
### 2.5 reverse
```
reverse(CID, level) -> CID
```
Reverse payload order at a specified granularity:
* `bit` — reverse all bits in the payload
* `byte` — reverse byte order
* `word` — reverse 2-byte units
* `long` — reverse 4-byte units
Errors: `ERR_REV_ALIGNMENT`, `ERR_INVALID_LEVEL`
### 2.6 splice
```
splice(CID_a, offset, CID_b) -> CID
```
Insert payload `b` into `a` at `offset`.
Errors: `ERR_SPLICE_RANGE`
---
## 3. Determinism & Closure
* Identical inputs MUST produce identical outputs.
* Inputs are immutable.
* Outputs MUST be valid inputs to any FPS/1 primitive.
---
## 4. Conformance Vectors
The canonical vectors live in:
```
/amduat/tests/fixtures/conformance/fps_1/
```
Vectors are required for each primitive and include output bytes and CID.

203
tier1/ms-1.md Normal file
View file

@ -0,0 +1,203 @@
# MS/1 — Concept Mapping Surface
Status: Draft
Owner: Architecture
Version: 0.1.0
SoT: No
Last Updated: 2026-02-21
Tags: [mapping, concepts, determinism]
**Document ID:** `MS/1`
**Layer:** L2 — Deterministic concept-to-data mapping surface
**Depends on (normative):**
* `ASL/1-CORE``Artifact`, `Reference`, TypeTag identity
* `TGK/1` — concept nodes and predicate edges
* `ADR-003` — canonical encoding discipline (BCF/1)
* `ADR-006` — deterministic error semantics
**Informative references:**
* `amduatd-api-endpoint-spec.target.md` §4.12 (MS/1 mapping surface target)
---
## 0. Conventions
The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHOULD**, and **MAY** are
to be interpreted as in RFC 2119.
MS/1 defines the **deterministic mapping** from `(Concept, ContextFrame)` to
outputs. It does not define transport or API endpoints.
---
## 1. Core Concepts
### 1.1 Concept
Concepts are graph nodes (TGK) with stable references.
### 1.2 Context Frame
A **Context Frame** is a canonical, content-addressed artifact encoding the
bindings needed to execute a concept mapping.
Logical model:
```
ContextFrame = {
bindings: Map<ConceptRef, Value>
predicates: Map<PredicateRef, Value> // optional
}
```
`Value` is one of:
* `DataRef` — an `ASL/1-CORE` reference to data bytes
* `Scalar` — a canonical scalar (`bool`, `int`, `float`, `string`, `null`)
### 1.3 Mapping Result
`ms_map` returns exactly one of:
* `produced` — a canonical output artifact
* `gap` — missing required bindings
* `ambiguity` — multiple admissible bindings
---
## 2. Context Frame Canonical Encoding (Normative)
Context Frames MUST be encoded deterministically as a canonical BCF/1 map:
```
{
"bindings": { <RefBytesHex> : <Value> ... },
"predicates": { <RefBytesHex> : <Value> ... } // optional
}
```
Rules:
1. Keys are `ReferenceBytes` hex (lowercase, no `0x`), sorted
byte-lexicographically by raw `ReferenceBytes`.
2. `Value` encoding is canonical BCF/1:
* `DataRef` is encoded as `{"ref": <RefBytesHex>}`.
* `Scalar` is encoded as the canonical scalar value.
3. No duplicate keys.
4. Missing `predicates` is equivalent to an empty map.
The resulting bytes are content-addressed; the `ContextFrame` CID is the
identity of the canonical bytes.
Context Frame artifacts MUST use `type_tag=amduat.context_frame`.
---
## 3. Mapping Entrypoint (Normative)
```
ms_map(concept_ref, context_frame, attachments?, concept_inputs?) -> Result
```
### 3.1 Effective Frame
The **effective** Context Frame is the canonical merge of:
1. `context_frame` bindings
2. `attachments` (data refs)
3. `concept_inputs` (concept refs)
Rules:
* `attachments` are added as bindings under their declared concept keys.
* `concept_inputs` are added as bindings where the value is a concept ref.
* Conflicts (same key, different value) MUST yield `ambiguity`.
### 3.2 Required Keys
A concept MAY declare required bindings via TGK edges:
* `ms.requires_key``concept_ref -> key_concept_ref`
* `ms.requires_predicate``concept_ref -> predicate_concept_ref`
Rules:
* Missing required `ms.requires_key` bindings MUST yield `gap`.
* Missing required `ms.requires_predicate` bindings MUST yield `gap`.
### 3.3 Result Determination
Given a concept and effective frame:
1. If any required binding is missing ⇒ return `gap`.
2. If any required binding is present with multiple admissible values ⇒ return
`ambiguity`.
3. Otherwise, execute deterministically and return `produced`.
All outcomes MUST be deterministic for identical inputs.
---
## 4. Gap & Ambiguity Artifacts (Normative)
Gap and Ambiguity are **first-class artifacts** stored for audit.
Canonical payloads (BCF/1 map, deterministic ordering):
### 4.1 Gap
```
{
"concept_ref": <RefBytesHex>,
"context_ref": <RefBytesHex>,
"missing_keys": [<RefBytesHex>...],
"missing_predicates": [<RefBytesHex>...]
}
```
### 4.2 Ambiguity
```
{
"concept_ref": <RefBytesHex>,
"context_ref": <RefBytesHex>,
"ambiguous_keys": [
{ "key": <RefBytesHex>, "candidates": [<RefBytesHex>...] }
]
}
```
* Lists are sorted by key, then candidate bytes.
* Missing arrays are encoded as empty arrays.
Type tags:
* Gap: `amduat.ms.gap`
* Ambiguity: `amduat.ms.ambiguity`
---
## 5. Deterministic Error Codes (ADR-006)
Errors MUST be mapped to deterministic codes:
* `ERR_MS_INVALID_FRAME` — non-canonical or malformed Context Frame.
* `ERR_MS_KEY_MISSING` — required key absent (Gap).
* `ERR_MS_PREDICATE_MISSING` — required predicate absent (Gap).
* `ERR_MS_AMBIGUOUS` — multiple admissible bindings (Ambiguity).
* `ERR_MS_UNSUPPORTED` — unsupported scalar or value kind.
These errors MUST NOT depend on runtime ordering or external state.
---
## 6. Non-Goals
MS/1 does not define:
* Transport or HTTP APIs
* Execution engines or optimizers
* Concept graph schemas beyond required key/predicate edges

82
tier1/pcb1.md Normal file
View file

@ -0,0 +1,82 @@
# PCB1 — Parameter Block Encoding (FCS/1)
Status: Approved
Owner: Architecture
Version: 1.0.0
SoT: Yes
Last Updated: 2026-02-21
Linked Phase Pack: PH07
Tags: [execution, parameters, deterministic]
**Document ID:** `PCB1`
**Layer:** L2 — Parameter block encoding for FCS/1 descriptors
**Depends on (normative):**
* `DDS` — PCB1 envelope and TLV layout (DDS §7.3)
* `ASL/1-CORE v0.4.x` — logical value model
---
## 0. Purpose & Scope
`PCB1` defines a **deterministic, canonical** encoding for parameter blocks used
by `FCS/1` descriptors. It standardizes slot manifests, slot data ordering, and
arity validation.
---
## 1. Logical Model
A PCB1 parameter block is a two-part payload:
```
PCB1 {
slot_manifest : list<Slot>
slot_data : bytes
}
Slot {
index : uint32
name : string
type : enum
digest : bytes[32]
}
```
Slots MUST appear in ascending `index` order. The `slot_data` is a packed byte
sequence that respects the manifest ordering.
---
## 2. Canonical Encoding
The canonical byte encoding is defined by DDS §7.3:
* COR/1 envelope with `MAGIC="PCB1"`, `VERSION=0x01`, `FLAGS=0x00`.
* Strict TLV order; no padding.
* Required TLVs:
* `slot_manifest (0x50)` — BCF/1 list of slot descriptors.
* `slot_data (0x51)` — packed bytes aligned with the manifest.
---
## 3. Validation Rules
Implementations MUST reject PCB1 payloads that violate any of the following:
1. TLVs are not in strict ascending order, or appear more than once.
2. `slot_manifest` entries are not sorted by `index`.
3. Slot digest mismatches (`ERR_PCB_DIGEST_MISMATCH`).
4. Non-deterministic or duplicated manifest ordering (`ERR_PCB_MANIFEST_ORDER`).
5. Arity mismatch between PCB1 slot count and the enclosing FCS/1 descriptor
(`ERR_PCB_ARITY_MISMATCH`).
---
## 4. Conformance
An implementation is **PCB1conformant** if it:
* Emits the exact canonical encoding in DDS §7.3.
* Enforces the validation rules in §3.

View file

@ -186,6 +186,27 @@ as defined in `ASL/1-STORE`. The store MAY expose more operations, but `PEL/1-SU
All `program_ref`, `input_refs`, `params_ref`, and `result_ref` values are interpreted relative to the `StoreConfig` used by the StoreInstance. All `program_ref`, `input_refs`, `params_ref`, and `result_ref` values are interpreted relative to the `StoreConfig` used by the StoreInstance.
### 2.3 ExecutionContext (metadata surface)
`PEL/1-SURF` execution is snapshot-bound. The **execution context metadata**
surface is intentionally minimal to preserve determinism:
```text
ExecutionContext {
snapshot_id : uint64 // opaque store-assigned snapshot ID
execution_config_ref : optional Reference // immutable config artifact
}
```
Rules:
* `snapshot_id` is the snapshot identity associated with the execution. If no
snapshot exists, callers MUST use `0`.
* `execution_config_ref`, when present, MUST refer to an immutable Artifact that
is **snapshot-scoped** (its meaning is fixed for the snapshot) and **deterministic**.
* The context is **metadata only** and MUST NOT change the semantics of `Exec_s`
beyond selecting deterministic, snapshot-scoped configuration.
--- ---
## 3. Surface Execution Model ## 3. Surface Execution Model
@ -209,7 +230,22 @@ run(
All references are interpreted relative to `store`s `StoreConfig`. All references are interpreted relative to `store`s `StoreConfig`.
### 3.2 Relationship to PEL/1-CORE ### 3.2 Determinism and context constraints
To preserve determinism across replay and federation:
* PEL execution MUST depend only on:
* the resolved Artifacts for `program_ref`, `input_refs`, and `params_ref`,
* the `ExecutionContext.snapshot_id`, and
* the Artifact referenced by `execution_config_ref` (if provided).
* PEL execution MUST NOT read storage or layout metadata, including but not
limited to: block IDs, segment IDs, physical offsets, storage stats, GC state,
or backend-specific layout details.
* Implementations MUST NOT call store APIs beyond `get` and `put` to derive
execution semantics. Index scans, log reads, or layout introspection are
forbidden for `PEL/1-SURF` determinism.
### 3.3 Relationship to PEL/1-CORE
For a supported scheme identified by `scheme_ref`, there exists a pure function `Exec_s` per `PEL/1-CORE`: For a supported scheme identified by `scheme_ref`, there exists a pure function `Exec_s` per `PEL/1-CORE`:

View file

@ -0,0 +1,99 @@
# PEL/OUTPUT-POLICY/1 — Materialization & Promotion Policy
Status: Draft
Owner: Architecture
Version: 0.1.0
SoT: No
Last Updated: 2026-02-21
Tags: [pel, outputs, promotion, retention]
**Document ID:** `PEL/OUTPUT-POLICY/1`
**Layer:** L2 — Output handling policy (no execution semantics)
**Depends on (normative):**
* `PEL/1-CORE` — execution semantics
* `ASL/LOG/1` — visibility and log ordering
* `ASL/1-CORE-INDEX` — visibility and tombstone semantics
**Informative references:**
* `ASL/SYSTEM/1` — promotion/publication/retention separation
---
## 0. Conventions
The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHOULD**, and **MAY** are
to be interpreted as in RFC 2119.
This document defines **policy** for handling PEL outputs. It does not change
PEL execution semantics.
---
## 1. Policy Dimensions
Each output produced by PEL is subject to three independent policy decisions:
### 1.1 Materialization
Controls whether output bytes are stored, cached, or virtualized.
* `store` — persist in the content index (authoritative).
* `cache` — persist in materialization cache only (performance hint).
* `virtualize` — compute in-memory only; do not persist.
### 1.2 Promotion
Controls whether an output is promoted from a derived view to **truth**.
* `promote` — output becomes eligible for truth/published workflows.
* `no_promote` — output remains derived and must not be treated as truth.
Promotion is **explicit**; it MUST NOT be inferred from execution.
### 1.3 Publication
Controls visibility (internal vs published) encoded in index metadata.
* `internal` — visible only to the local domain.
* `published` — visible to federation/replay.
Publication is distinct from promotion; a promoted output may still be internal.
---
## 2. Policy Hooks (Normative)
Core implementations MUST expose a policy hook that can be invoked **per
output** to decide the three dimensions above.
The hook MUST be deterministic and MUST depend only on:
* the execution inputs (program, params, inputs, snapshot state)
* explicit policy configuration supplied by the caller
The hook MUST NOT read storage layout or non-snapshot metadata.
---
## 3. Default Policy
If no hook is supplied, the default policy is:
* `materialization = store`
* `promotion = no_promote`
* `publication = internal`
---
## 4. API Layer Mapping (Informative)
API layers should expose explicit controls for:
* promotion (`publish` / `promote` flags)
* visibility (`internal` vs `published`)
* materialization (`store` vs `cache` vs `virtualize`)
The API MUST NOT implicitly promote outputs without an explicit policy choice.

View file

@ -364,6 +364,23 @@ At the ASL/1 layer:
This scheme assumes such an encoding exists and is canonical; it does not define the layout itself. This scheme assumes such an encoding exists and is canonical; it does not define the layout itself.
### 2.8 ProgramDescriptor (explicit executable artifacts)
To prevent **implicit execution** of non-program artifacts (including receipts),
this scheme defines **ProgramDescriptor** semantics:
* Only **explicitly tagged Program Artifacts** are executable.
* An artifact is executable **only if** its `type_tag` equals
`TYPE_TAG_PEL_PROGRAM_DAG_1` and its `bytes` decode as a `Program` under
`ENC/PEL-PROGRAM-DAG/1`.
* Artifacts with any other `type_tag` (including receipt or evidence tags such
as `TYPE_TAG_FER1_RECEIPT_1`) **MUST NOT** be interpreted as programs, even if
their bytes happen to be decodable as a `Program`.
* Artifacts with **no** `type_tag` are **not** executable.
These rules ensure that only explicitly designated program artifacts can be
executed, eliminating accidental execution of receipt payloads or other data.
--- ---
## 3. Structural Validity ## 3. Structural Validity

View file

@ -326,6 +326,28 @@ coverage `TV-MPR-001…003` (hash triple, missing weights, signature domain) and
`TV-IER-001…004` (ok, missing seed, fingerprint mismatch, log digest mismatch) `TV-IER-001…004` (ok, missing seed, fingerprint mismatch, log digest mismatch)
respectively with scenario summaries in accompanying `summary.md` files. respectively with scenario summaries in accompanying `summary.md` files.
### FR-031A FLS/1 Ordering & Error Semantics
FLS/1 envelopes and payloads MUST follow deterministic ordering and reject
malformed linkage metadata. Implementations SHALL:
* Enforce strict TLV ordering and reject duplicates per DDS §7.8.1.
* Enforce channel catalog ordering (`channel_id` ascending) and uniqueness.
* Enforce message sequence bounds against the channel catalog.
* Enforce termination ledger ordering and bounds.
Violations MUST map to ADR-006 error codes (non-exhaustive):
* `ERR_FLS_TAG_ORDER`, `ERR_FLS_DUPLICATE_TAG`, `ERR_FLS_UNKNOWN_TAG`
* `ERR_FLS_CID_LENGTH`, `ERR_FLS_TIMESTAMP_LENGTH`
* `ERR_FLS_SESSION_ID_LENGTH`, `ERR_FLS_CHANNEL_ORDER`, `ERR_FLS_CHANNEL_DUPLICATE`
* `ERR_FLS_CHANNEL_RANGE`, `ERR_FLS_SEQ_RANGE`
* `ERR_FLS_SIGNATURE_DOMAIN`, `ERR_FLS_SIGNATURE_LENGTH`, `ERR_FLS_SIGNATURE`
* `ERR_FLS_PAYLOAD_CID_MISMATCH`, `ERR_FLS_PAYLOAD_KIND`
Deterministic ordering and error mapping are required for replay stability and
federated transport interoperability.
### FR-032 CT/1 Deterministic Replay (D1) ### FR-032 CT/1 Deterministic Replay (D1)
Given identical AC/1 + DTF/1 + topology inputs, executing the runtime twice in Given identical AC/1 + DTF/1 + topology inputs, executing the runtime twice in
@ -475,6 +497,15 @@ Errors MUST be emitted as stable symbolic codes including but not limited to:
* Requirements link to tests/defects in Phase Packs * Requirements link to tests/defects in Phase Packs
* ADRs reference affected FR/NFR IDs * ADRs reference affected FR/NFR IDs
### 10.1 Traceability Register (PH04)
Traceability register entries anchor evidence and lineage tracking for Phase 04
surfaces.
| Entry ID | Surface | Phase | Evidence Path | Notes |
| --- | --- | --- | --- | --- |
| PH04-TR-FLS-001 | FLS/1 | PH04 | `/amduat/logs/ph04/evidence/fls1/PH04-EV-FLS-001/` | FLS/1 linkage envelopes + session/message ordering + error mapping coverage. |
--- ---
## 11. Future Phases ## 11. Future Phases

View file

@ -28,10 +28,15 @@ Tags: [traceability, execution]
* `PEL/1-SURF v0.2.x` — store-backed execution surface (producer of many EdgeArtifacts) * `PEL/1-SURF v0.2.x` — store-backed execution surface (producer of many EdgeArtifacts)
* `CIL/1`, `FER/1`, `FCT/1`, `OI/1` — provenance and fact profiles that consume graph queries * `CIL/1`, `FER/1`, `FCT/1`, `OI/1` — provenance and fact profiles that consume graph queries
* (future) `TGK/PROV/1` — provenance / trace operators over `ProvenanceGraph` * (future) `TGK/PROV/1` — provenance / trace operators over `ProvenanceGraph`
* `ADR-031` — ASL + TGK index separation boundary
> **Normativity note** > **Normativity note**
> `TGK/STORE/1` is a near-core **graph store profile**, not a kernel surface. It introduces no new identity scheme. It defines how to expose and query the `ProvenanceGraph` from `TGK/1-CORE` over Artifacts reachable via `ASL/1-STORE` or equivalent feeds, and constrains graph indexes and query semantics. > `TGK/STORE/1` is a near-core **graph store profile**, not a kernel surface. It introduces no new identity scheme. It defines how to expose and query the `ProvenanceGraph` from `TGK/1-CORE` over Artifacts reachable via `ASL/1-STORE` or equivalent feeds, and constrains graph indexes and query semantics.
> **Index separation note**
> `TGK/STORE/1` assumes **separate** ASL and TGK indexes with explicit
> cross-layer invariants (ADR-031). A merged index is intentionally rejected.
© 2025 Niklas Rydberg. © 2025 Niklas Rydberg.
## License ## License
@ -115,6 +120,23 @@ In this document:
* **ArtifactRef**, **Node**, and **EdgeRef** are all ASL/1 `Reference` values. * **ArtifactRef**, **Node**, and **EdgeRef** are all ASL/1 `Reference` values.
* **ExecutionEnvironment** is as in `TGK/1-CORE`: an abstract context with a finite Artifact set and a fixed TGK profile set “in effect” at a snapshot. * **ExecutionEnvironment** is as in `TGK/1-CORE`: an abstract context with a finite Artifact set and a fixed TGK profile set “in effect” at a snapshot.
---
## 0.1 ASL + TGK Index Boundary (Normative)
TGK indexes are **derived** from ASL visibility and MUST respect the following
invariants (see ADR-031):
1. TGK indexes MUST only include artifacts visible under ASL replay at the
same `(snapshot_id, log_position)`.
2. TGK indexes MUST enforce ASL tombstones and tombstone lifts by `logseq`.
3. TGK indexes MUST NOT introduce new identity schemes; all nodes/edges are
ASL `Reference` values.
4. TGK index updates MUST be deterministically derived from ASL-admitted
segments and log records. No side-channel writes.
5. Replaying ASL log + sealed segments MUST yield the same TGK index state
(up to encoding) as an online build.
Additional terms introduced here: Additional terms introduced here:
```text ```text