diff --git a/CMakeLists.txt b/CMakeLists.txt index e048d30..8316992 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,8 @@ target_link_libraries(amduat_federation PRIVATE amduat_asl amduat_enc amduat_util amduat_fed ) -set(amduatd_sources src/amduatd.c src/amduatd_caps.c src/amduatd_space.c) +set(amduatd_sources src/amduatd.c src/amduatd_caps.c src/amduatd_space.c + src/amduatd_concepts.c) if(AMDUATD_ENABLE_UI) list(APPEND amduatd_sources src/amduatd_ui.c) endif() diff --git a/src/amduatd.c b/src/amduatd.c index 677e3ec..0df8103 100644 --- a/src/amduatd.c +++ b/src/amduatd.c @@ -33,6 +33,7 @@ #include "amduat/util/hex.h" #include "amduat/util/log.h" #include "amduat/hash/asl1.h" +#include "amduatd_concepts.h" #include "amduatd_ui.h" #include "amduatd_caps.h" #include "amduatd_space.h" @@ -74,12 +75,25 @@ bool amduatd_send_json_error(int fd, const char *reason, const char *msg); +amduatd_ref_status_t amduatd_decode_ref_or_name_latest( + amduat_asl_store_t *store, + const amduat_asl_store_fs_config_t *cfg, + const amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg, + const char *s, + size_t len, + amduat_reference_t *out_ref); + #if AMDUATD_ENABLE_UI bool amduatd_seed_ui_html(amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, amduat_reference_t *out_ref); #endif +bool amduatd_seed_ms_ui_state(amduat_asl_store_t *store, + amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg); + static uint64_t amduatd_now_ms(void) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { @@ -94,814 +108,7 @@ static bool amduatd_strbuf_append_char(amduatd_strbuf_t *b, char c); static const char *const AMDUATD_DEFAULT_ROOT = ".amduat-asl"; static const char *const AMDUATD_DEFAULT_SOCK = "amduatd.sock"; -static const char *const AMDUATD_EDGES_FILE = ".amduatd.edges"; -static const char *const AMDUATD_EDGE_COLLECTION = "daemon/edges"; static const uint64_t AMDUATD_FED_TICK_MS = 1000u; -static const uint32_t AMDUATD_EDGE_VIEW_BATCH = 1024u; - -static const uint16_t AMDUATD_EDGE_COLLECTION_KIND = 1u; - -typedef struct { - amduat_reference_t record_ref; - amduat_reference_t src_ref; - amduat_reference_t dst_ref; - char *rel; -} amduatd_edge_entry_t; - -typedef struct { - amduatd_edge_entry_t *items; - size_t len; - size_t cap; -} amduatd_edge_list_t; - -static void amduatd_edge_entry_free(amduatd_edge_entry_t *entry) { - if (entry == NULL) { - return; - } - amduat_reference_free(&entry->record_ref); - amduat_reference_free(&entry->src_ref); - amduat_reference_free(&entry->dst_ref); - free(entry->rel); - entry->rel = NULL; -} - -static void amduatd_edge_list_clear(amduatd_edge_list_t *list) { - if (list == NULL) { - return; - } - for (size_t i = 0; i < list->len; ++i) { - amduatd_edge_entry_free(&list->items[i]); - } - free(list->items); - list->items = NULL; - list->len = 0; - list->cap = 0; -} - -static bool amduatd_edge_list_push(amduatd_edge_list_t *list, - const amduatd_edge_entry_t *entry) { - amduatd_edge_entry_t cloned; - amduatd_edge_entry_t *next; - - if (list == NULL || entry == NULL) { - return false; - } - memset(&cloned, 0, sizeof(cloned)); - if (!amduat_reference_clone(entry->record_ref, &cloned.record_ref) || - !amduat_reference_clone(entry->src_ref, &cloned.src_ref) || - !amduat_reference_clone(entry->dst_ref, &cloned.dst_ref)) { - amduatd_edge_entry_free(&cloned); - return false; - } - if (entry->rel != NULL) { - cloned.rel = strdup(entry->rel); - if (cloned.rel == NULL) { - amduatd_edge_entry_free(&cloned); - return false; - } - } - if (list->len == list->cap) { - size_t next_cap = list->cap != 0 ? list->cap * 2u : 64u; - next = (amduatd_edge_entry_t *)realloc(list->items, - next_cap * sizeof(*list->items)); - if (next == NULL) { - amduatd_edge_entry_free(&cloned); - return false; - } - list->items = next; - list->cap = next_cap; - } - list->items[list->len++] = cloned; - return true; -} - -typedef struct amduatd_concepts_t { - const char *root_path; - char edges_path[1024]; - char *edge_collection_name; - amduat_reference_t rel_aliases_ref; - amduat_reference_t rel_materializes_ref; - amduat_reference_t rel_represents_ref; - amduat_reference_t rel_requires_key_ref; - amduat_reference_t rel_within_domain_ref; - amduat_reference_t rel_computed_by_ref; - amduat_reference_t rel_has_provenance_ref; - amduat_asl_collection_store_t edge_collection; - amduatd_edge_list_t edges; -} amduatd_concepts_t; - -static bool amduatd_concepts_put_name_artifact(amduat_asl_store_t *store, - const char *name, - amduat_reference_t *out_ref); -static bool amduatd_concepts_put_edge(amduat_asl_store_t *store, - amduatd_concepts_t *c, - amduat_reference_t from, - amduat_reference_t to, - amduat_reference_t relation_concept_ref, - amduat_octets_t actor, - amduat_reference_t *out_edge_ref); -static bool amduatd_concepts_lookup_alias(amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - const amduatd_concepts_t *c, - const char *name, - amduat_reference_t *out_concept_ref); - -static void amduatd_concepts_free(amduatd_concepts_t *c) { - if (c == NULL) { - return; - } - amduat_reference_free(&c->rel_aliases_ref); - amduat_reference_free(&c->rel_materializes_ref); - amduat_reference_free(&c->rel_represents_ref); - amduat_reference_free(&c->rel_requires_key_ref); - amduat_reference_free(&c->rel_within_domain_ref); - amduat_reference_free(&c->rel_computed_by_ref); - amduat_reference_free(&c->rel_has_provenance_ref); - amduatd_edge_list_clear(&c->edges); - free(c->edge_collection_name); - c->edge_collection_name = NULL; -} - -enum { - AMDUATD_EDGE_MAGIC_LEN = 8, - AMDUATD_EDGE_VERSION = 1 -}; - -static const uint8_t k_amduatd_edge_magic[AMDUATD_EDGE_MAGIC_LEN] = { - 'A', 'S', 'L', 'E', 'D', 'G', '1', '\0' -}; - -static const char *const AMDUATD_EDGE_SCHEMA = "tgk/edge"; - -static const char *const AMDUATD_REL_ALIAS = "alias"; -static const char *const AMDUATD_REL_MATERIALIZES = "materializes"; -static const char *const AMDUATD_REL_REPRESENTS = "represents"; -static const char *const AMDUATD_REL_REQUIRES_KEY = "requires_key"; -static const char *const AMDUATD_REL_WITHIN_DOMAIN = "within_domain"; -static const char *const AMDUATD_REL_COMPUTED_BY = "computed_by"; -static const char *const AMDUATD_REL_HAS_PROVENANCE = "has_provenance"; - -static void amduatd_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 bool amduatd_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 void amduatd_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 amduatd_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 amduatd_add_size(size_t *acc, size_t add) { - if (*acc > SIZE_MAX - add) { - return false; - } - *acc += add; - return true; -} - -static bool amduatd_is_ascii(amduat_octets_t bytes) { - if (bytes.len == 0u || bytes.data == NULL) { - return false; - } - for (size_t i = 0u; i < bytes.len; ++i) { - uint8_t c = bytes.data[i]; - if (c < 0x20u || c > 0x7eu) { - return false; - } - } - return true; -} - -static bool amduatd_edge_payload_encode(amduat_reference_t src, - amduat_reference_t dst, - const char *rel, - amduat_octets_t *out_payload) { - amduat_octets_t src_bytes = amduat_octets(NULL, 0u); - amduat_octets_t dst_bytes = amduat_octets(NULL, 0u); - size_t total_len = 0u; - size_t offset = 0u; - uint8_t *buffer = NULL; - uint32_t rel_len = 0u; - - if (out_payload == NULL || rel == NULL) { - return false; - } - out_payload->data = NULL; - out_payload->len = 0u; - - { - size_t rel_len_full = strlen(rel); - if (rel_len_full > UINT32_MAX) { - return false; - } - rel_len = (uint32_t)rel_len_full; - } - if (rel_len == 0u) { - return false; - } - if (!amduatd_is_ascii(amduat_octets((const uint8_t *)rel, rel_len))) { - return false; - } - if (!amduat_enc_asl1_core_encode_reference_v1(src, &src_bytes) || - !amduat_enc_asl1_core_encode_reference_v1(dst, &dst_bytes)) { - free((void *)src_bytes.data); - free((void *)dst_bytes.data); - return false; - } - if (src_bytes.len > UINT32_MAX || dst_bytes.len > UINT32_MAX) { - free((void *)src_bytes.data); - free((void *)dst_bytes.data); - return false; - } - - if (!amduatd_add_size(&total_len, - AMDUATD_EDGE_MAGIC_LEN + 4u + 4u + src_bytes.len) || - !amduatd_add_size(&total_len, 4u + dst_bytes.len) || - !amduatd_add_size(&total_len, 4u + rel_len) || - !amduatd_add_size(&total_len, 1u)) { - free((void *)src_bytes.data); - free((void *)dst_bytes.data); - return false; - } - - buffer = (uint8_t *)malloc(total_len); - if (buffer == NULL) { - free((void *)src_bytes.data); - free((void *)dst_bytes.data); - return false; - } - - memcpy(buffer + offset, k_amduatd_edge_magic, AMDUATD_EDGE_MAGIC_LEN); - offset += AMDUATD_EDGE_MAGIC_LEN; - amduatd_store_u32_le(buffer + offset, AMDUATD_EDGE_VERSION); - offset += 4u; - - amduatd_store_u32_le(buffer + offset, (uint32_t)src_bytes.len); - offset += 4u; - memcpy(buffer + offset, src_bytes.data, src_bytes.len); - offset += src_bytes.len; - - amduatd_store_u32_le(buffer + offset, (uint32_t)dst_bytes.len); - offset += 4u; - memcpy(buffer + offset, dst_bytes.data, dst_bytes.len); - offset += dst_bytes.len; - - amduatd_store_u32_le(buffer + offset, rel_len); - offset += 4u; - memcpy(buffer + offset, rel, rel_len); - offset += rel_len; - - buffer[offset++] = 0u; - - free((void *)src_bytes.data); - free((void *)dst_bytes.data); - - if (offset != total_len) { - free(buffer); - return false; - } - - out_payload->data = buffer; - out_payload->len = total_len; - return true; -} - -static bool amduatd_edge_payload_decode(amduat_octets_t payload, - amduat_reference_t *out_src, - amduat_reference_t *out_dst, - char **out_rel) { - size_t offset = 0u; - uint32_t version = 0u; - uint32_t src_len = 0u; - uint32_t dst_len = 0u; - uint32_t rel_len = 0u; - uint8_t flags = 0u; - amduat_octets_t src_bytes; - amduat_octets_t dst_bytes; - char *rel = NULL; - - if (out_src == NULL || out_dst == NULL || out_rel == NULL) { - return false; - } - *out_src = amduat_reference(0u, amduat_octets(NULL, 0u)); - *out_dst = amduat_reference(0u, amduat_octets(NULL, 0u)); - *out_rel = NULL; - - if (payload.len < AMDUATD_EDGE_MAGIC_LEN + 4u + 1u || - payload.data == NULL) { - return false; - } - if (memcmp(payload.data, k_amduatd_edge_magic, - AMDUATD_EDGE_MAGIC_LEN) != 0) { - return false; - } - offset += AMDUATD_EDGE_MAGIC_LEN; - if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &version) || - version != AMDUATD_EDGE_VERSION) { - return false; - } - if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &src_len)) { - return false; - } - if (payload.len - offset < src_len) { - return false; - } - src_bytes = amduat_octets(payload.data + offset, src_len); - offset += src_len; - if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &dst_len)) { - return false; - } - if (payload.len - offset < dst_len) { - return false; - } - dst_bytes = amduat_octets(payload.data + offset, dst_len); - offset += dst_len; - if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &rel_len)) { - return false; - } - if (payload.len - offset < rel_len + 1u) { - return false; - } - if (!amduatd_is_ascii(amduat_octets(payload.data + offset, rel_len))) { - return false; - } - rel = (char *)malloc(rel_len + 1u); - if (rel == NULL) { - return false; - } - memcpy(rel, payload.data + offset, rel_len); - rel[rel_len] = '\0'; - offset += rel_len; - flags = payload.data[offset++]; - (void)flags; - - if (offset != payload.len) { - free(rel); - return false; - } - if (!amduat_enc_asl1_core_decode_reference_v1(src_bytes, out_src) || - !amduat_enc_asl1_core_decode_reference_v1(dst_bytes, out_dst)) { - amduat_reference_free(out_src); - amduat_reference_free(out_dst); - free(rel); - return false; - } - - *out_rel = rel; - return true; -} - -static const char *amduatd_relation_name_for_ref( - const amduatd_concepts_t *c, - amduat_reference_t ref) { - if (c == NULL) { - return NULL; - } - if (amduat_reference_eq(ref, c->rel_aliases_ref)) { - return AMDUATD_REL_ALIAS; - } - if (amduat_reference_eq(ref, c->rel_materializes_ref)) { - return AMDUATD_REL_MATERIALIZES; - } - if (amduat_reference_eq(ref, c->rel_represents_ref)) { - return AMDUATD_REL_REPRESENTS; - } - if (amduat_reference_eq(ref, c->rel_requires_key_ref)) { - return AMDUATD_REL_REQUIRES_KEY; - } - if (amduat_reference_eq(ref, c->rel_within_domain_ref)) { - return AMDUATD_REL_WITHIN_DOMAIN; - } - if (amduat_reference_eq(ref, c->rel_computed_by_ref)) { - return AMDUATD_REL_COMPUTED_BY; - } - if (amduat_reference_eq(ref, c->rel_has_provenance_ref)) { - return AMDUATD_REL_HAS_PROVENANCE; - } - return NULL; -} - -static bool amduatd_build_collection_head_name(const char *name, - char **out_name) { - size_t name_len; - size_t total_len; - char *buffer; - size_t offset = 0u; - - if (name == NULL || out_name == NULL) { - return false; - } - if (!amduat_asl_pointer_name_is_valid(name)) { - return false; - } - name_len = strlen(name); - total_len = 11u + name_len + 5u + 1u; - buffer = (char *)malloc(total_len); - if (buffer == NULL) { - return false; - } - memcpy(buffer + offset, "collection/", 11u); - offset += 11u; - memcpy(buffer + offset, name, name_len); - offset += name_len; - memcpy(buffer + offset, "/head", 5u); - offset += 5u; - buffer[offset] = '\0'; - *out_name = buffer; - return true; -} - -static bool amduatd_build_collection_log_head_name(const char *name, - char **out_name) { - size_t name_len; - size_t log_name_len; - size_t total_len; - char *buffer; - size_t offset = 0u; - - if (name == NULL || out_name == NULL) { - return false; - } - if (!amduat_asl_pointer_name_is_valid(name)) { - return false; - } - name_len = strlen(name); - log_name_len = 11u + name_len + 4u; - total_len = 4u + log_name_len + 5u + 1u; - buffer = (char *)malloc(total_len); - if (buffer == NULL) { - return false; - } - memcpy(buffer + offset, "log/", 4u); - offset += 4u; - memcpy(buffer + offset, "collection/", 11u); - offset += 11u; - memcpy(buffer + offset, name, name_len); - offset += name_len; - memcpy(buffer + offset, "/log", 4u); - offset += 4u; - memcpy(buffer + offset, "/head", 5u); - offset += 5u; - buffer[offset] = '\0'; - *out_name = buffer; - return true; -} - -static bool amduatd_get_none_ref(amduat_asl_store_t *store, - amduat_reference_t *out_ref) { - amduat_artifact_t none_artifact; - - if (store == NULL || out_ref == NULL) { - return false; - } - if (!amduat_asl_none_artifact(&none_artifact)) { - return false; - } - if (amduat_asl_store_put(store, none_artifact, out_ref) != - AMDUAT_ASL_STORE_OK) { - amduat_artifact_free(&none_artifact); - return false; - } - amduat_artifact_free(&none_artifact); - return true; -} - -static bool amduatd_put_view_params(amduat_asl_store_t *store, - uint64_t from_offset, - uint32_t limit, - amduat_reference_t *out_ref) { - uint8_t payload[12]; - - if (store == NULL || out_ref == NULL) { - return false; - } - payload[0] = (uint8_t)(from_offset & 0xffu); - payload[1] = (uint8_t)((from_offset >> 8) & 0xffu); - payload[2] = (uint8_t)((from_offset >> 16) & 0xffu); - payload[3] = (uint8_t)((from_offset >> 24) & 0xffu); - payload[4] = (uint8_t)((from_offset >> 32) & 0xffu); - payload[5] = (uint8_t)((from_offset >> 40) & 0xffu); - payload[6] = (uint8_t)((from_offset >> 48) & 0xffu); - payload[7] = (uint8_t)((from_offset >> 56) & 0xffu); - payload[8] = (uint8_t)(limit & 0xffu); - payload[9] = (uint8_t)((limit >> 8) & 0xffu); - payload[10] = (uint8_t)((limit >> 16) & 0xffu); - payload[11] = (uint8_t)((limit >> 24) & 0xffu); - - if (amduat_asl_record_store_put( - store, - amduat_octets("pel/params", strlen("pel/params")), - amduat_octets(payload, sizeof(payload)), - out_ref) != AMDUAT_ASL_STORE_OK) { - return false; - } - return true; -} - -static void amduatd_program_free(amduat_pel_program_t *program) { - if (program == NULL || program->nodes == NULL) { - return; - } - for (size_t i = 0u; i < program->nodes_len; ++i) { - free(program->nodes[i].inputs); - } - free(program->nodes); - free(program->roots); - program->nodes = NULL; - program->roots = NULL; - program->nodes_len = 0u; - program->roots_len = 0u; -} - -static bool amduatd_build_collection_view_program( - amduat_pel_program_t *out_program) { - static const char k_op_snapshot[] = "collection.snapshot_decode_v1"; - static const char k_op_read_range[] = "log.read_range_v1"; - static const char k_op_merge[] = "collection.merge_refs_v1"; - amduat_pel_node_t *nodes; - amduat_pel_root_ref_t *roots; - - if (out_program == NULL) { - return false; - } - memset(out_program, 0, sizeof(*out_program)); - - nodes = (amduat_pel_node_t *)calloc(3u, sizeof(*nodes)); - roots = (amduat_pel_root_ref_t *)calloc(1u, sizeof(*roots)); - if (nodes == NULL || roots == NULL) { - free(nodes); - free(roots); - return false; - } - - nodes[0].id = 1u; - nodes[0].op.name = amduat_octets(k_op_snapshot, strlen(k_op_snapshot)); - nodes[0].op.version = 1u; - nodes[0].inputs_len = 1u; - nodes[0].inputs = - (amduat_pel_dag_input_t *)calloc(1u, sizeof(*nodes[0].inputs)); - if (nodes[0].inputs == NULL) { - free(nodes); - free(roots); - return false; - } - nodes[0].inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; - nodes[0].inputs[0].value.external.input_index = 0u; - nodes[0].params = amduat_octets(NULL, 0u); - - nodes[1].id = 2u; - nodes[1].op.name = amduat_octets(k_op_read_range, strlen(k_op_read_range)); - nodes[1].op.version = 1u; - nodes[1].inputs_len = 3u; - nodes[1].inputs = - (amduat_pel_dag_input_t *)calloc(3u, sizeof(*nodes[1].inputs)); - if (nodes[1].inputs == NULL) { - free(nodes[0].inputs); - free(nodes); - free(roots); - return false; - } - nodes[1].inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; - nodes[1].inputs[0].value.external.input_index = 1u; - nodes[1].inputs[1].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; - nodes[1].inputs[1].value.external.input_index = 2u; - nodes[1].inputs[2].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; - nodes[1].inputs[2].value.external.input_index = 3u; - nodes[1].params = amduat_octets(NULL, 0u); - - nodes[2].id = 3u; - nodes[2].op.name = amduat_octets(k_op_merge, strlen(k_op_merge)); - nodes[2].op.version = 1u; - nodes[2].inputs_len = 4u; - nodes[2].inputs = - (amduat_pel_dag_input_t *)calloc(4u, sizeof(*nodes[2].inputs)); - if (nodes[2].inputs == NULL) { - free(nodes[1].inputs); - free(nodes[0].inputs); - free(nodes); - free(roots); - return false; - } - nodes[2].inputs[0].kind = AMDUAT_PEL_DAG_INPUT_NODE; - nodes[2].inputs[0].value.node.node_id = 1u; - nodes[2].inputs[0].value.node.output_index = 0u; - nodes[2].inputs[1].kind = AMDUAT_PEL_DAG_INPUT_NODE; - nodes[2].inputs[1].value.node.node_id = 2u; - nodes[2].inputs[1].value.node.output_index = 0u; - nodes[2].inputs[2].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; - nodes[2].inputs[2].value.external.input_index = 2u; - nodes[2].inputs[3].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; - nodes[2].inputs[3].value.external.input_index = 3u; - nodes[2].params = amduat_octets(NULL, 0u); - - roots[0].node_id = 3u; - roots[0].output_index = 0u; - - out_program->nodes = nodes; - out_program->nodes_len = 3u; - out_program->roots = roots; - out_program->roots_len = 1u; - return true; -} - -static bool amduatd_collection_view(amduat_asl_store_t *store, - const char *root_path, - const char *name, - uint64_t from, - uint32_t limit, - amduat_asl_collection_view_t *out_view) { - amduat_asl_pointer_store_t pointer_store; - amduat_reference_t snapshot_ref; - amduat_reference_t log_head_ref; - amduat_reference_t none_ref; - amduat_reference_t params_ref; - amduat_reference_t program_ref; - amduat_reference_t input_refs[4]; - amduat_pel_program_t program; - amduat_octets_t program_bytes = amduat_octets(NULL, 0u); - amduat_pel_run_result_t run_result; - amduat_artifact_t view_artifact; - amduat_asl_collection_view_t view; - amduat_type_tag_t expected_type_tag; - amduat_asl_encoding_profile_id_t expected_profile; - char *snapshot_name = NULL; - char *log_head_name = NULL; - bool snapshot_exists = false; - bool log_head_exists = false; - bool ok = false; - - if (out_view == NULL) { - return false; - } - memset(out_view, 0, sizeof(*out_view)); - memset(&snapshot_ref, 0, sizeof(snapshot_ref)); - memset(&log_head_ref, 0, sizeof(log_head_ref)); - memset(&none_ref, 0, sizeof(none_ref)); - memset(¶ms_ref, 0, sizeof(params_ref)); - memset(&program_ref, 0, sizeof(program_ref)); - memset(&run_result, 0, sizeof(run_result)); - memset(&view, 0, sizeof(view)); - - if (store == NULL || root_path == NULL || name == NULL) { - return false; - } - if (!amduat_asl_pointer_store_init(&pointer_store, root_path)) { - return false; - } - if (!amduatd_get_none_ref(store, &none_ref)) { - return false; - } - if (!amduatd_build_collection_head_name(name, &snapshot_name) || - !amduatd_build_collection_log_head_name(name, &log_head_name)) { - goto view_cleanup; - } - if (amduat_asl_pointer_get(&pointer_store, snapshot_name, - &snapshot_exists, &snapshot_ref) != - AMDUAT_ASL_POINTER_OK) { - goto view_cleanup; - } - if (!snapshot_exists && - !amduat_reference_clone(none_ref, &snapshot_ref)) { - goto view_cleanup; - } - if (amduat_asl_pointer_get(&pointer_store, log_head_name, - &log_head_exists, &log_head_ref) != - AMDUAT_ASL_POINTER_OK) { - goto view_cleanup; - } - if (!log_head_exists && - !amduat_reference_clone(none_ref, &log_head_ref)) { - goto view_cleanup; - } - if (!amduatd_put_view_params(store, from, limit, ¶ms_ref)) { - goto view_cleanup; - } - if (!amduatd_build_collection_view_program(&program)) { - goto view_cleanup; - } - if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) { - amduatd_program_free(&program); - goto view_cleanup; - } - amduatd_program_free(&program); - - if (!amduat_pel_program_dag_desc_get_program_binding(&expected_type_tag, - &expected_profile)) { - goto view_cleanup; - } - if (expected_profile != AMDUAT_PEL_ENC_PROGRAM_DAG_V1) { - goto view_cleanup; - } - { - amduat_artifact_t program_artifact = - amduat_artifact_with_type(program_bytes, expected_type_tag); - if (amduat_asl_store_put(store, program_artifact, &program_ref) != - AMDUAT_ASL_STORE_OK) { - goto view_cleanup; - } - } - - input_refs[0] = snapshot_ref; - input_refs[1] = log_head_ref; - input_refs[2] = params_ref; - input_refs[3] = params_ref; - - if (!amduat_pel_surf_run_with_result(store, - amduat_pel_program_dag_scheme_ref(), - program_ref, - input_refs, - 4u, - true, - params_ref, - &run_result)) { - goto view_cleanup; - } - if (!run_result.has_result_value || - run_result.result_value.core_result.status != AMDUAT_PEL_EXEC_STATUS_OK || - run_result.output_refs_len != 1u) { - goto view_cleanup; - } - - memset(&view_artifact, 0, sizeof(view_artifact)); - if (amduat_asl_store_get(store, run_result.output_refs[0], - &view_artifact) != AMDUAT_ASL_STORE_OK) { - goto view_cleanup; - } - if (!view_artifact.has_type_tag || - view_artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_COLLECTION_VIEW_1 || - !amduat_asl_collection_view_decode_v1(view_artifact.bytes, &view)) { - amduat_artifact_free(&view_artifact); - goto view_cleanup; - } - amduat_artifact_free(&view_artifact); - - *out_view = view; - memset(&view, 0, sizeof(view)); - ok = true; - -view_cleanup: - amduat_asl_collection_view_free(&view); - if (run_result.has_result_value) { - amduat_enc_pel1_result_free(&run_result.result_value); - } - if (run_result.output_refs != NULL) { - amduat_pel_surf_free_refs(run_result.output_refs, - run_result.output_refs_len); - } - amduat_pel_surf_free_ref(&run_result.result_ref); - amduat_reference_free(&snapshot_ref); - amduat_reference_free(&log_head_ref); - amduat_reference_free(&none_ref); - amduat_reference_free(¶ms_ref); - amduat_reference_free(&program_ref); - free((void *)program_bytes.data); - free(snapshot_name); - free(log_head_name); - return ok; -} - static const char k_amduatd_contract_v1_json[] = "{" "\"contract\":\"AMDUATD/API/1\"," @@ -1095,55 +302,6 @@ bool amduatd_write_all(int fd, const uint8_t *buf, size_t len) { bool amduatd_read_exact(int fd, uint8_t *buf, size_t len); -static bool amduatd_read_file(const char *path, uint8_t **out, size_t *out_len) { - uint8_t *buf = NULL; - size_t cap = 0; - size_t len = 0; - FILE *f; - - if (out != NULL) { - *out = NULL; - } - if (out_len != NULL) { - *out_len = 0; - } - if (path == NULL || out == NULL || out_len == NULL) { - return false; - } - f = fopen(path, "rb"); - if (f == NULL) { - return false; - } - for (;;) { - size_t n; - if (len == cap) { - size_t next_cap = cap != 0 ? cap * 2u : 4096u; - uint8_t *next = (uint8_t *)realloc(buf, next_cap); - if (next == NULL) { - free(buf); - fclose(f); - return false; - } - buf = next; - cap = next_cap; - } - n = fread(buf + len, 1, cap - len, f); - len += n; - if (n == 0) { - if (ferror(f)) { - free(buf); - fclose(f); - return false; - } - break; - } - } - fclose(f); - *out = buf; - *out_len = len; - return true; -} - bool amduatd_read_urandom(uint8_t *out, size_t len) { int fd; if (out == NULL || len == 0) { @@ -1161,1176 +319,6 @@ bool amduatd_read_urandom(uint8_t *out, size_t len) { return true; } -static bool amduatd_build_prefixed_bytes(const char *prefix, - const char *text, - uint8_t **out, - size_t *out_len) { - size_t p_len; - size_t t_len; - size_t len; - uint8_t *buf; - - if (out != NULL) { - *out = NULL; - } - if (out_len != NULL) { - *out_len = 0; - } - if (prefix == NULL || text == NULL || out == NULL || out_len == NULL) { - return false; - } - p_len = strlen(prefix); - t_len = strlen(text); - if (p_len > SIZE_MAX - 1u || t_len > SIZE_MAX - (p_len + 1u)) { - return false; - } - len = p_len + 1u + t_len; - buf = (uint8_t *)malloc(len); - if (buf == NULL) { - return false; - } - memcpy(buf, prefix, p_len); - buf[p_len] = 0; - memcpy(buf + p_len + 1u, text, t_len); - *out = buf; - *out_len = len; - return true; -} - -static bool amduatd_concepts_seed_relation(amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - const amduatd_cfg_t *dcfg, - const char *relation_name, - amduat_reference_t *out_ref) { - uint8_t *bytes = NULL; - size_t bytes_len = 0; - amduat_artifact_t artifact; - char *scoped_name = NULL; - amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); - bool ok = false; - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - - if (out_ref == NULL) { - return false; - } - *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - if (store == NULL || cfg == NULL || relation_name == NULL) { - return false; - } - if (!amduatd_space_scope_name(space, relation_name, &scoped_bytes)) { - return false; - } - scoped_name = (char *)scoped_bytes.data; - if (!amduatd_build_prefixed_bytes("AMDUATD/RELATION/1", scoped_name, &bytes, - &bytes_len)) { - goto seed_cleanup; - } - artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); - if (!amduat_asl_ref_derive(artifact, - cfg->config.encoding_profile_id, - cfg->config.hash_id, - out_ref, - NULL)) { - goto seed_cleanup; - } - (void)amduat_asl_store_put(store, artifact, out_ref); - ok = true; - -seed_cleanup: - free(bytes); - free(scoped_name); - return ok; -} - -static bool amduatd_concepts_ensure_alias(amduatd_concepts_t *c, - amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - const amduatd_cfg_t *dcfg, - const char *alias_name, - amduat_reference_t concept_ref) { - amduat_reference_t existing; - amduat_reference_t name_ref; - amduat_reference_t edge_ref; - char *scoped_name = NULL; - amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); - bool ok = false; - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - - memset(&existing, 0, sizeof(existing)); - memset(&name_ref, 0, sizeof(name_ref)); - memset(&edge_ref, 0, sizeof(edge_ref)); - - if (c == NULL || store == NULL || cfg == NULL || alias_name == NULL) { - return false; - } - if (!amduatd_space_scope_name(space, alias_name, &scoped_bytes)) { - amduat_log(AMDUAT_LOG_ERROR, - "ensure alias: scope failed for %s", alias_name); - return false; - } - scoped_name = (char *)scoped_bytes.data; - if (amduatd_concepts_lookup_alias(store, cfg, c, scoped_name, &existing)) { - amduat_reference_free(&existing); - ok = true; - goto alias_cleanup; - } - if (!amduatd_concepts_put_name_artifact(store, scoped_name, &name_ref)) { - amduat_log(AMDUAT_LOG_ERROR, - "ensure alias: name artifact failed for %s", scoped_name); - goto alias_cleanup; - } - if (!amduatd_concepts_put_edge(store, - c, - name_ref, - concept_ref, - c->rel_aliases_ref, - amduat_octets(NULL, 0u), - &edge_ref)) { - amduat_log(AMDUAT_LOG_ERROR, - "ensure alias: put edge failed for %s", scoped_name); - goto alias_cleanup; - } - ok = true; - -alias_cleanup: - amduat_reference_free(&name_ref); - amduat_reference_free(&edge_ref); - free(scoped_name); - return ok; -} - -static bool amduatd_concepts_load_edges(amduatd_concepts_t *c, - amduat_asl_store_t *store) { - amduat_asl_collection_view_t view; - uint64_t from = 0u; - bool ok = false; - - if (c == NULL || store == NULL) { - return false; - } - - amduatd_edge_list_clear(&c->edges); - - while (true) { - memset(&view, 0, sizeof(view)); - if (!amduatd_collection_view(store, - c->root_path, - c->edge_collection_name, - from, - AMDUATD_EDGE_VIEW_BATCH, - &view)) { - return false; - } - - for (size_t i = 0u; i < view.refs_len; ++i) { - amduat_reference_t record_ref = view.refs[i]; - amduat_asl_record_t record; - amduat_reference_t src_ref; - amduat_reference_t dst_ref; - char *rel = NULL; - amduatd_edge_entry_t entry; - amduat_asl_store_error_t err; - bool schema_ok = false; - - memset(&record, 0, sizeof(record)); - err = amduat_asl_record_store_get(store, record_ref, &record); - if (err != AMDUAT_ASL_STORE_OK) { - continue; - } - if (record.schema.len == strlen(AMDUATD_EDGE_SCHEMA) && - memcmp(record.schema.data, AMDUATD_EDGE_SCHEMA, - record.schema.len) == 0) { - schema_ok = true; - } - if (!schema_ok || - !amduatd_edge_payload_decode(record.payload, - &src_ref, - &dst_ref, - &rel)) { - amduat_asl_record_free(&record); - continue; - } - amduat_asl_record_free(&record); - - memset(&entry, 0, sizeof(entry)); - if (!amduat_reference_clone(record_ref, &entry.record_ref)) { - amduat_reference_free(&src_ref); - amduat_reference_free(&dst_ref); - free(rel); - goto load_cleanup; - } - entry.src_ref = src_ref; - entry.dst_ref = dst_ref; - entry.rel = rel; - if (!amduatd_edge_list_push(&c->edges, &entry)) { - amduatd_edge_entry_free(&entry); - goto load_cleanup; - } - amduatd_edge_entry_free(&entry); - } - - if (view.refs_len == 0u || view.computed_up_to_offset <= from) { - break; - } - from = view.computed_up_to_offset; - amduat_asl_collection_view_free(&view); - } - - ok = true; - -load_cleanup: - amduat_asl_collection_view_free(&view); - return ok; -} - -static bool amduatd_concepts_append_edge_record(amduatd_concepts_t *c, - amduat_asl_store_t *store, - amduat_reference_t src, - amduat_reference_t dst, - const char *rel, - amduat_octets_t actor, - amduat_reference_t *out_ref) { - amduat_octets_t payload = amduat_octets(NULL, 0u); - amduat_reference_t record_ref; - amduatd_edge_entry_t entry; - uint64_t offset = 0u; - - if (out_ref != NULL) { - *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - } - if (c == NULL || store == NULL || rel == NULL) { - return false; - } - if (!amduatd_edge_payload_encode(src, dst, rel, &payload)) { - return false; - } - if (amduat_asl_record_store_put(store, - amduat_octets(AMDUATD_EDGE_SCHEMA, - strlen(AMDUATD_EDGE_SCHEMA)), - payload, - &record_ref) != AMDUAT_ASL_STORE_OK) { - free((void *)payload.data); - return false; - } - free((void *)payload.data); - { - amduat_asl_collection_error_t append_err = - amduat_asl_collection_append(&c->edge_collection, - c->edge_collection_name, - record_ref, - AMDUATD_EDGE_COLLECTION_KIND, - actor, - &offset); - if (append_err != AMDUAT_ASL_COLLECTION_OK) { - if (c->edge_collection_name != NULL && - !amduat_asl_pointer_name_is_valid(c->edge_collection_name)) { - amduat_log(AMDUAT_LOG_ERROR, "edge collection name invalid: %s", - c->edge_collection_name); - } - if (c->edge_collection_name != NULL) { - size_t name_len = strlen(c->edge_collection_name); - size_t total_len = 11u + name_len + 4u + 1u; - char *log_name = (char *)malloc(total_len); - if (log_name != NULL) { - size_t pos = 0u; - memcpy(log_name + pos, "collection/", 11u); - pos += 11u; - memcpy(log_name + pos, c->edge_collection_name, name_len); - pos += name_len; - memcpy(log_name + pos, "/log", 4u); - pos += 4u; - log_name[pos] = '\0'; - if (!amduat_asl_pointer_name_is_valid(log_name)) { - amduat_log(AMDUAT_LOG_ERROR, "edge log name invalid: %s", log_name); - } - { - size_t head_len = 4u + pos + 5u + 1u; - char *head_name = (char *)malloc(head_len); - if (head_name != NULL) { - size_t hpos = 0u; - memcpy(head_name + hpos, "log/", 4u); - hpos += 4u; - memcpy(head_name + hpos, log_name, pos); - hpos += pos; - memcpy(head_name + hpos, "/head", 5u); - hpos += 5u; - head_name[hpos] = '\0'; - if (!amduat_asl_pointer_name_is_valid(head_name)) { - amduat_log(AMDUAT_LOG_ERROR, - "edge log head name invalid: %s", head_name); - } else { - bool head_exists = false; - amduat_reference_t head_ref; - memset(&head_ref, 0, sizeof(head_ref)); - amduat_asl_pointer_error_t ptr_err = - amduat_asl_pointer_get(&c->edge_collection.pointer_store, - head_name, - &head_exists, - &head_ref); - if (ptr_err != AMDUAT_ASL_POINTER_OK) { - amduat_log(AMDUAT_LOG_ERROR, - "edge log head pointer get failed: %d", ptr_err); - } - amduat_reference_free(&head_ref); - } - free(head_name); - } - } - free(log_name); - } - } - amduat_log(AMDUAT_LOG_ERROR, "edge append failed for %s (err=%d)", - c->edge_collection_name != NULL ? c->edge_collection_name - : "?", - (int)append_err); - amduat_reference_free(&record_ref); - return false; - } - } - - memset(&entry, 0, sizeof(entry)); - entry.record_ref = record_ref; - entry.src_ref = src; - entry.dst_ref = dst; - entry.rel = (char *)rel; - if (!amduatd_edge_list_push(&c->edges, &entry)) { - amduat_reference_free(&record_ref); - return false; - } - - if (offset != 0u && (offset % 256u) == 0u) { - amduat_reference_t snapshot_ref; - bool swapped = false; - memset(&snapshot_ref, 0, sizeof(snapshot_ref)); - (void)amduat_asl_collection_snapshot(&c->edge_collection, - c->edge_collection_name, - offset, - &snapshot_ref, - &swapped); - amduat_reference_free(&snapshot_ref); - } - - if (out_ref != NULL) { - *out_ref = record_ref; - } else { - amduat_reference_free(&record_ref); - } - return true; -} - -static bool amduatd_concepts_migrate_edges(amduatd_concepts_t *c, - amduat_asl_store_t *store) { - uint8_t *file_bytes = NULL; - size_t file_len = 0; - bool ok = true; - - if (c == NULL || store == NULL) { - return false; - } - if (!amduatd_read_file(c->edges_path, &file_bytes, &file_len)) { - return true; - } - - { - char *text = (char *)file_bytes; - char *line = text; - char *end = text + file_len; - while (line < end) { - char *nl = memchr(line, '\n', (size_t)(end - line)); - size_t n = nl != NULL ? (size_t)(nl - line) : (size_t)(end - line); - char *tmp; - amduat_reference_t ref; - bool ok_ref; - - while (n != 0 && (line[n - 1] == '\r' || line[n - 1] == ' ' || - line[n - 1] == '\t')) { - n--; - } - while (n != 0 && (*line == ' ' || *line == '\t')) { - line++; - n--; - } - if (n != 0) { - tmp = (char *)malloc(n + 1u); - if (tmp == NULL) { - ok = false; - break; - } - memcpy(tmp, line, n); - tmp[n] = '\0'; - memset(&ref, 0, sizeof(ref)); - ok_ref = amduat_asl_ref_decode_hex(tmp, &ref); - free(tmp); - if (ok_ref) { - amduat_artifact_t artifact; - amduat_tgk_edge_body_t edge; - const char *rel_name = NULL; - memset(&artifact, 0, sizeof(artifact)); - if (amduat_asl_store_get(store, ref, &artifact) == - AMDUAT_ASL_STORE_OK && - artifact.has_type_tag && - artifact.type_tag.tag_id == AMDUAT_TYPE_TAG_TGK1_EDGE_V1) { - memset(&edge, 0, sizeof(edge)); - if (amduat_enc_tgk1_edge_decode_v1(artifact.bytes, &edge)) { - if (edge.from_len == 1 && edge.to_len == 1) { - rel_name = amduatd_relation_name_for_ref(c, edge.payload); - } - if (rel_name != NULL) { - if (!amduatd_concepts_append_edge_record(c, - store, - edge.from[0], - edge.to[0], - rel_name, - amduat_octets(NULL, 0u), - NULL)) { - ok = false; - } - } - amduat_enc_tgk1_edge_free(&edge); - } - amduat_asl_artifact_free(&artifact); - } else if (artifact.bytes.data != NULL) { - amduat_asl_artifact_free(&artifact); - } - amduat_reference_free(&ref); - if (!ok) { - break; - } - } - } - if (nl == NULL) { - break; - } - line = nl + 1; - } - } - - free(file_bytes); - - if (ok) { - char migrated_path[1100]; - (void)snprintf(migrated_path, - sizeof(migrated_path), - "%s.migrated", - c->edges_path); - (void)rename(c->edges_path, migrated_path); - } - - return ok; -} - -static bool amduatd_concepts_migrate_unscoped_edges( - amduatd_concepts_t *c, - amduat_asl_store_t *store, - const char *unscoped_collection) { - amduat_asl_collection_view_t view; - uint64_t from = 0u; - bool ok = false; - - if (c == NULL || store == NULL || unscoped_collection == NULL) { - return false; - } - - while (true) { - memset(&view, 0, sizeof(view)); - if (!amduatd_collection_view(store, - c->root_path, - unscoped_collection, - from, - AMDUATD_EDGE_VIEW_BATCH, - &view)) { - return false; - } - - for (size_t i = 0u; i < view.refs_len; ++i) { - uint64_t offset = 0u; - if (amduat_asl_collection_append(&c->edge_collection, - c->edge_collection_name, - view.refs[i], - AMDUATD_EDGE_COLLECTION_KIND, - amduat_octets(NULL, 0u), - &offset) != AMDUAT_ASL_COLLECTION_OK) { - goto migrate_cleanup; - } - if (offset != 0u && (offset % 256u) == 0u) { - amduat_reference_t snapshot_ref; - bool swapped = false; - memset(&snapshot_ref, 0, sizeof(snapshot_ref)); - (void)amduat_asl_collection_snapshot(&c->edge_collection, - c->edge_collection_name, - offset, - &snapshot_ref, - &swapped); - amduat_reference_free(&snapshot_ref); - } - } - - if (view.refs_len == 0u || view.computed_up_to_offset <= from) { - break; - } - from = view.computed_up_to_offset; - amduat_asl_collection_view_free(&view); - } - - ok = true; - -migrate_cleanup: - amduat_asl_collection_view_free(&view); - return ok; -} - -static bool amduatd_concepts_init(amduatd_concepts_t *c, - amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - const amduatd_cfg_t *dcfg, - const char *root_path) { - amduat_octets_t scoped_collection = amduat_octets(NULL, 0u); - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - - if (c == NULL || store == NULL || cfg == NULL || root_path == NULL) { - return false; - } - memset(c, 0, sizeof(*c)); - c->root_path = root_path; - (void)snprintf(c->edges_path, sizeof(c->edges_path), "%s/%s", root_path, - AMDUATD_EDGES_FILE); - if (!amduatd_space_edges_collection_name(space, &scoped_collection)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: scope edges failed"); - return false; - } - c->edge_collection_name = (char *)scoped_collection.data; - if (c->edge_collection_name == NULL) { - return false; - } - if (!amduat_asl_collection_store_init(&c->edge_collection, root_path, - store)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: collection store init failed"); - return false; - } - - if (!amduatd_concepts_seed_relation(store, cfg, dcfg, "aliases", - &c->rel_aliases_ref)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed aliases failed"); - return false; - } - if (!amduatd_concepts_ensure_alias(c, store, cfg, dcfg, "ms.aliases", - c->rel_aliases_ref)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: ensure ms.aliases failed"); - return false; - } - if (!amduatd_concepts_seed_relation(store, cfg, dcfg, "materializesAs", - &c->rel_materializes_ref)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed materializesAs failed"); - return false; - } - if (!amduatd_concepts_ensure_alias(c, store, cfg, dcfg, "ms.materializes_as", - c->rel_materializes_ref)) { - amduat_log(AMDUAT_LOG_ERROR, - "concepts init: ensure ms.materializes_as failed"); - return false; - } - if (!amduatd_concepts_seed_relation(store, cfg, dcfg, "represents", - &c->rel_represents_ref)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed represents failed"); - return false; - } - if (!amduatd_concepts_ensure_alias(c, store, cfg, dcfg, "ms.represents", - c->rel_represents_ref)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: ensure ms.represents failed"); - return false; - } - if (!amduatd_concepts_seed_relation(store, cfg, dcfg, "requiresKey", - &c->rel_requires_key_ref)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed requiresKey failed"); - return false; - } - if (!amduatd_concepts_ensure_alias(c, store, cfg, dcfg, "ms.requires_key", - c->rel_requires_key_ref)) { - amduat_log(AMDUAT_LOG_ERROR, - "concepts init: ensure ms.requires_key failed"); - return false; - } - if (!amduatd_concepts_seed_relation(store, cfg, dcfg, "withinDomain", - &c->rel_within_domain_ref)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed withinDomain failed"); - return false; - } - if (!amduatd_concepts_ensure_alias(c, store, cfg, dcfg, "ms.within_domain", - c->rel_within_domain_ref)) { - amduat_log(AMDUAT_LOG_ERROR, - "concepts init: ensure ms.within_domain failed"); - return false; - } - if (!amduatd_concepts_seed_relation(store, cfg, dcfg, "computedBy", - &c->rel_computed_by_ref)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed computedBy failed"); - return false; - } - if (!amduatd_concepts_ensure_alias(c, store, cfg, dcfg, "ms.computed_by", - c->rel_computed_by_ref)) { - amduat_log(AMDUAT_LOG_ERROR, - "concepts init: ensure ms.computed_by failed"); - return false; - } - if (!amduatd_concepts_seed_relation(store, cfg, dcfg, "hasProvenance", - &c->rel_has_provenance_ref)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed hasProvenance failed"); - return false; - } - if (!amduatd_concepts_ensure_alias(c, store, cfg, dcfg, "ms.has_provenance", - c->rel_has_provenance_ref)) { - amduat_log(AMDUAT_LOG_ERROR, - "concepts init: ensure ms.has_provenance failed"); - return false; - } - if (!amduatd_concepts_load_edges(c, store)) { - amduat_log(AMDUAT_LOG_ERROR, "concepts init: load edges failed"); - return false; - } - if (c->edges.len == 0u) { - if (space != NULL && space->enabled) { - if (amduatd_space_should_migrate_unscoped_edges(space)) { - if (!amduatd_concepts_migrate_unscoped_edges( - c, - store, - AMDUATD_EDGE_COLLECTION)) { - return false; - } - if (!amduatd_concepts_load_edges(c, store)) { - return false; - } - } - } else { - if (!amduatd_concepts_migrate_edges(c, store)) { - return false; - } - if (!amduatd_concepts_load_edges(c, store)) { - return false; - } - } - } - - return true; -} - -static bool amduatd_concepts_derive_name_ref( - const amduat_asl_store_fs_config_t *cfg, - const char *name, - amduat_reference_t *out_ref) { - uint8_t *bytes = NULL; - size_t bytes_len = 0; - amduat_artifact_t artifact; - - if (out_ref == NULL) { - return false; - } - *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - if (cfg == NULL || name == NULL) { - return false; - } - if (!amduatd_build_prefixed_bytes("AMDUATD/NAME/1", name, &bytes, - &bytes_len)) { - return false; - } - artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); - if (!amduat_asl_ref_derive(artifact, - cfg->config.encoding_profile_id, - cfg->config.hash_id, - out_ref, - NULL)) { - free(bytes); - return false; - } - free(bytes); - return true; -} - -static bool amduatd_concepts_put_name_artifact(amduat_asl_store_t *store, - const char *name, - amduat_reference_t *out_ref) { - uint8_t *bytes = NULL; - size_t bytes_len = 0; - amduat_artifact_t artifact; - - if (out_ref != NULL) { - *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - } - if (store == NULL || name == NULL || out_ref == NULL) { - return false; - } - if (!amduatd_build_prefixed_bytes("AMDUATD/NAME/1", name, &bytes, - &bytes_len)) { - return false; - } - artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); - if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) { - free(bytes); - return false; - } - free(bytes); - return true; -} - -static bool amduatd_concepts_put_concept_id(amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - amduat_reference_t *out_ref) { - uint8_t rnd[16]; - uint8_t *bytes = NULL; - size_t bytes_len = 0; - amduat_artifact_t artifact; - - if (out_ref != NULL) { - *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - } - if (store == NULL || cfg == NULL || out_ref == NULL) { - return false; - } - if (!amduatd_read_urandom(rnd, sizeof(rnd))) { - return false; - } - bytes_len = strlen("AMDUATD/CONCEPT-ID/1") + 1u + sizeof(rnd); - bytes = (uint8_t *)malloc(bytes_len); - if (bytes == NULL) { - return false; - } - memcpy(bytes, "AMDUATD/CONCEPT-ID/1", strlen("AMDUATD/CONCEPT-ID/1")); - bytes[strlen("AMDUATD/CONCEPT-ID/1")] = 0; - memcpy(bytes + strlen("AMDUATD/CONCEPT-ID/1") + 1u, rnd, sizeof(rnd)); - artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); - if (!amduat_asl_ref_derive(artifact, - cfg->config.encoding_profile_id, - cfg->config.hash_id, - out_ref, - NULL)) { - free(bytes); - return false; - } - if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) { - free(bytes); - return false; - } - free(bytes); - return true; -} - -static bool amduatd_concepts_put_edge(amduat_asl_store_t *store, - amduatd_concepts_t *c, - amduat_reference_t from, - amduat_reference_t to, - amduat_reference_t relation_concept_ref, - amduat_octets_t actor, - amduat_reference_t *out_edge_ref) { - const char *rel_name = NULL; - - if (out_edge_ref != NULL) { - *out_edge_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - } - if (store == NULL || c == NULL || out_edge_ref == NULL) { - return false; - } - rel_name = amduatd_relation_name_for_ref(c, relation_concept_ref); - if (rel_name == NULL) { - return false; - } - return amduatd_concepts_append_edge_record(c, - store, - from, - to, - rel_name, - actor, - out_edge_ref); -} - -static bool amduatd_concepts_lookup_alias(amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - const amduatd_concepts_t *c, - const char *name, - amduat_reference_t *out_concept_ref) { - amduat_reference_t name_ref; - size_t i; - - if (out_concept_ref != NULL) { - *out_concept_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - } - if (store == NULL || cfg == NULL || c == NULL || name == NULL || - out_concept_ref == NULL) { - return false; - } - if (!amduatd_concepts_derive_name_ref(cfg, name, &name_ref)) { - return false; - } - - for (i = c->edges.len; i > 0; --i) { - const amduatd_edge_entry_t *entry = &c->edges.items[i - 1u]; - if (entry->rel == NULL) { - continue; - } - if (strcmp(entry->rel, AMDUATD_REL_ALIAS) != 0) { - continue; - } - if (amduat_reference_eq(entry->src_ref, name_ref)) { - amduat_reference_clone(entry->dst_ref, out_concept_ref); - amduat_reference_free(&name_ref); - return true; - } - } - - amduat_reference_free(&name_ref); - return false; -} - -static bool amduatd_concepts_resolve_latest(amduat_asl_store_t *store, - const amduatd_concepts_t *c, - amduat_reference_t concept_ref, - amduat_reference_t *out_ref) { - size_t i; - - if (out_ref != NULL) { - *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - } - if (store == NULL || c == NULL || out_ref == NULL) { - return false; - } - - for (i = c->edges.len; i > 0; --i) { - const amduatd_edge_entry_t *entry = &c->edges.items[i - 1u]; - if (entry->rel == NULL) { - continue; - } - if (strcmp(entry->rel, AMDUATD_REL_MATERIALIZES) != 0) { - continue; - } - if (amduat_reference_eq(entry->src_ref, concept_ref)) { - amduat_reference_clone(entry->dst_ref, out_ref); - return true; - } - } - - return false; -} - -static bool amduatd_parse_name_artifact(amduat_artifact_t artifact, - const amduatd_cfg_t *dcfg, - char *out, - size_t cap) { - const uint8_t *bytes; - size_t len; - const char *prefix = "AMDUATD/NAME/1"; - size_t prefix_len; - size_t i; - size_t name_len; - char scoped[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; - amduat_octets_t unscoped = amduat_octets(NULL, 0u); - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - - if (out != NULL && cap != 0) { - out[0] = '\0'; - } - if (out == NULL || cap == 0) { - return false; - } - if (artifact.bytes.len != 0 && artifact.bytes.data == NULL) { - return false; - } - bytes = artifact.bytes.data; - len = artifact.bytes.len; - prefix_len = strlen(prefix); - if (len < prefix_len + 1u) { - return false; - } - if (memcmp(bytes, prefix, prefix_len) != 0 || bytes[prefix_len] != 0) { - return false; - } - for (i = prefix_len + 1u; i < len; ++i) { - if (bytes[i] == 0) { - return false; - } - } - name_len = len - (prefix_len + 1u); - if (name_len == 0 || name_len >= sizeof(scoped)) { - return false; - } - memcpy(scoped, bytes + prefix_len + 1u, name_len); - scoped[name_len] = '\0'; - if (!amduatd_space_unscoped_name(space, scoped, &unscoped)) { - return false; - } - if (unscoped.len >= cap) { - free((void *)unscoped.data); - return false; - } - if (unscoped.len != 0u) { - memcpy(out, unscoped.data, unscoped.len); - } - out[unscoped.len] = '\0'; - free((void *)unscoped.data); - return true; -} - -static bool amduatd_handle_get_concepts(int fd, - amduat_asl_store_t *store, - const amduatd_concepts_t *concepts, - const amduatd_cfg_t *dcfg) { - amduatd_strbuf_t b; - bool first = true; - size_t i; - - if (store == NULL || concepts == NULL) { - return amduatd_send_json_error(fd, 500, "Internal Server Error", - "internal error"); - } - memset(&b, 0, sizeof(b)); - if (!amduatd_strbuf_append_cstr(&b, "{\"concepts\":[")) { - amduatd_strbuf_free(&b); - return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); - } - - for (i = 0; i < concepts->edges.len; ++i) { - const amduatd_edge_entry_t *entry = &concepts->edges.items[i]; - amduat_artifact_t artifact; - amduat_asl_store_error_t err; - char name[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; - char *concept_hex = NULL; - - if (entry->rel == NULL || - strcmp(entry->rel, AMDUATD_REL_ALIAS) != 0) { - continue; - } - - memset(&artifact, 0, sizeof(artifact)); - err = amduat_asl_store_get(store, entry->src_ref, &artifact); - if (err != AMDUAT_ASL_STORE_OK || - !amduatd_parse_name_artifact(artifact, dcfg, name, sizeof(name))) { - amduat_asl_artifact_free(&artifact); - continue; - } - amduat_asl_artifact_free(&artifact); - - if (!amduat_asl_ref_encode_hex(entry->dst_ref, &concept_hex)) { - continue; - } - - if (!first) { - (void)amduatd_strbuf_append_char(&b, ','); - } - first = false; - - (void)amduatd_strbuf_append_cstr(&b, "{\"name\":\""); - (void)amduatd_strbuf_append_cstr(&b, name); - (void)amduatd_strbuf_append_cstr(&b, "\",\"concept_ref\":\""); - (void)amduatd_strbuf_append_cstr(&b, concept_hex); - (void)amduatd_strbuf_append_cstr(&b, "\"}"); - - free(concept_hex); - } - - if (!amduatd_strbuf_append_cstr(&b, "]}\n")) { - amduatd_strbuf_free(&b); - return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); - } - { - bool ok = amduatd_http_send_json(fd, 200, "OK", b.data, false); - amduatd_strbuf_free(&b); - return ok; - } -} - -static bool amduatd_append_relation_entry(amduatd_strbuf_t *b, - bool *first, - const char *name, - amduat_reference_t ref) { - char *hex = NULL; - if (b == NULL || first == NULL || name == NULL) { - return false; - } - if (!amduat_asl_ref_encode_hex(ref, &hex)) { - return false; - } - if (!*first) { - (void)amduatd_strbuf_append_char(b, ','); - } - *first = false; - (void)amduatd_strbuf_append_cstr(b, "{\"name\":\""); - (void)amduatd_strbuf_append_cstr(b, name); - (void)amduatd_strbuf_append_cstr(b, "\",\"concept_ref\":\""); - (void)amduatd_strbuf_append_cstr(b, hex); - (void)amduatd_strbuf_append_cstr(b, "\"}"); - free(hex); - return true; -} - -static bool amduatd_handle_get_relations(int fd, - const amduatd_concepts_t *concepts) { - amduatd_strbuf_t b; - bool first = true; - - if (concepts == NULL) { - return amduatd_send_json_error(fd, 500, "Internal Server Error", - "internal error"); - } - memset(&b, 0, sizeof(b)); - if (!amduatd_strbuf_append_cstr(&b, "{\"relations\":[")) { - amduatd_strbuf_free(&b); - return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); - } - if (!amduatd_append_relation_entry(&b, &first, "ms.aliases", - concepts->rel_aliases_ref) || - !amduatd_append_relation_entry(&b, &first, "ms.materializes_as", - concepts->rel_materializes_ref) || - !amduatd_append_relation_entry(&b, &first, "ms.represents", - concepts->rel_represents_ref) || - !amduatd_append_relation_entry(&b, &first, "ms.requires_key", - concepts->rel_requires_key_ref) || - !amduatd_append_relation_entry(&b, &first, "ms.within_domain", - concepts->rel_within_domain_ref) || - !amduatd_append_relation_entry(&b, &first, "ms.computed_by", - concepts->rel_computed_by_ref) || - !amduatd_append_relation_entry(&b, &first, "ms.has_provenance", - concepts->rel_has_provenance_ref)) { - amduatd_strbuf_free(&b); - return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); - } - (void)amduatd_strbuf_append_cstr(&b, "]}\n"); - - { - bool ok = amduatd_http_send_json(fd, 200, "OK", b.data, false); - amduatd_strbuf_free(&b); - return ok; - } -} - -static bool amduatd_handle_get_concept(int fd, - amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - const amduatd_concepts_t *concepts, - const amduatd_cfg_t *dcfg, - const char *name) { - amduat_reference_t concept_ref; - amduat_reference_t latest_ref; - amduatd_strbuf_t b; - char *concept_hex = NULL; - char *latest_hex = NULL; - char *scoped_name = NULL; - amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - bool have_latest = false; - size_t i; - size_t version_count = 0; - - memset(&concept_ref, 0, sizeof(concept_ref)); - memset(&latest_ref, 0, sizeof(latest_ref)); - memset(&b, 0, sizeof(b)); - - if (store == NULL || cfg == NULL || concepts == NULL || name == NULL) { - return amduatd_send_json_error(fd, 500, "Internal Server Error", - "internal error"); - } - if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { - return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); - } - scoped_name = (char *)scoped_bytes.data; - amduatd_space_log_mapping(space, name, scoped_name); - if (!amduatd_concepts_lookup_alias(store, cfg, concepts, scoped_name, - &concept_ref)) { - free(scoped_name); - return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept"); - } - free(scoped_name); - if (amduatd_concepts_resolve_latest(store, concepts, concept_ref, - &latest_ref)) { - have_latest = true; - } - - if (!amduat_asl_ref_encode_hex(concept_ref, &concept_hex)) { - amduat_reference_free(&concept_ref); - amduat_reference_free(&latest_ref); - return amduatd_send_json_error(fd, 500, "Internal Server Error", - "encode error"); - } - if (have_latest) { - if (!amduat_asl_ref_encode_hex(latest_ref, &latest_hex)) { - free(concept_hex); - amduat_reference_free(&concept_ref); - amduat_reference_free(&latest_ref); - return amduatd_send_json_error(fd, 500, "Internal Server Error", - "encode error"); - } - } - - if (!amduatd_strbuf_append_cstr(&b, "{")) { - free(concept_hex); - free(latest_hex); - amduat_reference_free(&concept_ref); - amduat_reference_free(&latest_ref); - return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); - } - (void)amduatd_strbuf_append_cstr(&b, "\"name\":\""); - (void)amduatd_strbuf_append_cstr(&b, name); - (void)amduatd_strbuf_append_cstr(&b, "\",\"concept_ref\":\""); - (void)amduatd_strbuf_append_cstr(&b, concept_hex); - (void)amduatd_strbuf_append_cstr(&b, "\",\"latest_ref\":"); - if (latest_hex != NULL) { - (void)amduatd_strbuf_append_cstr(&b, "\""); - (void)amduatd_strbuf_append_cstr(&b, latest_hex); - (void)amduatd_strbuf_append_cstr(&b, "\""); - } else { - (void)amduatd_strbuf_append_cstr(&b, "null"); - } - (void)amduatd_strbuf_append_cstr(&b, ",\"versions\":["); - - for (i = 0; i < concepts->edges.len; ++i) { - const amduatd_edge_entry_t *entry = &concepts->edges.items[i]; - char *edge_hex = NULL; - char *ref_hex = NULL; - - if (entry->rel == NULL || - strcmp(entry->rel, AMDUATD_REL_MATERIALIZES) != 0) { - continue; - } - if (!amduat_reference_eq(entry->src_ref, concept_ref)) { - continue; - } - - if (!amduat_asl_ref_encode_hex(entry->record_ref, &edge_hex) || - !amduat_asl_ref_encode_hex(entry->dst_ref, &ref_hex)) { - free(edge_hex); - free(ref_hex); - continue; - } - - if (version_count != 0) { - (void)amduatd_strbuf_append_char(&b, ','); - } - version_count++; - - (void)amduatd_strbuf_append_cstr(&b, "{\"edge_ref\":\""); - (void)amduatd_strbuf_append_cstr(&b, edge_hex); - (void)amduatd_strbuf_append_cstr(&b, "\",\"ref\":\""); - (void)amduatd_strbuf_append_cstr(&b, ref_hex); - (void)amduatd_strbuf_append_cstr(&b, "\"}"); - - free(edge_hex); - free(ref_hex); - - if (version_count >= 64u) { - break; - } - } - - (void)amduatd_strbuf_append_cstr(&b, "]}\n"); - - free(concept_hex); - free(latest_hex); - amduat_reference_free(&concept_ref); - amduat_reference_free(&latest_ref); - - { - bool ok = amduatd_http_send_json(fd, 200, "OK", b.data, false); - amduatd_strbuf_free(&b); - return ok; - } -} - bool amduatd_read_exact(int fd, uint8_t *buf, size_t len) { size_t off = 0; while (off < len) { @@ -2757,28 +745,6 @@ const char *amduatd_query_param(const char *path, return NULL; } -static bool amduatd_bytes_contains(const uint8_t *data, - size_t len, - const char *needle) { - size_t i; - size_t nlen; - - if (data == NULL || needle == NULL) { - return false; - } - nlen = strlen(needle); - if (nlen == 0 || nlen > len) { - return false; - } - for (i = 0; i + nlen <= len; ++i) { - if (memcmp(data + i, needle, nlen) == 0) { - return true; - } - } - return false; -} - - static void amduatd_strbuf_free(amduatd_strbuf_t *b) { if (b == NULL) { return; @@ -3298,62 +1264,6 @@ static bool amduatd_decode_ref_hex_str(const char *s, return ok; } -amduatd_ref_status_t amduatd_decode_ref_or_name_latest( - amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - const amduatd_concepts_t *concepts, - const amduatd_cfg_t *dcfg, - const char *s, - size_t len, - amduat_reference_t *out_ref) { - amduat_reference_t concept_ref; - char *tmp = NULL; - char *scoped_name = NULL; - amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - - if (out_ref != NULL) { - memset(out_ref, 0, sizeof(*out_ref)); - } - if (store == NULL || cfg == NULL || concepts == NULL || s == NULL || - out_ref == NULL) { - return AMDUATD_REF_ERR_INTERNAL; - } - - if (amduatd_decode_ref_hex_str(s, len, out_ref)) { - return AMDUATD_REF_OK; - } - - if (!amduatd_copy_json_str(s, len, &tmp)) { - return AMDUATD_REF_ERR_INTERNAL; - } - if (!amduatd_space_scope_name(space, tmp, &scoped_bytes)) { - free(tmp); - return AMDUATD_REF_ERR_INVALID; - } - scoped_name = (char *)scoped_bytes.data; - amduatd_space_log_mapping(space, tmp, scoped_name); - memset(&concept_ref, 0, sizeof(concept_ref)); - if (!amduatd_concepts_lookup_alias(store, - cfg, - concepts, - scoped_name, - &concept_ref)) { - free(scoped_name); - free(tmp); - return AMDUATD_REF_ERR_NOT_FOUND; - } - free(scoped_name); - free(tmp); - - if (!amduatd_concepts_resolve_latest(store, concepts, concept_ref, out_ref)) { - amduat_reference_free(&concept_ref); - return AMDUATD_REF_ERR_NOT_FOUND; - } - amduat_reference_free(&concept_ref); - return AMDUATD_REF_OK; -} - bool amduatd_send_json_error(int fd, int code, const char *reason, @@ -3585,642 +1495,6 @@ static bool amduatd_seed_api_contract(amduat_asl_store_t *store, return true; } -typedef struct { - char *key_hex; - char *type_hex; - char *key_text; - char *value_hex; -} amduatd_seed_entry_t; - -static int amduatd_seed_entry_cmp(const void *a, const void *b) { - const amduatd_seed_entry_t *x = (const amduatd_seed_entry_t *)a; - const amduatd_seed_entry_t *y = (const amduatd_seed_entry_t *)b; - if (x == NULL || y == NULL) { - return 0; - } - return strcmp(x->key_hex != NULL ? x->key_hex : "", - y->key_hex != NULL ? y->key_hex : ""); -} - -static bool amduatd_seed_concept_if_missing( - amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - amduatd_concepts_t *concepts, - const amduatd_cfg_t *dcfg, - const char *name, - amduat_reference_t *out_concept_ref) { - amduat_reference_t name_ref; - amduat_reference_t concept_ref; - amduat_reference_t edge_ref; - char *scoped_name = NULL; - amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - bool ok = false; - - if (out_concept_ref != NULL) { - *out_concept_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - } - if (store == NULL || cfg == NULL || concepts == NULL || name == NULL || - out_concept_ref == NULL) { - return false; - } - if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { - return false; - } - scoped_name = (char *)scoped_bytes.data; - if (amduatd_concepts_lookup_alias(store, cfg, concepts, scoped_name, - out_concept_ref)) { - ok = true; - goto seed_cleanup; - } - - memset(&name_ref, 0, sizeof(name_ref)); - memset(&concept_ref, 0, sizeof(concept_ref)); - memset(&edge_ref, 0, sizeof(edge_ref)); - - if (!amduatd_concepts_put_name_artifact(store, scoped_name, &name_ref)) { - goto seed_cleanup; - } - if (!amduatd_concepts_put_concept_id(store, cfg, &concept_ref)) { - goto seed_cleanup; - } - if (!amduatd_concepts_put_edge(store, - concepts, - name_ref, - concept_ref, - concepts->rel_aliases_ref, - amduat_octets(NULL, 0u), - &edge_ref)) { - goto seed_cleanup; - } - *out_concept_ref = concept_ref; - ok = true; - goto seed_cleanup; - -seed_cleanup: - if (!ok) { - amduat_reference_free(&concept_ref); - } - amduat_reference_free(&name_ref); - amduat_reference_free(&edge_ref); - free(scoped_name); - return ok; -} - -static bool amduatd_seed_store_artifact(amduat_asl_store_t *store, - amduat_artifact_t artifact, - amduat_reference_t *out_ref) { - if (out_ref != NULL) { - *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - } - if (store == NULL || out_ref == NULL) { - return false; - } - if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) { - return false; - } - return true; -} - -static bool amduatd_seed_pel_identity_program( - amduat_asl_store_t *store, - amduat_reference_t *out_program_ref) { - amduat_pel_program_t program; - amduat_pel_node_t node; - amduat_pel_dag_input_t inputs[1]; - amduat_pel_root_ref_t root; - amduat_octets_t program_bytes; - amduat_artifact_t artifact; - - if (out_program_ref != NULL) { - *out_program_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); - } - if (store == NULL || out_program_ref == NULL) { - return false; - } - - memset(&program, 0, sizeof(program)); - memset(&node, 0, sizeof(node)); - memset(inputs, 0, sizeof(inputs)); - memset(&root, 0, sizeof(root)); - program_bytes = amduat_octets(NULL, 0u); - - inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; - inputs[0].value.external.input_index = 0; - - node.id = 1; - node.op.name = amduat_octets("pel.bytes.concat", - strlen("pel.bytes.concat")); - node.op.version = 1; - node.inputs = inputs; - node.inputs_len = 1; - node.params = amduat_octets(NULL, 0u); - - root.node_id = 1; - root.output_index = 0; - - program.nodes = &node; - program.nodes_len = 1; - program.roots = &root; - program.roots_len = 1; - - if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) { - return false; - } - artifact = amduat_artifact_with_type(program_bytes, - amduat_type_tag(0x00000101u)); - if (!amduatd_seed_store_artifact(store, artifact, out_program_ref)) { - free((void *)program_bytes.data); - return false; - } - free((void *)program_bytes.data); - return true; -} - -static bool amduatd_seed_materializes_if_missing( - amduat_asl_store_t *store, - amduatd_concepts_t *concepts, - amduat_reference_t concept_ref, - amduat_reference_t target_ref) { - amduat_reference_t latest; - amduat_reference_t edge_ref; - - memset(&latest, 0, sizeof(latest)); - if (amduatd_concepts_resolve_latest(store, concepts, concept_ref, &latest)) { - amduat_reference_free(&latest); - return true; - } - if (!amduatd_concepts_put_edge(store, - concepts, - concept_ref, - target_ref, - concepts->rel_materializes_ref, - amduat_octets(NULL, 0u), - &edge_ref)) { - return false; - } - amduat_reference_free(&edge_ref); - return true; -} - -static bool amduatd_seed_ms_ui_state(amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - amduatd_concepts_t *concepts, - const amduatd_cfg_t *dcfg) { - amduat_reference_t type_string_ref; - amduat_reference_t type_ref_ref; - amduat_reference_t key_title_ref; - amduat_reference_t key_status_ref; - amduat_reference_t key_latest_ref; - amduat_reference_t schema_concept_ref; - amduat_reference_t registry_concept_ref; - amduat_reference_t identity_program_ref; - amduat_reference_t schema_input_ref; - amduat_reference_t registry_input_ref; - amduat_reference_t schema_output_ref; - amduat_reference_t registry_output_ref; - amduat_reference_t seed_manifest_ref; - amduat_reference_t seed_environment_ref; - amduat_reference_t seed_executor_ref; - amduat_reference_t receipt_ref; - amduat_pel_run_result_t run_result; - amduatd_seed_entry_t fields[3]; - amduatd_seed_entry_t entries[3]; - amduatd_strbuf_t b; - amduat_artifact_t artifact; - char *seed_latest_hex = NULL; - size_t i; - bool ok = false; - - memset(&type_string_ref, 0, sizeof(type_string_ref)); - memset(&type_ref_ref, 0, sizeof(type_ref_ref)); - memset(&key_title_ref, 0, sizeof(key_title_ref)); - memset(&key_status_ref, 0, sizeof(key_status_ref)); - memset(&key_latest_ref, 0, sizeof(key_latest_ref)); - memset(&schema_concept_ref, 0, sizeof(schema_concept_ref)); - memset(®istry_concept_ref, 0, sizeof(registry_concept_ref)); - memset(&identity_program_ref, 0, sizeof(identity_program_ref)); - memset(&schema_input_ref, 0, sizeof(schema_input_ref)); - memset(®istry_input_ref, 0, sizeof(registry_input_ref)); - memset(&schema_output_ref, 0, sizeof(schema_output_ref)); - memset(®istry_output_ref, 0, sizeof(registry_output_ref)); - memset(&seed_manifest_ref, 0, sizeof(seed_manifest_ref)); - memset(&seed_environment_ref, 0, sizeof(seed_environment_ref)); - memset(&seed_executor_ref, 0, sizeof(seed_executor_ref)); - memset(&receipt_ref, 0, sizeof(receipt_ref)); - memset(&run_result, 0, sizeof(run_result)); - memset(fields, 0, sizeof(fields)); - memset(entries, 0, sizeof(entries)); - memset(&b, 0, sizeof(b)); - memset(&artifact, 0, sizeof(artifact)); - - if (!amduatd_seed_concept_if_missing(store, cfg, concepts, dcfg, - "ms.type.string", - &type_string_ref) || - !amduatd_seed_concept_if_missing(store, cfg, concepts, dcfg, - "ms.type.ref", - &type_ref_ref) || - !amduatd_seed_concept_if_missing(store, cfg, concepts, dcfg, - "ms.ui.field.title", - &key_title_ref) || - !amduatd_seed_concept_if_missing(store, cfg, concepts, dcfg, - "ms.ui.field.status", - &key_status_ref) || - !amduatd_seed_concept_if_missing(store, cfg, concepts, dcfg, - "ms.ui.field.latest_ref", - &key_latest_ref) || - !amduatd_seed_concept_if_missing(store, cfg, concepts, dcfg, - "ms.ui.state.schema.v1", - &schema_concept_ref) || - !amduatd_seed_concept_if_missing(store, cfg, concepts, dcfg, - "ms.ui.state.registry.v1", - ®istry_concept_ref)) { - goto seed_cleanup; - } - - if (!amduatd_seed_pel_identity_program(store, &identity_program_ref)) { - goto seed_cleanup; - } - - { - amduat_reference_t latest_schema; - amduat_reference_t latest_registry; - bool have_schema = false; - bool have_registry = false; - bool schema_ok = false; - bool registry_ok = false; - - memset(&latest_schema, 0, sizeof(latest_schema)); - memset(&latest_registry, 0, sizeof(latest_registry)); - have_schema = amduatd_concepts_resolve_latest(store, - concepts, - schema_concept_ref, - &latest_schema); - have_registry = amduatd_concepts_resolve_latest(store, - concepts, - registry_concept_ref, - &latest_registry); - if (have_schema && have_registry) { - amduat_artifact_t schema_artifact; - amduat_artifact_t registry_artifact; - amduat_asl_store_error_t err; - - memset(&schema_artifact, 0, sizeof(schema_artifact)); - memset(®istry_artifact, 0, sizeof(registry_artifact)); - err = amduat_asl_store_get(store, latest_schema, &schema_artifact); - if (err == AMDUAT_ASL_STORE_OK && - schema_artifact.bytes.len != 0 && - schema_artifact.bytes.data != NULL && - amduatd_bytes_contains(schema_artifact.bytes.data, - schema_artifact.bytes.len, - "\"key_text\"")) { - schema_ok = true; - } - err = amduat_asl_store_get(store, latest_registry, ®istry_artifact); - if (err == AMDUAT_ASL_STORE_OK && - registry_artifact.bytes.len != 0 && - registry_artifact.bytes.data != NULL && - amduatd_bytes_contains(registry_artifact.bytes.data, - registry_artifact.bytes.len, - "\"value_text\"")) { - registry_ok = true; - } - amduat_asl_artifact_free(&schema_artifact); - amduat_asl_artifact_free(®istry_artifact); - } - amduat_reference_free(&latest_schema); - amduat_reference_free(&latest_registry); - if (have_schema && have_registry && schema_ok && registry_ok) { - ok = true; - goto seed_cleanup; - } - } - - { - const char *payload = "{\"seed\":\"manifest\"}"; - artifact = amduat_artifact(amduat_octets(payload, strlen(payload))); - if (!amduatd_seed_store_artifact(store, artifact, &seed_manifest_ref)) { - goto seed_cleanup; - } - } - { - const char *payload = "{\"seed\":\"environment\"}"; - artifact = amduat_artifact(amduat_octets(payload, strlen(payload))); - if (!amduatd_seed_store_artifact(store, artifact, &seed_environment_ref)) { - goto seed_cleanup; - } - } - { - const char *payload = "{\"seed\":\"executor\"}"; - artifact = amduat_artifact(amduat_octets(payload, strlen(payload))); - if (!amduatd_seed_store_artifact(store, artifact, &seed_executor_ref)) { - goto seed_cleanup; - } - } - - { - amduat_reference_t hello_ref; - amduat_reference_t title_ref; - amduat_reference_t status_ref; - char *hello_hex = NULL; - const char *payload = "hello"; - - memset(&hello_ref, 0, sizeof(hello_ref)); - memset(&title_ref, 0, sizeof(title_ref)); - memset(&status_ref, 0, sizeof(status_ref)); - - artifact = amduat_artifact(amduat_octets(payload, strlen(payload))); - if (!amduatd_seed_store_artifact(store, artifact, &hello_ref)) { - goto seed_cleanup; - } - if (!amduat_asl_ref_encode_hex(hello_ref, &hello_hex)) { - goto seed_cleanup; - } - seed_latest_hex = hello_hex; - - artifact = amduat_artifact(amduat_octets("Amduat UI", - strlen("Amduat UI"))); - if (!amduatd_seed_store_artifact(store, artifact, &title_ref)) { - free(hello_hex); - goto seed_cleanup; - } - - artifact = amduat_artifact(amduat_octets("ready", strlen("ready"))); - if (!amduatd_seed_store_artifact(store, artifact, &status_ref)) { - free(hello_hex); - goto seed_cleanup; - } - - fields[0].key_hex = NULL; - fields[1].key_hex = NULL; - fields[2].key_hex = NULL; - if (!amduat_asl_ref_encode_hex(key_title_ref, &fields[0].key_hex) || - !amduat_asl_ref_encode_hex(key_status_ref, &fields[1].key_hex) || - !amduat_asl_ref_encode_hex(key_latest_ref, &fields[2].key_hex)) { - goto seed_cleanup; - } - fields[0].type_hex = strdup("ms.type.string"); - fields[1].type_hex = strdup("ms.type.string"); - fields[2].type_hex = strdup("ms.type.ref"); - if (fields[0].type_hex == NULL || fields[1].type_hex == NULL || - fields[2].type_hex == NULL) { - goto seed_cleanup; - } - fields[0].key_text = strdup("title"); - fields[1].key_text = strdup("status"); - fields[2].key_text = strdup("latest_ref"); - if (fields[0].key_text == NULL || fields[1].key_text == NULL || - fields[2].key_text == NULL) { - goto seed_cleanup; - } - - entries[0].key_hex = fields[0].key_hex; - entries[1].key_hex = fields[1].key_hex; - entries[2].key_hex = fields[2].key_hex; - - entries[0].value_hex = strdup("Amduat UI"); - entries[1].value_hex = strdup("ready"); - entries[2].value_hex = hello_hex; - if (entries[0].value_hex == NULL || entries[1].value_hex == NULL || - entries[2].value_hex == NULL) { - goto seed_cleanup; - } - hello_hex = NULL; - seed_latest_hex = NULL; - - qsort(fields, 3, sizeof(fields[0]), amduatd_seed_entry_cmp); - qsort(entries, 3, sizeof(entries[0]), amduatd_seed_entry_cmp); - - if (!amduatd_strbuf_append_cstr(&b, - "{\"schema_version\":1,\"fields\":[")) { - goto seed_cleanup; - } - for (i = 0; i < 3; ++i) { - if (i != 0 && !amduatd_strbuf_append_char(&b, ',')) { - goto seed_cleanup; - } - if (!amduatd_strbuf_append_cstr(&b, "{\"key_ref\":\"") || - !amduatd_strbuf_append_cstr(&b, fields[i].key_hex) || - !amduatd_strbuf_append_cstr(&b, "\",\"key_text\":\"") || - !amduatd_strbuf_append_cstr(&b, fields[i].key_text) || - !amduatd_strbuf_append_cstr(&b, "\",\"type_ref\":\"") || - !amduatd_strbuf_append_cstr(&b, fields[i].type_hex) || - !amduatd_strbuf_append_cstr(&b, "\"}")) { - goto seed_cleanup; - } - } - if (!amduatd_strbuf_append_cstr(&b, "]}")) { - goto seed_cleanup; - } - artifact = amduat_artifact(amduat_octets(b.data, b.len)); - if (!amduatd_seed_store_artifact(store, artifact, &schema_input_ref)) { - goto seed_cleanup; - } - amduatd_strbuf_free(&b); - - memset(&b, 0, sizeof(b)); - if (!amduatd_strbuf_append_cstr(&b, - "{\"registry_version\":1,\"entries\":[")) { - goto seed_cleanup; - } - for (i = 0; i < 3; ++i) { - if (i != 0 && !amduatd_strbuf_append_char(&b, ',')) { - goto seed_cleanup; - } - if (!amduatd_strbuf_append_cstr(&b, "{\"key_ref\":\"") || - !amduatd_strbuf_append_cstr(&b, entries[i].key_hex) || - !amduatd_strbuf_append_cstr(&b, - i == 2 ? "\",\"value_ref\":\"" - : "\",\"value_text\":\"") || - !amduatd_strbuf_append_cstr(&b, entries[i].value_hex) || - !amduatd_strbuf_append_cstr(&b, "\"}")) { - goto seed_cleanup; - } - } - if (!amduatd_strbuf_append_cstr(&b, "]}")) { - goto seed_cleanup; - } - artifact = amduat_artifact(amduat_octets(b.data, b.len)); - if (!amduatd_seed_store_artifact(store, artifact, ®istry_input_ref)) { - goto seed_cleanup; - } - } - - { - amduat_reference_t inputs[1]; - amduat_reference_t scheme_ref = amduat_pel_program_dag_scheme_ref(); - amduat_artifact_t receipt_artifact; - amduat_reference_t edge_ref; - amduat_octets_t evaluator_id = amduat_octets("amduatd-seed", - strlen("amduatd-seed")); - - inputs[0] = schema_input_ref; - if (!amduat_pel_surf_run_with_result(store, - scheme_ref, - identity_program_ref, - inputs, - 1, - false, - amduat_reference(0u, - amduat_octets(NULL, 0u)), - &run_result)) { - goto seed_cleanup; - } - if (!run_result.has_result_value || run_result.output_refs_len != 1) { - goto seed_cleanup; - } - if (!amduat_reference_clone(run_result.output_refs[0], - &schema_output_ref)) { - goto seed_cleanup; - } - - if (!amduat_fer1_receipt_from_pel_result(&run_result.result_value, - seed_manifest_ref, - seed_environment_ref, - evaluator_id, - seed_executor_ref, - false, - amduat_reference(0u, - amduat_octets(NULL, 0u)), - amduat_octets(NULL, 0u), - 0, - 0, - &receipt_artifact)) { - goto seed_cleanup; - } - if (!amduatd_seed_store_artifact(store, receipt_artifact, &receipt_ref)) { - amduat_asl_artifact_free(&receipt_artifact); - goto seed_cleanup; - } - amduat_asl_artifact_free(&receipt_artifact); - if (!amduatd_concepts_put_edge(store, - concepts, - schema_output_ref, - receipt_ref, - concepts->rel_has_provenance_ref, - amduat_octets(NULL, 0u), - &edge_ref)) { - goto seed_cleanup; - } - amduat_reference_free(&edge_ref); - amduat_reference_free(&receipt_ref); - amduat_enc_pel1_result_free(&run_result.result_value); - amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len); - amduat_pel_surf_free_ref(&run_result.result_ref); - memset(&run_result, 0, sizeof(run_result)); - } - - { - amduat_reference_t inputs[1]; - amduat_reference_t scheme_ref = amduat_pel_program_dag_scheme_ref(); - amduat_artifact_t receipt_artifact; - amduat_reference_t edge_ref; - amduat_octets_t evaluator_id = amduat_octets("amduatd-seed", - strlen("amduatd-seed")); - - inputs[0] = registry_input_ref; - if (!amduat_pel_surf_run_with_result(store, - scheme_ref, - identity_program_ref, - inputs, - 1, - false, - amduat_reference(0u, - amduat_octets(NULL, 0u)), - &run_result)) { - goto seed_cleanup; - } - if (!run_result.has_result_value || run_result.output_refs_len != 1) { - goto seed_cleanup; - } - if (!amduat_reference_clone(run_result.output_refs[0], - ®istry_output_ref)) { - goto seed_cleanup; - } - - if (!amduat_fer1_receipt_from_pel_result(&run_result.result_value, - seed_manifest_ref, - seed_environment_ref, - evaluator_id, - seed_executor_ref, - false, - amduat_reference(0u, - amduat_octets(NULL, 0u)), - amduat_octets(NULL, 0u), - 0, - 0, - &receipt_artifact)) { - goto seed_cleanup; - } - if (!amduatd_seed_store_artifact(store, receipt_artifact, &receipt_ref)) { - amduat_asl_artifact_free(&receipt_artifact); - goto seed_cleanup; - } - amduat_asl_artifact_free(&receipt_artifact); - if (!amduatd_concepts_put_edge(store, - concepts, - registry_output_ref, - receipt_ref, - concepts->rel_has_provenance_ref, - amduat_octets(NULL, 0u), - &edge_ref)) { - goto seed_cleanup; - } - amduat_reference_free(&edge_ref); - amduat_reference_free(&receipt_ref); - amduat_enc_pel1_result_free(&run_result.result_value); - amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len); - amduat_pel_surf_free_ref(&run_result.result_ref); - memset(&run_result, 0, sizeof(run_result)); - } - - if (!amduatd_seed_materializes_if_missing(store, concepts, - schema_concept_ref, - schema_output_ref) || - !amduatd_seed_materializes_if_missing(store, concepts, - registry_concept_ref, - registry_output_ref)) { - goto seed_cleanup; - } - - ok = true; - -seed_cleanup: - if (run_result.has_result_value) { - amduat_enc_pel1_result_free(&run_result.result_value); - } - if (run_result.output_refs != NULL) { - amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len); - } - amduat_pel_surf_free_ref(&run_result.result_ref); - - amduat_reference_free(&type_string_ref); - amduat_reference_free(&type_ref_ref); - amduat_reference_free(&key_title_ref); - amduat_reference_free(&key_status_ref); - amduat_reference_free(&key_latest_ref); - amduat_reference_free(&schema_concept_ref); - amduat_reference_free(®istry_concept_ref); - amduat_reference_free(&identity_program_ref); - amduat_reference_free(&schema_input_ref); - amduat_reference_free(®istry_input_ref); - amduat_reference_free(&schema_output_ref); - amduat_reference_free(®istry_output_ref); - amduat_reference_free(&seed_manifest_ref); - amduat_reference_free(&seed_environment_ref); - amduat_reference_free(&seed_executor_ref); - - for (i = 0; i < 3; ++i) { - free(fields[i].key_hex); - free(fields[i].type_hex); - free(fields[i].key_text); - free(entries[i].value_hex); - } - amduatd_strbuf_free(&b); - free(seed_latest_hex); - return ok; -} - bool amduatd_handle_get_artifact(int fd, amduat_asl_store_t *store, const amduatd_http_req_t *req, @@ -6688,214 +3962,6 @@ pel_programs_cleanup: return ok; } -static bool amduatd_handle_post_concepts(int fd, - amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - amduatd_concepts_t *concepts, - const amduatd_cfg_t *dcfg, - const amduatd_http_req_t *req) { - uint8_t *body = NULL; - const char *p = NULL; - const char *end = NULL; - char *name = NULL; - bool have_name = false; - bool have_ref = false; - amduat_reference_t target_ref; - amduat_reference_t name_ref; - amduat_reference_t concept_ref; - amduat_reference_t edge_ref; - char *concept_hex = NULL; - char *scoped_name = NULL; - amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - char json[4096]; - bool ok = false; - amduat_octets_t actor = amduat_octets(NULL, 0u); - - memset(&target_ref, 0, sizeof(target_ref)); - memset(&name_ref, 0, sizeof(name_ref)); - memset(&concept_ref, 0, sizeof(concept_ref)); - memset(&edge_ref, 0, sizeof(edge_ref)); - - if (store == NULL || cfg == NULL || concepts == NULL || req == NULL) { - return amduatd_send_json_error(fd, 500, "Internal Server Error", - "internal error"); - } - if (req->has_actor) { - actor = req->actor; - } - if (req->content_length == 0) { - return amduatd_send_json_error(fd, 400, "Bad Request", "missing body"); - } - if (req->content_length > (256u * 1024u)) { - return amduatd_send_json_error(fd, 413, "Payload Too Large", - "payload too large"); - } - body = (uint8_t *)malloc(req->content_length); - if (body == NULL) { - return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); - } - if (!amduatd_read_exact(fd, body, req->content_length)) { - free(body); - return false; - } - p = (const char *)body; - end = (const char *)body + req->content_length; - if (!amduatd_json_expect(&p, end, '{')) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); - goto concepts_cleanup; - } - - for (;;) { - const char *key = NULL; - size_t key_len = 0; - const char *sv = NULL; - size_t sv_len = 0; - const char *cur = NULL; - - cur = amduatd_json_skip_ws(p, end); - if (cur < end && *cur == '}') { - p = cur + 1; - break; - } - if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) || - !amduatd_json_expect(&p, end, ':')) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); - goto concepts_cleanup; - } - - if (key_len == strlen("name") && memcmp(key, "name", key_len) == 0) { - if (have_name || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || - !amduatd_copy_json_str(sv, sv_len, &name)) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); - goto concepts_cleanup; - } - have_name = true; - } else if (key_len == strlen("ref") && memcmp(key, "ref", key_len) == 0) { - if (have_ref || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || - !amduatd_decode_ref_hex_str(sv, sv_len, &target_ref)) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid ref"); - goto concepts_cleanup; - } - have_ref = true; - } else { - if (!amduatd_json_skip_value(&p, end, 0)) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); - goto concepts_cleanup; - } - } - - cur = amduatd_json_skip_ws(p, end); - if (cur < end && *cur == ',') { - p = cur + 1; - continue; - } - if (cur < end && *cur == '}') { - p = cur + 1; - break; - } - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); - goto concepts_cleanup; - } - - if (!have_name) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing name"); - goto concepts_cleanup; - } - - if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); - goto concepts_cleanup; - } - scoped_name = (char *)scoped_bytes.data; - amduatd_space_log_mapping(space, name, scoped_name); - - if (!amduatd_concepts_put_name_artifact(store, scoped_name, &name_ref)) { - ok = amduatd_send_json_error(fd, 500, "Internal Server Error", - "store error"); - goto concepts_cleanup; - } - - { - amduat_reference_t existing; - memset(&existing, 0, sizeof(existing)); - if (amduatd_concepts_lookup_alias(store, - cfg, - concepts, - scoped_name, - &existing)) { - amduat_reference_free(&existing); - ok = amduatd_send_json_error(fd, 409, "Conflict", "name exists"); - goto concepts_cleanup; - } - } - - if (!amduatd_concepts_put_concept_id(store, cfg, &concept_ref)) { - ok = amduatd_send_json_error(fd, 500, "Internal Server Error", - "store error"); - goto concepts_cleanup; - } - - if (!amduatd_concepts_put_edge(store, - concepts, - name_ref, - concept_ref, - concepts->rel_aliases_ref, - actor, - &edge_ref)) { - ok = amduatd_send_json_error(fd, 500, "Internal Server Error", - "store error"); - goto concepts_cleanup; - } - - if (have_ref) { - if (!amduatd_concepts_put_edge(store, - concepts, - concept_ref, - target_ref, - concepts->rel_materializes_ref, - actor, - &edge_ref)) { - ok = amduatd_send_json_error(fd, 500, "Internal Server Error", - "store error"); - goto concepts_cleanup; - } - } - - if (!amduat_asl_ref_encode_hex(concept_ref, &concept_hex)) { - ok = amduatd_send_json_error(fd, 500, "Internal Server Error", - "encode error"); - goto concepts_cleanup; - } - - { - int n = snprintf(json, - sizeof(json), - "{" - "\"name\":\"%s\"," - "\"concept_ref\":\"%s\"" - "}\n", - name, - concept_hex); - free(concept_hex); - if (n <= 0 || (size_t)n >= sizeof(json)) { - ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); - goto concepts_cleanup; - } - ok = amduatd_http_send_json(fd, 200, "OK", json, false); - } - -concepts_cleanup: - free(body); - free(name); - free(scoped_name); - amduat_reference_free(&target_ref); - amduat_reference_free(&name_ref); - amduat_reference_free(&concept_ref); - amduat_reference_free(&edge_ref); - return ok; -} - typedef struct { char *key_hex; char *value_hex; @@ -7444,234 +4510,6 @@ cf_cleanup: return ok; } -static bool amduatd_path_extract_name(const char *path, - const char *prefix, - char *out, - size_t cap) { - size_t plen; - const char *p; - size_t len; - - if (out != NULL && cap != 0) { - out[0] = '\0'; - } - if (path == NULL || prefix == NULL || out == NULL || cap == 0) { - return false; - } - plen = strlen(prefix); - if (strncmp(path, prefix, plen) != 0) { - return false; - } - p = path + plen; - if (*p == '\0') { - return false; - } - len = strlen(p); - if (len >= cap) { - len = cap - 1; - } - memcpy(out, p, len); - out[len] = '\0'; - return true; -} - -static bool amduatd_handle_post_concept_publish(int fd, - amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - amduatd_concepts_t *concepts, - const amduatd_cfg_t *dcfg, - const char *name, - const amduatd_http_req_t *req) { - uint8_t *body = NULL; - const char *p = NULL; - const char *end = NULL; - amduat_reference_t target_ref; - amduat_reference_t concept_ref; - amduat_reference_t edge_ref; - bool have_ref = false; - bool ok = false; - amduat_octets_t actor = amduat_octets(NULL, 0u); - char *scoped_name = NULL; - amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - - memset(&target_ref, 0, sizeof(target_ref)); - memset(&concept_ref, 0, sizeof(concept_ref)); - memset(&edge_ref, 0, sizeof(edge_ref)); - - if (store == NULL || cfg == NULL || concepts == NULL || name == NULL || - req == NULL) { - return amduatd_send_json_error(fd, 500, "Internal Server Error", - "internal error"); - } - if (req->has_actor) { - actor = req->actor; - } - if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { - return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); - } - scoped_name = (char *)scoped_bytes.data; - amduatd_space_log_mapping(space, name, scoped_name); - if (req->content_length == 0) { - free(scoped_name); - return amduatd_send_json_error(fd, 400, "Bad Request", "missing body"); - } - if (req->content_length > (256u * 1024u)) { - free(scoped_name); - return amduatd_send_json_error(fd, 413, "Payload Too Large", - "payload too large"); - } - - if (!amduatd_concepts_lookup_alias(store, - cfg, - concepts, - scoped_name, - &concept_ref)) { - free(scoped_name); - return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept"); - } - free(scoped_name); - - body = (uint8_t *)malloc(req->content_length); - if (body == NULL) { - ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); - goto publish_cleanup; - } - if (!amduatd_read_exact(fd, body, req->content_length)) { - free(body); - amduat_reference_free(&concept_ref); - return false; - } - - p = (const char *)body; - end = (const char *)body + req->content_length; - if (!amduatd_json_expect(&p, end, '{')) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); - goto publish_cleanup; - } - for (;;) { - const char *key = NULL; - size_t key_len = 0; - const char *sv = NULL; - size_t sv_len = 0; - const char *cur = NULL; - - cur = amduatd_json_skip_ws(p, end); - if (cur < end && *cur == '}') { - p = cur + 1; - break; - } - if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) || - !amduatd_json_expect(&p, end, ':')) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); - goto publish_cleanup; - } - if (key_len == strlen("ref") && memcmp(key, "ref", key_len) == 0) { - if (have_ref || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || - !amduatd_decode_ref_hex_str(sv, sv_len, &target_ref)) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid ref"); - goto publish_cleanup; - } - have_ref = true; - } else { - if (!amduatd_json_skip_value(&p, end, 0)) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); - goto publish_cleanup; - } - } - cur = amduatd_json_skip_ws(p, end); - if (cur < end && *cur == ',') { - p = cur + 1; - continue; - } - if (cur < end && *cur == '}') { - p = cur + 1; - break; - } - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); - goto publish_cleanup; - } - if (!have_ref) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing ref"); - goto publish_cleanup; - } - - if (!amduatd_concepts_put_edge(store, - concepts, - concept_ref, - target_ref, - concepts->rel_materializes_ref, - actor, - &edge_ref)) { - ok = amduatd_send_json_error(fd, 500, "Internal Server Error", - "store error"); - goto publish_cleanup; - } - - ok = amduatd_http_send_json(fd, 200, "OK", "{\"ok\":true}\n", false); - -publish_cleanup: - free(body); - amduat_reference_free(&target_ref); - amduat_reference_free(&concept_ref); - amduat_reference_free(&edge_ref); - return ok; -} - -static bool amduatd_handle_get_resolve(int fd, - amduat_asl_store_t *store, - const amduat_asl_store_fs_config_t *cfg, - const amduatd_concepts_t *concepts, - const amduatd_cfg_t *dcfg, - const char *name) { - amduat_reference_t concept_ref; - amduat_reference_t latest_ref; - char *hex = NULL; - char json[2048]; - int n; - char *scoped_name = NULL; - amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); - const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; - - memset(&concept_ref, 0, sizeof(concept_ref)); - memset(&latest_ref, 0, sizeof(latest_ref)); - - if (store == NULL || cfg == NULL || concepts == NULL || name == NULL) { - return amduatd_send_json_error(fd, 500, "Internal Server Error", - "internal error"); - } - if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { - return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); - } - scoped_name = (char *)scoped_bytes.data; - amduatd_space_log_mapping(space, name, scoped_name); - if (!amduatd_concepts_lookup_alias(store, cfg, concepts, scoped_name, - &concept_ref)) { - free(scoped_name); - return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept"); - } - free(scoped_name); - if (!amduatd_concepts_resolve_latest(store, concepts, concept_ref, - &latest_ref)) { - amduat_reference_free(&concept_ref); - return amduatd_send_json_error(fd, 404, "Not Found", "no versions"); - } - amduat_reference_free(&concept_ref); - - if (!amduat_asl_ref_encode_hex(latest_ref, &hex)) { - amduat_reference_free(&latest_ref); - return amduatd_send_json_error(fd, 500, "Internal Server Error", - "encode error"); - } - amduat_reference_free(&latest_ref); - n = snprintf(json, sizeof(json), "{\"ref\":\"%s\"}\n", hex); - free(hex); - if (n <= 0 || (size_t)n >= sizeof(json)) { - return amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); - } - return amduatd_http_send_json(fd, 200, "OK", json, false); -} - static bool amduatd_handle_conn(int fd, amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, @@ -7799,61 +4637,31 @@ static bool amduatd_handle_conn(int fd, &req); goto conn_cleanup; } - if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/concepts") == 0) { - ok = amduatd_handle_post_concepts(fd, store, cfg, concepts, dcfg, &req); - goto conn_cleanup; - } - if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/concepts") == 0) { - ok = amduatd_handle_get_concepts(fd, store, concepts, dcfg); - goto conn_cleanup; - } - if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/relations") == 0) { - ok = amduatd_handle_get_relations(fd, concepts); - goto conn_cleanup; + { + amduatd_ctx_t concepts_ctx; + amduatd_http_resp_t concepts_resp; + + concepts_ctx.store = store; + concepts_ctx.ui_ref = ui_ref; + concepts_ctx.store_cfg = cfg; + concepts_ctx.concepts = concepts; + concepts_ctx.daemon_cfg = dcfg; + concepts_ctx.root_path = root_path; + concepts_ctx.caps = caps; + concepts_resp.fd = fd; + concepts_resp.ok = false; + if (amduatd_concepts_can_handle(&req)) { + if (amduatd_concepts_handle(&concepts_ctx, &req, &concepts_resp)) { + ok = concepts_resp.ok; + goto conn_cleanup; + } + } } if (strcmp(req.method, "GET") == 0 && strncmp(no_query, "/v1/fed/artifacts/", 18) == 0) { ok = amduatd_handle_get_fed_artifact(fd, store, &req, req.path); goto conn_cleanup; } - if (strcmp(req.method, "GET") == 0 && strncmp(no_query, "/v1/concepts/", 13) == 0) { - const char *name = no_query + 13; - if (name[0] == '\0') { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing name"); - goto conn_cleanup; - } - ok = amduatd_handle_get_concept(fd, store, cfg, concepts, dcfg, name); - goto conn_cleanup; - } - if (strcmp(req.method, "POST") == 0 && - strncmp(no_query, "/v1/concepts/", 13) == 0 && - strstr(no_query, "/publish") != NULL) { - char name[256]; - char *slash; - if (!amduatd_path_extract_name(no_query, "/v1/concepts/", name, - sizeof(name))) { - return amduatd_send_json_error(fd, 400, "Bad Request", "invalid path"); - } - slash = strstr(name, "/publish"); - if (slash == NULL || strcmp(slash, "/publish") != 0) { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid path"); - goto conn_cleanup; - } - *slash = '\0'; - ok = amduatd_handle_post_concept_publish(fd, store, cfg, concepts, dcfg, - name, - &req); - goto conn_cleanup; - } - if (strcmp(req.method, "GET") == 0 && strncmp(no_query, "/v1/resolve/", 12) == 0) { - const char *name = no_query + 12; - if (name[0] == '\0') { - ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing name"); - goto conn_cleanup; - } - ok = amduatd_handle_get_resolve(fd, store, cfg, concepts, dcfg, name); - goto conn_cleanup; - } if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/cap/resolve") == 0) { amduatd_ctx_t caps_ctx; @@ -8034,12 +4842,12 @@ int main(int argc, char **argv) { return 8; } #endif - if (!amduatd_concepts_init(&concepts, &store, &cfg, &dcfg, root)) { + if (!amduatd_concepts_init(&concepts, &store, &dcfg.space, root, true)) { fprintf(stderr, "error: failed to init concept edges\n"); return 8; } #if AMDUATD_ENABLE_UI - if (!amduatd_seed_ms_ui_state(&store, &cfg, &concepts, &dcfg)) { + if (!amduatd_seed_ms_ui_state(&store, &concepts, &dcfg)) { fprintf(stderr, "error: failed to seed ms ui state\n"); return 8; } diff --git a/src/amduatd_concepts.c b/src/amduatd_concepts.c new file mode 100644 index 0000000..50823ca --- /dev/null +++ b/src/amduatd_concepts.c @@ -0,0 +1,3428 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#define _POSIX_C_SOURCE 200809L + +#include "amduatd_concepts.h" + +#include "amduatd_caps.h" + +#include "amduat/asl/artifact_io.h" +#include "amduat/asl/collection_view.h" +#include "amduat/asl/none.h" +#include "amduat/asl/record.h" +#include "amduat/asl/asl_pointer_fs.h" +#include "amduat/asl/ref_derive.h" +#include "amduat/asl/ref_text.h" +#include "amduat/enc/asl1_core_codec.h" +#include "amduat/enc/fer1_receipt.h" +#include "amduat/enc/pel1_result.h" +#include "amduat/enc/pel_program_dag.h" +#include "amduat/enc/tgk1_edge.h" +#include "amduat/fer/receipt.h" +#include "amduat/format/pel.h" +#include "amduat/pel/program_dag.h" +#include "amduat/pel/program_dag_desc.h" +#include "amduat/pel/run.h" +#include "amduat/util/log.h" + +#include +#include +#include +#include +#include +#include +#include + +bool amduatd_read_exact(int fd, uint8_t *buf, size_t len); +bool amduatd_read_urandom(uint8_t *out, size_t len); + +bool amduatd_http_send_json(int fd, + int code, + const char *reason, + const char *json, + bool head_only); + +bool amduatd_send_json_error(int fd, + int code, + const char *reason, + const char *msg); + +const char *amduatd_json_skip_ws(const char *p, const char *end); + +bool amduatd_json_expect(const char **p, + const char *end, + char expected); + +bool amduatd_json_parse_string_noesc(const char **p, + const char *end, + const char **out, + size_t *out_len); + +bool amduatd_json_skip_value(const char **p, + const char *end, + int depth); + +bool amduatd_copy_json_str(const char *s, size_t len, char **out); + +typedef struct amduatd_strbuf { + char *data; + size_t len; + size_t cap; +} amduatd_strbuf_t; + +static const char *const AMDUATD_EDGES_FILE = ".amduatd.edges"; +static const char *const AMDUATD_EDGE_COLLECTION = "daemon/edges"; +static const uint32_t AMDUATD_EDGE_VIEW_BATCH = 1024u; +static const uint16_t AMDUATD_EDGE_COLLECTION_KIND = 1u; + +static void amduatd_edge_entry_free(amduatd_edge_entry_t *entry) { + if (entry == NULL) { + return; + } + amduat_reference_free(&entry->record_ref); + amduat_reference_free(&entry->src_ref); + amduat_reference_free(&entry->dst_ref); + free(entry->rel); + entry->rel = NULL; +} + +static void amduatd_edge_list_clear(amduatd_edge_list_t *list) { + if (list == NULL) { + return; + } + for (size_t i = 0; i < list->len; ++i) { + amduatd_edge_entry_free(&list->items[i]); + } + free(list->items); + list->items = NULL; + list->len = 0; + list->cap = 0; +} + +static bool amduatd_edge_list_push(amduatd_edge_list_t *list, + const amduatd_edge_entry_t *entry) { + amduatd_edge_entry_t cloned; + amduatd_edge_entry_t *next; + + if (list == NULL || entry == NULL) { + return false; + } + memset(&cloned, 0, sizeof(cloned)); + if (!amduat_reference_clone(entry->record_ref, &cloned.record_ref) || + !amduat_reference_clone(entry->src_ref, &cloned.src_ref) || + !amduat_reference_clone(entry->dst_ref, &cloned.dst_ref)) { + amduatd_edge_entry_free(&cloned); + return false; + } + if (entry->rel != NULL) { + cloned.rel = strdup(entry->rel); + if (cloned.rel == NULL) { + amduatd_edge_entry_free(&cloned); + return false; + } + } + if (list->len == list->cap) { + size_t next_cap = list->cap != 0 ? list->cap * 2u : 64u; + next = (amduatd_edge_entry_t *)realloc(list->items, + next_cap * sizeof(*list->items)); + if (next == NULL) { + amduatd_edge_entry_free(&cloned); + return false; + } + list->items = next; + list->cap = next_cap; + } + list->items[list->len++] = cloned; + return true; +} + +void amduatd_concepts_free(amduatd_concepts_t *c) { + if (c == NULL) { + return; + } + amduat_reference_free(&c->rel_aliases_ref); + amduat_reference_free(&c->rel_materializes_ref); + amduat_reference_free(&c->rel_represents_ref); + amduat_reference_free(&c->rel_requires_key_ref); + amduat_reference_free(&c->rel_within_domain_ref); + amduat_reference_free(&c->rel_computed_by_ref); + amduat_reference_free(&c->rel_has_provenance_ref); + amduatd_edge_list_clear(&c->edges); + free(c->edge_collection_name); + c->edge_collection_name = NULL; +} + +enum { + AMDUATD_EDGE_MAGIC_LEN = 8, + AMDUATD_EDGE_VERSION = 1 +}; + +static const uint8_t k_amduatd_edge_magic[AMDUATD_EDGE_MAGIC_LEN] = { + 'A', 'S', 'L', 'E', 'D', 'G', '1', '\0' +}; + +static const char *const AMDUATD_EDGE_SCHEMA = "tgk/edge"; + +static const char *const AMDUATD_REL_ALIAS = "alias"; +static const char *const AMDUATD_REL_MATERIALIZES = "materializes"; +static const char *const AMDUATD_REL_REPRESENTS = "represents"; +static const char *const AMDUATD_REL_REQUIRES_KEY = "requires_key"; +static const char *const AMDUATD_REL_WITHIN_DOMAIN = "within_domain"; +static const char *const AMDUATD_REL_COMPUTED_BY = "computed_by"; +static const char *const AMDUATD_REL_HAS_PROVENANCE = "has_provenance"; + +static bool amduatd_concepts_put_name_artifact(amduat_asl_store_t *store, + const char *name, + amduat_reference_t *out_ref); +static bool amduatd_concepts_put_edge(amduat_asl_store_t *store, + amduatd_concepts_t *c, + amduat_reference_t from, + amduat_reference_t to, + amduat_reference_t relation_concept_ref, + amduat_octets_t actor, + amduat_reference_t *out_edge_ref); +static bool amduatd_concepts_lookup_alias(amduat_asl_store_t *store, + const amduatd_concepts_t *c, + const char *name, + amduat_reference_t *out_concept_ref); + +static void amduatd_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 bool amduatd_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 void amduatd_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 amduatd_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 amduatd_add_size(size_t *acc, size_t add) { + if (*acc > SIZE_MAX - add) { + return false; + } + *acc += add; + return true; +} + +static bool amduatd_is_ascii(amduat_octets_t bytes) { + if (bytes.len == 0u || bytes.data == NULL) { + return false; + } + for (size_t i = 0u; i < bytes.len; ++i) { + uint8_t c = bytes.data[i]; + if (c < 0x20u || c > 0x7eu) { + return false; + } + } + return true; +} + +static bool amduatd_edge_payload_encode(amduat_reference_t src, + amduat_reference_t dst, + const char *rel, + amduat_octets_t *out_payload) { + amduat_octets_t src_bytes = amduat_octets(NULL, 0u); + amduat_octets_t dst_bytes = amduat_octets(NULL, 0u); + size_t total_len = 0u; + size_t offset = 0u; + uint8_t *buffer = NULL; + uint32_t rel_len = 0u; + + if (out_payload == NULL || rel == NULL) { + return false; + } + out_payload->data = NULL; + out_payload->len = 0u; + + { + size_t rel_len_full = strlen(rel); + if (rel_len_full > UINT32_MAX) { + return false; + } + rel_len = (uint32_t)rel_len_full; + } + if (rel_len == 0u) { + return false; + } + if (!amduatd_is_ascii(amduat_octets((const uint8_t *)rel, rel_len))) { + return false; + } + if (!amduat_enc_asl1_core_encode_reference_v1(src, &src_bytes) || + !amduat_enc_asl1_core_encode_reference_v1(dst, &dst_bytes)) { + free((void *)src_bytes.data); + free((void *)dst_bytes.data); + return false; + } + if (src_bytes.len > UINT32_MAX || dst_bytes.len > UINT32_MAX) { + free((void *)src_bytes.data); + free((void *)dst_bytes.data); + return false; + } + + if (!amduatd_add_size(&total_len, + AMDUATD_EDGE_MAGIC_LEN + 4u + 4u + src_bytes.len) || + !amduatd_add_size(&total_len, 4u + dst_bytes.len) || + !amduatd_add_size(&total_len, 4u + rel_len) || + !amduatd_add_size(&total_len, 1u)) { + free((void *)src_bytes.data); + free((void *)dst_bytes.data); + return false; + } + + buffer = (uint8_t *)malloc(total_len); + if (buffer == NULL) { + free((void *)src_bytes.data); + free((void *)dst_bytes.data); + return false; + } + + memcpy(buffer + offset, k_amduatd_edge_magic, AMDUATD_EDGE_MAGIC_LEN); + offset += AMDUATD_EDGE_MAGIC_LEN; + amduatd_store_u32_le(buffer + offset, AMDUATD_EDGE_VERSION); + offset += 4u; + + amduatd_store_u32_le(buffer + offset, (uint32_t)src_bytes.len); + offset += 4u; + memcpy(buffer + offset, src_bytes.data, src_bytes.len); + offset += src_bytes.len; + + amduatd_store_u32_le(buffer + offset, (uint32_t)dst_bytes.len); + offset += 4u; + memcpy(buffer + offset, dst_bytes.data, dst_bytes.len); + offset += dst_bytes.len; + + amduatd_store_u32_le(buffer + offset, rel_len); + offset += 4u; + memcpy(buffer + offset, rel, rel_len); + offset += rel_len; + + buffer[offset++] = 0u; + + free((void *)src_bytes.data); + free((void *)dst_bytes.data); + + if (offset != total_len) { + free(buffer); + return false; + } + + out_payload->data = buffer; + out_payload->len = total_len; + return true; +} + +static bool amduatd_edge_payload_decode(amduat_octets_t payload, + amduat_reference_t *out_src, + amduat_reference_t *out_dst, + char **out_rel) { + size_t offset = 0u; + uint32_t version = 0u; + uint32_t src_len = 0u; + uint32_t dst_len = 0u; + uint32_t rel_len = 0u; + uint8_t flags = 0u; + amduat_octets_t src_bytes; + amduat_octets_t dst_bytes; + char *rel = NULL; + + if (out_src == NULL || out_dst == NULL || out_rel == NULL) { + return false; + } + *out_src = amduat_reference(0u, amduat_octets(NULL, 0u)); + *out_dst = amduat_reference(0u, amduat_octets(NULL, 0u)); + *out_rel = NULL; + + if (payload.len < AMDUATD_EDGE_MAGIC_LEN + 4u + 1u || + payload.data == NULL) { + return false; + } + if (memcmp(payload.data, k_amduatd_edge_magic, + AMDUATD_EDGE_MAGIC_LEN) != 0) { + return false; + } + offset += AMDUATD_EDGE_MAGIC_LEN; + if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &version) || + version != AMDUATD_EDGE_VERSION) { + return false; + } + if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &src_len)) { + return false; + } + if (payload.len - offset < src_len) { + return false; + } + src_bytes = amduat_octets(payload.data + offset, src_len); + offset += src_len; + if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &dst_len)) { + return false; + } + if (payload.len - offset < dst_len) { + return false; + } + dst_bytes = amduat_octets(payload.data + offset, dst_len); + offset += dst_len; + if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &rel_len)) { + return false; + } + if (payload.len - offset < rel_len + 1u) { + return false; + } + if (!amduatd_is_ascii(amduat_octets(payload.data + offset, rel_len))) { + return false; + } + rel = (char *)malloc(rel_len + 1u); + if (rel == NULL) { + return false; + } + memcpy(rel, payload.data + offset, rel_len); + rel[rel_len] = '\0'; + offset += rel_len; + flags = payload.data[offset++]; + (void)flags; + + if (offset != payload.len) { + free(rel); + return false; + } + if (!amduat_enc_asl1_core_decode_reference_v1(src_bytes, out_src) || + !amduat_enc_asl1_core_decode_reference_v1(dst_bytes, out_dst)) { + amduat_reference_free(out_src); + amduat_reference_free(out_dst); + free(rel); + return false; + } + + *out_rel = rel; + return true; +} + +static const char *amduatd_relation_name_for_ref( + const amduatd_concepts_t *c, + amduat_reference_t ref) { + if (c == NULL) { + return NULL; + } + if (amduat_reference_eq(ref, c->rel_aliases_ref)) { + return AMDUATD_REL_ALIAS; + } + if (amduat_reference_eq(ref, c->rel_materializes_ref)) { + return AMDUATD_REL_MATERIALIZES; + } + if (amduat_reference_eq(ref, c->rel_represents_ref)) { + return AMDUATD_REL_REPRESENTS; + } + if (amduat_reference_eq(ref, c->rel_requires_key_ref)) { + return AMDUATD_REL_REQUIRES_KEY; + } + if (amduat_reference_eq(ref, c->rel_within_domain_ref)) { + return AMDUATD_REL_WITHIN_DOMAIN; + } + if (amduat_reference_eq(ref, c->rel_computed_by_ref)) { + return AMDUATD_REL_COMPUTED_BY; + } + if (amduat_reference_eq(ref, c->rel_has_provenance_ref)) { + return AMDUATD_REL_HAS_PROVENANCE; + } + return NULL; +} + +static bool amduatd_build_collection_head_name(const char *name, + char **out_name) { + size_t name_len; + size_t total_len; + char *buffer; + size_t offset = 0u; + + if (name == NULL || out_name == NULL) { + return false; + } + if (!amduat_asl_pointer_name_is_valid(name)) { + return false; + } + name_len = strlen(name); + total_len = 11u + name_len + 5u + 1u; + buffer = (char *)malloc(total_len); + if (buffer == NULL) { + return false; + } + memcpy(buffer + offset, "collection/", 11u); + offset += 11u; + memcpy(buffer + offset, name, name_len); + offset += name_len; + memcpy(buffer + offset, "/head", 5u); + offset += 5u; + buffer[offset] = '\0'; + *out_name = buffer; + return true; +} + +static bool amduatd_build_collection_log_head_name(const char *name, + char **out_name) { + size_t name_len; + size_t log_name_len; + size_t total_len; + char *buffer; + size_t offset = 0u; + + if (name == NULL || out_name == NULL) { + return false; + } + if (!amduat_asl_pointer_name_is_valid(name)) { + return false; + } + name_len = strlen(name); + log_name_len = 11u + name_len + 4u; + total_len = 4u + log_name_len + 5u + 1u; + buffer = (char *)malloc(total_len); + if (buffer == NULL) { + return false; + } + memcpy(buffer + offset, "log/", 4u); + offset += 4u; + memcpy(buffer + offset, "collection/", 11u); + offset += 11u; + memcpy(buffer + offset, name, name_len); + offset += name_len; + memcpy(buffer + offset, "/log", 4u); + offset += 4u; + memcpy(buffer + offset, "/head", 5u); + offset += 5u; + buffer[offset] = '\0'; + *out_name = buffer; + return true; +} + +static bool amduatd_get_none_ref(amduat_asl_store_t *store, + amduat_reference_t *out_ref) { + amduat_artifact_t none_artifact; + + if (store == NULL || out_ref == NULL) { + return false; + } + if (!amduat_asl_none_artifact(&none_artifact)) { + return false; + } + if (amduat_asl_store_put(store, none_artifact, out_ref) != + AMDUAT_ASL_STORE_OK) { + amduat_artifact_free(&none_artifact); + return false; + } + amduat_artifact_free(&none_artifact); + return true; +} + +static bool amduatd_put_view_params(amduat_asl_store_t *store, + uint64_t from_offset, + uint32_t limit, + amduat_reference_t *out_ref) { + uint8_t payload[12]; + + if (store == NULL || out_ref == NULL) { + return false; + } + payload[0] = (uint8_t)(from_offset & 0xffu); + payload[1] = (uint8_t)((from_offset >> 8) & 0xffu); + payload[2] = (uint8_t)((from_offset >> 16) & 0xffu); + payload[3] = (uint8_t)((from_offset >> 24) & 0xffu); + payload[4] = (uint8_t)((from_offset >> 32) & 0xffu); + payload[5] = (uint8_t)((from_offset >> 40) & 0xffu); + payload[6] = (uint8_t)((from_offset >> 48) & 0xffu); + payload[7] = (uint8_t)((from_offset >> 56) & 0xffu); + payload[8] = (uint8_t)(limit & 0xffu); + payload[9] = (uint8_t)((limit >> 8) & 0xffu); + payload[10] = (uint8_t)((limit >> 16) & 0xffu); + payload[11] = (uint8_t)((limit >> 24) & 0xffu); + + if (amduat_asl_record_store_put( + store, + amduat_octets("pel/params", strlen("pel/params")), + amduat_octets(payload, sizeof(payload)), + out_ref) != AMDUAT_ASL_STORE_OK) { + return false; + } + return true; +} + +static void amduatd_program_free(amduat_pel_program_t *program) { + if (program == NULL || program->nodes == NULL) { + return; + } + for (size_t i = 0u; i < program->nodes_len; ++i) { + free(program->nodes[i].inputs); + } + free(program->nodes); + free(program->roots); + program->nodes = NULL; + program->roots = NULL; + program->nodes_len = 0u; + program->roots_len = 0u; +} + +static bool amduatd_build_collection_view_program( + amduat_pel_program_t *out_program) { + static const char k_op_snapshot[] = "collection.snapshot_decode_v1"; + static const char k_op_read_range[] = "log.read_range_v1"; + static const char k_op_merge[] = "collection.merge_refs_v1"; + amduat_pel_node_t *nodes; + amduat_pel_root_ref_t *roots; + + if (out_program == NULL) { + return false; + } + memset(out_program, 0, sizeof(*out_program)); + + nodes = (amduat_pel_node_t *)calloc(3u, sizeof(*nodes)); + roots = (amduat_pel_root_ref_t *)calloc(1u, sizeof(*roots)); + if (nodes == NULL || roots == NULL) { + free(nodes); + free(roots); + return false; + } + + nodes[0].id = 1u; + nodes[0].op.name = amduat_octets(k_op_snapshot, strlen(k_op_snapshot)); + nodes[0].op.version = 1u; + nodes[0].inputs_len = 1u; + nodes[0].inputs = + (amduat_pel_dag_input_t *)calloc(1u, sizeof(*nodes[0].inputs)); + if (nodes[0].inputs == NULL) { + free(nodes); + free(roots); + return false; + } + nodes[0].inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + nodes[0].inputs[0].value.external.input_index = 0u; + nodes[0].params = amduat_octets(NULL, 0u); + + nodes[1].id = 2u; + nodes[1].op.name = amduat_octets(k_op_read_range, strlen(k_op_read_range)); + nodes[1].op.version = 1u; + nodes[1].inputs_len = 3u; + nodes[1].inputs = + (amduat_pel_dag_input_t *)calloc(3u, sizeof(*nodes[1].inputs)); + if (nodes[1].inputs == NULL) { + free(nodes[0].inputs); + free(nodes); + free(roots); + return false; + } + nodes[1].inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + nodes[1].inputs[0].value.external.input_index = 1u; + nodes[1].inputs[1].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + nodes[1].inputs[1].value.external.input_index = 2u; + nodes[1].inputs[2].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + nodes[1].inputs[2].value.external.input_index = 3u; + nodes[1].params = amduat_octets(NULL, 0u); + + nodes[2].id = 3u; + nodes[2].op.name = amduat_octets(k_op_merge, strlen(k_op_merge)); + nodes[2].op.version = 1u; + nodes[2].inputs_len = 4u; + nodes[2].inputs = + (amduat_pel_dag_input_t *)calloc(4u, sizeof(*nodes[2].inputs)); + if (nodes[2].inputs == NULL) { + free(nodes[1].inputs); + free(nodes[0].inputs); + free(nodes); + free(roots); + return false; + } + nodes[2].inputs[0].kind = AMDUAT_PEL_DAG_INPUT_NODE; + nodes[2].inputs[0].value.node.node_id = 1u; + nodes[2].inputs[0].value.node.output_index = 0u; + nodes[2].inputs[1].kind = AMDUAT_PEL_DAG_INPUT_NODE; + nodes[2].inputs[1].value.node.node_id = 2u; + nodes[2].inputs[1].value.node.output_index = 0u; + nodes[2].inputs[2].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + nodes[2].inputs[2].value.external.input_index = 2u; + nodes[2].inputs[3].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + nodes[2].inputs[3].value.external.input_index = 3u; + nodes[2].params = amduat_octets(NULL, 0u); + + roots[0].node_id = 3u; + roots[0].output_index = 0u; + + out_program->nodes = nodes; + out_program->nodes_len = 3u; + out_program->roots = roots; + out_program->roots_len = 1u; + return true; +} + +static bool amduatd_collection_view(amduat_asl_store_t *store, + const char *root_path, + const char *name, + uint64_t from, + uint32_t limit, + amduat_asl_collection_view_t *out_view) { + amduat_asl_pointer_store_t pointer_store; + amduat_reference_t snapshot_ref; + amduat_reference_t log_head_ref; + amduat_reference_t none_ref; + amduat_reference_t params_ref; + amduat_reference_t program_ref; + amduat_reference_t input_refs[4]; + amduat_pel_program_t program; + amduat_octets_t program_bytes = amduat_octets(NULL, 0u); + amduat_pel_run_result_t run_result; + amduat_artifact_t view_artifact; + amduat_asl_collection_view_t view; + amduat_type_tag_t expected_type_tag; + amduat_asl_encoding_profile_id_t expected_profile; + char *snapshot_name = NULL; + char *log_head_name = NULL; + bool snapshot_exists = false; + bool log_head_exists = false; + bool ok = false; + + if (out_view == NULL) { + return false; + } + memset(out_view, 0, sizeof(*out_view)); + memset(&snapshot_ref, 0, sizeof(snapshot_ref)); + memset(&log_head_ref, 0, sizeof(log_head_ref)); + memset(&none_ref, 0, sizeof(none_ref)); + memset(¶ms_ref, 0, sizeof(params_ref)); + memset(&program_ref, 0, sizeof(program_ref)); + memset(&run_result, 0, sizeof(run_result)); + memset(&view, 0, sizeof(view)); + + if (store == NULL || root_path == NULL || name == NULL) { + return false; + } + if (!amduat_asl_pointer_store_init(&pointer_store, root_path)) { + return false; + } + if (!amduatd_get_none_ref(store, &none_ref)) { + return false; + } + if (!amduatd_build_collection_head_name(name, &snapshot_name) || + !amduatd_build_collection_log_head_name(name, &log_head_name)) { + goto view_cleanup; + } + if (amduat_asl_pointer_get(&pointer_store, snapshot_name, + &snapshot_exists, &snapshot_ref) != + AMDUAT_ASL_POINTER_OK) { + goto view_cleanup; + } + if (!snapshot_exists && + !amduat_reference_clone(none_ref, &snapshot_ref)) { + goto view_cleanup; + } + if (amduat_asl_pointer_get(&pointer_store, log_head_name, + &log_head_exists, &log_head_ref) != + AMDUAT_ASL_POINTER_OK) { + goto view_cleanup; + } + if (!log_head_exists && + !amduat_reference_clone(none_ref, &log_head_ref)) { + goto view_cleanup; + } + if (!amduatd_put_view_params(store, from, limit, ¶ms_ref)) { + goto view_cleanup; + } + if (!amduatd_build_collection_view_program(&program)) { + goto view_cleanup; + } + if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) { + amduatd_program_free(&program); + goto view_cleanup; + } + amduatd_program_free(&program); + + if (!amduat_pel_program_dag_desc_get_program_binding(&expected_type_tag, + &expected_profile)) { + goto view_cleanup; + } + if (expected_profile != AMDUAT_PEL_ENC_PROGRAM_DAG_V1) { + goto view_cleanup; + } + { + amduat_artifact_t program_artifact = + amduat_artifact_with_type(program_bytes, expected_type_tag); + if (amduat_asl_store_put(store, program_artifact, &program_ref) != + AMDUAT_ASL_STORE_OK) { + goto view_cleanup; + } + } + + input_refs[0] = snapshot_ref; + input_refs[1] = log_head_ref; + input_refs[2] = params_ref; + input_refs[3] = params_ref; + + if (!amduat_pel_surf_run_with_result(store, + amduat_pel_program_dag_scheme_ref(), + program_ref, + input_refs, + 4u, + true, + params_ref, + &run_result)) { + goto view_cleanup; + } + if (!run_result.has_result_value || + run_result.result_value.core_result.status != AMDUAT_PEL_EXEC_STATUS_OK || + run_result.output_refs_len != 1u) { + goto view_cleanup; + } + + memset(&view_artifact, 0, sizeof(view_artifact)); + if (amduat_asl_store_get(store, run_result.output_refs[0], + &view_artifact) != AMDUAT_ASL_STORE_OK) { + goto view_cleanup; + } + if (!view_artifact.has_type_tag || + view_artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_COLLECTION_VIEW_1 || + !amduat_asl_collection_view_decode_v1(view_artifact.bytes, &view)) { + amduat_artifact_free(&view_artifact); + goto view_cleanup; + } + amduat_artifact_free(&view_artifact); + + *out_view = view; + memset(&view, 0, sizeof(view)); + ok = true; + +view_cleanup: + if (run_result.has_result_value) { + amduat_enc_pel1_result_free(&run_result.result_value); + } + if (run_result.output_refs != NULL) { + amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len); + } + amduat_pel_surf_free_ref(&run_result.result_ref); + amduat_reference_free(&snapshot_ref); + amduat_reference_free(&log_head_ref); + amduat_reference_free(&none_ref); + amduat_reference_free(¶ms_ref); + amduat_reference_free(&program_ref); + amduat_asl_collection_view_free(&view); + free((void *)program_bytes.data); + free(snapshot_name); + free(log_head_name); + return ok; +} + +static bool amduatd_read_file(const char *path, uint8_t **out, size_t *out_len) { + uint8_t *buf = NULL; + size_t cap = 0; + size_t len = 0; + FILE *f; + + if (out != NULL) { + *out = NULL; + } + if (out_len != NULL) { + *out_len = 0; + } + if (path == NULL || out == NULL || out_len == NULL) { + return false; + } + f = fopen(path, "rb"); + if (f == NULL) { + return false; + } + for (;;) { + size_t n; + if (len == cap) { + size_t next_cap = cap != 0 ? cap * 2u : 4096u; + uint8_t *next = (uint8_t *)realloc(buf, next_cap); + if (next == NULL) { + free(buf); + fclose(f); + return false; + } + buf = next; + cap = next_cap; + } + n = fread(buf + len, 1, cap - len, f); + len += n; + if (n == 0) { + if (ferror(f)) { + free(buf); + fclose(f); + return false; + } + break; + } + } + fclose(f); + *out = buf; + *out_len = len; + return true; +} + +static bool amduatd_build_prefixed_bytes(const char *prefix, + const char *text, + uint8_t **out, + size_t *out_len) { + size_t p_len; + size_t t_len; + size_t len; + uint8_t *buf; + + if (out != NULL) { + *out = NULL; + } + if (out_len != NULL) { + *out_len = 0; + } + if (prefix == NULL || text == NULL || out == NULL || out_len == NULL) { + return false; + } + p_len = strlen(prefix); + t_len = strlen(text); + if (p_len > SIZE_MAX - 1u || t_len > SIZE_MAX - (p_len + 1u)) { + return false; + } + len = p_len + 1u + t_len; + buf = (uint8_t *)malloc(len); + if (buf == NULL) { + return false; + } + memcpy(buf, prefix, p_len); + buf[p_len] = 0; + memcpy(buf + p_len + 1u, text, t_len); + *out = buf; + *out_len = len; + return true; +} + +static bool amduatd_concepts_seed_relation(amduat_asl_store_t *store, + const amduatd_space_t *space, + const char *relation_name, + amduat_reference_t *out_ref) { + uint8_t *bytes = NULL; + size_t bytes_len = 0; + amduat_artifact_t artifact; + char *scoped_name = NULL; + amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); + bool ok = false; + + if (out_ref == NULL) { + return false; + } + *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + if (store == NULL || relation_name == NULL) { + return false; + } + if (!amduatd_space_scope_name(space, relation_name, &scoped_bytes)) { + return false; + } + scoped_name = (char *)scoped_bytes.data; + if (!amduatd_build_prefixed_bytes("AMDUATD/RELATION/1", scoped_name, &bytes, + &bytes_len)) { + goto seed_cleanup; + } + artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); + if (!amduat_asl_ref_derive(artifact, + store->config.encoding_profile_id, + store->config.hash_id, + out_ref, + NULL)) { + goto seed_cleanup; + } + (void)amduat_asl_store_put(store, artifact, out_ref); + ok = true; + +seed_cleanup: + free(bytes); + free(scoped_name); + return ok; +} + +static bool amduatd_concepts_ensure_alias(amduatd_concepts_t *c, + amduat_asl_store_t *store, + const amduatd_space_t *space, + const char *alias_name, + amduat_reference_t concept_ref) { + amduat_reference_t existing; + amduat_reference_t name_ref; + amduat_reference_t edge_ref; + char *scoped_name = NULL; + amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); + bool ok = false; + + memset(&existing, 0, sizeof(existing)); + memset(&name_ref, 0, sizeof(name_ref)); + memset(&edge_ref, 0, sizeof(edge_ref)); + + if (c == NULL || store == NULL || alias_name == NULL) { + return false; + } + if (!amduatd_space_scope_name(space, alias_name, &scoped_bytes)) { + amduat_log(AMDUAT_LOG_ERROR, + "ensure alias: scope failed for %s", alias_name); + return false; + } + scoped_name = (char *)scoped_bytes.data; + if (amduatd_concepts_lookup_alias(store, c, scoped_name, &existing)) { + amduat_reference_free(&existing); + ok = true; + goto alias_cleanup; + } + if (!amduatd_concepts_put_name_artifact(store, scoped_name, &name_ref)) { + amduat_log(AMDUAT_LOG_ERROR, + "ensure alias: name artifact failed for %s", scoped_name); + goto alias_cleanup; + } + if (!amduatd_concepts_put_edge(store, + c, + name_ref, + concept_ref, + c->rel_aliases_ref, + amduat_octets(NULL, 0u), + &edge_ref)) { + amduat_log(AMDUAT_LOG_ERROR, + "ensure alias: put edge failed for %s", scoped_name); + goto alias_cleanup; + } + ok = true; + +alias_cleanup: + amduat_reference_free(&name_ref); + amduat_reference_free(&edge_ref); + free(scoped_name); + return ok; +} + +static bool amduatd_concepts_load_edges(amduatd_concepts_t *c, + amduat_asl_store_t *store) { + amduat_asl_collection_view_t view; + uint64_t from = 0u; + bool ok = false; + + if (c == NULL || store == NULL) { + return false; + } + + amduatd_edge_list_clear(&c->edges); + + while (true) { + memset(&view, 0, sizeof(view)); + if (!amduatd_collection_view(store, + c->root_path, + c->edge_collection_name, + from, + AMDUATD_EDGE_VIEW_BATCH, + &view)) { + return false; + } + + for (size_t i = 0u; i < view.refs_len; ++i) { + amduat_reference_t record_ref = view.refs[i]; + amduat_asl_record_t record; + amduat_reference_t src_ref; + amduat_reference_t dst_ref; + char *rel = NULL; + amduatd_edge_entry_t entry; + amduat_asl_store_error_t err; + bool schema_ok = false; + + memset(&record, 0, sizeof(record)); + err = amduat_asl_record_store_get(store, record_ref, &record); + if (err != AMDUAT_ASL_STORE_OK) { + continue; + } + if (record.schema.len == strlen(AMDUATD_EDGE_SCHEMA) && + memcmp(record.schema.data, AMDUATD_EDGE_SCHEMA, + record.schema.len) == 0) { + schema_ok = true; + } + if (!schema_ok || + !amduatd_edge_payload_decode(record.payload, + &src_ref, + &dst_ref, + &rel)) { + amduat_asl_record_free(&record); + continue; + } + amduat_asl_record_free(&record); + + memset(&entry, 0, sizeof(entry)); + if (!amduat_reference_clone(record_ref, &entry.record_ref)) { + amduat_reference_free(&src_ref); + amduat_reference_free(&dst_ref); + free(rel); + goto load_cleanup; + } + entry.src_ref = src_ref; + entry.dst_ref = dst_ref; + entry.rel = rel; + if (!amduatd_edge_list_push(&c->edges, &entry)) { + amduatd_edge_entry_free(&entry); + goto load_cleanup; + } + amduatd_edge_entry_free(&entry); + } + + if (view.refs_len == 0u || view.computed_up_to_offset <= from) { + break; + } + from = view.computed_up_to_offset; + amduat_asl_collection_view_free(&view); + } + + ok = true; + +load_cleanup: + amduat_asl_collection_view_free(&view); + return ok; +} + +static bool amduatd_concepts_append_edge_record(amduatd_concepts_t *c, + amduat_asl_store_t *store, + amduat_reference_t src, + amduat_reference_t dst, + const char *rel, + amduat_octets_t actor, + amduat_reference_t *out_ref) { + amduat_octets_t payload = amduat_octets(NULL, 0u); + amduat_reference_t record_ref; + amduatd_edge_entry_t entry; + uint64_t offset = 0u; + + if (out_ref != NULL) { + *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + } + if (c == NULL || store == NULL || rel == NULL) { + return false; + } + if (!amduatd_edge_payload_encode(src, dst, rel, &payload)) { + return false; + } + if (amduat_asl_record_store_put(store, + amduat_octets(AMDUATD_EDGE_SCHEMA, + strlen(AMDUATD_EDGE_SCHEMA)), + payload, + &record_ref) != AMDUAT_ASL_STORE_OK) { + free((void *)payload.data); + return false; + } + free((void *)payload.data); + { + amduat_asl_collection_error_t append_err = + amduat_asl_collection_append(&c->edge_collection, + c->edge_collection_name, + record_ref, + AMDUATD_EDGE_COLLECTION_KIND, + actor, + &offset); + if (append_err != AMDUAT_ASL_COLLECTION_OK) { + if (c->edge_collection_name != NULL && + !amduat_asl_pointer_name_is_valid(c->edge_collection_name)) { + amduat_log(AMDUAT_LOG_ERROR, "edge collection name invalid: %s", + c->edge_collection_name); + } + if (c->edge_collection_name != NULL) { + size_t name_len = strlen(c->edge_collection_name); + size_t total_len = 11u + name_len + 4u + 1u; + char *log_name = (char *)malloc(total_len); + if (log_name != NULL) { + size_t pos = 0u; + memcpy(log_name + pos, "collection/", 11u); + pos += 11u; + memcpy(log_name + pos, c->edge_collection_name, name_len); + pos += name_len; + memcpy(log_name + pos, "/log", 4u); + pos += 4u; + log_name[pos] = '\0'; + if (!amduat_asl_pointer_name_is_valid(log_name)) { + amduat_log(AMDUAT_LOG_ERROR, "edge log name invalid: %s", log_name); + } + { + size_t head_len = 4u + pos + 5u + 1u; + char *head_name = (char *)malloc(head_len); + if (head_name != NULL) { + size_t hpos = 0u; + memcpy(head_name + hpos, "log/", 4u); + hpos += 4u; + memcpy(head_name + hpos, log_name, pos); + hpos += pos; + memcpy(head_name + hpos, "/head", 5u); + hpos += 5u; + head_name[hpos] = '\0'; + if (!amduat_asl_pointer_name_is_valid(head_name)) { + amduat_log(AMDUAT_LOG_ERROR, + "edge log head name invalid: %s", head_name); + } else { + bool head_exists = false; + amduat_reference_t head_ref; + memset(&head_ref, 0, sizeof(head_ref)); + amduat_asl_pointer_error_t ptr_err = + amduat_asl_pointer_get(&c->edge_collection.pointer_store, + head_name, + &head_exists, + &head_ref); + if (ptr_err != AMDUAT_ASL_POINTER_OK) { + amduat_log(AMDUAT_LOG_ERROR, + "edge log head pointer get failed: %d", ptr_err); + } + amduat_reference_free(&head_ref); + } + free(head_name); + } + } + free(log_name); + } + } + amduat_log(AMDUAT_LOG_ERROR, "edge append failed for %s (err=%d)", + c->edge_collection_name != NULL ? c->edge_collection_name + : "?", + (int)append_err); + amduat_reference_free(&record_ref); + return false; + } + } + + memset(&entry, 0, sizeof(entry)); + entry.record_ref = record_ref; + entry.src_ref = src; + entry.dst_ref = dst; + entry.rel = (char *)rel; + if (!amduatd_edge_list_push(&c->edges, &entry)) { + amduat_reference_free(&record_ref); + return false; + } + + if (offset != 0u && (offset % 256u) == 0u) { + amduat_reference_t snapshot_ref; + bool swapped = false; + memset(&snapshot_ref, 0, sizeof(snapshot_ref)); + (void)amduat_asl_collection_snapshot(&c->edge_collection, + c->edge_collection_name, + offset, + &snapshot_ref, + &swapped); + amduat_reference_free(&snapshot_ref); + } + + if (out_ref != NULL) { + *out_ref = record_ref; + } else { + amduat_reference_free(&record_ref); + } + return true; +} + +static bool amduatd_concepts_migrate_edges(amduatd_concepts_t *c, + amduat_asl_store_t *store) { + uint8_t *file_bytes = NULL; + size_t file_len = 0; + bool ok = true; + + if (c == NULL || store == NULL) { + return false; + } + if (!amduatd_read_file(c->edges_path, &file_bytes, &file_len)) { + return true; + } + + { + char *text = (char *)file_bytes; + char *line = text; + char *end = text + file_len; + while (line < end) { + char *nl = memchr(line, '\n', (size_t)(end - line)); + size_t n = nl != NULL ? (size_t)(nl - line) : (size_t)(end - line); + char *tmp; + amduat_reference_t ref; + bool ok_ref; + + while (n != 0 && (line[n - 1] == '\r' || line[n - 1] == ' ' || + line[n - 1] == '\t')) { + n--; + } + while (n != 0 && (*line == ' ' || *line == '\t')) { + line++; + n--; + } + if (n != 0) { + tmp = (char *)malloc(n + 1u); + if (tmp == NULL) { + ok = false; + break; + } + memcpy(tmp, line, n); + tmp[n] = '\0'; + memset(&ref, 0, sizeof(ref)); + ok_ref = amduat_asl_ref_decode_hex(tmp, &ref); + free(tmp); + if (ok_ref) { + amduat_artifact_t artifact; + amduat_tgk_edge_body_t edge; + const char *rel_name = NULL; + memset(&artifact, 0, sizeof(artifact)); + if (amduat_asl_store_get(store, ref, &artifact) == + AMDUAT_ASL_STORE_OK && + artifact.has_type_tag && + artifact.type_tag.tag_id == AMDUAT_TYPE_TAG_TGK1_EDGE_V1) { + memset(&edge, 0, sizeof(edge)); + if (amduat_enc_tgk1_edge_decode_v1(artifact.bytes, &edge)) { + if (edge.from_len == 1 && edge.to_len == 1) { + rel_name = amduatd_relation_name_for_ref(c, edge.payload); + } + if (rel_name != NULL) { + if (!amduatd_concepts_append_edge_record(c, + store, + edge.from[0], + edge.to[0], + rel_name, + amduat_octets(NULL, 0u), + NULL)) { + ok = false; + } + } + amduat_enc_tgk1_edge_free(&edge); + } + amduat_asl_artifact_free(&artifact); + } else if (artifact.bytes.data != NULL) { + amduat_asl_artifact_free(&artifact); + } + amduat_reference_free(&ref); + if (!ok) { + break; + } + } + } + if (nl == NULL) { + break; + } + line = nl + 1; + } + } + + free(file_bytes); + + if (ok) { + char migrated_path[1100]; + (void)snprintf(migrated_path, + sizeof(migrated_path), + "%s.migrated", + c->edges_path); + (void)rename(c->edges_path, migrated_path); + } + + return ok; +} + +static bool amduatd_concepts_migrate_unscoped_edges( + amduatd_concepts_t *c, + amduat_asl_store_t *store, + const char *unscoped_collection) { + amduat_asl_collection_view_t view; + uint64_t from = 0u; + bool ok = false; + + if (c == NULL || store == NULL || unscoped_collection == NULL) { + return false; + } + + while (true) { + memset(&view, 0, sizeof(view)); + if (!amduatd_collection_view(store, + c->root_path, + unscoped_collection, + from, + AMDUATD_EDGE_VIEW_BATCH, + &view)) { + return false; + } + + for (size_t i = 0u; i < view.refs_len; ++i) { + uint64_t offset = 0u; + if (amduat_asl_collection_append(&c->edge_collection, + c->edge_collection_name, + view.refs[i], + AMDUATD_EDGE_COLLECTION_KIND, + amduat_octets(NULL, 0u), + &offset) != AMDUAT_ASL_COLLECTION_OK) { + goto migrate_cleanup; + } + if (offset != 0u && (offset % 256u) == 0u) { + amduat_reference_t snapshot_ref; + bool swapped = false; + memset(&snapshot_ref, 0, sizeof(snapshot_ref)); + (void)amduat_asl_collection_snapshot(&c->edge_collection, + c->edge_collection_name, + offset, + &snapshot_ref, + &swapped); + amduat_reference_free(&snapshot_ref); + } + } + + if (view.refs_len == 0u || view.computed_up_to_offset <= from) { + break; + } + from = view.computed_up_to_offset; + amduat_asl_collection_view_free(&view); + } + + ok = true; + +migrate_cleanup: + amduat_asl_collection_view_free(&view); + return ok; +} + +bool amduatd_concepts_init(amduatd_concepts_t *c, + amduat_asl_store_t *store, + const amduatd_space_t *space, + const char *root_path, + bool enable_migrations) { + amduat_octets_t scoped_collection = amduat_octets(NULL, 0u); + + if (c == NULL || store == NULL || root_path == NULL) { + return false; + } + memset(c, 0, sizeof(*c)); + c->root_path = root_path; + (void)snprintf(c->edges_path, sizeof(c->edges_path), "%s/%s", root_path, + AMDUATD_EDGES_FILE); + if (!amduatd_space_edges_collection_name(space, &scoped_collection)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: scope edges failed"); + return false; + } + c->edge_collection_name = (char *)scoped_collection.data; + if (c->edge_collection_name == NULL) { + return false; + } + if (!amduat_asl_collection_store_init(&c->edge_collection, root_path, + store)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: collection store init failed"); + return false; + } + + if (!amduatd_concepts_seed_relation(store, space, "aliases", + &c->rel_aliases_ref)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed aliases failed"); + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, space, "ms.aliases", + c->rel_aliases_ref)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: ensure ms.aliases failed"); + return false; + } + if (!amduatd_concepts_seed_relation(store, space, "materializesAs", + &c->rel_materializes_ref)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed materializesAs failed"); + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, space, "ms.materializes_as", + c->rel_materializes_ref)) { + amduat_log(AMDUAT_LOG_ERROR, + "concepts init: ensure ms.materializes_as failed"); + return false; + } + if (!amduatd_concepts_seed_relation(store, space, "represents", + &c->rel_represents_ref)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed represents failed"); + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, space, "ms.represents", + c->rel_represents_ref)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: ensure ms.represents failed"); + return false; + } + if (!amduatd_concepts_seed_relation(store, space, "requiresKey", + &c->rel_requires_key_ref)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed requiresKey failed"); + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, space, "ms.requires_key", + c->rel_requires_key_ref)) { + amduat_log(AMDUAT_LOG_ERROR, + "concepts init: ensure ms.requires_key failed"); + return false; + } + if (!amduatd_concepts_seed_relation(store, space, "withinDomain", + &c->rel_within_domain_ref)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed withinDomain failed"); + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, space, "ms.within_domain", + c->rel_within_domain_ref)) { + amduat_log(AMDUAT_LOG_ERROR, + "concepts init: ensure ms.within_domain failed"); + return false; + } + if (!amduatd_concepts_seed_relation(store, space, "computedBy", + &c->rel_computed_by_ref)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed computedBy failed"); + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, space, "ms.computed_by", + c->rel_computed_by_ref)) { + amduat_log(AMDUAT_LOG_ERROR, + "concepts init: ensure ms.computed_by failed"); + return false; + } + if (!amduatd_concepts_seed_relation(store, space, "hasProvenance", + &c->rel_has_provenance_ref)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed hasProvenance failed"); + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, space, "ms.has_provenance", + c->rel_has_provenance_ref)) { + amduat_log(AMDUAT_LOG_ERROR, + "concepts init: ensure ms.has_provenance failed"); + return false; + } + if (!amduatd_concepts_load_edges(c, store)) { + amduat_log(AMDUAT_LOG_ERROR, "concepts init: load edges failed"); + return false; + } + if (enable_migrations && c->edges.len == 0u) { + if (space != NULL && space->enabled) { + if (amduatd_space_should_migrate_unscoped_edges(space)) { + if (!amduatd_concepts_migrate_unscoped_edges( + c, + store, + AMDUATD_EDGE_COLLECTION)) { + return false; + } + if (!amduatd_concepts_load_edges(c, store)) { + return false; + } + } + } else { + if (!amduatd_concepts_migrate_edges(c, store)) { + return false; + } + if (!amduatd_concepts_load_edges(c, store)) { + return false; + } + } + } + + return true; +} + +static bool amduatd_concepts_derive_name_ref( + amduat_asl_store_t *store, + const char *name, + amduat_reference_t *out_ref) { + uint8_t *bytes = NULL; + size_t bytes_len = 0; + amduat_artifact_t artifact; + + if (out_ref == NULL) { + return false; + } + *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + if (store == NULL || name == NULL) { + return false; + } + if (!amduatd_build_prefixed_bytes("AMDUATD/NAME/1", name, &bytes, + &bytes_len)) { + return false; + } + artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); + if (!amduat_asl_ref_derive(artifact, + store->config.encoding_profile_id, + store->config.hash_id, + out_ref, + NULL)) { + free(bytes); + return false; + } + free(bytes); + return true; +} + +static bool amduatd_concepts_put_name_artifact(amduat_asl_store_t *store, + const char *name, + amduat_reference_t *out_ref) { + uint8_t *bytes = NULL; + size_t bytes_len = 0; + amduat_artifact_t artifact; + + if (out_ref != NULL) { + *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + } + if (store == NULL || name == NULL || out_ref == NULL) { + return false; + } + if (!amduatd_build_prefixed_bytes("AMDUATD/NAME/1", name, &bytes, + &bytes_len)) { + return false; + } + artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); + if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) { + free(bytes); + return false; + } + free(bytes); + return true; +} + +static bool amduatd_concepts_put_concept_id(amduat_asl_store_t *store, + amduat_reference_t *out_ref) { + uint8_t rnd[16]; + uint8_t *bytes = NULL; + size_t bytes_len = 0; + amduat_artifact_t artifact; + + if (out_ref != NULL) { + *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + } + if (store == NULL || out_ref == NULL) { + return false; + } + if (!amduatd_read_urandom(rnd, sizeof(rnd))) { + return false; + } + bytes_len = strlen("AMDUATD/CONCEPT-ID/1") + 1u + sizeof(rnd); + bytes = (uint8_t *)malloc(bytes_len); + if (bytes == NULL) { + return false; + } + memcpy(bytes, "AMDUATD/CONCEPT-ID/1", strlen("AMDUATD/CONCEPT-ID/1")); + bytes[strlen("AMDUATD/CONCEPT-ID/1")] = 0; + memcpy(bytes + strlen("AMDUATD/CONCEPT-ID/1") + 1u, rnd, sizeof(rnd)); + artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); + if (!amduat_asl_ref_derive(artifact, + store->config.encoding_profile_id, + store->config.hash_id, + out_ref, + NULL)) { + free(bytes); + return false; + } + if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) { + free(bytes); + return false; + } + free(bytes); + return true; +} + +static bool amduatd_concepts_put_edge(amduat_asl_store_t *store, + amduatd_concepts_t *c, + amduat_reference_t from, + amduat_reference_t to, + amduat_reference_t relation_concept_ref, + amduat_octets_t actor, + amduat_reference_t *out_edge_ref) { + const char *rel_name = NULL; + + if (out_edge_ref != NULL) { + *out_edge_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + } + if (store == NULL || c == NULL || out_edge_ref == NULL) { + return false; + } + rel_name = amduatd_relation_name_for_ref(c, relation_concept_ref); + if (rel_name == NULL) { + return false; + } + return amduatd_concepts_append_edge_record(c, + store, + from, + to, + rel_name, + actor, + out_edge_ref); +} + +static bool amduatd_concepts_lookup_alias(amduat_asl_store_t *store, + const amduatd_concepts_t *c, + const char *name, + amduat_reference_t *out_concept_ref) { + amduat_reference_t name_ref; + size_t i; + + if (out_concept_ref != NULL) { + *out_concept_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + } + if (store == NULL || c == NULL || name == NULL || out_concept_ref == NULL) { + return false; + } + if (!amduatd_concepts_derive_name_ref(store, name, &name_ref)) { + return false; + } + + for (i = c->edges.len; i > 0; --i) { + const amduatd_edge_entry_t *entry = &c->edges.items[i - 1u]; + if (entry->rel == NULL) { + continue; + } + if (strcmp(entry->rel, AMDUATD_REL_ALIAS) != 0) { + continue; + } + if (amduat_reference_eq(entry->src_ref, name_ref)) { + amduat_reference_clone(entry->dst_ref, out_concept_ref); + amduat_reference_free(&name_ref); + return true; + } + } + + amduat_reference_free(&name_ref); + return false; +} + +static bool amduatd_concepts_resolve_latest(amduat_asl_store_t *store, + const amduatd_concepts_t *c, + amduat_reference_t concept_ref, + amduat_reference_t *out_ref) { + size_t i; + + if (out_ref != NULL) { + *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + } + if (store == NULL || c == NULL || out_ref == NULL) { + return false; + } + + for (i = c->edges.len; i > 0; --i) { + const amduatd_edge_entry_t *entry = &c->edges.items[i - 1u]; + if (entry->rel == NULL) { + continue; + } + if (strcmp(entry->rel, AMDUATD_REL_MATERIALIZES) != 0) { + continue; + } + if (amduat_reference_eq(entry->src_ref, concept_ref)) { + amduat_reference_clone(entry->dst_ref, out_ref); + return true; + } + } + + return false; +} + +static bool amduatd_parse_name_artifact(amduat_artifact_t artifact, + const amduatd_cfg_t *dcfg, + char *out, + size_t cap) { + const uint8_t *bytes; + size_t len; + const char *prefix = "AMDUATD/NAME/1"; + size_t prefix_len; + size_t i; + size_t name_len; + char scoped[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + amduat_octets_t unscoped = amduat_octets(NULL, 0u); + const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; + + if (out != NULL && cap != 0) { + out[0] = '\0'; + } + if (out == NULL || cap == 0) { + return false; + } + if (artifact.bytes.len != 0 && artifact.bytes.data == NULL) { + return false; + } + bytes = artifact.bytes.data; + len = artifact.bytes.len; + prefix_len = strlen(prefix); + if (len < prefix_len + 1u) { + return false; + } + if (memcmp(bytes, prefix, prefix_len) != 0 || bytes[prefix_len] != 0) { + return false; + } + for (i = prefix_len + 1u; i < len; ++i) { + if (bytes[i] == 0) { + return false; + } + } + name_len = len - (prefix_len + 1u); + if (name_len == 0 || name_len >= sizeof(scoped)) { + return false; + } + memcpy(scoped, bytes + prefix_len + 1u, name_len); + scoped[name_len] = '\0'; + if (!amduatd_space_unscoped_name(space, scoped, &unscoped)) { + return false; + } + if (unscoped.len >= cap) { + free((void *)unscoped.data); + return false; + } + if (unscoped.len != 0u) { + memcpy(out, unscoped.data, unscoped.len); + } + out[unscoped.len] = '\0'; + free((void *)unscoped.data); + return true; +} + +static bool amduatd_strbuf_reserve(amduatd_strbuf_t *b, size_t extra); +static bool amduatd_strbuf_append(amduatd_strbuf_t *b, + const char *data, + size_t n); +static void amduatd_strbuf_free(amduatd_strbuf_t *b); +static bool amduatd_strbuf_append_cstr(amduatd_strbuf_t *b, const char *s); +static bool amduatd_strbuf_append_char(amduatd_strbuf_t *b, char c); + +static bool amduatd_handle_get_concepts(int fd, + amduat_asl_store_t *store, + const amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg) { + amduatd_strbuf_t b; + bool first = true; + size_t i; + + if (store == NULL || concepts == NULL) { + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "internal error"); + } + memset(&b, 0, sizeof(b)); + if (!amduatd_strbuf_append_cstr(&b, "{\"concepts\":[")) { + amduatd_strbuf_free(&b); + return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); + } + + for (i = 0; i < concepts->edges.len; ++i) { + const amduatd_edge_entry_t *entry = &concepts->edges.items[i]; + amduat_artifact_t artifact; + amduat_asl_store_error_t err; + char name[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + char *concept_hex = NULL; + + if (entry->rel == NULL || + strcmp(entry->rel, AMDUATD_REL_ALIAS) != 0) { + continue; + } + + memset(&artifact, 0, sizeof(artifact)); + err = amduat_asl_store_get(store, entry->src_ref, &artifact); + if (err != AMDUAT_ASL_STORE_OK || + !amduatd_parse_name_artifact(artifact, dcfg, name, sizeof(name))) { + amduat_asl_artifact_free(&artifact); + continue; + } + amduat_asl_artifact_free(&artifact); + + if (!amduat_asl_ref_encode_hex(entry->dst_ref, &concept_hex)) { + continue; + } + + if (!first) { + (void)amduatd_strbuf_append_char(&b, ','); + } + first = false; + + (void)amduatd_strbuf_append_cstr(&b, "{\"name\":\""); + (void)amduatd_strbuf_append_cstr(&b, name); + (void)amduatd_strbuf_append_cstr(&b, "\",\"concept_ref\":\""); + (void)amduatd_strbuf_append_cstr(&b, concept_hex); + (void)amduatd_strbuf_append_cstr(&b, "\"}"); + + free(concept_hex); + } + + if (!amduatd_strbuf_append_cstr(&b, "]}\n")) { + amduatd_strbuf_free(&b); + return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); + } + { + bool ok = amduatd_http_send_json(fd, 200, "OK", b.data, false); + amduatd_strbuf_free(&b); + return ok; + } +} + +static bool amduatd_append_relation_entry(amduatd_strbuf_t *b, + bool *first, + const char *name, + amduat_reference_t ref) { + char *hex = NULL; + if (b == NULL || first == NULL || name == NULL) { + return false; + } + if (!amduat_asl_ref_encode_hex(ref, &hex)) { + return false; + } + if (!*first) { + (void)amduatd_strbuf_append_char(b, ','); + } + *first = false; + (void)amduatd_strbuf_append_cstr(b, "{\"name\":\""); + (void)amduatd_strbuf_append_cstr(b, name); + (void)amduatd_strbuf_append_cstr(b, "\",\"concept_ref\":\""); + (void)amduatd_strbuf_append_cstr(b, hex); + (void)amduatd_strbuf_append_cstr(b, "\"}"); + free(hex); + return true; +} + +static bool amduatd_handle_get_relations(int fd, + const amduatd_concepts_t *concepts) { + amduatd_strbuf_t b; + bool first = true; + + if (concepts == NULL) { + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "internal error"); + } + memset(&b, 0, sizeof(b)); + if (!amduatd_strbuf_append_cstr(&b, "{\"relations\":[")) { + amduatd_strbuf_free(&b); + return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); + } + if (!amduatd_append_relation_entry(&b, &first, "ms.aliases", + concepts->rel_aliases_ref) || + !amduatd_append_relation_entry(&b, &first, "ms.materializes_as", + concepts->rel_materializes_ref) || + !amduatd_append_relation_entry(&b, &first, "ms.represents", + concepts->rel_represents_ref) || + !amduatd_append_relation_entry(&b, &first, "ms.requires_key", + concepts->rel_requires_key_ref) || + !amduatd_append_relation_entry(&b, &first, "ms.within_domain", + concepts->rel_within_domain_ref) || + !amduatd_append_relation_entry(&b, &first, "ms.computed_by", + concepts->rel_computed_by_ref) || + !amduatd_append_relation_entry(&b, &first, "ms.has_provenance", + concepts->rel_has_provenance_ref)) { + amduatd_strbuf_free(&b); + return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); + } + (void)amduatd_strbuf_append_cstr(&b, "]}\n"); + + { + bool ok = amduatd_http_send_json(fd, 200, "OK", b.data, false); + amduatd_strbuf_free(&b); + return ok; + } +} + +static bool amduatd_handle_get_concept(int fd, + amduat_asl_store_t *store, + const amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg, + const char *name) { + amduat_reference_t concept_ref; + amduat_reference_t latest_ref; + amduatd_strbuf_t b; + char *concept_hex = NULL; + char *latest_hex = NULL; + char *scoped_name = NULL; + amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); + const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; + bool have_latest = false; + size_t i; + size_t version_count = 0; + + memset(&concept_ref, 0, sizeof(concept_ref)); + memset(&latest_ref, 0, sizeof(latest_ref)); + memset(&b, 0, sizeof(b)); + + if (store == NULL || concepts == NULL || name == NULL) { + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "internal error"); + } + if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); + } + scoped_name = (char *)scoped_bytes.data; + amduatd_space_log_mapping(space, name, scoped_name); + if (!amduatd_concepts_lookup_alias(store, concepts, scoped_name, + &concept_ref)) { + free(scoped_name); + return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept"); + } + free(scoped_name); + + if (amduatd_concepts_resolve_latest(store, concepts, concept_ref, + &latest_ref)) { + have_latest = true; + } + + if (!amduat_asl_ref_encode_hex(concept_ref, &concept_hex)) { + amduat_reference_free(&concept_ref); + amduat_reference_free(&latest_ref); + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "encode error"); + } + if (have_latest) { + if (!amduat_asl_ref_encode_hex(latest_ref, &latest_hex)) { + free(concept_hex); + amduat_reference_free(&concept_ref); + amduat_reference_free(&latest_ref); + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "encode error"); + } + } + + if (!amduatd_strbuf_append_cstr(&b, "{")) { + free(concept_hex); + free(latest_hex); + amduat_reference_free(&concept_ref); + amduat_reference_free(&latest_ref); + return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); + } + (void)amduatd_strbuf_append_cstr(&b, "\"name\":\""); + (void)amduatd_strbuf_append_cstr(&b, name); + (void)amduatd_strbuf_append_cstr(&b, "\",\"concept_ref\":\""); + (void)amduatd_strbuf_append_cstr(&b, concept_hex); + (void)amduatd_strbuf_append_cstr(&b, "\",\"latest_ref\":"); + if (latest_hex != NULL) { + (void)amduatd_strbuf_append_cstr(&b, "\""); + (void)amduatd_strbuf_append_cstr(&b, latest_hex); + (void)amduatd_strbuf_append_cstr(&b, "\""); + } else { + (void)amduatd_strbuf_append_cstr(&b, "null"); + } + (void)amduatd_strbuf_append_cstr(&b, ",\"versions\":["); + + for (i = 0; i < concepts->edges.len; ++i) { + const amduatd_edge_entry_t *entry = &concepts->edges.items[i]; + char *edge_hex = NULL; + char *ref_hex = NULL; + if (entry->rel == NULL || + strcmp(entry->rel, AMDUATD_REL_MATERIALIZES) != 0) { + continue; + } + if (!amduat_reference_eq(entry->src_ref, concept_ref)) { + continue; + } + if (!amduat_asl_ref_encode_hex(entry->record_ref, &edge_hex) || + !amduat_asl_ref_encode_hex(entry->dst_ref, &ref_hex)) { + free(edge_hex); + free(ref_hex); + continue; + } + + if (version_count != 0) { + (void)amduatd_strbuf_append_char(&b, ','); + } + version_count++; + + (void)amduatd_strbuf_append_cstr(&b, "{\"edge_ref\":\""); + (void)amduatd_strbuf_append_cstr(&b, edge_hex); + (void)amduatd_strbuf_append_cstr(&b, "\",\"ref\":\""); + (void)amduatd_strbuf_append_cstr(&b, ref_hex); + (void)amduatd_strbuf_append_cstr(&b, "\"}"); + + free(edge_hex); + free(ref_hex); + + if (version_count >= 64u) { + break; + } + } + + (void)amduatd_strbuf_append_cstr(&b, "]}\n"); + free(concept_hex); + free(latest_hex); + amduat_reference_free(&concept_ref); + amduat_reference_free(&latest_ref); + + { + bool ok = amduatd_http_send_json(fd, 200, "OK", b.data, false); + amduatd_strbuf_free(&b); + return ok; + } +} + +static bool amduatd_decode_ref_hex_str(const char *s, + size_t len, + amduat_reference_t *out_ref) { + char *tmp = NULL; + bool ok; + if (out_ref == NULL) { + return false; + } + memset(out_ref, 0, sizeof(*out_ref)); + if (!amduatd_copy_json_str(s, len, &tmp)) { + return false; + } + ok = amduat_asl_ref_decode_hex(tmp, out_ref); + free(tmp); + return ok; +} + +amduatd_ref_status_t amduatd_decode_ref_or_name_latest( + amduat_asl_store_t *store, + const amduat_asl_store_fs_config_t *cfg, + const amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg, + const char *s, + size_t len, + amduat_reference_t *out_ref) { + amduat_reference_t concept_ref; + char *tmp = NULL; + char *scoped_name = NULL; + amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); + const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; + + (void)cfg; + if (out_ref != NULL) { + memset(out_ref, 0, sizeof(*out_ref)); + } + if (store == NULL || cfg == NULL || concepts == NULL || s == NULL || + out_ref == NULL) { + return AMDUATD_REF_ERR_INTERNAL; + } + + if (amduatd_decode_ref_hex_str(s, len, out_ref)) { + return AMDUATD_REF_OK; + } + + if (!amduatd_copy_json_str(s, len, &tmp)) { + return AMDUATD_REF_ERR_INTERNAL; + } + if (!amduatd_space_scope_name(space, tmp, &scoped_bytes)) { + free(tmp); + return AMDUATD_REF_ERR_INVALID; + } + scoped_name = (char *)scoped_bytes.data; + amduatd_space_log_mapping(space, tmp, scoped_name); + memset(&concept_ref, 0, sizeof(concept_ref)); + if (!amduatd_concepts_lookup_alias(store, + concepts, + scoped_name, + &concept_ref)) { + free(scoped_name); + free(tmp); + return AMDUATD_REF_ERR_NOT_FOUND; + } + free(scoped_name); + free(tmp); + + if (!amduatd_concepts_resolve_latest(store, concepts, concept_ref, out_ref)) { + amduat_reference_free(&concept_ref); + return AMDUATD_REF_ERR_NOT_FOUND; + } + amduat_reference_free(&concept_ref); + return AMDUATD_REF_OK; +} + +static bool amduatd_seed_concept_if_missing( + amduat_asl_store_t *store, + amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg, + const char *name, + amduat_reference_t *out_concept_ref) { + amduat_reference_t name_ref; + amduat_reference_t concept_ref; + amduat_reference_t edge_ref; + char *scoped_name = NULL; + amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); + const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; + bool ok = false; + + if (out_concept_ref != NULL) { + *out_concept_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + } + if (store == NULL || concepts == NULL || name == NULL || + out_concept_ref == NULL) { + return false; + } + if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { + return false; + } + scoped_name = (char *)scoped_bytes.data; + if (amduatd_concepts_lookup_alias(store, concepts, scoped_name, + out_concept_ref)) { + ok = true; + goto seed_cleanup; + } + + memset(&name_ref, 0, sizeof(name_ref)); + memset(&concept_ref, 0, sizeof(concept_ref)); + memset(&edge_ref, 0, sizeof(edge_ref)); + + if (!amduatd_concepts_put_name_artifact(store, scoped_name, &name_ref)) { + goto seed_cleanup; + } + if (!amduatd_concepts_put_concept_id(store, &concept_ref)) { + goto seed_cleanup; + } + if (!amduatd_concepts_put_edge(store, + concepts, + name_ref, + concept_ref, + concepts->rel_aliases_ref, + amduat_octets(NULL, 0u), + &edge_ref)) { + goto seed_cleanup; + } + *out_concept_ref = concept_ref; + ok = true; + goto seed_cleanup; + +seed_cleanup: + if (!ok) { + amduat_reference_free(&concept_ref); + } + amduat_reference_free(&name_ref); + amduat_reference_free(&edge_ref); + free(scoped_name); + return ok; +} + +static bool amduatd_seed_store_artifact(amduat_asl_store_t *store, + amduat_artifact_t artifact, + amduat_reference_t *out_ref) { + if (out_ref != NULL) { + *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + } + if (store == NULL || out_ref == NULL) { + return false; + } + if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) { + return false; + } + return true; +} + +static bool amduatd_seed_pel_identity_program( + amduat_asl_store_t *store, + amduat_reference_t *out_program_ref) { + amduat_pel_program_t program; + amduat_pel_node_t node; + amduat_pel_dag_input_t inputs[1]; + amduat_pel_root_ref_t root; + amduat_octets_t program_bytes; + amduat_artifact_t artifact; + + if (out_program_ref != NULL) { + *out_program_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + } + if (store == NULL || out_program_ref == NULL) { + return false; + } + + memset(&program, 0, sizeof(program)); + memset(&node, 0, sizeof(node)); + memset(inputs, 0, sizeof(inputs)); + memset(&root, 0, sizeof(root)); + program_bytes = amduat_octets(NULL, 0u); + + inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + inputs[0].value.external.input_index = 0; + + node.id = 1; + node.op.name = amduat_octets("pel.bytes.concat", + strlen("pel.bytes.concat")); + node.op.version = 1; + node.inputs = inputs; + node.inputs_len = 1; + node.params = amduat_octets(NULL, 0u); + + root.node_id = 1; + root.output_index = 0; + + program.nodes = &node; + program.nodes_len = 1; + program.roots = &root; + program.roots_len = 1; + + if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) { + return false; + } + artifact = amduat_artifact_with_type(program_bytes, + amduat_type_tag(0x00000101u)); + if (!amduatd_seed_store_artifact(store, artifact, out_program_ref)) { + free((void *)program_bytes.data); + return false; + } + free((void *)program_bytes.data); + return true; +} + +static bool amduatd_seed_materializes_if_missing( + amduat_asl_store_t *store, + amduatd_concepts_t *concepts, + amduat_reference_t concept_ref, + amduat_reference_t target_ref) { + amduat_reference_t latest; + amduat_reference_t edge_ref; + + memset(&latest, 0, sizeof(latest)); + if (amduatd_concepts_resolve_latest(store, concepts, concept_ref, &latest)) { + amduat_reference_free(&latest); + return true; + } + if (!amduatd_concepts_put_edge(store, + concepts, + concept_ref, + target_ref, + concepts->rel_materializes_ref, + amduat_octets(NULL, 0u), + &edge_ref)) { + return false; + } + amduat_reference_free(&edge_ref); + return true; +} + +static bool amduatd_bytes_contains(const uint8_t *data, + size_t len, + const char *needle) { + size_t needle_len; + if (data == NULL || needle == NULL) { + return false; + } + needle_len = strlen(needle); + if (needle_len == 0 || needle_len > len) { + return false; + } + for (size_t i = 0; i + needle_len <= len; ++i) { + if (memcmp(data + i, needle, needle_len) == 0) { + return true; + } + } + return false; +} + +typedef struct { + char *key_hex; + char *type_hex; + char *key_text; + char *value_hex; +} amduatd_seed_entry_t; + +static int amduatd_seed_entry_cmp(const void *a, const void *b) { + const amduatd_seed_entry_t *x = (const amduatd_seed_entry_t *)a; + const amduatd_seed_entry_t *y = (const amduatd_seed_entry_t *)b; + if (x == NULL || y == NULL) { + return 0; + } + return strcmp(x->key_hex != NULL ? x->key_hex : "", + y->key_hex != NULL ? y->key_hex : ""); +} + +bool amduatd_seed_ms_ui_state(amduat_asl_store_t *store, + amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg) { + amduat_reference_t type_string_ref; + amduat_reference_t type_ref_ref; + amduat_reference_t key_title_ref; + amduat_reference_t key_status_ref; + amduat_reference_t key_latest_ref; + amduat_reference_t schema_concept_ref; + amduat_reference_t registry_concept_ref; + amduat_reference_t identity_program_ref; + amduat_reference_t schema_input_ref; + amduat_reference_t registry_input_ref; + amduat_reference_t schema_output_ref; + amduat_reference_t registry_output_ref; + amduat_reference_t seed_manifest_ref; + amduat_reference_t seed_environment_ref; + amduat_reference_t seed_executor_ref; + amduat_reference_t receipt_ref; + amduat_pel_run_result_t run_result; + amduatd_seed_entry_t fields[3]; + amduatd_seed_entry_t entries[3]; + amduatd_strbuf_t b; + amduat_artifact_t artifact; + char *seed_latest_hex = NULL; + size_t i; + bool ok = false; + + memset(&type_string_ref, 0, sizeof(type_string_ref)); + memset(&type_ref_ref, 0, sizeof(type_ref_ref)); + memset(&key_title_ref, 0, sizeof(key_title_ref)); + memset(&key_status_ref, 0, sizeof(key_status_ref)); + memset(&key_latest_ref, 0, sizeof(key_latest_ref)); + memset(&schema_concept_ref, 0, sizeof(schema_concept_ref)); + memset(®istry_concept_ref, 0, sizeof(registry_concept_ref)); + memset(&identity_program_ref, 0, sizeof(identity_program_ref)); + memset(&schema_input_ref, 0, sizeof(schema_input_ref)); + memset(®istry_input_ref, 0, sizeof(registry_input_ref)); + memset(&schema_output_ref, 0, sizeof(schema_output_ref)); + memset(®istry_output_ref, 0, sizeof(registry_output_ref)); + memset(&seed_manifest_ref, 0, sizeof(seed_manifest_ref)); + memset(&seed_environment_ref, 0, sizeof(seed_environment_ref)); + memset(&seed_executor_ref, 0, sizeof(seed_executor_ref)); + memset(&receipt_ref, 0, sizeof(receipt_ref)); + memset(&run_result, 0, sizeof(run_result)); + memset(fields, 0, sizeof(fields)); + memset(entries, 0, sizeof(entries)); + memset(&b, 0, sizeof(b)); + memset(&artifact, 0, sizeof(artifact)); + + if (!amduatd_seed_concept_if_missing(store, concepts, dcfg, + "ms.type.string", + &type_string_ref) || + !amduatd_seed_concept_if_missing(store, concepts, dcfg, + "ms.type.ref", + &type_ref_ref) || + !amduatd_seed_concept_if_missing(store, concepts, dcfg, + "ms.ui.field.title", + &key_title_ref) || + !amduatd_seed_concept_if_missing(store, concepts, dcfg, + "ms.ui.field.status", + &key_status_ref) || + !amduatd_seed_concept_if_missing(store, concepts, dcfg, + "ms.ui.field.latest_ref", + &key_latest_ref) || + !amduatd_seed_concept_if_missing(store, concepts, dcfg, + "ms.ui.state.schema.v1", + &schema_concept_ref) || + !amduatd_seed_concept_if_missing(store, concepts, dcfg, + "ms.ui.state.registry.v1", + ®istry_concept_ref)) { + goto seed_cleanup; + } + + if (!amduatd_seed_pel_identity_program(store, &identity_program_ref)) { + goto seed_cleanup; + } + + { + amduat_reference_t latest_schema; + amduat_reference_t latest_registry; + bool have_schema = false; + bool have_registry = false; + bool schema_ok = false; + bool registry_ok = false; + + memset(&latest_schema, 0, sizeof(latest_schema)); + memset(&latest_registry, 0, sizeof(latest_registry)); + have_schema = amduatd_concepts_resolve_latest(store, + concepts, + schema_concept_ref, + &latest_schema); + have_registry = amduatd_concepts_resolve_latest(store, + concepts, + registry_concept_ref, + &latest_registry); + if (have_schema && have_registry) { + amduat_artifact_t schema_artifact; + amduat_artifact_t registry_artifact; + amduat_asl_store_error_t err; + + memset(&schema_artifact, 0, sizeof(schema_artifact)); + memset(®istry_artifact, 0, sizeof(registry_artifact)); + err = amduat_asl_store_get(store, latest_schema, &schema_artifact); + if (err == AMDUAT_ASL_STORE_OK && + schema_artifact.bytes.len != 0 && + schema_artifact.bytes.data != NULL && + amduatd_bytes_contains(schema_artifact.bytes.data, + schema_artifact.bytes.len, + "\"key_text\"")) { + schema_ok = true; + } + err = amduat_asl_store_get(store, latest_registry, ®istry_artifact); + if (err == AMDUAT_ASL_STORE_OK && + registry_artifact.bytes.len != 0 && + registry_artifact.bytes.data != NULL && + amduatd_bytes_contains(registry_artifact.bytes.data, + registry_artifact.bytes.len, + "\"value_text\"")) { + registry_ok = true; + } + amduat_asl_artifact_free(&schema_artifact); + amduat_asl_artifact_free(®istry_artifact); + } + amduat_reference_free(&latest_schema); + amduat_reference_free(&latest_registry); + if (have_schema && have_registry && schema_ok && registry_ok) { + ok = true; + goto seed_cleanup; + } + } + + { + const char *payload = "{\"seed\":\"manifest\"}"; + artifact = amduat_artifact(amduat_octets(payload, strlen(payload))); + if (!amduatd_seed_store_artifact(store, artifact, &seed_manifest_ref)) { + goto seed_cleanup; + } + } + { + const char *payload = "{\"seed\":\"environment\"}"; + artifact = amduat_artifact(amduat_octets(payload, strlen(payload))); + if (!amduatd_seed_store_artifact(store, artifact, &seed_environment_ref)) { + goto seed_cleanup; + } + } + { + const char *payload = "{\"seed\":\"executor\"}"; + artifact = amduat_artifact(amduat_octets(payload, strlen(payload))); + if (!amduatd_seed_store_artifact(store, artifact, &seed_executor_ref)) { + goto seed_cleanup; + } + } + + { + amduat_reference_t hello_ref; + amduat_reference_t title_ref; + amduat_reference_t status_ref; + char *hello_hex = NULL; + const char *payload = "hello"; + + memset(&hello_ref, 0, sizeof(hello_ref)); + memset(&title_ref, 0, sizeof(title_ref)); + memset(&status_ref, 0, sizeof(status_ref)); + + artifact = amduat_artifact(amduat_octets(payload, strlen(payload))); + if (!amduatd_seed_store_artifact(store, artifact, &hello_ref)) { + goto seed_cleanup; + } + if (!amduat_asl_ref_encode_hex(hello_ref, &hello_hex)) { + goto seed_cleanup; + } + seed_latest_hex = hello_hex; + + artifact = amduat_artifact(amduat_octets("Amduat UI", + strlen("Amduat UI"))); + if (!amduatd_seed_store_artifact(store, artifact, &title_ref)) { + free(hello_hex); + goto seed_cleanup; + } + + artifact = amduat_artifact(amduat_octets("ready", strlen("ready"))); + if (!amduatd_seed_store_artifact(store, artifact, &status_ref)) { + free(hello_hex); + goto seed_cleanup; + } + + fields[0].key_hex = NULL; + fields[1].key_hex = NULL; + fields[2].key_hex = NULL; + if (!amduat_asl_ref_encode_hex(key_title_ref, &fields[0].key_hex) || + !amduat_asl_ref_encode_hex(key_status_ref, &fields[1].key_hex) || + !amduat_asl_ref_encode_hex(key_latest_ref, &fields[2].key_hex)) { + goto seed_cleanup; + } + fields[0].type_hex = strdup("ms.type.string"); + fields[1].type_hex = strdup("ms.type.string"); + fields[2].type_hex = strdup("ms.type.ref"); + if (fields[0].type_hex == NULL || fields[1].type_hex == NULL || + fields[2].type_hex == NULL) { + goto seed_cleanup; + } + fields[0].key_text = strdup("title"); + fields[1].key_text = strdup("status"); + fields[2].key_text = strdup("latest_ref"); + if (fields[0].key_text == NULL || fields[1].key_text == NULL || + fields[2].key_text == NULL) { + goto seed_cleanup; + } + + entries[0].key_hex = fields[0].key_hex; + entries[1].key_hex = fields[1].key_hex; + entries[2].key_hex = fields[2].key_hex; + + entries[0].value_hex = strdup("Amduat UI"); + entries[1].value_hex = strdup("ready"); + entries[2].value_hex = hello_hex; + if (entries[0].value_hex == NULL || entries[1].value_hex == NULL || + entries[2].value_hex == NULL) { + goto seed_cleanup; + } + hello_hex = NULL; + seed_latest_hex = NULL; + + qsort(fields, 3, sizeof(fields[0]), amduatd_seed_entry_cmp); + qsort(entries, 3, sizeof(entries[0]), amduatd_seed_entry_cmp); + + if (!amduatd_strbuf_append_cstr(&b, + "{\"schema_version\":1,\"fields\":[")) { + goto seed_cleanup; + } + for (i = 0; i < 3; ++i) { + if (i != 0 && !amduatd_strbuf_append_char(&b, ',')) { + goto seed_cleanup; + } + if (!amduatd_strbuf_append_cstr(&b, "{\"key_ref\":\"") || + !amduatd_strbuf_append_cstr(&b, fields[i].key_hex) || + !amduatd_strbuf_append_cstr(&b, "\",\"key_text\":\"") || + !amduatd_strbuf_append_cstr(&b, fields[i].key_text) || + !amduatd_strbuf_append_cstr(&b, "\",\"type_ref\":\"") || + !amduatd_strbuf_append_cstr(&b, fields[i].type_hex) || + !amduatd_strbuf_append_cstr(&b, "\"}")) { + goto seed_cleanup; + } + } + if (!amduatd_strbuf_append_cstr(&b, "]}")) { + goto seed_cleanup; + } + artifact = amduat_artifact(amduat_octets(b.data, b.len)); + if (!amduatd_seed_store_artifact(store, artifact, &schema_input_ref)) { + goto seed_cleanup; + } + amduatd_strbuf_free(&b); + + memset(&b, 0, sizeof(b)); + if (!amduatd_strbuf_append_cstr(&b, + "{\"registry_version\":1,\"entries\":[")) { + goto seed_cleanup; + } + for (i = 0; i < 3; ++i) { + if (i != 0 && !amduatd_strbuf_append_char(&b, ',')) { + goto seed_cleanup; + } + if (!amduatd_strbuf_append_cstr(&b, "{\"key_ref\":\"") || + !amduatd_strbuf_append_cstr(&b, entries[i].key_hex) || + !amduatd_strbuf_append_cstr(&b, + i == 2 ? "\",\"value_ref\":\"" + : "\",\"value_text\":\"") || + !amduatd_strbuf_append_cstr(&b, entries[i].value_hex) || + !amduatd_strbuf_append_cstr(&b, "\"}")) { + goto seed_cleanup; + } + } + if (!amduatd_strbuf_append_cstr(&b, "]}")) { + goto seed_cleanup; + } + artifact = amduat_artifact(amduat_octets(b.data, b.len)); + if (!amduatd_seed_store_artifact(store, artifact, ®istry_input_ref)) { + goto seed_cleanup; + } + } + + { + amduat_reference_t inputs[1]; + amduat_reference_t scheme_ref = amduat_pel_program_dag_scheme_ref(); + amduat_artifact_t receipt_artifact; + amduat_reference_t edge_ref; + amduat_octets_t evaluator_id = amduat_octets("amduatd-seed", + strlen("amduatd-seed")); + + inputs[0] = schema_input_ref; + if (!amduat_pel_surf_run_with_result(store, + scheme_ref, + identity_program_ref, + inputs, + 1, + false, + amduat_reference(0u, + amduat_octets(NULL, 0u)), + &run_result)) { + goto seed_cleanup; + } + if (!run_result.has_result_value || run_result.output_refs_len != 1) { + goto seed_cleanup; + } + if (!amduat_reference_clone(run_result.output_refs[0], + &schema_output_ref)) { + goto seed_cleanup; + } + + if (!amduat_fer1_receipt_from_pel_result(&run_result.result_value, + seed_manifest_ref, + seed_environment_ref, + evaluator_id, + seed_executor_ref, + false, + amduat_reference(0u, + amduat_octets(NULL, 0u)), + amduat_octets(NULL, 0u), + 0, + 0, + &receipt_artifact)) { + goto seed_cleanup; + } + if (!amduatd_seed_store_artifact(store, receipt_artifact, &receipt_ref)) { + amduat_asl_artifact_free(&receipt_artifact); + goto seed_cleanup; + } + amduat_asl_artifact_free(&receipt_artifact); + if (!amduatd_concepts_put_edge(store, + concepts, + schema_output_ref, + receipt_ref, + concepts->rel_has_provenance_ref, + amduat_octets(NULL, 0u), + &edge_ref)) { + goto seed_cleanup; + } + amduat_reference_free(&edge_ref); + amduat_reference_free(&receipt_ref); + amduat_enc_pel1_result_free(&run_result.result_value); + amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len); + amduat_pel_surf_free_ref(&run_result.result_ref); + memset(&run_result, 0, sizeof(run_result)); + } + + { + amduat_reference_t inputs[1]; + amduat_reference_t scheme_ref = amduat_pel_program_dag_scheme_ref(); + amduat_artifact_t receipt_artifact; + amduat_reference_t edge_ref; + amduat_octets_t evaluator_id = amduat_octets("amduatd-seed", + strlen("amduatd-seed")); + + inputs[0] = registry_input_ref; + if (!amduat_pel_surf_run_with_result(store, + scheme_ref, + identity_program_ref, + inputs, + 1, + false, + amduat_reference(0u, + amduat_octets(NULL, 0u)), + &run_result)) { + goto seed_cleanup; + } + if (!run_result.has_result_value || run_result.output_refs_len != 1) { + goto seed_cleanup; + } + if (!amduat_reference_clone(run_result.output_refs[0], + ®istry_output_ref)) { + goto seed_cleanup; + } + + if (!amduat_fer1_receipt_from_pel_result(&run_result.result_value, + seed_manifest_ref, + seed_environment_ref, + evaluator_id, + seed_executor_ref, + false, + amduat_reference(0u, + amduat_octets(NULL, 0u)), + amduat_octets(NULL, 0u), + 0, + 0, + &receipt_artifact)) { + goto seed_cleanup; + } + if (!amduatd_seed_store_artifact(store, receipt_artifact, &receipt_ref)) { + amduat_asl_artifact_free(&receipt_artifact); + goto seed_cleanup; + } + amduat_asl_artifact_free(&receipt_artifact); + if (!amduatd_concepts_put_edge(store, + concepts, + registry_output_ref, + receipt_ref, + concepts->rel_has_provenance_ref, + amduat_octets(NULL, 0u), + &edge_ref)) { + goto seed_cleanup; + } + amduat_reference_free(&edge_ref); + amduat_reference_free(&receipt_ref); + amduat_enc_pel1_result_free(&run_result.result_value); + amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len); + amduat_pel_surf_free_ref(&run_result.result_ref); + memset(&run_result, 0, sizeof(run_result)); + } + + if (!amduatd_seed_materializes_if_missing(store, concepts, + schema_concept_ref, + schema_output_ref) || + !amduatd_seed_materializes_if_missing(store, concepts, + registry_concept_ref, + registry_output_ref)) { + goto seed_cleanup; + } + + ok = true; + +seed_cleanup: + if (run_result.has_result_value) { + amduat_enc_pel1_result_free(&run_result.result_value); + } + if (run_result.output_refs != NULL) { + amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len); + } + amduat_pel_surf_free_ref(&run_result.result_ref); + + amduat_reference_free(&type_string_ref); + amduat_reference_free(&type_ref_ref); + amduat_reference_free(&key_title_ref); + amduat_reference_free(&key_status_ref); + amduat_reference_free(&key_latest_ref); + amduat_reference_free(&schema_concept_ref); + amduat_reference_free(®istry_concept_ref); + amduat_reference_free(&identity_program_ref); + amduat_reference_free(&schema_input_ref); + amduat_reference_free(®istry_input_ref); + amduat_reference_free(&schema_output_ref); + amduat_reference_free(®istry_output_ref); + amduat_reference_free(&seed_manifest_ref); + amduat_reference_free(&seed_environment_ref); + amduat_reference_free(&seed_executor_ref); + + for (i = 0; i < 3; ++i) { + free(fields[i].key_hex); + free(fields[i].type_hex); + free(fields[i].key_text); + free(entries[i].value_hex); + } + amduatd_strbuf_free(&b); + free(seed_latest_hex); + return ok; +} + +static bool amduatd_handle_post_concepts(int fd, + amduat_asl_store_t *store, + amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg, + const amduatd_http_req_t *req) { + uint8_t *body = NULL; + const char *p = NULL; + const char *end = NULL; + char *name = NULL; + bool have_name = false; + bool have_ref = false; + amduat_reference_t target_ref; + amduat_reference_t name_ref; + amduat_reference_t concept_ref; + amduat_reference_t edge_ref; + char *concept_hex = NULL; + char *scoped_name = NULL; + amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); + const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; + char json[4096]; + bool ok = false; + amduat_octets_t actor = amduat_octets(NULL, 0u); + + memset(&target_ref, 0, sizeof(target_ref)); + memset(&name_ref, 0, sizeof(name_ref)); + memset(&concept_ref, 0, sizeof(concept_ref)); + memset(&edge_ref, 0, sizeof(edge_ref)); + + if (store == NULL || concepts == NULL || req == NULL) { + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "internal error"); + } + if (req->has_actor) { + actor = req->actor; + } + if (req->content_length == 0) { + return amduatd_send_json_error(fd, 400, "Bad Request", "missing body"); + } + if (req->content_length > (256u * 1024u)) { + return amduatd_send_json_error(fd, 413, "Payload Too Large", + "payload too large"); + } + body = (uint8_t *)malloc(req->content_length); + if (body == NULL) { + return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); + } + if (!amduatd_read_exact(fd, body, req->content_length)) { + free(body); + return false; + } + p = (const char *)body; + end = (const char *)body + req->content_length; + if (!amduatd_json_expect(&p, end, '{')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); + goto concepts_cleanup; + } + + for (;;) { + const char *key = NULL; + size_t key_len = 0; + const char *sv = NULL; + size_t sv_len = 0; + const char *cur = NULL; + + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) || + !amduatd_json_expect(&p, end, ':')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); + goto concepts_cleanup; + } + + if (key_len == strlen("name") && memcmp(key, "name", key_len) == 0) { + if (have_name || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || + !amduatd_copy_json_str(sv, sv_len, &name)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); + goto concepts_cleanup; + } + have_name = true; + } else if (key_len == strlen("ref") && memcmp(key, "ref", key_len) == 0) { + if (have_ref || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || + !amduatd_decode_ref_hex_str(sv, sv_len, &target_ref)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid ref"); + goto concepts_cleanup; + } + have_ref = true; + } else { + if (!amduatd_json_skip_value(&p, end, 0)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); + goto concepts_cleanup; + } + } + + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == ',') { + p = cur + 1; + continue; + } + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); + goto concepts_cleanup; + } + + if (!have_name) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing name"); + goto concepts_cleanup; + } + + if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); + goto concepts_cleanup; + } + scoped_name = (char *)scoped_bytes.data; + amduatd_space_log_mapping(space, name, scoped_name); + + if (!amduatd_concepts_put_name_artifact(store, scoped_name, &name_ref)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "store error"); + goto concepts_cleanup; + } + + { + amduat_reference_t existing; + memset(&existing, 0, sizeof(existing)); + if (amduatd_concepts_lookup_alias(store, + concepts, + scoped_name, + &existing)) { + amduat_reference_free(&existing); + ok = amduatd_send_json_error(fd, 409, "Conflict", "name exists"); + goto concepts_cleanup; + } + } + + if (!amduatd_concepts_put_concept_id(store, &concept_ref)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "store error"); + goto concepts_cleanup; + } + + if (!amduatd_concepts_put_edge(store, + concepts, + name_ref, + concept_ref, + concepts->rel_aliases_ref, + actor, + &edge_ref)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "store error"); + goto concepts_cleanup; + } + + if (have_ref) { + if (!amduatd_concepts_put_edge(store, + concepts, + concept_ref, + target_ref, + concepts->rel_materializes_ref, + actor, + &edge_ref)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "store error"); + goto concepts_cleanup; + } + } + + if (!amduat_asl_ref_encode_hex(concept_ref, &concept_hex)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "encode error"); + goto concepts_cleanup; + } + + { + int n = snprintf(json, + sizeof(json), + "{" + "\"name\":\"%s\"," + "\"concept_ref\":\"%s\"" + "}\n", + name, + concept_hex); + free(concept_hex); + if (n <= 0 || (size_t)n >= sizeof(json)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); + goto concepts_cleanup; + } + ok = amduatd_http_send_json(fd, 200, "OK", json, false); + } + +concepts_cleanup: + free(body); + free(name); + free(scoped_name); + amduat_reference_free(&target_ref); + amduat_reference_free(&name_ref); + amduat_reference_free(&concept_ref); + amduat_reference_free(&edge_ref); + return ok; +} + +static bool amduatd_path_extract_name(const char *path, + const char *prefix, + char *out, + size_t cap) { + size_t plen; + const char *p; + size_t len; + + if (out != NULL && cap != 0) { + out[0] = '\0'; + } + if (path == NULL || prefix == NULL || out == NULL || cap == 0) { + return false; + } + plen = strlen(prefix); + if (strncmp(path, prefix, plen) != 0) { + return false; + } + p = path + plen; + if (*p == '\0') { + return false; + } + len = strlen(p); + if (len >= cap) { + len = cap - 1; + } + memcpy(out, p, len); + out[len] = '\0'; + return true; +} + +static bool amduatd_handle_post_concept_publish(int fd, + amduat_asl_store_t *store, + amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg, + const char *name, + const amduatd_http_req_t *req) { + uint8_t *body = NULL; + const char *p = NULL; + const char *end = NULL; + amduat_reference_t target_ref; + amduat_reference_t concept_ref; + amduat_reference_t edge_ref; + bool have_ref = false; + bool ok = false; + amduat_octets_t actor = amduat_octets(NULL, 0u); + char *scoped_name = NULL; + amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); + const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; + + memset(&target_ref, 0, sizeof(target_ref)); + memset(&concept_ref, 0, sizeof(concept_ref)); + memset(&edge_ref, 0, sizeof(edge_ref)); + + if (store == NULL || concepts == NULL || name == NULL || req == NULL) { + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "internal error"); + } + if (req->has_actor) { + actor = req->actor; + } + if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); + } + scoped_name = (char *)scoped_bytes.data; + amduatd_space_log_mapping(space, name, scoped_name); + if (req->content_length == 0) { + free(scoped_name); + return amduatd_send_json_error(fd, 400, "Bad Request", "missing body"); + } + if (req->content_length > (256u * 1024u)) { + free(scoped_name); + return amduatd_send_json_error(fd, 413, "Payload Too Large", + "payload too large"); + } + + if (!amduatd_concepts_lookup_alias(store, + concepts, + scoped_name, + &concept_ref)) { + free(scoped_name); + return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept"); + } + free(scoped_name); + + body = (uint8_t *)malloc(req->content_length); + if (body == NULL) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); + goto publish_cleanup; + } + if (!amduatd_read_exact(fd, body, req->content_length)) { + free(body); + amduat_reference_free(&concept_ref); + return false; + } + + p = (const char *)body; + end = (const char *)body + req->content_length; + if (!amduatd_json_expect(&p, end, '{')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); + goto publish_cleanup; + } + for (;;) { + const char *key = NULL; + size_t key_len = 0; + const char *sv = NULL; + size_t sv_len = 0; + const char *cur = NULL; + + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) || + !amduatd_json_expect(&p, end, ':')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); + goto publish_cleanup; + } + if (key_len == strlen("ref") && memcmp(key, "ref", key_len) == 0) { + if (have_ref || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || + !amduatd_decode_ref_hex_str(sv, sv_len, &target_ref)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid ref"); + goto publish_cleanup; + } + have_ref = true; + } else { + if (!amduatd_json_skip_value(&p, end, 0)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); + goto publish_cleanup; + } + } + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == ',') { + p = cur + 1; + continue; + } + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); + goto publish_cleanup; + } + if (!have_ref) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing ref"); + goto publish_cleanup; + } + + if (!amduatd_concepts_put_edge(store, + concepts, + concept_ref, + target_ref, + concepts->rel_materializes_ref, + actor, + &edge_ref)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "store error"); + goto publish_cleanup; + } + + ok = amduatd_http_send_json(fd, 200, "OK", "{\"ok\":true}\n", false); + +publish_cleanup: + free(body); + amduat_reference_free(&target_ref); + amduat_reference_free(&concept_ref); + amduat_reference_free(&edge_ref); + return ok; +} + +static bool amduatd_handle_get_resolve(int fd, + amduat_asl_store_t *store, + const amduatd_concepts_t *concepts, + const amduatd_cfg_t *dcfg, + const char *name) { + amduat_reference_t concept_ref; + amduat_reference_t latest_ref; + char *hex = NULL; + char json[2048]; + int n; + char *scoped_name = NULL; + amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u); + const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL; + + memset(&concept_ref, 0, sizeof(concept_ref)); + memset(&latest_ref, 0, sizeof(latest_ref)); + + if (store == NULL || concepts == NULL || name == NULL) { + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "internal error"); + } + if (!amduatd_space_scope_name(space, name, &scoped_bytes)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); + } + scoped_name = (char *)scoped_bytes.data; + amduatd_space_log_mapping(space, name, scoped_name); + if (!amduatd_concepts_lookup_alias(store, concepts, scoped_name, + &concept_ref)) { + free(scoped_name); + return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept"); + } + free(scoped_name); + if (!amduatd_concepts_resolve_latest(store, concepts, concept_ref, + &latest_ref)) { + amduat_reference_free(&concept_ref); + return amduatd_send_json_error(fd, 404, "Not Found", "no versions"); + } + amduat_reference_free(&concept_ref); + + if (!amduat_asl_ref_encode_hex(latest_ref, &hex)) { + amduat_reference_free(&latest_ref); + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "encode error"); + } + amduat_reference_free(&latest_ref); + n = snprintf(json, sizeof(json), "{\"ref\":\"%s\"}\n", hex); + free(hex); + if (n <= 0 || (size_t)n >= sizeof(json)) { + return amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); + } + return amduatd_http_send_json(fd, 200, "OK", json, false); +} + +static void amduatd_concepts_path_without_query(const char *path, + char *out, + size_t cap) { + const char *q = NULL; + size_t n = 0; + if (out == NULL || cap == 0) { + return; + } + out[0] = '\0'; + if (path == NULL) { + return; + } + q = strchr(path, '?'); + n = q != NULL ? (size_t)(q - path) : strlen(path); + if (n >= cap) { + n = cap - 1; + } + memcpy(out, path, n); + out[n] = '\0'; +} + +bool amduatd_concepts_can_handle(const amduatd_http_req_t *req) { + char no_query[1024]; + + if (req == NULL) { + return false; + } + amduatd_concepts_path_without_query(req->path, no_query, sizeof(no_query)); + + if (strcmp(req->method, "POST") == 0 && + strcmp(no_query, "/v1/concepts") == 0) { + return true; + } + if (strcmp(req->method, "GET") == 0 && + strcmp(no_query, "/v1/concepts") == 0) { + return true; + } + if (strcmp(req->method, "GET") == 0 && + strcmp(no_query, "/v1/relations") == 0) { + return true; + } + if (strcmp(req->method, "GET") == 0 && + strncmp(no_query, "/v1/concepts/", 13) == 0) { + return true; + } + if (strcmp(req->method, "POST") == 0 && + strncmp(no_query, "/v1/concepts/", 13) == 0 && + strstr(no_query, "/publish") != NULL) { + return true; + } + if (strcmp(req->method, "GET") == 0 && + strncmp(no_query, "/v1/resolve/", 12) == 0) { + return true; + } + + return false; +} + +bool amduatd_concepts_handle(amduatd_ctx_t *ctx, + const amduatd_http_req_t *req, + amduatd_http_resp_t *resp) { + char no_query[1024]; + + if (ctx == NULL || req == NULL || resp == NULL) { + return false; + } + + amduatd_concepts_path_without_query(req->path, no_query, sizeof(no_query)); + + if (strcmp(req->method, "POST") == 0 && strcmp(no_query, "/v1/concepts") == 0) { + resp->ok = amduatd_handle_post_concepts(resp->fd, + ctx->store, + ctx->concepts, + ctx->daemon_cfg, + req); + return true; + } + if (strcmp(req->method, "GET") == 0 && strcmp(no_query, "/v1/concepts") == 0) { + resp->ok = amduatd_handle_get_concepts(resp->fd, + ctx->store, + ctx->concepts, + ctx->daemon_cfg); + return true; + } + if (strcmp(req->method, "GET") == 0 && strcmp(no_query, "/v1/relations") == 0) { + resp->ok = amduatd_handle_get_relations(resp->fd, ctx->concepts); + return true; + } + if (strcmp(req->method, "GET") == 0 && strncmp(no_query, "/v1/concepts/", 13) == 0) { + const char *name = no_query + 13; + if (name[0] == '\0') { + resp->ok = amduatd_send_json_error(resp->fd, 400, "Bad Request", + "missing name"); + return true; + } + resp->ok = amduatd_handle_get_concept(resp->fd, + ctx->store, + ctx->concepts, + ctx->daemon_cfg, + name); + return true; + } + if (strcmp(req->method, "POST") == 0 && + strncmp(no_query, "/v1/concepts/", 13) == 0 && + strstr(no_query, "/publish") != NULL) { + char name[256]; + char *slash; + if (!amduatd_path_extract_name(no_query, "/v1/concepts/", name, + sizeof(name))) { + resp->ok = amduatd_send_json_error(resp->fd, 400, "Bad Request", + "invalid path"); + return true; + } + slash = strstr(name, "/publish"); + if (slash == NULL || strcmp(slash, "/publish") != 0) { + resp->ok = amduatd_send_json_error(resp->fd, 400, "Bad Request", + "invalid path"); + return true; + } + *slash = '\0'; + resp->ok = amduatd_handle_post_concept_publish(resp->fd, + ctx->store, + ctx->concepts, + ctx->daemon_cfg, + name, + req); + return true; + } + if (strcmp(req->method, "GET") == 0 && strncmp(no_query, "/v1/resolve/", 12) == 0) { + const char *name = no_query + 12; + if (name[0] == '\0') { + resp->ok = amduatd_send_json_error(resp->fd, 400, "Bad Request", + "missing name"); + return true; + } + resp->ok = amduatd_handle_get_resolve(resp->fd, + ctx->store, + ctx->concepts, + ctx->daemon_cfg, + name); + return true; + } + + return false; +} + +static void amduatd_strbuf_free(amduatd_strbuf_t *b) { + if (b == NULL) { + return; + } + free(b->data); + b->data = NULL; + b->len = 0u; + b->cap = 0u; +} + +static bool amduatd_strbuf_reserve(amduatd_strbuf_t *b, size_t extra) { + char *next = NULL; + size_t need = 0u; + size_t next_cap; + if (b == NULL) { + return false; + } + if (b->len > SIZE_MAX - extra) { + return false; + } + need = b->len + extra; + if (need <= b->cap) { + return true; + } + next_cap = b->cap != 0u ? b->cap * 2u : 128u; + if (next_cap < need) { + next_cap = need; + } + next = (char *)realloc(b->data, next_cap); + if (next == NULL) { + return false; + } + b->data = next; + b->cap = next_cap; + return true; +} + +static bool amduatd_strbuf_append(amduatd_strbuf_t *b, + const char *data, + size_t n) { + if (b == NULL || (data == NULL && n != 0u)) { + return false; + } + if (!amduatd_strbuf_reserve(b, n + 1u)) { + return false; + } + if (n != 0u) { + memcpy(b->data + b->len, data, n); + b->len += n; + } + b->data[b->len] = '\0'; + return true; +} + +static bool amduatd_strbuf_append_cstr(amduatd_strbuf_t *b, const char *s) { + return amduatd_strbuf_append(b, s != NULL ? s : "", s != NULL ? strlen(s) : 0u); +} + +static bool amduatd_strbuf_append_char(amduatd_strbuf_t *b, char c) { + return amduatd_strbuf_append(b, &c, 1u); +} diff --git a/src/amduatd_concepts.h b/src/amduatd_concepts.h new file mode 100644 index 0000000..bc0765d --- /dev/null +++ b/src/amduatd_concepts.h @@ -0,0 +1,61 @@ +#ifndef AMDUATD_CONCEPTS_H +#define AMDUATD_CONCEPTS_H + +#include "amduat/asl/collection.h" +#include "amduatd_space.h" +#include "amduatd_ui.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + amduat_reference_t record_ref; + amduat_reference_t src_ref; + amduat_reference_t dst_ref; + char *rel; +} amduatd_edge_entry_t; + +typedef struct { + amduatd_edge_entry_t *items; + size_t len; + size_t cap; +} amduatd_edge_list_t; + +typedef struct amduatd_concepts_t { + const char *root_path; + char edges_path[1024]; + char *edge_collection_name; + amduat_reference_t rel_aliases_ref; + amduat_reference_t rel_materializes_ref; + amduat_reference_t rel_represents_ref; + amduat_reference_t rel_requires_key_ref; + amduat_reference_t rel_within_domain_ref; + amduat_reference_t rel_computed_by_ref; + amduat_reference_t rel_has_provenance_ref; + amduat_asl_collection_store_t edge_collection; + amduatd_edge_list_t edges; +} amduatd_concepts_t; + +bool amduatd_concepts_init(amduatd_concepts_t *c, + amduat_asl_store_t *store, + const amduatd_space_t *space, + const char *root_path, + bool enable_migrations); + +void amduatd_concepts_free(amduatd_concepts_t *c); + +bool amduatd_concepts_can_handle(const amduatd_http_req_t *req); + +bool amduatd_concepts_handle(amduatd_ctx_t *ctx, + const amduatd_http_req_t *req, + amduatd_http_resp_t *resp); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AMDUATD_CONCEPTS_H */