2026-01-23 20:55:41 +01:00
# ifndef _GNU_SOURCE
# define _GNU_SOURCE
# endif
2025-12-22 15:23:54 +01:00
# define _POSIX_C_SOURCE 200809L
# include "amduat/asl/artifact_io.h"
2026-01-23 20:55:41 +01:00
# include "amduat/asl/collection.h"
# include "amduat/asl/collection_view.h"
# include "amduat/asl/none.h"
# include "amduat/asl/record.h"
2026-01-23 22:28:56 +01:00
# include "amduat/asl/asl_pointer_fs.h"
2025-12-22 15:23:54 +01:00
# include "amduat/asl/asl_store_fs.h"
# include "amduat/asl/asl_store_fs_meta.h"
# include "amduat/asl/ref_text.h"
# include "amduat/asl/store.h"
2025-12-22 19:37:41 +01:00
# include "amduat/asl/ref_derive.h"
2025-12-22 15:23:54 +01:00
# include "amduat/enc/asl1_core_codec.h"
2026-01-21 19:51:26 +01:00
# include "amduat/enc/asl_log.h"
# include "amduat/enc/fer1_receipt.h"
# include "amduat/fed/replay.h"
2025-12-22 19:10:14 +01:00
# include "amduat/enc/pel1_result.h"
2025-12-22 20:14:47 +01:00
# include "amduat/enc/pel_program_dag.h"
2025-12-22 21:03:00 +01:00
# include "amduat/enc/tgk1_edge.h"
2025-12-23 09:14:58 +01:00
# include "amduat/fer/receipt.h"
2025-12-22 19:10:14 +01:00
# include "amduat/format/pel.h"
2025-12-22 21:03:00 +01:00
# include "amduat/tgk/core.h"
2026-01-21 19:51:26 +01:00
# include "federation/coord.h"
# include "federation/transport_stub.h"
2025-12-22 20:14:47 +01:00
# include "amduat/pel/program_dag.h"
2025-12-22 19:10:14 +01:00
# include "amduat/pel/program_dag_desc.h"
2025-12-22 20:14:47 +01:00
# include "amduat/pel/opreg_kernel.h"
2025-12-22 19:10:14 +01:00
# include "amduat/pel/run.h"
2025-12-22 20:14:47 +01:00
# include "amduat/util/hex.h"
2026-01-23 20:55:41 +01:00
# include "amduat/util/log.h"
2026-01-23 23:08:41 +01:00
# include "amduat/hash/asl1.h"
2026-01-24 06:29:51 +01:00
# include "amduatd_concepts.h"
2026-01-24 06:50:53 +01:00
# include "amduatd_http.h"
2026-01-23 23:30:29 +01:00
# include "amduatd_ui.h"
2026-01-24 03:03:54 +01:00
# include "amduatd_caps.h"
2026-01-24 03:21:14 +01:00
# include "amduatd_space.h"
2026-01-24 08:44:28 +01:00
# include "amduatd_store.h"
2026-01-24 09:42:02 +01:00
# include "amduatd_derivation_index.h"
2025-12-22 15:23:54 +01:00
# include <errno.h>
# include <signal.h>
# include <stdbool.h>
# include <stddef.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <strings.h>
# include <string.h>
2025-12-22 21:03:00 +01:00
# include <fcntl.h>
2026-01-21 19:51:26 +01:00
# include <time.h>
2026-01-23 20:55:41 +01:00
# include <pwd.h>
2025-12-22 15:23:54 +01:00
2026-01-21 19:51:26 +01:00
# include <sys/select.h>
2025-12-22 15:23:54 +01:00
# include <sys/socket.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <sys/un.h>
# include <unistd.h>
2025-12-22 21:13:26 +01:00
typedef struct amduatd_strbuf {
char * data ;
size_t len ;
size_t cap ;
} amduatd_strbuf_t ;
2026-01-24 06:29:51 +01:00
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 ) ;
2026-01-23 23:30:29 +01:00
# if AMDUATD_ENABLE_UI
bool amduatd_seed_ui_html ( amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
amduat_reference_t * out_ref ) ;
# endif
2026-01-24 06:29:51 +01:00
bool amduatd_seed_ms_ui_state ( amduat_asl_store_t * store ,
amduatd_concepts_t * concepts ,
const amduatd_cfg_t * dcfg ) ;
2026-01-21 19:51:26 +01:00
static uint64_t amduatd_now_ms ( void ) {
struct timespec ts ;
if ( clock_gettime ( CLOCK_MONOTONIC , & ts ) ! = 0 ) {
return 0 ;
}
return ( ( uint64_t ) ts . tv_sec * 1000u ) + ( ( uint64_t ) ts . tv_nsec / 1000000u ) ;
}
2025-12-22 21:13:26 +01:00
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 ) ;
2025-12-22 15:23:54 +01:00
static const char * const AMDUATD_DEFAULT_ROOT = " .amduat-asl " ;
static const char * const AMDUATD_DEFAULT_SOCK = " amduatd.sock " ;
2026-01-21 19:51:26 +01:00
static const uint64_t AMDUATD_FED_TICK_MS = 1000u ;
2026-01-24 06:29:51 +01:00
static const char k_amduatd_contract_v1_json [ ] =
" { "
" \" contract \" : \" AMDUATD/API/1 \" , "
" \" base_path \" : \" /v1 \" , "
" \" endpoints \" :[ "
" { \" method \" : \" GET \" , \" path \" : \" /v1/ui \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/meta \" }, "
" { \" method \" : \" HEAD \" , \" path \" : \" /v1/meta \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/contract \" }, "
" { \" method \" : \" POST \" , \" path \" : \" /v1/capabilities \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/cap/resolve \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/fed/records \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/fed/artifacts/{ref} \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/fed/status \" }, "
" { \" method \" : \" POST \" , \" path \" : \" /v1/concepts \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/concepts \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/concepts/{name} \" }, "
" { \" 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/context_frames \" } "
" ], "
" \" schemas \" :{ "
" \" capability_mint_request \" :{ "
" \" type \" : \" object \" , "
" \" required \" :[ \" kind \" , \" target \" , \" expiry_seconds \" ], "
" \" properties \" :{ "
" \" kind \" :{ \" type \" : \" string \" }, "
" \" target \" :{ \" type \" : \" object \" }, "
" \" expiry_seconds \" :{ \" type \" : \" integer \" } "
" } "
" }, "
" \" capability_mint_response \" :{ "
" \" type \" : \" object \" , "
" \" required \" :[ \" token \" ], "
" \" properties \" :{ "
" \" token \" :{ \" type \" : \" string \" } "
" } "
" }, "
" \" pel_run_request \" :{ "
" \" type \" : \" object \" , "
" \" required \" :[ \" program_ref \" , \" input_refs \" ], "
" \" properties \" :{ "
" \" 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' \" }, "
" \" 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 \" }, "
" \" executor_fingerprint_ref \" :{ \" type \" : \" string \" , \" description \" : \" hex ref or concept name \" }, "
" \" run_id_hex \" :{ \" type \" : \" string \" , \" description \" : \" hex bytes \" }, "
" \" limits \" :{ \" type \" : \" object \" , \" required \" :[ \" cpu_ms \" , \" wall_ms \" , \" max_rss_kib \" , \" io_reads \" , \" io_writes \" ], \" properties \" :{ "
" \" cpu_ms \" :{ \" type \" : \" integer \" }, "
" \" wall_ms \" :{ \" type \" : \" integer \" }, "
" \" max_rss_kib \" :{ \" type \" : \" integer \" }, "
" \" io_reads \" :{ \" type \" : \" integer \" }, "
" \" io_writes \" :{ \" type \" : \" integer \" } "
" }}, "
" \" logs \" :{ \" type \" : \" array \" , \" items \" :{ \" type \" : \" object \" , \" required \" :[ \" kind \" , \" log_ref \" , \" sha256_hex \" ], \" properties \" :{ "
" \" kind \" :{ \" type \" : \" integer \" }, "
" \" log_ref \" :{ \" type \" : \" string \" , \" description \" : \" hex ref or concept name \" }, "
" \" sha256_hex \" :{ \" type \" : \" string \" , \" description \" : \" hex bytes \" } "
" }}}, "
" \" determinism_level \" :{ \" type \" : \" integer \" , \" description \" : \" 0-255 \" }, "
" \" rng_seed_hex \" :{ \" type \" : \" string \" , \" description \" : \" hex bytes \" }, "
" \" signature_hex \" :{ \" type \" : \" string \" , \" description \" : \" hex bytes \" }, "
" \" started_at \" :{ \" type \" : \" integer \" }, "
" \" completed_at \" :{ \" type \" : \" integer \" } "
" } "
" } "
" } "
" }, "
" \" pel_run_response \" :{ "
" \" type \" : \" object \" , "
" \" required \" :[ \" result_ref \" , \" output_refs \" , \" status \" ], "
" \" 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 \" } "
" } "
" }, "
" \" fed_records_response \" :{ "
" \" type \" : \" object \" , "
" \" required \" :[ \" domain_id \" , \" snapshot_id \" , \" log_prefix \" , \" next_logseq \" , \" records \" ], "
" \" properties \" :{ "
" \" domain_id \" :{ \" type \" : \" integer \" }, "
" \" snapshot_id \" :{ \" type \" : \" integer \" }, "
" \" log_prefix \" :{ \" type \" : \" integer \" }, "
" \" next_logseq \" :{ \" type \" : \" integer \" , \" description \" : \" Paging cursor; last emitted logseq + 1, or from_logseq if no records emitted. \" }, "
" \" records \" :{ \" type \" : \" array \" , \" items \" :{ "
" \" type \" : \" object \" , "
" \" required \" :[ \" domain_id \" , \" type \" , \" ref \" , \" logseq \" , \" snapshot_id \" , \" log_prefix \" ], "
" \" properties \" :{ "
" \" domain_id \" :{ \" type \" : \" integer \" }, "
" \" type \" :{ \" type \" : \" integer \" }, "
" \" ref \" :{ \" type \" : \" string \" }, "
" \" logseq \" :{ \" type \" : \" integer \" }, "
" \" snapshot_id \" :{ \" type \" : \" integer \" }, "
" \" log_prefix \" :{ \" type \" : \" integer \" }, "
" \" visibility \" :{ \" type \" : \" integer \" }, "
" \" has_source \" :{ \" type \" : \" boolean \" }, "
" \" source_domain \" :{ \" type \" : \" integer \" }, "
" \" notes \" :{ \" type \" : \" string \" , \" description \" : \" Type mapping: ARTIFACT_PUBLISH -> ARTIFACT, PER when type_tag=FER1_RECEIPT_1, TGK_EDGE when type_tag=TGK1_EDGE_V1; ARTIFACT_UNPUBLISH -> TOMBSTONE. \" } "
" } "
" }} "
" } "
" }, "
" \" fed_status_response \" :{ "
" \" type \" : \" object \" , "
" \" required \" :[ \" status \" , \" domain_id \" , \" registry_ref \" , \" last_tick_ms \" ], "
" \" properties \" :{ "
" \" status \" :{ \" type \" : \" string \" }, "
" \" domain_id \" :{ \" type \" : \" integer \" }, "
" \" registry_ref \" :{ \" type \" :[ \" string \" , \" null \" ]}, "
" \" last_tick_ms \" :{ \" type \" : \" integer \" } "
" } "
" }, "
" \" 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 \" ], "
" \" properties \" :{ "
" \" nodes \" :{ \" type \" : \" array \" }, "
" \" roots \" :{ \" type \" : \" array \" } "
" } "
" } "
" } "
" } \n " ;
2026-01-23 23:08:41 +01:00
2026-01-24 06:29:51 +01:00
static volatile sig_atomic_t amduatd_should_exit = 0 ;
2026-01-23 20:55:41 +01:00
2026-01-24 06:29:51 +01:00
static void amduatd_on_signal ( int signo ) {
( void ) signo ;
amduatd_should_exit = 1 ;
2026-01-23 20:55:41 +01:00
}
typedef struct {
uid_t * uids ;
size_t len ;
size_t cap ;
} amduatd_allowlist_t ;
static void amduatd_allowlist_free ( amduatd_allowlist_t * list ) {
if ( list = = NULL ) {
return ;
}
free ( list - > uids ) ;
list - > uids = NULL ;
list - > len = 0 ;
list - > cap = 0 ;
}
static bool amduatd_allowlist_add ( amduatd_allowlist_t * list , uid_t uid ) {
if ( list = = NULL ) {
return false ;
}
for ( size_t i = 0u ; i < list - > len ; + + i ) {
if ( list - > uids [ i ] = = uid ) {
return true ;
}
}
if ( list - > len = = list - > cap ) {
size_t next_cap = list - > cap ! = 0 ? list - > cap * 2u : 8u ;
uid_t * next = ( uid_t * ) realloc ( list - > uids , next_cap * sizeof ( * next ) ) ;
if ( next = = NULL ) {
return false ;
}
list - > uids = next ;
list - > cap = next_cap ;
}
list - > uids [ list - > len + + ] = uid ;
return true ;
}
static bool amduatd_actor_allowed ( const amduatd_allowlist_t * list ,
bool has_uid ,
uid_t uid ) {
if ( list = = NULL | | list - > len = = 0u ) {
return true ;
}
if ( ! has_uid ) {
return false ;
}
for ( size_t i = 0u ; i < list - > len ; + + i ) {
if ( list - > uids [ i ] = = uid ) {
return true ;
}
}
return false ;
}
static bool amduatd_get_peer_actor ( int client_fd ,
amduat_octets_t * out_actor ,
bool * out_has_actor ,
bool * out_has_uid ,
uid_t * out_uid ) {
char * actor = NULL ;
if ( out_actor = = NULL | | out_has_actor = = NULL | |
out_has_uid = = NULL | | out_uid = = NULL ) {
return false ;
}
* out_actor = amduat_octets ( NULL , 0u ) ;
* out_has_actor = false ;
* out_has_uid = false ;
* out_uid = 0 ;
# ifdef SO_PEERCRED
{
struct ucred cred ;
socklen_t len = ( socklen_t ) sizeof ( cred ) ;
if ( getsockopt ( client_fd ,
SOL_SOCKET ,
SO_PEERCRED ,
& cred ,
& len ) = = 0 & &
len = = sizeof ( cred ) ) {
int needed = snprintf ( NULL , 0 ,
" uid:%u gid:%u pid:%u " ,
( unsigned int ) cred . uid ,
( unsigned int ) cred . gid ,
( unsigned int ) cred . pid ) ;
if ( needed < 0 ) {
return false ;
}
actor = ( char * ) malloc ( ( size_t ) needed + 1u ) ;
if ( actor = = NULL ) {
return false ;
}
snprintf ( actor ,
( size_t ) needed + 1u ,
" uid:%u gid:%u pid:%u " ,
( unsigned int ) cred . uid ,
( unsigned int ) cred . gid ,
( unsigned int ) cred . pid ) ;
* out_actor = amduat_octets ( ( uint8_t * ) actor , ( size_t ) needed ) ;
* out_has_actor = true ;
* out_has_uid = true ;
* out_uid = cred . uid ;
amduat_log ( AMDUAT_LOG_DEBUG , " request actor=%s " , actor ) ;
return true ;
}
}
# endif
actor = strdup ( " unknown " ) ;
if ( actor = = NULL ) {
return false ;
}
* out_actor = amduat_octets ( ( uint8_t * ) actor , strlen ( actor ) ) ;
* out_has_actor = true ;
* out_has_uid = false ;
* out_uid = 0 ;
amduat_log ( AMDUAT_LOG_DEBUG , " request actor=unknown " ) ;
return true ;
}
2025-12-22 19:10:14 +01:00
static void amduatd_strbuf_free ( amduatd_strbuf_t * b ) {
if ( b = = NULL ) {
return ;
}
free ( b - > data ) ;
b - > data = NULL ;
b - > len = 0 ;
b - > cap = 0 ;
}
static bool amduatd_strbuf_reserve ( amduatd_strbuf_t * b , size_t extra ) {
size_t need ;
size_t next_cap ;
char * next ;
if ( b = = NULL ) {
return false ;
}
if ( extra > ( SIZE_MAX - b - > len ) ) {
return false ;
}
need = b - > len + extra ;
if ( need < = b - > cap ) {
return true ;
}
next_cap = b - > cap ! = 0 ? b - > cap : 256u ;
while ( next_cap < need ) {
if ( next_cap > ( SIZE_MAX / 2u ) ) {
next_cap = need ;
break ;
}
next_cap * = 2u ;
}
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 * s ,
size_t n ) {
if ( b = = NULL ) {
return false ;
}
if ( n = = 0 ) {
return true ;
}
if ( s = = NULL ) {
return false ;
}
if ( ! amduatd_strbuf_reserve ( b , n + 1u ) ) {
return false ;
}
memcpy ( b - > data + b - > len , s , 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 ) ;
}
2025-12-22 20:14:47 +01:00
static bool amduatd_json_parse_u32 ( const char * * p ,
const char * end ,
uint32_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 ' | | v > 0xffffffffULL ) {
return false ;
}
* out = ( uint32_t ) v ;
* p = cur ;
return true ;
}
2025-12-23 09:14:58 +01:00
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 ' ;
}
2025-12-22 19:10:14 +01:00
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 ;
}
2025-12-22 21:03:00 +01:00
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 ;
}
2025-12-22 15:23:54 +01:00
static bool amduatd_parse_type_tag_hex ( const char * text ,
bool * out_has_type_tag ,
amduat_type_tag_t * out_type_tag ) {
char * end = NULL ;
unsigned long v = 0 ;
if ( out_has_type_tag ! = NULL ) {
* out_has_type_tag = false ;
}
if ( text = = NULL | | text [ 0 ] = = ' \0 ' | | out_type_tag = = NULL | |
out_has_type_tag = = NULL ) {
return true ;
}
errno = 0 ;
v = strtoul ( text , & end , 0 ) ;
if ( errno ! = 0 | | end = = text | | * end ! = ' \0 ' | | v > 0xffffffffUL ) {
return false ;
}
* out_has_type_tag = true ;
out_type_tag - > tag_id = ( uint32_t ) v ;
return true ;
}
static bool amduatd_handle_meta ( int fd ,
const amduat_asl_store_fs_config_t * cfg ,
2025-12-22 19:37:41 +01:00
amduat_reference_t api_contract_ref ,
2025-12-22 15:23:54 +01:00
bool head_only ) {
2025-12-22 19:37:41 +01:00
char json [ 1024 ] ;
char * contract_hex = NULL ;
int n ;
if ( api_contract_ref . hash_id ! = 0 & & api_contract_ref . digest . data ! = NULL & &
api_contract_ref . digest . len ! = 0 ) {
( void ) amduat_asl_ref_encode_hex ( api_contract_ref , & contract_hex ) ;
}
if ( contract_hex ! = NULL ) {
n = snprintf ( json ,
sizeof ( json ) ,
" { "
" \" store_id \" : \" %s \" , "
" \" encoding_profile_id \" : \" 0x%04x \" , "
" \" hash_id \" : \" 0x%04x \" , "
" \" api_contract_ref \" : \" %s \" "
" } \n " ,
cfg ! = NULL ? cfg - > store_id : " " ,
cfg ! = NULL ? ( unsigned int ) cfg - > config . encoding_profile_id
: 0u ,
cfg ! = NULL ? ( unsigned int ) cfg - > config . hash_id : 0u ,
contract_hex ) ;
} else {
n = snprintf ( json ,
sizeof ( json ) ,
" { "
" \" store_id \" : \" %s \" , "
" \" encoding_profile_id \" : \" 0x%04x \" , "
" \" hash_id \" : \" 0x%04x \" , "
" \" api_contract_ref \" :null "
" } \n " ,
cfg ! = NULL ? cfg - > store_id : " " ,
cfg ! = NULL ? ( unsigned int ) cfg - > config . encoding_profile_id
: 0u ,
cfg ! = NULL ? ( unsigned int ) cfg - > config . hash_id : 0u ) ;
}
free ( contract_hex ) ;
2025-12-22 15:23:54 +01:00
if ( n < = 0 | | ( size_t ) n > = sizeof ( json ) ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " , " error \n " ,
head_only ) ;
}
return amduatd_http_send_json ( fd , 200 , " OK " , json , head_only ) ;
}
2025-12-22 19:37:41 +01:00
static bool amduatd_handle_get_contract ( int fd ,
2025-12-22 19:51:51 +01:00
amduat_asl_store_t * store ,
const amduatd_http_req_t * req ,
2025-12-22 19:37:41 +01:00
amduat_reference_t api_contract_ref ) {
char * hex = NULL ;
2025-12-22 19:51:51 +01:00
char format [ 32 ] ;
2025-12-22 19:37:41 +01:00
if ( api_contract_ref . hash_id = = 0 | | api_contract_ref . digest . data = = NULL | |
api_contract_ref . digest . len = = 0 ) {
2025-12-22 19:51:51 +01:00
return amduatd_http_send_not_found ( fd , req ) ;
2025-12-22 19:37:41 +01:00
}
2025-12-22 19:51:51 +01:00
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 ) ;
2025-12-22 19:37:41 +01:00
}
2025-12-22 19:51:51 +01:00
{
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 ;
}
2025-12-22 19:37:41 +01:00
}
}
static bool amduatd_seed_api_contract ( 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_contract_v1_json ,
strlen ( k_amduatd_contract_v1_json ) ) ) ;
( 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 ) ;
2026-01-24 06:29:51 +01:00
if ( err ! = AMDUAT_ASL_STORE_OK ) {
return false ;
2025-12-23 09:14:58 +01:00
}
2026-01-24 06:29:51 +01:00
return true ;
2025-12-23 09:14:58 +01:00
}
2026-01-24 03:03:54 +01:00
bool amduatd_handle_get_artifact ( int fd ,
2025-12-23 09:14:58 +01:00
amduat_asl_store_t * store ,
const amduatd_http_req_t * req ,
const char * path ,
bool head_only ) {
char no_query [ 1024 ] ;
char format [ 32 ] ;
const char * ref_text = NULL ;
amduat_reference_t ref ;
amduat_artifact_t artifact ;
amduat_asl_store_error_t err ;
bool want_artifact = false ;
bool want_info = false ;
memset ( & ref , 0 , sizeof ( ref ) ) ;
memset ( & artifact , 0 , sizeof ( artifact ) ) ;
amduatd_path_without_query ( path , no_query , sizeof ( no_query ) ) ;
if ( strncmp ( no_query , " /v1/artifacts/ " , 14 ) ! = 0 ) {
( void ) head_only ;
return amduatd_http_send_not_found ( fd , req ) ;
2025-12-22 15:23:54 +01:00
}
ref_text = no_query + 14 ;
if ( ref_text [ 0 ] = = ' \0 ' ) {
return amduatd_http_send_text ( fd , 400 , " Bad Request " , " missing ref \n " ,
head_only ) ;
}
if ( ! amduat_asl_ref_decode_hex ( ref_text , & ref ) ) {
return amduatd_http_send_text ( fd , 400 , " Bad Request " , " invalid ref \n " ,
head_only ) ;
}
memset ( format , 0 , sizeof ( format ) ) ;
if ( amduatd_query_param ( path , " format " , format , sizeof ( format ) ) ! = NULL ) {
if ( strcmp ( format , " artifact " ) = = 0 ) {
want_artifact = true ;
2025-12-22 21:25:06 +01:00
want_info = false ;
} else if ( strcmp ( format , " info " ) = = 0 ) {
want_artifact = false ;
want_info = true ;
2025-12-22 15:23:54 +01:00
} else if ( strcmp ( format , " raw " ) = = 0 | | format [ 0 ] = = ' \0 ' ) {
want_artifact = false ;
2025-12-22 21:25:06 +01:00
want_info = false ;
2025-12-22 15:23:54 +01:00
} else {
free ( ( void * ) ref . digest . data ) ;
return amduatd_http_send_text ( fd , 400 , " Bad Request " ,
" invalid format \n " , head_only ) ;
}
}
err = amduat_asl_store_get ( store , ref , & artifact ) ;
free ( ( void * ) ref . digest . data ) ;
if ( err = = AMDUAT_ASL_STORE_ERR_NOT_FOUND ) {
2025-12-22 19:46:59 +01:00
if ( ! head_only ) {
return amduatd_http_send_not_found ( fd , req ) ;
}
return amduatd_http_send_text ( fd , 404 , " Not Found " , " not found \n " , true ) ;
2025-12-22 15:23:54 +01:00
}
if ( err ! = AMDUAT_ASL_STORE_OK ) {
amduat_asl_artifact_free ( & artifact ) ;
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" store error \n " , head_only ) ;
}
if ( ! want_artifact ) {
2025-12-22 21:25:06 +01:00
if ( want_info ) {
char json [ 256 ] ;
int n ;
if ( artifact . has_type_tag ) {
n = snprintf ( json ,
sizeof ( json ) ,
" { "
" \" len \" :%zu, "
" \" has_type_tag \" :true, "
" \" type_tag \" : \" 0x%08x \" "
" } \n " ,
artifact . bytes . len ,
( unsigned int ) artifact . type_tag . tag_id ) ;
} else {
n = snprintf ( json ,
sizeof ( json ) ,
" { "
" \" len \" :%zu, "
" \" has_type_tag \" :false, "
" \" type_tag \" :null "
" } \n " ,
artifact . bytes . len ) ;
}
amduat_asl_artifact_free ( & artifact ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( json ) ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" error \n " , head_only ) ;
}
return amduatd_http_send_json ( fd , 200 , " OK " , json , head_only ) ;
}
2025-12-22 15:23:54 +01:00
bool ok = amduatd_http_send_status (
fd ,
200 ,
" OK " ,
" application/octet-stream " ,
artifact . bytes . data ,
artifact . bytes . len ,
head_only ) ;
amduat_asl_artifact_free ( & artifact ) ;
return ok ;
}
{
amduat_octets_t out = amduat_octets ( NULL , 0 ) ;
bool ok = amduat_enc_asl1_core_encode_artifact_v1 ( artifact , & out ) ;
amduat_asl_artifact_free ( & artifact ) ;
if ( ! ok ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" encode error \n " , head_only ) ;
}
ok = amduatd_http_send_status ( fd ,
200 ,
" OK " ,
" application/vnd.amduat.asl.artifact+v1 " ,
out . data ,
out . len ,
head_only ) ;
free ( ( void * ) out . data ) ;
return ok ;
}
}
static bool amduatd_handle_head_artifact ( int fd ,
amduat_asl_store_t * store ,
2025-12-22 19:46:59 +01:00
const amduatd_http_req_t * req ,
2025-12-22 15:23:54 +01:00
const char * path ) {
2025-12-22 19:46:59 +01:00
return amduatd_handle_get_artifact ( fd , store , req , path , true ) ;
2025-12-22 15:23:54 +01:00
}
2026-01-21 19:51:26 +01:00
static bool amduatd_parse_u64_str ( const char * s , uint64_t * out ) {
char * end = NULL ;
unsigned long long v ;
if ( s = = NULL | | out = = NULL | | s [ 0 ] = = ' \0 ' ) {
return false ;
}
errno = 0 ;
v = strtoull ( s , & end , 10 ) ;
if ( errno ! = 0 | | end = = s | | * end ! = ' \0 ' ) {
return false ;
}
* out = ( uint64_t ) v ;
return true ;
}
static bool amduatd_parse_u32_str ( const char * s , uint32_t * out ) {
uint64_t tmp = 0 ;
if ( ! amduatd_parse_u64_str ( s , & tmp ) ) {
return false ;
}
if ( tmp > UINT32_MAX ) {
return false ;
}
* out = ( uint32_t ) tmp ;
return true ;
}
static bool amduatd_handle_get_fed_records ( int fd ,
amduat_asl_store_t * store ,
const amduatd_http_req_t * req ) {
char domain_buf [ 32 ] ;
char from_buf [ 32 ] ;
char limit_buf [ 32 ] ;
uint32_t domain_id = 0 ;
uint64_t from_logseq = 0 ;
uint64_t next_logseq = 0 ;
uint64_t limit = 256 ;
uint64_t emitted = 0 ;
amduat_asl_index_state_t state ;
amduat_asl_log_record_t * records = NULL ;
size_t record_count = 0 ;
size_t i ;
amduatd_strbuf_t b ;
bool first = true ;
if ( store = = NULL | | req = = NULL ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" internal error \n " , false ) ;
}
if ( amduatd_query_param ( req - > path , " domain_id " ,
domain_buf , sizeof ( domain_buf ) ) = = NULL | |
! amduatd_parse_u32_str ( domain_buf , & domain_id ) ) {
return amduatd_http_send_text ( fd , 400 , " Bad Request " ,
" missing domain_id \n " , false ) ;
}
if ( amduatd_query_param ( req - > path , " from_logseq " ,
from_buf , sizeof ( from_buf ) ) ! = NULL ) {
if ( ! amduatd_parse_u64_str ( from_buf , & from_logseq ) ) {
return amduatd_http_send_text ( fd , 400 , " Bad Request " ,
" invalid from_logseq \n " , false ) ;
}
}
if ( amduatd_query_param ( req - > path , " limit " ,
limit_buf , sizeof ( limit_buf ) ) ! = NULL ) {
if ( ! amduatd_parse_u64_str ( limit_buf , & limit ) | | limit = = 0u | |
limit > 10000u ) {
return amduatd_http_send_text ( fd , 400 , " Bad Request " ,
" invalid limit \n " , false ) ;
}
}
2026-01-24 08:44:28 +01:00
if ( store - > ops . log_scan = = NULL | | store - > ops . current_state = = NULL ) {
return amduatd_http_send_text ( fd , 501 , " Not Implemented " ,
" requires index backend \n " , false ) ;
}
2026-01-21 19:51:26 +01:00
if ( ! amduat_asl_index_current_state ( store , & state ) ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" store error \n " , false ) ;
}
2026-01-24 08:44:28 +01:00
{
amduat_asl_store_error_t scan_err ;
scan_err = amduat_asl_log_scan ( store , & records , & record_count ) ;
if ( scan_err = = AMDUAT_ASL_STORE_ERR_UNSUPPORTED ) {
return amduatd_http_send_text ( fd , 501 , " Not Implemented " ,
" requires index backend \n " , false ) ;
}
if ( scan_err ! = AMDUAT_ASL_STORE_OK ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" log scan error \n " , false ) ;
}
2026-01-21 19:51:26 +01:00
}
memset ( & b , 0 , sizeof ( b ) ) ;
if ( ! amduatd_strbuf_append_cstr ( & b , " { " ) ) {
goto fed_records_oom ;
}
{
char header [ 256 ] ;
int n = snprintf ( header ,
sizeof ( header ) ,
" \" domain_id \" :%u, "
" \" snapshot_id \" :%llu, "
" \" log_prefix \" :%llu, "
" \" records \" :[ " ,
( unsigned int ) domain_id ,
( unsigned long long ) state . snapshot_id ,
( unsigned long long ) state . log_position ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( header ) ) {
goto fed_records_oom ;
}
if ( ! amduatd_strbuf_append_cstr ( & b , header ) ) {
goto fed_records_oom ;
}
}
next_logseq = from_logseq ;
for ( i = 0 ; i < record_count ; + + i ) {
const amduat_asl_log_record_t * rec = & records [ i ] ;
amduat_reference_t ref ;
char * ref_hex = NULL ;
char entry [ 512 ] ;
int n ;
if ( rec - > logseq < from_logseq ) {
continue ;
}
memset ( & ref , 0 , sizeof ( ref ) ) ;
if ( rec - > record_type = = AMDUAT_ASL_LOG_RECORD_TOMBSTONE | |
rec - > record_type = = AMDUAT_ASL_LOG_RECORD_ARTIFACT_PUBLISH | |
rec - > record_type = = AMDUAT_ASL_LOG_RECORD_ARTIFACT_UNPUBLISH ) {
if ( ! amduat_asl_log_decode_artifact_ref ( rec - > payload , & ref ) ) {
continue ;
}
} else {
continue ;
}
if ( ! amduat_asl_ref_encode_hex ( ref , & ref_hex ) ) {
amduat_reference_free ( & ref ) ;
goto fed_records_oom ;
}
{
uint32_t rec_type = AMDUAT_FED_REC_TOMBSTONE ;
amduat_artifact_t artifact ;
amduat_asl_store_error_t store_err ;
if ( rec - > record_type = = AMDUAT_ASL_LOG_RECORD_ARTIFACT_PUBLISH ) {
rec_type = AMDUAT_FED_REC_ARTIFACT ;
memset ( & artifact , 0 , sizeof ( artifact ) ) ;
store_err = amduat_asl_store_get ( store , ref , & artifact ) ;
if ( store_err = = AMDUAT_ASL_STORE_OK & & artifact . has_type_tag ) {
if ( artifact . type_tag . tag_id = = AMDUAT_TYPE_TAG_TGK1_EDGE_V1 ) {
rec_type = AMDUAT_FED_REC_TGK_EDGE ;
} else if ( artifact . type_tag . tag_id = =
AMDUAT_TYPE_TAG_FER1_RECEIPT_1 ) {
rec_type = AMDUAT_FED_REC_PER ;
}
}
if ( store_err = = AMDUAT_ASL_STORE_OK ) {
amduat_asl_artifact_free ( & artifact ) ;
}
} else if ( rec - > record_type = = AMDUAT_ASL_LOG_RECORD_ARTIFACT_UNPUBLISH ) {
rec_type = AMDUAT_FED_REC_TOMBSTONE ;
}
n = snprintf ( entry ,
sizeof ( entry ) ,
" %s{ \" domain_id \" :%u, "
" \" type \" :%u, "
" \" ref \" : \" %s \" , "
" \" logseq \" :%llu, "
" \" snapshot_id \" :%llu, "
" \" log_prefix \" :%llu, "
" \" visibility \" :1, "
" \" has_source \" :false, "
" \" source_domain \" :0} " ,
first ? " " : " , " ,
( unsigned int ) domain_id ,
( unsigned int ) rec_type ,
ref_hex ,
( unsigned long long ) rec - > logseq ,
( unsigned long long ) state . snapshot_id ,
( unsigned long long ) state . log_position ) ;
}
free ( ref_hex ) ;
amduat_reference_free ( & ref ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( entry ) ) {
goto fed_records_oom ;
}
if ( ! amduatd_strbuf_append_cstr ( & b , entry ) ) {
goto fed_records_oom ;
}
first = false ;
if ( rec - > logseq > = next_logseq ) {
next_logseq = rec - > logseq + 1u ;
}
emitted + + ;
if ( emitted > = limit ) {
break ;
}
}
{
char tail [ 128 ] ;
int n = snprintf ( tail ,
sizeof ( tail ) ,
" ], \" next_logseq \" :%llu} \n " ,
( unsigned long long ) next_logseq ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( tail ) ) {
goto fed_records_oom ;
}
if ( ! amduatd_strbuf_append_cstr ( & b , tail ) ) {
goto fed_records_oom ;
}
}
amduat_enc_asl_log_free ( records , record_count ) ;
{
bool ok = amduatd_http_send_json ( fd , 200 , " OK " , b . data , false ) ;
amduatd_strbuf_free ( & b ) ;
return ok ;
}
fed_records_oom :
if ( records ! = NULL ) {
amduat_enc_asl_log_free ( records , record_count ) ;
}
amduatd_strbuf_free ( & b ) ;
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" oom \n " , false ) ;
}
static bool amduatd_handle_get_fed_artifact ( int fd ,
amduat_asl_store_t * store ,
const amduatd_http_req_t * req ,
const char * path ) {
char no_query [ 1024 ] ;
const char * ref_text = NULL ;
amduat_reference_t ref ;
amduat_artifact_t artifact ;
amduat_asl_store_error_t err ;
if ( store = = NULL | | req = = NULL | | path = = NULL ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" internal error \n " , false ) ;
}
amduatd_path_without_query ( path , no_query , sizeof ( no_query ) ) ;
if ( strncmp ( no_query , " /v1/fed/artifacts/ " , 18 ) ! = 0 ) {
return amduatd_http_send_text ( fd , 404 , " Not Found " , " not found \n " , false ) ;
}
ref_text = no_query + 18 ;
memset ( & ref , 0 , sizeof ( ref ) ) ;
if ( ! amduat_asl_ref_decode_hex ( ref_text , & ref ) ) {
return amduatd_http_send_text ( fd , 400 , " Bad Request " ,
" invalid ref \n " , false ) ;
}
memset ( & artifact , 0 , sizeof ( artifact ) ) ;
err = amduat_asl_store_get ( store , ref , & artifact ) ;
amduat_reference_free ( & ref ) ;
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 ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" store error \n " , false ) ;
}
{
bool ok = amduatd_http_send_status ( fd ,
200 ,
" OK " ,
" application/octet-stream " ,
artifact . bytes . data ,
artifact . bytes . len ,
false ) ;
amduat_asl_artifact_free ( & artifact ) ;
return ok ;
}
}
static bool amduatd_handle_get_fed_status ( int fd ,
const amduat_fed_coord_t * coord ,
const amduatd_http_req_t * req ) {
amduat_fed_coord_status_t status ;
char * ref_hex = NULL ;
char json [ 512 ] ;
int n ;
( void ) req ;
amduat_fed_coord_get_status ( coord , & status ) ;
if ( status . registry_ref . hash_id ! = 0 & &
status . registry_ref . digest . data ! = NULL ) {
if ( ! amduat_asl_ref_encode_hex ( status . registry_ref , & ref_hex ) ) {
amduat_reference_free ( & status . registry_ref ) ;
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" encode error \n " , false ) ;
}
}
if ( ref_hex ! = NULL ) {
n = snprintf ( json ,
sizeof ( json ) ,
" { \" status \" : \" ok \" , \" domain_id \" :%u, "
" \" registry_ref \" : \" %s \" , \" last_tick_ms \" :%llu} \n " ,
( unsigned int ) status . domain_id ,
ref_hex ,
( unsigned long long ) status . last_tick_ms ) ;
} else {
n = snprintf ( json ,
sizeof ( json ) ,
" { \" status \" : \" ok \" , \" domain_id \" :%u, "
" \" registry_ref \" :null, \" last_tick_ms \" :%llu} \n " ,
( unsigned int ) status . domain_id ,
( unsigned long long ) status . last_tick_ms ) ;
}
free ( ref_hex ) ;
amduat_reference_free ( & status . registry_ref ) ;
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 ) ;
}
2025-12-22 15:23:54 +01:00
static bool amduatd_handle_post_artifacts ( int fd ,
amduat_asl_store_t * store ,
const amduatd_http_req_t * req ) {
uint8_t * body = NULL ;
bool has_type_tag = false ;
amduat_type_tag_t type_tag ;
amduat_asl_io_format_t input_format = AMDUAT_ASL_IO_RAW ;
amduat_artifact_t artifact ;
amduat_reference_t ref ;
amduat_asl_store_error_t err ;
char * ref_hex = NULL ;
char json [ 2048 ] ;
memset ( & artifact , 0 , sizeof ( artifact ) ) ;
memset ( & ref , 0 , sizeof ( ref ) ) ;
memset ( & type_tag , 0 , sizeof ( type_tag ) ) ;
if ( req = = NULL ) {
return amduatd_http_send_text ( fd , 400 , " Bad Request " , " bad request \n " ,
false ) ;
}
if ( req - > content_length > ( 64u * 1024u * 1024u ) ) {
return amduatd_http_send_text ( fd , 413 , " Payload Too Large " ,
" payload too large \n " , false ) ;
}
if ( req - > content_length ! = 0 ) {
body = ( uint8_t * ) malloc ( req - > content_length ) ;
if ( body = = NULL ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" oom \n " , false ) ;
}
if ( ! amduatd_read_exact ( fd , body , req - > content_length ) ) {
free ( body ) ;
return false ;
}
} else {
body = NULL ;
}
if ( req - > content_type [ 0 ] ! = ' \0 ' & &
strstr ( req - > content_type , " application/vnd.amduat.asl.artifact+v1 " ) ! =
NULL ) {
input_format = AMDUAT_ASL_IO_ARTIFACT ;
}
if ( input_format = = AMDUAT_ASL_IO_RAW ) {
if ( ! amduatd_parse_type_tag_hex ( req - > x_type_tag ,
& has_type_tag ,
& type_tag ) ) {
free ( body ) ;
return amduatd_http_send_text ( fd , 400 , " Bad Request " ,
" invalid X-Amduat-Type-Tag \n " , false ) ;
}
}
if ( ! amduat_asl_artifact_from_bytes ( amduat_octets ( body , req - > content_length ) ,
input_format ,
has_type_tag ,
type_tag ,
& artifact ) ) {
free ( body ) ;
return amduatd_http_send_text ( fd , 400 , " Bad Request " ,
" invalid artifact \n " , false ) ;
}
err = amduat_asl_store_put ( store , artifact , & ref ) ;
amduat_asl_artifact_free ( & artifact ) ;
if ( err ! = AMDUAT_ASL_STORE_OK ) {
free ( ( void * ) ref . digest . data ) ;
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" store error \n " , false ) ;
}
if ( ! amduat_asl_ref_encode_hex ( ref , & ref_hex ) ) {
free ( ( void * ) ref . digest . data ) ;
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" encode ref error \n " , false ) ;
}
free ( ( void * ) ref . digest . data ) ;
{
int n = snprintf ( json , sizeof ( json ) , " { \" ref \" : \" %s \" } \n " , ref_hex ) ;
free ( ref_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 ) ;
}
}
2025-12-22 19:10:14 +01:00
static bool amduatd_handle_post_pel_run ( int fd ,
amduat_asl_store_t * store ,
2025-12-22 21:03:00 +01:00
const amduat_asl_store_fs_config_t * cfg ,
const amduatd_concepts_t * concepts ,
2026-01-23 22:28:56 +01:00
const amduatd_cfg_t * dcfg ,
2026-01-24 09:42:02 +01:00
const char * root_path ,
2025-12-22 19:10:14 +01:00
const amduatd_http_req_t * req ) {
uint8_t * body = NULL ;
const char * p = NULL ;
const char * end = NULL ;
bool have_program_ref = false ;
bool have_input_refs = false ;
bool have_scheme_ref = false ;
bool scheme_is_dag = false ;
bool has_params_ref = false ;
2025-12-23 09:14:58 +01:00
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 ;
2026-01-21 19:51:26 +01:00
bool receipt_has_executor_fingerprint = false ;
bool receipt_has_run_id = false ;
bool receipt_has_limits = false ;
bool receipt_has_logs = false ;
bool receipt_has_determinism = false ;
bool receipt_has_rng_seed = false ;
bool receipt_has_signature = false ;
2025-12-23 09:14:58 +01:00
char * receipt_evaluator_id = NULL ;
uint8_t * receipt_parity_digest = NULL ;
size_t receipt_parity_digest_len = 0 ;
2026-01-21 19:51:26 +01:00
uint8_t * receipt_run_id = NULL ;
size_t receipt_run_id_len = 0 ;
uint8_t * receipt_rng_seed = NULL ;
size_t receipt_rng_seed_len = 0 ;
uint8_t * receipt_signature = NULL ;
size_t receipt_signature_len = 0 ;
2025-12-23 09:14:58 +01:00
uint64_t receipt_started_at = 0 ;
uint64_t receipt_completed_at = 0 ;
2026-01-21 19:51:26 +01:00
uint8_t receipt_determinism_level = 0 ;
2025-12-23 09:14:58 +01:00
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 ;
2026-01-21 19:51:26 +01:00
amduat_reference_t receipt_executor_fingerprint_ref ;
amduat_fer1_limits_t receipt_limits ;
amduat_fer1_log_entry_t * receipt_logs = NULL ;
size_t receipt_logs_len = 0 ;
size_t receipt_logs_cap = 0 ;
2025-12-22 19:10:14 +01:00
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 ;
2025-12-23 09:14:58 +01:00
amduat_artifact_t receipt_artifact ;
amduat_reference_t receipt_ref ;
bool receipt_emitted = false ;
2025-12-22 19:10:14 +01:00
int status_code = 200 ;
const char * status_reason = " OK " ;
bool ok = false ;
memset ( & scheme_ref , 0 , sizeof ( scheme_ref ) ) ;
memset ( & program_ref , 0 , sizeof ( program_ref ) ) ;
memset ( & params_ref , 0 , sizeof ( params_ref ) ) ;
memset ( & run_result , 0 , sizeof ( run_result ) ) ;
2025-12-23 09:14:58 +01:00
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 ) ) ;
2026-01-21 19:51:26 +01:00
memset ( & receipt_executor_fingerprint_ref , 0 ,
sizeof ( receipt_executor_fingerprint_ref ) ) ;
memset ( & receipt_limits , 0 , sizeof ( receipt_limits ) ) ;
2025-12-23 09:14:58 +01:00
memset ( & receipt_artifact , 0 , sizeof ( receipt_artifact ) ) ;
memset ( & receipt_ref , 0 , sizeof ( receipt_ref ) ) ;
2025-12-22 19:10:14 +01:00
if ( store = = NULL | | req = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
2025-12-22 21:03:00 +01:00
if ( cfg = = NULL | | concepts = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
2025-12-22 19:10:14 +01:00
if ( req - > content_length > ( 1u * 1024u * 1024u ) ) {
return amduatd_send_json_error ( fd , 413 , " Payload Too Large " ,
" payload too large " ) ;
}
if ( req - > content_length = = 0 ) {
return amduatd_send_json_error ( fd , 400 , " Bad Request " , " missing body " ) ;
}
body = NULL ;
if ( req - > content_length ! = 0 ) {
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 pel_run_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 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid json key " ) ;
goto pel_run_cleanup ;
}
if ( ! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto pel_run_cleanup ;
}
if ( key_len = = strlen ( " program_ref " ) & &
memcmp ( key , " program_ref " , key_len ) = = 0 ) {
2025-12-22 21:03:00 +01:00
amduatd_ref_status_t st ;
2025-12-22 19:10:14 +01:00
if ( have_program_ref ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" duplicate program_ref " ) ;
goto pel_run_cleanup ;
}
2025-12-22 21:03:00 +01:00
if ( ! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid program_ref " ) ;
goto pel_run_cleanup ;
}
2026-01-23 22:28:56 +01:00
st = amduatd_decode_ref_or_name_latest ( store , cfg , concepts , dcfg , sv ,
sv_len ,
2025-12-22 21:03:00 +01:00
& program_ref ) ;
if ( st = = AMDUATD_REF_ERR_NOT_FOUND ) {
ok = amduatd_send_json_error ( fd , 404 , " Not Found " ,
" program_ref not found " ) ;
goto pel_run_cleanup ;
}
if ( st ! = AMDUATD_REF_OK ) {
2025-12-22 19:10:14 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid program_ref " ) ;
goto pel_run_cleanup ;
}
have_program_ref = true ;
} else if ( key_len = = strlen ( " scheme_ref " ) & &
memcmp ( key , " scheme_ref " , key_len ) = = 0 ) {
if ( have_scheme_ref ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" duplicate scheme_ref " ) ;
goto pel_run_cleanup ;
}
if ( ! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid scheme_ref " ) ;
goto pel_run_cleanup ;
}
if ( sv_len = = 3 & & memcmp ( sv , " dag " , 3 ) = = 0 ) {
scheme_ref = amduat_pel_program_dag_scheme_ref ( ) ;
scheme_is_dag = true ;
} else if ( ! amduatd_decode_ref_hex_str ( sv , sv_len , & scheme_ref ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid scheme_ref " ) ;
goto pel_run_cleanup ;
}
have_scheme_ref = true ;
} else if ( key_len = = strlen ( " params_ref " ) & &
memcmp ( key , " params_ref " , key_len ) = = 0 ) {
2025-12-22 21:03:00 +01:00
amduatd_ref_status_t st ;
2025-12-22 19:10:14 +01:00
if ( has_params_ref ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" duplicate params_ref " ) ;
goto pel_run_cleanup ;
}
2025-12-22 21:03:00 +01:00
if ( ! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid params_ref " ) ;
goto pel_run_cleanup ;
}
2026-01-23 22:28:56 +01:00
st = amduatd_decode_ref_or_name_latest ( store , cfg , concepts , dcfg , sv ,
sv_len ,
2025-12-22 21:03:00 +01:00
& params_ref ) ;
if ( st = = AMDUATD_REF_ERR_NOT_FOUND ) {
ok = amduatd_send_json_error ( fd , 404 , " Not Found " ,
" params_ref not found " ) ;
goto pel_run_cleanup ;
}
if ( st ! = AMDUATD_REF_OK ) {
2025-12-22 19:10:14 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid params_ref " ) ;
goto pel_run_cleanup ;
}
has_params_ref = true ;
} else if ( key_len = = strlen ( " input_refs " ) & &
memcmp ( key , " input_refs " , key_len ) = = 0 ) {
size_t cap = 0 ;
if ( have_input_refs ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" duplicate input_refs " ) ;
goto pel_run_cleanup ;
}
if ( ! amduatd_json_expect ( & p , end , ' [ ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input_refs " ) ;
goto pel_run_cleanup ;
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
have_input_refs = true ;
} else {
for ( ; ; ) {
amduat_reference_t ref ;
memset ( & ref , 0 , sizeof ( ref ) ) ;
2025-12-22 21:03:00 +01:00
if ( ! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
2025-12-22 19:10:14 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input_ref " ) ;
goto pel_run_cleanup ;
}
2025-12-22 21:03:00 +01:00
{
amduatd_ref_status_t st = amduatd_decode_ref_or_name_latest (
2026-01-23 22:28:56 +01:00
store , cfg , concepts , dcfg , sv , sv_len , & ref ) ;
2025-12-22 21:03:00 +01:00
if ( st = = AMDUATD_REF_ERR_NOT_FOUND ) {
ok = amduatd_send_json_error ( fd , 404 , " Not Found " ,
" input_ref not found " ) ;
goto pel_run_cleanup ;
}
if ( st ! = AMDUATD_REF_OK ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input_ref " ) ;
goto pel_run_cleanup ;
}
}
2025-12-22 19:10:14 +01:00
if ( input_refs_len = = cap ) {
size_t next_cap = cap ! = 0 ? cap * 2u : 4u ;
amduat_reference_t * next ;
if ( next_cap > ( SIZE_MAX / sizeof ( * input_refs ) ) ) {
amduat_reference_free ( & ref ) ;
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" too many input_refs " ) ;
goto pel_run_cleanup ;
}
next = ( amduat_reference_t * ) realloc (
input_refs , next_cap * sizeof ( * input_refs ) ) ;
if ( next = = NULL ) {
amduat_reference_free ( & ref ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" oom " ) ;
goto pel_run_cleanup ;
}
input_refs = next ;
cap = next_cap ;
}
input_refs [ input_refs_len + + ] = ref ;
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur > = end ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input_refs " ) ;
goto pel_run_cleanup ;
}
if ( * cur = = ' , ' ) {
p = cur + 1 ;
continue ;
}
if ( * cur = = ' ] ' ) {
p = cur + 1 ;
have_input_refs = true ;
break ;
}
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input_refs " ) ;
goto pel_run_cleanup ;
}
}
2025-12-23 09:14:58 +01:00
} 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 ;
}
2026-01-23 22:28:56 +01:00
st = amduatd_decode_ref_or_name_latest ( store , cfg , concepts , dcfg ,
sv , sv_len ,
& receipt_input_manifest_ref ) ;
2025-12-23 09:14:58 +01:00
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 ;
}
2026-01-23 22:28:56 +01:00
st = amduatd_decode_ref_or_name_latest ( store , cfg , concepts , dcfg ,
sv , sv_len ,
& receipt_environment_ref ) ;
2025-12-23 09:14:58 +01:00
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 ;
}
2026-01-23 22:28:56 +01:00
st = amduatd_decode_ref_or_name_latest ( store , cfg , concepts , dcfg ,
sv , sv_len ,
& receipt_executor_ref ) ;
2025-12-23 09:14:58 +01:00
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 ;
}
2026-01-23 22:28:56 +01:00
st = amduatd_decode_ref_or_name_latest ( store , cfg , concepts , dcfg ,
sv , sv_len , & receipt_sbom_ref ) ;
2025-12-23 09:14:58 +01:00
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 ;
2026-01-21 19:51:26 +01:00
} else if ( rkey_len = = strlen ( " executor_fingerprint_ref " ) & &
memcmp ( rkey , " executor_fingerprint_ref " , rkey_len ) = = 0 ) {
amduatd_ref_status_t st ;
if ( receipt_has_executor_fingerprint | |
! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid executor_fingerprint_ref " ) ;
goto pel_run_cleanup ;
}
st = amduatd_decode_ref_or_name_latest (
2026-01-23 22:28:56 +01:00
store , cfg , concepts , dcfg , sv , sv_len ,
2026-01-21 19:51:26 +01:00
& receipt_executor_fingerprint_ref ) ;
if ( st = = AMDUATD_REF_ERR_NOT_FOUND ) {
ok = amduatd_send_json_error ( fd , 404 , " Not Found " ,
" executor_fingerprint_ref not found " ) ;
goto pel_run_cleanup ;
}
if ( st ! = AMDUATD_REF_OK ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid executor_fingerprint_ref " ) ;
goto pel_run_cleanup ;
}
receipt_has_executor_fingerprint = true ;
2025-12-23 09:14:58 +01:00
} 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 ;
2026-01-21 19:51:26 +01:00
} else if ( rkey_len = = strlen ( " run_id_hex " ) & &
memcmp ( rkey , " run_id_hex " , rkey_len ) = = 0 ) {
char * tmp = NULL ;
uint8_t * bytes = NULL ;
size_t bytes_len = 0 ;
if ( receipt_has_run_id | |
! 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 run_id_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 run_id_hex " ) ;
goto pel_run_cleanup ;
}
free ( tmp ) ;
free ( receipt_run_id ) ;
receipt_run_id = bytes ;
receipt_run_id_len = bytes_len ;
receipt_has_run_id = true ;
} else if ( rkey_len = = strlen ( " limits " ) & &
memcmp ( rkey , " limits " , rkey_len ) = = 0 ) {
bool have_cpu = false ;
bool have_wall = false ;
bool have_rss = false ;
bool have_reads = false ;
bool have_writes = false ;
if ( receipt_has_limits | | ! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid limits " ) ;
goto pel_run_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 , & sv , & sv_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid limits " ) ;
goto pel_run_cleanup ;
}
if ( sv_len = = strlen ( " cpu_ms " ) & &
memcmp ( sv , " cpu_ms " , sv_len ) = = 0 ) {
if ( have_cpu | | ! amduatd_json_parse_u64 ( & p , end ,
& receipt_limits . cpu_ms ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid limits cpu_ms " ) ;
goto pel_run_cleanup ;
}
have_cpu = true ;
} else if ( sv_len = = strlen ( " wall_ms " ) & &
memcmp ( sv , " wall_ms " , sv_len ) = = 0 ) {
if ( have_wall | | ! amduatd_json_parse_u64 ( & p , end ,
& receipt_limits . wall_ms ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid limits wall_ms " ) ;
goto pel_run_cleanup ;
}
have_wall = true ;
} else if ( sv_len = = strlen ( " max_rss_kib " ) & &
memcmp ( sv , " max_rss_kib " , sv_len ) = = 0 ) {
if ( have_rss | | ! amduatd_json_parse_u64 ( & p , end ,
& receipt_limits . max_rss_kib ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid limits max_rss_kib " ) ;
goto pel_run_cleanup ;
}
have_rss = true ;
} else if ( sv_len = = strlen ( " io_reads " ) & &
memcmp ( sv , " io_reads " , sv_len ) = = 0 ) {
if ( have_reads | | ! amduatd_json_parse_u64 ( & p , end ,
& receipt_limits . io_reads ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid limits io_reads " ) ;
goto pel_run_cleanup ;
}
have_reads = true ;
} else if ( sv_len = = strlen ( " io_writes " ) & &
memcmp ( sv , " io_writes " , sv_len ) = = 0 ) {
if ( have_writes | | ! amduatd_json_parse_u64 ( & p , end ,
& receipt_limits . io_writes ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid limits io_writes " ) ;
goto pel_run_cleanup ;
}
have_writes = true ;
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid limits " ) ;
goto pel_run_cleanup ;
}
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur > = end ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid limits " ) ;
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 limits " ) ;
goto pel_run_cleanup ;
}
if ( ! have_cpu | | ! have_wall | | ! have_rss | |
! have_reads | | ! have_writes ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing limits fields " ) ;
goto pel_run_cleanup ;
}
receipt_has_limits = true ;
} else if ( rkey_len = = strlen ( " logs " ) & &
memcmp ( rkey , " logs " , rkey_len ) = = 0 ) {
if ( receipt_has_logs | | ! amduatd_json_expect ( & p , end , ' [ ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid logs " ) ;
goto pel_run_cleanup ;
}
receipt_has_logs = true ;
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
} else {
for ( ; ; ) {
amduat_fer1_log_entry_t entry ;
bool have_kind = false ;
bool have_log_ref = false ;
bool have_sha = false ;
memset ( & entry , 0 , sizeof ( entry ) ) ;
if ( ! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid log entry " ) ;
goto pel_run_cleanup ;
}
for ( ; ; ) {
const char * lkey = NULL ;
size_t lkey_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 , & lkey , & lkey_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid log entry " ) ;
goto pel_run_cleanup ;
}
if ( lkey_len = = strlen ( " kind " ) & &
memcmp ( lkey , " kind " , lkey_len ) = = 0 ) {
uint32_t kind = 0 ;
if ( have_kind | |
! amduatd_json_parse_u32_loose ( & p , end , & kind ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid log kind " ) ;
goto pel_run_cleanup ;
}
entry . kind = kind ;
have_kind = true ;
} else if ( lkey_len = = strlen ( " log_ref " ) & &
memcmp ( lkey , " log_ref " , lkey_len ) = = 0 ) {
amduatd_ref_status_t st ;
if ( have_log_ref | |
! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid log_ref " ) ;
goto pel_run_cleanup ;
}
st = amduatd_decode_ref_or_name_latest (
2026-01-23 22:28:56 +01:00
store , cfg , concepts , dcfg , sv , sv_len , & entry . log_ref ) ;
2026-01-21 19:51:26 +01:00
if ( st = = AMDUATD_REF_ERR_NOT_FOUND ) {
ok = amduatd_send_json_error ( fd , 404 , " Not Found " ,
" log_ref not found " ) ;
goto pel_run_cleanup ;
}
if ( st ! = AMDUATD_REF_OK ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid log_ref " ) ;
goto pel_run_cleanup ;
}
have_log_ref = true ;
} else if ( lkey_len = = strlen ( " sha256_hex " ) & &
memcmp ( lkey , " sha256_hex " , lkey_len ) = = 0 ) {
char * tmp = NULL ;
uint8_t * bytes = NULL ;
size_t bytes_len = 0 ;
if ( have_sha | |
! 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 sha256_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 sha256_hex " ) ;
goto pel_run_cleanup ;
}
free ( tmp ) ;
entry . sha256 = amduat_octets ( bytes , bytes_len ) ;
have_sha = true ;
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid log entry " ) ;
goto pel_run_cleanup ;
}
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur > = end ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid log entry " ) ;
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 log entry " ) ;
goto pel_run_cleanup ;
}
if ( ! have_kind | | ! have_log_ref | | ! have_sha ) {
if ( have_log_ref ) {
amduat_reference_free ( & entry . log_ref ) ;
}
if ( have_sha ) {
amduat_octets_free ( & entry . sha256 ) ;
}
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing log fields " ) ;
goto pel_run_cleanup ;
}
if ( receipt_logs_len = = receipt_logs_cap ) {
size_t next_cap = receipt_logs_cap ! = 0 ? receipt_logs_cap * 2u : 4u ;
amduat_fer1_log_entry_t * next =
( amduat_fer1_log_entry_t * ) realloc (
receipt_logs , next_cap * sizeof ( * receipt_logs ) ) ;
if ( next = = NULL ) {
amduat_reference_free ( & entry . log_ref ) ;
amduat_octets_free ( & entry . sha256 ) ;
ok = amduatd_send_json_error ( fd , 500 ,
" Internal Server Error " ,
" oom " ) ;
goto pel_run_cleanup ;
}
receipt_logs = next ;
receipt_logs_cap = next_cap ;
}
receipt_logs [ receipt_logs_len + + ] = entry ;
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 logs " ) ;
goto pel_run_cleanup ;
}
}
} else if ( rkey_len = = strlen ( " determinism_level " ) & &
memcmp ( rkey , " determinism_level " , rkey_len ) = = 0 ) {
uint32_t level = 0 ;
if ( receipt_has_determinism | |
! amduatd_json_parse_u32_loose ( & p , end , & level ) | |
level > 255u ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid determinism_level " ) ;
goto pel_run_cleanup ;
}
receipt_determinism_level = ( uint8_t ) level ;
receipt_has_determinism = true ;
} else if ( rkey_len = = strlen ( " rng_seed_hex " ) & &
memcmp ( rkey , " rng_seed_hex " , rkey_len ) = = 0 ) {
char * tmp = NULL ;
uint8_t * bytes = NULL ;
size_t bytes_len = 0 ;
if ( receipt_has_rng_seed | |
! 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 rng_seed_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 rng_seed_hex " ) ;
goto pel_run_cleanup ;
}
free ( tmp ) ;
free ( receipt_rng_seed ) ;
receipt_rng_seed = bytes ;
receipt_rng_seed_len = bytes_len ;
receipt_has_rng_seed = true ;
} else if ( rkey_len = = strlen ( " signature_hex " ) & &
memcmp ( rkey , " signature_hex " , rkey_len ) = = 0 ) {
char * tmp = NULL ;
uint8_t * bytes = NULL ;
size_t bytes_len = 0 ;
if ( receipt_has_signature | |
! 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 signature_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 signature_hex " ) ;
goto pel_run_cleanup ;
}
free ( tmp ) ;
free ( receipt_signature ) ;
receipt_signature = bytes ;
receipt_signature_len = bytes_len ;
receipt_has_signature = true ;
2025-12-23 09:14:58 +01:00
} 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 ;
}
2025-12-22 19:10:14 +01:00
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto pel_run_cleanup ;
}
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur > = end ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
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 json " ) ;
goto pel_run_cleanup ;
}
p = amduatd_json_skip_ws ( p , end ) ;
if ( p ! = end ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto pel_run_cleanup ;
}
free ( body ) ;
body = NULL ;
if ( ! have_program_ref ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing program_ref " ) ;
goto pel_run_cleanup ;
}
if ( ! have_input_refs ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing input_refs " ) ;
goto pel_run_cleanup ;
}
if ( ! have_scheme_ref ) {
scheme_ref = amduat_pel_program_dag_scheme_ref ( ) ;
scheme_is_dag = true ;
}
2025-12-23 09:14:58 +01:00
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 ;
}
2025-12-22 19:10:14 +01:00
if ( ! amduat_pel_surf_run_with_result ( store ,
scheme_ref ,
program_ref ,
input_refs ,
input_refs_len ,
has_params_ref ,
params_ref ,
& run_result ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" pel run failed " ) ;
goto pel_run_cleanup ;
}
if ( run_result . has_result_value & & run_result . result_value . has_store_failure ) {
if ( run_result . result_value . store_failure . error_code = =
AMDUAT_PEL_STORE_ERROR_NOT_FOUND ) {
status_code = 404 ;
status_reason = " Not Found " ;
} else {
status_code = 500 ;
status_reason = " Internal Server Error " ;
}
}
2025-12-23 09:14:58 +01:00
if ( want_receipt ) {
amduat_octets_t evaluator_id ;
amduat_octets_t parity_digest ;
2026-01-21 19:51:26 +01:00
bool use_receipt_v1_1 = receipt_has_executor_fingerprint | |
receipt_has_run_id | |
receipt_has_limits | |
receipt_has_logs | |
receipt_has_determinism | |
receipt_has_rng_seed | |
receipt_has_signature ;
2025-12-23 09:14:58 +01:00
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 ) ;
2026-01-21 19:51:26 +01:00
if ( use_receipt_v1_1 ) {
amduat_octets_t run_id = amduat_octets ( receipt_run_id ,
receipt_run_id_len ) ;
amduat_octets_t rng_seed = amduat_octets ( receipt_rng_seed ,
receipt_rng_seed_len ) ;
amduat_octets_t signature = amduat_octets ( receipt_signature ,
receipt_signature_len ) ;
if ( ! amduat_fer1_receipt_from_pel_run_v1_1 (
& run_result ,
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_has_executor_fingerprint ,
receipt_executor_fingerprint_ref ,
receipt_has_run_id ,
run_id ,
receipt_has_limits ,
receipt_limits ,
receipt_logs ,
receipt_logs_len ,
receipt_has_determinism ,
receipt_determinism_level ,
receipt_has_rng_seed ,
rng_seed ,
receipt_has_signature ,
signature ,
& receipt_artifact ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" receipt build failed " ) ;
goto pel_run_cleanup ;
}
} else {
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 ;
}
2025-12-23 09:14:58 +01:00
}
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 ;
}
2026-01-24 09:42:02 +01:00
if ( dcfg ! = NULL & & dcfg - > derivation_index_enabled ) {
amduat_asl_store_error_t idx_err = AMDUAT_ASL_STORE_OK ;
if ( ! amduatd_derivation_index_pel_run ( root_path ,
true ,
program_ref ,
input_refs ,
input_refs_len ,
has_params_ref ,
params_ref ,
& run_result ,
receipt_emitted ,
receipt_ref ,
& idx_err ) ) {
if ( dcfg - > derivation_index_strict ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" derivation index failed " ) ;
goto pel_run_cleanup ;
}
amduat_log ( AMDUAT_LOG_WARN ,
" pel run derivation index failed: %d " ,
( int ) idx_err ) ;
}
}
2025-12-22 19:10:14 +01:00
{
amduatd_strbuf_t resp ;
char * result_hex = NULL ;
char * trace_hex = NULL ;
2025-12-23 09:14:58 +01:00
char * receipt_hex = NULL ;
2025-12-22 19:10:14 +01:00
const char * status = " UNKNOWN " ;
size_t i ;
memset ( & resp , 0 , sizeof ( resp ) ) ;
if ( ! amduat_asl_ref_encode_hex ( run_result . result_ref , & result_hex ) ) {
amduatd_strbuf_free ( & resp ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
goto pel_run_cleanup ;
}
if ( run_result . has_result_value ) {
status = amduat_format_pel_status_name (
run_result . result_value . core_result . status ) ;
if ( run_result . result_value . has_trace_ref ) {
if ( ! amduat_asl_ref_encode_hex ( run_result . result_value . trace_ref ,
& trace_hex ) ) {
free ( result_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 , " { \" result_ref \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & resp , result_hex ) | |
! amduatd_strbuf_append_cstr ( & resp , " \" " ) ) {
free ( result_hex ) ;
free ( trace_hex ) ;
amduatd_strbuf_free ( & resp ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto pel_run_cleanup ;
}
free ( result_hex ) ;
if ( trace_hex ! = NULL ) {
if ( ! amduatd_strbuf_append_cstr ( & resp , " , \" trace_ref \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & resp , trace_hex ) | |
! amduatd_strbuf_append_cstr ( & resp , " \" " ) ) {
free ( trace_hex ) ;
amduatd_strbuf_free ( & resp ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto pel_run_cleanup ;
}
free ( trace_hex ) ;
}
2025-12-23 09:14:58 +01:00
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 ) ;
}
2025-12-22 19:10:14 +01:00
if ( ! amduatd_strbuf_append_cstr ( & resp , " , \" output_refs \" :[ " ) ) {
amduatd_strbuf_free ( & resp ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto pel_run_cleanup ;
}
for ( i = 0 ; i < run_result . output_refs_len ; + + i ) {
char * out_hex = NULL ;
if ( ! amduat_asl_ref_encode_hex ( run_result . output_refs [ i ] , & out_hex ) ) {
amduatd_strbuf_free ( & resp ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
goto pel_run_cleanup ;
}
if ( i ! = 0 ) {
if ( ! amduatd_strbuf_append_char ( & resp , ' , ' ) ) {
free ( out_hex ) ;
amduatd_strbuf_free ( & resp ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto pel_run_cleanup ;
}
}
if ( ! amduatd_strbuf_append_cstr ( & resp , " \" " ) | |
! amduatd_strbuf_append_cstr ( & resp , out_hex ) | |
! amduatd_strbuf_append_cstr ( & resp , " \" " ) ) {
free ( out_hex ) ;
amduatd_strbuf_free ( & resp ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto pel_run_cleanup ;
}
free ( out_hex ) ;
}
if ( ! amduatd_strbuf_append_cstr ( & resp , " ], \" status \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & resp , status ) | |
! amduatd_strbuf_append_cstr ( & resp , " \" } \n " ) ) {
amduatd_strbuf_free ( & resp ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto pel_run_cleanup ;
}
ok = amduatd_http_send_json ( fd , status_code , status_reason , resp . data , false ) ;
amduatd_strbuf_free ( & resp ) ;
}
pel_run_cleanup :
if ( body ! = NULL ) {
free ( body ) ;
}
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 ) ;
if ( input_refs ! = NULL ) {
size_t i ;
for ( i = 0 ; i < input_refs_len ; + + i ) {
amduat_reference_free ( & input_refs [ i ] ) ;
}
free ( input_refs ) ;
}
if ( has_params_ref ) {
amduat_reference_free ( & params_ref ) ;
}
if ( have_program_ref ) {
amduat_reference_free ( & program_ref ) ;
}
if ( have_scheme_ref & & ! scheme_is_dag ) {
amduat_reference_free ( & scheme_ref ) ;
}
2025-12-23 09:14:58 +01:00
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 ) ;
}
2026-01-21 19:51:26 +01:00
if ( receipt_has_executor_fingerprint ) {
amduat_reference_free ( & receipt_executor_fingerprint_ref ) ;
}
if ( receipt_logs ! = NULL ) {
size_t i ;
for ( i = 0 ; i < receipt_logs_len ; + + i ) {
amduat_reference_free ( & receipt_logs [ i ] . log_ref ) ;
amduat_octets_free ( & receipt_logs [ i ] . sha256 ) ;
}
free ( receipt_logs ) ;
}
2025-12-23 09:14:58 +01:00
free ( receipt_evaluator_id ) ;
free ( receipt_parity_digest ) ;
2026-01-21 19:51:26 +01:00
free ( receipt_run_id ) ;
free ( receipt_rng_seed ) ;
free ( receipt_signature ) ;
2025-12-22 19:10:14 +01:00
return ok ;
}
2025-12-22 20:14:47 +01:00
typedef struct {
amduat_pel_node_t * nodes ;
size_t nodes_len ;
amduat_pel_root_ref_t * roots ;
size_t roots_len ;
} amduatd_pel_program_tmp_t ;
static void amduatd_pel_program_tmp_free ( amduatd_pel_program_tmp_t * tmp ) {
size_t i ;
if ( tmp = = NULL ) {
return ;
}
if ( tmp - > nodes ! = NULL ) {
for ( i = 0 ; i < tmp - > nodes_len ; + + i ) {
free ( tmp - > nodes [ i ] . inputs ) ;
tmp - > nodes [ i ] . inputs = NULL ;
tmp - > nodes [ i ] . inputs_len = 0 ;
free ( ( void * ) tmp - > nodes [ i ] . params . data ) ;
tmp - > nodes [ i ] . params . data = NULL ;
tmp - > nodes [ i ] . params . len = 0 ;
}
}
free ( tmp - > nodes ) ;
tmp - > nodes = NULL ;
tmp - > nodes_len = 0 ;
free ( tmp - > roots ) ;
tmp - > roots = NULL ;
tmp - > roots_len = 0 ;
}
static bool amduatd_parse_params_hex ( const char * s ,
size_t len ,
amduat_octets_t * out ) {
char * tmp = NULL ;
uint8_t * bytes = NULL ;
size_t out_len = 0 ;
if ( out ! = NULL ) {
* out = amduat_octets ( NULL , 0 ) ;
}
if ( s = = NULL | | out = = NULL ) {
return false ;
}
if ( ! amduatd_copy_json_str ( s , len , & tmp ) ) {
return false ;
}
if ( tmp [ 0 ] = = ' \0 ' ) {
free ( tmp ) ;
* out = amduat_octets ( NULL , 0 ) ;
return true ;
}
if ( ! amduat_hex_decode_alloc ( tmp , & bytes , & out_len ) ) {
free ( tmp ) ;
return false ;
}
free ( tmp ) ;
* out = amduat_octets ( bytes , out_len ) ;
return true ;
}
static bool amduatd_handle_post_pel_programs ( int fd ,
amduat_asl_store_t * store ,
const amduatd_http_req_t * req ) {
uint8_t * body = NULL ;
const char * p = NULL ;
const char * end = NULL ;
amduatd_pel_program_tmp_t tmp ;
amduat_pel_program_t program ;
amduat_octets_t program_bytes ;
amduat_artifact_t artifact ;
amduat_reference_t program_ref ;
char * ref_hex = NULL ;
char json [ 2048 ] ;
bool ok = false ;
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
memset ( & program , 0 , sizeof ( program ) ) ;
program_bytes = amduat_octets ( NULL , 0 ) ;
memset ( & artifact , 0 , sizeof ( artifact ) ) ;
memset ( & program_ref , 0 , sizeof ( program_ref ) ) ;
if ( store = = 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 > ( 2u * 1024u * 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 pel_programs_cleanup ;
}
{
bool have_nodes = false ;
bool have_roots = false ;
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 pel_programs_cleanup ;
}
if ( key_len = = strlen ( " nodes " ) & & memcmp ( key , " nodes " , key_len ) = = 0 ) {
size_t cap = 0 ;
if ( have_nodes ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" duplicate nodes " ) ;
goto pel_programs_cleanup ;
}
if ( ! amduatd_json_expect ( & p , end , ' [ ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid nodes " ) ;
goto pel_programs_cleanup ;
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
have_nodes = true ;
} else {
for ( ; ; ) {
amduat_pel_node_t node ;
bool have_id = false ;
bool have_op = false ;
bool have_inputs = false ;
bool have_params_hex = false ;
amduat_octets_t op_name = amduat_octets ( NULL , 0 ) ;
uint32_t op_ver = 0 ;
memset ( & node , 0 , sizeof ( node ) ) ;
if ( ! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid node " ) ;
goto pel_programs_cleanup ;
}
for ( ; ; ) {
const char * nk = NULL ;
size_t nk_len = 0 ;
const char * sv = NULL ;
size_t sv_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 , & nk , & nk_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid node " ) ;
goto pel_programs_cleanup ;
}
if ( nk_len = = strlen ( " id " ) & & memcmp ( nk , " id " , nk_len ) = = 0 ) {
uint32_t id = 0 ;
2025-12-23 09:14:58 +01:00
if ( have_id ) {
2025-12-22 20:14:47 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
2025-12-23 09:14:58 +01:00
" 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 " ) ;
}
2025-12-22 20:14:47 +01:00
goto pel_programs_cleanup ;
}
node . id = ( amduat_pel_node_id_t ) id ;
have_id = true ;
} else if ( nk_len = = strlen ( " op " ) & &
memcmp ( nk , " op " , nk_len ) = = 0 ) {
bool have_name = false ;
bool have_version = false ;
if ( have_op | | ! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid op " ) ;
goto pel_programs_cleanup ;
}
for ( ; ; ) {
const char * okey = NULL ;
size_t okey_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 , & okey ,
& okey_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid op " ) ;
goto pel_programs_cleanup ;
}
if ( okey_len = = strlen ( " name " ) & &
memcmp ( okey , " name " , okey_len ) = = 0 ) {
if ( have_name | |
! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid op name " ) ;
goto pel_programs_cleanup ;
}
op_name = amduat_octets ( sv , sv_len ) ;
have_name = true ;
} else if ( okey_len = = strlen ( " version " ) & &
memcmp ( okey , " version " , okey_len ) = = 0 ) {
if ( have_version | |
2025-12-23 09:14:58 +01:00
! amduatd_json_parse_u32_loose ( & p , end , & op_ver ) ) {
2025-12-22 20:14:47 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid op version " ) ;
goto pel_programs_cleanup ;
}
have_version = true ;
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid op " ) ;
goto pel_programs_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 op " ) ;
goto pel_programs_cleanup ;
}
if ( ! have_name | | ! have_version ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing op fields " ) ;
goto pel_programs_cleanup ;
}
node . op . name = op_name ;
node . op . version = op_ver ;
have_op = true ;
} else if ( nk_len = = strlen ( " inputs " ) & &
memcmp ( nk , " inputs " , nk_len ) = = 0 ) {
size_t icap = 0 ;
if ( have_inputs | | ! amduatd_json_expect ( & p , end , ' [ ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid inputs " ) ;
goto pel_programs_cleanup ;
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
have_inputs = true ;
} else {
for ( ; ; ) {
amduat_pel_dag_input_t in ;
memset ( & in , 0 , sizeof ( in ) ) ;
if ( ! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input " ) ;
goto pel_programs_cleanup ;
}
{
const char * ik = NULL ;
size_t ik_len = 0 ;
if ( ! amduatd_json_parse_string_noesc ( & p , end , & ik ,
& ik_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) | |
! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input " ) ;
goto pel_programs_cleanup ;
}
if ( ik_len = = strlen ( " external " ) & &
memcmp ( ik , " external " , ik_len ) = = 0 ) {
uint32_t idx = 0 ;
const char * k2 = NULL ;
size_t k2_len = 0 ;
if ( ! amduatd_json_parse_string_noesc ( & p , end , & k2 ,
& k2_len ) | |
k2_len ! = strlen ( " input_index " ) | |
memcmp ( k2 , " input_index " , k2_len ) ! = 0 | |
! amduatd_json_expect ( & p , end , ' : ' ) | |
2025-12-23 09:14:58 +01:00
! amduatd_json_parse_u32_loose ( & p , end , & idx ) | |
2025-12-22 20:14:47 +01:00
! amduatd_json_expect ( & p , end , ' } ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid external input " ) ;
goto pel_programs_cleanup ;
}
in . kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL ;
in . value . external . input_index = idx ;
} else if ( ik_len = = strlen ( " node " ) & &
memcmp ( ik , " node " , ik_len ) = = 0 ) {
bool have_node_id = false ;
bool have_output_index = false ;
uint32_t nid = 0 ;
uint32_t oidx = 0 ;
for ( ; ; ) {
const char * k2 = NULL ;
size_t k2_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 , & k2 ,
& k2_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid node input " ) ;
goto pel_programs_cleanup ;
}
if ( k2_len = = strlen ( " node_id " ) & &
memcmp ( k2 , " node_id " , k2_len ) = = 0 ) {
if ( have_node_id | |
2025-12-23 09:14:58 +01:00
! amduatd_json_parse_u32_loose ( & p , end , & nid ) ) {
2025-12-22 20:14:47 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid node_id " ) ;
goto pel_programs_cleanup ;
}
have_node_id = true ;
} else if ( k2_len = = strlen ( " output_index " ) & &
memcmp ( k2 , " output_index " , k2_len ) = = 0 ) {
if ( have_output_index | |
2025-12-23 09:14:58 +01:00
! amduatd_json_parse_u32_loose ( & p , end , & oidx ) ) {
2025-12-22 20:14:47 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid output_index " ) ;
goto pel_programs_cleanup ;
}
have_output_index = true ;
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid node input " ) ;
goto pel_programs_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 node input " ) ;
goto pel_programs_cleanup ;
}
if ( ! have_node_id | | ! have_output_index ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing node input fields " ) ;
goto pel_programs_cleanup ;
}
in . kind = AMDUAT_PEL_DAG_INPUT_NODE ;
in . value . node . node_id = ( amduat_pel_node_id_t ) nid ;
in . value . node . output_index = oidx ;
} else {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input kind " ) ;
goto pel_programs_cleanup ;
}
if ( ! amduatd_json_expect ( & p , end , ' } ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input " ) ;
goto pel_programs_cleanup ;
}
}
if ( node . inputs_len = = icap ) {
size_t next_cap = icap ! = 0 ? icap * 2u : 4u ;
amduat_pel_dag_input_t * next =
( amduat_pel_dag_input_t * ) realloc (
node . inputs , next_cap * sizeof ( * node . inputs ) ) ;
if ( next = = NULL ) {
ok = amduatd_send_json_error ( fd , 500 ,
" Internal Server Error " ,
" oom " ) ;
goto pel_programs_cleanup ;
}
node . inputs = next ;
icap = next_cap ;
}
node . inputs [ node . inputs_len + + ] = in ;
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' , ' ) {
p = cur + 1 ;
continue ;
}
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
have_inputs = true ;
break ;
}
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid inputs " ) ;
goto pel_programs_cleanup ;
}
}
} else if ( nk_len = = strlen ( " params_hex " ) & &
memcmp ( nk , " params_hex " , nk_len ) = = 0 ) {
if ( have_params_hex | |
! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) | |
! amduatd_parse_params_hex ( sv , sv_len , & node . params ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid params_hex " ) ;
goto pel_programs_cleanup ;
}
have_params_hex = true ;
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid node " ) ;
goto pel_programs_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_id | | ! have_op | | ! have_inputs | | ! have_params_hex ) {
free ( node . inputs ) ;
free ( ( void * ) node . params . data ) ;
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing node fields " ) ;
goto pel_programs_cleanup ;
}
if ( amduat_pel_kernel_op_lookup ( node . op . name , node . op . version ) = =
NULL ) {
free ( node . inputs ) ;
free ( ( void * ) node . params . data ) ;
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" unknown op " ) ;
goto pel_programs_cleanup ;
}
if ( tmp . nodes_len = = cap ) {
size_t next_cap = cap ! = 0 ? cap * 2u : 4u ;
amduat_pel_node_t * next =
( amduat_pel_node_t * ) realloc ( tmp . nodes ,
next_cap * sizeof ( * tmp . nodes ) ) ;
if ( next = = NULL ) {
free ( node . inputs ) ;
free ( ( void * ) node . params . data ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" oom " ) ;
goto pel_programs_cleanup ;
}
tmp . nodes = next ;
cap = next_cap ;
}
tmp . nodes [ tmp . nodes_len + + ] = node ;
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' , ' ) {
p = cur + 1 ;
continue ;
}
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
have_nodes = true ;
break ;
}
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid nodes " ) ;
goto pel_programs_cleanup ;
}
}
} else if ( key_len = = strlen ( " roots " ) & &
memcmp ( key , " roots " , key_len ) = = 0 ) {
size_t cap = 0 ;
if ( have_roots ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" duplicate roots " ) ;
goto pel_programs_cleanup ;
}
if ( ! amduatd_json_expect ( & p , end , ' [ ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid roots " ) ;
goto pel_programs_cleanup ;
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
have_roots = true ;
} else {
for ( ; ; ) {
amduat_pel_root_ref_t root ;
bool have_node_id = false ;
bool have_output_index = false ;
memset ( & root , 0 , sizeof ( root ) ) ;
if ( ! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid root " ) ;
goto pel_programs_cleanup ;
}
for ( ; ; ) {
const char * rk = NULL ;
size_t rk_len = 0 ;
uint32_t v = 0 ;
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' } ' ) {
p = cur + 1 ;
break ;
}
if ( ! amduatd_json_parse_string_noesc ( & p , end , & rk , & rk_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid root " ) ;
goto pel_programs_cleanup ;
}
if ( rk_len = = strlen ( " node_id " ) & &
memcmp ( rk , " node_id " , rk_len ) = = 0 ) {
2025-12-23 09:14:58 +01:00
if ( have_node_id | |
! amduatd_json_parse_u32_loose ( & p , end , & v ) ) {
2025-12-22 20:14:47 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid root node_id " ) ;
goto pel_programs_cleanup ;
}
root . node_id = ( amduat_pel_node_id_t ) v ;
have_node_id = true ;
} else if ( rk_len = = strlen ( " output_index " ) & &
memcmp ( rk , " output_index " , rk_len ) = = 0 ) {
2025-12-23 09:14:58 +01:00
if ( have_output_index | |
! amduatd_json_parse_u32_loose ( & p , end , & v ) ) {
2025-12-22 20:14:47 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid root output_index " ) ;
goto pel_programs_cleanup ;
}
root . output_index = v ;
have_output_index = true ;
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid root " ) ;
goto pel_programs_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 root " ) ;
goto pel_programs_cleanup ;
}
if ( ! have_node_id | | ! have_output_index ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing root fields " ) ;
goto pel_programs_cleanup ;
}
if ( tmp . roots_len = = cap ) {
size_t next_cap = cap ! = 0 ? cap * 2u : 2u ;
amduat_pel_root_ref_t * next =
( amduat_pel_root_ref_t * ) realloc (
tmp . roots , next_cap * sizeof ( * tmp . roots ) ) ;
if ( next = = NULL ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" oom " ) ;
goto pel_programs_cleanup ;
}
tmp . roots = next ;
cap = next_cap ;
}
tmp . roots [ tmp . roots_len + + ] = root ;
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' , ' ) {
p = cur + 1 ;
continue ;
}
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
have_roots = true ;
break ;
}
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid roots " ) ;
goto pel_programs_cleanup ;
}
}
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto pel_programs_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 pel_programs_cleanup ;
}
p = amduatd_json_skip_ws ( p , end ) ;
if ( p ! = end ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto pel_programs_cleanup ;
}
if ( ! have_nodes | | ! have_roots ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing nodes/roots " ) ;
goto pel_programs_cleanup ;
}
}
program . nodes = tmp . nodes ;
program . nodes_len = tmp . nodes_len ;
program . roots = tmp . roots ;
program . roots_len = tmp . roots_len ;
if ( ! amduat_pel_program_dag_validate ( & program ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid program " ) ;
goto pel_programs_cleanup ;
}
if ( ! amduat_enc_pel_program_dag_encode_v1 ( & program , & program_bytes ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
goto pel_programs_cleanup ;
}
artifact = amduat_artifact_with_type ( program_bytes ,
amduat_type_tag ( AMDUAT_PEL_TYPE_TAG_PROGRAM_DAG_1 ) ) ;
if ( amduat_asl_store_put ( store , artifact , & program_ref ) ! = AMDUAT_ASL_STORE_OK ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" store error " ) ;
goto pel_programs_cleanup ;
}
if ( ! amduat_asl_ref_encode_hex ( program_ref , & ref_hex ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode ref error " ) ;
goto pel_programs_cleanup ;
}
{
int n = snprintf ( json , sizeof ( json ) , " { \" program_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 pel_programs_cleanup ;
}
ok = amduatd_http_send_json ( fd , 200 , " OK " , json , false ) ;
goto pel_programs_cleanup ;
}
pel_programs_cleanup :
free ( body ) ;
free ( ( void * ) program_bytes . data ) ;
amduat_reference_free ( & program_ref ) ;
amduatd_pel_program_tmp_free ( & tmp ) ;
return ok ;
}
2025-12-23 09:14:58 +01:00
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 ,
2026-01-23 22:28:56 +01:00
const amduatd_cfg_t * dcfg ,
2025-12-23 09:14:58 +01:00
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 ,
2026-01-23 22:28:56 +01:00
dcfg ,
2025-12-23 09:14:58 +01:00
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 ,
2026-01-23 22:28:56 +01:00
dcfg ,
2025-12-23 09:14:58 +01:00
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 ,
2026-01-23 22:28:56 +01:00
dcfg ,
2025-12-23 09:14:58 +01:00
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 ;
}
2025-12-22 15:23:54 +01:00
static bool amduatd_handle_conn ( int fd ,
amduat_asl_store_t * store ,
2025-12-22 19:37:41 +01:00
const amduat_asl_store_fs_config_t * cfg ,
2025-12-22 21:03:00 +01:00
amduat_reference_t api_contract_ref ,
2025-12-23 09:14:58 +01:00
amduat_reference_t ui_ref ,
2026-01-21 19:51:26 +01:00
amduatd_concepts_t * concepts ,
2026-01-23 22:28:56 +01:00
const amduatd_cfg_t * dcfg ,
2026-01-23 20:55:41 +01:00
const amduat_fed_coord_t * coord ,
2026-01-23 23:08:41 +01:00
const amduatd_allowlist_t * allowlist ,
2026-01-24 03:03:54 +01:00
amduatd_caps_t * caps ,
2026-01-23 23:08:41 +01:00
const char * root_path ) {
2025-12-22 15:23:54 +01:00
amduatd_http_req_t req ;
char no_query [ 1024 ] ;
2026-01-23 20:55:41 +01:00
bool ok = false ;
amduat_octets_t actor = amduat_octets ( NULL , 0u ) ;
bool has_actor = false ;
bool has_uid = false ;
uid_t uid = 0 ;
2026-01-24 10:16:22 +01:00
amduatd_space_t req_space ;
const amduatd_space_t * effective_space = NULL ;
amduatd_cfg_t req_cfg ;
const amduatd_cfg_t * effective_cfg = dcfg ;
2025-12-22 15:23:54 +01:00
if ( ! amduatd_http_parse_request ( fd , & req ) ) {
return false ;
}
2026-01-23 20:55:41 +01:00
if ( ! amduatd_get_peer_actor ( fd , & actor , & has_actor , & has_uid , & uid ) ) {
actor = amduat_octets ( NULL , 0u ) ;
has_actor = false ;
has_uid = false ;
}
req . has_actor = has_actor ;
req . actor = actor ;
req . has_uid = has_uid ;
req . uid = uid ;
2026-01-23 23:08:41 +01:00
amduatd_path_without_query ( req . path , no_query , sizeof ( no_query ) ) ;
2026-01-24 10:16:22 +01:00
{
const char * space_header = NULL ;
amduatd_space_resolve_status_t space_st ;
if ( req . x_space [ 0 ] ! = ' \0 ' ) {
space_header = req . x_space ;
}
space_st = amduatd_space_resolve_effective ( & dcfg - > space ,
space_header ,
& req_space ,
& effective_space ) ;
if ( space_st ! = AMDUATD_SPACE_RESOLVE_OK | | effective_space = = NULL ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid X-Amduat-Space " ) ;
goto conn_cleanup ;
}
if ( effective_space ! = & dcfg - > space ) {
req_cfg = * dcfg ;
req_cfg . space = req_space ;
effective_cfg = & req_cfg ;
}
req . effective_space = effective_space ;
}
2026-01-23 23:08:41 +01:00
if ( ! ( strcmp ( req . method , " GET " ) = = 0 & &
strcmp ( no_query , " /v1/cap/resolve " ) = = 0 ) & &
! amduatd_actor_allowed ( allowlist , has_uid , uid ) ) {
2026-01-23 20:55:41 +01:00
amduat_log ( AMDUAT_LOG_DEBUG ,
" reject uid=%u (not in allowlist) " ,
( unsigned int ) uid ) ;
ok = amduatd_http_send_text ( fd , 403 , " Forbidden " , " forbidden \n " , false ) ;
goto conn_cleanup ;
}
2026-01-23 23:30:29 +01:00
{
amduatd_ctx_t ui_ctx ;
amduatd_http_resp_t ui_resp ;
ui_ctx . store = store ;
ui_ctx . ui_ref = ui_ref ;
2026-01-24 03:03:54 +01:00
ui_ctx . store_cfg = cfg ;
ui_ctx . concepts = concepts ;
2026-01-24 10:16:22 +01:00
ui_ctx . daemon_cfg = effective_cfg ;
2026-01-24 03:03:54 +01:00
ui_ctx . root_path = root_path ;
ui_ctx . caps = caps ;
2026-01-23 23:30:29 +01:00
ui_resp . fd = fd ;
ui_resp . ok = false ;
if ( amduatd_ui_can_handle ( & req ) ) {
if ( amduatd_ui_handle ( & ui_ctx , & req , & ui_resp ) ) {
ok = ui_resp . ok ;
goto conn_cleanup ;
}
}
2025-12-22 20:14:47 +01:00
}
2025-12-22 15:23:54 +01:00
if ( strcmp ( req . method , " GET " ) = = 0 & & strcmp ( no_query , " /v1/meta " ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_meta ( fd , cfg , api_contract_ref , false ) ;
goto conn_cleanup ;
2025-12-22 15:23:54 +01:00
}
if ( strcmp ( req . method , " HEAD " ) = = 0 & & strcmp ( no_query , " /v1/meta " ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_meta ( fd , cfg , api_contract_ref , true ) ;
goto conn_cleanup ;
2025-12-22 19:37:41 +01:00
}
if ( strcmp ( req . method , " GET " ) = = 0 & & strcmp ( no_query , " /v1/contract " ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_get_contract ( fd , store , & req , api_contract_ref ) ;
goto conn_cleanup ;
2025-12-22 15:23:54 +01:00
}
2026-01-21 19:51:26 +01:00
if ( strcmp ( req . method , " GET " ) = = 0 & &
strcmp ( no_query , " /v1/fed/records " ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_get_fed_records ( fd , store , & req ) ;
goto conn_cleanup ;
2026-01-21 19:51:26 +01:00
}
if ( strcmp ( req . method , " GET " ) = = 0 & &
strcmp ( no_query , " /v1/fed/status " ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_get_fed_status ( fd , coord , & req ) ;
goto conn_cleanup ;
2026-01-21 19:51:26 +01:00
}
2025-12-22 15:23:54 +01:00
if ( strcmp ( req . method , " POST " ) = = 0 & & strcmp ( no_query , " /v1/artifacts " ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_post_artifacts ( fd , store , & req ) ;
goto conn_cleanup ;
2025-12-22 15:23:54 +01:00
}
2026-01-23 23:08:41 +01:00
if ( strcmp ( req . method , " POST " ) = = 0 & &
strcmp ( no_query , " /v1/capabilities " ) = = 0 ) {
2026-01-24 03:03:54 +01:00
amduatd_ctx_t caps_ctx ;
amduatd_http_resp_t caps_resp ;
caps_ctx . store = store ;
caps_ctx . ui_ref = ui_ref ;
caps_ctx . store_cfg = cfg ;
caps_ctx . concepts = concepts ;
2026-01-24 10:16:22 +01:00
caps_ctx . daemon_cfg = effective_cfg ;
2026-01-24 03:03:54 +01:00
caps_ctx . root_path = root_path ;
caps_ctx . caps = caps ;
caps_resp . fd = fd ;
caps_resp . ok = false ;
if ( amduatd_caps_handle ( & caps_ctx , & req , & caps_resp ) ) {
ok = caps_resp . ok ;
}
2026-01-23 23:08:41 +01:00
goto conn_cleanup ;
}
2025-12-22 19:10:14 +01:00
if ( strcmp ( req . method , " POST " ) = = 0 & & strcmp ( no_query , " /v1/pel/run " ) = = 0 ) {
2026-01-24 10:16:22 +01:00
ok = amduatd_handle_post_pel_run ( fd , store , cfg , concepts , effective_cfg ,
2026-01-24 09:42:02 +01:00
root_path , & req ) ;
2026-01-23 20:55:41 +01:00
goto conn_cleanup ;
2025-12-22 19:10:14 +01:00
}
2025-12-22 20:14:47 +01:00
if ( strcmp ( req . method , " POST " ) = = 0 & &
strcmp ( no_query , " /v1/pel/programs " ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_post_pel_programs ( fd , store , & req ) ;
goto conn_cleanup ;
2025-12-22 20:14:47 +01:00
}
2025-12-23 09:14:58 +01:00
if ( strcmp ( req . method , " POST " ) = = 0 & &
strcmp ( no_query , " /v1/context_frames " ) = = 0 ) {
2026-01-24 10:16:22 +01:00
ok = amduatd_handle_post_context_frames ( fd , store , cfg , concepts ,
effective_cfg ,
2026-01-23 22:28:56 +01:00
& req ) ;
2026-01-23 20:55:41 +01:00
goto conn_cleanup ;
2025-12-23 09:14:58 +01:00
}
2026-01-24 06:29:51 +01:00
{
amduatd_ctx_t concepts_ctx ;
amduatd_http_resp_t concepts_resp ;
concepts_ctx . store = store ;
concepts_ctx . ui_ref = ui_ref ;
concepts_ctx . store_cfg = cfg ;
concepts_ctx . concepts = concepts ;
2026-01-24 10:16:22 +01:00
concepts_ctx . daemon_cfg = effective_cfg ;
2026-01-24 06:29:51 +01:00
concepts_ctx . root_path = root_path ;
concepts_ctx . caps = caps ;
concepts_resp . fd = fd ;
concepts_resp . ok = false ;
if ( amduatd_concepts_can_handle ( & req ) ) {
if ( amduatd_concepts_handle ( & concepts_ctx , & req , & concepts_resp ) ) {
ok = concepts_resp . ok ;
goto conn_cleanup ;
}
}
2025-12-23 09:14:58 +01:00
}
2026-01-21 19:51:26 +01:00
if ( strcmp ( req . method , " GET " ) = = 0 & &
strncmp ( no_query , " /v1/fed/artifacts/ " , 18 ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_get_fed_artifact ( fd , store , & req , req . path ) ;
goto conn_cleanup ;
2026-01-21 19:51:26 +01:00
}
2026-01-23 23:08:41 +01:00
if ( strcmp ( req . method , " GET " ) = = 0 & &
strcmp ( no_query , " /v1/cap/resolve " ) = = 0 ) {
2026-01-24 03:03:54 +01:00
amduatd_ctx_t caps_ctx ;
amduatd_http_resp_t caps_resp ;
caps_ctx . store = store ;
caps_ctx . ui_ref = ui_ref ;
caps_ctx . store_cfg = cfg ;
caps_ctx . concepts = concepts ;
2026-01-24 10:16:22 +01:00
caps_ctx . daemon_cfg = effective_cfg ;
2026-01-24 03:03:54 +01:00
caps_ctx . root_path = root_path ;
caps_ctx . caps = caps ;
caps_resp . fd = fd ;
caps_resp . ok = false ;
if ( amduatd_caps_handle ( & caps_ctx , & req , & caps_resp ) ) {
ok = caps_resp . ok ;
}
2026-01-23 23:08:41 +01:00
goto conn_cleanup ;
}
2025-12-22 15:23:54 +01:00
if ( strcmp ( req . method , " GET " ) = = 0 & &
strncmp ( no_query , " /v1/artifacts/ " , 14 ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_get_artifact ( fd , store , & req , req . path , false ) ;
goto conn_cleanup ;
2025-12-22 15:23:54 +01:00
}
if ( strcmp ( req . method , " HEAD " ) = = 0 & &
strncmp ( no_query , " /v1/artifacts/ " , 14 ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_head_artifact ( fd , store , & req , req . path ) ;
goto conn_cleanup ;
2025-12-22 15:23:54 +01:00
}
2026-01-23 20:55:41 +01:00
ok = amduatd_http_send_not_found ( fd , & req ) ;
conn_cleanup :
2026-01-24 06:50:53 +01:00
amduatd_http_req_free ( & req ) ;
2026-01-23 20:55:41 +01:00
return ok ;
2025-12-22 15:23:54 +01:00
}
static void amduatd_print_usage ( FILE * stream ) {
fprintf ( stream ,
" usage: \n "
" amduatd [--root PATH] [--sock PATH] \n "
2026-01-23 22:28:56 +01:00
" [--space SPACE_ID] [--migrate-unscoped-edges] \n "
2026-01-24 07:22:51 +01:00
" [--edges-refresh-ms MS] \n "
2026-01-24 08:44:28 +01:00
" [--store-backend fs|index] \n "
2026-01-23 20:55:41 +01:00
" [--allow-uid UID] [--allow-user NAME] \n "
2026-01-23 23:08:41 +01:00
" [--enable-cap-reads] \n "
2026-01-24 09:42:02 +01:00
" [--enable-derivation-index] \n "
" [--derivation-index-strict] \n "
2025-12-22 15:23:54 +01:00
" \n "
" defaults: \n "
" --root %s \n "
" --sock %s \n " ,
AMDUATD_DEFAULT_ROOT ,
AMDUATD_DEFAULT_SOCK ) ;
}
int main ( int argc , char * * argv ) {
const char * root = AMDUATD_DEFAULT_ROOT ;
const char * sock_path = AMDUATD_DEFAULT_SOCK ;
2026-01-23 22:28:56 +01:00
const char * space_id = NULL ;
bool migrate_unscoped_edges = false ;
2026-01-23 23:08:41 +01:00
bool enable_cap_reads = false ;
2026-01-24 08:44:28 +01:00
amduatd_store_backend_t store_backend = AMDUATD_STORE_BACKEND_FS ;
2026-01-23 22:28:56 +01:00
amduatd_cfg_t dcfg ;
2026-01-24 03:03:54 +01:00
amduatd_caps_t caps ;
2025-12-22 15:23:54 +01:00
amduat_asl_store_fs_config_t cfg ;
2026-01-24 08:44:28 +01:00
amduatd_store_ctx_t store_ctx ;
2025-12-22 15:23:54 +01:00
amduat_asl_store_t store ;
2025-12-22 19:37:41 +01:00
amduat_reference_t api_contract_ref ;
2025-12-23 09:14:58 +01:00
amduat_reference_t ui_ref ;
2025-12-22 21:03:00 +01:00
amduatd_concepts_t concepts ;
2026-01-21 19:51:26 +01:00
amduat_fed_transport_stub_t fed_stub ;
amduat_fed_coord_cfg_t fed_cfg ;
amduat_fed_coord_t * fed_coord = NULL ;
2026-01-23 20:55:41 +01:00
amduatd_allowlist_t allowlist ;
2025-12-22 15:23:54 +01:00
int i ;
int sfd = - 1 ;
2026-01-21 19:51:26 +01:00
uint64_t last_tick_ms = 0 ;
2026-01-24 07:22:51 +01:00
uint64_t last_edges_refresh_ms = 0 ;
2025-12-22 15:23:54 +01:00
2025-12-22 19:37:41 +01:00
memset ( & api_contract_ref , 0 , sizeof ( api_contract_ref ) ) ;
2025-12-23 09:14:58 +01:00
memset ( & ui_ref , 0 , sizeof ( ui_ref ) ) ;
2025-12-22 21:03:00 +01:00
memset ( & concepts , 0 , sizeof ( concepts ) ) ;
2026-01-23 20:55:41 +01:00
memset ( & allowlist , 0 , sizeof ( allowlist ) ) ;
2026-01-23 22:28:56 +01:00
memset ( & dcfg , 0 , sizeof ( dcfg ) ) ;
2026-01-24 03:03:54 +01:00
memset ( & caps , 0 , sizeof ( caps ) ) ;
2025-12-22 19:37:41 +01:00
2025-12-22 15:23:54 +01:00
for ( i = 1 ; i < argc ; + + i ) {
if ( strcmp ( argv [ i ] , " --root " ) = = 0 ) {
if ( i + 1 > = argc ) {
fprintf ( stderr , " error: --root requires a path \n " ) ;
return 2 ;
}
root = argv [ + + i ] ;
} else if ( strcmp ( argv [ i ] , " --sock " ) = = 0 ) {
if ( i + 1 > = argc ) {
fprintf ( stderr , " error: --sock requires a path \n " ) ;
return 2 ;
}
sock_path = argv [ + + i ] ;
2026-01-23 22:28:56 +01:00
} else if ( strcmp ( argv [ i ] , " --space " ) = = 0 ) {
if ( i + 1 > = argc ) {
fprintf ( stderr , " error: --space requires a value \n " ) ;
return 2 ;
}
space_id = argv [ + + i ] ;
2026-01-24 03:21:14 +01:00
if ( ! amduatd_space_space_id_is_valid ( space_id ) ) {
2026-01-23 22:28:56 +01:00
fprintf ( stderr , " error: invalid --space \n " ) ;
return 2 ;
}
} else if ( strcmp ( argv [ i ] , " --migrate-unscoped-edges " ) = = 0 ) {
migrate_unscoped_edges = true ;
2026-01-24 07:22:51 +01:00
} else if ( strcmp ( argv [ i ] , " --edges-refresh-ms " ) = = 0 ) {
char * endp = NULL ;
unsigned long long refresh_val ;
if ( i + 1 > = argc ) {
fprintf ( stderr , " error: --edges-refresh-ms requires a value \n " ) ;
return 2 ;
}
refresh_val = strtoull ( argv [ + + i ] , & endp , 10 ) ;
if ( endp = = argv [ i ] | | * endp ! = ' \0 ' ) {
fprintf ( stderr , " error: invalid --edges-refresh-ms \n " ) ;
return 2 ;
}
dcfg . edges_refresh_ms = ( uint64_t ) refresh_val ;
2026-01-24 08:44:28 +01:00
} else if ( strcmp ( argv [ i ] , " --store-backend " ) = = 0 ) {
if ( i + 1 > = argc ) {
fprintf ( stderr , " error: --store-backend requires a value \n " ) ;
return 2 ;
}
if ( ! amduatd_store_backend_parse ( argv [ + + i ] , & store_backend ) ) {
fprintf ( stderr , " error: invalid --store-backend \n " ) ;
return 2 ;
}
2026-01-23 20:55:41 +01:00
} else if ( strcmp ( argv [ i ] , " --allow-uid " ) = = 0 ) {
char * endp = NULL ;
unsigned long uid_val ;
if ( i + 1 > = argc ) {
fprintf ( stderr , " error: --allow-uid requires a value \n " ) ;
return 2 ;
}
uid_val = strtoul ( argv [ + + i ] , & endp , 10 ) ;
if ( endp = = argv [ i ] | | * endp ! = ' \0 ' | | uid_val > UINT32_MAX ) {
fprintf ( stderr , " error: invalid --allow-uid \n " ) ;
return 2 ;
}
if ( ! amduatd_allowlist_add ( & allowlist , ( uid_t ) uid_val ) ) {
fprintf ( stderr , " error: failed to add allow-uid \n " ) ;
return 2 ;
}
} else if ( strcmp ( argv [ i ] , " --allow-user " ) = = 0 ) {
struct passwd * pwd ;
if ( i + 1 > = argc ) {
fprintf ( stderr , " error: --allow-user requires a value \n " ) ;
return 2 ;
}
pwd = getpwnam ( argv [ + + i ] ) ;
if ( pwd = = NULL ) {
fprintf ( stderr , " error: unknown user for --allow-user \n " ) ;
return 2 ;
}
if ( ! amduatd_allowlist_add ( & allowlist , pwd - > pw_uid ) ) {
fprintf ( stderr , " error: failed to add allow-user \n " ) ;
return 2 ;
}
2026-01-23 23:08:41 +01:00
} else if ( strcmp ( argv [ i ] , " --enable-cap-reads " ) = = 0 ) {
enable_cap_reads = true ;
2026-01-24 09:42:02 +01:00
} else if ( strcmp ( argv [ i ] , " --enable-derivation-index " ) = = 0 ) {
dcfg . derivation_index_enabled = true ;
} else if ( strcmp ( argv [ i ] , " --derivation-index-strict " ) = = 0 ) {
dcfg . derivation_index_enabled = true ;
dcfg . derivation_index_strict = true ;
2025-12-22 15:23:54 +01:00
} else if ( strcmp ( argv [ i ] , " --help " ) = = 0 | | strcmp ( argv [ i ] , " -h " ) = = 0 ) {
amduatd_print_usage ( stdout ) ;
return 0 ;
} else {
fprintf ( stderr , " error: unknown option: %s \n " , argv [ i ] ) ;
return 2 ;
}
}
2026-01-24 03:21:14 +01:00
if ( ! amduatd_space_init ( & dcfg . space , space_id , migrate_unscoped_edges ) ) {
fprintf ( stderr , " error: invalid --space \n " ) ;
return 2 ;
}
2026-01-23 22:28:56 +01:00
2026-01-24 08:44:28 +01:00
if ( ! amduatd_store_init ( & store , & cfg , & store_ctx , root , store_backend ) ) {
2025-12-22 15:23:54 +01:00
fprintf ( stderr , " error: failed to initialize store: %s \n " , root ) ;
return 8 ;
}
2026-01-24 08:44:28 +01:00
amduat_log ( AMDUAT_LOG_INFO ,
" store backend=%s " ,
amduatd_store_backend_name ( store_backend ) ) ;
2026-01-24 09:42:02 +01:00
amduat_log ( AMDUAT_LOG_INFO ,
" derivation index=%s strict=%s " ,
dcfg . derivation_index_enabled ? " on " : " off " ,
dcfg . derivation_index_strict ? " on " : " off " ) ;
2026-01-24 03:03:54 +01:00
if ( ! amduatd_caps_init ( & caps , & dcfg , root ) ) {
2026-01-23 23:08:41 +01:00
amduat_log ( AMDUAT_LOG_WARN , " capabilities unavailable " ) ;
}
2026-01-24 03:03:54 +01:00
caps . enable_cap_reads = enable_cap_reads ;
2025-12-22 15:23:54 +01:00
2025-12-22 19:37:41 +01:00
if ( ! amduatd_seed_api_contract ( & store , & cfg , & api_contract_ref ) ) {
fprintf ( stderr , " error: failed to seed api contract \n " ) ;
return 8 ;
}
2026-01-23 23:30:29 +01:00
# if AMDUATD_ENABLE_UI
2025-12-23 09:14:58 +01:00
if ( ! amduatd_seed_ui_html ( & store , & cfg , & ui_ref ) ) {
fprintf ( stderr , " error: failed to seed ui html \n " ) ;
return 8 ;
}
2026-01-23 23:30:29 +01:00
# endif
2026-01-24 06:29:51 +01:00
if ( ! amduatd_concepts_init ( & concepts , & store , & dcfg . space , root , true ) ) {
2025-12-22 21:03:00 +01:00
fprintf ( stderr , " error: failed to init concept edges \n " ) ;
return 8 ;
}
2026-01-23 23:30:29 +01:00
# if AMDUATD_ENABLE_UI
2026-01-24 06:29:51 +01:00
if ( ! amduatd_seed_ms_ui_state ( & store , & concepts , & dcfg ) ) {
2025-12-23 09:14:58 +01:00
fprintf ( stderr , " error: failed to seed ms ui state \n " ) ;
return 8 ;
}
2026-01-23 23:30:29 +01:00
# endif
2025-12-22 19:37:41 +01:00
2026-01-21 19:51:26 +01:00
amduat_fed_transport_stub_init ( & fed_stub ) ;
memset ( & fed_cfg , 0 , sizeof ( fed_cfg ) ) ;
fed_cfg . local_domain_id = 0 ;
fed_cfg . authoritative_store = & store ;
fed_cfg . cache_store = NULL ;
fed_cfg . transport = amduat_fed_transport_stub_ops ( & fed_stub ) ;
if ( amduat_fed_coord_open ( & fed_cfg , & fed_coord ) ! = AMDUAT_FED_COORD_OK ) {
fed_coord = NULL ;
}
2025-12-22 15:23:54 +01:00
signal ( SIGINT , amduatd_on_signal ) ;
signal ( SIGTERM , amduatd_on_signal ) ;
sfd = socket ( AF_UNIX , SOCK_STREAM , 0 ) ;
if ( sfd < 0 ) {
perror ( " socket " ) ;
return 8 ;
}
{
struct sockaddr_un addr ;
memset ( & addr , 0 , sizeof ( addr ) ) ;
addr . sun_family = AF_UNIX ;
if ( strlen ( sock_path ) > = sizeof ( addr . sun_path ) ) {
fprintf ( stderr , " error: socket path too long \n " ) ;
close ( sfd ) ;
return 2 ;
}
strncpy ( addr . sun_path , sock_path , sizeof ( addr . sun_path ) - 1 ) ;
( void ) unlink ( sock_path ) ;
if ( bind ( sfd , ( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
perror ( " bind " ) ;
close ( sfd ) ;
return 8 ;
}
( void ) chmod ( sock_path , 0660 ) ;
if ( listen ( sfd , 16 ) < 0 ) {
perror ( " listen " ) ;
( void ) unlink ( sock_path ) ;
close ( sfd ) ;
return 8 ;
}
}
fprintf ( stderr , " amduatd: root=%s sock=%s \n " , root , sock_path ) ;
while ( ! amduatd_should_exit ) {
2026-01-21 19:51:26 +01:00
fd_set rfds ;
struct timeval tv ;
struct timeval * tvp = NULL ;
int rc ;
if ( fed_coord ! = NULL ) {
uint64_t now_ms = amduatd_now_ms ( ) ;
uint64_t due_ms = last_tick_ms = = 0
? now_ms
: last_tick_ms + AMDUATD_FED_TICK_MS ;
uint64_t wait_ms = due_ms < = now_ms ? 0 : ( due_ms - now_ms ) ;
tv . tv_sec = ( time_t ) ( wait_ms / 1000u ) ;
tv . tv_usec = ( suseconds_t ) ( ( wait_ms % 1000u ) * 1000u ) ;
tvp = & tv ;
}
FD_ZERO ( & rfds ) ;
FD_SET ( sfd , & rfds ) ;
rc = select ( sfd + 1 , & rfds , NULL , NULL , tvp ) ;
if ( rc < 0 ) {
2025-12-22 15:23:54 +01:00
if ( errno = = EINTR ) {
continue ;
}
2026-01-21 19:51:26 +01:00
perror ( " select " ) ;
2025-12-22 15:23:54 +01:00
break ;
}
2026-01-21 19:51:26 +01:00
if ( fed_coord ! = NULL ) {
uint64_t now_ms = amduatd_now_ms ( ) ;
if ( last_tick_ms = = 0 | |
now_ms - last_tick_ms > = AMDUATD_FED_TICK_MS ) {
( void ) amduat_fed_coord_tick ( fed_coord , now_ms ) ;
last_tick_ms = now_ms ;
}
}
if ( rc = = 0 ) {
continue ;
}
if ( ! FD_ISSET ( sfd , & rfds ) ) {
continue ;
}
{
int cfd = accept ( sfd , NULL , NULL ) ;
if ( cfd < 0 ) {
if ( errno = = EINTR ) {
continue ;
}
perror ( " accept " ) ;
break ;
}
( void ) amduatd_handle_conn ( cfd ,
& store ,
& cfg ,
api_contract_ref ,
ui_ref ,
& concepts ,
2026-01-23 22:28:56 +01:00
& dcfg ,
2026-01-23 20:55:41 +01:00
fed_coord ,
2026-01-23 23:08:41 +01:00
& allowlist ,
2026-01-24 03:03:54 +01:00
& caps ,
2026-01-23 23:08:41 +01:00
root ) ;
2026-01-21 19:51:26 +01:00
( void ) close ( cfd ) ;
2026-01-24 07:22:51 +01:00
if ( dcfg . edges_refresh_ms ! = 0u ) {
uint64_t now_ms = amduatd_now_ms ( ) ;
if ( last_edges_refresh_ms = = 0u | |
now_ms - last_edges_refresh_ms > = dcfg . edges_refresh_ms ) {
amduatd_ctx_t refresh_ctx ;
refresh_ctx . store = & store ;
refresh_ctx . ui_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
refresh_ctx . store_cfg = & cfg ;
refresh_ctx . concepts = & concepts ;
refresh_ctx . daemon_cfg = & dcfg ;
refresh_ctx . root_path = root ;
refresh_ctx . caps = & caps ;
( void ) amduatd_concepts_refresh_edges ( & refresh_ctx , 0u ) ;
last_edges_refresh_ms = now_ms ;
}
}
2026-01-21 19:51:26 +01:00
}
2025-12-22 15:23:54 +01:00
}
2025-12-22 19:37:41 +01:00
amduat_reference_free ( & api_contract_ref ) ;
2025-12-23 09:14:58 +01:00
amduat_reference_free ( & ui_ref ) ;
2025-12-22 21:03:00 +01:00
amduatd_concepts_free ( & concepts ) ;
2026-01-21 19:51:26 +01:00
if ( fed_coord ! = NULL ) {
amduat_fed_coord_close ( fed_coord ) ;
}
2026-01-23 20:55:41 +01:00
amduatd_allowlist_free ( & allowlist ) ;
2025-12-22 15:23:54 +01:00
( void ) unlink ( sock_path ) ;
( void ) close ( sfd ) ;
return 0 ;
}