#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); }