diff --git a/README.md b/README.md index 81694b5..e65c37b 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,12 @@ curl --unix-socket amduatd.sock http://localhost/v1/meta Discover the store-backed API contract ref: +```sh +curl --unix-socket amduatd.sock 'http://localhost/v1/contract?format=ref' +``` + +Fetch the contract bytes (JSON): + ```sh curl --unix-socket amduatd.sock http://localhost/v1/contract ``` @@ -75,7 +81,8 @@ curl --unix-socket amduatd.sock -X POST http://localhost/v1/pel/run \ ## Current endpoints - `GET /v1/meta` → `{store_id, encoding_profile_id, hash_id, api_contract_ref}` -- `GET /v1/contract` → `{ref}` (fetch bytes via `GET /v1/artifacts/{ref}`) +- `GET /v1/contract` → contract bytes (JSON) (+ `X-Amduat-Contract-Ref` header) +- `GET /v1/contract?format=ref` → `{ref}` - `POST /v1/artifacts` - raw bytes: `Content-Type: application/octet-stream` (+ optional `X-Amduat-Type-Tag: 0x...`) - artifact framing: `Content-Type: application/vnd.amduat.asl.artifact+v1` diff --git a/src/amduatd.c b/src/amduatd.c index 6f46944..3932773 100644 --- a/src/amduatd.c +++ b/src/amduatd.c @@ -854,27 +854,93 @@ static bool amduatd_handle_meta(int fd, } static bool amduatd_handle_get_contract(int fd, + amduat_asl_store_t *store, + const amduatd_http_req_t *req, amduat_reference_t api_contract_ref) { char *hex = NULL; - char json[2048]; - int n; + char format[32]; if (api_contract_ref.hash_id == 0 || api_contract_ref.digest.data == NULL || api_contract_ref.digest.len == 0) { - return amduatd_http_send_json(fd, 404, "Not Found", "{\"ref\":null}\n", - false); + return amduatd_http_send_not_found(fd, req); } - if (!amduat_asl_ref_encode_hex(api_contract_ref, &hex)) { - return amduatd_http_send_text(fd, 500, "Internal Server Error", - "encode error\n", false); + + memset(format, 0, sizeof(format)); + if (req != NULL && + amduatd_query_param(req->path, "format", format, sizeof(format)) != + NULL && + strcmp(format, "ref") == 0) { + char json[2048]; + int n; + if (!amduat_asl_ref_encode_hex(api_contract_ref, &hex)) { + return amduatd_http_send_text(fd, 500, "Internal Server Error", + "encode error\n", false); + } + n = snprintf(json, sizeof(json), "{\"ref\":\"%s\"}\n", hex); + free(hex); + if (n <= 0 || (size_t)n >= sizeof(json)) { + return amduatd_http_send_text(fd, 500, "Internal Server Error", + "error\n", false); + } + return amduatd_http_send_json(fd, 200, "OK", json, false); } - n = snprintf(json, sizeof(json), "{\"ref\":\"%s\"}\n", hex); - free(hex); - if (n <= 0 || (size_t)n >= sizeof(json)) { - return amduatd_http_send_text(fd, 500, "Internal Server Error", "error\n", - false); + + { + amduat_artifact_t artifact; + amduat_asl_store_error_t err; + + memset(&artifact, 0, sizeof(artifact)); + err = amduat_asl_store_get(store, api_contract_ref, &artifact); + if (err == AMDUAT_ASL_STORE_ERR_NOT_FOUND) { + return amduatd_http_send_not_found(fd, req); + } + 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); + } + + if (!amduat_asl_ref_encode_hex(api_contract_ref, &hex)) { + amduat_asl_artifact_free(&artifact); + return amduatd_http_send_text(fd, 500, "Internal Server Error", + "encode error\n", false); + } + + { + char hdr[600]; + int n = snprintf(hdr, + sizeof(hdr), + "HTTP/1.1 200 OK\r\n" + "Content-Length: %zu\r\n" + "Content-Type: application/json\r\n" + "X-Amduat-Contract-Ref: %s\r\n" + "Connection: close\r\n" + "\r\n", + artifact.bytes.len, + hex); + free(hex); + if (n <= 0 || (size_t)n >= sizeof(hdr)) { + amduat_asl_artifact_free(&artifact); + return false; + } + if (!amduatd_write_all(fd, (const uint8_t *)hdr, (size_t)n)) { + amduat_asl_artifact_free(&artifact); + return false; + } + if (artifact.bytes.len != 0) { + bool ok = amduatd_write_all(fd, artifact.bytes.data, artifact.bytes.len); + amduat_asl_artifact_free(&artifact); + return ok; + } + amduat_asl_artifact_free(&artifact); + return true; + } } - return amduatd_http_send_json(fd, 200, "OK", json, false); } static bool amduatd_seed_api_contract(amduat_asl_store_t *store, @@ -1518,7 +1584,7 @@ static bool amduatd_handle_conn(int fd, return amduatd_handle_meta(fd, cfg, api_contract_ref, true); } if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/contract") == 0) { - return amduatd_handle_get_contract(fd, api_contract_ref); + return amduatd_handle_get_contract(fd, store, &req, api_contract_ref); } if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/artifacts") == 0) {