From 60d183b86bd22751f8bb049d828a1d560dc4be01 Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Tue, 23 Dec 2025 09:14:58 +0100 Subject: [PATCH] Add pel.format.encode and seed ui registry --- src/amduatd.c | 2161 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 2095 insertions(+), 66 deletions(-) diff --git a/src/amduatd.c b/src/amduatd.c index f081890..71ab760 100644 --- a/src/amduatd.c +++ b/src/amduatd.c @@ -10,6 +10,7 @@ #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/tgk/core.h" #include "amduat/pel/program_dag.h" @@ -114,15 +115,40 @@ typedef struct { char edges_path[1024]; 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; amduatd_ref_list_t edge_refs; } 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_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_ref_list_free(&c->edge_refs); } @@ -141,11 +167,13 @@ static const char k_amduatd_contract_v1_json[] = "{\"method\":\"POST\",\"path\":\"/v1/concepts/{name}/publish\"}," "{\"method\":\"GET\",\"path\":\"/v1/resolve/{name}\"}," "{\"method\":\"POST\",\"path\":\"/v1/artifacts\"}," + "{\"method\":\"GET\",\"path\":\"/v1/relations\"}," "{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}\"}," "{\"method\":\"HEAD\",\"path\":\"/v1/artifacts/{ref}\"}," "{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}?format=info\"}," "{\"method\":\"POST\",\"path\":\"/v1/pel/run\"}," - "{\"method\":\"POST\",\"path\":\"/v1/pel/programs\"}" + "{\"method\":\"POST\",\"path\":\"/v1/pel/programs\"}," + "{\"method\":\"POST\",\"path\":\"/v1/context_frames\"}" "]," "\"schemas\":{" "\"pel_run_request\":{" @@ -155,7 +183,21 @@ static const char k_amduatd_contract_v1_json[] = "\"program_ref\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," "\"input_refs\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}}," "\"params_ref\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," - "\"scheme_ref\":{\"type\":\"string\",\"description\":\"hex ref or 'dag'\"}" + "\"scheme_ref\":{\"type\":\"string\",\"description\":\"hex ref or 'dag'\"}," + "\"receipt\":{" + "\"type\":\"object\"," + "\"required\":[\"input_manifest_ref\",\"environment_ref\",\"evaluator_id\",\"executor_ref\",\"started_at\",\"completed_at\"]," + "\"properties\":{" + "\"input_manifest_ref\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," + "\"environment_ref\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," + "\"evaluator_id\":{\"type\":\"string\",\"description\":\"opaque evaluator bytes (utf-8)\"}," + "\"executor_ref\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," + "\"sbom_ref\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," + "\"parity_digest_hex\":{\"type\":\"string\",\"description\":\"hex bytes\"}," + "\"started_at\":{\"type\":\"integer\"}," + "\"completed_at\":{\"type\":\"integer\"}" + "}" + "}" "}" "}," "\"pel_run_response\":{" @@ -164,10 +206,33 @@ static const char k_amduatd_contract_v1_json[] = "\"properties\":{" "\"result_ref\":{\"type\":\"string\",\"description\":\"hex ref\"}," "\"trace_ref\":{\"type\":\"string\",\"description\":\"hex ref\"}," + "\"receipt_ref\":{\"type\":\"string\",\"description\":\"hex ref\"}," "\"output_refs\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"description\":\"hex ref\"}}," "\"status\":{\"type\":\"string\"}" "}" "}," + "\"context_frame_request\":{" + "\"type\":\"object\"," + "\"required\":[\"bindings\"]," + "\"properties\":{" + "\"bindings\":{" + "\"type\":\"array\"," + "\"items\":{" + "\"type\":\"object\"," + "\"required\":[\"key\"]," + "\"properties\":{" + "\"key\":{\"type\":\"string\",\"description\":\"concept name or hex ref\"}," + "\"value\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," + "\"value_ref\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," + "\"value_scalar\":{\"type\":\"object\",\"properties\":{" + "\"int\":{\"type\":\"integer\"}," + "\"enum\":{\"type\":\"string\",\"description\":\"concept name or hex ref\"}" + "}}" + "}" + "}" + "}" + "}" + "}," "\"pel_program_author_request\":{" "\"type\":\"object\"," "\"required\":[\"nodes\",\"roots\"]," @@ -187,35 +252,86 @@ static const char k_amduatd_ui_html[] = " \n" " amduatd — Concept editor\n" " \n" "\n" "\n" - "
\n" - "

amduatd — Concept editor

\n" - "
Load shows the latest materialized bytes; Save uploads a new artifact and publishes it as a new version.
\n" - "
\n" - "
\n" - "
\n" - "
\n" + "
\n" + "
\n" + "
\n" + "
\n" + " amduatd\n" + "
\n" + " \n" + "
\n" + "
\n" + "
\n" + "

Concept editor + PEL runner

\n" + "

Load shows the latest materialized bytes; Save uploads a new artifact and publishes a new version. Use the runner to execute PEL programs against stored artifacts.

\n" + "
\n" + " Open editor\n" + " Run program\n" + "
\n" + "
\n" + "
\n" + "
\n" "
\n" " \n" " \n" @@ -242,31 +358,55 @@ static const char k_amduatd_ui_html[] = "
\n" "
\n" "\n" - "
\n" - "
Upload bytes (sets program_ref)
\n" - "
\n" - " \n" - " \n" + "
\n" + "
\n" + "
Upload bytes (sets program_ref)
\n" + "
\n" + " \n" + " \n" + "
\n" + "
\n" + "
Run
\n" + " \n" + "
input_refs (comma-separated hex refs or names)
\n" + " \n" + "
params_ref (optional)
\n" + " \n" + "
scheme_ref (optional, default dag)
\n" + " \n" + "
\n" + " \n" + " /v1/contract\n" + " /v1/meta\n" + "
\n" + "
Response
\n" + "
\n"
+    "          
\n" + "
\n" + "
Relations
\n" + "
\n" + " \n" + "
\n" + "
\n"
+    "          
\n" "
\n" - "
\n" - "
Run
\n" - " \n" - "
input_refs (comma-separated hex refs or names)
\n" - " \n" - "
params_ref (optional)
\n" - " \n" - "
scheme_ref (optional, default dag)
\n" - " \n" - "
\n" - " \n" - " /v1/contract\n" - " /v1/meta\n" - "
\n" - "
Response
\n" - "
\n"
-    "      
\n" - "
\n" - "
\n" + " \n" + "
\n" + "
\n" + "

About

\n" + "

amduatd is a local-first mapping surface over a single ASL store root. This UI is a lightweight editor and runner for concepts and PEL programs.

\n" + "
\n" + " \n" + "
\n" + "
\n" + " \n" + " \n" "\n" " \n" "\n" "\n"; @@ -592,6 +740,47 @@ static bool amduatd_concepts_seed_relation(amduat_asl_store_t *store, return true; } +static bool amduatd_concepts_ensure_alias(amduatd_concepts_t *c, + amduat_asl_store_t *store, + const amduat_asl_store_fs_config_t *cfg, + const char *alias_name, + amduat_reference_t concept_ref) { + amduat_reference_t existing; + amduat_reference_t name_ref; + amduat_reference_t edge_ref; + + 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_name_valid(alias_name)) { + return false; + } + if (amduatd_concepts_lookup_alias(store, cfg, c, alias_name, &existing)) { + amduat_reference_free(&existing); + return true; + } + if (!amduatd_concepts_put_name_artifact(store, alias_name, &name_ref)) { + return false; + } + if (!amduatd_concepts_put_edge(store, + c, + name_ref, + concept_ref, + c->rel_aliases_ref, + &edge_ref)) { + amduat_reference_free(&name_ref); + amduat_reference_free(&edge_ref); + return false; + } + amduat_reference_free(&name_ref); + amduat_reference_free(&edge_ref); + return true; +} + static bool amduatd_concepts_init(amduatd_concepts_t *c, amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, @@ -612,10 +801,58 @@ static bool amduatd_concepts_init(amduatd_concepts_t *c, &c->rel_aliases_ref)) { return false; } + if (!amduatd_concepts_ensure_alias(c, store, cfg, "ms.aliases", + c->rel_aliases_ref)) { + return false; + } if (!amduatd_concepts_seed_relation(store, cfg, "materializesAs", &c->rel_materializes_ref)) { return false; } + if (!amduatd_concepts_ensure_alias(c, store, cfg, "ms.materializes_as", + c->rel_materializes_ref)) { + return false; + } + if (!amduatd_concepts_seed_relation(store, cfg, "represents", + &c->rel_represents_ref)) { + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, cfg, "ms.represents", + c->rel_represents_ref)) { + return false; + } + if (!amduatd_concepts_seed_relation(store, cfg, "requiresKey", + &c->rel_requires_key_ref)) { + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, cfg, "ms.requires_key", + c->rel_requires_key_ref)) { + return false; + } + if (!amduatd_concepts_seed_relation(store, cfg, "withinDomain", + &c->rel_within_domain_ref)) { + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, cfg, "ms.within_domain", + c->rel_within_domain_ref)) { + return false; + } + if (!amduatd_concepts_seed_relation(store, cfg, "computedBy", + &c->rel_computed_by_ref)) { + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, cfg, "ms.computed_by", + c->rel_computed_by_ref)) { + return false; + } + if (!amduatd_concepts_seed_relation(store, cfg, "hasProvenance", + &c->rel_has_provenance_ref)) { + return false; + } + if (!amduatd_concepts_ensure_alias(c, store, cfg, "ms.has_provenance", + c->rel_has_provenance_ref)) { + return false; + } if (!amduatd_read_file(c->edges_path, &file_bytes, &file_len)) { return true; @@ -1081,6 +1318,70 @@ static bool amduatd_handle_get_concepts(int fd, } } +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, @@ -1534,6 +1835,27 @@ static 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) { @@ -1720,6 +2042,146 @@ static bool amduatd_json_parse_u32(const char **p, return true; } +static bool amduatd_json_parse_u64(const char **p, + const char *end, + uint64_t *out) { + const char *cur; + const char *start; + unsigned long long v; + char *tmp = NULL; + char *endp = NULL; + size_t n; + + if (out != NULL) { + *out = 0; + } + if (p == NULL || *p == NULL || out == NULL) { + return false; + } + + cur = amduatd_json_skip_ws(*p, end); + start = cur; + if (cur >= end) { + return false; + } + if (*cur < '0' || *cur > '9') { + return false; + } + cur++; + while (cur < end && *cur >= '0' && *cur <= '9') { + cur++; + } + n = (size_t)(cur - start); + tmp = (char *)malloc(n + 1u); + if (tmp == NULL) { + return false; + } + memcpy(tmp, start, n); + tmp[n] = '\0'; + errno = 0; + v = strtoull(tmp, &endp, 10); + free(tmp); + if (errno != 0 || endp == NULL || *endp != '\0') { + return false; + } + *out = (uint64_t)v; + *p = cur; + return true; +} + +static bool amduatd_json_parse_u32_loose(const char **p, + const char *end, + uint32_t *out) { + const char *cur; + const char *sv = NULL; + size_t sv_len = 0; + uint64_t v = 0; + size_t i; + + if (out != NULL) { + *out = 0; + } + if (p == NULL || *p == NULL || out == NULL) { + return false; + } + + cur = amduatd_json_skip_ws(*p, end); + if (cur >= end) { + return false; + } + if (*cur == '"') { + if (!amduatd_json_parse_string_noesc(p, end, &sv, &sv_len)) { + return false; + } + if (sv_len == 0) { + return false; + } + for (i = 0; i < sv_len; ++i) { + unsigned char c = (unsigned char)sv[i]; + if (c < '0' || c > '9') { + return false; + } + v = (v * 10u) + (uint64_t)(c - '0'); + if (v > 0xffffffffULL) { + return false; + } + } + *out = (uint32_t)v; + return true; + } + if (*cur < '0' || *cur > '9') { + return false; + } + for (;;) { + unsigned char c = (unsigned char)*cur; + if (c < '0' || c > '9') { + break; + } + v = (v * 10u) + (uint64_t)(c - '0'); + if (v > 0xffffffffULL) { + return false; + } + cur++; + if (cur >= end) { + break; + } + } + *out = (uint32_t)v; + *p = cur; + return true; +} + +static void amduatd_json_peek_token(const char *p, + const char *end, + char *out, + size_t cap) { + const char *cur; + size_t n = 0; + if (out != NULL && cap != 0) { + out[0] = '\0'; + } + if (out == NULL || cap == 0) { + return; + } + cur = amduatd_json_skip_ws(p, end); + while (cur < end && n + 1 < cap) { + unsigned char c = (unsigned char)*cur; + if (c == ',' || c == '}' || c == ']' || c == '\n' || c == '\r') { + break; + } + if (c < 0x20u || c > 0x7eu) { + out[n++] = '?'; + } else { + out[n++] = (char)c; + } + cur++; + if (n >= 24u) { + break; + } + } + out[n] = '\0'; +} + static bool amduatd_json_skip_string(const char **p, const char *end) { const char *cur; if (p == NULL || *p == NULL) { @@ -2196,6 +2658,645 @@ 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 char *name, + amduat_reference_t *out_concept_ref) { + amduat_reference_t name_ref; + amduat_reference_t concept_ref; + amduat_reference_t edge_ref; + + 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_concepts_lookup_alias(store, cfg, concepts, name, + out_concept_ref)) { + return true; + } + + 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, name, &name_ref)) { + return false; + } + if (!amduatd_concepts_put_concept_id(store, cfg, &concept_ref)) { + amduat_reference_free(&name_ref); + return false; + } + if (!amduatd_concepts_put_edge(store, + concepts, + name_ref, + concept_ref, + concepts->rel_aliases_ref, + &edge_ref)) { + amduat_reference_free(&name_ref); + amduat_reference_free(&concept_ref); + return false; + } + amduat_reference_free(&name_ref); + amduat_reference_free(&edge_ref); + *out_concept_ref = concept_ref; + return true; +} + +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, + &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) { + 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, "ms.type.string", + &type_string_ref) || + !amduatd_seed_concept_if_missing(store, cfg, concepts, "ms.type.ref", + &type_ref_ref) || + !amduatd_seed_concept_if_missing(store, cfg, concepts, "ms.ui.field.title", + &key_title_ref) || + !amduatd_seed_concept_if_missing(store, cfg, concepts, "ms.ui.field.status", + &key_status_ref) || + !amduatd_seed_concept_if_missing(store, cfg, concepts, + "ms.ui.field.latest_ref", + &key_latest_ref) || + !amduatd_seed_concept_if_missing(store, cfg, concepts, + "ms.ui.state.schema.v1", + &schema_concept_ref) || + !amduatd_seed_concept_if_missing(store, cfg, concepts, + "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, + &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, + &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_seed_ui_html(amduat_asl_store_t *store, + const amduat_asl_store_fs_config_t *cfg, + amduat_reference_t *out_ref) { + amduat_artifact_t artifact; + amduat_asl_store_error_t err; + + if (out_ref != NULL) { + memset(out_ref, 0, sizeof(*out_ref)); + } + if (store == NULL || cfg == NULL || out_ref == NULL) { + return false; + } + + artifact = amduat_artifact(amduat_octets(k_amduatd_ui_html, + strlen(k_amduatd_ui_html))); + (void)amduat_asl_ref_derive(artifact, + cfg->config.encoding_profile_id, + cfg->config.hash_id, + out_ref, + NULL); + err = amduat_asl_store_put(store, artifact, out_ref); + if (err != AMDUAT_ASL_STORE_OK) { + return false; + } + return true; +} + static bool amduatd_handle_get_artifact(int fd, amduat_asl_store_t *store, const amduatd_http_req_t *req, @@ -2437,12 +3538,32 @@ static bool amduatd_handle_post_pel_run(int fd, bool have_scheme_ref = false; bool scheme_is_dag = false; bool has_params_ref = false; + bool want_receipt = false; + bool receipt_have_input_manifest = false; + bool receipt_have_environment = false; + bool receipt_have_evaluator = false; + bool receipt_have_executor = false; + bool receipt_have_started = false; + bool receipt_have_completed = false; + bool receipt_has_sbom = false; + char *receipt_evaluator_id = NULL; + uint8_t *receipt_parity_digest = NULL; + size_t receipt_parity_digest_len = 0; + uint64_t receipt_started_at = 0; + uint64_t receipt_completed_at = 0; + amduat_reference_t receipt_input_manifest_ref; + amduat_reference_t receipt_environment_ref; + amduat_reference_t receipt_executor_ref; + amduat_reference_t receipt_sbom_ref; amduat_reference_t scheme_ref; amduat_reference_t program_ref; amduat_reference_t params_ref; amduat_reference_t *input_refs = NULL; size_t input_refs_len = 0; amduat_pel_run_result_t run_result; + amduat_artifact_t receipt_artifact; + amduat_reference_t receipt_ref; + bool receipt_emitted = false; int status_code = 200; const char *status_reason = "OK"; bool ok = false; @@ -2451,6 +3572,12 @@ static bool amduatd_handle_post_pel_run(int fd, memset(&program_ref, 0, sizeof(program_ref)); memset(¶ms_ref, 0, sizeof(params_ref)); memset(&run_result, 0, sizeof(run_result)); + memset(&receipt_input_manifest_ref, 0, sizeof(receipt_input_manifest_ref)); + memset(&receipt_environment_ref, 0, sizeof(receipt_environment_ref)); + memset(&receipt_executor_ref, 0, sizeof(receipt_executor_ref)); + memset(&receipt_sbom_ref, 0, sizeof(receipt_sbom_ref)); + memset(&receipt_artifact, 0, sizeof(receipt_artifact)); + memset(&receipt_ref, 0, sizeof(receipt_ref)); if (store == NULL || req == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", @@ -2667,6 +3794,201 @@ static bool amduatd_handle_post_pel_run(int fd, goto pel_run_cleanup; } } + } else if (key_len == strlen("receipt") && + memcmp(key, "receipt", key_len) == 0) { + const char *rkey = NULL; + size_t rkey_len = 0; + if (want_receipt) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "duplicate receipt"); + goto pel_run_cleanup; + } + if (!amduatd_json_expect(&p, end, '{')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid receipt"); + goto pel_run_cleanup; + } + want_receipt = true; + for (;;) { + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + if (!amduatd_json_parse_string_noesc(&p, end, &rkey, &rkey_len) || + !amduatd_json_expect(&p, end, ':')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid receipt"); + goto pel_run_cleanup; + } + if (rkey_len == strlen("input_manifest_ref") && + memcmp(rkey, "input_manifest_ref", rkey_len) == 0) { + amduatd_ref_status_t st; + if (receipt_have_input_manifest || + !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid input_manifest_ref"); + goto pel_run_cleanup; + } + st = amduatd_decode_ref_or_name_latest(store, cfg, concepts, sv, + sv_len, &receipt_input_manifest_ref); + if (st == AMDUATD_REF_ERR_NOT_FOUND) { + ok = amduatd_send_json_error(fd, 404, "Not Found", + "input_manifest_ref not found"); + goto pel_run_cleanup; + } + if (st != AMDUATD_REF_OK) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid input_manifest_ref"); + goto pel_run_cleanup; + } + receipt_have_input_manifest = true; + } else if (rkey_len == strlen("environment_ref") && + memcmp(rkey, "environment_ref", rkey_len) == 0) { + amduatd_ref_status_t st; + if (receipt_have_environment || + !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid environment_ref"); + goto pel_run_cleanup; + } + st = amduatd_decode_ref_or_name_latest(store, cfg, concepts, sv, + sv_len, &receipt_environment_ref); + if (st == AMDUATD_REF_ERR_NOT_FOUND) { + ok = amduatd_send_json_error(fd, 404, "Not Found", + "environment_ref not found"); + goto pel_run_cleanup; + } + if (st != AMDUATD_REF_OK) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid environment_ref"); + goto pel_run_cleanup; + } + receipt_have_environment = true; + } else if (rkey_len == strlen("evaluator_id") && + memcmp(rkey, "evaluator_id", rkey_len) == 0) { + char *tmp = NULL; + if (receipt_have_evaluator || + !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || + !amduatd_copy_json_str(sv, sv_len, &tmp)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid evaluator_id"); + free(tmp); + goto pel_run_cleanup; + } + free(receipt_evaluator_id); + receipt_evaluator_id = tmp; + receipt_have_evaluator = true; + } else if (rkey_len == strlen("executor_ref") && + memcmp(rkey, "executor_ref", rkey_len) == 0) { + amduatd_ref_status_t st; + if (receipt_have_executor || + !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid executor_ref"); + goto pel_run_cleanup; + } + st = amduatd_decode_ref_or_name_latest(store, cfg, concepts, sv, + sv_len, &receipt_executor_ref); + if (st == AMDUATD_REF_ERR_NOT_FOUND) { + ok = amduatd_send_json_error(fd, 404, "Not Found", + "executor_ref not found"); + goto pel_run_cleanup; + } + if (st != AMDUATD_REF_OK) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid executor_ref"); + goto pel_run_cleanup; + } + receipt_have_executor = true; + } else if (rkey_len == strlen("sbom_ref") && + memcmp(rkey, "sbom_ref", rkey_len) == 0) { + amduatd_ref_status_t st; + if (receipt_has_sbom || + !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid sbom_ref"); + goto pel_run_cleanup; + } + st = amduatd_decode_ref_or_name_latest(store, cfg, concepts, sv, + sv_len, &receipt_sbom_ref); + if (st == AMDUATD_REF_ERR_NOT_FOUND) { + ok = amduatd_send_json_error(fd, 404, "Not Found", + "sbom_ref not found"); + goto pel_run_cleanup; + } + if (st != AMDUATD_REF_OK) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid sbom_ref"); + goto pel_run_cleanup; + } + receipt_has_sbom = true; + } else if (rkey_len == strlen("parity_digest_hex") && + memcmp(rkey, "parity_digest_hex", rkey_len) == 0) { + char *tmp = NULL; + uint8_t *bytes = NULL; + size_t bytes_len = 0; + if (!amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || + !amduatd_copy_json_str(sv, sv_len, &tmp)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid parity_digest_hex"); + free(tmp); + goto pel_run_cleanup; + } + if (!amduat_hex_decode_alloc(tmp, &bytes, &bytes_len)) { + free(tmp); + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid parity_digest_hex"); + goto pel_run_cleanup; + } + free(tmp); + free(receipt_parity_digest); + receipt_parity_digest = bytes; + receipt_parity_digest_len = bytes_len; + } else if (rkey_len == strlen("started_at") && + memcmp(rkey, "started_at", rkey_len) == 0) { + if (receipt_have_started || + !amduatd_json_parse_u64(&p, end, &receipt_started_at)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid started_at"); + goto pel_run_cleanup; + } + receipt_have_started = true; + } else if (rkey_len == strlen("completed_at") && + memcmp(rkey, "completed_at", rkey_len) == 0) { + if (receipt_have_completed || + !amduatd_json_parse_u64(&p, end, &receipt_completed_at)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid completed_at"); + goto pel_run_cleanup; + } + receipt_have_completed = true; + } else { + if (!amduatd_json_skip_value(&p, end, 0)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid receipt"); + goto pel_run_cleanup; + } + } + + cur = amduatd_json_skip_ws(p, end); + if (cur >= end) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid receipt"); + goto pel_run_cleanup; + } + if (*cur == ',') { + p = cur + 1; + continue; + } + if (*cur == '}') { + p = cur + 1; + break; + } + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid receipt"); + goto pel_run_cleanup; + } } else { if (!amduatd_json_skip_value(&p, end, 0)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); @@ -2713,6 +4035,14 @@ static bool amduatd_handle_post_pel_run(int fd, scheme_ref = amduat_pel_program_dag_scheme_ref(); scheme_is_dag = true; } + if (want_receipt && + (!receipt_have_input_manifest || !receipt_have_environment || + !receipt_have_evaluator || !receipt_have_executor || + !receipt_have_started || !receipt_have_completed)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "missing receipt fields"); + goto pel_run_cleanup; + } if (!amduat_pel_surf_run_with_result(store, scheme_ref, @@ -2738,10 +4068,58 @@ static bool amduatd_handle_post_pel_run(int fd, } } + if (want_receipt) { + amduat_octets_t evaluator_id; + amduat_octets_t parity_digest; + + if (!run_result.has_result_value) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "receipt unavailable"); + goto pel_run_cleanup; + } + if (run_result.result_value.output_refs_len != 1) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "receipt requires single output"); + goto pel_run_cleanup; + } + evaluator_id = amduat_octets(receipt_evaluator_id, + receipt_evaluator_id != NULL + ? strlen(receipt_evaluator_id) + : 0u); + parity_digest = amduat_octets(receipt_parity_digest, + receipt_parity_digest_len); + if (!amduat_fer1_receipt_from_pel_result( + &run_result.result_value, + receipt_input_manifest_ref, + receipt_environment_ref, + evaluator_id, + receipt_executor_ref, + receipt_has_sbom, + receipt_sbom_ref, + parity_digest, + receipt_started_at, + receipt_completed_at, + &receipt_artifact)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "receipt build failed"); + goto pel_run_cleanup; + } + if (amduat_asl_store_put(store, receipt_artifact, &receipt_ref) != + AMDUAT_ASL_STORE_OK) { + amduat_asl_artifact_free(&receipt_artifact); + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "receipt store failed"); + goto pel_run_cleanup; + } + amduat_asl_artifact_free(&receipt_artifact); + receipt_emitted = true; + } + { amduatd_strbuf_t resp; char *result_hex = NULL; char *trace_hex = NULL; + char *receipt_hex = NULL; const char *status = "UNKNOWN"; size_t i; @@ -2792,6 +4170,24 @@ static bool amduatd_handle_post_pel_run(int fd, free(trace_hex); } + if (receipt_emitted) { + if (!amduat_asl_ref_encode_hex(receipt_ref, &receipt_hex)) { + amduatd_strbuf_free(&resp); + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "encode error"); + goto pel_run_cleanup; + } + if (!amduatd_strbuf_append_cstr(&resp, ",\"receipt_ref\":\"") || + !amduatd_strbuf_append_cstr(&resp, receipt_hex) || + !amduatd_strbuf_append_cstr(&resp, "\"")) { + free(receipt_hex); + amduatd_strbuf_free(&resp); + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); + goto pel_run_cleanup; + } + free(receipt_hex); + } + if (!amduatd_strbuf_append_cstr(&resp, ",\"output_refs\":[")) { amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); @@ -2865,6 +4261,23 @@ pel_run_cleanup: if (have_scheme_ref && !scheme_is_dag) { amduat_reference_free(&scheme_ref); } + if (receipt_emitted) { + amduat_reference_free(&receipt_ref); + } + if (receipt_have_input_manifest) { + amduat_reference_free(&receipt_input_manifest_ref); + } + if (receipt_have_environment) { + amduat_reference_free(&receipt_environment_ref); + } + if (receipt_have_executor) { + amduat_reference_free(&receipt_executor_ref); + } + if (receipt_has_sbom) { + amduat_reference_free(&receipt_sbom_ref); + } + free(receipt_evaluator_id); + free(receipt_parity_digest); return ok; } @@ -3052,9 +4465,26 @@ static bool amduatd_handle_post_pel_programs(int fd, if (nk_len == strlen("id") && memcmp(nk, "id", nk_len) == 0) { uint32_t id = 0; - if (have_id || !amduatd_json_parse_u32(&p, end, &id)) { + if (have_id) { ok = amduatd_send_json_error(fd, 400, "Bad Request", - "invalid node id"); + "duplicate node id"); + goto pel_programs_cleanup; + } + if (!amduatd_json_parse_u32_loose(&p, end, &id)) { + char near[32]; + char msg[96]; + int n; + amduatd_json_peek_token(p, end, near, sizeof(near)); + n = snprintf(msg, + sizeof(msg), + "invalid node id near '%s'", + near[0] != '\0' ? near : "?"); + if (n > 0 && (size_t)n < sizeof(msg)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", msg); + } else { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid node id"); + } goto pel_programs_cleanup; } node.id = (amduat_pel_node_id_t)id; @@ -3096,7 +4526,7 @@ static bool amduatd_handle_post_pel_programs(int fd, } else if (okey_len == strlen("version") && memcmp(okey, "version", okey_len) == 0) { if (have_version || - !amduatd_json_parse_u32(&p, end, &op_ver)) { + !amduatd_json_parse_u32_loose(&p, end, &op_ver)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid op version"); goto pel_programs_cleanup; @@ -3172,7 +4602,7 @@ static bool amduatd_handle_post_pel_programs(int fd, k2_len != strlen("input_index") || memcmp(k2, "input_index", k2_len) != 0 || !amduatd_json_expect(&p, end, ':') || - !amduatd_json_parse_u32(&p, end, &idx) || + !amduatd_json_parse_u32_loose(&p, end, &idx) || !amduatd_json_expect(&p, end, '}')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid external input"); @@ -3204,7 +4634,7 @@ static bool amduatd_handle_post_pel_programs(int fd, if (k2_len == strlen("node_id") && memcmp(k2, "node_id", k2_len) == 0) { if (have_node_id || - !amduatd_json_parse_u32(&p, end, &nid)) { + !amduatd_json_parse_u32_loose(&p, end, &nid)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid node_id"); goto pel_programs_cleanup; @@ -3213,7 +4643,7 @@ static bool amduatd_handle_post_pel_programs(int fd, } else if (k2_len == strlen("output_index") && memcmp(k2, "output_index", k2_len) == 0) { if (have_output_index || - !amduatd_json_parse_u32(&p, end, &oidx)) { + !amduatd_json_parse_u32_loose(&p, end, &oidx)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid output_index"); goto pel_programs_cleanup; @@ -3414,7 +4844,8 @@ static bool amduatd_handle_post_pel_programs(int fd, } if (rk_len == strlen("node_id") && memcmp(rk, "node_id", rk_len) == 0) { - if (have_node_id || !amduatd_json_parse_u32(&p, end, &v)) { + if (have_node_id || + !amduatd_json_parse_u32_loose(&p, end, &v)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid root node_id"); goto pel_programs_cleanup; @@ -3423,7 +4854,8 @@ static bool amduatd_handle_post_pel_programs(int fd, have_node_id = true; } else if (rk_len == strlen("output_index") && memcmp(rk, "output_index", rk_len) == 0) { - if (have_output_index || !amduatd_json_parse_u32(&p, end, &v)) { + if (have_output_index || + !amduatd_json_parse_u32_loose(&p, end, &v)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid root output_index"); goto pel_programs_cleanup; @@ -3565,14 +4997,43 @@ pel_programs_cleanup: return ok; } -static bool amduatd_handle_get_ui(int fd) { - return amduatd_http_send_status(fd, - 200, - "OK", - "text/html; charset=utf-8", - (const uint8_t *)k_amduatd_ui_html, - strlen(k_amduatd_ui_html), - false); +static bool amduatd_handle_get_ui(int fd, + amduat_asl_store_t *store, + amduat_reference_t ui_ref) { + amduat_artifact_t artifact; + amduat_asl_store_error_t err; + + if (store == NULL || ui_ref.hash_id == 0 || ui_ref.digest.data == NULL || + ui_ref.digest.len == 0) { + return amduatd_http_send_text(fd, 500, "Internal Server Error", + "ui not available\n", false); + } + memset(&artifact, 0, sizeof(artifact)); + err = amduat_asl_store_get(store, ui_ref, &artifact); + if (err == AMDUAT_ASL_STORE_ERR_NOT_FOUND) { + return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", false); + } + if (err != AMDUAT_ASL_STORE_OK) { + amduat_asl_artifact_free(&artifact); + return amduatd_http_send_text(fd, 500, "Internal Server Error", + "store error\n", false); + } + if (artifact.bytes.len != 0 && artifact.bytes.data == NULL) { + amduat_asl_artifact_free(&artifact); + return amduatd_http_send_text(fd, 500, "Internal Server Error", + "store error\n", false); + } + { + bool ok = amduatd_http_send_status(fd, + 200, + "OK", + "text/html; charset=utf-8", + artifact.bytes.data, + artifact.bytes.len, + false); + amduat_asl_artifact_free(&artifact); + return ok; + } } static bool amduatd_handle_post_concepts(int fd, @@ -3765,6 +5226,550 @@ concepts_cleanup: return ok; } +typedef struct { + char *key_hex; + char *value_hex; + uint64_t value_int; + int value_kind; +} amduatd_cf_binding_t; + +static void amduatd_cf_binding_free(amduatd_cf_binding_t *b) { + if (b == NULL) { + return; + } + free(b->key_hex); + free(b->value_hex); + b->key_hex = NULL; + b->value_hex = NULL; + b->value_int = 0; + b->value_kind = 0; +} + +static int amduatd_cf_binding_cmp(const void *a, const void *b) { + const amduatd_cf_binding_t *x = (const amduatd_cf_binding_t *)a; + const amduatd_cf_binding_t *y = (const amduatd_cf_binding_t *)b; + int c; + if (x == NULL || y == NULL) { + return 0; + } + c = strcmp(x->key_hex != NULL ? x->key_hex : "", + y->key_hex != NULL ? y->key_hex : ""); + if (c != 0) { + return c; + } + if (x->value_kind != y->value_kind) { + return x->value_kind < y->value_kind ? -1 : 1; + } + if (x->value_kind == 1) { + if (x->value_int < y->value_int) { + return -1; + } + if (x->value_int > y->value_int) { + return 1; + } + return 0; + } + return strcmp(x->value_hex != NULL ? x->value_hex : "", + y->value_hex != NULL ? y->value_hex : ""); +} + +static bool amduatd_handle_post_context_frames( + int fd, + amduat_asl_store_t *store, + const amduat_asl_store_fs_config_t *cfg, + const amduatd_concepts_t *concepts, + const amduatd_http_req_t *req) { + uint8_t *body = NULL; + const char *p = NULL; + const char *end = NULL; + amduatd_cf_binding_t *bindings = NULL; + size_t bindings_len = 0; + size_t bindings_cap = 0; + amduatd_strbuf_t b; + amduat_artifact_t artifact; + amduat_reference_t ref; + char *ref_hex = NULL; + bool ok = false; + size_t i; + + memset(&b, 0, sizeof(b)); + memset(&artifact, 0, sizeof(artifact)); + memset(&ref, 0, sizeof(ref)); + + if (store == NULL || cfg == NULL || concepts == NULL || req == NULL) { + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "internal error"); + } + 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 cf_cleanup; + } + + for (;;) { + const char *key = NULL; + size_t key_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 cf_cleanup; + } + + if (key_len == strlen("bindings") && memcmp(key, "bindings", key_len) == 0) { + if (!amduatd_json_expect(&p, end, '[')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid bindings"); + goto cf_cleanup; + } + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == ']') { + p = cur + 1; + } else { + for (;;) { + const char *bk = NULL; + size_t bk_len = 0; + const char *sv = NULL; + size_t sv_len = 0; + char *key_text = NULL; + char *value_text = NULL; + char *value_ref_text = NULL; + char *value_enum_text = NULL; + amduat_reference_t key_ref; + amduat_reference_t value_ref; + amduat_reference_t enum_ref; + bool have_key = false; + bool have_value = false; + bool have_value_ref = false; + bool have_value_scalar = false; + bool have_value_int = false; + bool have_value_enum = false; + uint64_t value_int = 0; + + memset(&key_ref, 0, sizeof(key_ref)); + memset(&value_ref, 0, sizeof(value_ref)); + memset(&enum_ref, 0, sizeof(enum_ref)); + + if (!amduatd_json_expect(&p, end, '{')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid binding"); + goto cf_cleanup; + } + for (;;) { + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + if (!amduatd_json_parse_string_noesc(&p, end, &bk, &bk_len) || + !amduatd_json_expect(&p, end, ':')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid binding"); + goto cf_cleanup; + } + if (bk_len == strlen("key") && memcmp(bk, "key", bk_len) == 0) { + if (have_key || + !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || + !amduatd_copy_json_str(sv, sv_len, &key_text)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid key"); + goto cf_cleanup; + } + have_key = true; + } else if (bk_len == strlen("value") && + memcmp(bk, "value", bk_len) == 0) { + if (have_value || have_value_ref || have_value_scalar || + !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || + !amduatd_copy_json_str(sv, sv_len, &value_text)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid value"); + goto cf_cleanup; + } + have_value = true; + } else if (bk_len == strlen("value_ref") && + memcmp(bk, "value_ref", bk_len) == 0) { + if (have_value || have_value_ref || have_value_scalar || + !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || + !amduatd_copy_json_str(sv, sv_len, &value_ref_text)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid value_ref"); + goto cf_cleanup; + } + have_value_ref = true; + } else if (bk_len == strlen("value_scalar") && + memcmp(bk, "value_scalar", bk_len) == 0) { + if (have_value || have_value_ref || have_value_scalar || + !amduatd_json_expect(&p, end, '{')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid value_scalar"); + goto cf_cleanup; + } + have_value_scalar = true; + for (;;) { + const char *sk = NULL; + size_t sk_len = 0; + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + if (!amduatd_json_parse_string_noesc(&p, end, &sk, &sk_len) || + !amduatd_json_expect(&p, end, ':')) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid value_scalar"); + goto cf_cleanup; + } + if (sk_len == strlen("int") && memcmp(sk, "int", sk_len) == 0) { + if (have_value_int || have_value_enum || + !amduatd_json_parse_u64(&p, end, &value_int)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid int"); + goto cf_cleanup; + } + have_value_int = true; + } else if (sk_len == strlen("enum") && + memcmp(sk, "enum", sk_len) == 0) { + if (have_value_int || have_value_enum || + !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || + !amduatd_copy_json_str(sv, sv_len, &value_enum_text)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid enum"); + goto cf_cleanup; + } + have_value_enum = true; + } else { + if (!amduatd_json_skip_value(&p, end, 0)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid value_scalar"); + goto cf_cleanup; + } + } + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == ',') { + p = cur + 1; + continue; + } + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + } + } else { + if (!amduatd_json_skip_value(&p, end, 0)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid binding"); + goto cf_cleanup; + } + } + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == ',') { + p = cur + 1; + continue; + } + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + } + if (!have_key || + (!have_value && !have_value_ref && !have_value_scalar)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "missing binding fields"); + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + goto cf_cleanup; + } + if (amduatd_decode_ref_or_name_latest(store, + cfg, + concepts, + key_text, + strlen(key_text), + &key_ref) != AMDUATD_REF_OK) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid key"); + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + goto cf_cleanup; + } + if (have_value || have_value_ref) { + const char *vtext = have_value ? value_text : value_ref_text; + if (amduatd_decode_ref_or_name_latest(store, + cfg, + concepts, + vtext, + strlen(vtext), + &value_ref) != AMDUATD_REF_OK) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid value"); + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + amduat_reference_free(&key_ref); + goto cf_cleanup; + } + } else if (have_value_scalar) { + if (have_value_enum) { + if (amduatd_decode_ref_or_name_latest(store, + cfg, + concepts, + value_enum_text, + strlen(value_enum_text), + &enum_ref) != AMDUATD_REF_OK) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid enum"); + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + amduat_reference_free(&key_ref); + goto cf_cleanup; + } + } else if (!have_value_int) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid value_scalar"); + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + amduat_reference_free(&key_ref); + goto cf_cleanup; + } + } + + if (bindings_len == bindings_cap) { + size_t next_cap = bindings_cap != 0 ? bindings_cap * 2u : 4u; + amduatd_cf_binding_t *next = + (amduatd_cf_binding_t *)realloc(bindings, + next_cap * sizeof(*bindings)); + if (next == NULL) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "oom"); + free(key_text); + free(value_text); + amduat_reference_free(&key_ref); + amduat_reference_free(&value_ref); + goto cf_cleanup; + } + bindings = next; + bindings_cap = next_cap; + } + + { + char *key_hex = NULL; + char *value_hex = NULL; + if (!amduat_asl_ref_encode_hex(key_ref, &key_hex)) { + free(key_hex); + free(value_hex); + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + amduat_reference_free(&key_ref); + amduat_reference_free(&value_ref); + amduat_reference_free(&enum_ref); + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "encode error"); + goto cf_cleanup; + } + if (have_value || have_value_ref) { + if (!amduat_asl_ref_encode_hex(value_ref, &value_hex)) { + free(key_hex); + free(value_hex); + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + amduat_reference_free(&key_ref); + amduat_reference_free(&value_ref); + amduat_reference_free(&enum_ref); + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "encode error"); + goto cf_cleanup; + } + bindings[bindings_len].value_kind = 0; + bindings[bindings_len].value_hex = value_hex; + } else if (have_value_int) { + bindings[bindings_len].value_kind = 1; + bindings[bindings_len].value_int = value_int; + } else if (have_value_enum) { + if (!amduat_asl_ref_encode_hex(enum_ref, &value_hex)) { + free(key_hex); + free(value_hex); + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + amduat_reference_free(&key_ref); + amduat_reference_free(&value_ref); + amduat_reference_free(&enum_ref); + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "encode error"); + goto cf_cleanup; + } + bindings[bindings_len].value_kind = 2; + bindings[bindings_len].value_hex = value_hex; + } else { + free(key_hex); + free(value_hex); + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + amduat_reference_free(&key_ref); + amduat_reference_free(&value_ref); + amduat_reference_free(&enum_ref); + ok = amduatd_send_json_error(fd, 400, "Bad Request", + "invalid value_scalar"); + goto cf_cleanup; + } + bindings[bindings_len].key_hex = key_hex; + bindings_len++; + } + + free(key_text); + free(value_text); + free(value_ref_text); + free(value_enum_text); + amduat_reference_free(&key_ref); + amduat_reference_free(&value_ref); + amduat_reference_free(&enum_ref); + + 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 bindings"); + goto cf_cleanup; + } + } + } else { + if (!amduatd_json_skip_value(&p, end, 0)) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); + goto cf_cleanup; + } + } + + cur = amduatd_json_skip_ws(p, end); + if (cur < end && *cur == ',') { + p = cur + 1; + continue; + } + if (cur < end && *cur == '}') { + p = cur + 1; + break; + } + } + + if (bindings_len == 0) { + ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing bindings"); + goto cf_cleanup; + } + + qsort(bindings, bindings_len, sizeof(*bindings), amduatd_cf_binding_cmp); + + if (!amduatd_strbuf_append_cstr(&b, "{\"bindings\":[")) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); + goto cf_cleanup; + } + for (i = 0; i < bindings_len; ++i) { + if (i != 0) { + (void)amduatd_strbuf_append_char(&b, ','); + } + (void)amduatd_strbuf_append_cstr(&b, "{\"key_ref\":\""); + (void)amduatd_strbuf_append_cstr(&b, bindings[i].key_hex); + if (bindings[i].value_kind == 0) { + (void)amduatd_strbuf_append_cstr(&b, "\",\"value_ref\":\""); + (void)amduatd_strbuf_append_cstr(&b, bindings[i].value_hex); + (void)amduatd_strbuf_append_cstr(&b, "\"}"); + } else if (bindings[i].value_kind == 1) { + char tmp[32]; + int n = snprintf(tmp, sizeof(tmp), "%llu", + (unsigned long long)bindings[i].value_int); + if (n <= 0 || (size_t)n >= sizeof(tmp)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); + goto cf_cleanup; + } + (void)amduatd_strbuf_append_cstr(&b, "\",\"value_int\":"); + (void)amduatd_strbuf_append_cstr(&b, tmp); + (void)amduatd_strbuf_append_cstr(&b, "}"); + } else if (bindings[i].value_kind == 2) { + (void)amduatd_strbuf_append_cstr(&b, "\",\"value_enum_ref\":\""); + (void)amduatd_strbuf_append_cstr(&b, bindings[i].value_hex); + (void)amduatd_strbuf_append_cstr(&b, "\"}"); + } else { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); + goto cf_cleanup; + } + } + (void)amduatd_strbuf_append_cstr(&b, "]}\n"); + + artifact = amduat_artifact(amduat_octets(b.data, b.len)); + if (amduat_asl_store_put(store, artifact, &ref) != AMDUAT_ASL_STORE_OK) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "store error"); + goto cf_cleanup; + } + if (!amduat_asl_ref_encode_hex(ref, &ref_hex)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", + "encode error"); + goto cf_cleanup; + } + { + char json[2048]; + int n = snprintf(json, sizeof(json), "{\"ref\":\"%s\"}\n", ref_hex); + free(ref_hex); + if (n <= 0 || (size_t)n >= sizeof(json)) { + ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); + goto cf_cleanup; + } + ok = amduatd_http_send_json(fd, 200, "OK", json, false); + } + +cf_cleanup: + free(body); + for (i = 0; i < bindings_len; ++i) { + amduatd_cf_binding_free(&bindings[i]); + } + free(bindings); + amduatd_strbuf_free(&b); + amduat_reference_free(&ref); + return ok; +} + static bool amduatd_path_extract_name(const char *path, const char *prefix, char *out, @@ -3969,6 +5974,7 @@ static bool amduatd_handle_conn(int fd, amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, amduat_reference_t api_contract_ref, + amduat_reference_t ui_ref, amduatd_concepts_t *concepts) { amduatd_http_req_t req; char no_query[1024]; @@ -3980,7 +5986,7 @@ static bool amduatd_handle_conn(int fd, amduatd_path_without_query(req.path, no_query, sizeof(no_query)); if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/ui") == 0) { - return amduatd_handle_get_ui(fd); + return amduatd_handle_get_ui(fd, store, ui_ref); } if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/meta") == 0) { @@ -4003,12 +6009,19 @@ static bool amduatd_handle_conn(int fd, strcmp(no_query, "/v1/pel/programs") == 0) { return amduatd_handle_post_pel_programs(fd, store, &req); } + if (strcmp(req.method, "POST") == 0 && + strcmp(no_query, "/v1/context_frames") == 0) { + return amduatd_handle_post_context_frames(fd, store, cfg, concepts, &req); + } if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/concepts") == 0) { return amduatd_handle_post_concepts(fd, store, cfg, concepts, &req); } if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/concepts") == 0) { return amduatd_handle_get_concepts(fd, store, concepts); } + if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/relations") == 0) { + return amduatd_handle_get_relations(fd, concepts); + } if (strcmp(req.method, "GET") == 0 && strncmp(no_query, "/v1/concepts/", 13) == 0) { const char *name = no_query + 13; if (name[0] == '\0') { @@ -4071,11 +6084,13 @@ int main(int argc, char **argv) { amduat_asl_store_fs_t fs; amduat_asl_store_t store; amduat_reference_t api_contract_ref; + amduat_reference_t ui_ref; amduatd_concepts_t concepts; int i; int sfd = -1; memset(&api_contract_ref, 0, sizeof(api_contract_ref)); + memset(&ui_ref, 0, sizeof(ui_ref)); memset(&concepts, 0, sizeof(concepts)); for (i = 1; i < argc; ++i) { @@ -4117,10 +6132,18 @@ int main(int argc, char **argv) { fprintf(stderr, "error: failed to seed api contract\n"); return 8; } + if (!amduatd_seed_ui_html(&store, &cfg, &ui_ref)) { + fprintf(stderr, "error: failed to seed ui html\n"); + return 8; + } if (!amduatd_concepts_init(&concepts, &store, &cfg, root)) { fprintf(stderr, "error: failed to init concept edges\n"); return 8; } + if (!amduatd_seed_ms_ui_state(&store, &cfg, &concepts)) { + fprintf(stderr, "error: failed to seed ms ui state\n"); + return 8; + } signal(SIGINT, amduatd_on_signal); signal(SIGTERM, amduatd_on_signal); @@ -4171,11 +6194,17 @@ int main(int argc, char **argv) { break; } - (void)amduatd_handle_conn(cfd, &store, &cfg, api_contract_ref, &concepts); + (void)amduatd_handle_conn(cfd, + &store, + &cfg, + api_contract_ref, + ui_ref, + &concepts); (void)close(cfd); } amduat_reference_free(&api_contract_ref); + amduat_reference_free(&ui_ref); amduatd_concepts_free(&concepts); (void)unlink(sock_path); (void)close(sfd);