Compare commits

..

No commits in common. "main" and "federation" have entirely different histories.

108 changed files with 329 additions and 17719 deletions

2
.gitignore vendored
View file

@ -16,5 +16,3 @@ Testing/
.DS_Store
tmp/
.amduat-asl
.codex
.claude

View file

@ -58,7 +58,6 @@ set(AMDUAT_UTIL_SRCS
src/internal/endian.c
src/internal/hex.c
src/internal/log.c
src/internal/string.c
)
set(AMDUAT_ASL_SRCS
@ -66,21 +65,16 @@ set(AMDUAT_ASL_SRCS
src/near_core/asl/collection_view.c
src/near_core/asl/none.c
src/near_core/asl/artifact_io.c
src/near_core/asl/identity.c
src/near_core/asl/io.c
src/near_core/asl/index_accel.c
src/near_core/asl/index_bloom.c
src/near_core/asl/index_snapshot.c
src/near_core/asl/index_replay.c
src/near_core/asl/log_cursor.c
src/near_core/asl/parse.c
src/near_core/asl/projection_checkpoint.c
src/near_core/asl/ref_io.c
src/near_core/asl/store.c
src/near_core/asl/ref_text.c
src/near_core/asl/registry.c
src/near_core/bytestore.c
src/near_core/fps.c
)
set(AMDUAT_ASL_LOG_STORE_SRCS
@ -109,8 +103,6 @@ set(AMDUAT_ENC_SRCS
src/near_core/enc/asl_log.c
src/near_core/enc/asl_core_index.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_desc.c
src/near_core/enc/pel_trace_dag.c
@ -165,11 +157,6 @@ set(AMDUAT_ASL_STORE_INDEX_FS_SRCS
src/adapters/asl_store_index_fs/asl_store_index_fs_layout.c
)
set(AMDUAT_ASL_STORE_MINIO_SRCS
src/adapters/asl_store_minio/asl_store_minio.c
src/adapters/asl_store_minio/s3_sigv4.c
)
set(AMDUAT_ASL_DERIVATION_INDEX_FS_SRCS
src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c
)
@ -226,10 +213,6 @@ amduat_add_lib(asl_store_index_fs SRCS ${AMDUAT_ASL_STORE_INDEX_FS_SRCS})
amduat_link(asl_store_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
target_compile_definitions(amduat_asl_store_index_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
amduat_add_lib(asl_store_minio SRCS ${AMDUAT_ASL_STORE_MINIO_SRCS})
amduat_link(asl_store_minio amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
target_compile_definitions(amduat_asl_store_minio_obj PRIVATE _POSIX_C_SOURCE=200809L)
amduat_add_lib(asl_derivation_index_fs SRCS ${AMDUAT_ASL_DERIVATION_INDEX_FS_SRCS})
amduat_link(asl_derivation_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
target_compile_definitions(amduat_asl_derivation_index_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
@ -321,43 +304,6 @@ target_link_libraries(amduat_pel_build
)
set_target_properties(amduat_pel_build PROPERTIES OUTPUT_NAME amduat-pel-build)
add_executable(amduat_scriptlab_adapter src/tools/amduat_scriptlab_adapter.c)
target_include_directories(amduat_scriptlab_adapter
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_compile_definitions(amduat_scriptlab_adapter
PRIVATE _POSIX_C_SOURCE=200809L
)
target_link_libraries(amduat_scriptlab_adapter
PRIVATE amduat_tgk_store_asl_index_fs amduat_asl_store_index_fs
amduat_asl_collection amduat_asl_record amduat_asl_derivation_index_fs
amduat_pel amduat_tgk amduat_asl amduat_enc amduat_hash_asl1
amduat_util pthread
)
set_target_properties(amduat_scriptlab_adapter
PROPERTIES OUTPUT_NAME amduat-scriptlab-adapter)
add_executable(amduat_conformance_run
src/tools/amduat_conformance_run.c
tests/conformance/asl_store/asl_store_conformance.c
tests/conformance/tgk_store/tgk_store_conformance.c
)
target_include_directories(amduat_conformance_run
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
PRIVATE ${AMDUAT_ROOT}
PRIVATE ${AMDUAT_ROOT}/tests/conformance/asl_store
PRIVATE ${AMDUAT_ROOT}/tests/conformance/tgk_store
)
target_link_libraries(amduat_conformance_run
PRIVATE amduat_asl_store_fs amduat_asl_store_index_fs
amduat_asl_store_minio
amduat_tgk_store_mem amduat_tgk_store_fs amduat_tgk_store_asl_index_fs
amduat_tgk amduat_asl amduat_enc amduat_hash_asl1 amduat_util
amduat_format
)
enable_testing()
add_executable(amduat_test_pel_program_dag tests/enc/test_pel_program_dag.c)
@ -366,7 +312,7 @@ target_include_directories(amduat_test_pel_program_dag
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_pel_program_dag
PRIVATE amduat_enc amduat_asl amduat_hash_asl1 amduat_util
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME pel_program_dag COMMAND amduat_test_pel_program_dag)
@ -376,7 +322,7 @@ target_include_directories(amduat_test_pel_trace_dag
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_pel_trace_dag
PRIVATE amduat_enc amduat_asl amduat_hash_asl1 amduat_util
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME pel_trace_dag COMMAND amduat_test_pel_trace_dag)
@ -399,7 +345,7 @@ target_include_directories(amduat_test_pel1_result
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_pel1_result
PRIVATE amduat_enc amduat_asl amduat_hash_asl1 amduat_util
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME pel1_result COMMAND amduat_test_pel1_result)
@ -410,7 +356,7 @@ target_include_directories(amduat_test_pel1_result_invariants
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_pel1_result_invariants
PRIVATE amduat_enc amduat_asl amduat_hash_asl1 amduat_util
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME pel1_result_invariants
COMMAND amduat_test_pel1_result_invariants)
@ -421,7 +367,7 @@ target_include_directories(amduat_test_asl_log
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_asl_log
PRIVATE amduat_enc amduat_asl amduat_hash_asl1 amduat_util
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME asl_log COMMAND amduat_test_asl_log)
@ -431,7 +377,7 @@ target_include_directories(amduat_test_asl_core_index
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_asl_core_index
PRIVATE amduat_enc amduat_asl amduat_hash_asl1 amduat_util
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME asl_core_index COMMAND amduat_test_asl_core_index)
@ -442,37 +388,17 @@ target_include_directories(amduat_test_asl_tgk_exec_plan
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_asl_tgk_exec_plan
PRIVATE amduat_enc amduat_asl amduat_hash_asl1 amduat_util
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
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_asl amduat_hash_asl1 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_asl amduat_hash_asl1 amduat_util
)
add_test(NAME fcs_pcb1 COMMAND amduat_test_fcs_pcb1)
add_executable(amduat_test_tgk1_edge tests/enc/test_tgk1_edge.c)
target_include_directories(amduat_test_tgk1_edge
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_tgk1_edge
PRIVATE amduat_enc amduat_asl amduat_hash_asl1 amduat_util
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME tgk1_edge COMMAND amduat_test_tgk1_edge)
@ -482,7 +408,7 @@ target_include_directories(amduat_test_fer1_receipt
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_link_libraries(amduat_test_fer1_receipt
PRIVATE amduat_enc amduat_asl amduat_hash_asl1 amduat_util
PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util
)
add_test(NAME fer1_receipt COMMAND amduat_test_fer1_receipt)
@ -496,17 +422,6 @@ target_link_libraries(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)
target_include_directories(amduat_test_tgk_store_fs
PRIVATE ${AMDUAT_INTERNAL_DIR}
@ -594,36 +509,6 @@ set_tests_properties(asl_store_index_fs PROPERTIES
TIMEOUT 120
)
add_executable(amduat_test_asl_log_store_index_fs
tests/asl/test_asl_log_store_index_fs.c)
target_include_directories(amduat_test_asl_log_store_index_fs
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_compile_definitions(amduat_test_asl_log_store_index_fs
PRIVATE _POSIX_C_SOURCE=200809L
)
target_link_libraries(amduat_test_asl_log_store_index_fs
PRIVATE amduat_asl_log_store amduat_asl_store_index_fs amduat_format pthread
)
add_test(NAME asl_log_store_index_fs COMMAND amduat_test_asl_log_store_index_fs)
set_tests_properties(asl_log_store_index_fs PROPERTIES TIMEOUT 30)
add_executable(amduat_test_asl_index_put_get_consistency
tests/asl/test_asl_index_put_get_consistency.c)
target_include_directories(amduat_test_asl_index_put_get_consistency
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_compile_definitions(amduat_test_asl_index_put_get_consistency
PRIVATE _POSIX_C_SOURCE=200809L
)
target_link_libraries(amduat_test_asl_index_put_get_consistency
PRIVATE amduat_asl_store_index_fs amduat_format pthread
)
add_test(NAME asl_index_put_get_consistency
COMMAND amduat_test_asl_index_put_get_consistency)
add_executable(amduat_test_asl_index_accel tests/asl/test_asl_index_accel.c)
target_include_directories(amduat_test_asl_index_accel
PRIVATE ${AMDUAT_INTERNAL_DIR}
@ -634,28 +519,6 @@ target_link_libraries(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_enc amduat_asl amduat_hash_asl1 amduat_util
)
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
tests/asl/test_asl_derivation_index_fs.c)
target_include_directories(amduat_test_asl_derivation_index_fs
@ -671,66 +534,6 @@ target_link_libraries(amduat_test_asl_derivation_index_fs
add_test(NAME asl_derivation_index_fs
COMMAND amduat_test_asl_derivation_index_fs)
add_executable(amduat_test_asl_projection_replay
tests/asl/test_asl_projection_replay.c)
target_include_directories(amduat_test_asl_projection_replay
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_compile_definitions(amduat_test_asl_projection_replay
PRIVATE _POSIX_C_SOURCE=200809L
)
target_link_libraries(amduat_test_asl_projection_replay
PRIVATE amduat_asl_store_index_fs amduat_asl_record amduat_asl
amduat_enc amduat_hash_asl1 amduat_util pthread
)
add_test(NAME asl_projection_replay
COMMAND amduat_test_asl_projection_replay)
add_executable(amduat_test_asl_store_conformance
tests/conformance/asl_store/test_asl_store_conformance.c
tests/conformance/asl_store/asl_store_conformance.c
)
target_include_directories(amduat_test_asl_store_conformance
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
PRIVATE ${AMDUAT_ROOT}
PRIVATE ${AMDUAT_ROOT}/tests/conformance/asl_store
)
target_compile_definitions(amduat_test_asl_store_conformance
PRIVATE _POSIX_C_SOURCE=200809L
)
target_link_libraries(amduat_test_asl_store_conformance
PRIVATE amduat_asl_store_fs amduat_asl_store_index_fs
amduat_asl_store_minio
amduat_asl amduat_enc amduat_hash_asl1 amduat_util
)
add_test(NAME asl_store_conformance
COMMAND amduat_test_asl_store_conformance
)
add_executable(amduat_test_tgk_store_conformance
tests/conformance/tgk_store/test_tgk_store_conformance.c
tests/conformance/tgk_store/tgk_store_conformance.c
)
target_include_directories(amduat_test_tgk_store_conformance
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
PRIVATE ${AMDUAT_ROOT}
PRIVATE ${AMDUAT_ROOT}/tests/conformance/tgk_store
)
target_compile_definitions(amduat_test_tgk_store_conformance
PRIVATE _POSIX_C_SOURCE=200809L
)
target_link_libraries(amduat_test_tgk_store_conformance
PRIVATE amduat_tgk_store_mem amduat_tgk_store_fs amduat_tgk_store_asl_index_fs
amduat_asl_store_fs amduat_asl_store_index_fs amduat_tgk amduat_asl
amduat_enc amduat_hash_asl1 amduat_util amduat_format
)
add_test(NAME tgk_store_conformance
COMMAND amduat_test_tgk_store_conformance
)
add_executable(amduat_test_pel_program_dag_exec
tests/pel/test_pel_program_dag_exec.c)
target_include_directories(amduat_test_pel_program_dag_exec
@ -826,20 +629,3 @@ target_link_libraries(amduat_test_fed_view
PRIVATE amduat_fed
)
add_test(NAME fed_view COMMAND amduat_test_fed_view)
add_executable(amduat_test_scriptlab_adapter
tests/substrate/test_scriptlab_adapter.c)
target_include_directories(amduat_test_scriptlab_adapter
PRIVATE ${AMDUAT_INTERNAL_DIR}
PRIVATE ${AMDUAT_INCLUDE_DIR}
)
target_compile_definitions(amduat_test_scriptlab_adapter
PRIVATE _POSIX_C_SOURCE=200809L
)
target_link_libraries(amduat_test_scriptlab_adapter
PRIVATE amduat_tgk_store_asl_index_fs amduat_asl_store_index_fs
amduat_asl_collection amduat_asl_record amduat_asl_derivation_index_fs
amduat_pel amduat_tgk amduat_asl amduat_enc amduat_hash_asl1
amduat_util pthread
)
add_test(NAME scriptlab_adapter COMMAND amduat_test_scriptlab_adapter)

View file

@ -1,198 +0,0 @@
# Substrate Contract v0.1
Status: Draft (repo-grounded)
Scope: Defines the substrate surfaces and invariants implemented in this repo for v0.1.
This contract is grounded in the current Amduat code and specs. Each requirement
below lists file references that define the behavior. If a line is marked **NEW**,
it is introduced in this change and implemented in code (referenced below).
---
## 1. Artifact Model (ASL/1-CORE)
1. **Artifact** = bytes + optional type tag; identity is content-addressed and immutable.
Sources: `tier1/asl-1-core.md`, `include/amduat/asl/core.h`.
2. **Reference** = `(hash_id, digest)`; equality is hash-id + digest equality.
Sources: `tier1/asl-1-core.md`, `include/amduat/asl/core.h`.
3. **Reference derivation** uses canonical encoding plus the ASL1 domain
separator (`"AMDUAT:ASL1:ART\0"`) and hash family; the API exposes this via
`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), with
registry/version metadata exposed in the core API.
Sources: `tier1/hash-asl1.md`, `include/amduat/hash/asl1.h`.
---
## 2. Record Artifacts (ASL records)
1. ASL records are artifacts with type tag `TYPE_TAG_ASL_RECORD_1` and a
payload encoding defined by `amduat_asl_record_encode_v1`/`decode_v1`.
Sources: `include/amduat/asl/record.h`, `src/core/asl_record.c`.
2. **Record schema is required and ASCII**; payload may be empty but must be
well-formed per the encoder rules.
Sources: `src/core/asl_record.c`.
3. Records are stored and retrieved via `amduat_asl_record_store_put/get`, which
wrap ASL store `put/get` over the encoded record bytes.
Sources: `include/amduat/asl/record.h`, `src/core/asl_record.c`, `include/amduat/asl/store.h`.
---
## 3. Authoritative CAS + Index/Log (ASL/1-STORE)
1. The authoritative content-addressed store surface is `amduat_asl_store_t` with
`put/get` and indexed variants `put_indexed/get_indexed`.
Sources: `include/amduat/asl/store.h`.
2. Index/log state is represented as `amduat_asl_index_state_t` with
`snapshot_id` and `log_position`.
Sources: `include/amduat/asl/store.h`, `docs/index-log-api-sketch.md`.
3. Index/log record types are encoded as ASL log records, including artifact
publish/unpublish, snapshot anchors, and tombstones.
Sources: `include/amduat/enc/asl_log.h`, `tier1/asl-log-1.md`.
4. The filesystem index/log adapter implements the indexed store surface and
emits log records with monotonically increasing `logseq` values.
Sources: `include/amduat/asl/asl_store_index_fs.h`, `src/adapters/asl_store_index_fs/asl_store_index_fs.c`.
---
## 4. Derivation / Provenance Index
1. Derivation records bind an output artifact ref to a program ref, input refs,
output index, and optional params/exec-profile.
Sources: `include/amduat/asl/asl_derivation_index_fs.h`.
2. Deterministic derivation SIDs are computed from program + inputs (+ optional
params/profile) via `amduat_pel_derivation_sid_compute`.
Sources: `include/amduat/pel/derivation_sid.h`, `src/core/derivation_sid.c`.
3. The filesystem derivation index stores and lists derivation records by
artifact reference.
Sources: `include/amduat/asl/asl_derivation_index_fs.h`,
`src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c`.
---
## 5. TGK Projection (Edges as Artifacts)
1. TGK edges are artifacts encoded via `ENC/TGK1-EDGE/1` with type tag
`TYPE_TAG_TGK1_EDGE_V1` and edge bodies containing `from`, `to`, and `payload` refs.
Sources: `include/amduat/enc/tgk1_edge.h`, `include/amduat/tgk/core.h`.
2. The TGK store surface provides graph queries (edges_from/to/scan/neighbors)
over projected edges.
Sources: `include/amduat/tgk/store.h`, `tier1/tgk-store-1.md`.
3. `amduat_tgk_store_asl_index_fs` implements a TGK projection over the ASL
index/log store.
Sources: `include/amduat/tgk/tgk_store_asl_index_fs.h`,
`src/adapters/tgk_store_asl_index_fs/tgk_store_asl_index_fs.c`.
---
## 6. Deterministic Transforms (PEL)
1. PEL surface execution results are stored as artifacts with type tag
`TYPE_TAG_PEL1_RESULT_1`, encoding `PEL_ENC_EXECUTION_RESULT_V1`.
Sources: `include/amduat/enc/pel1_result.h`, `include/amduat/pel/surf.h`.
2. Trace DAGs and Program DAGs are encoded as artifacts with their respective
type tags and encoding profiles.
Sources: `include/amduat/enc/pel_trace_dag.h`, `include/amduat/enc/pel_program_dag.h`.
3. A minimal job record for deterministic transforms is the tuple:
`(program_ref, input_refs, params_ref?, exec_profile?, result_ref, trace_ref?)`.
Sources: `include/amduat/pel/surf.h`, `include/amduat/enc/pel1_result.h`,
`include/amduat/pel/derivation_sid.h`.
---
## 7. Projection Hooks + Checkpoints (for external projectors)
1. **Projection feed cursor** is the ASL log position (`amduat_asl_log_position_t`)
from `amduat_asl_index_state_t` and log records are read via the ASL log API.
Sources: `include/amduat/asl/store.h`, `include/amduat/enc/asl_log.h`.
2. **NEW:** `amduat_asl_log_read_since` provides a cursor-based view of log
records (records with `logseq` > cursor), enabling deterministic replay from
a stable log position.
Sources: `include/amduat/asl/log_cursor.h`, `src/near_core/asl/log_cursor.c`.
3. **NEW:** Projection checkpoints are encoded via
`amduat_asl_projection_checkpoint_encode_v1/decode_v1` and stored as ASL
record artifacts. The v0.1 schema is derived from `docs/projection-checkpoint-schema.md`.
Sources: `include/amduat/asl/projection_checkpoint.h`,
`src/near_core/asl/projection_checkpoint.c`, `docs/projection-checkpoint-schema.md`,
`include/amduat/asl/record.h`.
4. **NEW (v0.1 convention):** Checkpoint record schema string is
`projection/checkpoint` and the record payload is the v1 checkpoint encoding.
Sources: `src/tools/amduat_scriptlab_adapter.c`, `tests/substrate/test_scriptlab_adapter.c`,
`include/amduat/asl/record.h`.
See `docs/projector-pattern.md` for the canonical cursor→project→checkpoint loop
using `amduat_asl_log_read_since(store, after_logseq, limit)` and
`projection/checkpoint` ASL records.
---
## 8. Non-Authoritative Projections
1. External projections (e.g., Elasticsearch, IPFS, JanusGraph) are derived and
must remain observationally equivalent to the ASL/TGK substrate.
Sources: `docs/amduat-oss-backend-proposal.md`, `docs/indexing-requirements-and-projections.md`.
---
## 9. Definition of Done (v0.1)
1. CAS, index/log store, and derivation index are available via the C surfaces
and filesystem adapters.
Sources: `include/amduat/asl/store.h`, `include/amduat/asl/asl_store_index_fs.h`,
`include/amduat/asl/asl_derivation_index_fs.h`.
2. TGK projection is available over ASL index/log store.
Sources: `include/amduat/tgk/tgk_store_asl_index_fs.h`.
3. PEL execution artifacts are produced and encoded as artifacts and can be
linked via derivation SIDs.
Sources: `include/amduat/pel/surf.h`, `include/amduat/enc/pel1_result.h`,
`include/amduat/pel/derivation_sid.h`.
4. **NEW:** Projection feed cursor + checkpoint artifacts are implemented and
validated by the scriptlab adapter flow.
Sources: `include/amduat/asl/log_cursor.h`, `include/amduat/asl/projection_checkpoint.h`,
`tests/substrate/test_scriptlab_adapter.c`.
---
## 10. Layer 1 Dependencies (Allowed vs Disallowed)
**Layer 1 (higher API) MAY depend on:**
1. ASL core and store surfaces (`amduat/asl/core.h`, `amduat/asl/store.h`).
2. ASL index/log state + replay (`amduat/asl/index_replay.h`, `amduat/enc/asl_log.h`).
3. Derivation/provenance index (`amduat/asl/asl_derivation_index_fs.h`,
`amduat/pel/derivation_sid.h`).
4. PEL deterministic artifacts and surface results (`amduat/pel/surf.h`,
`amduat/enc/pel1_result.h`).
5. TGK edge artifacts + TGK store projection (`amduat/enc/tgk1_edge.h`,
`amduat/tgk/store.h`, `amduat/tgk/tgk_store_asl_index_fs.h`).
6. Projection replay primitives (cursor + checkpoint helpers)
(`amduat/asl/log_cursor.h`, `amduat/asl/projection_checkpoint.h`).
**Layer 1 MUST NOT depend on:**
1. CLI tools (`src/tools/*`) as APIs.
2. Any networking, auth, tenancy, or server layers (none are provided).
3. App/domain schemas beyond raw ASL records (records may carry schema bytes,
but core does not interpret them).

View file

@ -1,98 +0,0 @@
# Adapter Error Taxonomy
Status: Draft
Scope: Canonical error mapping for ASL and TGK adapters and derived projections, used by conformance tests to prevent drift.
---
# 1. Purpose
This taxonomy defines a canonical internal error set. Adapters MUST map backend-specific errors into these categories so that conformance tests remain stable across backends.
---
# 2. Canonical Error Set
## 2.1 ASL Store Errors
Canonical enum (from `include/amduat/asl/store.h`):
1. `AMDUAT_ASL_STORE_ERR_INTEGRITY`
2. `AMDUAT_ASL_STORE_ERR_UNSUPPORTED`
3. `AMDUAT_ASL_STORE_ERR_NOT_FOUND`
4. `AMDUAT_ASL_STORE_ERR_IO`
## 2.2 TGK Graph Store Errors
Canonical enum (from `include/amduat/tgk/store.h`):
1. `GS_ERR_NOT_EDGE`
2. `GS_ERR_ARTIFACT_ERROR`
3. `GS_ERR_UNSUPPORTED`
4. `GS_ERR_INTEGRITY`
## 2.3 Projection Errors (Derived Indexers)
Canonical categories:
1. NotFound
2. PermissionDenied
3. TransientIO
4. InvalidDocument
5. InternalError
Projection errors must never be surfaced as authoritative ASL or TGK errors.
---
# 3. Mapping Rules
## 3.1 ASL/1-STORE Mapping
1. `ERR_NOT_FOUND``AMDUAT_ASL_STORE_ERR_NOT_FOUND`
2. `ERR_INTEGRITY``AMDUAT_ASL_STORE_ERR_INTEGRITY`
3. `ERR_UNSUPPORTED``AMDUAT_ASL_STORE_ERR_UNSUPPORTED`
4. Backend I/O or transport failure → `AMDUAT_ASL_STORE_ERR_IO`
Notes:
1. `ASL/1-STORE` does not standardize I/O or auth errors; those must map to `ERR_IO` for conformance tests.
2. A `put` collision on identical reference with different bytes MUST map to `ERR_INTEGRITY`.
## 3.2 TGK/STORE/1 Mapping
1. Reference resolves to non-edge artifact → `GS_ERR_NOT_EDGE`
2. Artifact-layer integrity failure during resolve → `GS_ERR_ARTIFACT_ERROR`
3. Unsupported profile/encoding → `GS_ERR_UNSUPPORTED`
4. Detected graph-level corruption → `GS_ERR_INTEGRITY`
Notes:
1. `GS_ERR_ARTIFACT_ERROR` is required for artifact-level conflicts; do not use `GS_ERR_INTEGRITY`.
2. Backend-specific errors must be normalized to the canonical TGK error set.
## 3.3 Projection Adapter Mapping
1. Index missing doc → NotFound
2. Authorization failure → PermissionDenied
3. Timeout, transport, or retryable failure → TransientIO
4. Schema or decode failure → InvalidDocument
5. Unknown failure → InternalError
Projection errors are operational only and must not be treated as ASL/TGK semantic failures.
---
# 4. Test Requirements
Conformance tests MUST include:
1. ASL: `get` on missing ref → `ERR_NOT_FOUND`.
2. ASL: `put` collision → `ERR_INTEGRITY`.
3. ASL: unsupported config → `ERR_UNSUPPORTED`.
4. ASL: I/O failure path → `ERR_IO`.
5. TGK: `resolve_edge` on non-edge → `GS_ERR_NOT_EDGE`.
6. TGK: artifact integrity conflict → `GS_ERR_ARTIFACT_ERROR`.
7. TGK: unsupported encoding/profile → `GS_ERR_UNSUPPORTED`.
---
# 5. Non-Goals
1. Backend-specific error codes.
2. Retry policy definitions.
3. Human-readable error messaging conventions.

View file

@ -1,50 +0,0 @@
# 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

@ -1,351 +0,0 @@
# Amduat Open-Source Backend Proposal
## Contract-First Backend Integration Architecture
**Status:** Proposal
**Scope:** Define how the OSS stack (MinIO, JanusGraph, Cassandra, Elasticsearch, Spark, Nessie, IPFS, Ollama) can be deployed as a backend implementation of Amduat while preserving semantic invariants.
---
# Related Documents
* `docs/implementation-plan.md`
* `docs/phase0-1-execution-checklist.md`
* `docs/decision-checklist.md`
* `docs/conformance-harness-spec.md`
* `docs/adapter-error-taxonomy.md`
* `docs/tgk-ordering-enforcement.md`
* `docs/indexing-requirements-and-projections.md`
* `docs/es-indexing-plan.md`
* `docs/projection-checkpoint-schema.md`
* `docs/oss-backend-adapter-boundary.md`
---
# 1. Executive Summary
This document proposes integrating a scalable open-source infrastructure stack as a backend implementation of Amduat.
The proposal preserves Amduats defining principle:
> The semantic core is invariant.
> Backends are interchangeable implementations of store/query surfaces.
The OSS stack provides scalability, distribution, analytics, and optional acceleration layers without altering the deterministic semantics defined by the Amduat core specifications.
---
# 2. Architectural Position
## 2.1 Semantic Core
Core semantics are defined by:
* `ASL/1-CORE`
* `PEL/1-CORE`
* `TGK/1-CORE`
Canonical encodings are provided by:
* `ENC/ASL1-CORE` (and related encoding profiles)
Identity derivation is defined by:
* `ASL/1-CORE`
* `HASH/ASL1`
These define:
* Artifact identity and immutability
* Core execution semantics
* Core graph semantics
* Deterministic projection rules
These are deployment-invariant.
## 2.2 Store / Query Surfaces
Operational interfaces are defined by:
* `ASL/1-STORE`
* `TGK/STORE/1`
Optional provenance semantics:
* `TGK/PROV/1`
Storage and indexing layers are pluggable implementations of these surfaces.
They do not redefine core semantics.
---
# 3. No Semantic Drift Rule
To preserve Amduats portability and determinism, all backend implementations must adhere to the following:
1. **No Backend-Specific Semantics**
* No interpretation of artifacts beyond canonical encoding.
* No implicit schema expansion.
* No hidden type coercion.
2. **No Semantic Elevation of Projections**
* Search indexes (e.g., Elasticsearch)
* Distribution caches (e.g., IPFS)
* Analytics views (e.g., Spark)
* AI-generated outputs
…are derived projections unless explicitly promoted by governance.
3. **Deterministic Reproducibility**
* The logical TGK graph must be reproducible from ASL state.
* Snapshot exports must reproduce identical graph projections.
4. **Surface Conformance Only**
* The OSS stack may optimize storage and indexing.
* It may not redefine core behavior.
5. **Observational Equivalence**
* Any backend-specific optimization must be observationally equivalent to the core semantics and query results defined by the specifications.
This rule guarantees backend substitution without semantic drift.
---
# 4. ASL/1-STORE Implementation (Artifact Layer)
## 4.1 Contract
Must implement:
* `put(Artifact) -> Reference`
* `get(Reference) -> Artifact | NotFound`
Identity derivation follows:
1. Canonical encoding (`ENC/ASL1-CORE`)
2. Identity derivation rules (`ASL/1-CORE`)
3. Hash function (`HASH/ASL1`)
In abstract form:
```
Reference = HASH( CANONICAL_ENCODE(Artifact) )
```
Reference derivation is deterministic and caller-independent.
## 4.2 Immutability
* Artifacts are immutable.
* Rewriting the same Artifact yields the same Reference.
* Mutation semantics do not exist at this layer.
## 4.3 Authority Rule
The authoritative store in any deployment is the backend implementation that conforms to `ASL/1-STORE`.
`ASL/1-STORE` is a specification; authority resides in whichever concrete backend implements it.
Caches and projections derive from this authoritative ASL implementation unless explicitly promoted by governance.
## 4.4 Proposed Backend
Primary implementation:
* MinIO-backed object storage
Optional:
* IPFS as distribution/cache layer
---
# 5. TGK/STORE/1 Implementation (Graph Query Surface)
## 5.1 Projection Model
The TGK graph is a logical projection derived from:
* Artifacts stored via ASL
* Core graph semantics defined in `TGK/1-CORE`
It is not an independently authoritative database.
Graph state must be reproducible from ASL state.
## 5.2 Required Operations
Backends must implement the required query operations and error model defined in `TGK/STORE/1`.
This proposal references the specification directly to avoid semantic drift through re-enumeration.
## 5.3 Read-Only Nature
* `GraphStoreInstance` is logically read-only.
* Edges arise from writing EdgeArtifacts to ASL.
* Deletion, compaction, or tombstone policies are backend or profile-level implementation concerns and are not part of `TGK/STORE/1`.
## 5.4 Proposed Backend
* JanusGraph as query engine
* Cassandra as storage backend
* Optional Elasticsearch as search projection
JanusGraph functions as an implementation detail of the `TGK/STORE/1` surface.
---
# 6. TGK/PROV/1 (Optional Provenance Layer)
`TGK/PROV/1` defines pure functions over a `ProvenanceGraph`.
* It does not require a particular store.
* It is evaluated against `TGK/1-CORE` graph semantics.
* Implementations may use `TGK/STORE/1` queries to realize provenance functions.
Provenance is layered above store semantics and does not alter core behavior.
Implementation options:
* Native traversal realization
* Snapshot-based traversal in analytics engines
* Hybrid realization
---
# 7. Projection & Acceleration Layers
Unless explicitly promoted by governance:
* Elasticsearch
* IPFS
* Lakehouse datasets (MinIO + Nessie)
* Spark analytics outputs
* AI-derived artifacts
…are derived projections.
Authoritative truth resides in:
* The deployed `ASL/1-STORE` implementation
* Deterministic TGK projection derived from ASL state
Projections may be eventually consistent.
Audit-grade claims must reference reproducible ASL state or snapshot identifiers.
---
# 8. Capability Tiers
Capability tiers define operational envelope, not semantic differences.
---
## Tier 0 — Embedded / Edge
Implements:
* `ASL/1-STORE`
* `TGK/STORE/1`
* Optional minimal `TGK/PROV/1`
Characteristics:
* Single-node
* Deterministic local projection
* Strong local consistency
* Optional sync/export
Use cases:
* Personal knowledge systems
* Offline assistant systems
---
## Tier 1 — Platform / Scale-Out
Implements:
* `ASL/1-STORE`
* `TGK/STORE/1`
* Optional `TGK/PROV/1`
Typical infrastructure:
* Distributed object store (e.g., MinIO)
* Distributed graph backend (e.g., JanusGraph over Cassandra)
* Optional derived search index
Characteristics:
* Multi-node
* Horizontal scaling
* Operational consistency
* Deterministic core preserved
Use cases:
* Community platforms
* Shared knowledge systems
---
## Tier 2 — Enterprise / Audit-Grade
Includes Tier 1 plus:
* Snapshot export capability
* Lakehouse projection
* Versioned snapshots (e.g., Nessie)
* Governance workflows
* Promotion pipelines
Characteristics:
* Snapshot-based reproducibility
* Audit traceability
* Multi-environment promotion
Use cases:
* Enterprise backbone systems
* Regulated environments
---
# 9. Conformance Requirements
Each backend must:
* Pass `ASL/1-STORE` conformance tests
* Pass `TGK/STORE/1` conformance and deterministic ordering tests
* Demonstrate projection reproducibility from ASL state
* Preserve encoding stability across versions
Tier 2 must additionally:
* Validate snapshot equivalence
* Demonstrate replay determinism from ASL state
Conformance is versioned alongside spec revisions.
---
# 10. Design Outcome
This architecture ensures:
* One invariant semantic kernel
* Backend substitution without semantic drift
* Scaling through infrastructure choice
* Deterministic reproducibility
* Clear separation between authority and projection layers
* Portability from embedded edge systems to enterprise-scale deployments
The OSS stack is therefore a backend implementation of Amduat — not a semantic authority.

View file

@ -1,165 +0,0 @@
# Conformance Harness Specification
Status: Draft
Scope: Define the Phase 0 conformance harness goals, backend matrix, fixture format, and pass/fail gates for ASL and TGK store surfaces.
---
# 1. Purpose
The harness ensures that all backend adapters implement existing store semantics without drift.
It MUST:
1. Run the same test vectors across multiple backends.
2. Enforce deterministic ordering rules.
3. Validate replay equivalence.
4. Provide a single command entry point for CI.
---
# 2. Targets and Scope
## 2.1 Surfaces Under Test
1. ASL/1-STORE
2. TGK/STORE/1
Optional extensions:
1. ASL/STORE-INDEX/1 (index/log store)
2. TGK/PROV/1 (if provenance surface is exposed)
## 2.2 Conformance Mode
All tests are:
1. Deterministic.
2. Backend-agnostic.
3. Semantics-first (no timing assumptions).
---
# 3. Backend Matrix
The harness MUST be able to run each test suite against:
## 3.1 ASL Backends
1. `asl_store_fs`
2. `asl_store_index_fs`
3. Future: `asl_store_minio`
## 3.2 TGK Backends
1. `tgk_store_mem`
2. `tgk_store_fs`
3. `tgk_store_asl_index_fs`
4. Future: `tgk_store_janus`
---
# 4. Canonical Fixture Format
Fixtures are the source of truth for all conformance tests.
## 4.1 Requirements
1. Versioned fixture schema.
2. Stable ordering of artifacts and edges.
3. Explicit references encoded as canonical `ReferenceBytes` hex.
4. Deterministic content generation (no randomness at runtime).
## 4.2 Minimal Fields
For ASL fixtures:
1. `artifact_bytes` (hex or base64, fixed encoding).
2. `type_tag` (optional).
3. `expected_reference` (canonical Reference hex).
For TGK fixtures:
1. `edge_artifact_reference` (canonical Reference hex).
2. `edge_body` (decoded representation).
3. `edge_type_id`
4. `from_refs` and `to_refs` (ordered lists, canonical hex).
---
# 5. Test Suite Requirements
## 5.1 ASL/1-STORE Suite
1. Put/get round-trip.
2. Idempotent `put`.
3. `get` on missing ref yields `ERR_NOT_FOUND`.
4. Collision on same ref yields `ERR_INTEGRITY`.
5. Unsupported config yields `ERR_UNSUPPORTED`.
6. Optional I/O fault path yields `ERR_IO`.
## 5.2 TGK/STORE/1 Suite
1. `resolve_edge` for valid edge returns `EdgeBody`.
2. `resolve_edge` for non-edge returns `GS_ERR_NOT_EDGE`.
3. `resolve_edge` for artifact integrity failure yields `GS_ERR_ARTIFACT_ERROR`.
4. `edges_from`, `edges_to`, `edges_incident` return identical lists across backends.
5. Deterministic ordering per `TGK/STORE/1` canonical `(hash_id, digest)` rule.
6. Optional `neighbors` returns canonical node order.
7. Optional `scan_edges` enumerates all edges exactly once.
## 5.3 Replay Equivalence
1. Build a fixture ASL log/index state.
2. Replay into a fresh store.
3. Assert identical references and TGK results.
---
# 6. Ordering and Equivalence Rules
## 6.1 Edge Ordering
Edge lists MUST be sorted in ascending lexicographic order of:
`hash_id_bytes || digest_bytes`, with `hash_id` encoded as big-endian `u16`.
## 6.2 Node Ordering
Neighbor lists MUST be sorted in ascending lexicographic order of:
`hash_id_bytes || digest_bytes`, with `hash_id` encoded as big-endian `u16`.
## 6.3 Cross-Backend Equivalence
For a fixed snapshot and `GraphStoreConfig`, all backends MUST return:
1. Identical edge sets.
2. Identical ordered lists for adjacency queries.
3. Identical results for `resolve_edge`.
---
# 7. Harness Structure
Suggested structure:
1. `tests/conformance/asl_store/*.c`
2. `tests/conformance/tgk_store/*.c`
3. `tests/fixtures/*.json`
4. `tools/amduat_conformance_run`
The harness runner MUST:
1. Accept backend selector flags.
2. Accept fixture set selection.
3. Emit a machine-readable summary.
---
# 8. CI Gate
An adapter cannot be merged unless:
1. It passes its conformance suite.
2. Cross-backend equivalence is demonstrated.
3. Replay equivalence passes on at least one backend pair.
---
# 9. Out of Scope
1. Performance benchmarking.
2. Load testing.
3. Backend-specific tuning.
Those are separate gates after conformance is proven.

View file

@ -1,101 +0,0 @@
# Decision Checklist (Gating Artifact)
This file records decisions that must be resolved before moving forward with
backend integration. It is the gating artifact for Phase 0/1.
---
## Phase 0 — Conformance Harness + Determinism
### Conformance Scope
* Tier 0 MUST-pass specs (backend/store focus):
* `ASL/1-CORE`
* `ASL/1-STORE`
* `ENC/ASL1-CORE`
* `HASH/ASL1`
* `TGK/1-CORE`
* `ENC/TGK1-EDGE/1`
* `TGK/STORE/1`
* Tier 1 MUST-pass specs (index/log + optional provenance):
* `ASL/LOG/1`
* `ENC/ASL-LOG/1`
* `ASL/1-CORE-INDEX`
* `ENC/ASL-CORE-INDEX/1`
* `ASL/STORE-INDEX/1`
* `ASL/INDEX-ACCEL/1`
* `ASL/FEDERATION/1` and `ASL/FEDERATION-REPLAY/1` (if federation is in scope)
* `TGK/PROV/1` (if provenance operators are surfaced)
Notes:
* The legacy filesystem store (`asl_store_fs`) is not a Tier 1 conformance target once index/log is used.
* PEL specs are implemented and tested in this repo, but they are not required for the Phase 0/1 backend gate unless the surface is shipped as part of the backend.
### TGK/STORE Equivalence Definition
* Exact definition of “same result” across backends:
* For a fixed `GraphStoreConfig` and snapshot where `(Artifacts, config.tgk_profiles)` are equal, all `TGK/STORE/1` operations must return identical results per `TGK/STORE-DET/1`.
* `resolve_edge(ref)` returns the same `EdgeBody` or the same error code.
* `edges_from`, `edges_to`, `edges_incident` return identical `GraphEdgeView` lists (same elements in the same order).
* If implemented, `neighbors` returns identical `Node` lists (same elements in the same order).
* If implemented, `scan_edges` enumerates the same edges with identical per-page ordering; `PageToken` bytes may differ but must represent the same position in the canonical ordering.
* Ordering requirements (explicitly enumerated):
* Edge ordering is canonical and global: ascending lexicographic order of `order_key(edge_ref) = hash_id_bytes || digest_bytes`, with `hash_id` as big-endian `u16` (`TGK/STORE-EDGE-ORDER/1`).
* Neighbor ordering is canonical: ascending lexicographic order of `(hash_id, digest)` with the same `u16` big-endian encoding (`TGK/STORE-NEIGHBORS/1`).
* Pagination/scan stability rules:
* `scan_edges` must enumerate each matching edge exactly once and preserve the canonical per-page ordering.
* Snapshot stability across pages is optional; if not stable, the implementation must still preserve per-call ordering and enumeration semantics for the snapshot used by that call.
### Error Model Mapping Policy
* Canonical internal error taxonomy:
* ASL store errors: `AMDUAT_ASL_STORE_ERR_INTEGRITY`, `AMDUAT_ASL_STORE_ERR_UNSUPPORTED`, `AMDUAT_ASL_STORE_ERR_NOT_FOUND`, `AMDUAT_ASL_STORE_ERR_IO`.
* TGK store errors: `GS_ERR_NOT_EDGE`, `GS_ERR_ARTIFACT_ERROR`, `GS_ERR_UNSUPPORTED`, `GS_ERR_INTEGRITY`.
* Adapter-specific error mapping rules:
* `ASL/1-STORE` specifies `ERR_NOT_FOUND`, `ERR_INTEGRITY`, `ERR_UNSUPPORTED`. I/O/auth failures are out of scope for the spec; in this repo they map to `AMDUAT_ASL_STORE_ERR_IO`.
* `TGK/STORE/1` requires artifact-layer conflicts during `resolve_edge` to surface as `GS_ERR_ARTIFACT_ERROR` (not `GS_ERR_INTEGRITY`).
* `GS_ERR_NOT_EDGE` is returned when a reference resolves to an Artifact that is not a TGK edge under the active profile set.
* Required error tests:
* ASL: `get` on missing ref → `ERR_NOT_FOUND`.
* ASL: `put` collision (same ref, different artifact) → `ERR_INTEGRITY`.
* ASL: unsupported config or hash/profile mismatch → `ERR_UNSUPPORTED`.
* ASL: adapter I/O faults → `ERR_IO` (non-normative but required for harness).
* TGK: `resolve_edge` non-edge artifact → `GS_ERR_NOT_EDGE`.
* TGK: `resolve_edge` artifact-layer integrity failure → `GS_ERR_ARTIFACT_ERROR`.
* TGK: unsupported profile/encoding → `GS_ERR_UNSUPPORTED`.
---
## Phase 1 — ASL MinIO Adapter
### Object Key Layout
* Reference → bucket/key mapping: TBD (not specified by current specs/tests).
* Prefix sharding / directory fanout: TBD (not specified by current specs/tests).
### Metadata Policy
* Encoding/profile version tracking: TBD (not specified by current specs/tests).
* Metadata location (object metadata/tags/in-band): TBD (not specified by current specs/tests).
### Multipart Policy
* Multipart threshold: TBD (not specified by current specs/tests).
* Fidelity guarantees and validation: TBD (not specified by current specs/tests).
### Consistency Assumptions
* Read-after-write expectations: TBD (not specified by current specs/tests).
* Deployment modes covered by tests: TBD (not specified by current specs/tests).
### Security / Credentials
* Credential source and rotation policy: TBD (not specified by current specs/tests).
* Permission boundaries: TBD (not specified by current specs/tests).
---
## Status
* Phase 0 decisions: DRAFTED (spec-derived; requires owner confirmation)
* Phase 1 decisions: NOT STARTED

View file

@ -1,120 +0,0 @@
# Elasticsearch Indexing Plan
Status: Draft
Scope: Minimal phased ES deployment aligned with indexing contract.
---
# Phase A — Artifact Metadata Projection (Minimal)
## Goal
Accelerate search and metadata filtering only.
## Index Name
`amduat_artifacts_v1`
## Document Structure
1. `_id`: canonical `ReferenceBytes` hex.
2. `reference`: canonical hex.
3. `projection_schema_version`: integer.
4. `kind`: keyword.
5. `enc_profile`: keyword.
6. `labels`: keyword array.
7. `created_at`: date, only if intrinsic to artifact.
8. `text`: text, optional, deterministic extractor.
9. `extractor_version`: keyword, if `text` present.
## Data Source
1. ASL authoritative log/index.
2. Deterministic decoding only.
## Query Patterns Accelerated
1. Text search.
2. Filter by kind.
3. Filter by labels.
4. Narrow candidate set.
## Verification Rule
All returned References MUST be confirmed via ASL/1-STORE before returning artifact data.
---
# Phase B — Edge Projection (Optional)
## Goal
Accelerate candidate neighbor discovery.
## Index Name
`amduat_edges_v1`
## Document Structure
1. `_id`: edge Reference hex.
2. `edge_reference`: canonical hex.
3. `src_ref`: canonical hex.
4. `dst_ref`: canonical hex.
5. `edge_label`: keyword.
6. `hash_id`: keyword or numeric.
7. `digest`: keyword.
8. `projection_schema_version`: integer.
## Data Source
1. EdgeArtifacts only.
2. TGK profile rules.
3. No inferred edges.
## Verification Rule
Returned edges MUST be validated via TGK/STORE/1 authoritative query, and canonical ordering MUST be applied before client response.
---
# Phase C — Enterprise Projection Hardening
## Additions
1. Projection checkpoint artifacts stored in ASL.
2. Schema version recorded in checkpoint.
3. Full rebuild test automation.
4. Lag metrics published.
5. Projection audit job comparing snapshot vs ES.
---
# Operational Guarantees
1. ES may be disabled without breaking semantics.
2. Projection rebuild must be deterministic.
3. Schema changes require replay.
4. Checkpoint artifact defines replay boundary.
---
# Observability Requirements
Expose:
1. ASL head log position.
2. Indexer checkpoint position.
3. Computed lag.
4. Projection error counts.
5. Rebuild duration.
6. Verification fallback rate.
---
# Final Guardrail
Elasticsearch accelerates discovery.
Authoritative semantics remain in ASL/1-STORE and TGK/STORE/1.
Ordering, identity, and correctness are never delegated to ES.

View file

@ -1,460 +0,0 @@
# Amduat Open-Source Backend
# Implementation Plan Proposal
**Status:** Architecture & Implementation Baseline v1
**Purpose:** Define phased implementation of the OSS backend stack as a spec-compliant implementation of Amduat store/query surfaces.
---
# 1. Goal and Scope
## 1.1 Purpose
This plan defines how to implement the open-source stack (MinIO, JanusGraph, Cassandra, Elasticsearch, Spark, Nessie, IPFS, Ollama) as a backend implementation of:
* `ASL/1-STORE`
* `TGK/STORE/1`
* Optional `TGK/PROV/1`
The plan ensures:
* Strict semantic conformance
* Deterministic reproducibility
* Backend substitutability
* Observational equivalence
## 1.2 Out of Scope
* Modifying core specifications
* Introducing new kernel ops
* Elevating projections to semantic authority
* Redefining identity or encoding rules
## 1.3 Target Tiers
This plan covers:
* Tier 0 — Embedded / Edge
* Tier 1 — Platform / Scale-Out
* Tier 2 — Enterprise / Audit-Grade
---
# 2. Semantic Invariants
All phases must preserve:
## 2.1 No Semantic Drift Rule
* No backend-specific interpretation of artifacts
* No implicit schema expansion
* No hidden type coercion
* No projection elevation without governance
* All optimizations must be observationally equivalent to spec-defined behavior
## 2.2 Non-Negotiable Properties
* Canonical encoding (`ENC/ASL1-CORE`)
* Deterministic identity (`ASL/1-CORE` + `HASH/ASL1`)
* Deterministic TGK projection (`TGK/1-CORE`)
* Store/query surface conformance
* Replay equivalence
---
# 3. Decision Checklist (Must Resolve Upfront)
These decisions must be explicitly documented before Phase 1 begins.
---
## 3.1 Conformance Tests
* Exact test suites for:
* `ASL/1-STORE`
* `TGK/STORE/1`
* Deterministic ordering checks
* Replay equivalence tests
* Acceptance criteria per tier
---
## 3.2 Backend Mapping
### ASL Mapping
* Artifact storage layout in MinIO
* Namespace strategy
* Object key derivation rules
* Versioning policy (if enabled)
### TGK Mapping
* Vertex/edge mapping strategy
* Deterministic ordering enforcement
* Cassandra schema design
* Index strategy in Elasticsearch (if used)
---
## 3.3 Consistency Model
Per tier:
* Write guarantees
* Read-after-write expectations
* Projection lag tolerance
* Eventual consistency boundaries
* Snapshot isolation model
---
## 3.4 Snapshot & Export Strategy
* Snapshot format (artifact set, graph export, columnar dataset)
* Snapshot identity model
* Export cadence (manual, periodic, event-triggered)
* Replay and equivalence validation rules
---
## 3.5 Governance & Projection Promotion
* Rules for elevating a projection to authoritative
* Audit requirements
* Approval workflow
---
## 3.6 Failure & Recovery
* Partial-write handling
* Corruption detection
* Divergence reconciliation
* Reprojection strategy
* Disaster recovery model
---
## 3.7 Pluggable Ops Registry
* Storage location
* Versioning strategy
* Discovery mechanism
* Trust policy
---
## 3.8 Execution Artifact Schemas
Define required fields for:
* ExecutionIntent
* ExecutionReceipt
* ExecutionOutput
* ExecutionObservation
Define canonical encoding and validation rules.
---
## 3.9 Security & Isolation
* Executor trust boundaries
* Secret management
* Artifact integrity validation
* Network isolation policies
---
## 3.10 Performance Targets
Define SLOs per tier:
* Ingest throughput
* Query latency
* Snapshot generation time
* Recovery time objectives
---
# 4. Architecture Mapping
---
## 4.1 ASL/1-STORE Implementation
### Responsibilities
* Canonical encoding validation
* Identity derivation
* Artifact immutability
* Retrieval guarantees
### Concrete Mapping
* MinIO object store
* Object key = Reference
* Metadata includes encoding profile version
* Optional replication policy
---
## 4.2 TGK/STORE/1 Implementation
### Responsibilities
* Deterministic graph projection
* Required query operations
* Error model conformance
* Deterministic ordering guarantees
### Concrete Mapping
* JanusGraph for query layer
* Cassandra for durable storage
* Explicit ordering enforcement in query results
* Elasticsearch as optional derived projection
---
## 4.3 Optional Projections
### Elasticsearch
* Full-text search
* Attribute filtering
### IPFS
* Artifact distribution and caching
### Lakehouse (MinIO + Nessie + Spark)
* Snapshot export
* Batch provenance analysis
* Audit workflows
All remain derived projections.
---
## 4.4 Provenance Strategy
`TGK/PROV/1` may be realized via:
* Native graph traversal
* Snapshot-based analytics
* Hybrid strategy
Must remain observationally equivalent.
---
# 5. Interfaces and Contracts
---
## 5.1 ASL Interface
* `put(Artifact) -> Reference`
* `get(Reference) -> Artifact`
Error model strictly per spec.
---
## 5.2 TGK Interface
* Required query operations per `TGK/STORE/1`
* Deterministic ordering rules
* Explicit error semantics
---
## 5.3 Serialization
* Canonical encoding at artifact boundary
* No alternative encoding permitted
* Versioned encoding profile tracking
---
# 6. Execution Model (Pluggable Ops)
---
## 6.1 Registry Structure
* External registry
* Versioned definitions
* Immutable op identifiers
* Policy-scoped availability
---
## 6.2 Executor Lifecycle
* Intent creation
* Dispatch
* Receipt capture
* Output capture
* Optional observation
---
## 6.3 Artifact Workflow
1. Write ExecutionIntent
2. Executor produces ExecutionReceipt
3. Executor produces ExecutionOutput
4. Optional ExecutionObservation
5. Link artifacts via EdgeArtifacts
All artifacts stored in ASL.
---
## 6.4 TGK Linking
* Intent → Receipt
* Receipt → Output
* Output → Observation
Graph projection ensures traceability.
---
# 7. Implementation Phases
---
## Phase 0 — Conformance Baseline
* Build ASL/TGK conformance harness
* Define canonical test datasets
* Establish replay checks
---
## Phase 1 — ASL Store
* MinIO integration
* Canonical encoding enforcement
* Hash-based identity derivation
* Conformance validation
---
## Phase 2 — TGK Projection
* Graph projection pipeline
* JanusGraph integration
* Deterministic ordering enforcement
* Conformance validation
---
## Phase 3 — Projections & Indexing
* Elasticsearch integration
* IPFS optional layer
* Monitoring of projection lag
---
## Phase 4 — Snapshot & Governance
* Snapshot export format
* Nessie integration
* Replay equivalence testing
* Governance workflows
---
## Phase 5 — Pluggable Ops & Executors
* Registry implementation
* Execution artifact schemas
* Executor runtime
* Traceability validation
---
# 8. Testing and Verification
Each phase must include:
* Deterministic replay checks
* Ordering validation tests
* Snapshot equivalence tests
* Cross-backend equivalence tests
No phase advances without passing conformance.
---
# 9. Operational Concerns
---
## 9.1 Deployment per Tier
Tier 0:
* Single-node embedded
Tier 1:
* Janus + Cassandra + ES
* MinIO
* Optional IPFS
Tier 2:
* Add Spark
* Add Nessie
* Governance workflows
---
## 9.2 Observability
* Artifact write metrics
* Graph projection metrics
* Projection lag monitoring
* Snapshot health indicators
---
## 9.3 Backup & Restore
* MinIO replication or erasure coding
* Cassandra repair strategy
* Snapshot archival policy
---
# 10. Open Questions and Risks
* Canonical test dataset size
* Cassandra compaction strategy
* Projection drift detection
* Snapshot storage growth
* Executor trust hardening
Each risk must have a mitigation strategy before Tier 2 rollout.
---
# Design Outcome
This implementation plan:
* Preserves core invariants
* Enables infrastructure scaling
* Prevents semantic leakage
* Structures phased, verifiable progress
* Makes the OSS stack a compliant backend implementation

View file

@ -1,145 +0,0 @@
# Indexing Requirements and Projections
Status: Draft
Scope: Define indexing contract, authority boundaries, and projection invariants for derived search/index systems (e.g., Elasticsearch).
---
# API Usage Rule (Normative)
1. Elasticsearch returns candidate References only.
2. Authoritative confirmation MUST occur via ASL/1-STORE and/or TGK/STORE/1.
3. Any ordering visible to clients MUST be computed using the canonical `(hash_id, digest)` ordering defined in TGK/STORE/1.
4. Elasticsearch MUST NOT be treated as a source of semantic truth.
---
# 1. Decision Checklist (Must Resolve Before Implementation)
1. Which artifact types are searchable.
2. Which fields are eligible for text extraction.
3. Whether `created_at` is intrinsic or ingestion-derived.
4. Checkpoint source of truth (ASL log position required).
5. Verification policy (which APIs require authoritative confirmation).
6. Edge indexing included in Phase A or deferred.
7. Redaction policy per TypeTag.
8. Projection schema versioning strategy.
No indexing implementation proceeds without resolving these.
---
# 2. Authority Boundary
## 2.1 Authoritative Sources
1. Artifact identity and content: ASL/1-STORE.
2. Graph semantics and ordering: TGK/STORE/1.
3. Canonical encoding: ENC/ASL1-CORE.
4. Identity derivation: ASL/1-CORE and HASH/ASL1.
Elasticsearch is a derived projection only.
## 2.2 No Semantic Drift Rule
1. No backend-specific interpretation of artifacts.
2. No inferred edges.
3. No implicit ordering.
4. No hidden type coercion.
5. All optimizations must be observationally equivalent to authoritative results.
---
# 3. Reference Encoding Rule
All `reference` fields indexed in ES MUST use canonical `ReferenceBytes` hex encoding as defined by ENC/ASL1-CORE.
This guarantees:
1. Unambiguous equality.
2. Stable identity across backends.
3. No ambiguity between `(hash_id, digest)` representations.
No alternative encodings are permitted.
---
# 4. Ordering Rule
Elasticsearch ordering MUST NOT be treated as authoritative.
Any client-visible ordering MUST be computed via the canonical `(hash_id, digest)` ordering defined in TGK/STORE/1.
If ES provides candidate edges, authoritative logic must re-order them.
---
# 5. Projection Contract
## 5.1 Replayability
Index state MUST be fully replayable from ASL log/index state, EdgeArtifacts, and deterministic decoding rules.
## 5.2 Observational Equivalence
Given identical ASL state, ES projection rebuilt from scratch MUST produce identical documents within a schema version.
## 5.3 Schema Versioning
Each projection index MUST have:
1. An explicit schema version, e.g., `projection_schema_version`.
2. The version recorded in a checkpoint artifact.
3. A versioned rebuild strategy.
Schema changes require full replay.
---
# 6. Lag Metric Definition
Lag is formally defined as:
```
Lag = ASL_head_log_position - indexer_checkpoint_log_position
```
Where:
1. `ASL_head_log_position` is authoritative.
2. `indexer_checkpoint_log_position` is recorded in a projection checkpoint artifact.
Lag MUST be observable via metrics.
---
# 7. Decoding and Extraction Rules
1. Artifact decoding MUST use only the declared encoding profile.
2. No heuristic decoding permitted.
3. Text extraction MUST be deterministic and versioned.
4. Extractor version MUST be stored in index documents.
---
# 8. Edge Indexing Guardrail
If edges are indexed:
1. Edge documents MUST be derived only from EdgeArtifacts.
2. No inferred adjacency allowed.
3. Edge semantics MUST respect TGK profile rules.
4. Ordering MUST be applied by the authoritative layer.
---
# 9. Security and Redaction
1. Per-TypeTag redaction policy is mandatory.
2. No secrets indexed unless explicitly approved.
3. Redaction rules MUST be declared before indexing begins.
---
# 10. Conformance Gates
1. System must function with ES disabled.
2. Full rebuild equivalence test required.
3. Ordering correctness test required.
4. Cross-backend equivalence test required.

View file

@ -1,67 +0,0 @@
# MinIO ASL Store Adapter
This adapter provides an S3-compatible `ASL/1-STORE` backend for MinIO. It is
opt-in for conformance runs and does not require MinIO or any external services
at build time.
## Required environment
These are required when running conformance against the MinIO backend:
- `AMDUAT_MINIO_ENDPOINT` (e.g. `http://localhost:9000`)
- `AMDUAT_MINIO_BUCKET` (e.g. `amduat`)
- `AMDUAT_MINIO_ACCESS_KEY`
- `AMDUAT_MINIO_SECRET_KEY`
Optional configuration:
- `AMDUAT_MINIO_REGION` (default: `us-east-1`)
- `AMDUAT_MINIO_PREFIX` (default: `asl`)
- `AMDUAT_MINIO_FORCE_PATH_STYLE` (default: `1`)
- `AMDUAT_MINIO_TLS_VERIFY` (default: `1`)
- `AMDUAT_MINIO_TIMEOUT_MS` (default: `30000`)
- `AMDUAT_MINIO_RETRY_MAX` (default: `3`)
## Conformance runs
MinIO is skipped unless explicitly enabled.
Example (opt-in conformance run):
```sh
AMDUAT_CONFORMANCE_ENABLE_MINIO=1 \
AMDUAT_MINIO_ENDPOINT=http://localhost:9000 \
AMDUAT_MINIO_BUCKET=amduat \
AMDUAT_MINIO_ACCESS_KEY=minioadmin \
AMDUAT_MINIO_SECRET_KEY=minioadmin \
./build/amduat_conformance_run --suite asl --backend minio --workdir /tmp/amduat_conformance
```
If required environment variables are missing, the backend is reported as
`SKIPPED` with the missing variable names.
## Key layout
Objects are stored under a sharded key derived from the canonical reference
hex:
```
<prefix>/<r0r1>/<r2r3>/<full_reference_hex>
```
## Error mapping
| HTTP/S3 response | Canonical taxonomy | Store error |
| --- | --- | --- |
| 404 | NotFound | `AMDUAT_ASL_STORE_ERR_NOT_FOUND` |
| 401, 403 | PermissionDenied | `AMDUAT_ASL_STORE_ERR_IO` |
| 408, 429 | TransientIO | `AMDUAT_ASL_STORE_ERR_IO` |
| 5xx | TransientIO | `AMDUAT_ASL_STORE_ERR_IO` |
| 4xx (other) | InvalidArtifact | `AMDUAT_ASL_STORE_ERR_INTEGRITY` |
| Network/TLS/parse errors | InternalError/TransientIO | `AMDUAT_ASL_STORE_ERR_IO` |
## Limitations
- HTTPS is not supported yet (HTTP-only). Use `http://` endpoints for now.
- Multipart uploads, streaming artifacts, and very large objects are not
implemented in Phase 0.

View file

@ -1,274 +0,0 @@
# OSS Backend Adapter Boundary
Status: Draft
Purpose: Define architectural boundaries, responsibilities, and conformance requirements for implementing the OSS backend stack (MinIO, JanusGraph, Cassandra, Elasticsearch, etc.) as spec-compliant adapters.
---
# 1. Architectural Position
The OSS backend is an implementation of store/query surfaces, not a semantic extension.
It implements:
1. ASL/1-STORE.
2. TGK/STORE/1.
3. Optional: TGK/PROV/1.
4. Optional: derived projections (ES, lakehouse).
It does not redefine:
1. Core semantics (ASL/1-CORE, PEL/1-CORE, TGK/1-CORE).
2. Canonical encoding (ENC/ASL1-CORE).
3. Identity derivation (ASL/1-CORE and HASH/ASL1).
---
# 2. Authority and Responsibility Model
## 2.1 Authoritative Components
Authoritative state resides in:
1. Deployed ASL/1-STORE implementation.
2. Deterministic TGK projection derived from ASL state.
Adapters must preserve this.
## 2.2 Derived Components
The following are derived projections:
1. Elasticsearch.
2. IPFS.
3. Lakehouse snapshots.
4. AI outputs.
Derived components must:
1. Be replayable.
2. Be observationally equivalent.
3. Never redefine semantics.
---
# 3. Adapter Modules
This document defines four adapter classes.
## 3.1 ASL Store Adapter (MinIO or S3)
### Purpose
Implements ASL/1-STORE.
### Responsibilities
1. Accept `put(Artifact) -> Reference`.
2. Enforce canonical encoding validation.
3. Persist artifact bytes under canonical identity.
4. Implement `get(Reference) -> Artifact | NotFound`.
5. Map backend errors to canonical error model.
### Non-Responsibilities
1. Computing identity (handled by core logic).
2. Interpreting artifact contents.
3. Modifying encoding.
4. Adding metadata not declared by spec.
### Determinism Requirements
1. Same Artifact → same Reference.
2. Same Reference → identical bytes returned.
3. No backend-specific mutation.
### Configuration Surface
1. Endpoint.
2. Credentials.
3. Bucket name.
4. Retry policy.
5. Multipart threshold.
6. Timeout policy.
---
## 3.2 TGK Store Adapter (JanusGraph plus Cassandra)
### Purpose
Implements TGK/STORE/1.
### Responsibilities
1. Provide required query operations defined in TGK/STORE/1.
2. Enforce canonical `(hash_id, digest)` ordering.
3. Maintain deterministic projection behavior.
4. Map errors to spec-defined error model.
### Non-Responsibilities
1. Creating semantic edges (done via EdgeArtifacts).
2. Redefining graph semantics.
3. Implicitly inferring relationships.
4. Providing authoritative ordering outside spec.
### Determinism Requirements
1. Same ASL state → same TGK answers.
2. Same queries → same ordered results.
3. Ordering must not depend on Janus internal traversal order.
### Ordering Enforcement Point
Ordering MUST be applied:
1. Explicitly in the adapter layer, or
2. In a canonical result transformation layer.
Never rely on backend default sort semantics.
---
## 3.3 Projection Adapter (Elasticsearch)
### Purpose
Accelerate search and filtering.
### Responsibilities
1. Project canonical artifact metadata.
2. Optionally project EdgeArtifacts.
3. Maintain replayable checkpoint.
4. Expose lag metric.
5. Enforce schema versioning.
### Non-Responsibilities
1. Authoritative artifact reads.
2. Authoritative graph queries.
3. Deterministic ordering.
### Verification Requirement
Any ES-derived candidate results must be:
1. Confirmed via ASL/1-STORE.
2. Ordered via TGK/STORE/1 logic before returning to clients.
---
## 3.4 Snapshot or Lakehouse Adapter (Future)
### Purpose
Batch export and audit workflows.
### Responsibilities
1. Snapshot export from authoritative state.
2. Versioned dataset generation.
3. Replay equivalence validation.
### Non-Responsibilities
1. Live semantic authority.
2. Mutation of ASL state.
---
# 4. Adapter Error Taxonomy
All adapters must map backend-specific errors into a canonical internal taxonomy:
1. NotFound.
2. InvalidArtifact.
3. PermissionDenied.
4. TransientIO.
5. InternalError.
No backend-specific error types may leak across the adapter boundary.
---
# 5. Conformance Gates
No adapter may be merged or promoted unless:
## 5.1 ASL Adapter
1. Passes ASL/1-STORE conformance suite.
2. Passes replay equivalence test.
3. Passes cross-backend equivalence test.
## 5.2 TGK Adapter
1. Passes TGK/STORE/1 required query suite.
2. Passes deterministic ordering test.
3. Passes cross-backend equivalence test.
## 5.3 Projection Adapter
1. System passes integration tests with projection disabled.
2. Rebuild equivalence test passes.
3. Lag metric exposed and validated.
---
# 6. Observability Contract
All adapters must expose:
1. Health endpoint.
2. Error metrics.
3. Operation latency metrics.
4. Determinism assertion failures.
5. Projection lag, if applicable.
---
# 7. Security Boundary
Adapters must:
1. Not embed secrets in artifacts.
2. Respect per-TypeTag redaction policies before indexing.
3. Enforce explicit credential configuration.
4. Avoid implicit trust of backend defaults.
---
# 8. Interaction with Conformance Harness
Adapter design is subordinate to the Phase 0 harness.
Implementation order:
1. Conformance harness operational.
2. Adapter scaffold.
3. Adapter passes harness.
4. Integration and load testing.
No adapter bypasses this sequence.
---
# 9. Design Outcome
This boundary ensures:
1. Thin adapters.
2. Zero semantic drift.
3. Deterministic reproducibility.
4. Backend substitutability.
5. Operational scalability without semantic mutation.
The OSS backend becomes:
An infrastructure implementation of store/query surfaces, not an evolution of the semantic core.
---
# Where You Are Now (Strategic Position)
You now have:
1. Semantic core locked.
2. Execution boundary defined.
3. Indexing contract defined.
4. Backend boundary defined.
5. Implementation phases defined.
You are ready to:
1. Design adapter scaffolds.
2. Build Phase 0 conformance harness.
3. Begin MinIO adapter implementation safely.

View file

@ -1,160 +0,0 @@
# Phase 0/1 Execution Checklist
## OSS Backend Enablement (MinIO-first)
### 0. Purpose
This checklist gates work so we do not start infrastructure integration until we can prove:
* spec conformance holds across multiple backends,
* deterministic ordering/replay invariants are testable,
* error-model mappings are pinned down.
---
## Phase 0 — Conformance Harness + Determinism Gates
### 0.1 Deliverables
1. **Multi-backend conformance runner** (same test vectors, multiple backends)
2. **Golden dataset generator** for ASL + TGK fixtures
3. **Deterministic equivalence suite**:
* ordering checks
* replay equivalence
* cross-backend observational equivalence (surface behavior)
### 0.2 Required decisions (turn into `docs/decision-checklist.md`)
* **Conformance test scope**: which spec assertions are MUST-pass for Tier 0 vs Tier 1
* **Equivalence definition**: what counts as “same result” for TGK/STORE operations (including ordering)
* **Error model mapping policy**: canonical internal error types vs adapter-specific errors
### 0.3 Conformance harness: what to build
**A) ASL/1-STORE conformance suite**
* Put/get round-trip for a set of canonical Artifacts
* Idempotent `put(Artifact)` returns same `Reference`
* `get(Reference)` exact artifact fidelity
* NotFound behavior and error codes
* Corruption detection behavior (if defined/expected by your layers)
* Optional: latency-independent invariants (no timing assumptions)
**B) TGK/STORE/1 conformance suite**
* Required operations per spec and error model
* Deterministic ordering assertions (as spec defines)
* “Same ASL state → same TGK answers” invariants
* Cross-backend equivalence: mem vs fs vs asl_index_fs must match for the same fixture set
**C) Replay determinism**
* Build a fixture log/index state
* Replay into a fresh store
* Assert equivalent outcomes (references returned, query results, ordering)
### 0.4 Acceptance criteria (Phase 0 gate)
* ✅ Same fixture set passes on at least **two existing backends**:
* `asl_store_fs` and `asl_store_index_fs`
* `tgk_store_mem` and `tgk_store_fs` (or `tgk_store_asl_index_fs`)
* ✅ Deterministic ordering checks are explicit and enforced
* ✅ A “backend contract test” can be run by CI with a single command
### 0.5 Where this touches the repo (suggested structure)
* `tests/conformance/asl_store/*.c`
* `tests/conformance/tgk_store/*.c`
* `tests/fixtures/*.bin|*.json|*.asl` (whatever your canonical fixture format is)
* `tools/amduat_conformance_run` (small runner, like your other tools)
---
## Phase 1 — ASL MinIO Adapter (Strict ASL/1-STORE)
### 1.1 Deliverables
1. `src/adapters/asl_store_s3_minio/asl_store_s3_minio.c` (name as you prefer)
2. Config + credentials plumbing for MinIO endpoint
3. Full Phase 0 conformance suite passing against MinIO
4. CI job (optional initially) that spins MinIO and runs conformance tests
### 1.2 Required decisions (Phase 1-specific)
* **Object key layout**: how `Reference` maps to bucket/key (e.g., prefix sharding)
* **Metadata policy**: do we store encoding/profile version as object metadata, tags, or in-band?
* **Multipart handling**: threshold + exact fidelity guarantees
* **Read-after-write expectation**: what you assume from MinIO deployment mode; what you test for
### 1.3 Adapter contract requirements (what must not change)
* `put(Artifact) -> Reference` computed by core rules; adapter must not accept a caller-provided reference
* `get(Reference) -> Artifact | NotFound` must return exact bytes for canonical decode
* Error mapping must be stable (auth errors, NotFound, transient IO)
### 1.4 Failure modes to explicitly test (Phase 1 gate)
* Bucket missing / permission denied
* NotFound for unknown reference
* Partial upload interruption (must not produce a “phantom success”)
* Corrupt object payload (if detectable at decode step, ensure it surfaces as decode error)
### 1.5 Acceptance criteria (Phase 1 gate)
* ✅ ASL/1-STORE conformance suite passes on MinIO
* ✅ Cross-backend equivalence: MinIO results identical to index/fs for the same fixture set
* ✅ Determinism: repeated `put` gives same reference, independent of environment
---
# Missing interfaces and tests to write (concrete list)
## A) Missing test harness capability (Phase 0)
* A single abstraction that can run the same tests against:
* `asl_store_fs`
* `asl_store_index_fs`
* future `asl_store_minio`
* Same for TGK store adapters
* A “golden fixture set” format (even if its just embedded byte arrays initially)
## B) Deterministic ordering assertions (Phase 0)
The repo has lots of tests, but not:
* a named suite that asserts **ordering** exactly as TGK/STORE defines
* cross-backend “same query, same ordering” checks
## C) Cross-backend equivalence harness (Phase 0)
You need an explicit mechanism that:
* seeds the same ASL artifact set into multiple ASL backends
* builds the same TGK projection inputs
* runs the same TGK queries and diffs results
## D) Adapter error normalization (Phase 0/1)
Define a small internal error taxonomy used by conformance tests so adapters can map:
* NotFound
* InvalidArtifact / decode error
* PermissionDenied
* TransientNetwork
* Internal/Unknown
Without this, MinIO will “pass” but be impossible to reason about operationally.
---
# What I would do next (lowest-risk sequence)
1. **Add `docs/decision-checklist.md`** and fill only Phase 0/1 decisions (dont boil the ocean).
2. Implement **conformance runner** that can already test FS vs index/log backends (prove the harness works).
3. Add deterministic ordering + replay equivalence tests.
4. Only then implement `asl_store_minio`, using the harness as your guardrail.

View file

@ -1,253 +0,0 @@
# Primitive Ops vs Pluggable Ops
This section defines the execution boundary between deterministic kernel semantics and extensible domain execution.
It preserves:
* Kernel determinism
* Store-neutral traceability
* Backend substitutability
* Observational equivalence
---
# 1. Terminology
## 1.1 Kernel / Primitive Op
A **Kernel Op** (Primitive Op) is an operation defined in:
`OPREG/PEL1-KERNEL`
It is part of the `PEL/1-CORE` contract.
Kernel ops define fixed, deterministic semantics and are considered part of Amduats invariant semantic core.
---
## 1.2 Pluggable Op
A **Pluggable Op** is an operation defined outside `OPREG/PEL1-KERNEL`.
It is not part of `PEL/1-CORE`.
Its execution semantics:
* Are versioned independently
* May be effectful
* Are governed by profile or deployment policy
* Must emit execution artifacts for traceability
Pluggable ops extend behavior without extending the kernel.
---
# 2. Kernel / Primitive Ops
## 2.1 Properties
Kernel ops:
* Are defined in `OPREG/PEL1-KERNEL`
* Are pure and deterministic
* Have no side effects
* Depend only on explicit inputs
* Produce canonical artifacts
* Have stable semantics governed by spec revision
Kernel ops are portable across all backend tiers.
They cannot:
* Access external systems
* Read hidden state
* Depend on environmental configuration
* Produce nondeterministic outputs
---
## 2.2 Examples
* `bytes.concat`
* `bytes.slice`
* `hash.asl1_256`
These operations:
* Operate solely on canonical encoded inputs
* Produce deterministic outputs
* Are observationally equivalent across implementations
---
# 3. Pluggable Ops
Pluggable ops enable domain-specific execution without extending the kernel.
They may:
* Invoke LLMs
* Execute SSH commands
* Configure infrastructure
* Perform device operations
* Call external APIs
They are not part of the core semantic contract.
---
## 3.1 Properties
Pluggable ops:
* Are defined in external registries
* May be effectful
* May interact with external systems
* May be nondeterministic
* Must produce execution artifacts
* Must remain observationally separable from kernel semantics
They extend behavior while preserving traceability.
---
# 4. Execution Artifact Model
Every pluggable op MUST emit execution artifacts to ASL.
These artifacts preserve determinism at the semantic layer, even if the effect itself is nondeterministic.
## 4.1 Required Artifacts
At minimum:
### 1. ExecutionIntent
Declares:
* Target
* Parameters
* Configuration
* Version of pluggable op
Represents the desired action.
---
### 2. ExecutionReceipt
Confirms:
* Action dispatch
* Acceptance by executor
* Execution attempt metadata
Represents the attempt.
---
### 3. ExecutionOutput
Captures:
* Stdout/stderr
* Returned data
* Produced artifacts
* Exit codes
Represents the immediate result.
---
### 4. ExecutionObservation (Recommended)
Captures:
* Observed external state
* Post-condition checks
* Environment validation
Represents the verified outcome.
---
All execution artifacts:
* Are canonical ASL artifacts
* Are immutable
* Derive identity via `ENC/ASL1-CORE` + `ASL/1-CORE` + `HASH/ASL1`
* Participate in TGK projection
---
# 5. Traceability Requirements
All pluggable ops MUST be traceable in TGK by linking:
* `ExecutionIntent → ExecutionReceipt`
* `ExecutionReceipt → ExecutionOutput`
* Optional: `ExecutionOutput → ExecutionObservation`
These links are created via EdgeArtifacts written to ASL.
This ensures:
* Deterministic graph projection
* Provenance recoverability
* Replay safety
* Auditability
Even if external execution is nondeterministic, the trace is deterministic.
---
# 6. Governance Boundary
The boundary between kernel and pluggable ops is strict.
## 6.1 Kernel
* Small
* Deterministic
* Slow-moving
* Versioned with PEL core
* Subject to formal governance
## 6.2 Pluggable
* Extensible
* Fast-moving
* Policy-scoped
* Profile-scoped
* May be deployment-specific
No pluggable op may redefine kernel semantics.
---
# 7. Interaction with Backend Plug-in Model
This execution model aligns with the No Semantic Drift Rule:
* Kernel ops are invariant across all backends.
* Pluggable ops must emit artifacts.
* Backends must treat execution artifacts as ordinary ASL artifacts.
* TGK projection must remain deterministic.
Backend-specific optimizations must remain observationally equivalent to the artifact graph defined by execution artifacts.
---
# 8. Design Outcome
This separation:
* Preserves deterministic core semantics
* Enables real-world execution
* Prevents semantic leakage into infrastructure
* Maintains backend substitutability
* Ensures enterprise-grade auditability
* Supports edge and embedded deployments
Primitive ops define what Amduat *is*.
Pluggable ops define what Amduat *does*.

View file

@ -1,81 +0,0 @@
# Projection Checkpoint Schema
Status: Draft
Scope: Schema for projection checkpoint artifacts used by derived indexers (e.g., Elasticsearch) to bind schema versions, replay state, and lag metrics to authoritative ASL state.
---
# 1. Purpose
Projection checkpoints make derived indexes replayable, auditable, and bounded. They tie a projection schema version to a specific ASL log position and snapshot identity so that rebuilds are deterministic and lag can be measured.
---
# 2. Canonical Checkpoint Artifact
Checkpoint artifacts are ASL artifacts whose bytes encode the following fields. The encoding is deterministic and versioned.
## 2.1 Minimal Fields
1. `checkpoint_version` (u32)
2. `projection_name` (string)
3. `projection_schema_version` (u32)
4. `asl_snapshot_id` (u64, optional if not available)
5. `asl_log_position` (u64, required)
6. `created_at` (u64, epoch milliseconds)
7. `builder_id` (string, identifying the indexer instance)
## 2.2 Optional Fields
1. `artifact_count` (u64)
2. `edge_count` (u64)
3. `error_count` (u64)
4. `lag_millis` (u64, derived)
5. `note` (string)
---
# 3. Identity and Storage
1. Checkpoints are stored as ASL artifacts using ASL/1-STORE semantics.
2. `projection_name` plus `projection_schema_version` must uniquely identify a projection format.
3. The checkpoint artifact reference is the authoritative identifier for the projection state.
---
# 4. Lag Metric Binding
Lag is defined as:
```
Lag = ASL_head_log_position - checkpoint.asl_log_position
```
The ASL head position is authoritative. The checkpoint log position is the last fully processed position.
---
# 5. Replay Rules
1. Rebuild begins at `checkpoint.asl_log_position + 1`.
2. Any schema change requires full replay from the earliest available log position.
3. Checkpoints must be monotonic in `asl_log_position` for a given projection.
---
# 6. Validation Requirements
When loading a checkpoint, the indexer MUST verify:
1. `projection_schema_version` matches the current build.
2. `asl_log_position` is not ahead of the authoritative ASL head.
3. `checkpoint_version` is supported.
4. If present, `asl_snapshot_id` matches the expected snapshot boundary.
---
# 7. Operational Guidance
1. Checkpoints should be written after a successful batch commit.
2. Checkpoints should be immutable; new checkpoints are separate artifacts.
3. Retain checkpoints for audit and rollback.

View file

@ -1,282 +0,0 @@
# Projector Pattern
**Status:** Draft
**Scope:** Define the minimal projector loop for derived projections in Amduat, using the repos cursor API and versioned checkpoint records stored as ASL records. This pattern applies to Elasticsearch indexing, lakehouse snapshots, IPFS exports, and other derived views.
---
## 1. Purpose
A **projector** is a deterministic process that:
1. reads authoritative ASL log/index state in a stable order,
2. applies a projection function to produce derived state (or derived side effects), and
3. persists progress as a **projection checkpoint record** stored in ASL.
Projectors accelerate discovery and analysis without becoming semantic authority.
---
## 2. Normative Rules
### 2.1 Authority
* **Authoritative truth** is the deployed `ASL/1-STORE` implementation plus deterministic TGK projection derived from ASL artifacts.
* Projectors produce **derived projections** only.
### 2.2 No Semantic Drift
* Projectors MUST NOT introduce backend-specific semantics.
* Projectors MUST NOT infer edges or meaning beyond declared encoding profiles and spec-defined decoding.
* Any optimization MUST be observationally equivalent to results obtainable from authoritative stores.
### 2.3 Disable-ability
* The system MUST remain correct when any projector is disabled.
* Projections may lag and may be eventually consistent.
---
## 3. Inputs, Outputs, and State
### 3.1 Inputs
A projector consumes:
* authoritative ASL index state (current head position)
* **ASL log records** read via cursor paging
* optional decoding of artifacts using only declared encoding profiles
**Important:** the ASL log is **not** a general “artifact publish/unpublish event stream.” Cursor replay iterates over **log records** (segment seals, tombstones, snapshot anchors, etc.), and any “what to project” logic must be derived from these records plus authoritative store state.
### 3.2 Outputs
A projector emits:
* derived side effects (e.g., write to Elasticsearch; export/pin to IPFS; write snapshot files)
* optional derived artifacts (if you store projection outputs back into ASL)
* **a projection checkpoint record** for restart/resume
### 3.3 Projector State Model
Projector state must be reconstructible from:
* authoritative ASL store + log/index state, and
* the last checkpoint record.
No hidden state is permitted.
---
## 4. Minimal Projector Loop
This is the canonical pattern. Implementations may wrap it in a service, cron job, or CLI, but the core loop remains the same.
### 4.1 Pseudocode (repo-aligned)
```c
// Inputs
amduat_asl_store_t *store = ...;
// Authoritative head
amduat_asl_index_state_t head = amduat_asl_index_current_state(store);
// Resume (optional)
projection_checkpoint_t ckpt;
bool have_ckpt = load_latest_checkpoint(store, /*projection_name*/ "...", &ckpt);
// Cursor start uses checkpoint log_position as after_logseq
uint64_t after_logseq = have_ckpt ? ckpt.log_position : 0;
for (;;) {
// Cursor reads log records (not “publish events”)
asl_log_page_t page = amduat_asl_log_read_since(store, after_logseq, /*limit*/ N);
if (page.count == 0) break;
// Project: interpret log records as needed, using only declared profiles for decoding.
projection_apply(page.records, page.count, /*derived sinks/state*/ ...);
// Advance deterministically to the returned next position
after_logseq = page.next_log_position;
// Persist checkpoint as an ASL record: schema = "projection/checkpoint"
projection_checkpoint_t next = (projection_checkpoint_t){
.projection_name = "...", // required
.projection_schema_version = SCHEMA_VER, // required
.log_position = after_logseq, // required
.builder_id = "...", // required
.created_at = 0, // required (0 allowed)
// optional: .snapshot_id = ..., counts, note, lag_millis, etc.
};
asl_record_t rec = projection_checkpoint_encode(&next); // schema "projection/checkpoint"
asl_put_record(store, &rec);
}
```
### 4.2 Determinism Requirements
* Given the same ASL store state and the same projection schema version, repeated runs MUST produce identical derived outputs (within the declared projection contract).
* Cursor paging boundaries MUST NOT affect results.
* A checkpoint MUST allow resume without changing derived outcomes.
### 4.3 Implementation Slice (Checklist + Stub)
**Checklist (minimal, repo-aligned):**
* Choose a stable `projection_name`, `projection_schema_version`, and `builder_id`.
* Read authoritative head state via `amduat_asl_index_current_state`.
* Page log records with `amduat_asl_log_read_since` (cursor-driven).
* Apply a deterministic projection step (even if it is just a counter in the stub).
* Encode a checkpoint via `amduat_asl_projection_checkpoint_encode_v1`.
* Store the checkpoint as an ASL record with schema `projection/checkpoint`.
* Verify the stored checkpoint decodes and matches the last processed `log_position`.
**Repo stub:** `tests/asl/test_asl_projection_replay.c` includes
`test_projector_loop_stub()` that runs the minimal loop and persists a checkpoint.
---
## 5. Projection Checkpoint Records
### 5.1 Storage Convention (Kernel Layer)
Projection checkpoints are stored as **ASL records** with schema string:
> `projection/checkpoint`
This is the kernel-layer storage convention for restart/resume progress tracking.
### 5.2 Required Fields (repo-aligned)
Current required fields are:
* `projection_name`
* `projection_schema_version`
* `log_position`
* `builder_id`
* `created_at` (may be `0`)
Optional fields may include (as supported by current implementation/schema):
* `snapshot_id`
* counts
* `note`
* `lag_millis`
(See `projection_checkpoint` implementation and schema doc.)
### 5.3 Lag Metric (repo-aligned)
Lag is computed against the authoritative ASL index head:
```text
Lag = head.log_position checkpoint.log_position
```
Where `head.log_position` comes from `amduat_asl_index_current_state(store)`.
---
## 6. Projection Schema Versioning
The repository currently treats `projection_schema_version` as a versioned field, but does not define major/minor/patch semantics in core.
**Recommended policy (not a MUST):**
* Treat incompatible schema changes as requiring full replay.
* Record schema version in each checkpoint.
* If schema version changes, prefer rebuild or explicit migration procedures.
Any stricter version policy should live in governance / deployment policy docs, not kernel-level requirements.
---
## 7. Decoding and Extraction Rules
* Projectors MAY decode artifacts, but decoding MUST use **only the declared encoding profile**.
* No heuristic decoding is permitted.
* Any extraction (e.g., text extraction for ES) must be deterministic and should be versioned (extractor version treated as part of the projection schema contract).
---
## 8. Ordering and Verification Rules
### 8.1 TGK Ordering
Any ordering visible to clients MUST be computed using canonical ordering defined by `TGK/STORE/1` (e.g., `(hash_id, digest)`), not by derived store defaults (ES sorting, DB insertion order, etc.).
### 8.2 Verification
If a projector drives an API that returns candidates (e.g., ES search):
* Results MUST be treated as **candidates**
* Truth MUST be confirmed against authoritative `ASL/1-STORE` and/or `TGK/STORE/1` before returning semantically meaningful answers.
---
## 9. How Specific Backends Fit
### 9.1 Elasticsearch
* ES stores derived documents keyed by canonical Reference hex.
* Projector writes ES docs and persists checkpoint records (`projection/checkpoint`) in ASL.
* ES is never authoritative; APIs must verify.
### 9.2 Lakehouse Snapshots (Nessie/Spark)
* Projector produces snapshot datasets derived from authoritative sources.
* Snapshot identifiers should be tied to a checkpoint boundary (log_position) for reproducibility.
### 9.3 IPFS Anchoring
* IPFS pinning is an export/distribution projection.
* Projector should emit pin/export receipts as artifacts (recommended).
* IPFS availability is not semantic truth.
---
## 10. Operational Guidance
### 10.1 Failure Handling
* Projector must be restartable from the last checkpoint.
* Partial writes to derived stores must not corrupt checkpoint progression:
* either write side effects then checkpoint, or
* checkpoint only after side effects are durably confirmed (policy choice; document per projector)
### 10.2 Observability
Expose:
* head log_position
* checkpoint log_position
* lag
* projection error counts / dead-letter queue
* rebuild duration
* verification fallback rate (for ES-backed APIs)
---
## 11. Non-Goals
This pattern does not define:
* daemon orchestration
* distributed consensus
* tenancy/authz
* semantic promotion of projections (governance handles promotion explicitly)
---
## 12. Design Outcome
This projector pattern ensures:
* replayable derived systems,
* stable restart/resume via checkpoint records,
* deterministic behavior independent of paging,
* strict separation of truth vs acceleration,
* safe foundations for MinIO/Janus/ES/IPFS without semantic drift.

View file

@ -1,76 +0,0 @@
# TGK Ordering Enforcement
Status: Draft
Scope: Canonical ordering rules for TGK/STORE/1 and where to enforce them in adapters and query pipelines.
---
# 1. Purpose
TGK/STORE/1 defines a canonical deterministic ordering for all edge and neighbor query results. This document records the rule and the enforcement points so implementations do not drift.
---
# 2. Canonical Ordering Rules
## 2.1 Edge Ordering
All adjacency and scan results MUST be ordered by:
1. For each edge `(edge_ref, body)`, form:
`order_key(edge_ref) = hash_id_bytes || digest_bytes`
2. `hash_id_bytes` is `edge_ref.hash_id` encoded as big-endian `u16`.
3. `digest_bytes` is the raw `edge_ref.digest`.
4. Sort edges in ascending lexicographic order of `order_key(edge_ref)`.
This is the normative ordering defined by TGK/STORE/1 and is equivalent to sorting by canonical `ReferenceBytes`.
## 2.2 Neighbor Ordering
If `neighbors` is implemented, neighbor results MUST be ordered by:
1. For each node `n`, form:
`node_order_key(n) = hash_id_bytes || digest_bytes`
2. `hash_id_bytes` is `n.hash_id` encoded as big-endian `u16`.
3. `digest_bytes` is the raw `n.digest`.
4. Sort neighbors in ascending lexicographic order of `node_order_key(n)`.
---
# 3. Enforcement Points
Ordering MUST be enforced by TGK/STORE/1 logic, not by backend defaults.
Acceptable enforcement points:
1. Adapter layer (preferred): apply ordering before returning `GraphEdgeView` lists.
2. Canonical result transformation layer: a shared sorting function used by all adapters.
Disallowed enforcement:
1. Relying on JanusGraph traversal order.
2. Relying on Elasticsearch sort order.
3. Any backend-specific implicit ordering.
---
# 4. Test Requirements
Conformance tests MUST verify:
1. Adjacency results are ordered per the canonical `(hash_id, digest)` rule.
2. `scan_edges` results follow the same ordering per page.
3. `neighbors` results use the canonical node ordering if the operation is exposed.
4. Cross-backend equivalence: the same snapshot yields identical ordered lists.
---
# 5. Implementation Notes
1. Sorting uses the canonical `ReferenceBytes` ordering for `edge_ref` and `node` references.
2. Ordering is snapshot-relative; it must be stable for a fixed snapshot.
3. If ES is used for candidate retrieval, the authoritative layer must re-apply ordering.

View file

@ -3,20 +3,14 @@
#include "amduat/asl/store.h"
#include <pthread.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
enum { AMDUAT_ASL_STORE_INDEX_FS_ROOT_MAX = 1024 };
enum {
AMDUAT_ASL_STORE_INDEX_FS_NEG_CACHE_CAP = 256,
AMDUAT_ASL_STORE_INDEX_FS_NEG_CACHE_DIGEST_MAX = 64
};
typedef struct {
bool enabled;
@ -36,15 +30,6 @@ typedef struct {
uint8_t record_visibility;
} amduat_asl_store_index_fs_visibility_policy_t;
typedef struct {
bool valid;
amduat_asl_snapshot_id_t snapshot_id;
amduat_asl_log_position_t log_position;
amduat_hash_id_t hash_id;
uint16_t digest_len;
uint8_t digest[AMDUAT_ASL_STORE_INDEX_FS_NEG_CACHE_DIGEST_MAX];
} amduat_asl_store_index_fs_neg_cache_entry_t;
typedef struct {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_snapshot_policy_t snapshot_policy;
@ -56,18 +41,7 @@ typedef struct {
uint64_t last_ingest_time_ns;
amduat_asl_snapshot_id_t next_snapshot_id;
bool snapshot_state_initialized;
bool log_tail_initialized;
uint64_t log_tail_logseq;
uint8_t log_tail_hash[32];
bool skip_exists_precheck;
void *open_segments;
amduat_asl_store_index_fs_neg_cache_entry_t
neg_cache[AMDUAT_ASL_STORE_INDEX_FS_NEG_CACHE_CAP];
uint16_t neg_cache_next;
pthread_mutex_t neg_cache_mutex;
pthread_mutex_t write_mutex;
uint32_t write_depth;
int write_lock_fd;
} amduat_asl_store_index_fs_t;
bool amduat_asl_store_index_fs_init(amduat_asl_store_index_fs_t *fs,

View file

@ -1,67 +0,0 @@
#ifndef AMDUAT_ASL_STORE_MINIO_H
#define AMDUAT_ASL_STORE_MINIO_H
#include "amduat/asl/store.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
enum {
AMDUAT_ASL_STORE_MINIO_ENDPOINT_MAX = 1024,
AMDUAT_ASL_STORE_MINIO_HOST_MAX = 256,
AMDUAT_ASL_STORE_MINIO_BUCKET_MAX = 256,
AMDUAT_ASL_STORE_MINIO_ACCESS_KEY_MAX = 256,
AMDUAT_ASL_STORE_MINIO_SECRET_KEY_MAX = 256,
AMDUAT_ASL_STORE_MINIO_REGION_MAX = 64,
AMDUAT_ASL_STORE_MINIO_PREFIX_MAX = 128
};
typedef struct {
amduat_asl_store_config_t store_config;
const char *endpoint;
const char *bucket;
const char *access_key;
const char *secret_key;
const char *region;
const char *prefix;
bool force_path_style;
bool tls_verify;
uint32_t timeout_ms;
uint32_t retry_max;
} amduat_asl_store_minio_config_t;
typedef struct {
amduat_asl_store_config_t store_config;
char endpoint[AMDUAT_ASL_STORE_MINIO_ENDPOINT_MAX];
char host[AMDUAT_ASL_STORE_MINIO_HOST_MAX];
uint16_t port;
bool use_tls;
char bucket[AMDUAT_ASL_STORE_MINIO_BUCKET_MAX];
char access_key[AMDUAT_ASL_STORE_MINIO_ACCESS_KEY_MAX];
char secret_key[AMDUAT_ASL_STORE_MINIO_SECRET_KEY_MAX];
char region[AMDUAT_ASL_STORE_MINIO_REGION_MAX];
char prefix[AMDUAT_ASL_STORE_MINIO_PREFIX_MAX];
bool force_path_style;
bool tls_verify;
uint32_t timeout_ms;
uint32_t retry_max;
} amduat_asl_store_minio_t;
void amduat_asl_store_minio_config_defaults(
amduat_asl_store_minio_config_t *config);
bool amduat_asl_store_minio_init(amduat_asl_store_minio_t *minio,
amduat_asl_store_minio_config_t config);
amduat_asl_store_ops_t amduat_asl_store_minio_ops(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ASL_STORE_MINIO_H */

View file

@ -1,28 +0,0 @@
#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 */

View file

@ -1,30 +0,0 @@
#ifndef AMDUAT_ASL_LOG_CURSOR_H
#define AMDUAT_ASL_LOG_CURSOR_H
#include "amduat/asl/store.h"
#include "amduat/enc/asl_log.h"
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Read log records with logseq > after_logseq.
* Caller owns out_records; free with amduat_enc_asl_log_free.
*/
bool amduat_asl_log_read_since(amduat_asl_store_t *store,
amduat_asl_log_position_t after_logseq,
size_t limit,
amduat_asl_log_record_t **out_records,
size_t *out_count,
amduat_asl_log_position_t *out_next_logseq,
bool *out_end);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ASL_LOG_CURSOR_H */

View file

@ -1,54 +0,0 @@
#ifndef AMDUAT_ASL_PROJECTION_CHECKPOINT_H
#define AMDUAT_ASL_PROJECTION_CHECKPOINT_H
#include "amduat/asl/store.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
enum {
AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION_V1 = 1
};
typedef struct {
uint32_t checkpoint_version;
amduat_octets_t projection_name;
uint32_t projection_schema_version;
bool has_snapshot_id;
amduat_asl_snapshot_id_t snapshot_id;
amduat_asl_log_position_t log_position;
uint64_t created_at;
amduat_octets_t builder_id;
bool has_artifact_count;
uint64_t artifact_count;
bool has_edge_count;
uint64_t edge_count;
bool has_error_count;
uint64_t error_count;
bool has_lag_millis;
uint64_t lag_millis;
bool has_note;
amduat_octets_t note;
} amduat_asl_projection_checkpoint_v1_t;
bool amduat_asl_projection_checkpoint_encode_v1(
const amduat_asl_projection_checkpoint_v1_t *checkpoint,
amduat_octets_t *out_bytes);
bool amduat_asl_projection_checkpoint_decode_v1(
amduat_octets_t bytes,
amduat_asl_projection_checkpoint_v1_t *out_checkpoint);
void amduat_asl_projection_checkpoint_free(
amduat_asl_projection_checkpoint_v1_t *checkpoint);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ASL_PROJECTION_CHECKPOINT_H */

View file

@ -1,137 +0,0 @@
#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 */

View file

@ -1,52 +0,0 @@
#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 */

View file

@ -1,38 +0,0 @@
#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 */

View file

@ -1,61 +0,0 @@
#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,15 +34,6 @@ typedef struct {
} amduat_hash_asl1_stream_t;
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]);
@ -58,11 +49,6 @@ bool amduat_hash_asl1_digest(amduat_hash_id_t hash_id,
amduat_octets_t input,
uint8_t *out,
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,
amduat_hash_asl1_stream_t *out);

View file

@ -1,17 +0,0 @@
#ifndef AMDUAT_UTIL_STRING_H
#define AMDUAT_UTIL_STRING_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* strdup replacement for strict C11 builds. */
char *amduat_strdup(const char *src);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_UTIL_STRING_H */

View file

@ -24,9 +24,6 @@ are data nodes that can be hashed and referenced.
## 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.
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":"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"}
{"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"}

View file

@ -1,43 +0,0 @@
# 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

@ -2,7 +2,6 @@
#include "amduat/asl/ref_text.h"
#include "amduat/enc/asl1_core_codec.h"
#include "amduat/util/string.h"
#include <errno.h>
#include <stdbool.h>
@ -610,7 +609,7 @@ amduat_asl_store_error_t amduat_asl_derivation_index_fs_add(
return AMDUAT_ASL_STORE_ERR_IO;
}
dir_a = amduat_strdup(path);
dir_a = strdup(path);
if (dir_a == NULL) {
free(path);
free(artifact_dir);
@ -627,7 +626,7 @@ amduat_asl_store_error_t amduat_asl_derivation_index_fs_add(
}
*dir_b = '\0';
dir_b = amduat_strdup(dir_a);
dir_b = strdup(dir_a);
if (dir_b == NULL) {
free(path);
free(dir_a);

View file

@ -187,7 +187,7 @@ bool amduat_asl_pointer_name_is_valid(const char *name) {
bool ok = (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '.' || c == '_' || c == '-' || c == '/' || c == ':';
c == '.' || c == '_' || c == '-' || c == '/';
if (!ok) {
return false;
}
@ -471,9 +471,7 @@ static amduat_asl_pointer_error_t amduat_asl_pointer_write_head(
const amduat_reference_t *prev_ref,
bool has_prev) {
char *tmp_path;
char *parent_dir = NULL;
size_t tmp_len;
int tmp_fd;
FILE *fp;
uint8_t header[AMDUAT_ASL_POINTER_MAGIC_LEN + 4u + 1u];
amduat_octets_t ref_bytes = amduat_octets(NULL, 0u);
@ -505,38 +503,17 @@ static amduat_asl_pointer_error_t amduat_asl_pointer_write_head(
prev_len = 0u;
}
tmp_len = strlen(path) + sizeof(".tmp.XXXXXX");
tmp_len = strlen(path) + 5u;
tmp_path = (char *)malloc(tmp_len);
if (tmp_path == NULL) {
free((void *)ref_bytes.data);
free((void *)prev_bytes.data);
return AMDUAT_ASL_POINTER_ERR_IO;
}
parent_dir = amduat_asl_pointer_parent_dir(path);
if (parent_dir == NULL || !amduat_asl_pointer_ensure_directory(parent_dir)) {
free(parent_dir);
free(tmp_path);
free((void *)ref_bytes.data);
free((void *)prev_bytes.data);
return AMDUAT_ASL_POINTER_ERR_IO;
}
snprintf(tmp_path, tmp_len, "%s.tmp.XXXXXX", path);
tmp_fd = mkstemp(tmp_path);
if (tmp_fd < 0 && errno == ENOENT &&
amduat_asl_pointer_ensure_directory(parent_dir)) {
tmp_fd = mkstemp(tmp_path);
}
if (tmp_fd < 0) {
free(parent_dir);
free(tmp_path);
free((void *)ref_bytes.data);
free((void *)prev_bytes.data);
return AMDUAT_ASL_POINTER_ERR_IO;
}
fp = fdopen(tmp_fd, "wb");
snprintf(tmp_path, tmp_len, "%s.tmp", path);
fp = fopen(tmp_path, "wb");
if (fp == NULL) {
close(tmp_fd);
(void)remove(tmp_path);
free(tmp_path);
free((void *)ref_bytes.data);
free((void *)prev_bytes.data);
@ -589,25 +566,23 @@ static amduat_asl_pointer_error_t amduat_asl_pointer_write_head(
}
if (err == AMDUAT_ASL_POINTER_OK && rename(tmp_path, path) != 0) {
if (errno == ENOENT && amduat_asl_pointer_ensure_directory(parent_dir) &&
rename(tmp_path, path) == 0) {
/* Recovered after recreating parent directory. */
} else {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
err = AMDUAT_ASL_POINTER_ERR_IO;
}
if (err == AMDUAT_ASL_POINTER_OK) {
if (!amduat_asl_pointer_fsync_directory(parent_dir)) {
amduat_log(AMDUAT_LOG_WARN,
"pointer fsync dir failed for %s", parent_dir);
err = AMDUAT_ASL_POINTER_ERR_IO;
char *parent_dir = amduat_asl_pointer_parent_dir(path);
if (parent_dir != NULL) {
if (!amduat_asl_pointer_fsync_directory(parent_dir)) {
amduat_log(AMDUAT_LOG_WARN,
"pointer fsync dir failed for %s", parent_dir);
err = AMDUAT_ASL_POINTER_ERR_IO;
}
free(parent_dir);
}
}
if (err != AMDUAT_ASL_POINTER_OK) {
(void)remove(tmp_path);
}
free(parent_dir);
free(tmp_path);
free((void *)ref_bytes.data);
free((void *)prev_bytes.data);
@ -651,13 +626,13 @@ amduat_asl_pointer_error_t amduat_asl_pointer_cas(
const amduat_reference_t *new_ref,
bool *out_swapped) {
char *head_path = NULL;
int lock_fd = -1;
int fd = -1;
struct flock lock;
bool exists = false;
amduat_reference_t current_ref;
amduat_reference_t prev_ref;
bool has_prev = false;
char *lock_path = NULL;
bool created = false;
amduat_asl_pointer_error_t err;
if (out_swapped == NULL) {
@ -679,18 +654,8 @@ amduat_asl_pointer_error_t amduat_asl_pointer_cas(
return AMDUAT_ASL_POINTER_ERR_IO;
}
lock_path = (char *)malloc(strlen(head_path) + sizeof(".lock"));
if (lock_path == NULL) {
free(head_path);
return AMDUAT_ASL_POINTER_ERR_IO;
}
snprintf(lock_path, strlen(head_path) + sizeof(".lock"), "%s.lock",
head_path);
/* Lock a stable sidecar file so lock ownership survives head-file renames. */
lock_fd = open(lock_path, O_RDWR | O_CREAT, 0644);
if (lock_fd < 0) {
free(lock_path);
fd = open(head_path, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
free(head_path);
return AMDUAT_ASL_POINTER_ERR_IO;
}
@ -700,13 +665,19 @@ amduat_asl_pointer_error_t amduat_asl_pointer_cas(
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
if (fcntl(lock_fd, F_SETLKW, &lock) != 0) {
close(lock_fd);
free(lock_path);
if (fcntl(fd, F_SETLKW, &lock) != 0) {
close(fd);
free(head_path);
return AMDUAT_ASL_POINTER_ERR_IO;
}
{
struct stat st;
if (fstat(fd, &st) == 0 && st.st_size == 0) {
created = true;
}
}
err = amduat_asl_pointer_read_head(head_path, name, &exists, &current_ref,
&prev_ref, &has_prev);
if (err == AMDUAT_ASL_POINTER_ERR_NOT_FOUND) {
@ -714,21 +685,24 @@ amduat_asl_pointer_error_t amduat_asl_pointer_cas(
err = AMDUAT_ASL_POINTER_OK;
}
if (err != AMDUAT_ASL_POINTER_OK) {
close(lock_fd);
free(lock_path);
close(fd);
free(head_path);
return err;
}
if (expected_exists != exists) {
close(lock_fd);
free(lock_path);
if (created) {
(void)unlink(head_path);
}
close(fd);
free(head_path);
return AMDUAT_ASL_POINTER_OK;
}
if (expected_exists && !amduat_reference_eq(*expected_ref, current_ref)) {
close(lock_fd);
free(lock_path);
if (created) {
(void)unlink(head_path);
}
close(fd);
free(head_path);
return AMDUAT_ASL_POINTER_OK;
}
@ -746,8 +720,7 @@ amduat_asl_pointer_error_t amduat_asl_pointer_cas(
if (has_prev) {
amduat_reference_free(&prev_ref);
}
close(lock_fd);
free(lock_path);
close(fd);
free(head_path);
return err;
}

View file

@ -2,7 +2,6 @@
#include "asl_store_fs_layout.h"
#include "amduat/asl/core.h"
#include "amduat/asl/identity.h"
#include "amduat/asl/ref_derive.h"
#include "amduat/enc/asl1_core.h"
#include "amduat/enc/asl1_core_codec.h"
@ -662,12 +661,10 @@ static amduat_asl_store_error_t amduat_asl_store_fs_get_impl(
}
stored_octets = amduat_octets(stored_bytes, stored_len);
if (!amduat_hash_asl1_digest_domain(
hash_desc->hash_id,
amduat_asl_identity_domain_separator(),
stored_octets,
computed_digest,
hash_desc->digest_len)) {
if (!amduat_hash_asl1_digest(hash_desc->hash_id,
stored_octets,
computed_digest,
hash_desc->digest_len)) {
free(computed_digest);
free(stored_bytes);
free(objects_path);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,288 +0,0 @@
#include "s3_sigv4.h"
#include "amduat/asl/core.h"
#include "amduat/hash/asl1.h"
#include "amduat/util/hex.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { AMDUAT_S3_SIGV4_SHA256_LEN = 32u, AMDUAT_S3_SIGV4_BLOCK_LEN = 64u };
static bool sha256_digest(const uint8_t *data, size_t len, uint8_t out[32]) {
amduat_octets_t input;
input.data = data;
input.len = len;
return amduat_hash_asl1_digest(AMDUAT_HASH_ASL1_ID_SHA256, input, out, 32u);
}
static bool sha256_hex(const uint8_t *data, size_t len, char out_hex[65]) {
uint8_t digest[AMDUAT_S3_SIGV4_SHA256_LEN];
if (!sha256_digest(data, len, digest)) {
return false;
}
if (!amduat_hex_encode_lower(digest, sizeof(digest), out_hex, 65u)) {
return false;
}
out_hex[64] = '\0';
return true;
}
static bool hmac_sha256(const uint8_t *key,
size_t key_len,
const uint8_t *msg,
size_t msg_len,
uint8_t out[32]) {
uint8_t key_block[AMDUAT_S3_SIGV4_BLOCK_LEN];
uint8_t ipad[AMDUAT_S3_SIGV4_BLOCK_LEN];
uint8_t opad[AMDUAT_S3_SIGV4_BLOCK_LEN];
uint8_t inner_hash[AMDUAT_S3_SIGV4_SHA256_LEN];
size_t i;
amduat_hash_asl1_stream_t stream;
memset(key_block, 0, sizeof(key_block));
if (key_len > sizeof(key_block)) {
if (!sha256_digest(key, key_len, key_block)) {
return false;
}
key_len = AMDUAT_S3_SIGV4_SHA256_LEN;
} else if (key_len > 0 && key != NULL) {
memcpy(key_block, key, key_len);
}
for (i = 0; i < sizeof(key_block); ++i) {
ipad[i] = (uint8_t)(key_block[i] ^ 0x36u);
opad[i] = (uint8_t)(key_block[i] ^ 0x5cu);
}
if (!amduat_hash_asl1_stream_init(AMDUAT_HASH_ASL1_ID_SHA256, &stream)) {
return false;
}
if (!amduat_hash_asl1_stream_update(
&stream, amduat_octets(ipad, sizeof(ipad))) ||
(msg_len > 0 &&
!amduat_hash_asl1_stream_update(
&stream, amduat_octets(msg, msg_len))) ||
!amduat_hash_asl1_stream_final(&stream, inner_hash, sizeof(inner_hash))) {
amduat_hash_asl1_stream_destroy(&stream);
return false;
}
amduat_hash_asl1_stream_destroy(&stream);
if (!amduat_hash_asl1_stream_init(AMDUAT_HASH_ASL1_ID_SHA256, &stream)) {
return false;
}
if (!amduat_hash_asl1_stream_update(
&stream, amduat_octets(opad, sizeof(opad))) ||
!amduat_hash_asl1_stream_update(
&stream, amduat_octets(inner_hash, sizeof(inner_hash))) ||
!amduat_hash_asl1_stream_final(&stream, out, AMDUAT_S3_SIGV4_SHA256_LEN)) {
amduat_hash_asl1_stream_destroy(&stream);
return false;
}
amduat_hash_asl1_stream_destroy(&stream);
return true;
}
bool amduat_s3_sigv4_build_authorization(const char *method,
const char *canonical_uri,
const char *canonical_query,
const char *host,
const char *payload_hash_hex,
const char *access_key,
const char *secret_key,
const char *region,
const char *amz_date,
char **out_authorization) {
const char *signed_headers = "host;x-amz-content-sha256;x-amz-date";
char date_stamp[9];
char *canonical_request = NULL;
char *string_to_sign = NULL;
char canonical_request_hash[65];
uint8_t k_date[AMDUAT_S3_SIGV4_SHA256_LEN];
uint8_t k_region[AMDUAT_S3_SIGV4_SHA256_LEN];
uint8_t k_service[AMDUAT_S3_SIGV4_SHA256_LEN];
uint8_t k_signing[AMDUAT_S3_SIGV4_SHA256_LEN];
uint8_t signature_bytes[AMDUAT_S3_SIGV4_SHA256_LEN];
char signature_hex[65];
char *auth_header = NULL;
size_t auth_len;
int rc;
if (out_authorization != NULL) {
*out_authorization = NULL;
}
if (method == NULL || canonical_uri == NULL || canonical_query == NULL ||
host == NULL || payload_hash_hex == NULL || access_key == NULL ||
secret_key == NULL || region == NULL || amz_date == NULL ||
out_authorization == NULL) {
return false;
}
if (strlen(amz_date) < 8u) {
return false;
}
memcpy(date_stamp, amz_date, 8u);
date_stamp[8] = '\0';
rc = snprintf(NULL,
0,
"%s\n%s\n%s\n"
"host:%s\n"
"x-amz-content-sha256:%s\n"
"x-amz-date:%s\n\n"
"%s\n"
"%s",
method,
canonical_uri,
canonical_query,
host,
payload_hash_hex,
amz_date,
signed_headers,
payload_hash_hex);
if (rc <= 0) {
return false;
}
canonical_request = (char *)malloc((size_t)rc + 1u);
if (canonical_request == NULL) {
return false;
}
snprintf(canonical_request,
(size_t)rc + 1u,
"%s\n%s\n%s\n"
"host:%s\n"
"x-amz-content-sha256:%s\n"
"x-amz-date:%s\n\n"
"%s\n"
"%s",
method,
canonical_uri,
canonical_query,
host,
payload_hash_hex,
amz_date,
signed_headers,
payload_hash_hex);
if (!sha256_hex((const uint8_t *)canonical_request,
strlen(canonical_request),
canonical_request_hash)) {
free(canonical_request);
return false;
}
rc = snprintf(NULL,
0,
"AWS4-HMAC-SHA256\n%s\n%s/%s/s3/aws4_request\n%s",
amz_date,
date_stamp,
region,
canonical_request_hash);
if (rc <= 0) {
free(canonical_request);
return false;
}
string_to_sign = (char *)malloc((size_t)rc + 1u);
if (string_to_sign == NULL) {
free(canonical_request);
return false;
}
snprintf(string_to_sign,
(size_t)rc + 1u,
"AWS4-HMAC-SHA256\n%s\n%s/%s/s3/aws4_request\n%s",
amz_date,
date_stamp,
region,
canonical_request_hash);
{
size_t secret_len = strlen(secret_key);
size_t key_len = 4u + secret_len;
char *key_prefix = (char *)malloc(key_len + 1u);
if (key_prefix == NULL) {
free(canonical_request);
free(string_to_sign);
return false;
}
memcpy(key_prefix, "AWS4", 4u);
memcpy(key_prefix + 4u, secret_key, secret_len);
key_prefix[key_len] = '\0';
if (!hmac_sha256((const uint8_t *)key_prefix,
key_len,
(const uint8_t *)date_stamp,
strlen(date_stamp),
k_date) ||
!hmac_sha256(k_date,
sizeof(k_date),
(const uint8_t *)region,
strlen(region),
k_region) ||
!hmac_sha256(k_region,
sizeof(k_region),
(const uint8_t *)"s3",
2u,
k_service) ||
!hmac_sha256(k_service,
sizeof(k_service),
(const uint8_t *)"aws4_request",
12u,
k_signing) ||
!hmac_sha256(k_signing,
sizeof(k_signing),
(const uint8_t *)string_to_sign,
strlen(string_to_sign),
signature_bytes)) {
free(key_prefix);
free(canonical_request);
free(string_to_sign);
return false;
}
free(key_prefix);
}
if (!amduat_hex_encode_lower(signature_bytes,
sizeof(signature_bytes),
signature_hex,
sizeof(signature_hex))) {
free(canonical_request);
free(string_to_sign);
return false;
}
auth_len = (size_t)snprintf(NULL,
0,
"AWS4-HMAC-SHA256 Credential=%s/%s/%s/s3/aws4_request, "
"SignedHeaders=%s, Signature=%s",
access_key,
date_stamp,
region,
signed_headers,
signature_hex);
auth_header = (char *)malloc(auth_len + 1u);
if (auth_header == NULL) {
free(canonical_request);
free(string_to_sign);
return false;
}
snprintf(auth_header,
auth_len + 1u,
"AWS4-HMAC-SHA256 Credential=%s/%s/%s/s3/aws4_request, "
"SignedHeaders=%s, Signature=%s",
access_key,
date_stamp,
region,
signed_headers,
signature_hex);
free(canonical_request);
free(string_to_sign);
*out_authorization = auth_header;
return true;
}

View file

@ -1,25 +0,0 @@
#ifndef AMDUAT_ADAPTERS_ASL_STORE_MINIO_S3_SIGV4_H
#define AMDUAT_ADAPTERS_ASL_STORE_MINIO_S3_SIGV4_H
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
bool amduat_s3_sigv4_build_authorization(const char *method,
const char *canonical_uri,
const char *canonical_query,
const char *host,
const char *payload_hash_hex,
const char *access_key,
const char *secret_key,
const char *region,
const char *amz_date,
char **out_authorization);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ADAPTERS_ASL_STORE_MINIO_S3_SIGV4_H */

View file

@ -1,5 +1,4 @@
#include "amduat/asl/log_store.h"
#include "amduat/asl/ref_text.h"
#include "amduat/enc/asl1_core_codec.h"
#include "amduat/util/log.h"
@ -13,8 +12,7 @@ enum {
AMDUAT_ASL_LOG_MAGIC_LEN = 8,
AMDUAT_ASL_LOG_VERSION = 1,
AMDUAT_ASL_LOG_CHUNK_MAX_ENTRIES = 1024u,
AMDUAT_ASL_LOG_MAX_RETRIES = 32u,
AMDUAT_ASL_LOG_VERIFY_MAX_RETRIES = 6u
AMDUAT_ASL_LOG_MAX_RETRIES = 8u
};
static const uint8_t k_amduat_asl_log_magic[AMDUAT_ASL_LOG_MAGIC_LEN] = {
@ -107,13 +105,6 @@ static bool amduat_asl_log_add_size(size_t *acc, size_t add) {
return true;
}
static void amduat_asl_log_retry_pause(uint32_t attempt) {
volatile uint32_t spins = (attempt < 15u ? (attempt + 1u) : 16u) * 50000u;
while (spins > 0u) {
spins--;
}
}
void amduat_asl_log_chunk_free(amduat_asl_log_chunk_t *chunk) {
if (chunk == NULL) {
return;
@ -534,9 +525,6 @@ amduat_asl_store_error_t amduat_asl_log_append(
}
if (!amduat_asl_log_build_pointer_name(log_name, &pointer_name)) {
amduat_log(AMDUAT_LOG_ERROR,
"asl_log_append: build pointer name failed (log=%s)",
log_name != NULL ? log_name : "(null)");
return AMDUAT_ASL_STORE_ERR_IO;
}
entry_count = (uint32_t)entries_len;
@ -553,12 +541,6 @@ amduat_asl_store_error_t amduat_asl_log_append(
ptr_err = amduat_asl_pointer_get(&log_store->pointer_store,
pointer_name, &head_exists, &head_ref);
if (ptr_err != AMDUAT_ASL_POINTER_OK) {
amduat_log(AMDUAT_LOG_ERROR,
"asl_log_append: pointer_get failed (log=%s pointer=%s attempt=%u err=%d)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
(int)ptr_err);
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
}
@ -567,34 +549,9 @@ amduat_asl_store_error_t amduat_asl_log_append(
store_err = amduat_asl_store_get(log_store->store, head_ref,
&head_artifact);
if (store_err != AMDUAT_ASL_STORE_OK) {
if (store_err == AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
char *head_ref_hex = NULL;
(void)amduat_asl_ref_encode_hex(head_ref, &head_ref_hex);
amduat_log(AMDUAT_LOG_WARN,
"asl_log_append: head chunk missing; retrying append from fresh head read (log=%s pointer=%s attempt=%u head_ref=%s)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
head_ref_hex != NULL ? head_ref_hex : "(hex-encode-failed)");
free(head_ref_hex);
amduat_reference_free(&head_ref);
amduat_asl_log_retry_pause(attempt);
continue;
} else {
char *head_ref_hex = NULL;
(void)amduat_asl_ref_encode_hex(head_ref, &head_ref_hex);
amduat_log(AMDUAT_LOG_ERROR,
"asl_log_append: store_get head failed (log=%s pointer=%s attempt=%u err=%d head_ref=%s)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
(int)store_err,
head_ref_hex != NULL ? head_ref_hex : "(hex-encode-failed)");
free(head_ref_hex);
amduat_reference_free(&head_ref);
free(pointer_name);
return store_err;
}
}
if (!head_artifact.has_type_tag ||
head_artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_LOG_CHUNK_1) {
@ -607,12 +564,6 @@ amduat_asl_store_error_t amduat_asl_log_append(
&head_chunk);
amduat_artifact_free(&head_artifact);
if (store_err != AMDUAT_ASL_STORE_OK) {
amduat_log(AMDUAT_LOG_ERROR,
"asl_log_append: decode head chunk failed (log=%s pointer=%s attempt=%u err=%d)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
(int)store_err);
amduat_reference_free(&head_ref);
free(pointer_name);
return store_err;
@ -647,12 +598,6 @@ amduat_asl_store_error_t amduat_asl_log_append(
store_err = amduat_asl_log_encode_chunk(&new_chunk, &encoded);
if (store_err != AMDUAT_ASL_STORE_OK) {
amduat_log(AMDUAT_LOG_ERROR,
"asl_log_append: encode chunk failed (log=%s pointer=%s attempt=%u err=%d)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
(int)store_err);
if (head_exists) {
amduat_reference_free(&head_ref);
}
@ -664,75 +609,12 @@ amduat_asl_store_error_t amduat_asl_log_append(
store_err = amduat_asl_store_put(log_store->store, artifact, &new_ref);
free((void *)encoded.data);
if (store_err != AMDUAT_ASL_STORE_OK) {
amduat_log(AMDUAT_LOG_ERROR,
"asl_log_append: store_put new chunk failed (log=%s pointer=%s attempt=%u err=%d)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
(int)store_err);
if (head_exists) {
amduat_reference_free(&head_ref);
}
free(pointer_name);
return store_err;
}
{
amduat_artifact_t verify_artifact;
amduat_asl_store_error_t verify_err;
uint32_t verify_attempt = 0u;
char *new_ref_hex = NULL;
(void)amduat_asl_ref_encode_hex(new_ref, &new_ref_hex);
while (verify_attempt < AMDUAT_ASL_LOG_VERIFY_MAX_RETRIES) {
verify_err = amduat_asl_store_get(log_store->store, new_ref,
&verify_artifact);
if (verify_err == AMDUAT_ASL_STORE_OK) {
break;
}
if (verify_err != AMDUAT_ASL_STORE_ERR_NOT_FOUND ||
verify_attempt + 1u >= AMDUAT_ASL_LOG_VERIFY_MAX_RETRIES) {
break;
}
verify_attempt++;
}
if (verify_err != AMDUAT_ASL_STORE_OK) {
if (verify_err == AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
/* Retry from a fresh head read instead of failing hard; this avoids
* surfacing transient visibility windows as append failures. */
amduat_log(AMDUAT_LOG_WARN,
"asl_log_append: store_put verify get missing; retrying append (log=%s pointer=%s attempt=%u verify_attempts=%u err=%d new_ref=%s)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
(unsigned)(verify_attempt + 1u),
(int)verify_err,
new_ref_hex != NULL ? new_ref_hex : "(hex-encode-failed)");
free(new_ref_hex);
amduat_reference_free(&new_ref);
if (head_exists) {
amduat_reference_free(&head_ref);
}
amduat_asl_log_retry_pause(attempt);
continue;
}
amduat_log(AMDUAT_LOG_ERROR,
"asl_log_append: store_put verify get failed (log=%s pointer=%s attempt=%u verify_attempts=%u err=%d new_ref=%s)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
(unsigned)(verify_attempt + 1u),
(int)verify_err,
new_ref_hex != NULL ? new_ref_hex : "(hex-encode-failed)");
free(new_ref_hex);
if (head_exists) {
amduat_reference_free(&head_ref);
}
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
} else {
amduat_artifact_free(&verify_artifact);
}
free(new_ref_hex);
}
{
bool swapped = false;
@ -747,12 +629,6 @@ amduat_asl_store_error_t amduat_asl_log_append(
amduat_reference_free(&head_ref);
}
if (cas_err != AMDUAT_ASL_POINTER_OK) {
amduat_log(AMDUAT_LOG_ERROR,
"asl_log_append: pointer_cas failed (log=%s pointer=%s attempt=%u err=%d)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)",
(unsigned)attempt,
(int)cas_err);
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
}
@ -765,15 +641,10 @@ amduat_asl_store_error_t amduat_asl_log_append(
return AMDUAT_ASL_STORE_OK;
}
amduat_log(AMDUAT_LOG_DEBUG, "log append CAS retry");
amduat_asl_log_retry_pause(attempt);
}
}
}
amduat_log(AMDUAT_LOG_ERROR,
"asl_log_append: max retries exhausted (log=%s pointer=%s)",
log_name != NULL ? log_name : "(null)",
pointer_name != NULL ? pointer_name : "(null)");
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
}

View file

@ -1,20 +0,0 @@
#include "amduat/util/string.h"
#include <stdlib.h>
#include <string.h>
char *amduat_strdup(const char *src) {
char *out;
size_t len;
if (src == NULL) {
return NULL;
}
len = strlen(src) + 1u;
out = (char *)malloc(len);
if (out == NULL) {
return NULL;
}
memcpy(out, src, len);
return out;
}

View file

@ -1,18 +0,0 @@
#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,101 +0,0 @@
#include "amduat/asl/log_cursor.h"
#include "amduat/enc/asl_log.h"
#include <stdlib.h>
#include <string.h>
static bool amduat_asl_log_cursor_copy_record(
const amduat_asl_log_record_t *src,
amduat_asl_log_record_t *out) {
if (src == NULL || out == NULL) {
return false;
}
*out = *src;
if (src->payload.len == 0u) {
out->payload = amduat_octets(NULL, 0u);
return true;
}
if (src->payload.data == NULL) {
return false;
}
{
uint8_t *buf = (uint8_t *)malloc(src->payload.len);
if (buf == NULL) {
return false;
}
memcpy(buf, src->payload.data, src->payload.len);
out->payload = amduat_octets(buf, src->payload.len);
}
return true;
}
bool amduat_asl_log_read_since(amduat_asl_store_t *store,
amduat_asl_log_position_t after_logseq,
size_t limit,
amduat_asl_log_record_t **out_records,
size_t *out_count,
amduat_asl_log_position_t *out_next_logseq,
bool *out_end) {
amduat_asl_log_record_t *records = NULL;
size_t record_count = 0u;
amduat_asl_store_error_t err;
size_t start = 0u;
size_t available;
size_t take;
if (out_records == NULL || out_count == NULL || out_next_logseq == NULL ||
out_end == NULL) {
return false;
}
*out_records = NULL;
*out_count = 0u;
*out_next_logseq = after_logseq;
*out_end = true;
if (store == NULL) {
return false;
}
err = amduat_asl_log_scan(store, &records, &record_count);
if (err != AMDUAT_ASL_STORE_OK) {
return false;
}
while (start < record_count && records[start].logseq <= after_logseq) {
start++;
}
available = record_count - start;
take = (limit == 0u || limit > available) ? available : limit;
if (take == 0u) {
*out_end = (start >= record_count);
amduat_enc_asl_log_free(records, record_count);
return true;
}
*out_records = (amduat_asl_log_record_t *)calloc(take, sizeof(**out_records));
if (*out_records == NULL) {
amduat_enc_asl_log_free(records, record_count);
return false;
}
for (size_t i = 0u; i < take; ++i) {
if (!amduat_asl_log_cursor_copy_record(&records[start + i],
&(*out_records)[i])) {
amduat_enc_asl_log_free(*out_records, i);
free(*out_records);
*out_records = NULL;
*out_count = 0u;
amduat_enc_asl_log_free(records, record_count);
return false;
}
}
*out_count = take;
*out_next_logseq = (*out_records)[take - 1u].logseq;
*out_end = (start + take >= record_count);
amduat_enc_asl_log_free(records, record_count);
return true;
}

View file

@ -1,472 +0,0 @@
#include "amduat/asl/projection_checkpoint.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
enum {
AMDUAT_ASL_PROJECTION_CHECKPOINT_MAGIC_LEN = 8,
AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION = 1,
AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_SNAPSHOT_ID = 1u << 0u,
AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_ARTIFACT_COUNT = 1u << 1u,
AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_EDGE_COUNT = 1u << 2u,
AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_ERROR_COUNT = 1u << 3u,
AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_LAG_MILLIS = 1u << 4u,
AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_NOTE = 1u << 5u
};
static const uint8_t k_amduat_asl_projection_checkpoint_magic[
AMDUAT_ASL_PROJECTION_CHECKPOINT_MAGIC_LEN] = {
'A', 'S', 'L', 'C', 'K', 'P', '1', '\0'
};
static void amduat_asl_checkpoint_store_u32_le(uint8_t *out, uint32_t value) {
out[0] = (uint8_t)(value & 0xffu);
out[1] = (uint8_t)((value >> 8) & 0xffu);
out[2] = (uint8_t)((value >> 16) & 0xffu);
out[3] = (uint8_t)((value >> 24) & 0xffu);
}
static void amduat_asl_checkpoint_store_u64_le(uint8_t *out, uint64_t value) {
out[0] = (uint8_t)(value & 0xffu);
out[1] = (uint8_t)((value >> 8) & 0xffu);
out[2] = (uint8_t)((value >> 16) & 0xffu);
out[3] = (uint8_t)((value >> 24) & 0xffu);
out[4] = (uint8_t)((value >> 32) & 0xffu);
out[5] = (uint8_t)((value >> 40) & 0xffu);
out[6] = (uint8_t)((value >> 48) & 0xffu);
out[7] = (uint8_t)((value >> 56) & 0xffu);
}
static bool amduat_asl_checkpoint_read_u32_le(const uint8_t *data,
size_t len,
size_t *offset,
uint32_t *out) {
if (len - *offset < 4u) {
return false;
}
*out = (uint32_t)data[*offset] |
((uint32_t)data[*offset + 1u] << 8) |
((uint32_t)data[*offset + 2u] << 16) |
((uint32_t)data[*offset + 3u] << 24);
*offset += 4u;
return true;
}
static bool amduat_asl_checkpoint_read_u64_le(const uint8_t *data,
size_t len,
size_t *offset,
uint64_t *out) {
if (len - *offset < 8u) {
return false;
}
*out = (uint64_t)data[*offset] |
((uint64_t)data[*offset + 1u] << 8) |
((uint64_t)data[*offset + 2u] << 16) |
((uint64_t)data[*offset + 3u] << 24) |
((uint64_t)data[*offset + 4u] << 32) |
((uint64_t)data[*offset + 5u] << 40) |
((uint64_t)data[*offset + 6u] << 48) |
((uint64_t)data[*offset + 7u] << 56);
*offset += 8u;
return true;
}
static bool amduat_asl_checkpoint_add_size(size_t *acc, size_t add) {
if (*acc > SIZE_MAX - add) {
return false;
}
*acc += add;
return true;
}
static bool amduat_asl_checkpoint_copy_octets(amduat_octets_t src,
amduat_octets_t *out) {
if (out == NULL) {
return false;
}
*out = amduat_octets(NULL, 0u);
if (src.len == 0u) {
return true;
}
if (src.data == NULL) {
return false;
}
{
uint8_t *buf = (uint8_t *)malloc(src.len);
if (buf == NULL) {
return false;
}
memcpy(buf, src.data, src.len);
*out = amduat_octets(buf, src.len);
}
return true;
}
bool amduat_asl_projection_checkpoint_encode_v1(
const amduat_asl_projection_checkpoint_v1_t *checkpoint,
amduat_octets_t *out_bytes) {
uint8_t *buffer;
size_t total_len = 0u;
size_t offset = 0u;
uint32_t flags = 0u;
if (checkpoint == NULL || out_bytes == NULL) {
return false;
}
out_bytes->data = NULL;
out_bytes->len = 0u;
if (checkpoint->checkpoint_version != AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION) {
return false;
}
if (checkpoint->projection_name.len == 0u ||
checkpoint->projection_name.data == NULL) {
return false;
}
if (checkpoint->builder_id.len == 0u ||
checkpoint->builder_id.data == NULL) {
return false;
}
if (checkpoint->projection_name.len > UINT32_MAX ||
checkpoint->builder_id.len > UINT32_MAX ||
checkpoint->note.len > UINT32_MAX) {
return false;
}
if (checkpoint->has_snapshot_id) {
flags |= AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_SNAPSHOT_ID;
}
if (checkpoint->has_artifact_count) {
flags |= AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_ARTIFACT_COUNT;
}
if (checkpoint->has_edge_count) {
flags |= AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_EDGE_COUNT;
}
if (checkpoint->has_error_count) {
flags |= AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_ERROR_COUNT;
}
if (checkpoint->has_lag_millis) {
flags |= AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_LAG_MILLIS;
}
if (checkpoint->has_note) {
flags |= AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_NOTE;
}
if (!amduat_asl_checkpoint_add_size(
&total_len,
AMDUAT_ASL_PROJECTION_CHECKPOINT_MAGIC_LEN + 4u + 4u)) {
return false;
}
if (!amduat_asl_checkpoint_add_size(&total_len,
4u + checkpoint->projection_name.len)) {
return false;
}
if (!amduat_asl_checkpoint_add_size(&total_len, 4u)) {
return false;
}
if (checkpoint->has_snapshot_id) {
if (!amduat_asl_checkpoint_add_size(&total_len, 8u)) {
return false;
}
}
if (!amduat_asl_checkpoint_add_size(&total_len, 8u + 8u)) {
return false;
}
if (!amduat_asl_checkpoint_add_size(&total_len,
4u + checkpoint->builder_id.len)) {
return false;
}
if (checkpoint->has_artifact_count &&
!amduat_asl_checkpoint_add_size(&total_len, 8u)) {
return false;
}
if (checkpoint->has_edge_count &&
!amduat_asl_checkpoint_add_size(&total_len, 8u)) {
return false;
}
if (checkpoint->has_error_count &&
!amduat_asl_checkpoint_add_size(&total_len, 8u)) {
return false;
}
if (checkpoint->has_lag_millis &&
!amduat_asl_checkpoint_add_size(&total_len, 8u)) {
return false;
}
if (checkpoint->has_note &&
!amduat_asl_checkpoint_add_size(&total_len, 4u + checkpoint->note.len)) {
return false;
}
buffer = (uint8_t *)malloc(total_len);
if (buffer == NULL) {
return false;
}
memcpy(buffer + offset, k_amduat_asl_projection_checkpoint_magic,
AMDUAT_ASL_PROJECTION_CHECKPOINT_MAGIC_LEN);
offset += AMDUAT_ASL_PROJECTION_CHECKPOINT_MAGIC_LEN;
amduat_asl_checkpoint_store_u32_le(buffer + offset,
AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION);
offset += 4u;
amduat_asl_checkpoint_store_u32_le(buffer + offset, flags);
offset += 4u;
amduat_asl_checkpoint_store_u32_le(buffer + offset,
(uint32_t)checkpoint->projection_name.len);
offset += 4u;
memcpy(buffer + offset,
checkpoint->projection_name.data,
checkpoint->projection_name.len);
offset += checkpoint->projection_name.len;
amduat_asl_checkpoint_store_u32_le(buffer + offset,
checkpoint->projection_schema_version);
offset += 4u;
if (checkpoint->has_snapshot_id) {
amduat_asl_checkpoint_store_u64_le(buffer + offset,
checkpoint->snapshot_id);
offset += 8u;
}
amduat_asl_checkpoint_store_u64_le(buffer + offset,
checkpoint->log_position);
offset += 8u;
amduat_asl_checkpoint_store_u64_le(buffer + offset,
checkpoint->created_at);
offset += 8u;
amduat_asl_checkpoint_store_u32_le(buffer + offset,
(uint32_t)checkpoint->builder_id.len);
offset += 4u;
memcpy(buffer + offset, checkpoint->builder_id.data,
checkpoint->builder_id.len);
offset += checkpoint->builder_id.len;
if (checkpoint->has_artifact_count) {
amduat_asl_checkpoint_store_u64_le(buffer + offset,
checkpoint->artifact_count);
offset += 8u;
}
if (checkpoint->has_edge_count) {
amduat_asl_checkpoint_store_u64_le(buffer + offset,
checkpoint->edge_count);
offset += 8u;
}
if (checkpoint->has_error_count) {
amduat_asl_checkpoint_store_u64_le(buffer + offset,
checkpoint->error_count);
offset += 8u;
}
if (checkpoint->has_lag_millis) {
amduat_asl_checkpoint_store_u64_le(buffer + offset,
checkpoint->lag_millis);
offset += 8u;
}
if (checkpoint->has_note) {
amduat_asl_checkpoint_store_u32_le(buffer + offset,
(uint32_t)checkpoint->note.len);
offset += 4u;
if (checkpoint->note.len != 0u) {
memcpy(buffer + offset, checkpoint->note.data, checkpoint->note.len);
offset += checkpoint->note.len;
}
}
if (offset != total_len) {
free(buffer);
return false;
}
*out_bytes = amduat_octets(buffer, total_len);
return true;
}
bool amduat_asl_projection_checkpoint_decode_v1(
amduat_octets_t bytes,
amduat_asl_projection_checkpoint_v1_t *out_checkpoint) {
size_t offset = 0u;
uint32_t version;
uint32_t flags;
uint32_t name_len;
uint32_t builder_len;
uint32_t note_len;
amduat_asl_projection_checkpoint_v1_t checkpoint;
if (out_checkpoint == NULL) {
return false;
}
memset(&checkpoint, 0, sizeof(checkpoint));
checkpoint.projection_name = amduat_octets(NULL, 0u);
checkpoint.builder_id = amduat_octets(NULL, 0u);
checkpoint.note = amduat_octets(NULL, 0u);
if (bytes.data == NULL || bytes.len < AMDUAT_ASL_PROJECTION_CHECKPOINT_MAGIC_LEN + 8u) {
return false;
}
if (memcmp(bytes.data,
k_amduat_asl_projection_checkpoint_magic,
AMDUAT_ASL_PROJECTION_CHECKPOINT_MAGIC_LEN) != 0) {
return false;
}
offset += AMDUAT_ASL_PROJECTION_CHECKPOINT_MAGIC_LEN;
if (!amduat_asl_checkpoint_read_u32_le(bytes.data, bytes.len, &offset, &version) ||
version != AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION) {
return false;
}
checkpoint.checkpoint_version = version;
if (!amduat_asl_checkpoint_read_u32_le(bytes.data, bytes.len, &offset, &flags)) {
return false;
}
if (!amduat_asl_checkpoint_read_u32_le(bytes.data, bytes.len, &offset, &name_len)) {
return false;
}
if (bytes.len - offset < name_len) {
return false;
}
if (name_len != 0u) {
checkpoint.projection_name = amduat_octets(NULL, 0u);
checkpoint.projection_name.len = name_len;
if (!amduat_asl_checkpoint_copy_octets(
amduat_octets(bytes.data + offset, name_len),
&checkpoint.projection_name)) {
return false;
}
}
offset += name_len;
if (!amduat_asl_checkpoint_read_u32_le(bytes.data, bytes.len, &offset,
&checkpoint.projection_schema_version)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
checkpoint.has_snapshot_id =
(flags & AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_SNAPSHOT_ID) != 0u;
if (checkpoint.has_snapshot_id) {
if (!amduat_asl_checkpoint_read_u64_le(bytes.data, bytes.len, &offset,
&checkpoint.snapshot_id)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
}
if (!amduat_asl_checkpoint_read_u64_le(bytes.data, bytes.len, &offset,
&checkpoint.log_position) ||
!amduat_asl_checkpoint_read_u64_le(bytes.data, bytes.len, &offset,
&checkpoint.created_at)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
if (!amduat_asl_checkpoint_read_u32_le(bytes.data, bytes.len, &offset,
&builder_len)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
if (bytes.len - offset < builder_len) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
if (builder_len != 0u) {
checkpoint.builder_id = amduat_octets(NULL, 0u);
checkpoint.builder_id.len = builder_len;
if (!amduat_asl_checkpoint_copy_octets(
amduat_octets(bytes.data + offset, builder_len),
&checkpoint.builder_id)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
}
offset += builder_len;
checkpoint.has_artifact_count =
(flags & AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_ARTIFACT_COUNT) != 0u;
if (checkpoint.has_artifact_count) {
if (!amduat_asl_checkpoint_read_u64_le(bytes.data, bytes.len, &offset,
&checkpoint.artifact_count)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
}
checkpoint.has_edge_count =
(flags & AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_EDGE_COUNT) != 0u;
if (checkpoint.has_edge_count) {
if (!amduat_asl_checkpoint_read_u64_le(bytes.data, bytes.len, &offset,
&checkpoint.edge_count)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
}
checkpoint.has_error_count =
(flags & AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_ERROR_COUNT) != 0u;
if (checkpoint.has_error_count) {
if (!amduat_asl_checkpoint_read_u64_le(bytes.data, bytes.len, &offset,
&checkpoint.error_count)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
}
checkpoint.has_lag_millis =
(flags & AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_LAG_MILLIS) != 0u;
if (checkpoint.has_lag_millis) {
if (!amduat_asl_checkpoint_read_u64_le(bytes.data, bytes.len, &offset,
&checkpoint.lag_millis)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
}
checkpoint.has_note =
(flags & AMDUAT_ASL_PROJECTION_CHECKPOINT_FLAG_NOTE) != 0u;
if (checkpoint.has_note) {
if (!amduat_asl_checkpoint_read_u32_le(bytes.data, bytes.len, &offset,
&note_len)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
if (bytes.len - offset < note_len) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
if (note_len != 0u) {
checkpoint.note = amduat_octets(NULL, 0u);
checkpoint.note.len = note_len;
if (!amduat_asl_checkpoint_copy_octets(
amduat_octets(bytes.data + offset, note_len),
&checkpoint.note)) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
}
offset += note_len;
}
if (offset != bytes.len) {
amduat_asl_projection_checkpoint_free(&checkpoint);
return false;
}
*out_checkpoint = checkpoint;
return true;
}
void amduat_asl_projection_checkpoint_free(
amduat_asl_projection_checkpoint_v1_t *checkpoint) {
if (checkpoint == NULL) {
return;
}
amduat_octets_free(&checkpoint->projection_name);
amduat_octets_free(&checkpoint->builder_id);
amduat_octets_free(&checkpoint->note);
checkpoint->projection_name = amduat_octets(NULL, 0u);
checkpoint->builder_id = amduat_octets(NULL, 0u);
checkpoint->note = amduat_octets(NULL, 0u);
}

View file

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

View file

@ -1,235 +0,0 @@
#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"
static amduat_enc_asl1_core_profile_desc_t g_enc_asl1_core_profiles[] = {
{AMDUAT_ENC_ASL1_CORE_V1, "ASL_ENC_CORE_V1", 0x0106, NULL}};
{AMDUAT_ENC_ASL1_CORE_V1, "ASL_ENC_CORE_V1", 0x0105, NULL}};
amduat_octets_t amduat_enc_asl1_core_key(
amduat_asl_encoding_profile_id_t profile_id,

View file

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

View file

@ -1,294 +0,0 @@
#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;
}

View file

@ -1,256 +0,0 @@
#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;
}

View file

@ -1,202 +0,0 @@
#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,58 +125,6 @@ bool amduat_hash_asl1_digest(amduat_hash_id_t hash_id,
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,
amduat_octets_t input) {
if (input.len != 0 && input.data == NULL) {

View file

@ -1,319 +0,0 @@
#include "asl_store_conformance.h"
#include "tgk_store_conformance.h"
#include "amduat/util/string.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
static void usage(FILE *out) {
fprintf(out,
"Usage: amduat_conformance_run [options]\n"
"\n"
"Options:\n"
" --suite asl|tgk|all Suite to run (default: all)\n"
" --backend <name|all> Backend to run (repeatable)\n"
" --workdir <path> Workdir for backend state\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");
}
static bool env_truthy(const char *value) {
if (value == NULL) {
return false;
}
return strcmp(value, "1") == 0 || strcmp(value, "true") == 0 ||
strcmp(value, "yes") == 0;
}
static bool append_backend(char **list, const char *backend) {
size_t list_len;
size_t backend_len;
size_t next_len;
char *next;
if (list == NULL || backend == NULL) {
return false;
}
if (*list == NULL) {
*list = amduat_strdup(backend);
return *list != NULL;
}
list_len = strlen(*list);
backend_len = strlen(backend);
next_len = list_len + 1u + backend_len + 1u;
next = (char *)realloc(*list, next_len);
if (next == NULL) {
return false;
}
next[list_len] = ',';
memcpy(next + list_len + 1u, backend, backend_len);
next[next_len - 1u] = '\0';
*list = next;
return true;
}
static int print_results(const char *suite,
const amduat_conformance_backend_result_t *results,
size_t results_len) {
size_t i;
int rc = 0;
if (results_len == 0) {
fprintf(stderr, "%s: no backends selected\n", suite);
return 1;
}
for (i = 0; i < results_len; ++i) {
const char *status =
results[i].status == AMDUAT_CONFORMANCE_STATUS_PASS
? "PASS"
: (results[i].status == AMDUAT_CONFORMANCE_STATUS_SKIP ? "SKIPPED"
: "FAIL");
printf("%s backend=%s %s\n", suite, results[i].backend, status);
if (results[i].status == AMDUAT_CONFORMANCE_STATUS_FAIL) {
rc = 1;
}
}
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(int 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) {
struct stat st;
if (path == NULL) {
return NULL;
}
if (stat(path, &st) == 0) {
return path;
}
if (strcmp(path, "tests/fixtures/conformance") == 0) {
if (stat("../tests/fixtures/conformance", &st) == 0) {
return "../tests/fixtures/conformance";
}
}
return path;
}
int main(int argc, char **argv) {
const char *suite = "all";
const char *fixtures = "tests/fixtures/conformance";
const char *workdir = "/tmp/amduat_conformance";
const char *json_out_path = NULL;
char *backend_selection = NULL;
bool include_optional = env_truthy(getenv("AMDUAT_CONFORMANCE_INCLUDE_OPTIONAL"));
bool json_out = false;
int i;
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) {
if (strcmp(argv[i], "--help") == 0) {
usage(stdout);
free(backend_selection);
return 0;
}
if (strcmp(argv[i], "--suite") == 0 && i + 1 < argc) {
suite = argv[++i];
continue;
}
if (strcmp(argv[i], "--backend") == 0 && i + 1 < argc) {
const char *value = argv[++i];
if (strcmp(value, "all") == 0) {
free(backend_selection);
backend_selection = amduat_strdup("all");
if (backend_selection == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
} else {
if (backend_selection == NULL || strcmp(backend_selection, "all") != 0) {
if (!append_backend(&backend_selection, value)) {
fprintf(stderr, "out of memory\n");
free(backend_selection);
return 1;
}
}
}
continue;
}
if (strcmp(argv[i], "--workdir") == 0 && i + 1 < argc) {
workdir = argv[++i];
continue;
}
if (strcmp(argv[i], "--fixtures") == 0 && i + 1 < argc) {
fixtures = argv[++i];
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]);
usage(stderr);
free(backend_selection);
return 1;
}
fixtures = fallback_fixtures_root(fixtures);
if (backend_selection == NULL) {
backend_selection = amduat_strdup("all");
if (backend_selection == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
}
if (strcmp(suite, "asl") == 0 || strcmp(suite, "all") == 0) {
int suite_rc = amduat_asl_store_conformance_run(
backend_selection, fixtures, workdir, include_optional, true,
&asl_results, &asl_results_len);
if (suite_rc != 0) {
rc = 1;
}
if (!json_out) {
if (print_results("asl", asl_results, asl_results_len) != 0) {
rc = 1;
}
}
}
if (strcmp(suite, "tgk") == 0 || strcmp(suite, "all") == 0) {
int suite_rc = amduat_tgk_store_conformance_run(
backend_selection, fixtures, workdir, include_optional, true,
&tgk_results, &tgk_results_len);
if (suite_rc != 0) {
rc = 1;
}
if (!json_out) {
if (print_results("tgk", tgk_results, tgk_results_len) != 0) {
rc = 1;
}
}
}
if (strcmp(suite, "asl") != 0 && strcmp(suite, "tgk") != 0 &&
strcmp(suite, "all") != 0) {
fprintf(stderr, "invalid suite: %s\n", suite);
usage(stderr);
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);
return rc;
}

View file

@ -1,402 +0,0 @@
#include "amduat/asl/asl_derivation_index_fs.h"
#include "amduat/asl/asl_store_index_fs.h"
#include "amduat/asl/collection.h"
#include "amduat/asl/log_cursor.h"
#include "amduat/asl/projection_checkpoint.h"
#include "amduat/asl/record.h"
#include "amduat/asl/ref_text.h"
#include "amduat/asl/store.h"
#include "amduat/enc/asl1_core.h"
#include "amduat/enc/tgk1_edge.h"
#include "amduat/hash/asl1.h"
#include "amduat/pel/derivation_sid.h"
#include "amduat/tgk/store.h"
#include "amduat/tgk/tgk_store_asl_index_fs.h"
#include "amduat/util/string.h"
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static char *make_temp_root(void) {
char *templ;
const char template_prefix[] = "/tmp/amduat_scriptlab_adapter_XXXXXX";
templ = (char *)malloc(sizeof(template_prefix));
if (templ == NULL) {
return NULL;
}
memcpy(templ, template_prefix, sizeof(template_prefix));
if (mkdtemp(templ) == NULL) {
free(templ);
return NULL;
}
return templ;
}
static void print_ref(const char *label, amduat_reference_t ref) {
char *hex = NULL;
if (!amduat_asl_ref_encode_hex(ref, &hex)) {
fprintf(stderr, "%s: <encode failed>\n", label);
return;
}
printf("%s=%s\n", label, hex);
free(hex);
}
static bool ref_list_contains(amduat_reference_t needle,
const amduat_reference_t *list,
size_t len) {
for (size_t i = 0u; i < len; ++i) {
if (amduat_reference_eq(needle, list[i])) {
return true;
}
}
return false;
}
int main(int argc, char **argv) {
const char *root_arg = NULL;
char *root = NULL;
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t asl_fs;
amduat_asl_store_t asl_store;
amduat_asl_collection_store_t collection_store;
amduat_asl_derivation_index_fs_t deriv_index;
amduat_reference_t node_a;
amduat_reference_t node_b;
amduat_reference_t edge_payload_ref;
amduat_reference_t edge_ref;
amduat_reference_t snapshot_ref;
amduat_reference_t program_ref;
amduat_reference_t checkpoint_ref;
amduat_octets_t edge_bytes = amduat_octets(NULL, 0u);
amduat_octets_t checkpoint_bytes = amduat_octets(NULL, 0u);
amduat_octets_t sid = amduat_octets(NULL, 0u);
amduat_asl_log_record_t *log_records = NULL;
size_t log_count = 0u;
amduat_asl_log_position_t next_logseq = 0u;
bool log_end = true;
amduat_asl_index_state_t index_state;
uint64_t collection_offset = 0u;
bool swapped = false;
int exit_code = 1;
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--root") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "--root requires a path\n");
return 2;
}
root_arg = argv[++i];
} else if (strcmp(argv[i], "--help") == 0 ||
strcmp(argv[i], "-h") == 0) {
fprintf(stdout, "usage: amduat-scriptlab-adapter [--root PATH]\n");
return 0;
} else {
fprintf(stderr, "unknown argument: %s\n", argv[i]);
return 2;
}
}
if (root_arg != NULL) {
root = amduat_strdup(root_arg);
} else {
root = make_temp_root();
}
if (root == NULL) {
fprintf(stderr, "failed to create root\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&asl_fs, config, root)) {
fprintf(stderr, "index fs init failed: %s\n", root);
goto cleanup_root;
}
amduat_asl_store_init(&asl_store, config, amduat_asl_store_index_fs_ops(), &asl_fs);
if (!amduat_asl_collection_store_init(&collection_store, root, &asl_store)) {
fprintf(stderr, "collection store init failed\n");
goto cleanup_root;
}
if (!amduat_asl_derivation_index_fs_init(&deriv_index, root)) {
fprintf(stderr, "derivation index init failed\n");
goto cleanup_root;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("scriptlab/node", strlen("scriptlab/node")),
amduat_octets("A", 1u),
&node_a) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "node A store failed\n");
goto cleanup_root;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("scriptlab/node", strlen("scriptlab/node")),
amduat_octets("B", 1u),
&node_b) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "node B store failed\n");
goto cleanup_nodes;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("tgk/edge_payload", strlen("tgk/edge_payload")),
amduat_octets("relates", strlen("relates")),
&edge_payload_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "edge payload store failed\n");
goto cleanup_nodes;
}
{
amduat_tgk_edge_body_t edge;
amduat_reference_t from_refs[1] = {node_a};
amduat_reference_t to_refs[1] = {node_b};
memset(&edge, 0, sizeof(edge));
edge.type = 1u;
edge.from = from_refs;
edge.from_len = 1u;
edge.to = to_refs;
edge.to_len = 1u;
edge.payload = edge_payload_ref;
if (!amduat_enc_tgk1_edge_encode_v1(&edge, &edge_bytes)) {
fprintf(stderr, "edge encode failed\n");
goto cleanup_nodes;
}
}
{
amduat_artifact_t edge_artifact = amduat_artifact_with_type(
edge_bytes, amduat_type_tag(AMDUAT_TYPE_TAG_TGK1_EDGE_V1));
if (amduat_asl_store_put(&asl_store, edge_artifact, &edge_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "edge store failed\n");
goto cleanup_edge_bytes;
}
}
amduat_octets_free(&edge_bytes);
if (amduat_asl_collection_append(&collection_store,
"tgk_edges",
edge_ref,
0u,
amduat_octets(NULL, 0u),
&collection_offset) != AMDUAT_ASL_COLLECTION_OK) {
fprintf(stderr, "collection append failed\n");
goto cleanup_edge_ref;
}
if (amduat_asl_collection_snapshot(&collection_store,
"tgk_edges",
UINT64_MAX,
&snapshot_ref,
&swapped) != AMDUAT_ASL_COLLECTION_OK) {
fprintf(stderr, "collection snapshot failed\n");
goto cleanup_edge_ref;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("scriptlab/transform", strlen("scriptlab/transform")),
amduat_octets("tgk_snapshot_v1", strlen("tgk_snapshot_v1")),
&program_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "program record store failed\n");
goto cleanup_snapshot_ref;
}
{
amduat_pel_derivation_sid_input_t sid_input;
amduat_asl_derivation_record_t record;
amduat_octets_t exec_profile =
amduat_octets("scriptlab/adapter", strlen("scriptlab/adapter"));
amduat_reference_t inputs[1] = {edge_ref};
memset(&sid_input, 0, sizeof(sid_input));
sid_input.program_ref = program_ref;
sid_input.input_refs = inputs;
sid_input.input_refs_len = 1u;
sid_input.has_params_ref = false;
sid_input.has_exec_profile = true;
sid_input.exec_profile = exec_profile;
if (!amduat_pel_derivation_sid_compute(&sid_input, &sid)) {
fprintf(stderr, "sid compute failed\n");
goto cleanup_program_ref;
}
memset(&record, 0, sizeof(record));
record.sid = sid;
record.program_ref = program_ref;
record.output_index = 0u;
record.input_refs = inputs;
record.input_refs_len = 1u;
record.has_params_ref = false;
record.has_exec_profile = true;
record.exec_profile = exec_profile;
if (amduat_asl_derivation_index_fs_add(&deriv_index,
snapshot_ref,
&record) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "derivation index add failed\n");
goto cleanup_sid;
}
}
if (!amduat_asl_index_current_state(&asl_store, &index_state)) {
fprintf(stderr, "index state read failed\n");
goto cleanup_sid;
}
if (!amduat_asl_log_read_since(&asl_store,
0u,
0u,
&log_records,
&log_count,
&next_logseq,
&log_end)) {
fprintf(stderr, "log read since failed\n");
goto cleanup_sid;
}
{
amduat_asl_projection_checkpoint_v1_t checkpoint;
memset(&checkpoint, 0, sizeof(checkpoint));
checkpoint.checkpoint_version = AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION_V1;
checkpoint.projection_name =
amduat_octets("scriptlab", strlen("scriptlab"));
checkpoint.projection_schema_version = 1u;
checkpoint.has_snapshot_id = true;
checkpoint.snapshot_id = index_state.snapshot_id;
checkpoint.log_position = next_logseq;
checkpoint.created_at = 0u;
checkpoint.builder_id =
amduat_octets("scriptlab-adapter", strlen("scriptlab-adapter"));
checkpoint.has_artifact_count = true;
checkpoint.artifact_count = log_count;
if (!amduat_asl_projection_checkpoint_encode_v1(&checkpoint,
&checkpoint_bytes)) {
fprintf(stderr, "checkpoint encode failed\n");
goto cleanup_log_records;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("projection/checkpoint",
strlen("projection/checkpoint")),
checkpoint_bytes,
&checkpoint_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "checkpoint store failed\n");
goto cleanup_checkpoint_bytes;
}
}
amduat_octets_free(&checkpoint_bytes);
{
amduat_tgk_identity_domain_t domains[1];
uint32_t edge_tags[1];
amduat_tgk_edge_type_id_t edge_types[1];
amduat_asl_encoding_profile_id_t encodings[1];
amduat_tgk_store_config_t tgk_config;
amduat_tgk_store_asl_index_fs_t tgk_fs;
amduat_tgk_store_t tgk_store;
amduat_tgk_edge_type_filter_t filter;
amduat_tgk_graph_edge_view_list_t edges;
bool found = false;
memset(&tgk_config, 0, sizeof(tgk_config));
domains[0].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1;
domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
tgk_config.id_space.domains = domains;
tgk_config.id_space.domains_len = 1u;
tgk_config.artifact_scope.description = amduat_octets(NULL, 0u);
edge_tags[0] = AMDUAT_TYPE_TAG_TGK1_EDGE_V1;
edge_types[0] = 1u;
encodings[0] = TGK1_EDGE_ENC_V1;
tgk_config.tgk_profiles.edge_tags = edge_tags;
tgk_config.tgk_profiles.edge_tags_len = 1u;
tgk_config.tgk_profiles.edge_types = edge_types;
tgk_config.tgk_profiles.edge_types_len = 1u;
tgk_config.tgk_profiles.encodings = encodings;
tgk_config.tgk_profiles.encodings_len = 1u;
if (!amduat_tgk_store_asl_index_fs_init(&tgk_fs, tgk_config, &asl_fs)) {
fprintf(stderr, "tgk store init failed\n");
goto cleanup_checkpoint_ref;
}
amduat_tgk_store_init(&tgk_store,
tgk_config,
amduat_tgk_store_asl_index_fs_ops(),
&tgk_fs);
filter.types = edge_types;
filter.types_len = 1u;
if (!amduat_tgk_store_edges_from(&tgk_store, node_a, filter, &edges)) {
fprintf(stderr, "tgk edges_from failed\n");
goto cleanup_checkpoint_ref;
}
for (size_t i = 0u; i < edges.len; ++i) {
amduat_tgk_edge_body_t *body = &edges.edges[i].body;
if (ref_list_contains(node_b, body->to, body->to_len)) {
found = true;
break;
}
}
amduat_tgk_graph_edge_view_list_free(&edges);
if (!found) {
fprintf(stderr, "tgk traversal missing expected edge\n");
goto cleanup_checkpoint_ref;
}
}
print_ref("node_a", node_a);
print_ref("node_b", node_b);
print_ref("edge", edge_ref);
print_ref("snapshot", snapshot_ref);
print_ref("checkpoint", checkpoint_ref);
exit_code = 0;
cleanup_checkpoint_ref:
amduat_reference_free(&checkpoint_ref);
cleanup_log_records:
amduat_enc_asl_log_free(log_records, log_count);
cleanup_checkpoint_bytes:
amduat_octets_free(&checkpoint_bytes);
cleanup_sid:
amduat_octets_free(&sid);
cleanup_program_ref:
amduat_reference_free(&program_ref);
cleanup_snapshot_ref:
amduat_reference_free(&snapshot_ref);
cleanup_edge_ref:
amduat_reference_free(&edge_ref);
cleanup_edge_bytes:
amduat_octets_free(&edge_bytes);
cleanup_nodes:
amduat_reference_free(&edge_payload_ref);
amduat_reference_free(&node_b);
amduat_reference_free(&node_a);
cleanup_root:
if (root_arg == NULL) {
fprintf(stderr, "root=%s\n", root);
}
free(root);
return exit_code;
}

View file

@ -1,139 +0,0 @@
#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

@ -1,177 +0,0 @@
#include "amduat/asl/asl_store_index_fs.h"
#include "amduat/asl/store.h"
#include "amduat/enc/asl1_core.h"
#include "amduat/hash/asl1.h"
#include "amduat/util/string.h"
#include <dirent.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static bool join_path(const char *base, const char *segment, char **out_path) {
size_t base_len;
size_t seg_len;
bool needs_sep;
size_t total_len;
char *buffer;
size_t offset;
if (base == NULL || segment == NULL || out_path == NULL) {
return false;
}
base_len = strlen(base);
seg_len = strlen(segment);
if (base_len == 0u || seg_len == 0u) {
return false;
}
needs_sep = base[base_len - 1u] != '/';
total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u;
buffer = (char *)malloc(total_len);
if (buffer == NULL) {
return false;
}
offset = 0u;
memcpy(buffer + offset, base, base_len);
offset += base_len;
if (needs_sep) {
buffer[offset++] = '/';
}
memcpy(buffer + offset, segment, seg_len);
offset += seg_len;
buffer[offset] = '\0';
*out_path = buffer;
return true;
}
static bool remove_tree(const char *path) {
struct stat st;
DIR *dir;
struct dirent *entry;
if (path == NULL) {
return false;
}
if (lstat(path, &st) != 0) {
return errno == ENOENT;
}
if (!S_ISDIR(st.st_mode)) {
return unlink(path) == 0;
}
dir = opendir(path);
if (dir == NULL) {
return false;
}
while ((entry = readdir(dir)) != NULL) {
char *child = NULL;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
if (!join_path(path, entry->d_name, &child)) {
closedir(dir);
return false;
}
if (!remove_tree(child)) {
free(child);
closedir(dir);
return false;
}
free(child);
}
if (closedir(dir) != 0) {
return false;
}
return rmdir(path) == 0;
}
static char *make_temp_root(void) {
char *tmpl = NULL;
char *root = NULL;
tmpl = amduat_strdup("/tmp/amduat-index-putget-XXXXXX");
if (tmpl == NULL) {
return NULL;
}
if (mkdtemp(tmpl) == NULL) {
free(tmpl);
return NULL;
}
root = tmpl;
return root;
}
int main(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
char *root = NULL;
int rc = 1;
size_t i;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root creation failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
for (i = 0u; i < 128u; ++i) {
char payload_buf[64];
size_t payload_len;
amduat_artifact_t artifact;
amduat_reference_t ref;
amduat_artifact_t loaded;
amduat_asl_store_error_t err;
payload_len = (size_t)snprintf(payload_buf, sizeof(payload_buf),
"payload-%zu", i);
if (payload_len == 0u || payload_len >= sizeof(payload_buf)) {
fprintf(stderr, "payload format failed at %zu\n", i);
goto cleanup;
}
artifact = amduat_artifact(
amduat_octets((const uint8_t *)payload_buf, payload_len));
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put(&store, artifact, &ref);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put failed at %zu: %d\n", i, (int)err);
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get(&store, ref, &loaded);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "get after put failed at %zu: %d\n", i, (int)err);
amduat_reference_free(&ref);
goto cleanup;
}
amduat_artifact_free(&loaded);
amduat_reference_free(&ref);
}
rc = 0;
cleanup:
if (root != NULL) {
if (!remove_tree(root)) {
fprintf(stderr, "warning: cleanup failed for %s\n", root);
}
free(root);
}
return rc;
}

View file

@ -1,86 +0,0 @@
#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,526 +0,0 @@
#include "amduat/asl/log_store.h"
#include "amduat/asl/asl_store_index_fs.h"
#include "amduat/asl/store.h"
#include "amduat/enc/asl1_core.h"
#include "amduat/hash/asl1.h"
#include "amduat/util/string.h"
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static bool join_path(const char *base, const char *segment, char **out_path) {
size_t base_len;
size_t seg_len;
bool needs_sep;
size_t total_len;
char *buffer;
size_t offset;
if (base == NULL || segment == NULL || out_path == NULL) {
return false;
}
base_len = strlen(base);
seg_len = strlen(segment);
if (base_len == 0u || seg_len == 0u) {
return false;
}
needs_sep = base[base_len - 1u] != '/';
total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u;
buffer = (char *)malloc(total_len);
if (buffer == NULL) {
return false;
}
offset = 0u;
memcpy(buffer + offset, base, base_len);
offset += base_len;
if (needs_sep) {
buffer[offset++] = '/';
}
memcpy(buffer + offset, segment, seg_len);
offset += seg_len;
buffer[offset] = '\0';
*out_path = buffer;
return true;
}
static bool remove_tree(const char *path) {
struct stat st;
DIR *dir;
struct dirent *entry;
if (path == NULL) {
return false;
}
if (lstat(path, &st) != 0) {
return errno == ENOENT;
}
if (!S_ISDIR(st.st_mode)) {
return unlink(path) == 0;
}
dir = opendir(path);
if (dir == NULL) {
return false;
}
while ((entry = readdir(dir)) != NULL) {
char *child = NULL;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
if (!join_path(path, entry->d_name, &child)) {
closedir(dir);
return false;
}
if (!remove_tree(child)) {
free(child);
closedir(dir);
return false;
}
free(child);
}
if (closedir(dir) != 0) {
return false;
}
return rmdir(path) == 0;
}
static bool refs_equal(amduat_reference_t a, amduat_reference_t b) {
if (a.hash_id != b.hash_id) {
return false;
}
if (a.digest.len != b.digest.len) {
return false;
}
if (a.digest.len == 0u) {
return true;
}
if (a.digest.data == NULL || b.digest.data == NULL) {
return false;
}
return memcmp(a.digest.data, b.digest.data, a.digest.len) == 0;
}
typedef struct {
amduat_asl_store_t *store;
amduat_asl_log_store_t *log_store;
const char *log_name;
uint16_t kind;
size_t iterations;
amduat_asl_store_error_t first_err;
uint64_t *observed_offsets;
} append_worker_t;
typedef struct {
amduat_asl_store_t *store;
uint16_t kind;
size_t iterations;
amduat_asl_store_error_t first_err;
} put_worker_t;
static void *append_worker_main(void *opaque) {
append_worker_t *worker = (append_worker_t *)opaque;
size_t i;
if (worker == NULL || worker->store == NULL || worker->log_store == NULL ||
worker->log_name == NULL) {
return NULL;
}
worker->first_err = AMDUAT_ASL_STORE_OK;
for (i = 0u; i < worker->iterations; ++i) {
char payload_buf[64];
size_t payload_len;
amduat_artifact_t payload_artifact;
amduat_reference_t payload_ref;
amduat_asl_log_entry_t entry;
amduat_asl_store_error_t err;
uint64_t first_offset = 0u;
payload_len = (size_t)snprintf(payload_buf, sizeof(payload_buf),
"worker-%u-%zu", (unsigned)worker->kind, i);
if (payload_len == 0u || payload_len >= sizeof(payload_buf)) {
worker->first_err = AMDUAT_ASL_STORE_ERR_IO;
return NULL;
}
payload_artifact =
amduat_artifact(amduat_octets((const uint8_t *)payload_buf, payload_len));
payload_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put(worker->store, payload_artifact, &payload_ref);
if (err != AMDUAT_ASL_STORE_OK) {
worker->first_err = err;
return NULL;
}
memset(&entry, 0, sizeof(entry));
entry.kind = worker->kind;
entry.payload_ref = payload_ref;
err = amduat_asl_log_append(worker->log_store,
worker->log_name,
&entry,
1u,
&first_offset);
amduat_reference_free(&payload_ref);
if (err != AMDUAT_ASL_STORE_OK) {
worker->first_err = err;
return NULL;
}
if (worker->observed_offsets != NULL && i < worker->iterations) {
worker->observed_offsets[i] = first_offset;
}
}
return NULL;
}
static void *put_worker_main(void *opaque) {
put_worker_t *worker = (put_worker_t *)opaque;
size_t i;
if (worker == NULL || worker->store == NULL) {
return NULL;
}
worker->first_err = AMDUAT_ASL_STORE_OK;
for (i = 0u; i < worker->iterations; ++i) {
char payload_buf[64];
size_t payload_len;
amduat_artifact_t payload_artifact;
amduat_reference_t payload_ref;
amduat_asl_store_error_t err;
payload_len = (size_t)snprintf(payload_buf, sizeof(payload_buf),
"put-only-%u-%zu", (unsigned)worker->kind, i);
if (payload_len == 0u || payload_len >= sizeof(payload_buf)) {
worker->first_err = AMDUAT_ASL_STORE_ERR_IO;
return NULL;
}
payload_artifact =
amduat_artifact(amduat_octets((const uint8_t *)payload_buf, payload_len));
payload_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put(worker->store, payload_artifact, &payload_ref);
amduat_reference_free(&payload_ref);
if (err != AMDUAT_ASL_STORE_OK) {
worker->first_err = err;
return NULL;
}
}
return NULL;
}
int main(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs_a;
amduat_asl_store_index_fs_t fs_b;
amduat_asl_store_t store_a;
amduat_asl_store_t store_b;
amduat_asl_log_store_t log_a;
amduat_asl_log_store_t log_b;
amduat_artifact_t payload_artifact_a;
amduat_artifact_t payload_artifact_b;
amduat_reference_t payload_ref_a;
amduat_reference_t payload_ref_b;
amduat_asl_log_entry_t entry_a;
amduat_asl_log_entry_t entry_b;
amduat_asl_log_entry_t *out_entries = NULL;
size_t out_len = 0u;
uint64_t out_next_offset = 0u;
bool out_end = false;
uint64_t first_offset = 0u;
const char *log_name = "collection/space/app1/daemon/edges/log";
enum { WORKER_ITERS = 32 };
append_worker_t worker_a;
append_worker_t worker_b;
append_worker_t mixed_append_worker;
put_worker_t mixed_put_worker;
pthread_t thread_a;
pthread_t thread_b;
pthread_t mixed_append_thread;
pthread_t mixed_put_thread;
bool started_a = false;
bool started_b = false;
bool started_mixed_append = false;
bool started_mixed_put = false;
uint64_t worker_a_offsets[WORKER_ITERS];
uint64_t worker_b_offsets[WORKER_ITERS];
char *root = NULL;
int rc = 1;
root = amduat_strdup("/tmp/amduat-log-index-XXXXXX");
if (root == NULL) {
fprintf(stderr, "alloc root template failed\n");
return 1;
}
if (mkdtemp(root) == NULL) {
fprintf(stderr, "mkdtemp failed\n");
goto cleanup;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs_a, config, root) ||
!amduat_asl_store_index_fs_init(&fs_b, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&store_a, config, amduat_asl_store_index_fs_ops(), &fs_a);
amduat_asl_store_init(&store_b, config, amduat_asl_store_index_fs_ops(), &fs_b);
if (!amduat_asl_log_store_init(&log_a, root, &store_a, NULL) ||
!amduat_asl_log_store_init(&log_b, root, &store_b, NULL)) {
fprintf(stderr, "log store init failed\n");
goto cleanup;
}
payload_artifact_a = amduat_artifact(amduat_octets((const uint8_t *)"payload-a", 9u));
payload_ref_a = amduat_reference(0u, amduat_octets(NULL, 0u));
if (amduat_asl_store_put(&store_a, payload_artifact_a, &payload_ref_a) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put payload a failed\n");
goto cleanup;
}
memset(&entry_a, 0, sizeof(entry_a));
entry_a.kind = 1u;
entry_a.payload_ref = payload_ref_a;
if (amduat_asl_log_append(&log_a, log_name, &entry_a, 1u, &first_offset) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "first append failed\n");
goto cleanup;
}
if (first_offset != 0u) {
fprintf(stderr, "first offset mismatch: %" PRIu64 "\n", first_offset);
goto cleanup;
}
payload_artifact_b = amduat_artifact(amduat_octets((const uint8_t *)"payload-b", 9u));
payload_ref_b = amduat_reference(0u, amduat_octets(NULL, 0u));
if (amduat_asl_store_put(&store_b, payload_artifact_b, &payload_ref_b) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put payload b failed\n");
goto cleanup;
}
memset(&entry_b, 0, sizeof(entry_b));
entry_b.kind = 2u;
entry_b.payload_ref = payload_ref_b;
if (amduat_asl_log_append(&log_b, log_name, &entry_b, 1u, &first_offset) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "second append failed\n");
goto cleanup;
}
if (first_offset != 1u) {
fprintf(stderr, "second first_offset mismatch: %" PRIu64 "\n", first_offset);
goto cleanup;
}
if (amduat_asl_log_read(&log_a,
log_name,
0u,
8u,
&out_entries,
&out_len,
&out_next_offset,
&out_end) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "log read failed\n");
goto cleanup;
}
if (out_len != 2u || out_next_offset != 2u || !out_end) {
fprintf(stderr, "readback shape mismatch len=%zu next=%" PRIu64 " end=%d\n",
out_len, out_next_offset, out_end ? 1 : 0);
goto cleanup;
}
if (out_entries[0].kind != 1u || !refs_equal(out_entries[0].payload_ref, payload_ref_a)) {
fprintf(stderr, "entry0 mismatch\n");
goto cleanup;
}
if (out_entries[1].kind != 2u || !refs_equal(out_entries[1].payload_ref, payload_ref_b)) {
fprintf(stderr, "entry1 mismatch\n");
goto cleanup;
}
amduat_asl_log_entries_free(out_entries, out_len);
out_entries = NULL;
out_len = 0u;
memset(&worker_a, 0, sizeof(worker_a));
worker_a.store = &store_a;
worker_a.log_store = &log_a;
worker_a.log_name = log_name;
worker_a.kind = 10u;
worker_a.iterations = WORKER_ITERS;
worker_a.observed_offsets = worker_a_offsets;
memset(&worker_b, 0, sizeof(worker_b));
worker_b.store = &store_b;
worker_b.log_store = &log_b;
worker_b.log_name = log_name;
worker_b.kind = 11u;
worker_b.iterations = WORKER_ITERS;
worker_b.observed_offsets = worker_b_offsets;
if (pthread_create(&thread_a, NULL, append_worker_main, &worker_a) != 0) {
fprintf(stderr, "pthread_create failed\n");
goto cleanup;
}
started_a = true;
if (pthread_create(&thread_b, NULL, append_worker_main, &worker_b) != 0) {
fprintf(stderr, "pthread_create failed\n");
goto cleanup;
}
started_b = true;
(void)pthread_join(thread_a, NULL);
(void)pthread_join(thread_b, NULL);
started_a = false;
started_b = false;
if (worker_a.first_err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "worker_a failed err=%d\n", (int)worker_a.first_err);
goto cleanup;
}
if (worker_b.first_err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "worker_b failed err=%d\n", (int)worker_b.first_err);
goto cleanup;
}
{
bool seen[2u + 2u * WORKER_ITERS];
size_t i;
memset(seen, 0, sizeof(seen));
for (i = 0u; i < WORKER_ITERS; ++i) {
if (worker_a_offsets[i] < 2u ||
worker_a_offsets[i] >= (uint64_t)(2u + 2u * WORKER_ITERS)) {
fprintf(stderr, "worker_a offset out of range: %" PRIu64 "\n",
worker_a_offsets[i]);
goto cleanup;
}
if (seen[worker_a_offsets[i]]) {
fprintf(stderr, "duplicate offset from worker_a: %" PRIu64 "\n",
worker_a_offsets[i]);
goto cleanup;
}
seen[worker_a_offsets[i]] = true;
}
for (i = 0u; i < WORKER_ITERS; ++i) {
if (worker_b_offsets[i] < 2u ||
worker_b_offsets[i] >= (uint64_t)(2u + 2u * WORKER_ITERS)) {
fprintf(stderr, "worker_b offset out of range: %" PRIu64 "\n",
worker_b_offsets[i]);
goto cleanup;
}
if (seen[worker_b_offsets[i]]) {
fprintf(stderr, "duplicate offset from worker_b: %" PRIu64 "\n",
worker_b_offsets[i]);
goto cleanup;
}
seen[worker_b_offsets[i]] = true;
}
for (i = 2u; i < (2u + 2u * WORKER_ITERS); ++i) {
if (!seen[i]) {
fprintf(stderr, "missing worker offset: %zu\n", i);
goto cleanup;
}
}
}
if (amduat_asl_log_read(&log_a,
log_name,
0u,
4096u,
&out_entries,
&out_len,
&out_next_offset,
&out_end) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "log read after stress failed\n");
goto cleanup;
}
if (out_len != (2u + 2u * WORKER_ITERS) || out_next_offset != out_len ||
!out_end) {
fprintf(stderr,
"stress read mismatch len=%zu next=%" PRIu64 " expected=%u end=%d\n",
out_len, out_next_offset, (unsigned)(2u + 2u * WORKER_ITERS),
out_end ? 1 : 0);
goto cleanup;
}
amduat_asl_log_entries_free(out_entries, out_len);
out_entries = NULL;
out_len = 0u;
memset(&mixed_append_worker, 0, sizeof(mixed_append_worker));
mixed_append_worker.store = &store_a;
mixed_append_worker.log_store = &log_a;
mixed_append_worker.log_name = log_name;
mixed_append_worker.kind = 12u;
mixed_append_worker.iterations = WORKER_ITERS;
memset(&mixed_put_worker, 0, sizeof(mixed_put_worker));
mixed_put_worker.store = &store_b;
mixed_put_worker.kind = 13u;
mixed_put_worker.iterations = WORKER_ITERS * 4u;
if (pthread_create(&mixed_append_thread, NULL, append_worker_main,
&mixed_append_worker) != 0) {
fprintf(stderr, "pthread_create mixed append failed\n");
goto cleanup;
}
started_mixed_append = true;
if (pthread_create(&mixed_put_thread, NULL, put_worker_main,
&mixed_put_worker) != 0) {
fprintf(stderr, "pthread_create mixed put failed\n");
goto cleanup;
}
started_mixed_put = true;
(void)pthread_join(mixed_append_thread, NULL);
(void)pthread_join(mixed_put_thread, NULL);
started_mixed_append = false;
started_mixed_put = false;
if (mixed_append_worker.first_err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "mixed append worker failed err=%d\n",
(int)mixed_append_worker.first_err);
goto cleanup;
}
if (mixed_put_worker.first_err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "mixed put worker failed err=%d\n",
(int)mixed_put_worker.first_err);
goto cleanup;
}
rc = 0;
cleanup:
if (started_a) {
(void)pthread_join(thread_a, NULL);
}
if (started_b) {
(void)pthread_join(thread_b, NULL);
}
if (started_mixed_append) {
(void)pthread_join(mixed_append_thread, NULL);
}
if (started_mixed_put) {
(void)pthread_join(mixed_put_thread, NULL);
}
amduat_asl_log_entries_free(out_entries, out_len);
amduat_reference_free(&payload_ref_a);
amduat_reference_free(&payload_ref_b);
if (root != NULL) {
if (!remove_tree(root)) {
fprintf(stderr, "warning: cleanup failed for %s\n", root);
}
free(root);
}
return rc;
}

View file

@ -1,527 +0,0 @@
#include "amduat/asl/asl_store_index_fs.h"
#include "amduat/asl/log_cursor.h"
#include "amduat/asl/projection_checkpoint.h"
#include "amduat/asl/record.h"
#include "amduat/asl/store.h"
#include "amduat/enc/asl1_core.h"
#include "amduat/hash/asl1.h"
#include <dirent.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static char *make_temp_root(void) {
char *templ;
const char template_prefix[] = "/tmp/amduat_test_asl_projection_replay_XXXXXX";
templ = (char *)malloc(sizeof(template_prefix));
if (templ == NULL) {
return NULL;
}
memcpy(templ, template_prefix, sizeof(template_prefix));
if (mkdtemp(templ) == NULL) {
free(templ);
return NULL;
}
return templ;
}
static bool remove_tree(const char *path) {
struct stat st;
DIR *dir;
struct dirent *entry;
if (path == NULL) {
return false;
}
if (stat(path, &st) != 0) {
return errno == ENOENT;
}
if (!S_ISDIR(st.st_mode)) {
return unlink(path) == 0;
}
dir = opendir(path);
if (dir == NULL) {
return false;
}
while ((entry = readdir(dir)) != NULL) {
char child[2048];
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0) {
continue;
}
snprintf(child, sizeof(child), "%s/%s", path, entry->d_name);
if (!remove_tree(child)) {
closedir(dir);
return false;
}
}
closedir(dir);
return rmdir(path) == 0;
}
static bool records_equal(const amduat_asl_log_record_t *a,
const amduat_asl_log_record_t *b) {
if (a->logseq != b->logseq || a->record_type != b->record_type) {
return false;
}
if (a->payload.len != b->payload.len) {
return false;
}
if (a->payload.len == 0u) {
return true;
}
if (a->payload.data == NULL || b->payload.data == NULL) {
return false;
}
return memcmp(a->payload.data, b->payload.data, a->payload.len) == 0;
}
static int test_cursor_deterministic_replay(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_artifact_t artifact;
amduat_reference_t ref;
amduat_asl_index_state_t state;
uint8_t payload_a[4] = {0x11, 0x22, 0x33, 0x44};
uint8_t payload_b[4] = {0xaa, 0xbb, 0xcc, 0xdd};
amduat_asl_log_record_t *all = NULL;
size_t all_len = 0u;
amduat_asl_log_position_t cursor = 0u;
bool end = false;
char *root;
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup_root;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
artifact = amduat_artifact(amduat_octets(payload_a, sizeof(payload_a)));
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
if (amduat_asl_store_put_indexed(&store, artifact, &ref, &state) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed A failed\n");
goto cleanup_root;
}
amduat_reference_free(&ref);
artifact = amduat_artifact(amduat_octets(payload_b, sizeof(payload_b)));
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
if (amduat_asl_store_put_indexed(&store, artifact, &ref, &state) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed B failed\n");
goto cleanup_root;
}
amduat_reference_free(&ref);
if (!amduat_asl_log_read_since(&store,
0u,
0u,
&all,
&all_len,
&cursor,
&end)) {
fprintf(stderr, "log read all failed\n");
goto cleanup_root;
}
cursor = 0u;
for (size_t i = 0u; i < all_len; ++i) {
amduat_asl_log_record_t *page = NULL;
size_t page_len = 0u;
amduat_asl_log_position_t next = 0u;
bool page_end = false;
if (!amduat_asl_log_read_since(&store,
cursor,
1u,
&page,
&page_len,
&next,
&page_end)) {
fprintf(stderr, "log page read failed\n");
goto cleanup_all;
}
if (page_len != 1u || !records_equal(&all[i], &page[0])) {
amduat_enc_asl_log_free(page, page_len);
fprintf(stderr, "log page mismatch\n");
goto cleanup_all;
}
cursor = next;
amduat_enc_asl_log_free(page, page_len);
}
{
amduat_asl_log_record_t *page = NULL;
size_t page_len = 0u;
amduat_asl_log_position_t next = 0u;
bool page_end = false;
if (!amduat_asl_log_read_since(&store,
cursor,
1u,
&page,
&page_len,
&next,
&page_end)) {
fprintf(stderr, "log tail read failed\n");
goto cleanup_all;
}
if (page_len != 0u || !page_end) {
amduat_enc_asl_log_free(page, page_len);
fprintf(stderr, "log tail mismatch\n");
goto cleanup_all;
}
amduat_enc_asl_log_free(page, page_len);
}
exit_code = 0;
cleanup_all:
amduat_enc_asl_log_free(all, all_len);
cleanup_root:
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
static int test_checkpoint_resume(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_index_state_t state;
amduat_asl_projection_checkpoint_v1_t checkpoint;
amduat_asl_projection_checkpoint_v1_t decoded;
amduat_octets_t checkpoint_bytes = amduat_octets(NULL, 0u);
amduat_reference_t checkpoint_ref;
amduat_asl_record_t checkpoint_record;
amduat_asl_log_record_t *page = NULL;
size_t page_len = 0u;
amduat_asl_log_position_t next = 0u;
bool end = false;
char *root;
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup_root;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
{
uint8_t payload[3] = {0x01, 0x02, 0x03};
amduat_artifact_t artifact = amduat_artifact(amduat_octets(payload, sizeof(payload)));
if (amduat_asl_store_put_indexed(&store, artifact, &checkpoint_ref, &state) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed failed\n");
goto cleanup_root;
}
amduat_reference_free(&checkpoint_ref);
}
if (!amduat_asl_index_current_state(&store, &state)) {
fprintf(stderr, "current_state failed\n");
goto cleanup_root;
}
memset(&checkpoint, 0, sizeof(checkpoint));
checkpoint.checkpoint_version = AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION_V1;
checkpoint.projection_name = amduat_octets("projection", strlen("projection"));
checkpoint.projection_schema_version = 1u;
checkpoint.has_snapshot_id = true;
checkpoint.snapshot_id = state.snapshot_id;
checkpoint.log_position = state.log_position;
checkpoint.created_at = 0u;
checkpoint.builder_id = amduat_octets("test", strlen("test"));
if (!amduat_asl_projection_checkpoint_encode_v1(&checkpoint,
&checkpoint_bytes)) {
fprintf(stderr, "checkpoint encode failed\n");
goto cleanup_root;
}
if (amduat_asl_record_store_put(
&store,
amduat_octets("projection/checkpoint",
strlen("projection/checkpoint")),
checkpoint_bytes,
&checkpoint_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "checkpoint record put failed\n");
goto cleanup_bytes;
}
if (amduat_asl_record_store_get(&store, checkpoint_ref, &checkpoint_record) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "checkpoint record get failed\n");
goto cleanup_ref;
}
if (!amduat_asl_projection_checkpoint_decode_v1(
checkpoint_record.payload, &decoded)) {
fprintf(stderr, "checkpoint decode failed\n");
amduat_asl_record_free(&checkpoint_record);
goto cleanup_ref;
}
if (decoded.log_position != state.log_position) {
fprintf(stderr, "checkpoint log position mismatch\n");
amduat_asl_projection_checkpoint_free(&decoded);
amduat_asl_record_free(&checkpoint_record);
goto cleanup_ref;
}
amduat_asl_projection_checkpoint_free(&decoded);
amduat_asl_record_free(&checkpoint_record);
if (!amduat_asl_log_read_since(&store,
state.log_position,
0u,
&page,
&page_len,
&next,
&end)) {
fprintf(stderr, "resume log read failed\n");
goto cleanup_ref;
}
if (page_len == 0u) {
fprintf(stderr, "resume expected non-empty log\n");
amduat_enc_asl_log_free(page, page_len);
goto cleanup_ref;
}
amduat_enc_asl_log_free(page, page_len);
if (!amduat_asl_log_read_since(&store,
next,
0u,
&page,
&page_len,
&next,
&end)) {
fprintf(stderr, "resume tail read failed\n");
goto cleanup_ref;
}
if (page_len != 0u || !end) {
fprintf(stderr, "resume expected empty tail\n");
amduat_enc_asl_log_free(page, page_len);
goto cleanup_ref;
}
amduat_enc_asl_log_free(page, page_len);
exit_code = 0;
cleanup_ref:
amduat_reference_free(&checkpoint_ref);
cleanup_bytes:
amduat_octets_free(&checkpoint_bytes);
cleanup_root:
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
static int test_projector_loop_stub(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_index_state_t state;
amduat_asl_log_record_t *page = NULL;
size_t page_len = 0u;
amduat_asl_log_position_t after = 0u;
amduat_asl_log_position_t next = 0u;
bool end = false;
amduat_reference_t checkpoint_ref;
amduat_asl_record_t checkpoint_record;
amduat_asl_projection_checkpoint_v1_t checkpoint;
amduat_asl_projection_checkpoint_v1_t decoded;
amduat_octets_t checkpoint_bytes = amduat_octets(NULL, 0u);
size_t projected_count = 0u;
char *root;
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup_root;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
{
uint8_t payload_a[2] = {0x10, 0x20};
uint8_t payload_b[2] = {0x30, 0x40};
amduat_reference_t ref = amduat_reference(0u, amduat_octets(NULL, 0u));
amduat_artifact_t artifact = amduat_artifact(amduat_octets(payload_a, sizeof(payload_a)));
if (amduat_asl_store_put_indexed(&store, artifact, &ref, &state) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed A failed\n");
amduat_reference_free(&ref);
goto cleanup_root;
}
amduat_reference_free(&ref);
artifact = amduat_artifact(amduat_octets(payload_b, sizeof(payload_b)));
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
if (amduat_asl_store_put_indexed(&store, artifact, &ref, &state) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed B failed\n");
amduat_reference_free(&ref);
goto cleanup_root;
}
amduat_reference_free(&ref);
}
if (!amduat_asl_index_current_state(&store, &state)) {
fprintf(stderr, "current_state failed\n");
goto cleanup_root;
}
checkpoint_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
while (!end) {
if (!amduat_asl_log_read_since(&store,
after,
0u,
&page,
&page_len,
&next,
&end)) {
fprintf(stderr, "projector log read failed\n");
goto cleanup_checkpoint_ref;
}
projected_count += page_len;
memset(&checkpoint, 0, sizeof(checkpoint));
checkpoint.checkpoint_version = AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION_V1;
checkpoint.projection_name =
amduat_octets("projector-stub", strlen("projector-stub"));
checkpoint.projection_schema_version = 1u;
checkpoint.has_snapshot_id = true;
checkpoint.snapshot_id = state.snapshot_id;
checkpoint.log_position = next;
checkpoint.created_at = 0u;
checkpoint.builder_id =
amduat_octets("test-projector", strlen("test-projector"));
checkpoint.has_artifact_count = true;
checkpoint.artifact_count = projected_count;
if (!amduat_asl_projection_checkpoint_encode_v1(&checkpoint,
&checkpoint_bytes)) {
fprintf(stderr, "checkpoint encode failed\n");
amduat_enc_asl_log_free(page, page_len);
goto cleanup_checkpoint_ref;
}
amduat_reference_free(&checkpoint_ref);
if (amduat_asl_record_store_put(
&store,
amduat_octets("projection/checkpoint",
strlen("projection/checkpoint")),
checkpoint_bytes,
&checkpoint_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "checkpoint store failed\n");
amduat_octets_free(&checkpoint_bytes);
amduat_enc_asl_log_free(page, page_len);
goto cleanup_checkpoint_ref;
}
amduat_octets_free(&checkpoint_bytes);
amduat_enc_asl_log_free(page, page_len);
after = next;
}
if (amduat_asl_record_store_get(&store, checkpoint_ref, &checkpoint_record) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "checkpoint record get failed\n");
goto cleanup_checkpoint_ref;
}
if (!amduat_octets_eq(
checkpoint_record.schema,
amduat_octets("projection/checkpoint",
strlen("projection/checkpoint")))) {
fprintf(stderr, "checkpoint schema mismatch\n");
amduat_asl_record_free(&checkpoint_record);
goto cleanup_checkpoint_ref;
}
if (!amduat_asl_projection_checkpoint_decode_v1(
checkpoint_record.payload, &decoded)) {
fprintf(stderr, "checkpoint decode failed\n");
amduat_asl_record_free(&checkpoint_record);
goto cleanup_checkpoint_ref;
}
if (decoded.log_position != state.log_position) {
fprintf(stderr, "checkpoint log position mismatch\n");
amduat_asl_projection_checkpoint_free(&decoded);
amduat_asl_record_free(&checkpoint_record);
goto cleanup_checkpoint_ref;
}
amduat_asl_projection_checkpoint_free(&decoded);
amduat_asl_record_free(&checkpoint_record);
exit_code = 0;
cleanup_checkpoint_ref:
amduat_octets_free(&checkpoint_bytes);
amduat_reference_free(&checkpoint_ref);
cleanup_root:
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
int main(void) {
if (test_cursor_deterministic_replay() != 0) {
return 1;
}
if (test_checkpoint_resume() != 0) {
return 1;
}
if (test_projector_loop_stub() != 0) {
return 1;
}
return 0;
}

View file

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

View file

@ -156,24 +156,6 @@ static bool read_file(const char *path, uint8_t **out_bytes, size_t *out_len) {
return true;
}
static bool write_file(const char *path, const uint8_t *bytes, size_t len) {
FILE *fp;
size_t written;
if (path == NULL || (len != 0u && bytes == NULL)) {
return false;
}
fp = fopen(path, "wb");
if (fp == NULL) {
return false;
}
written = fwrite(bytes, 1u, len, fp);
if (fclose(fp) != 0) {
return false;
}
return written == len;
}
static bool build_log_path(const char *root, char **out_path) {
char *index_path = NULL;
bool ok;
@ -743,349 +725,6 @@ cleanup:
return exit_code;
}
static int test_snapshot_fast_path_with_corrupt_log(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_store_error_t err;
amduat_asl_index_state_t state;
amduat_artifact_t artifact;
amduat_artifact_t loaded;
amduat_reference_t ref;
uint8_t payload[10];
char *root;
char *log_path = NULL;
static const uint8_t corrupt_log[] = {'B', 'A', 'D'};
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
memset(payload, 0x4c, sizeof(payload));
artifact = amduat_artifact(amduat_octets(payload, sizeof(payload)));
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put_indexed(&store, artifact, &ref, &state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed failed: %d\n", err);
goto cleanup;
}
err = amduat_asl_store_index_fs_snapshot_create(&fs, 1u, NULL, NULL);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "snapshot create failed: %d\n", err);
goto cleanup;
}
if (!amduat_asl_index_current_state(&store, &state)) {
fprintf(stderr, "current_state failed\n");
goto cleanup;
}
if (state.snapshot_id != 1u) {
fprintf(stderr, "expected snapshot_id=1\n");
goto cleanup;
}
if (!build_log_path(root, &log_path) ||
!write_file(log_path, corrupt_log, sizeof(corrupt_log))) {
fprintf(stderr, "corrupt log write failed\n");
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref, state, &loaded);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "get_indexed with snapshot fast path failed: %d\n", err);
goto cleanup;
}
if (!amduat_artifact_eq(artifact, loaded)) {
fprintf(stderr, "artifact mismatch from snapshot fast path\n");
amduat_artifact_free(&loaded);
goto cleanup;
}
amduat_artifact_free(&loaded);
exit_code = 0;
cleanup:
free(log_path);
amduat_reference_free(&ref);
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
static int test_snapshot_fallback_replays_delta(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_store_error_t err;
amduat_asl_index_state_t snapshot_state;
amduat_asl_index_state_t fallback_state;
amduat_artifact_t artifact_a;
amduat_artifact_t loaded;
amduat_reference_t ref_a;
uint8_t payload_a[7];
char *root;
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
memset(payload_a, 0x11, sizeof(payload_a));
artifact_a = amduat_artifact(amduat_octets(payload_a, sizeof(payload_a)));
ref_a = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put_indexed(&store, artifact_a, &ref_a, &snapshot_state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed a failed: %d\n", err);
goto cleanup;
}
err = amduat_asl_store_index_fs_snapshot_create(&fs, 1u, NULL, NULL);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "snapshot create failed: %d\n", err);
goto cleanup;
}
if (!amduat_asl_index_current_state(&store, &snapshot_state)) {
fprintf(stderr, "snapshot current_state failed\n");
goto cleanup;
}
if (snapshot_state.log_position == 0u) {
fprintf(stderr, "snapshot log position was zero\n");
goto cleanup;
}
fallback_state = snapshot_state;
fallback_state.log_position -= 1u;
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref_a, fallback_state, &loaded);
if (err != AMDUAT_ASL_STORE_ERR_INTEGRITY) {
fprintf(stderr, "fallback expected integrity: %d\n", err);
amduat_artifact_free(&loaded);
goto cleanup;
}
amduat_artifact_free(&loaded);
exit_code = 0;
cleanup:
amduat_reference_free(&ref_a);
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
static int test_snapshot_fast_path_not_found_and_tombstoned(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_store_error_t err;
amduat_asl_index_state_t state;
amduat_asl_index_state_t tombstone_state;
amduat_reference_t ref;
amduat_reference_t missing_ref;
amduat_artifact_t artifact;
amduat_artifact_t loaded;
uint8_t payload[8];
uint8_t *missing_digest = NULL;
int exit_code = 1;
char *root;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
memset(payload, 0x66, sizeof(payload));
artifact = amduat_artifact(amduat_octets(payload, sizeof(payload)));
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
missing_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put_indexed(&store, artifact, &ref, &state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed failed: %d\n", err);
goto cleanup;
}
err = amduat_asl_store_index_fs_snapshot_create(&fs, 1u, NULL, NULL);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "snapshot create failed: %d\n", err);
goto cleanup;
}
if (!amduat_asl_index_current_state(&store, &state)) {
fprintf(stderr, "current_state failed\n");
goto cleanup;
}
missing_digest = (uint8_t *)malloc(ref.digest.len);
if (missing_digest == NULL || ref.digest.len == 0u) {
fprintf(stderr, "missing ref build failed\n");
goto cleanup;
}
memcpy(missing_digest, ref.digest.data, ref.digest.len);
missing_digest[0] ^= 0xffu;
missing_ref.hash_id = ref.hash_id;
missing_ref.digest = amduat_octets(missing_digest, ref.digest.len);
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, missing_ref, state, &loaded);
if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
fprintf(stderr, "snapshot not found expected: %d\n", err);
amduat_artifact_free(&loaded);
goto cleanup;
}
amduat_artifact_free(&loaded);
err = amduat_asl_store_tombstone(&store, ref, 1u, 2u, &tombstone_state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "tombstone failed: %d\n", err);
goto cleanup;
}
err = amduat_asl_store_index_fs_snapshot_create(&fs, 2u, NULL, NULL);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "snapshot create after tombstone failed: %d\n", err);
goto cleanup;
}
if (!amduat_asl_index_current_state(&store, &state)) {
fprintf(stderr, "current_state after tombstone failed\n");
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref, state, &loaded);
if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
fprintf(stderr, "snapshot tombstoned expected not found: %d\n", err);
amduat_artifact_free(&loaded);
goto cleanup;
}
amduat_artifact_free(&loaded);
exit_code = 0;
cleanup:
amduat_reference_free(&ref);
amduat_reference_free(&missing_ref);
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
static int test_snapshot_fast_path_integrity_error_unchanged(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_store_error_t err;
amduat_asl_index_state_t state;
amduat_artifact_t artifact;
amduat_artifact_t loaded;
amduat_reference_t ref;
uint8_t payload[6];
char *root;
char *segment_path = NULL;
static const uint8_t corrupt_segment[] = {'X', 'X', 'X'};
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
memset(payload, 0x31, sizeof(payload));
artifact = amduat_artifact(amduat_octets(payload, sizeof(payload)));
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put_indexed(&store, artifact, &ref, &state);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "put_indexed failed: %d\n", err);
goto cleanup;
}
err = amduat_asl_store_index_fs_snapshot_create(&fs, 1u, NULL, NULL);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "snapshot create failed: %d\n", err);
goto cleanup;
}
if (!amduat_asl_index_current_state(&store, &state)) {
fprintf(stderr, "current_state failed\n");
goto cleanup;
}
if (!build_segment_path(root, 1u, &segment_path) ||
!write_file(segment_path, corrupt_segment, sizeof(corrupt_segment))) {
fprintf(stderr, "segment corruption failed\n");
goto cleanup;
}
loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, ref, state, &loaded);
if (err != AMDUAT_ASL_STORE_ERR_INTEGRITY) {
fprintf(stderr, "expected integrity error, got: %d\n", err);
amduat_artifact_free(&loaded);
goto cleanup;
}
amduat_artifact_free(&loaded);
exit_code = 0;
cleanup:
free(segment_path);
amduat_reference_free(&ref);
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
static int test_segment_batch_packing(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
@ -1955,219 +1594,10 @@ static int test_writer_reader_stress(void) {
return state.failed ? 1 : 0;
}
/*
* test_miss_latency_bounded
*
* Regression test for the miss-path bloom precheck optimisation.
*
* Builds a store with k_segment_count segments (one artifact each, sized at
* k_payload_bytes so each segment file is meaningfully large) then performs
* k_miss_trials GET lookups for a ref that is definitively absent. Each
* lookup must complete within k_miss_deadline_ns nanoseconds, and the total
* across all trials must be within k_total_deadline_ns.
*
* Without the bloom precheck every miss reads each candidate segment in full
* (stat + read + SHA256 + decode). With the precheck, only the 368-byte
* header+bloom prefix is read before the bloom filter short-circuits the
* lookup. On the test hardware this reduces per-miss latency by 10-20×;
* the bounds below are deliberately loose enough to pass on slow CI yet
* still catch a regression to the full-scan path.
*/
static int test_miss_latency_bounded(void) {
enum {
k_segment_count = 200,
k_miss_trials = 20,
k_payload_bytes = 16384
};
/* Each miss must finish within 2 s; total across all trials within 5 s.
* Pre-fix (full scan), 200 segments × 16 KB each 3.2 MB of I/O + SHA256
* per miss. Post-fix (bloom precheck) 73 KB per miss. Even on slow
* CI the 10× I/O reduction makes it straightforward to stay within 5 s. */
const uint64_t k_miss_deadline_ns = (uint64_t)2000 * 1000 * 1000;
const uint64_t k_total_deadline_ns = (uint64_t)5000 * 1000 * 1000;
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t fs;
amduat_asl_store_t store;
amduat_asl_store_error_t err;
amduat_asl_index_state_t state;
amduat_reference_t *refs = NULL;
uint8_t *payload = NULL;
uint8_t *missing_digest = NULL;
amduat_reference_t missing_ref;
char *root = NULL;
int exit_code = 1;
size_t i;
uint64_t total_miss_ns = 0u;
size_t digest_len = 0u;
missing_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "test_miss_latency_bounded: temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
fprintf(stderr, "test_miss_latency_bounded: index fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
refs = (amduat_reference_t *)calloc(k_segment_count, sizeof(*refs));
payload = (uint8_t *)malloc(k_payload_bytes);
if (refs == NULL || payload == NULL) {
fprintf(stderr, "test_miss_latency_bounded: alloc failed\n");
goto cleanup;
}
/* Fill payload with deterministic data. */
for (i = 0; i < (size_t)k_payload_bytes; ++i) {
payload[i] = (uint8_t)(i & 0xffu);
}
/* Insert k_segment_count artifacts, each in its own segment
* (default pending_bytes limit forces a flush every record). */
for (i = 0; i < (size_t)k_segment_count; ++i) {
/* Perturb payload per artifact so each has a distinct digest. */
payload[0] = (uint8_t)(i & 0xffu);
payload[1] = (uint8_t)((i >> 8) & 0xffu);
refs[i] = amduat_reference(0u, amduat_octets(NULL, 0u));
err = amduat_asl_store_put(&store,
amduat_artifact(amduat_octets(payload,
k_payload_bytes)),
&refs[i]);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "test_miss_latency_bounded: put %zu failed: %d\n",
i, err);
goto cleanup;
}
}
if (!amduat_asl_index_current_state(&store, &state)) {
fprintf(stderr, "test_miss_latency_bounded: current_state failed\n");
goto cleanup;
}
/* Build a ref with a valid shape but a digest not present in the store.
* Flip the first byte of a known ref's digest. */
if (refs[0].digest.len == 0u || refs[0].digest.data == NULL) {
fprintf(stderr, "test_miss_latency_bounded: ref[0] has no digest\n");
goto cleanup;
}
digest_len = refs[0].digest.len;
missing_digest = (uint8_t *)malloc(digest_len);
if (missing_digest == NULL) {
fprintf(stderr, "test_miss_latency_bounded: missing_digest alloc failed\n");
goto cleanup;
}
memcpy(missing_digest, refs[0].digest.data, digest_len);
missing_digest[0] ^= 0xffu;
missing_ref = amduat_reference(refs[0].hash_id,
amduat_octets(missing_digest, digest_len));
/* Correctness check: the missing ref must return NOT_FOUND. */
{
amduat_artifact_t loaded = amduat_artifact(amduat_octets(NULL, 0u));
err = amduat_asl_store_get_indexed(&store, missing_ref, state, &loaded);
if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
fprintf(stderr,
"test_miss_latency_bounded: expected NOT_FOUND, got %d\n", err);
amduat_artifact_free(&loaded);
goto cleanup;
}
amduat_artifact_free(&loaded);
}
/* Performance: k_miss_trials miss GETs must each finish within deadline. */
for (i = 0; i < (size_t)k_miss_trials; ++i) {
amduat_artifact_t loaded = amduat_artifact(amduat_octets(NULL, 0u));
uint64_t t0 = now_ns();
uint64_t elapsed;
err = amduat_asl_store_get_indexed(&store, missing_ref, state, &loaded);
elapsed = now_ns() - t0;
amduat_artifact_free(&loaded);
if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
fprintf(stderr,
"test_miss_latency_bounded: trial %zu: expected NOT_FOUND, "
"got %d\n",
i, err);
goto cleanup;
}
total_miss_ns += elapsed;
if (elapsed > k_miss_deadline_ns) {
fprintf(stderr,
"test_miss_latency_bounded: trial %zu took %.1f ms "
"(limit %.1f ms)\n",
i,
(double)elapsed / 1e6,
(double)k_miss_deadline_ns / 1e6);
goto cleanup;
}
}
fprintf(stderr,
"test_miss_latency_bounded: %d segments, %d miss trials, "
"total=%.1f ms, avg=%.2f ms/miss\n",
k_segment_count,
k_miss_trials,
(double)total_miss_ns / 1e6,
(double)total_miss_ns / 1e6 / (double)k_miss_trials);
if (total_miss_ns > k_total_deadline_ns) {
fprintf(stderr,
"test_miss_latency_bounded: total %.1f ms exceeds limit %.1f ms\n",
(double)total_miss_ns / 1e6,
(double)k_total_deadline_ns / 1e6);
goto cleanup;
}
exit_code = 0;
cleanup:
if (refs != NULL) {
for (i = 0; i < (size_t)k_segment_count; ++i) {
amduat_reference_free(&refs[i]);
}
}
free(refs);
free(payload);
/* missing_digest is owned by missing_ref; amduat_reference_free frees it. */
amduat_reference_free(&missing_ref);
if (root != NULL) {
if (!remove_tree(root)) {
fprintf(stderr, "test_miss_latency_bounded: cleanup failed\n");
exit_code = 1;
}
free(root);
}
return exit_code;
}
int main(void) {
if (test_round_trip() != 0) {
return 1;
}
if (test_snapshot_fast_path_with_corrupt_log() != 0) {
return 1;
}
if (test_snapshot_fallback_replays_delta() != 0) {
return 1;
}
if (test_snapshot_fast_path_not_found_and_tombstoned() != 0) {
return 1;
}
if (test_snapshot_fast_path_integrity_error_unchanged() != 0) {
return 1;
}
if (test_snapshot_truncation() != 0) {
return 1;
}
@ -2180,9 +1610,6 @@ int main(void) {
if (test_gc_keeps_visible() != 0) {
return 1;
}
if (test_miss_latency_bounded() != 0) {
return 1;
}
if (test_large_round_trip_perf() != 0) {
return 1;
}

File diff suppressed because it is too large Load diff

View file

@ -1,26 +0,0 @@
#ifndef AMDUAT_TESTS_CONFORMANCE_ASL_STORE_CONFORMANCE_H
#define AMDUAT_TESTS_CONFORMANCE_ASL_STORE_CONFORMANCE_H
#include "tests/conformance/conformance_result.h"
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
int amduat_asl_store_conformance_run(
const char *backend_selection,
const char *fixtures_root,
const char *workdir,
bool include_optional,
bool verbose,
amduat_conformance_backend_result_t **out_results,
size_t *out_len);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_TESTS_CONFORMANCE_ASL_STORE_CONFORMANCE_H */

View file

@ -1,87 +0,0 @@
#include "asl_store_conformance.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static bool env_truthy(const char *value) {
if (value == NULL) {
return false;
}
return strcmp(value, "1") == 0 || strcmp(value, "true") == 0 ||
strcmp(value, "yes") == 0;
}
static char *make_temp_root(void) {
char *templ;
const char template_prefix[] = "/tmp/amduat_conformance_asl_XXXXXX";
templ = (char *)malloc(sizeof(template_prefix));
if (templ == NULL) {
return NULL;
}
memcpy(templ, template_prefix, sizeof(template_prefix));
if (mkdtemp(templ) == NULL) {
free(templ);
return NULL;
}
return templ;
}
static const char *default_fixtures_root(void) {
struct stat st;
if (stat("tests/fixtures/conformance", &st) == 0) {
return "tests/fixtures/conformance";
}
if (stat("../tests/fixtures/conformance", &st) == 0) {
return "../tests/fixtures/conformance";
}
return "tests/fixtures/conformance";
}
int main(void) {
const char *backend_env = getenv("AMDUAT_CONFORMANCE_ASL_BACKENDS");
const char *fixtures_env = getenv("AMDUAT_CONFORMANCE_FIXTURES");
const char *workdir_env = getenv("AMDUAT_CONFORMANCE_WORKDIR");
const char *optional_env = getenv("AMDUAT_CONFORMANCE_INCLUDE_OPTIONAL");
const char *backend_selection = backend_env != NULL ? backend_env : "all";
const char *fixtures_root =
fixtures_env != NULL ? fixtures_env : default_fixtures_root();
char *owned_workdir = NULL;
const char *workdir = workdir_env;
bool include_optional = env_truthy(optional_env);
amduat_conformance_backend_result_t *results = NULL;
size_t results_len = 0u;
size_t i;
int rc;
if (workdir == NULL) {
owned_workdir = make_temp_root();
if (owned_workdir == NULL) {
fprintf(stderr, "failed to create temp workdir\n");
return 1;
}
workdir = owned_workdir;
}
rc = amduat_asl_store_conformance_run(backend_selection, fixtures_root,
workdir, include_optional, false,
&results, &results_len);
if (rc != 0) {
fprintf(stderr, "asl store conformance failed\n");
}
for (i = 0; i < results_len; ++i) {
if (results[i].status == AMDUAT_CONFORMANCE_STATUS_FAIL) {
rc = 1;
}
}
free(results);
free(owned_workdir);
return rc;
}

View file

@ -1,25 +0,0 @@
#ifndef AMDUAT_TESTS_CONFORMANCE_RESULT_H
#define AMDUAT_TESTS_CONFORMANCE_RESULT_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
const char *backend;
int status;
} amduat_conformance_backend_result_t;
enum {
AMDUAT_CONFORMANCE_STATUS_PASS = 0,
AMDUAT_CONFORMANCE_STATUS_FAIL = 1,
AMDUAT_CONFORMANCE_STATUS_SKIP = 2
};
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_TESTS_CONFORMANCE_RESULT_H */

View file

@ -1,87 +0,0 @@
#include "tgk_store_conformance.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static bool env_truthy(const char *value) {
if (value == NULL) {
return false;
}
return strcmp(value, "1") == 0 || strcmp(value, "true") == 0 ||
strcmp(value, "yes") == 0;
}
static char *make_temp_root(void) {
char *templ;
const char template_prefix[] = "/tmp/amduat_conformance_tgk_XXXXXX";
templ = (char *)malloc(sizeof(template_prefix));
if (templ == NULL) {
return NULL;
}
memcpy(templ, template_prefix, sizeof(template_prefix));
if (mkdtemp(templ) == NULL) {
free(templ);
return NULL;
}
return templ;
}
static const char *default_fixtures_root(void) {
struct stat st;
if (stat("tests/fixtures/conformance", &st) == 0) {
return "tests/fixtures/conformance";
}
if (stat("../tests/fixtures/conformance", &st) == 0) {
return "../tests/fixtures/conformance";
}
return "tests/fixtures/conformance";
}
int main(void) {
const char *backend_env = getenv("AMDUAT_CONFORMANCE_TGK_BACKENDS");
const char *fixtures_env = getenv("AMDUAT_CONFORMANCE_FIXTURES");
const char *workdir_env = getenv("AMDUAT_CONFORMANCE_WORKDIR");
const char *optional_env = getenv("AMDUAT_CONFORMANCE_INCLUDE_OPTIONAL");
const char *backend_selection = backend_env != NULL ? backend_env : "all";
const char *fixtures_root =
fixtures_env != NULL ? fixtures_env : default_fixtures_root();
char *owned_workdir = NULL;
const char *workdir = workdir_env;
bool include_optional = env_truthy(optional_env);
amduat_conformance_backend_result_t *results = NULL;
size_t results_len = 0u;
size_t i;
int rc;
if (workdir == NULL) {
owned_workdir = make_temp_root();
if (owned_workdir == NULL) {
fprintf(stderr, "failed to create temp workdir\n");
return 1;
}
workdir = owned_workdir;
}
rc = amduat_tgk_store_conformance_run(backend_selection, fixtures_root,
workdir, include_optional, false,
&results, &results_len);
if (rc != 0) {
fprintf(stderr, "tgk store conformance failed\n");
}
for (i = 0; i < results_len; ++i) {
if (results[i].status == AMDUAT_CONFORMANCE_STATUS_FAIL) {
rc = 1;
}
}
free(results);
free(owned_workdir);
return rc;
}

File diff suppressed because it is too large Load diff

View file

@ -1,26 +0,0 @@
#ifndef AMDUAT_TESTS_CONFORMANCE_TGK_STORE_CONFORMANCE_H
#define AMDUAT_TESTS_CONFORMANCE_TGK_STORE_CONFORMANCE_H
#include "tests/conformance/conformance_result.h"
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
int amduat_tgk_store_conformance_run(
const char *backend_selection,
const char *fixtures_root,
const char *workdir,
bool include_optional,
bool verbose,
amduat_conformance_backend_result_t **out_results,
size_t *out_len);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_TESTS_CONFORMANCE_TGK_STORE_CONFORMANCE_H */

View file

@ -13,77 +13,6 @@ enum {
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) {
out[0] = (uint8_t)(value & 0xffu);
out[1] = (uint8_t)((value >> 8) & 0xffu);
@ -288,68 +217,6 @@ cleanup:
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) {
if (test_round_trip() != 0) {
return 1;
@ -360,11 +227,5 @@ int main(void) {
if (test_truncated_decode() != 0) {
return 1;
}
if (test_invalid_input_count() != 0) {
return 1;
}
if (test_invalid_snapshot_range() != 0) {
return 1;
}
return test_fixture_plan0();
return test_invalid_input_count();
}

View file

@ -1,236 +0,0 @@
#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;
}

View file

@ -1,626 +0,0 @@
#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;
}

View file

@ -1,22 +0,0 @@
# 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 +0,0 @@
hello amduat

View file

@ -1,23 +0,0 @@
{
"schema": "amduat.conformance.fixtures.v0",
"fixtures": [
{
"id": "a0",
"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"
}
]
}

View file

@ -1,12 +0,0 @@
{
"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

@ -1,65 +0,0 @@
{
"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

@ -1,62 +0,0 @@
#!/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

@ -1,99 +0,0 @@
# 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

@ -1,26 +0,0 @@
{
"schema": "amduat.conformance.tgk_edges.v0",
"edges": [
{
"id": "e1",
"type": 16,
"from": ["00010a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"],
"to": ["00010b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"],
"payload": "00011a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a"
},
{
"id": "e2",
"type": 16,
"from": ["00010a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"],
"to": ["00010c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"],
"payload": "00011b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b"
},
{
"id": "e3",
"type": 32,
"from": ["00010b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"],
"to": ["00010a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"],
"payload": "00011c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c"
}
]
}

View file

@ -1,442 +0,0 @@
#include "amduat/asl/asl_derivation_index_fs.h"
#include "amduat/asl/asl_store_index_fs.h"
#include "amduat/asl/collection.h"
#include "amduat/asl/log_cursor.h"
#include "amduat/asl/projection_checkpoint.h"
#include "amduat/asl/record.h"
#include "amduat/asl/store.h"
#include "amduat/enc/asl1_core.h"
#include "amduat/enc/tgk1_edge.h"
#include "amduat/hash/asl1.h"
#include "amduat/pel/derivation_sid.h"
#include "amduat/tgk/store.h"
#include "amduat/tgk/tgk_store_asl_index_fs.h"
#include <dirent.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static char *make_temp_root(void) {
char *templ;
const char template_prefix[] = "/tmp/amduat_test_scriptlab_adapter_XXXXXX";
templ = (char *)malloc(sizeof(template_prefix));
if (templ == NULL) {
return NULL;
}
memcpy(templ, template_prefix, sizeof(template_prefix));
if (mkdtemp(templ) == NULL) {
free(templ);
return NULL;
}
return templ;
}
static bool remove_tree(const char *path) {
struct stat st;
DIR *dir;
struct dirent *entry;
if (path == NULL) {
return false;
}
if (lstat(path, &st) != 0) {
return errno == ENOENT;
}
if (!S_ISDIR(st.st_mode)) {
return unlink(path) == 0;
}
dir = opendir(path);
if (dir == NULL) {
return false;
}
while ((entry = readdir(dir)) != NULL) {
char child[2048];
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0) {
continue;
}
snprintf(child, sizeof(child), "%s/%s", path, entry->d_name);
if (!remove_tree(child)) {
closedir(dir);
return false;
}
}
closedir(dir);
return rmdir(path) == 0;
}
static bool ref_list_contains(amduat_reference_t needle,
const amduat_reference_t *list,
size_t len) {
for (size_t i = 0u; i < len; ++i) {
if (amduat_reference_eq(needle, list[i])) {
return true;
}
}
return false;
}
static int test_scriptlab_adapter_flow(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t asl_fs;
amduat_asl_store_t asl_store;
amduat_asl_collection_store_t collection_store;
amduat_asl_derivation_index_fs_t deriv_index;
amduat_reference_t node_a;
amduat_reference_t node_b;
amduat_reference_t edge_payload_ref;
amduat_reference_t edge_ref;
amduat_reference_t snapshot_ref;
amduat_reference_t program_ref;
amduat_reference_t checkpoint_ref;
amduat_octets_t edge_bytes = amduat_octets(NULL, 0u);
amduat_octets_t checkpoint_bytes = amduat_octets(NULL, 0u);
amduat_octets_t sid = amduat_octets(NULL, 0u);
amduat_asl_log_record_t *log_records = NULL;
size_t log_count = 0u;
amduat_asl_log_position_t next_logseq = 0u;
bool log_end = true;
amduat_asl_index_state_t index_state;
uint64_t collection_offset = 0u;
bool swapped = false;
char *root;
int exit_code = 1;
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "temp root failed\n");
return 1;
}
memset(&config, 0, sizeof(config));
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_index_fs_init(&asl_fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup_root;
}
amduat_asl_store_init(&asl_store, config, amduat_asl_store_index_fs_ops(), &asl_fs);
if (!amduat_asl_collection_store_init(&collection_store, root, &asl_store)) {
fprintf(stderr, "collection store init failed\n");
goto cleanup_root;
}
if (!amduat_asl_derivation_index_fs_init(&deriv_index, root)) {
fprintf(stderr, "derivation index init failed\n");
goto cleanup_root;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("scriptlab/node", strlen("scriptlab/node")),
amduat_octets("A", 1u),
&node_a) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "node A store failed\n");
goto cleanup_root;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("scriptlab/node", strlen("scriptlab/node")),
amduat_octets("B", 1u),
&node_b) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "node B store failed\n");
goto cleanup_nodes;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("tgk/edge_payload", strlen("tgk/edge_payload")),
amduat_octets("relates", strlen("relates")),
&edge_payload_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "edge payload store failed\n");
goto cleanup_nodes;
}
{
amduat_tgk_edge_body_t edge;
amduat_reference_t from_refs[1] = {node_a};
amduat_reference_t to_refs[1] = {node_b};
memset(&edge, 0, sizeof(edge));
edge.type = 1u;
edge.from = from_refs;
edge.from_len = 1u;
edge.to = to_refs;
edge.to_len = 1u;
edge.payload = edge_payload_ref;
if (!amduat_enc_tgk1_edge_encode_v1(&edge, &edge_bytes)) {
fprintf(stderr, "edge encode failed\n");
goto cleanup_nodes;
}
}
{
amduat_artifact_t edge_artifact = amduat_artifact_with_type(
edge_bytes, amduat_type_tag(AMDUAT_TYPE_TAG_TGK1_EDGE_V1));
if (amduat_asl_store_put(&asl_store, edge_artifact, &edge_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "edge store failed\n");
goto cleanup_edge_bytes;
}
}
amduat_octets_free(&edge_bytes);
if (amduat_asl_collection_append(&collection_store,
"tgk_edges",
edge_ref,
0u,
amduat_octets(NULL, 0u),
&collection_offset) != AMDUAT_ASL_COLLECTION_OK) {
fprintf(stderr, "collection append failed\n");
goto cleanup_edge_ref;
}
if (amduat_asl_collection_snapshot(&collection_store,
"tgk_edges",
UINT64_MAX,
&snapshot_ref,
&swapped) != AMDUAT_ASL_COLLECTION_OK) {
fprintf(stderr, "collection snapshot failed\n");
goto cleanup_edge_ref;
}
{
amduat_asl_record_t snapshot_record;
if (amduat_asl_record_store_get(&asl_store, snapshot_ref, &snapshot_record) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "snapshot record fetch failed\n");
goto cleanup_snapshot_ref;
}
{
amduat_asl_collection_snapshot_payload_t payload;
if (!amduat_asl_collection_snapshot_payload_decode_v1(
snapshot_record.payload, &payload)) {
fprintf(stderr, "snapshot payload decode failed\n");
amduat_asl_record_free(&snapshot_record);
goto cleanup_snapshot_ref;
}
if (!ref_list_contains(edge_ref, payload.refs, payload.refs_len)) {
fprintf(stderr, "snapshot missing edge ref\n");
amduat_asl_collection_snapshot_payload_free(&payload);
amduat_asl_record_free(&snapshot_record);
goto cleanup_snapshot_ref;
}
amduat_asl_collection_snapshot_payload_free(&payload);
}
amduat_asl_record_free(&snapshot_record);
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("scriptlab/transform", strlen("scriptlab/transform")),
amduat_octets("tgk_snapshot_v1", strlen("tgk_snapshot_v1")),
&program_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "program record store failed\n");
goto cleanup_snapshot_ref;
}
{
amduat_pel_derivation_sid_input_t sid_input;
amduat_asl_derivation_record_t record;
amduat_octets_t exec_profile =
amduat_octets("scriptlab/adapter", strlen("scriptlab/adapter"));
amduat_reference_t inputs[1] = {edge_ref};
memset(&sid_input, 0, sizeof(sid_input));
sid_input.program_ref = program_ref;
sid_input.input_refs = inputs;
sid_input.input_refs_len = 1u;
sid_input.has_params_ref = false;
sid_input.has_exec_profile = true;
sid_input.exec_profile = exec_profile;
if (!amduat_pel_derivation_sid_compute(&sid_input, &sid)) {
fprintf(stderr, "sid compute failed\n");
goto cleanup_program_ref;
}
memset(&record, 0, sizeof(record));
record.sid = sid;
record.program_ref = program_ref;
record.output_index = 0u;
record.input_refs = inputs;
record.input_refs_len = 1u;
record.has_params_ref = false;
record.has_exec_profile = true;
record.exec_profile = exec_profile;
if (amduat_asl_derivation_index_fs_add(&deriv_index,
snapshot_ref,
&record) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "derivation index add failed\n");
goto cleanup_sid;
}
}
if (!amduat_asl_index_current_state(&asl_store, &index_state)) {
fprintf(stderr, "index state read failed\n");
goto cleanup_sid;
}
if (!amduat_asl_log_read_since(&asl_store,
0u,
0u,
&log_records,
&log_count,
&next_logseq,
&log_end)) {
fprintf(stderr, "log read since failed\n");
goto cleanup_sid;
}
{
amduat_asl_projection_checkpoint_v1_t checkpoint;
amduat_asl_projection_checkpoint_v1_t decoded;
memset(&checkpoint, 0, sizeof(checkpoint));
checkpoint.checkpoint_version = AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION_V1;
checkpoint.projection_name =
amduat_octets("scriptlab", strlen("scriptlab"));
checkpoint.projection_schema_version = 1u;
checkpoint.has_snapshot_id = true;
checkpoint.snapshot_id = index_state.snapshot_id;
checkpoint.log_position = next_logseq;
checkpoint.created_at = 0u;
checkpoint.builder_id =
amduat_octets("scriptlab-adapter", strlen("scriptlab-adapter"));
checkpoint.has_artifact_count = true;
checkpoint.artifact_count = log_count;
if (!amduat_asl_projection_checkpoint_encode_v1(&checkpoint,
&checkpoint_bytes)) {
fprintf(stderr, "checkpoint encode failed\n");
goto cleanup_log_records;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("projection/checkpoint",
strlen("projection/checkpoint")),
checkpoint_bytes,
&checkpoint_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "checkpoint store failed\n");
goto cleanup_checkpoint_bytes;
}
if (!amduat_asl_projection_checkpoint_decode_v1(checkpoint_bytes,
&decoded)) {
fprintf(stderr, "checkpoint decode failed\n");
goto cleanup_checkpoint_ref;
}
if (decoded.log_position != next_logseq) {
fprintf(stderr, "checkpoint log position mismatch\n");
amduat_asl_projection_checkpoint_free(&decoded);
goto cleanup_checkpoint_ref;
}
amduat_asl_projection_checkpoint_free(&decoded);
}
{
amduat_tgk_identity_domain_t domains[1];
uint32_t edge_tags[1];
amduat_tgk_edge_type_id_t edge_types[1];
amduat_asl_encoding_profile_id_t encodings[1];
amduat_tgk_store_config_t tgk_config;
amduat_tgk_store_asl_index_fs_t tgk_fs;
amduat_tgk_store_t tgk_store;
amduat_tgk_edge_type_filter_t filter;
amduat_tgk_graph_edge_view_list_t edges;
bool found = false;
memset(&tgk_config, 0, sizeof(tgk_config));
domains[0].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1;
domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
tgk_config.id_space.domains = domains;
tgk_config.id_space.domains_len = 1u;
tgk_config.artifact_scope.description = amduat_octets(NULL, 0u);
edge_tags[0] = AMDUAT_TYPE_TAG_TGK1_EDGE_V1;
edge_types[0] = 1u;
encodings[0] = TGK1_EDGE_ENC_V1;
tgk_config.tgk_profiles.edge_tags = edge_tags;
tgk_config.tgk_profiles.edge_tags_len = 1u;
tgk_config.tgk_profiles.edge_types = edge_types;
tgk_config.tgk_profiles.edge_types_len = 1u;
tgk_config.tgk_profiles.encodings = encodings;
tgk_config.tgk_profiles.encodings_len = 1u;
if (!amduat_tgk_store_asl_index_fs_init(&tgk_fs, tgk_config, &asl_fs)) {
fprintf(stderr, "tgk store init failed\n");
goto cleanup_checkpoint_ref;
}
amduat_tgk_store_init(&tgk_store,
tgk_config,
amduat_tgk_store_asl_index_fs_ops(),
&tgk_fs);
filter.types = edge_types;
filter.types_len = 1u;
if (!amduat_tgk_store_edges_from(&tgk_store, node_a, filter, &edges)) {
fprintf(stderr, "tgk edges_from failed\n");
goto cleanup_checkpoint_ref;
}
for (size_t i = 0u; i < edges.len; ++i) {
amduat_tgk_edge_body_t *body = &edges.edges[i].body;
if (ref_list_contains(node_b, body->to, body->to_len)) {
found = true;
break;
}
}
amduat_tgk_graph_edge_view_list_free(&edges);
if (!found) {
fprintf(stderr, "tgk traversal missing expected edge\n");
goto cleanup_checkpoint_ref;
}
}
exit_code = 0;
cleanup_checkpoint_ref:
amduat_reference_free(&checkpoint_ref);
cleanup_log_records:
amduat_enc_asl_log_free(log_records, log_count);
cleanup_checkpoint_bytes:
amduat_octets_free(&checkpoint_bytes);
cleanup_sid:
amduat_octets_free(&sid);
cleanup_program_ref:
amduat_reference_free(&program_ref);
cleanup_snapshot_ref:
amduat_reference_free(&snapshot_ref);
cleanup_edge_ref:
amduat_reference_free(&edge_ref);
cleanup_edge_bytes:
amduat_octets_free(&edge_bytes);
cleanup_nodes:
amduat_reference_free(&edge_payload_ref);
amduat_reference_free(&node_b);
amduat_reference_free(&node_a);
cleanup_root:
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
int main(void) {
return test_scriptlab_adapter_flow();
}

View file

@ -1,103 +0,0 @@
#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

@ -1,65 +0,0 @@
{
"hash_id": "0x0001",
"artifacts": [
{
"name": "program",
"case": null,
"type_tag": "0x00000101",
"artifact_bytes_hex": "01000001010000000000000036000100000001000000000000001070656c2e62797465732e706172616d73000000010000000000000000000000010000000000000000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d"
}
},
{
"name": "trace_case_a",
"case": "A",
"type_tag": "0x00000102",
"artifact_bytes_hex": "010000010200000000000000d30001000000220001c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d0000000000000000000000010000002200013e7077fd2f66d689e0cee6a7cf5b37bf2dca7c979af356d0a31cbc5c85605c7d00000001000000000000001070656c2e62797465732e706172616d73000000010000000000000000010000002200013e7077fd2f66d689e0cee6a7cf5b37bf2dca7c979af356d0a31cbc5c85605c7d00000000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "84f1a84d714c3038f39d21bb31e157f5d354e64542aa5ae76285ae3489ad477f"
}
},
{
"name": "trace_case_b",
"case": "B",
"type_tag": "0x00000102",
"artifact_bytes_hex": "0100000102000000000000005e0001000000220001c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d03030000000300000000000000000000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "f1541038d73fcd3b588843506779f1f7aea48fd3b598b68da905776c87bdcdd6"
}
},
{
"name": "edge_case_a",
"case": "A",
"type_tag": "0x00000201",
"artifact_bytes_hex": "0100000201000000000000008000010000001000000001000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d000000010000002200013e7077fd2f66d689e0cee6a7cf5b37bf2dca7c979af356d0a31cbc5c85605c7d00000022000184f1a84d714c3038f39d21bb31e157f5d354e64542aa5ae76285ae3489ad477f",
"ref": {
"hash_id": "0x0001",
"digest_hex": "bba74729a89580021c6625908ea762c0f0f0a3599b05934b0bb581a7016d975d"
}
},
{
"name": "edge_case_b",
"case": "B",
"type_tag": "0x00000201",
"artifact_bytes_hex": "0100000201000000000000005a00010000001000000001000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d00000000000000220001f1541038d73fcd3b588843506779f1f7aea48fd3b598b68da905776c87bdcdd6",
"ref": {
"hash_id": "0x0001",
"digest_hex": "013abc2facf80800b8472707d1d87dcf12e8bc9562eeddf46e1d2186e5436e65"
}
},
{
"name": "scheme_desc",
"case": null,
"type_tag": "0x00000100",
"artifact_bytes_hex": "0100000100000000000000001f00010000001150454c2f50524f4752414d2d4441472f310000010101010000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd"
}
}
]
}

View file

@ -1,118 +0,0 @@
{
"hash_id": "0x0001",
"artifacts": [
{
"name": "scheme_desc",
"case": null,
"type_tag": "0x00000100",
"artifact_bytes_hex": "0100000100000000000000001f00010000001150454c2f50524f4752414d2d4441472f310000010101010000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd"
}
},
{
"name": "program",
"case": null,
"type_tag": "0x00000101",
"artifact_bytes_hex": "01000001010000000000000036000100000001000000000000001070656c2e62797465732e706172616d73000000010000000000000000000000010000000000000000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d"
}
},
{
"name": "result_case_a",
"case": "A",
"type_tag": "0x00000103",
"artifact_bytes_hex": "010000010300000000000000d70001000000220001c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d00000000000000010000002200013e7077fd2f66d689e0cee6a7cf5b37bf2dca7c979af356d0a31cbc5c85605c7d010000002200013e7077fd2f66d689e0cee6a7cf5b37bf2dca7c979af356d0a31cbc5c85605c7d0000000100000000220001c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd000000000000000000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "9aec50c18bd4e079da3acf54bc17598a404c08395044f11a462f9326b3ececc0"
}
},
{
"name": "result_case_b",
"case": "B",
"type_tag": "0x00000103",
"artifact_bytes_hex": "0100000103000000000000008b0001000000220001c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d0000000000000000000000000103000000220001c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd030000000300000000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "97c4e17c93a1539794040d0bb0cd79a0bd173ab38a530e9503435029bc6e57a9"
}
},
{
"name": "trace_case_a_with_exec_result_ref",
"case": "A",
"type_tag": "0x00000102",
"artifact_bytes_hex": "010000010200000000000000f90001000000220001c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d000000000000010000002200019aec50c18bd4e079da3acf54bc17598a404c08395044f11a462f9326b3ececc000000000010000002200013e7077fd2f66d689e0cee6a7cf5b37bf2dca7c979af356d0a31cbc5c85605c7d00000001000000000000001070656c2e62797465732e706172616d73000000010000000000000000010000002200013e7077fd2f66d689e0cee6a7cf5b37bf2dca7c979af356d0a31cbc5c85605c7d00000000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "b303ac68081673e7785e4765d895efed475a5d1c1b4f2a675b42df40ef06913c"
}
},
{
"name": "trace_case_b_with_exec_result_ref",
"case": "B",
"type_tag": "0x00000102",
"artifact_bytes_hex": "010000010200000000000000840001000000220001c50fb2a734a5cc233c3875b70a7d96eaad374f000029771d8bef1af2cd6384dd000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d0303000000030100000022000197c4e17c93a1539794040d0bb0cd79a0bd173ab38a530e9503435029bc6e57a9000000000000000000",
"ref": {
"hash_id": "0x0001",
"digest_hex": "23eb6d3f723a18d265c9e3f049b033623b219d0ff828e227ece1d557f223e4f8"
}
},
{
"name": "edge_case_a_payload_result_ref",
"case": "A",
"type_tag": "0x00000201",
"artifact_bytes_hex": "010000020100000000000000a600010000001000000001000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d000000020000002200013e7077fd2f66d689e0cee6a7cf5b37bf2dca7c979af356d0a31cbc5c85605c7d0000002200019aec50c18bd4e079da3acf54bc17598a404c08395044f11a462f9326b3ececc00000002200019aec50c18bd4e079da3acf54bc17598a404c08395044f11a462f9326b3ececc0",
"ref": {
"hash_id": "0x0001",
"digest_hex": "21de6c0c1fb9112a485558ed5596e36594ecc16b7869f1ef4d4dd3cd74c8a6c4"
}
},
{
"name": "edge_case_b_payload_result_ref",
"case": "B",
"type_tag": "0x00000201",
"artifact_bytes_hex": "0100000201000000000000008000010000001000000001000000220001f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d0000000100000022000197c4e17c93a1539794040d0bb0cd79a0bd173ab38a530e9503435029bc6e57a900000022000197c4e17c93a1539794040d0bb0cd79a0bd173ab38a530e9503435029bc6e57a9",
"ref": {
"hash_id": "0x0001",
"digest_hex": "3aa41e2c2c5ad9884a00fb047a0728ce163e3be5493d55c1d4261be3b116f317"
}
}
],
"links": {
"A": {
"program_ref": "0x0001:f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d",
"execution_result_ref": "0x0001:9aec50c18bd4e079da3acf54bc17598a404c08395044f11a462f9326b3ececc0",
"trace_ref": "0x0001:b303ac68081673e7785e4765d895efed475a5d1c1b4f2a675b42df40ef06913c",
"edge_ref": "0x0001:21de6c0c1fb9112a485558ed5596e36594ecc16b7869f1ef4d4dd3cd74c8a6c4",
"expected_edge_body": {
"from": [
"0x0001:f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d"
],
"to": [
"0x0001:3e7077fd2f66d689e0cee6a7cf5b37bf2dca7c979af356d0a31cbc5c85605c7d",
"0x0001:9aec50c18bd4e079da3acf54bc17598a404c08395044f11a462f9326b3ececc0"
],
"payload": "0x0001:9aec50c18bd4e079da3acf54bc17598a404c08395044f11a462f9326b3ececc0"
}
},
"B": {
"program_ref": "0x0001:f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d",
"execution_result_ref": "0x0001:97c4e17c93a1539794040d0bb0cd79a0bd173ab38a530e9503435029bc6e57a9",
"trace_ref": "0x0001:23eb6d3f723a18d265c9e3f049b033623b219d0ff828e227ece1d557f223e4f8",
"edge_ref": "0x0001:3aa41e2c2c5ad9884a00fb047a0728ce163e3be5493d55c1d4261be3b116f317",
"expected_edge_body": {
"from": [
"0x0001:f59617255b09759dfff00952babc61ee9b4163cf4715a181539c5294fa4aa63d"
],
"to": [
"0x0001:97c4e17c93a1539794040d0bb0cd79a0bd173ab38a530e9503435029bc6e57a9"
],
"payload": "0x0001:97c4e17c93a1539794040d0bb0cd79a0bd173ab38a530e9503435029bc6e57a9"
}
}
}
}

View file

@ -2,9 +2,9 @@
Status: Approved
Owner: Niklas Rydberg
Version: 0.4.2
Version: 0.4.1
SoT: Yes
Last Updated: 2026-02-21
Last Updated: 2025-11-16
Linked Phase Pack: N/A
Tags: [deterministic, binary-minimalism]
@ -20,7 +20,7 @@ Tags: [deterministic, binary-minimalism]
**Informative references:**
* `ENC/ASL1-CORE v1.x` — canonical encoding profile (`ASL_ENC_CORE_V1`)
* `HASH/ASL1 v0.2.5` — ASL1 hash family and `HashId` assignments
* `HASH/ASL1 v0.2.2` — ASL1 hash family and `HashId` assignments
* `ASL/1-STORE v0.4.0` — content-addressable store over ASL/1-CORE
* `TGK/1-CORE v0.7.0` — trace graph kernel over `Reference`
* `PEL/1` — execution substrate
@ -289,16 +289,12 @@ then the Reference `R` for `A` under `(P, H)` is:
```text
ArtifactBytes = encode_P(A)
digest = H("AMDUAT:ASL1:ART\0" || ArtifactBytes)
digest = H(ArtifactBytes)
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**
> Any component that claims to derive References from Artifacts for a given `(EncodingProfileId, HashId)` **MUST** use this exact procedure, including the domain separator.
> Any component that claims to derive References from Artifacts for a given `(EncodingProfileId, HashId)` **MUST** use this exact procedure.
### 4.3 Deterministic agreement lemma (informative)
@ -310,7 +306,7 @@ For any two conformant implementations that share:
then for any Artifact `A`:
* both will compute identical `ArtifactBytes`,
* both will compute identical `digest = H("AMDUAT:ASL1:ART\0" || ArtifactBytes)`,
* both will compute identical `digest = H(ArtifactBytes)`,
* both will form identical `Reference {hash_id = HID, digest = digest}`.
This is the basis for cross-Store and cross-domain determinism in Amduat.
@ -320,7 +316,6 @@ 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:
* `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.
Other `(EncodingProfileId, HashId)` pairs are allowed but must be explicitly declared by the consuming profile or implementation.
@ -500,5 +495,4 @@ Everything else — API design, transport formats, performance characteristics,
## 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.

View file

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

View file

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

View file

@ -115,22 +115,6 @@ in `ENC-ASL-LOG`. The envelope MUST include:
* `payload` (type-specific bytes)
* `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)
@ -168,8 +152,6 @@ Semantics:
* Segment MUST be immutable.
* All referenced blocks MUST already be sealed.
* 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
@ -229,9 +211,6 @@ typedef struct {
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.
* Enables log truncation below anchor with care.
@ -270,8 +249,7 @@ typedef struct {
To reconstruct CURRENT:
1. Load latest snapshot anchor (if any). If absent, treat
`snapshot_id = 0` and `snapshot.logseq = 0`.
1. Load latest snapshot anchor (if any).
2. Initialize visible segments from that snapshot.
3. Replay all log records with `logseq > snapshot.logseq`.
4. Apply records in order:

View file

@ -87,10 +87,6 @@ Excludes:
* Memory residency or caching
* 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
@ -148,10 +144,6 @@ 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
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
* **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
Status: Approved
Status: Draft
Owner: Architecture
Version: 1.0.0
SoT: Yes
Last Updated: 2026-02-21
Version: 0.1.0
SoT: No
Last Updated: 2025-01-17
Linked Phase Pack: N/A
Tags: [execution, query, tgk, determinism]
@ -82,10 +82,6 @@ Each operator includes:
* `traversal`: optional traversal parameters
* `aggregation`: optional aggregation parameters
Snapshot bounds MUST satisfy:
* `logseq_min <= logseq_max`
---
## 2.1 Query Abstraction (Informative)

View file

@ -1,83 +0,0 @@
# 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,104 +453,6 @@ 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`.
* 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)
> **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
Status: Approved
Status: Draft
Owner: Niklas Rydberg
Version: 1.0.0
SoT: Yes
Last Updated: 2026-02-21
Version: 0.1.0
SoT: No
Last Updated: 2025-11-16
Linked Phase Pack: N/A
Tags: [encoding, index, deterministic]

View file

@ -141,21 +141,6 @@ continue replay without failure.
* Unknown `record_type` values are the only exception: they MUST be skipped
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)

View file

@ -2,9 +2,9 @@
Status: Approved
Owner: Niklas Rydberg
Version: 1.0.6
Version: 1.0.5
SoT: Yes
Last Updated: 2026-02-21
Last Updated: 2025-11-16
Linked Phase Pack: N/A
Tags: [deterministic, binary-minimalism]
@ -16,11 +16,11 @@ Tags: [deterministic, binary-minimalism]
**Depends on (normative):**
* **ASL/1-CORE v0.4.2** (value model: `Artifact`, `TypeTag`, `Reference`, `HashId`)
* **ASL/1-CORE v0.4.1** (value model: `Artifact`, `TypeTag`, `Reference`, `HashId`)
**Integrates with (cross-profile rules):**
* **HASH/ASL1 v0.2.5** (ASL1 hash family: registry of `HashId → algorithm, digest length`)
* **HASH/ASL1 v0.2.4** (ASL1 hash family: registry of `HashId → algorithm, digest length`)
* 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).
@ -424,14 +424,10 @@ For encoding profile `ASL_ENC_CORE_V1`, the canonical rule for constructing `Ref
```text
ArtifactBytes = encode_artifact_core_v1(A)
digest = H("AMDUAT:ASL1:ART\0" || ArtifactBytes)
digest = H(ArtifactBytes)
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:
* `A` is an `Artifact` (ASL/1-CORE),
@ -465,7 +461,7 @@ If two implementations:
then for any `Artifact A` they MUST:
* produce identical `ArtifactBytes = encode_artifact_core_v1(A)`,
* produce identical `digest = H("AMDUAT:ASL1:ART\0" || ArtifactBytes)`,
* produce identical `digest = H(ArtifactBytes)`,
* 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.
@ -510,10 +506,10 @@ Canonical `ArtifactBytes`:
00 0000000000000002 DEAD
```
Digest with `HASH-ASL1-256` (SHA-256) and domain separation:
Digest with `HASH-ASL1-256` (SHA-256):
```text
digest = SHA-256("AMDUAT:ASL1:ART\0" || 00 0000000000000002 DEAD)
digest = SHA-256(00 0000000000000002 DEAD)
```
Assuming `HashId = 0001` for `HASH-ASL1-256`, the `ReferenceBytes` are:
@ -559,7 +555,7 @@ Hashing and `ReferenceBytes` proceed as in §6.1.
## 7. Conformance
An implementation conforms to `ENC/ASL1-CORE v1.0.6` if and only if it:
An implementation conforms to `ENC/ASL1-CORE v1.0.5` if and only if it:
1. **Correctly encodes and decodes Artifacts**
@ -577,7 +573,7 @@ An implementation conforms to `ENC/ASL1-CORE v1.0.6` if and only if it:
3. **Implements canonical hashing correctly**
* 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.
* Uses `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.
* Does not derive `Reference`s from non-canonical or alternative encodings in contexts that claim to use `ASL_ENC_CORE_V1`.
@ -599,5 +595,4 @@ Everything else — transport, storage layout, replication, indexing, overlays,
## 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.

View file

@ -1,95 +0,0 @@
# 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.

View file

@ -1,183 +0,0 @@
# 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.

View file

@ -1,137 +0,0 @@
# 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.

Some files were not shown because too many files have changed in this diff Show more