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-23 23:30:29 +01:00
# include "amduatd_ui.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 ;
static bool amduatd_http_send_json ( int fd ,
int code ,
const char * reason ,
const char * json ,
bool head_only ) ;
static bool amduatd_send_json_error ( int fd ,
int code ,
const char * reason ,
const char * msg ) ;
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-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 " ;
2025-12-22 21:03:00 +01:00
static const char * const AMDUATD_EDGES_FILE = " .amduatd.edges " ;
2026-01-23 20:55:41 +01:00
static const char * const AMDUATD_EDGE_COLLECTION = " daemon/edges " ;
2026-01-21 19:51:26 +01:00
static const uint64_t AMDUATD_FED_TICK_MS = 1000u ;
2026-01-23 22:28:56 +01:00
static const uint32_t AMDUATD_EDGE_VIEW_BATCH = 1024u ;
2025-12-22 21:03:00 +01:00
2026-01-23 20:55:41 +01:00
static const uint16_t AMDUATD_EDGE_COLLECTION_KIND = 1u ;
2026-01-23 23:08:41 +01:00
static const uint64_t AMDUATD_CAP_MAX_EXPIRY_SECONDS = 7u * 24u * 60u * 60u ;
2025-12-22 21:03:00 +01:00
2026-01-23 22:28:56 +01:00
typedef struct {
const char * space_id ;
bool space_enabled ;
bool migrate_unscoped_edges ;
} amduatd_cfg_t ;
2026-01-23 23:08:41 +01:00
typedef struct {
bool enabled ;
bool reads_enabled ;
uint8_t hmac_key [ 32 ] ;
size_t hmac_key_len ;
} amduatd_cap_state_t ;
2025-12-22 21:03:00 +01:00
typedef struct {
2026-01-23 20:55:41 +01:00
amduat_reference_t record_ref ;
amduat_reference_t src_ref ;
amduat_reference_t dst_ref ;
char * rel ;
} amduatd_edge_entry_t ;
typedef struct {
amduatd_edge_entry_t * items ;
2025-12-22 21:03:00 +01:00
size_t len ;
size_t cap ;
2026-01-23 20:55:41 +01:00
} amduatd_edge_list_t ;
2025-12-22 21:03:00 +01:00
2026-01-23 20:55:41 +01:00
static void amduatd_edge_entry_free ( amduatd_edge_entry_t * entry ) {
if ( entry = = NULL ) {
return ;
}
amduat_reference_free ( & entry - > record_ref ) ;
amduat_reference_free ( & entry - > src_ref ) ;
amduat_reference_free ( & entry - > dst_ref ) ;
free ( entry - > rel ) ;
entry - > rel = NULL ;
}
static void amduatd_edge_list_clear ( amduatd_edge_list_t * list ) {
2025-12-22 21:03:00 +01:00
if ( list = = NULL ) {
return ;
}
2026-01-23 20:55:41 +01:00
for ( size_t i = 0 ; i < list - > len ; + + i ) {
amduatd_edge_entry_free ( & list - > items [ i ] ) ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 20:55:41 +01:00
free ( list - > items ) ;
list - > items = NULL ;
2025-12-22 21:03:00 +01:00
list - > len = 0 ;
list - > cap = 0 ;
}
2026-01-23 20:55:41 +01:00
static bool amduatd_edge_list_push ( amduatd_edge_list_t * list ,
const amduatd_edge_entry_t * entry ) {
amduatd_edge_entry_t cloned ;
amduatd_edge_entry_t * next ;
2025-12-22 21:03:00 +01:00
2026-01-23 20:55:41 +01:00
if ( list = = NULL | | entry = = NULL ) {
2025-12-22 21:03:00 +01:00
return false ;
}
memset ( & cloned , 0 , sizeof ( cloned ) ) ;
2026-01-23 20:55:41 +01:00
if ( ! amduat_reference_clone ( entry - > record_ref , & cloned . record_ref ) | |
! amduat_reference_clone ( entry - > src_ref , & cloned . src_ref ) | |
! amduat_reference_clone ( entry - > dst_ref , & cloned . dst_ref ) ) {
amduatd_edge_entry_free ( & cloned ) ;
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 20:55:41 +01:00
if ( entry - > rel ! = NULL ) {
cloned . rel = strdup ( entry - > rel ) ;
if ( cloned . rel = = NULL ) {
amduatd_edge_entry_free ( & cloned ) ;
return false ;
}
}
2025-12-22 21:03:00 +01:00
if ( list - > len = = list - > cap ) {
size_t next_cap = list - > cap ! = 0 ? list - > cap * 2u : 64u ;
2026-01-23 20:55:41 +01:00
next = ( amduatd_edge_entry_t * ) realloc ( list - > items ,
next_cap * sizeof ( * list - > items ) ) ;
2025-12-22 21:03:00 +01:00
if ( next = = NULL ) {
2026-01-23 20:55:41 +01:00
amduatd_edge_entry_free ( & cloned ) ;
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 20:55:41 +01:00
list - > items = next ;
2025-12-22 21:03:00 +01:00
list - > cap = next_cap ;
}
2026-01-23 20:55:41 +01:00
list - > items [ list - > len + + ] = cloned ;
2025-12-22 21:03:00 +01:00
return true ;
}
typedef struct {
const char * root_path ;
char edges_path [ 1024 ] ;
2026-01-23 22:28:56 +01:00
char * edge_collection_name ;
2025-12-22 21:03:00 +01:00
amduat_reference_t rel_aliases_ref ;
amduat_reference_t rel_materializes_ref ;
2025-12-23 09:14:58 +01:00
amduat_reference_t rel_represents_ref ;
amduat_reference_t rel_requires_key_ref ;
amduat_reference_t rel_within_domain_ref ;
amduat_reference_t rel_computed_by_ref ;
amduat_reference_t rel_has_provenance_ref ;
2026-01-23 20:55:41 +01:00
amduat_asl_collection_store_t edge_collection ;
amduatd_edge_list_t edges ;
2025-12-22 21:03:00 +01:00
} amduatd_concepts_t ;
2025-12-23 09:14:58 +01:00
static bool amduatd_concepts_put_name_artifact ( amduat_asl_store_t * store ,
const char * name ,
amduat_reference_t * out_ref ) ;
static bool amduatd_concepts_put_edge ( amduat_asl_store_t * store ,
amduatd_concepts_t * c ,
amduat_reference_t from ,
amduat_reference_t to ,
amduat_reference_t relation_concept_ref ,
2026-01-23 20:55:41 +01:00
amduat_octets_t actor ,
2025-12-23 09:14:58 +01:00
amduat_reference_t * out_edge_ref ) ;
static bool amduatd_concepts_lookup_alias ( amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
const amduatd_concepts_t * c ,
const char * name ,
amduat_reference_t * out_concept_ref ) ;
2025-12-22 21:03:00 +01:00
static void amduatd_concepts_free ( amduatd_concepts_t * c ) {
if ( c = = NULL ) {
return ;
}
amduat_reference_free ( & c - > rel_aliases_ref ) ;
amduat_reference_free ( & c - > rel_materializes_ref ) ;
2025-12-23 09:14:58 +01:00
amduat_reference_free ( & c - > rel_represents_ref ) ;
amduat_reference_free ( & c - > rel_requires_key_ref ) ;
amduat_reference_free ( & c - > rel_within_domain_ref ) ;
amduat_reference_free ( & c - > rel_computed_by_ref ) ;
amduat_reference_free ( & c - > rel_has_provenance_ref ) ;
2026-01-23 20:55:41 +01:00
amduatd_edge_list_clear ( & c - > edges ) ;
2026-01-23 22:28:56 +01:00
free ( c - > edge_collection_name ) ;
c - > edge_collection_name = NULL ;
2026-01-23 20:55:41 +01:00
}
enum {
AMDUATD_EDGE_MAGIC_LEN = 8 ,
AMDUATD_EDGE_VERSION = 1
} ;
static const uint8_t k_amduatd_edge_magic [ AMDUATD_EDGE_MAGIC_LEN ] = {
' A ' , ' S ' , ' L ' , ' E ' , ' D ' , ' G ' , ' 1 ' , ' \0 '
} ;
2026-01-23 23:08:41 +01:00
enum {
AMDUATD_CAP_MAGIC_LEN = 8 ,
AMDUATD_CAP_VERSION = 1
} ;
enum {
AMDUATD_CAP_KIND_ARTIFACT_REF = 1 ,
AMDUATD_CAP_KIND_POINTER_NAME = 2 ,
AMDUATD_CAP_KIND_PEL_VIEW = 3
} ;
static const uint8_t k_amduatd_cap_magic [ AMDUATD_CAP_MAGIC_LEN ] = {
' A ' , ' S ' , ' L ' , ' C ' , ' A ' , ' P ' , ' 1 ' , ' \0 '
} ;
2026-01-23 20:55:41 +01:00
static const char * const AMDUATD_EDGE_SCHEMA = " tgk/edge " ;
static const char * const AMDUATD_REL_ALIAS = " alias " ;
static const char * const AMDUATD_REL_MATERIALIZES = " materializes " ;
static const char * const AMDUATD_REL_REPRESENTS = " represents " ;
static const char * const AMDUATD_REL_REQUIRES_KEY = " requires_key " ;
static const char * const AMDUATD_REL_WITHIN_DOMAIN = " within_domain " ;
static const char * const AMDUATD_REL_COMPUTED_BY = " computed_by " ;
static const char * const AMDUATD_REL_HAS_PROVENANCE = " has_provenance " ;
static void amduatd_store_u32_le ( uint8_t * out , uint32_t value ) {
out [ 0 ] = ( uint8_t ) ( value & 0xffu ) ;
out [ 1 ] = ( uint8_t ) ( ( value > > 8 ) & 0xffu ) ;
out [ 2 ] = ( uint8_t ) ( ( value > > 16 ) & 0xffu ) ;
out [ 3 ] = ( uint8_t ) ( ( value > > 24 ) & 0xffu ) ;
}
static bool amduatd_read_u32_le ( const uint8_t * data ,
size_t len ,
size_t * offset ,
uint32_t * out ) {
if ( len - * offset < 4u ) {
return false ;
}
* out = ( uint32_t ) data [ * offset ] |
( ( uint32_t ) data [ * offset + 1u ] < < 8 ) |
( ( uint32_t ) data [ * offset + 2u ] < < 16 ) |
( ( uint32_t ) data [ * offset + 3u ] < < 24 ) ;
* offset + = 4u ;
return true ;
}
2026-01-23 23:08:41 +01:00
static void amduatd_store_u64_le ( uint8_t * out , uint64_t value ) {
out [ 0 ] = ( uint8_t ) ( value & 0xffu ) ;
out [ 1 ] = ( uint8_t ) ( ( value > > 8 ) & 0xffu ) ;
out [ 2 ] = ( uint8_t ) ( ( value > > 16 ) & 0xffu ) ;
out [ 3 ] = ( uint8_t ) ( ( value > > 24 ) & 0xffu ) ;
out [ 4 ] = ( uint8_t ) ( ( value > > 32 ) & 0xffu ) ;
out [ 5 ] = ( uint8_t ) ( ( value > > 40 ) & 0xffu ) ;
out [ 6 ] = ( uint8_t ) ( ( value > > 48 ) & 0xffu ) ;
out [ 7 ] = ( uint8_t ) ( ( value > > 56 ) & 0xffu ) ;
}
static bool amduatd_read_u64_le ( const uint8_t * data ,
size_t len ,
size_t * offset ,
uint64_t * out ) {
if ( len - * offset < 8u ) {
return false ;
}
* out = ( uint64_t ) data [ * offset ] |
( ( uint64_t ) data [ * offset + 1u ] < < 8 ) |
( ( uint64_t ) data [ * offset + 2u ] < < 16 ) |
( ( uint64_t ) data [ * offset + 3u ] < < 24 ) |
( ( uint64_t ) data [ * offset + 4u ] < < 32 ) |
( ( uint64_t ) data [ * offset + 5u ] < < 40 ) |
( ( uint64_t ) data [ * offset + 6u ] < < 48 ) |
( ( uint64_t ) data [ * offset + 7u ] < < 56 ) ;
* offset + = 8u ;
return true ;
}
2026-01-23 20:55:41 +01:00
static bool amduatd_add_size ( size_t * acc , size_t add ) {
if ( * acc > SIZE_MAX - add ) {
return false ;
}
* acc + = add ;
return true ;
}
static bool amduatd_is_ascii ( amduat_octets_t bytes ) {
if ( bytes . len = = 0u | | bytes . data = = NULL ) {
return false ;
}
for ( size_t i = 0u ; i < bytes . len ; + + i ) {
uint8_t c = bytes . data [ i ] ;
if ( c < 0x20u | | c > 0x7eu ) {
return false ;
}
}
return true ;
}
static bool amduatd_edge_payload_encode ( amduat_reference_t src ,
amduat_reference_t dst ,
const char * rel ,
amduat_octets_t * out_payload ) {
amduat_octets_t src_bytes = amduat_octets ( NULL , 0u ) ;
amduat_octets_t dst_bytes = amduat_octets ( NULL , 0u ) ;
size_t total_len = 0u ;
size_t offset = 0u ;
uint8_t * buffer = NULL ;
uint32_t rel_len = 0u ;
if ( out_payload = = NULL | | rel = = NULL ) {
return false ;
}
out_payload - > data = NULL ;
out_payload - > len = 0u ;
{
size_t rel_len_full = strlen ( rel ) ;
if ( rel_len_full > UINT32_MAX ) {
return false ;
}
rel_len = ( uint32_t ) rel_len_full ;
}
if ( rel_len = = 0u ) {
return false ;
}
if ( ! amduatd_is_ascii ( amduat_octets ( ( const uint8_t * ) rel , rel_len ) ) ) {
return false ;
}
if ( ! amduat_enc_asl1_core_encode_reference_v1 ( src , & src_bytes ) | |
! amduat_enc_asl1_core_encode_reference_v1 ( dst , & dst_bytes ) ) {
free ( ( void * ) src_bytes . data ) ;
free ( ( void * ) dst_bytes . data ) ;
return false ;
}
if ( src_bytes . len > UINT32_MAX | | dst_bytes . len > UINT32_MAX ) {
free ( ( void * ) src_bytes . data ) ;
free ( ( void * ) dst_bytes . data ) ;
return false ;
}
if ( ! amduatd_add_size ( & total_len ,
AMDUATD_EDGE_MAGIC_LEN + 4u + 4u + src_bytes . len ) | |
! amduatd_add_size ( & total_len , 4u + dst_bytes . len ) | |
! amduatd_add_size ( & total_len , 4u + rel_len ) | |
! amduatd_add_size ( & total_len , 1u ) ) {
free ( ( void * ) src_bytes . data ) ;
free ( ( void * ) dst_bytes . data ) ;
return false ;
}
buffer = ( uint8_t * ) malloc ( total_len ) ;
if ( buffer = = NULL ) {
free ( ( void * ) src_bytes . data ) ;
free ( ( void * ) dst_bytes . data ) ;
return false ;
}
memcpy ( buffer + offset , k_amduatd_edge_magic , AMDUATD_EDGE_MAGIC_LEN ) ;
offset + = AMDUATD_EDGE_MAGIC_LEN ;
amduatd_store_u32_le ( buffer + offset , AMDUATD_EDGE_VERSION ) ;
offset + = 4u ;
amduatd_store_u32_le ( buffer + offset , ( uint32_t ) src_bytes . len ) ;
offset + = 4u ;
memcpy ( buffer + offset , src_bytes . data , src_bytes . len ) ;
offset + = src_bytes . len ;
amduatd_store_u32_le ( buffer + offset , ( uint32_t ) dst_bytes . len ) ;
offset + = 4u ;
memcpy ( buffer + offset , dst_bytes . data , dst_bytes . len ) ;
offset + = dst_bytes . len ;
amduatd_store_u32_le ( buffer + offset , rel_len ) ;
offset + = 4u ;
memcpy ( buffer + offset , rel , rel_len ) ;
offset + = rel_len ;
buffer [ offset + + ] = 0u ;
free ( ( void * ) src_bytes . data ) ;
free ( ( void * ) dst_bytes . data ) ;
if ( offset ! = total_len ) {
free ( buffer ) ;
return false ;
}
out_payload - > data = buffer ;
out_payload - > len = total_len ;
return true ;
}
static bool amduatd_edge_payload_decode ( amduat_octets_t payload ,
amduat_reference_t * out_src ,
amduat_reference_t * out_dst ,
char * * out_rel ) {
size_t offset = 0u ;
uint32_t version = 0u ;
uint32_t src_len = 0u ;
uint32_t dst_len = 0u ;
uint32_t rel_len = 0u ;
uint8_t flags = 0u ;
amduat_octets_t src_bytes ;
amduat_octets_t dst_bytes ;
char * rel = NULL ;
if ( out_src = = NULL | | out_dst = = NULL | | out_rel = = NULL ) {
return false ;
}
* out_src = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
* out_dst = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
* out_rel = NULL ;
if ( payload . len < AMDUATD_EDGE_MAGIC_LEN + 4u + 1u | |
payload . data = = NULL ) {
return false ;
}
if ( memcmp ( payload . data , k_amduatd_edge_magic ,
AMDUATD_EDGE_MAGIC_LEN ) ! = 0 ) {
return false ;
}
offset + = AMDUATD_EDGE_MAGIC_LEN ;
if ( ! amduatd_read_u32_le ( payload . data , payload . len , & offset , & version ) | |
version ! = AMDUATD_EDGE_VERSION ) {
return false ;
}
if ( ! amduatd_read_u32_le ( payload . data , payload . len , & offset , & src_len ) ) {
return false ;
}
if ( payload . len - offset < src_len ) {
return false ;
}
src_bytes = amduat_octets ( payload . data + offset , src_len ) ;
offset + = src_len ;
if ( ! amduatd_read_u32_le ( payload . data , payload . len , & offset , & dst_len ) ) {
return false ;
}
if ( payload . len - offset < dst_len ) {
return false ;
}
dst_bytes = amduat_octets ( payload . data + offset , dst_len ) ;
offset + = dst_len ;
if ( ! amduatd_read_u32_le ( payload . data , payload . len , & offset , & rel_len ) ) {
return false ;
}
if ( payload . len - offset < rel_len + 1u ) {
return false ;
}
if ( ! amduatd_is_ascii ( amduat_octets ( payload . data + offset , rel_len ) ) ) {
return false ;
}
rel = ( char * ) malloc ( rel_len + 1u ) ;
if ( rel = = NULL ) {
return false ;
}
memcpy ( rel , payload . data + offset , rel_len ) ;
rel [ rel_len ] = ' \0 ' ;
offset + = rel_len ;
flags = payload . data [ offset + + ] ;
( void ) flags ;
if ( offset ! = payload . len ) {
free ( rel ) ;
return false ;
}
if ( ! amduat_enc_asl1_core_decode_reference_v1 ( src_bytes , out_src ) | |
! amduat_enc_asl1_core_decode_reference_v1 ( dst_bytes , out_dst ) ) {
amduat_reference_free ( out_src ) ;
amduat_reference_free ( out_dst ) ;
free ( rel ) ;
return false ;
}
* out_rel = rel ;
return true ;
}
static const char * amduatd_relation_name_for_ref (
const amduatd_concepts_t * c ,
amduat_reference_t ref ) {
if ( c = = NULL ) {
return NULL ;
}
if ( amduat_reference_eq ( ref , c - > rel_aliases_ref ) ) {
return AMDUATD_REL_ALIAS ;
}
if ( amduat_reference_eq ( ref , c - > rel_materializes_ref ) ) {
return AMDUATD_REL_MATERIALIZES ;
}
if ( amduat_reference_eq ( ref , c - > rel_represents_ref ) ) {
return AMDUATD_REL_REPRESENTS ;
}
if ( amduat_reference_eq ( ref , c - > rel_requires_key_ref ) ) {
return AMDUATD_REL_REQUIRES_KEY ;
}
if ( amduat_reference_eq ( ref , c - > rel_within_domain_ref ) ) {
return AMDUATD_REL_WITHIN_DOMAIN ;
}
if ( amduat_reference_eq ( ref , c - > rel_computed_by_ref ) ) {
return AMDUATD_REL_COMPUTED_BY ;
}
if ( amduat_reference_eq ( ref , c - > rel_has_provenance_ref ) ) {
return AMDUATD_REL_HAS_PROVENANCE ;
}
return NULL ;
}
static bool amduatd_build_collection_head_name ( const char * name ,
char * * out_name ) {
size_t name_len ;
size_t total_len ;
char * buffer ;
size_t offset = 0u ;
if ( name = = NULL | | out_name = = NULL ) {
return false ;
}
if ( ! amduat_asl_pointer_name_is_valid ( name ) ) {
return false ;
}
name_len = strlen ( name ) ;
total_len = 11u + name_len + 5u + 1u ;
buffer = ( char * ) malloc ( total_len ) ;
if ( buffer = = NULL ) {
return false ;
}
memcpy ( buffer + offset , " collection/ " , 11u ) ;
offset + = 11u ;
memcpy ( buffer + offset , name , name_len ) ;
offset + = name_len ;
memcpy ( buffer + offset , " /head " , 5u ) ;
offset + = 5u ;
buffer [ offset ] = ' \0 ' ;
* out_name = buffer ;
return true ;
}
static bool amduatd_build_collection_log_head_name ( const char * name ,
char * * out_name ) {
size_t name_len ;
size_t log_name_len ;
size_t total_len ;
char * buffer ;
size_t offset = 0u ;
if ( name = = NULL | | out_name = = NULL ) {
return false ;
}
if ( ! amduat_asl_pointer_name_is_valid ( name ) ) {
return false ;
}
name_len = strlen ( name ) ;
log_name_len = 11u + name_len + 4u ;
total_len = 4u + log_name_len + 5u + 1u ;
buffer = ( char * ) malloc ( total_len ) ;
if ( buffer = = NULL ) {
return false ;
}
memcpy ( buffer + offset , " log/ " , 4u ) ;
offset + = 4u ;
memcpy ( buffer + offset , " collection/ " , 11u ) ;
offset + = 11u ;
memcpy ( buffer + offset , name , name_len ) ;
offset + = name_len ;
memcpy ( buffer + offset , " /log " , 4u ) ;
offset + = 4u ;
memcpy ( buffer + offset , " /head " , 5u ) ;
offset + = 5u ;
buffer [ offset ] = ' \0 ' ;
* out_name = buffer ;
return true ;
}
static bool amduatd_get_none_ref ( amduat_asl_store_t * store ,
amduat_reference_t * out_ref ) {
amduat_artifact_t none_artifact ;
if ( store = = NULL | | out_ref = = NULL ) {
return false ;
}
if ( ! amduat_asl_none_artifact ( & none_artifact ) ) {
return false ;
}
if ( amduat_asl_store_put ( store , none_artifact , out_ref ) ! =
AMDUAT_ASL_STORE_OK ) {
amduat_artifact_free ( & none_artifact ) ;
return false ;
}
amduat_artifact_free ( & none_artifact ) ;
return true ;
}
static bool amduatd_put_view_params ( amduat_asl_store_t * store ,
uint64_t from_offset ,
uint32_t limit ,
amduat_reference_t * out_ref ) {
uint8_t payload [ 12 ] ;
if ( store = = NULL | | out_ref = = NULL ) {
return false ;
}
payload [ 0 ] = ( uint8_t ) ( from_offset & 0xffu ) ;
payload [ 1 ] = ( uint8_t ) ( ( from_offset > > 8 ) & 0xffu ) ;
payload [ 2 ] = ( uint8_t ) ( ( from_offset > > 16 ) & 0xffu ) ;
payload [ 3 ] = ( uint8_t ) ( ( from_offset > > 24 ) & 0xffu ) ;
payload [ 4 ] = ( uint8_t ) ( ( from_offset > > 32 ) & 0xffu ) ;
payload [ 5 ] = ( uint8_t ) ( ( from_offset > > 40 ) & 0xffu ) ;
payload [ 6 ] = ( uint8_t ) ( ( from_offset > > 48 ) & 0xffu ) ;
payload [ 7 ] = ( uint8_t ) ( ( from_offset > > 56 ) & 0xffu ) ;
payload [ 8 ] = ( uint8_t ) ( limit & 0xffu ) ;
payload [ 9 ] = ( uint8_t ) ( ( limit > > 8 ) & 0xffu ) ;
payload [ 10 ] = ( uint8_t ) ( ( limit > > 16 ) & 0xffu ) ;
payload [ 11 ] = ( uint8_t ) ( ( limit > > 24 ) & 0xffu ) ;
if ( amduat_asl_record_store_put (
store ,
amduat_octets ( " pel/params " , strlen ( " pel/params " ) ) ,
amduat_octets ( payload , sizeof ( payload ) ) ,
out_ref ) ! = AMDUAT_ASL_STORE_OK ) {
return false ;
}
return true ;
}
static void amduatd_program_free ( amduat_pel_program_t * program ) {
if ( program = = NULL | | program - > nodes = = NULL ) {
return ;
}
for ( size_t i = 0u ; i < program - > nodes_len ; + + i ) {
free ( program - > nodes [ i ] . inputs ) ;
}
free ( program - > nodes ) ;
free ( program - > roots ) ;
program - > nodes = NULL ;
program - > roots = NULL ;
program - > nodes_len = 0u ;
program - > roots_len = 0u ;
}
static bool amduatd_build_collection_view_program (
amduat_pel_program_t * out_program ) {
static const char k_op_snapshot [ ] = " collection.snapshot_decode_v1 " ;
static const char k_op_read_range [ ] = " log.read_range_v1 " ;
static const char k_op_merge [ ] = " collection.merge_refs_v1 " ;
amduat_pel_node_t * nodes ;
amduat_pel_root_ref_t * roots ;
if ( out_program = = NULL ) {
return false ;
}
memset ( out_program , 0 , sizeof ( * out_program ) ) ;
nodes = ( amduat_pel_node_t * ) calloc ( 3u , sizeof ( * nodes ) ) ;
roots = ( amduat_pel_root_ref_t * ) calloc ( 1u , sizeof ( * roots ) ) ;
if ( nodes = = NULL | | roots = = NULL ) {
free ( nodes ) ;
free ( roots ) ;
return false ;
}
nodes [ 0 ] . id = 1u ;
nodes [ 0 ] . op . name = amduat_octets ( k_op_snapshot , strlen ( k_op_snapshot ) ) ;
nodes [ 0 ] . op . version = 1u ;
nodes [ 0 ] . inputs_len = 1u ;
nodes [ 0 ] . inputs =
( amduat_pel_dag_input_t * ) calloc ( 1u , sizeof ( * nodes [ 0 ] . inputs ) ) ;
if ( nodes [ 0 ] . inputs = = NULL ) {
free ( nodes ) ;
free ( roots ) ;
return false ;
}
nodes [ 0 ] . inputs [ 0 ] . kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL ;
nodes [ 0 ] . inputs [ 0 ] . value . external . input_index = 0u ;
nodes [ 0 ] . params = amduat_octets ( NULL , 0u ) ;
nodes [ 1 ] . id = 2u ;
nodes [ 1 ] . op . name = amduat_octets ( k_op_read_range , strlen ( k_op_read_range ) ) ;
nodes [ 1 ] . op . version = 1u ;
nodes [ 1 ] . inputs_len = 3u ;
nodes [ 1 ] . inputs =
( amduat_pel_dag_input_t * ) calloc ( 3u , sizeof ( * nodes [ 1 ] . inputs ) ) ;
if ( nodes [ 1 ] . inputs = = NULL ) {
free ( nodes [ 0 ] . inputs ) ;
free ( nodes ) ;
free ( roots ) ;
return false ;
}
nodes [ 1 ] . inputs [ 0 ] . kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL ;
nodes [ 1 ] . inputs [ 0 ] . value . external . input_index = 1u ;
nodes [ 1 ] . inputs [ 1 ] . kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL ;
nodes [ 1 ] . inputs [ 1 ] . value . external . input_index = 2u ;
nodes [ 1 ] . inputs [ 2 ] . kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL ;
nodes [ 1 ] . inputs [ 2 ] . value . external . input_index = 3u ;
nodes [ 1 ] . params = amduat_octets ( NULL , 0u ) ;
nodes [ 2 ] . id = 3u ;
nodes [ 2 ] . op . name = amduat_octets ( k_op_merge , strlen ( k_op_merge ) ) ;
nodes [ 2 ] . op . version = 1u ;
nodes [ 2 ] . inputs_len = 4u ;
nodes [ 2 ] . inputs =
( amduat_pel_dag_input_t * ) calloc ( 4u , sizeof ( * nodes [ 2 ] . inputs ) ) ;
if ( nodes [ 2 ] . inputs = = NULL ) {
free ( nodes [ 1 ] . inputs ) ;
free ( nodes [ 0 ] . inputs ) ;
free ( nodes ) ;
free ( roots ) ;
return false ;
}
nodes [ 2 ] . inputs [ 0 ] . kind = AMDUAT_PEL_DAG_INPUT_NODE ;
nodes [ 2 ] . inputs [ 0 ] . value . node . node_id = 1u ;
nodes [ 2 ] . inputs [ 0 ] . value . node . output_index = 0u ;
nodes [ 2 ] . inputs [ 1 ] . kind = AMDUAT_PEL_DAG_INPUT_NODE ;
nodes [ 2 ] . inputs [ 1 ] . value . node . node_id = 2u ;
nodes [ 2 ] . inputs [ 1 ] . value . node . output_index = 0u ;
nodes [ 2 ] . inputs [ 2 ] . kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL ;
nodes [ 2 ] . inputs [ 2 ] . value . external . input_index = 2u ;
nodes [ 2 ] . inputs [ 3 ] . kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL ;
nodes [ 2 ] . inputs [ 3 ] . value . external . input_index = 3u ;
nodes [ 2 ] . params = amduat_octets ( NULL , 0u ) ;
roots [ 0 ] . node_id = 3u ;
roots [ 0 ] . output_index = 0u ;
out_program - > nodes = nodes ;
out_program - > nodes_len = 3u ;
out_program - > roots = roots ;
out_program - > roots_len = 1u ;
return true ;
}
static bool amduatd_collection_view ( amduat_asl_store_t * store ,
const char * root_path ,
const char * name ,
uint64_t from ,
uint32_t limit ,
amduat_asl_collection_view_t * out_view ) {
amduat_asl_pointer_store_t pointer_store ;
amduat_reference_t snapshot_ref ;
amduat_reference_t log_head_ref ;
amduat_reference_t none_ref ;
amduat_reference_t params_ref ;
amduat_reference_t program_ref ;
amduat_reference_t input_refs [ 4 ] ;
amduat_pel_program_t program ;
amduat_octets_t program_bytes = amduat_octets ( NULL , 0u ) ;
amduat_pel_run_result_t run_result ;
amduat_artifact_t view_artifact ;
amduat_asl_collection_view_t view ;
amduat_type_tag_t expected_type_tag ;
amduat_asl_encoding_profile_id_t expected_profile ;
char * snapshot_name = NULL ;
char * log_head_name = NULL ;
bool snapshot_exists = false ;
bool log_head_exists = false ;
bool ok = false ;
if ( out_view = = NULL ) {
return false ;
}
memset ( out_view , 0 , sizeof ( * out_view ) ) ;
memset ( & snapshot_ref , 0 , sizeof ( snapshot_ref ) ) ;
memset ( & log_head_ref , 0 , sizeof ( log_head_ref ) ) ;
memset ( & none_ref , 0 , sizeof ( none_ref ) ) ;
memset ( & params_ref , 0 , sizeof ( params_ref ) ) ;
memset ( & program_ref , 0 , sizeof ( program_ref ) ) ;
memset ( & run_result , 0 , sizeof ( run_result ) ) ;
memset ( & view , 0 , sizeof ( view ) ) ;
if ( store = = NULL | | root_path = = NULL | | name = = NULL ) {
return false ;
}
if ( ! amduat_asl_pointer_store_init ( & pointer_store , root_path ) ) {
return false ;
}
if ( ! amduatd_get_none_ref ( store , & none_ref ) ) {
return false ;
}
if ( ! amduatd_build_collection_head_name ( name , & snapshot_name ) | |
! amduatd_build_collection_log_head_name ( name , & log_head_name ) ) {
goto view_cleanup ;
}
if ( amduat_asl_pointer_get ( & pointer_store , snapshot_name ,
& snapshot_exists , & snapshot_ref ) ! =
AMDUAT_ASL_POINTER_OK ) {
goto view_cleanup ;
}
if ( ! snapshot_exists & &
! amduat_reference_clone ( none_ref , & snapshot_ref ) ) {
goto view_cleanup ;
}
if ( amduat_asl_pointer_get ( & pointer_store , log_head_name ,
& log_head_exists , & log_head_ref ) ! =
AMDUAT_ASL_POINTER_OK ) {
goto view_cleanup ;
}
if ( ! log_head_exists & &
! amduat_reference_clone ( none_ref , & log_head_ref ) ) {
goto view_cleanup ;
}
if ( ! amduatd_put_view_params ( store , from , limit , & params_ref ) ) {
goto view_cleanup ;
}
if ( ! amduatd_build_collection_view_program ( & program ) ) {
goto view_cleanup ;
}
if ( ! amduat_enc_pel_program_dag_encode_v1 ( & program , & program_bytes ) ) {
amduatd_program_free ( & program ) ;
goto view_cleanup ;
}
amduatd_program_free ( & program ) ;
if ( ! amduat_pel_program_dag_desc_get_program_binding ( & expected_type_tag ,
& expected_profile ) ) {
goto view_cleanup ;
}
if ( expected_profile ! = AMDUAT_PEL_ENC_PROGRAM_DAG_V1 ) {
goto view_cleanup ;
}
{
amduat_artifact_t program_artifact =
amduat_artifact_with_type ( program_bytes , expected_type_tag ) ;
if ( amduat_asl_store_put ( store , program_artifact , & program_ref ) ! =
AMDUAT_ASL_STORE_OK ) {
goto view_cleanup ;
}
}
input_refs [ 0 ] = snapshot_ref ;
input_refs [ 1 ] = log_head_ref ;
input_refs [ 2 ] = params_ref ;
input_refs [ 3 ] = params_ref ;
if ( ! amduat_pel_surf_run_with_result ( store ,
amduat_pel_program_dag_scheme_ref ( ) ,
program_ref ,
input_refs ,
4u ,
true ,
params_ref ,
& run_result ) ) {
goto view_cleanup ;
}
if ( ! run_result . has_result_value | |
run_result . result_value . core_result . status ! = AMDUAT_PEL_EXEC_STATUS_OK | |
run_result . output_refs_len ! = 1u ) {
goto view_cleanup ;
}
memset ( & view_artifact , 0 , sizeof ( view_artifact ) ) ;
if ( amduat_asl_store_get ( store , run_result . output_refs [ 0 ] ,
& view_artifact ) ! = AMDUAT_ASL_STORE_OK ) {
goto view_cleanup ;
}
if ( ! view_artifact . has_type_tag | |
view_artifact . type_tag . tag_id ! = AMDUAT_TYPE_TAG_ASL_COLLECTION_VIEW_1 | |
! amduat_asl_collection_view_decode_v1 ( view_artifact . bytes , & view ) ) {
amduat_artifact_free ( & view_artifact ) ;
goto view_cleanup ;
}
amduat_artifact_free ( & view_artifact ) ;
* out_view = view ;
memset ( & view , 0 , sizeof ( view ) ) ;
ok = true ;
view_cleanup :
amduat_asl_collection_view_free ( & view ) ;
if ( run_result . has_result_value ) {
amduat_enc_pel1_result_free ( & run_result . result_value ) ;
}
if ( run_result . output_refs ! = NULL ) {
amduat_pel_surf_free_refs ( run_result . output_refs ,
run_result . output_refs_len ) ;
}
amduat_pel_surf_free_ref ( & run_result . result_ref ) ;
amduat_reference_free ( & snapshot_ref ) ;
amduat_reference_free ( & log_head_ref ) ;
amduat_reference_free ( & none_ref ) ;
amduat_reference_free ( & params_ref ) ;
amduat_reference_free ( & program_ref ) ;
free ( ( void * ) program_bytes . data ) ;
free ( snapshot_name ) ;
free ( log_head_name ) ;
return ok ;
2025-12-22 21:03:00 +01:00
}
2025-12-22 15:23:54 +01:00
2025-12-22 19:37:41 +01:00
static const char k_amduatd_contract_v1_json [ ] =
" { "
" \" contract \" : \" AMDUATD/API/1 \" , "
" \" base_path \" : \" /v1 \" , "
" \" endpoints \" :[ "
2025-12-22 21:03:00 +01:00
" { \" method \" : \" GET \" , \" path \" : \" /v1/ui \" }, "
2025-12-22 19:37:41 +01:00
" { \" method \" : \" GET \" , \" path \" : \" /v1/meta \" }, "
" { \" method \" : \" HEAD \" , \" path \" : \" /v1/meta \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/contract \" }, "
2026-01-23 23:08:41 +01:00
" { \" method \" : \" POST \" , \" path \" : \" /v1/capabilities \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/cap/resolve \" }, "
2026-01-21 19:51:26 +01:00
" { \" method \" : \" GET \" , \" path \" : \" /v1/fed/records \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/fed/artifacts/{ref} \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/fed/status \" }, "
2025-12-22 21:03:00 +01:00
" { \" method \" : \" POST \" , \" path \" : \" /v1/concepts \" }, "
2025-12-22 21:13:26 +01:00
" { \" method \" : \" GET \" , \" path \" : \" /v1/concepts \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/concepts/{name} \" }, "
2025-12-22 21:03:00 +01:00
" { \" method \" : \" POST \" , \" path \" : \" /v1/concepts/{name}/publish \" }, "
" { \" method \" : \" GET \" , \" path \" : \" /v1/resolve/{name} \" }, "
2025-12-22 19:37:41 +01:00
" { \" method \" : \" POST \" , \" path \" : \" /v1/artifacts \" }, "
2025-12-23 09:14:58 +01:00
" { \" method \" : \" GET \" , \" path \" : \" /v1/relations \" }, "
2025-12-22 19:37:41 +01:00
" { \" method \" : \" GET \" , \" path \" : \" /v1/artifacts/{ref} \" }, "
" { \" method \" : \" HEAD \" , \" path \" : \" /v1/artifacts/{ref} \" }, "
2025-12-22 21:25:06 +01:00
" { \" method \" : \" GET \" , \" path \" : \" /v1/artifacts/{ref}?format=info \" }, "
2025-12-22 20:14:47 +01:00
" { \" method \" : \" POST \" , \" path \" : \" /v1/pel/run \" }, "
2025-12-23 09:14:58 +01:00
" { \" method \" : \" POST \" , \" path \" : \" /v1/pel/programs \" }, "
" { \" method \" : \" POST \" , \" path \" : \" /v1/context_frames \" } "
2025-12-22 19:37:41 +01:00
" ], "
" \" schemas \" :{ "
2026-01-23 23:08:41 +01:00
" \" 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 \" } "
" } "
" }, "
2025-12-22 19:37:41 +01:00
" \" pel_run_request \" :{ "
" \" type \" : \" object \" , "
" \" required \" :[ \" program_ref \" , \" input_refs \" ], "
" \" properties \" :{ "
2025-12-22 21:03:00 +01:00
" \" 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 \" }, "
2025-12-23 09:14:58 +01:00
" \" 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 \" }, "
2026-01-21 19:51:26 +01:00
" \" 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 \" }, "
2025-12-23 09:14:58 +01:00
" \" started_at \" :{ \" type \" : \" integer \" }, "
" \" completed_at \" :{ \" type \" : \" integer \" } "
" } "
" } "
2025-12-22 19:37:41 +01:00
" } "
" }, "
" \" 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 \" }, "
2025-12-23 09:14:58 +01:00
" \" receipt_ref \" :{ \" type \" : \" string \" , \" description \" : \" hex ref \" }, "
2025-12-22 19:37:41 +01:00
" \" output_refs \" :{ \" type \" : \" array \" , \" items \" :{ \" type \" : \" string \" , \" description \" : \" hex ref \" }}, "
" \" status \" :{ \" type \" : \" string \" } "
" } "
2025-12-22 20:14:47 +01:00
" }, "
2026-01-21 19:51:26 +01:00
" \" 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 \" } "
" } "
" }, "
2025-12-23 09:14:58 +01:00
" \" 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 \" } "
" }} "
" } "
" } "
" } "
" } "
" }, "
2025-12-22 20:14:47 +01:00
" \" pel_program_author_request \" :{ "
" \" type \" : \" object \" , "
" \" required \" :[ \" nodes \" , \" roots \" ], "
" \" properties \" :{ "
" \" nodes \" :{ \" type \" : \" array \" }, "
" \" roots \" :{ \" type \" : \" array \" } "
" } "
2025-12-22 19:37:41 +01:00
" } "
" } "
" } \n " ;
2025-12-22 15:23:54 +01:00
static volatile sig_atomic_t amduatd_should_exit = 0 ;
static void amduatd_on_signal ( int signo ) {
( void ) signo ;
amduatd_should_exit = 1 ;
}
static bool amduatd_write_all ( int fd , const uint8_t * buf , size_t len ) {
size_t off = 0 ;
while ( off < len ) {
ssize_t n = write ( fd , buf + off , len - off ) ;
if ( n < 0 ) {
if ( errno = = EINTR ) {
continue ;
}
return false ;
}
if ( n = = 0 ) {
return false ;
}
off + = ( size_t ) n ;
}
return true ;
}
2025-12-22 21:03:00 +01:00
static bool amduatd_read_exact ( int fd , uint8_t * buf , size_t len ) ;
static bool amduatd_read_file ( const char * path , uint8_t * * out , size_t * out_len ) {
uint8_t * buf = NULL ;
size_t cap = 0 ;
size_t len = 0 ;
FILE * f ;
if ( out ! = NULL ) {
* out = NULL ;
}
if ( out_len ! = NULL ) {
* out_len = 0 ;
}
if ( path = = NULL | | out = = NULL | | out_len = = NULL ) {
return false ;
}
f = fopen ( path , " rb " ) ;
if ( f = = NULL ) {
return false ;
}
for ( ; ; ) {
size_t n ;
if ( len = = cap ) {
size_t next_cap = cap ! = 0 ? cap * 2u : 4096u ;
uint8_t * next = ( uint8_t * ) realloc ( buf , next_cap ) ;
if ( next = = NULL ) {
free ( buf ) ;
fclose ( f ) ;
return false ;
}
buf = next ;
cap = next_cap ;
}
n = fread ( buf + len , 1 , cap - len , f ) ;
len + = n ;
if ( n = = 0 ) {
if ( ferror ( f ) ) {
free ( buf ) ;
fclose ( f ) ;
return false ;
}
break ;
}
}
fclose ( f ) ;
* out = buf ;
* out_len = len ;
return true ;
}
static bool amduatd_read_urandom ( uint8_t * out , size_t len ) {
int fd ;
if ( out = = NULL | | len = = 0 ) {
return false ;
}
fd = open ( " /dev/urandom " , O_RDONLY ) ;
if ( fd < 0 ) {
return false ;
}
if ( ! amduatd_read_exact ( fd , out , len ) ) {
close ( fd ) ;
return false ;
}
close ( fd ) ;
return true ;
}
2026-01-23 23:08:41 +01:00
static bool amduatd_ct_memcmp ( const uint8_t * a ,
const uint8_t * b ,
size_t len ) {
uint8_t diff = 0u ;
if ( a = = NULL | | b = = NULL ) {
return false ;
}
for ( size_t i = 0u ; i < len ; + + i ) {
diff | = ( uint8_t ) ( a [ i ] ^ b [ i ] ) ;
}
return diff = = 0u ;
}
static bool amduatd_sha256_digest ( amduat_octets_t input , uint8_t out [ 32 ] ) {
return amduat_hash_asl1_digest ( AMDUAT_HASH_ASL1_ID_SHA256 ,
input ,
out ,
32u ) ;
}
static bool amduatd_hmac_sha256 ( const uint8_t * key ,
size_t key_len ,
const uint8_t * data ,
size_t data_len ,
uint8_t out [ 32 ] ) {
uint8_t key_block [ 64 ] ;
uint8_t ipad [ 64 ] ;
uint8_t opad [ 64 ] ;
uint8_t inner [ 32 ] ;
size_t i ;
amduat_hash_asl1_stream_t stream ;
if ( key = = NULL | | data = = NULL | | out = = NULL ) {
return false ;
}
memset ( key_block , 0 , sizeof ( key_block ) ) ;
if ( key_len > sizeof ( key_block ) ) {
if ( ! amduatd_sha256_digest ( amduat_octets ( key , key_len ) , key_block ) ) {
return false ;
}
} else if ( key_len ! = 0u ) {
memcpy ( key_block , key , key_len ) ;
}
for ( i = 0u ; i < sizeof ( key_block ) ; + + i ) {
ipad [ i ] = ( uint8_t ) ( key_block [ i ] ^ 0x36u ) ;
opad [ i ] = ( uint8_t ) ( key_block [ i ] ^ 0x5cu ) ;
}
if ( ! amduat_hash_asl1_stream_init ( AMDUAT_HASH_ASL1_ID_SHA256 , & stream ) ) {
return false ;
}
if ( ! amduat_hash_asl1_stream_update ( & stream ,
amduat_octets ( ipad , sizeof ( ipad ) ) ) | |
! amduat_hash_asl1_stream_update ( & stream ,
amduat_octets ( data , data_len ) ) | |
! amduat_hash_asl1_stream_final ( & stream , inner , sizeof ( inner ) ) ) {
amduat_hash_asl1_stream_destroy ( & stream ) ;
return false ;
}
amduat_hash_asl1_stream_destroy ( & stream ) ;
if ( ! amduat_hash_asl1_stream_init ( AMDUAT_HASH_ASL1_ID_SHA256 , & stream ) ) {
return false ;
}
if ( ! amduat_hash_asl1_stream_update ( & stream ,
amduat_octets ( opad , sizeof ( opad ) ) ) | |
! amduat_hash_asl1_stream_update ( & stream ,
amduat_octets ( inner , sizeof ( inner ) ) ) | |
! amduat_hash_asl1_stream_final ( & stream , out , 32u ) ) {
amduat_hash_asl1_stream_destroy ( & stream ) ;
return false ;
}
amduat_hash_asl1_stream_destroy ( & stream ) ;
return true ;
}
static int amduatd_b64url_value ( char c ) {
if ( c > = ' A ' & & c < = ' Z ' ) {
return c - ' A ' ;
}
if ( c > = ' a ' & & c < = ' z ' ) {
return c - ' a ' + 26 ;
}
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
return c - ' 0 ' + 52 ;
}
if ( c = = ' - ' ) {
return 62 ;
}
if ( c = = ' _ ' ) {
return 63 ;
}
return - 1 ;
}
static bool amduatd_b64url_encode ( const uint8_t * data ,
size_t len ,
char * * out ) {
static const char k_table [ ] =
" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_ " ;
size_t out_len ;
size_t i ;
size_t o = 0 ;
char * buf ;
if ( out ! = NULL ) {
* out = NULL ;
}
if ( data = = NULL | | out = = NULL ) {
return false ;
}
out_len = ( len / 3u ) * 4u ;
if ( len % 3u = = 1u ) {
out_len + = 2u ;
} else if ( len % 3u = = 2u ) {
out_len + = 3u ;
}
if ( out_len > SIZE_MAX - 1u ) {
return false ;
}
buf = ( char * ) malloc ( out_len + 1u ) ;
if ( buf = = NULL ) {
return false ;
}
for ( i = 0u ; i + 2u < len ; i + = 3u ) {
uint32_t v = ( ( uint32_t ) data [ i ] < < 16 ) |
( ( uint32_t ) data [ i + 1u ] < < 8 ) |
( uint32_t ) data [ i + 2u ] ;
buf [ o + + ] = k_table [ ( v > > 18 ) & 0x3fu ] ;
buf [ o + + ] = k_table [ ( v > > 12 ) & 0x3fu ] ;
buf [ o + + ] = k_table [ ( v > > 6 ) & 0x3fu ] ;
buf [ o + + ] = k_table [ v & 0x3fu ] ;
}
if ( len % 3u = = 1u ) {
uint32_t v = ( uint32_t ) data [ i ] < < 16 ;
buf [ o + + ] = k_table [ ( v > > 18 ) & 0x3fu ] ;
buf [ o + + ] = k_table [ ( v > > 12 ) & 0x3fu ] ;
} else if ( len % 3u = = 2u ) {
uint32_t v = ( ( uint32_t ) data [ i ] < < 16 ) |
( ( uint32_t ) data [ i + 1u ] < < 8 ) ;
buf [ o + + ] = k_table [ ( v > > 18 ) & 0x3fu ] ;
buf [ o + + ] = k_table [ ( v > > 12 ) & 0x3fu ] ;
buf [ o + + ] = k_table [ ( v > > 6 ) & 0x3fu ] ;
}
buf [ o ] = ' \0 ' ;
* out = buf ;
return true ;
}
static bool amduatd_b64url_decode ( const char * text ,
uint8_t * * out ,
size_t * out_len ) {
size_t len ;
size_t clean_len ;
size_t i = 0u ;
size_t o = 0u ;
uint8_t * buf ;
if ( out ! = NULL ) {
* out = NULL ;
}
if ( out_len ! = NULL ) {
* out_len = 0 ;
}
if ( text = = NULL | | out = = NULL | | out_len = = NULL ) {
return false ;
}
len = strlen ( text ) ;
clean_len = len ;
while ( clean_len > 0u & & text [ clean_len - 1u ] = = ' = ' ) {
clean_len - - ;
}
if ( clean_len % 4u = = 1u ) {
return false ;
}
* out_len = ( clean_len / 4u ) * 3u ;
if ( clean_len % 4u = = 2u ) {
* out_len + = 1u ;
} else if ( clean_len % 4u = = 3u ) {
* out_len + = 2u ;
}
if ( * out_len > SIZE_MAX ) {
return false ;
}
buf = ( uint8_t * ) malloc ( * out_len ) ;
if ( buf = = NULL ) {
return false ;
}
while ( i + 4u < = clean_len ) {
int v0 = amduatd_b64url_value ( text [ i ] ) ;
int v1 = amduatd_b64url_value ( text [ i + 1u ] ) ;
int v2 = amduatd_b64url_value ( text [ i + 2u ] ) ;
int v3 = amduatd_b64url_value ( text [ i + 3u ] ) ;
uint32_t v ;
if ( v0 < 0 | | v1 < 0 | | v2 < 0 | | v3 < 0 ) {
free ( buf ) ;
return false ;
}
v = ( ( uint32_t ) v0 < < 18 ) | ( ( uint32_t ) v1 < < 12 ) |
( ( uint32_t ) v2 < < 6 ) | ( uint32_t ) v3 ;
buf [ o + + ] = ( uint8_t ) ( ( v > > 16 ) & 0xffu ) ;
buf [ o + + ] = ( uint8_t ) ( ( v > > 8 ) & 0xffu ) ;
buf [ o + + ] = ( uint8_t ) ( v & 0xffu ) ;
i + = 4u ;
}
if ( clean_len % 4u = = 2u ) {
int v0 = amduatd_b64url_value ( text [ i ] ) ;
int v1 = amduatd_b64url_value ( text [ i + 1u ] ) ;
uint32_t v ;
if ( v0 < 0 | | v1 < 0 ) {
free ( buf ) ;
return false ;
}
v = ( ( uint32_t ) v0 < < 18 ) | ( ( uint32_t ) v1 < < 12 ) ;
buf [ o + + ] = ( uint8_t ) ( ( v > > 16 ) & 0xffu ) ;
} else if ( clean_len % 4u = = 3u ) {
int v0 = amduatd_b64url_value ( text [ i ] ) ;
int v1 = amduatd_b64url_value ( text [ i + 1u ] ) ;
int v2 = amduatd_b64url_value ( text [ i + 2u ] ) ;
uint32_t v ;
if ( v0 < 0 | | v1 < 0 | | v2 < 0 ) {
free ( buf ) ;
return false ;
}
v = ( ( uint32_t ) v0 < < 18 ) | ( ( uint32_t ) v1 < < 12 ) | ( ( uint32_t ) v2 < < 6 ) ;
buf [ o + + ] = ( uint8_t ) ( ( v > > 16 ) & 0xffu ) ;
buf [ o + + ] = ( uint8_t ) ( ( v > > 8 ) & 0xffu ) ;
}
if ( o ! = * out_len ) {
free ( buf ) ;
return false ;
}
* out = buf ;
return true ;
}
2025-12-22 21:03:00 +01:00
static bool amduatd_name_valid ( const char * name ) {
size_t i ;
size_t len ;
if ( name = = NULL ) {
return false ;
}
len = strlen ( name ) ;
if ( len = = 0 | | len > 64 ) {
return false ;
}
for ( i = 0 ; i < len ; + + i ) {
unsigned char c = ( unsigned char ) name [ i ] ;
if ( ( c > = ' a ' & & c < = ' z ' ) | | ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' _ ' | |
c = = ' - ' | | c = = ' . ' | | c = = ' / ' ) {
continue ;
}
return false ;
}
return true ;
}
2026-01-23 22:28:56 +01:00
static bool amduatd_strdup_cstr ( const char * s , char * * out ) {
size_t len ;
char * buf ;
if ( out = = NULL ) {
return false ;
}
* out = NULL ;
if ( s = = NULL ) {
return false ;
}
len = strlen ( s ) ;
if ( len > SIZE_MAX - 1u ) {
return false ;
}
buf = ( char * ) malloc ( len + 1u ) ;
if ( buf = = NULL ) {
return false ;
}
if ( len ! = 0 ) {
memcpy ( buf , s , len ) ;
}
buf [ len ] = ' \0 ' ;
* out = buf ;
return true ;
}
static bool amduatd_space_id_valid ( const char * space_id ) {
if ( space_id = = NULL | | space_id [ 0 ] = = ' \0 ' ) {
return false ;
}
if ( strchr ( space_id , ' / ' ) ! = NULL ) {
return false ;
}
return amduat_asl_pointer_name_is_valid ( space_id ) ;
}
static void amduatd_log_scoped_name ( const amduatd_cfg_t * cfg ,
const char * user_name ,
const char * scoped_name ) {
if ( cfg = = NULL | | ! cfg - > space_enabled ) {
return ;
}
if ( user_name = = NULL | | scoped_name = = NULL ) {
return ;
}
amduat_log ( AMDUAT_LOG_DEBUG , " %s -> %s " , user_name , scoped_name ) ;
}
static bool amduatd_scope_name ( const amduatd_cfg_t * cfg ,
const char * user_name ,
amduat_octets_t * out_scoped_name ) {
char * buf = NULL ;
size_t user_len = 0 ;
size_t space_len = 0 ;
size_t total_len = 0 ;
size_t offset = 0 ;
if ( out_scoped_name ! = NULL ) {
* out_scoped_name = amduat_octets ( NULL , 0u ) ;
}
if ( user_name = = NULL | | out_scoped_name = = NULL ) {
return false ;
}
if ( cfg = = NULL | | ! cfg - > space_enabled | | cfg - > space_id = = NULL ) {
if ( ! amduatd_name_valid ( user_name ) ) {
return false ;
}
if ( ! amduatd_strdup_cstr ( user_name , & buf ) ) {
return false ;
}
* out_scoped_name = amduat_octets ( ( const uint8_t * ) buf , strlen ( buf ) ) ;
return true ;
}
if ( strncmp ( user_name , " space/ " , 6 ) = = 0 ) {
return false ;
}
if ( ! amduat_asl_pointer_name_is_valid ( user_name ) ) {
return false ;
}
if ( ! amduatd_space_id_valid ( cfg - > space_id ) ) {
return false ;
}
user_len = strlen ( user_name ) ;
space_len = strlen ( cfg - > space_id ) ;
if ( space_len > SIZE_MAX - 7u | |
user_len > SIZE_MAX - ( 7u + space_len ) ) {
return false ;
}
total_len = 6u + space_len + 1u + user_len ;
buf = ( char * ) malloc ( total_len + 1u ) ;
if ( buf = = NULL ) {
return false ;
}
memcpy ( buf + offset , " space/ " , 6u ) ;
offset + = 6u ;
memcpy ( buf + offset , cfg - > space_id , space_len ) ;
offset + = space_len ;
buf [ offset + + ] = ' / ' ;
if ( user_len ! = 0 ) {
memcpy ( buf + offset , user_name , user_len ) ;
offset + = user_len ;
}
buf [ offset ] = ' \0 ' ;
if ( ! amduat_asl_pointer_name_is_valid ( buf ) ) {
free ( buf ) ;
return false ;
}
* out_scoped_name = amduat_octets ( ( const uint8_t * ) buf , total_len ) ;
return true ;
}
static bool amduatd_scope_name_cstr ( const amduatd_cfg_t * cfg ,
const char * user_name ,
char * * out_scoped_name ) {
amduat_octets_t scoped ;
if ( out_scoped_name ! = NULL ) {
* out_scoped_name = NULL ;
}
if ( out_scoped_name = = NULL ) {
return false ;
}
if ( ! amduatd_scope_name ( cfg , user_name , & scoped ) ) {
return false ;
}
* out_scoped_name = ( char * ) scoped . data ;
return * out_scoped_name ! = NULL ;
}
static bool amduatd_unscoped_name ( const amduatd_cfg_t * cfg ,
const char * scoped_name ,
char * out ,
size_t cap ) {
const char * suffix = NULL ;
size_t suffix_len = 0 ;
if ( out ! = NULL & & cap ! = 0 ) {
out [ 0 ] = ' \0 ' ;
}
if ( out = = NULL | | cap = = 0 | | scoped_name = = NULL ) {
return false ;
}
if ( cfg = = NULL | | ! cfg - > space_enabled | | cfg - > space_id = = NULL ) {
size_t len = strlen ( scoped_name ) ;
if ( ! amduatd_name_valid ( scoped_name ) ) {
return false ;
}
if ( len > = cap ) {
return false ;
}
memcpy ( out , scoped_name , len ) ;
out [ len ] = ' \0 ' ;
return true ;
}
if ( ! amduatd_space_id_valid ( cfg - > space_id ) ) {
return false ;
}
{
size_t space_len = strlen ( cfg - > space_id ) ;
size_t prefix_len = 6u + space_len + 1u ;
if ( strncmp ( scoped_name , " space/ " , 6u ) ! = 0 ) {
return false ;
}
if ( strncmp ( scoped_name + 6u , cfg - > space_id , space_len ) ! = 0 ) {
return false ;
}
if ( scoped_name [ 6u + space_len ] ! = ' / ' ) {
return false ;
}
suffix = scoped_name + prefix_len ;
}
if ( suffix = = NULL | | suffix [ 0 ] = = ' \0 ' ) {
return false ;
}
if ( ! amduat_asl_pointer_name_is_valid ( suffix ) ) {
return false ;
}
suffix_len = strlen ( suffix ) ;
if ( suffix_len > = cap ) {
return false ;
}
memcpy ( out , suffix , suffix_len ) ;
out [ suffix_len ] = ' \0 ' ;
return true ;
}
2026-01-23 23:08:41 +01:00
typedef struct {
uint8_t kind ;
char * space ;
size_t space_len ;
uint64_t expiry_unix ;
amduat_reference_t artifact_ref ;
char * pointer_name ;
amduat_reference_t program_ref ;
amduat_reference_t * input_refs ;
size_t input_refs_len ;
bool has_params_ref ;
amduat_reference_t params_ref ;
} amduatd_cap_token_t ;
2025-12-22 21:03:00 +01:00
2026-01-23 23:08:41 +01:00
static void amduatd_cap_token_free ( amduatd_cap_token_t * token ) {
if ( token = = NULL ) {
return ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 23:08:41 +01:00
free ( token - > space ) ;
token - > space = NULL ;
token - > space_len = 0u ;
amduat_reference_free ( & token - > artifact_ref ) ;
free ( token - > pointer_name ) ;
token - > pointer_name = NULL ;
amduat_reference_free ( & token - > program_ref ) ;
if ( token - > input_refs ! = NULL ) {
for ( size_t i = 0u ; i < token - > input_refs_len ; + + i ) {
amduat_reference_free ( & token - > input_refs [ i ] ) ;
}
free ( token - > input_refs ) ;
}
token - > input_refs = NULL ;
token - > input_refs_len = 0u ;
if ( token - > has_params_ref ) {
amduat_reference_free ( & token - > params_ref ) ;
}
token - > has_params_ref = false ;
memset ( token , 0 , sizeof ( * token ) ) ;
}
static bool amduatd_pointer_name_scoped_for_space ( const amduatd_cfg_t * dcfg ,
const char * name ) {
size_t space_len ;
if ( dcfg = = NULL | | name = = NULL ) {
return false ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 23:08:41 +01:00
if ( ! amduat_asl_pointer_name_is_valid ( name ) ) {
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 23:08:41 +01:00
if ( ! dcfg - > space_enabled | | dcfg - > space_id = = NULL ) {
return true ;
}
space_len = strlen ( dcfg - > space_id ) ;
if ( strncmp ( name , " space/ " , 6u ) ! = 0 ) {
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 23:08:41 +01:00
if ( strncmp ( name + 6u , dcfg - > space_id , space_len ) ! = 0 ) {
return false ;
}
if ( name [ 6u + space_len ] ! = ' / ' ) {
2025-12-22 21:03:00 +01:00
return false ;
}
return true ;
}
2026-01-23 23:08:41 +01:00
static const char * amduatd_cap_kind_name ( uint8_t kind ) {
switch ( kind ) {
case AMDUATD_CAP_KIND_ARTIFACT_REF :
return " artifact_ref " ;
case AMDUATD_CAP_KIND_POINTER_NAME :
return " pointer_name " ;
case AMDUATD_CAP_KIND_PEL_VIEW :
return " pel_view " ;
default :
return " unknown " ;
}
}
2025-12-22 21:03:00 +01:00
2026-01-23 23:08:41 +01:00
static void amduatd_cap_token_hash_prefix ( const uint8_t * bytes ,
size_t len ,
char out [ 9 ] ) {
uint8_t digest [ 32 ] ;
if ( out = = NULL ) {
return ;
}
if ( bytes = = NULL | | len = = 0u | |
! amduatd_sha256_digest ( amduat_octets ( bytes , len ) , digest ) ) {
strncpy ( out , " unknown " , 8u ) ;
out [ 8 ] = ' \0 ' ;
return ;
}
if ( ! amduat_hex_encode_lower ( digest , 4u , out , 9u ) ) {
strncpy ( out , " unknown " , 8u ) ;
out [ 8 ] = ' \0 ' ;
return ;
}
out [ 8 ] = ' \0 ' ;
}
static bool amduatd_cap_init ( const char * root_path , amduatd_cap_state_t * cap ) {
char secrets_path [ 1024 ] ;
char key_path [ 1060 ] ;
struct stat st ;
int fd = - 1 ;
uint8_t key_bytes [ 32 ] ;
if ( cap = = NULL ) {
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 23:08:41 +01:00
memset ( cap , 0 , sizeof ( * cap ) ) ;
if ( root_path = = NULL ) {
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 23:08:41 +01:00
{
int n = snprintf ( secrets_path , sizeof ( secrets_path ) ,
" %s/secrets " , root_path ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( secrets_path ) ) {
2026-01-23 22:28:56 +01:00
return false ;
}
2026-01-23 23:08:41 +01:00
}
if ( stat ( secrets_path , & st ) ! = 0 ) {
if ( errno ! = ENOENT ) {
return false ;
}
if ( mkdir ( secrets_path , 0700 ) ! = 0 ) {
return false ;
}
} else if ( ! S_ISDIR ( st . st_mode ) ) {
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 23:08:41 +01:00
if ( stat ( secrets_path , & st ) ! = 0 ) {
return false ;
2026-01-23 22:28:56 +01:00
}
2026-01-23 23:08:41 +01:00
if ( ( st . st_mode & 0077u ) ! = 0u ) {
amduat_log ( AMDUAT_LOG_WARN ,
" capabilities disabled: secrets dir permissions too open " ) ;
return false ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 20:55:41 +01:00
2026-01-23 23:08:41 +01:00
{
int n = snprintf ( key_path , sizeof ( key_path ) ,
" %s/secrets/cap_hmac_key " , root_path ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( key_path ) ) {
return false ;
}
}
if ( stat ( key_path , & st ) ! = 0 ) {
if ( errno ! = ENOENT ) {
return false ;
}
fd = open ( key_path , O_WRONLY | O_CREAT | O_EXCL , 0600 ) ;
if ( fd < 0 ) {
return false ;
}
if ( ! amduatd_read_urandom ( key_bytes , sizeof ( key_bytes ) ) ) {
close ( fd ) ;
return false ;
}
if ( ! amduatd_write_all ( fd , key_bytes , sizeof ( key_bytes ) ) ) {
close ( fd ) ;
return false ;
}
close ( fd ) ;
}
2026-01-23 20:55:41 +01:00
2026-01-23 23:08:41 +01:00
if ( stat ( key_path , & st ) ! = 0 ) {
2026-01-23 20:55:41 +01:00
return false ;
}
2026-01-23 23:08:41 +01:00
if ( ! S_ISREG ( st . st_mode ) | | ( st . st_mode & 0077u ) ! = 0u | |
st . st_size ! = ( off_t ) sizeof ( key_bytes ) ) {
amduat_log ( AMDUAT_LOG_WARN ,
" capabilities disabled: cap_hmac_key permissions invalid " ) ;
2026-01-23 20:55:41 +01:00
return false ;
}
2026-01-23 23:08:41 +01:00
fd = open ( key_path , O_RDONLY ) ;
if ( fd < 0 ) {
return false ;
2026-01-23 20:55:41 +01:00
}
2026-01-23 23:08:41 +01:00
if ( ! amduatd_read_exact ( fd , key_bytes , sizeof ( key_bytes ) ) ) {
close ( fd ) ;
return false ;
}
close ( fd ) ;
memcpy ( cap - > hmac_key , key_bytes , sizeof ( key_bytes ) ) ;
cap - > hmac_key_len = sizeof ( key_bytes ) ;
cap - > enabled = true ;
return true ;
}
static bool amduatd_cap_build_token ( const amduatd_cap_token_t * token ,
const uint8_t * key ,
size_t key_len ,
uint8_t * * out ,
size_t * out_len ) {
size_t total = 0u ;
size_t payload_len = 0u ;
size_t offset = 0u ;
size_t hmac_offset = 0u ;
uint8_t * buf = NULL ;
uint8_t hmac [ 32 ] ;
amduat_octets_t ref_bytes = amduat_octets ( NULL , 0u ) ;
amduat_octets_t program_bytes = amduat_octets ( NULL , 0u ) ;
amduat_octets_t * input_bytes = NULL ;
amduat_octets_t params_bytes = amduat_octets ( NULL , 0u ) ;
if ( out ! = NULL ) {
* out = NULL ;
}
if ( out_len ! = NULL ) {
* out_len = 0u ;
}
if ( token = = NULL | | out = = NULL | | out_len = = NULL | |
key = = NULL | | key_len = = 0u ) {
return false ;
}
if ( token - > kind = = AMDUATD_CAP_KIND_ARTIFACT_REF ) {
if ( ! amduat_enc_asl1_core_encode_reference_v1 ( token - > artifact_ref ,
& ref_bytes ) ) {
return false ;
}
payload_len = 4u + ref_bytes . len ;
} else if ( token - > kind = = AMDUATD_CAP_KIND_POINTER_NAME ) {
if ( token - > pointer_name = = NULL ) {
return false ;
}
payload_len = 4u + strlen ( token - > pointer_name ) ;
} else if ( token - > kind = = AMDUATD_CAP_KIND_PEL_VIEW ) {
size_t inputs_total = 0u ;
if ( ! amduat_enc_asl1_core_encode_reference_v1 ( token - > program_ref ,
& program_bytes ) ) {
return false ;
}
if ( token - > input_refs_len ! = 0u ) {
input_bytes = ( amduat_octets_t * ) calloc ( token - > input_refs_len ,
sizeof ( * input_bytes ) ) ;
if ( input_bytes = = NULL ) {
free ( ( void * ) program_bytes . data ) ;
return false ;
}
for ( size_t i = 0u ; i < token - > input_refs_len ; + + i ) {
if ( ! amduat_enc_asl1_core_encode_reference_v1 ( token - > input_refs [ i ] ,
& input_bytes [ i ] ) ) {
free ( ( void * ) program_bytes . data ) ;
for ( size_t j = 0u ; j < i ; + + j ) {
free ( ( void * ) input_bytes [ j ] . data ) ;
}
free ( input_bytes ) ;
return false ;
}
if ( ! amduatd_add_size ( & inputs_total , 4u + input_bytes [ i ] . len ) ) {
free ( ( void * ) program_bytes . data ) ;
for ( size_t j = 0u ; j < = i ; + + j ) {
free ( ( void * ) input_bytes [ j ] . data ) ;
}
free ( input_bytes ) ;
return false ;
}
}
}
if ( token - > has_params_ref ) {
if ( ! amduat_enc_asl1_core_encode_reference_v1 ( token - > params_ref ,
& params_bytes ) ) {
free ( ( void * ) program_bytes . data ) ;
if ( input_bytes ! = NULL ) {
for ( size_t i = 0u ; i < token - > input_refs_len ; + + i ) {
free ( ( void * ) input_bytes [ i ] . data ) ;
}
free ( input_bytes ) ;
}
return false ;
}
}
payload_len = 4u + program_bytes . len + 4u + inputs_total + 1u ;
if ( token - > has_params_ref ) {
payload_len + = 4u + params_bytes . len ;
}
} else {
return false ;
}
if ( ! amduatd_add_size ( & total , AMDUATD_CAP_MAGIC_LEN ) | |
! amduatd_add_size ( & total , 4u ) | |
! amduatd_add_size ( & total , 4u + token - > space_len ) | |
! amduatd_add_size ( & total , 8u ) | |
! amduatd_add_size ( & total , 1u ) | |
! amduatd_add_size ( & total , payload_len ) | |
! amduatd_add_size ( & total , 4u ) | |
! amduatd_add_size ( & total , sizeof ( hmac ) ) ) {
free ( ( void * ) ref_bytes . data ) ;
free ( ( void * ) program_bytes . data ) ;
if ( input_bytes ! = NULL ) {
for ( size_t i = 0u ; i < token - > input_refs_len ; + + i ) {
free ( ( void * ) input_bytes [ i ] . data ) ;
}
free ( input_bytes ) ;
}
free ( ( void * ) params_bytes . data ) ;
return false ;
}
buf = ( uint8_t * ) malloc ( total ) ;
if ( buf = = NULL ) {
free ( ( void * ) ref_bytes . data ) ;
free ( ( void * ) program_bytes . data ) ;
if ( input_bytes ! = NULL ) {
for ( size_t i = 0u ; i < token - > input_refs_len ; + + i ) {
free ( ( void * ) input_bytes [ i ] . data ) ;
}
free ( input_bytes ) ;
}
free ( ( void * ) params_bytes . data ) ;
return false ;
}
memcpy ( buf + offset , k_amduatd_cap_magic , AMDUATD_CAP_MAGIC_LEN ) ;
offset + = AMDUATD_CAP_MAGIC_LEN ;
amduatd_store_u32_le ( buf + offset , AMDUATD_CAP_VERSION ) ;
offset + = 4u ;
amduatd_store_u32_le ( buf + offset , ( uint32_t ) token - > space_len ) ;
offset + = 4u ;
if ( token - > space_len ! = 0u & & token - > space ! = NULL ) {
memcpy ( buf + offset , token - > space , token - > space_len ) ;
offset + = token - > space_len ;
}
amduatd_store_u64_le ( buf + offset , token - > expiry_unix ) ;
offset + = 8u ;
buf [ offset + + ] = token - > kind ;
if ( token - > kind = = AMDUATD_CAP_KIND_ARTIFACT_REF ) {
amduatd_store_u32_le ( buf + offset , ( uint32_t ) ref_bytes . len ) ;
offset + = 4u ;
if ( ref_bytes . len ! = 0u ) {
memcpy ( buf + offset , ref_bytes . data , ref_bytes . len ) ;
offset + = ref_bytes . len ;
}
} else if ( token - > kind = = AMDUATD_CAP_KIND_POINTER_NAME ) {
uint32_t name_len = ( uint32_t ) strlen ( token - > pointer_name ) ;
amduatd_store_u32_le ( buf + offset , name_len ) ;
offset + = 4u ;
if ( name_len ! = 0u ) {
memcpy ( buf + offset , token - > pointer_name , name_len ) ;
offset + = name_len ;
}
} else if ( token - > kind = = AMDUATD_CAP_KIND_PEL_VIEW ) {
amduatd_store_u32_le ( buf + offset , ( uint32_t ) program_bytes . len ) ;
offset + = 4u ;
if ( program_bytes . len ! = 0u ) {
memcpy ( buf + offset , program_bytes . data , program_bytes . len ) ;
offset + = program_bytes . len ;
}
amduatd_store_u32_le ( buf + offset , ( uint32_t ) token - > input_refs_len ) ;
offset + = 4u ;
if ( input_bytes ! = NULL ) {
for ( size_t i = 0u ; i < token - > input_refs_len ; + + i ) {
amduatd_store_u32_le ( buf + offset , ( uint32_t ) input_bytes [ i ] . len ) ;
offset + = 4u ;
if ( input_bytes [ i ] . len ! = 0u ) {
memcpy ( buf + offset , input_bytes [ i ] . data , input_bytes [ i ] . len ) ;
offset + = input_bytes [ i ] . len ;
}
}
}
buf [ offset + + ] = token - > has_params_ref ? 1u : 0u ;
if ( token - > has_params_ref ) {
amduatd_store_u32_le ( buf + offset , ( uint32_t ) params_bytes . len ) ;
offset + = 4u ;
if ( params_bytes . len ! = 0u ) {
memcpy ( buf + offset , params_bytes . data , params_bytes . len ) ;
offset + = params_bytes . len ;
}
}
}
hmac_offset = offset ;
amduatd_store_u32_le ( buf + offset , ( uint32_t ) sizeof ( hmac ) ) ;
offset + = 4u ;
if ( ! amduatd_hmac_sha256 ( key , key_len , buf , hmac_offset , hmac ) ) {
free ( buf ) ;
free ( ( void * ) ref_bytes . data ) ;
free ( ( void * ) program_bytes . data ) ;
if ( input_bytes ! = NULL ) {
for ( size_t i = 0u ; i < token - > input_refs_len ; + + i ) {
free ( ( void * ) input_bytes [ i ] . data ) ;
}
free ( input_bytes ) ;
}
free ( ( void * ) params_bytes . data ) ;
return false ;
}
memcpy ( buf + offset , hmac , sizeof ( hmac ) ) ;
offset + = sizeof ( hmac ) ;
free ( ( void * ) ref_bytes . data ) ;
free ( ( void * ) program_bytes . data ) ;
if ( input_bytes ! = NULL ) {
for ( size_t i = 0u ; i < token - > input_refs_len ; + + i ) {
free ( ( void * ) input_bytes [ i ] . data ) ;
}
free ( input_bytes ) ;
}
free ( ( void * ) params_bytes . data ) ;
if ( offset ! = total ) {
free ( buf ) ;
return false ;
}
* out = buf ;
* out_len = total ;
return true ;
}
static bool amduatd_cap_decode_token ( const uint8_t * bytes ,
size_t len ,
amduatd_cap_token_t * out_token ,
size_t * out_hmac_offset ,
uint32_t * out_hmac_len ,
const uint8_t * * out_hmac ) {
size_t offset = 0u ;
uint32_t version = 0u ;
uint32_t space_len = 0u ;
uint32_t ref_len = 0u ;
uint32_t input_count = 0u ;
uint32_t hmac_len = 0u ;
uint64_t expiry = 0u ;
uint8_t kind = 0u ;
amduatd_cap_token_t token ;
const uint8_t * hmac_ptr = NULL ;
if ( out_token = = NULL | | out_hmac_offset = = NULL | |
out_hmac_len = = NULL | | out_hmac = = NULL ) {
return false ;
}
memset ( & token , 0 , sizeof ( token ) ) ;
if ( len < AMDUATD_CAP_MAGIC_LEN + 4u + 4u + 8u + 1u + 4u + 32u ) {
return false ;
}
if ( memcmp ( bytes , k_amduatd_cap_magic , AMDUATD_CAP_MAGIC_LEN ) ! = 0 ) {
return false ;
}
offset + = AMDUATD_CAP_MAGIC_LEN ;
if ( ! amduatd_read_u32_le ( bytes , len , & offset , & version ) | |
version ! = AMDUATD_CAP_VERSION ) {
return false ;
}
if ( ! amduatd_read_u32_le ( bytes , len , & offset , & space_len ) ) {
return false ;
}
if ( len - offset < space_len ) {
return false ;
}
if ( space_len ! = 0u ) {
token . space = ( char * ) malloc ( space_len + 1u ) ;
if ( token . space = = NULL ) {
return false ;
}
memcpy ( token . space , bytes + offset , space_len ) ;
token . space [ space_len ] = ' \0 ' ;
token . space_len = space_len ;
}
offset + = space_len ;
if ( ! amduatd_read_u64_le ( bytes , len , & offset , & expiry ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
token . expiry_unix = expiry ;
if ( offset > = len ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
kind = bytes [ offset + + ] ;
token . kind = kind ;
if ( kind = = AMDUATD_CAP_KIND_ARTIFACT_REF ) {
if ( ! amduatd_read_u32_le ( bytes , len , & offset , & ref_len ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
if ( len - offset < ref_len ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
if ( ! amduat_enc_asl1_core_decode_reference_v1 (
amduat_octets ( bytes + offset , ref_len ) , & token . artifact_ref ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
offset + = ref_len ;
} else if ( kind = = AMDUATD_CAP_KIND_POINTER_NAME ) {
uint32_t name_len = 0u ;
if ( ! amduatd_read_u32_le ( bytes , len , & offset , & name_len ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
if ( len - offset < name_len ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
token . pointer_name = ( char * ) malloc ( name_len + 1u ) ;
if ( token . pointer_name = = NULL ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
memcpy ( token . pointer_name , bytes + offset , name_len ) ;
token . pointer_name [ name_len ] = ' \0 ' ;
offset + = name_len ;
if ( ! amduat_asl_pointer_name_is_valid ( token . pointer_name ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
} else if ( kind = = AMDUATD_CAP_KIND_PEL_VIEW ) {
if ( ! amduatd_read_u32_le ( bytes , len , & offset , & ref_len ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
if ( len - offset < ref_len ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
if ( ! amduat_enc_asl1_core_decode_reference_v1 (
amduat_octets ( bytes + offset , ref_len ) , & token . program_ref ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
offset + = ref_len ;
if ( ! amduatd_read_u32_le ( bytes , len , & offset , & input_count ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
if ( input_count ! = 0u ) {
token . input_refs = ( amduat_reference_t * ) calloc (
input_count , sizeof ( * token . input_refs ) ) ;
if ( token . input_refs = = NULL ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
token . input_refs_len = input_count ;
for ( uint32_t i = 0u ; i < input_count ; + + i ) {
if ( ! amduatd_read_u32_le ( bytes , len , & offset , & ref_len ) | |
len - offset < ref_len | |
! amduat_enc_asl1_core_decode_reference_v1 (
amduat_octets ( bytes + offset , ref_len ) ,
& token . input_refs [ i ] ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
offset + = ref_len ;
}
}
if ( offset > = len ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
token . has_params_ref = bytes [ offset + + ] ! = 0u ;
if ( token . has_params_ref ) {
if ( ! amduatd_read_u32_le ( bytes , len , & offset , & ref_len ) | |
len - offset < ref_len | |
! amduat_enc_asl1_core_decode_reference_v1 (
amduat_octets ( bytes + offset , ref_len ) ,
& token . params_ref ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
offset + = ref_len ;
}
} else {
amduatd_cap_token_free ( & token ) ;
return false ;
}
* out_hmac_offset = offset ;
if ( ! amduatd_read_u32_le ( bytes , len , & offset , & hmac_len ) ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
if ( len - offset < hmac_len ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
hmac_ptr = bytes + offset ;
offset + = hmac_len ;
if ( offset ! = len ) {
amduatd_cap_token_free ( & token ) ;
return false ;
}
* out_hmac_len = hmac_len ;
* out_hmac = hmac_ptr ;
* out_token = token ;
return true ;
}
static bool amduatd_cap_verify_token ( const amduatd_cap_state_t * cap ,
const amduatd_cfg_t * dcfg ,
const uint8_t * bytes ,
size_t len ,
amduatd_cap_token_t * out_token ,
const char * * out_reason ) {
amduatd_cap_token_t token ;
size_t hmac_offset = 0u ;
uint32_t hmac_len = 0u ;
const uint8_t * hmac = NULL ;
uint8_t expected [ 32 ] ;
time_t now ;
if ( out_reason ! = NULL ) {
* out_reason = " invalid " ;
}
if ( cap = = NULL | | dcfg = = NULL | | bytes = = NULL | | out_token = = NULL ) {
return false ;
}
if ( ! amduatd_cap_decode_token ( bytes , len , & token ,
& hmac_offset , & hmac_len , & hmac ) ) {
return false ;
}
if ( hmac_len ! = sizeof ( expected ) ) {
* out_token = token ;
return false ;
}
if ( ! amduatd_hmac_sha256 ( cap - > hmac_key ,
cap - > hmac_key_len ,
bytes ,
hmac_offset ,
expected ) | |
! amduatd_ct_memcmp ( expected , hmac , sizeof ( expected ) ) ) {
if ( out_reason ! = NULL ) {
* out_reason = " bad-mac " ;
}
* out_token = token ;
return false ;
}
now = time ( NULL ) ;
if ( now < 0 | | token . expiry_unix < ( uint64_t ) now ) {
if ( out_reason ! = NULL ) {
* out_reason = " expired " ;
}
* out_token = token ;
return false ;
}
if ( dcfg - > space_enabled ) {
if ( token . space_len = = 0u | | token . space = = NULL | |
strcmp ( token . space , dcfg - > space_id ) ! = 0 ) {
if ( out_reason ! = NULL ) {
* out_reason = " wrong-space " ;
}
* out_token = token ;
return false ;
}
} else {
if ( token . space_len ! = 0u ) {
if ( out_reason ! = NULL ) {
* out_reason = " wrong-space " ;
}
* out_token = token ;
return false ;
}
}
if ( token . kind = = AMDUATD_CAP_KIND_POINTER_NAME & &
! amduatd_pointer_name_scoped_for_space ( dcfg , token . pointer_name ) ) {
if ( out_reason ! = NULL ) {
* out_reason = " wrong-space " ;
}
* out_token = token ;
return false ;
}
* out_token = token ;
if ( out_reason ! = NULL ) {
* out_reason = " ok " ;
}
return true ;
}
static bool amduatd_build_prefixed_bytes ( const char * prefix ,
const char * text ,
uint8_t * * out ,
size_t * out_len ) {
size_t p_len ;
size_t t_len ;
size_t len ;
uint8_t * buf ;
if ( out ! = NULL ) {
* out = NULL ;
}
if ( out_len ! = NULL ) {
* out_len = 0 ;
}
if ( prefix = = NULL | | text = = NULL | | out = = NULL | | out_len = = NULL ) {
return false ;
}
p_len = strlen ( prefix ) ;
t_len = strlen ( text ) ;
if ( p_len > SIZE_MAX - 1u | | t_len > SIZE_MAX - ( p_len + 1u ) ) {
return false ;
}
len = p_len + 1u + t_len ;
buf = ( uint8_t * ) malloc ( len ) ;
if ( buf = = NULL ) {
return false ;
}
memcpy ( buf , prefix , p_len ) ;
buf [ p_len ] = 0 ;
memcpy ( buf + p_len + 1u , text , t_len ) ;
* out = buf ;
* out_len = len ;
return true ;
}
static bool amduatd_concepts_seed_relation ( amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
const amduatd_cfg_t * dcfg ,
const char * relation_name ,
amduat_reference_t * out_ref ) {
uint8_t * bytes = NULL ;
size_t bytes_len = 0 ;
amduat_artifact_t artifact ;
char * scoped_name = NULL ;
bool ok = false ;
if ( out_ref = = NULL ) {
return false ;
}
* out_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
if ( store = = NULL | | cfg = = NULL | | relation_name = = NULL ) {
return false ;
}
if ( dcfg ! = NULL & & dcfg - > space_enabled ) {
if ( ! amduatd_scope_name_cstr ( dcfg , relation_name , & scoped_name ) ) {
return false ;
}
} else if ( ! amduatd_strdup_cstr ( relation_name , & scoped_name ) ) {
return false ;
}
if ( ! amduatd_build_prefixed_bytes ( " AMDUATD/RELATION/1 " , scoped_name , & bytes ,
& bytes_len ) ) {
goto seed_cleanup ;
}
artifact = amduat_artifact ( amduat_octets ( bytes , bytes_len ) ) ;
if ( ! amduat_asl_ref_derive ( artifact ,
cfg - > config . encoding_profile_id ,
cfg - > config . hash_id ,
out_ref ,
NULL ) ) {
goto seed_cleanup ;
}
( void ) amduat_asl_store_put ( store , artifact , out_ref ) ;
ok = true ;
seed_cleanup :
free ( bytes ) ;
free ( scoped_name ) ;
return ok ;
}
static bool amduatd_concepts_ensure_alias ( amduatd_concepts_t * c ,
amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
const amduatd_cfg_t * dcfg ,
const char * alias_name ,
amduat_reference_t concept_ref ) {
amduat_reference_t existing ;
amduat_reference_t name_ref ;
amduat_reference_t edge_ref ;
char * scoped_name = NULL ;
bool ok = false ;
memset ( & existing , 0 , sizeof ( existing ) ) ;
memset ( & name_ref , 0 , sizeof ( name_ref ) ) ;
memset ( & edge_ref , 0 , sizeof ( edge_ref ) ) ;
if ( c = = NULL | | store = = NULL | | cfg = = NULL | | alias_name = = NULL ) {
return false ;
}
if ( ! amduatd_scope_name_cstr ( dcfg , alias_name , & scoped_name ) ) {
amduat_log ( AMDUAT_LOG_ERROR ,
" ensure alias: scope failed for %s " , alias_name ) ;
return false ;
}
if ( amduatd_concepts_lookup_alias ( store , cfg , c , scoped_name , & existing ) ) {
amduat_reference_free ( & existing ) ;
ok = true ;
goto alias_cleanup ;
}
if ( ! amduatd_concepts_put_name_artifact ( store , scoped_name , & name_ref ) ) {
amduat_log ( AMDUAT_LOG_ERROR ,
" ensure alias: name artifact failed for %s " , scoped_name ) ;
goto alias_cleanup ;
2026-01-23 20:55:41 +01:00
}
if ( ! amduatd_concepts_put_edge ( store ,
c ,
name_ref ,
concept_ref ,
c - > rel_aliases_ref ,
amduat_octets ( NULL , 0u ) ,
& edge_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR ,
" ensure alias: put edge failed for %s " , scoped_name ) ;
goto alias_cleanup ;
2026-01-23 20:55:41 +01:00
}
2026-01-23 22:28:56 +01:00
ok = true ;
alias_cleanup :
2026-01-23 20:55:41 +01:00
amduat_reference_free ( & name_ref ) ;
amduat_reference_free ( & edge_ref ) ;
2026-01-23 22:28:56 +01:00
free ( scoped_name ) ;
return ok ;
2026-01-23 20:55:41 +01:00
}
static bool amduatd_concepts_load_edges ( amduatd_concepts_t * c ,
amduat_asl_store_t * store ) {
amduat_asl_collection_view_t view ;
2026-01-23 22:28:56 +01:00
uint64_t from = 0u ;
2026-01-23 20:55:41 +01:00
bool ok = false ;
if ( c = = NULL | | store = = NULL ) {
return false ;
}
amduatd_edge_list_clear ( & c - > edges ) ;
2026-01-23 22:28:56 +01:00
while ( true ) {
memset ( & view , 0 , sizeof ( view ) ) ;
if ( ! amduatd_collection_view ( store ,
c - > root_path ,
c - > edge_collection_name ,
from ,
AMDUATD_EDGE_VIEW_BATCH ,
& view ) ) {
return false ;
2026-01-23 20:55:41 +01:00
}
2026-01-23 22:28:56 +01:00
for ( size_t i = 0u ; i < view . refs_len ; + + i ) {
amduat_reference_t record_ref = view . refs [ i ] ;
amduat_asl_record_t record ;
amduat_reference_t src_ref ;
amduat_reference_t dst_ref ;
char * rel = NULL ;
amduatd_edge_entry_t entry ;
amduat_asl_store_error_t err ;
bool schema_ok = false ;
memset ( & record , 0 , sizeof ( record ) ) ;
err = amduat_asl_record_store_get ( store , record_ref , & record ) ;
if ( err ! = AMDUAT_ASL_STORE_OK ) {
continue ;
}
if ( record . schema . len = = strlen ( AMDUATD_EDGE_SCHEMA ) & &
memcmp ( record . schema . data , AMDUATD_EDGE_SCHEMA ,
record . schema . len ) = = 0 ) {
schema_ok = true ;
}
if ( ! schema_ok | |
! amduatd_edge_payload_decode ( record . payload ,
& src_ref ,
& dst_ref ,
& rel ) ) {
amduat_asl_record_free ( & record ) ;
continue ;
}
2026-01-23 20:55:41 +01:00
amduat_asl_record_free ( & record ) ;
2026-01-23 22:28:56 +01:00
memset ( & entry , 0 , sizeof ( entry ) ) ;
if ( ! amduat_reference_clone ( record_ref , & entry . record_ref ) ) {
amduat_reference_free ( & src_ref ) ;
amduat_reference_free ( & dst_ref ) ;
free ( rel ) ;
goto load_cleanup ;
}
entry . src_ref = src_ref ;
entry . dst_ref = dst_ref ;
entry . rel = rel ;
if ( ! amduatd_edge_list_push ( & c - > edges , & entry ) ) {
amduatd_edge_entry_free ( & entry ) ;
goto load_cleanup ;
}
2026-01-23 20:55:41 +01:00
amduatd_edge_entry_free ( & entry ) ;
}
2026-01-23 22:28:56 +01:00
if ( view . refs_len = = 0u | | view . computed_up_to_offset < = from ) {
break ;
}
from = view . computed_up_to_offset ;
amduat_asl_collection_view_free ( & view ) ;
2026-01-23 20:55:41 +01:00
}
ok = true ;
load_cleanup :
amduat_asl_collection_view_free ( & view ) ;
return ok ;
}
static bool amduatd_concepts_append_edge_record ( amduatd_concepts_t * c ,
amduat_asl_store_t * store ,
amduat_reference_t src ,
amduat_reference_t dst ,
const char * rel ,
amduat_octets_t actor ,
amduat_reference_t * out_ref ) {
amduat_octets_t payload = amduat_octets ( NULL , 0u ) ;
amduat_reference_t record_ref ;
amduatd_edge_entry_t entry ;
uint64_t offset = 0u ;
if ( out_ref ! = NULL ) {
* out_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
}
if ( c = = NULL | | store = = NULL | | rel = = NULL ) {
return false ;
}
if ( ! amduatd_edge_payload_encode ( src , dst , rel , & payload ) ) {
return false ;
}
if ( amduat_asl_record_store_put ( store ,
amduat_octets ( AMDUATD_EDGE_SCHEMA ,
strlen ( AMDUATD_EDGE_SCHEMA ) ) ,
payload ,
& record_ref ) ! = AMDUAT_ASL_STORE_OK ) {
free ( ( void * ) payload . data ) ;
return false ;
}
free ( ( void * ) payload . data ) ;
2026-01-23 22:28:56 +01:00
{
amduat_asl_collection_error_t append_err =
amduat_asl_collection_append ( & c - > edge_collection ,
c - > edge_collection_name ,
record_ref ,
AMDUATD_EDGE_COLLECTION_KIND ,
actor ,
& offset ) ;
if ( append_err ! = AMDUAT_ASL_COLLECTION_OK ) {
if ( c - > edge_collection_name ! = NULL & &
! amduat_asl_pointer_name_is_valid ( c - > edge_collection_name ) ) {
amduat_log ( AMDUAT_LOG_ERROR , " edge collection name invalid: %s " ,
c - > edge_collection_name ) ;
}
if ( c - > edge_collection_name ! = NULL ) {
size_t name_len = strlen ( c - > edge_collection_name ) ;
size_t total_len = 11u + name_len + 4u + 1u ;
char * log_name = ( char * ) malloc ( total_len ) ;
if ( log_name ! = NULL ) {
size_t pos = 0u ;
memcpy ( log_name + pos , " collection/ " , 11u ) ;
pos + = 11u ;
memcpy ( log_name + pos , c - > edge_collection_name , name_len ) ;
pos + = name_len ;
memcpy ( log_name + pos , " /log " , 4u ) ;
pos + = 4u ;
log_name [ pos ] = ' \0 ' ;
if ( ! amduat_asl_pointer_name_is_valid ( log_name ) ) {
amduat_log ( AMDUAT_LOG_ERROR , " edge log name invalid: %s " , log_name ) ;
}
{
size_t head_len = 4u + pos + 5u + 1u ;
char * head_name = ( char * ) malloc ( head_len ) ;
if ( head_name ! = NULL ) {
size_t hpos = 0u ;
memcpy ( head_name + hpos , " log/ " , 4u ) ;
hpos + = 4u ;
memcpy ( head_name + hpos , log_name , pos ) ;
hpos + = pos ;
memcpy ( head_name + hpos , " /head " , 5u ) ;
hpos + = 5u ;
head_name [ hpos ] = ' \0 ' ;
if ( ! amduat_asl_pointer_name_is_valid ( head_name ) ) {
amduat_log ( AMDUAT_LOG_ERROR ,
" edge log head name invalid: %s " , head_name ) ;
} else {
bool head_exists = false ;
amduat_reference_t head_ref ;
memset ( & head_ref , 0 , sizeof ( head_ref ) ) ;
amduat_asl_pointer_error_t ptr_err =
amduat_asl_pointer_get ( & c - > edge_collection . pointer_store ,
head_name ,
& head_exists ,
& head_ref ) ;
if ( ptr_err ! = AMDUAT_ASL_POINTER_OK ) {
amduat_log ( AMDUAT_LOG_ERROR ,
" edge log head pointer get failed: %d " , ptr_err ) ;
}
amduat_reference_free ( & head_ref ) ;
}
free ( head_name ) ;
}
}
free ( log_name ) ;
}
}
amduat_log ( AMDUAT_LOG_ERROR , " edge append failed for %s (err=%d) " ,
c - > edge_collection_name ! = NULL ? c - > edge_collection_name
: " ? " ,
( int ) append_err ) ;
amduat_reference_free ( & record_ref ) ;
return false ;
}
2026-01-23 20:55:41 +01:00
}
memset ( & entry , 0 , sizeof ( entry ) ) ;
entry . record_ref = record_ref ;
entry . src_ref = src ;
entry . dst_ref = dst ;
entry . rel = ( char * ) rel ;
if ( ! amduatd_edge_list_push ( & c - > edges , & entry ) ) {
amduat_reference_free ( & record_ref ) ;
return false ;
}
if ( offset ! = 0u & & ( offset % 256u ) = = 0u ) {
amduat_reference_t snapshot_ref ;
bool swapped = false ;
memset ( & snapshot_ref , 0 , sizeof ( snapshot_ref ) ) ;
( void ) amduat_asl_collection_snapshot ( & c - > edge_collection ,
2026-01-23 22:28:56 +01:00
c - > edge_collection_name ,
2026-01-23 20:55:41 +01:00
offset ,
& snapshot_ref ,
& swapped ) ;
amduat_reference_free ( & snapshot_ref ) ;
}
if ( out_ref ! = NULL ) {
* out_ref = record_ref ;
} else {
amduat_reference_free ( & record_ref ) ;
}
return true ;
}
static bool amduatd_concepts_migrate_edges ( amduatd_concepts_t * c ,
amduat_asl_store_t * store ) {
uint8_t * file_bytes = NULL ;
size_t file_len = 0 ;
bool ok = true ;
if ( c = = NULL | | store = = NULL ) {
return false ;
}
if ( ! amduatd_read_file ( c - > edges_path , & file_bytes , & file_len ) ) {
return true ;
}
{
char * text = ( char * ) file_bytes ;
char * line = text ;
char * end = text + file_len ;
while ( line < end ) {
char * nl = memchr ( line , ' \n ' , ( size_t ) ( end - line ) ) ;
size_t n = nl ! = NULL ? ( size_t ) ( nl - line ) : ( size_t ) ( end - line ) ;
char * tmp ;
amduat_reference_t ref ;
bool ok_ref ;
while ( n ! = 0 & & ( line [ n - 1 ] = = ' \r ' | | line [ n - 1 ] = = ' ' | |
line [ n - 1 ] = = ' \t ' ) ) {
n - - ;
}
while ( n ! = 0 & & ( * line = = ' ' | | * line = = ' \t ' ) ) {
line + + ;
n - - ;
}
if ( n ! = 0 ) {
tmp = ( char * ) malloc ( n + 1u ) ;
if ( tmp = = NULL ) {
ok = false ;
break ;
}
memcpy ( tmp , line , n ) ;
tmp [ n ] = ' \0 ' ;
memset ( & ref , 0 , sizeof ( ref ) ) ;
ok_ref = amduat_asl_ref_decode_hex ( tmp , & ref ) ;
free ( tmp ) ;
if ( ok_ref ) {
amduat_artifact_t artifact ;
amduat_tgk_edge_body_t edge ;
const char * rel_name = NULL ;
memset ( & artifact , 0 , sizeof ( artifact ) ) ;
if ( amduat_asl_store_get ( store , ref , & artifact ) = =
AMDUAT_ASL_STORE_OK & &
artifact . has_type_tag & &
artifact . type_tag . tag_id = = AMDUAT_TYPE_TAG_TGK1_EDGE_V1 ) {
memset ( & edge , 0 , sizeof ( edge ) ) ;
if ( amduat_enc_tgk1_edge_decode_v1 ( artifact . bytes , & edge ) ) {
if ( edge . from_len = = 1 & & edge . to_len = = 1 ) {
rel_name = amduatd_relation_name_for_ref ( c , edge . payload ) ;
}
if ( rel_name ! = NULL ) {
if ( ! amduatd_concepts_append_edge_record ( c ,
store ,
edge . from [ 0 ] ,
edge . to [ 0 ] ,
rel_name ,
amduat_octets ( NULL , 0u ) ,
NULL ) ) {
ok = false ;
}
}
amduat_enc_tgk1_edge_free ( & edge ) ;
}
amduat_asl_artifact_free ( & artifact ) ;
} else if ( artifact . bytes . data ! = NULL ) {
amduat_asl_artifact_free ( & artifact ) ;
}
amduat_reference_free ( & ref ) ;
if ( ! ok ) {
break ;
}
}
}
if ( nl = = NULL ) {
break ;
}
line = nl + 1 ;
}
}
2025-12-23 09:14:58 +01:00
2026-01-23 20:55:41 +01:00
free ( file_bytes ) ;
2025-12-23 09:14:58 +01:00
2026-01-23 20:55:41 +01:00
if ( ok ) {
char migrated_path [ 1100 ] ;
( void ) snprintf ( migrated_path ,
sizeof ( migrated_path ) ,
" %s.migrated " ,
c - > edges_path ) ;
( void ) rename ( c - > edges_path , migrated_path ) ;
2025-12-23 09:14:58 +01:00
}
2026-01-23 20:55:41 +01:00
return ok ;
2025-12-23 09:14:58 +01:00
}
2026-01-23 22:28:56 +01:00
static bool amduatd_concepts_migrate_unscoped_edges (
amduatd_concepts_t * c ,
amduat_asl_store_t * store ,
const char * unscoped_collection ) {
amduat_asl_collection_view_t view ;
uint64_t from = 0u ;
bool ok = false ;
if ( c = = NULL | | store = = NULL | | unscoped_collection = = NULL ) {
return false ;
}
while ( true ) {
memset ( & view , 0 , sizeof ( view ) ) ;
if ( ! amduatd_collection_view ( store ,
c - > root_path ,
unscoped_collection ,
from ,
AMDUATD_EDGE_VIEW_BATCH ,
& view ) ) {
return false ;
}
for ( size_t i = 0u ; i < view . refs_len ; + + i ) {
uint64_t offset = 0u ;
if ( amduat_asl_collection_append ( & c - > edge_collection ,
c - > edge_collection_name ,
view . refs [ i ] ,
AMDUATD_EDGE_COLLECTION_KIND ,
amduat_octets ( NULL , 0u ) ,
& offset ) ! = AMDUAT_ASL_COLLECTION_OK ) {
goto migrate_cleanup ;
}
if ( offset ! = 0u & & ( offset % 256u ) = = 0u ) {
amduat_reference_t snapshot_ref ;
bool swapped = false ;
memset ( & snapshot_ref , 0 , sizeof ( snapshot_ref ) ) ;
( void ) amduat_asl_collection_snapshot ( & c - > edge_collection ,
c - > edge_collection_name ,
offset ,
& snapshot_ref ,
& swapped ) ;
amduat_reference_free ( & snapshot_ref ) ;
}
}
if ( view . refs_len = = 0u | | view . computed_up_to_offset < = from ) {
break ;
}
from = view . computed_up_to_offset ;
amduat_asl_collection_view_free ( & view ) ;
}
ok = true ;
migrate_cleanup :
amduat_asl_collection_view_free ( & view ) ;
return ok ;
}
2025-12-22 21:03:00 +01:00
static bool amduatd_concepts_init ( amduatd_concepts_t * c ,
amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
2026-01-23 22:28:56 +01:00
const amduatd_cfg_t * dcfg ,
2025-12-22 21:03:00 +01:00
const char * root_path ) {
2026-01-23 22:28:56 +01:00
amduat_octets_t scoped_collection = amduat_octets ( NULL , 0u ) ;
2025-12-22 21:03:00 +01:00
if ( c = = NULL | | store = = NULL | | cfg = = NULL | | root_path = = NULL ) {
return false ;
}
memset ( c , 0 , sizeof ( * c ) ) ;
c - > root_path = root_path ;
( void ) snprintf ( c - > edges_path , sizeof ( c - > edges_path ) , " %s/%s " , root_path ,
AMDUATD_EDGES_FILE ) ;
2026-01-23 22:28:56 +01:00
if ( ! amduatd_scope_name ( dcfg , AMDUATD_EDGE_COLLECTION , & scoped_collection ) ) {
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: scope edges failed " ) ;
return false ;
}
c - > edge_collection_name = ( char * ) scoped_collection . data ;
if ( c - > edge_collection_name = = NULL ) {
return false ;
}
2026-01-23 20:55:41 +01:00
if ( ! amduat_asl_collection_store_init ( & c - > edge_collection , root_path ,
store ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: collection store init failed " ) ;
2026-01-23 20:55:41 +01:00
return false ;
}
2025-12-22 21:03:00 +01:00
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_seed_relation ( store , cfg , dcfg , " aliases " ,
2025-12-22 21:03:00 +01:00
& c - > rel_aliases_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: seed aliases failed " ) ;
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_ensure_alias ( c , store , cfg , dcfg , " ms.aliases " ,
2025-12-23 09:14:58 +01:00
c - > rel_aliases_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: ensure ms.aliases failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_seed_relation ( store , cfg , dcfg , " materializesAs " ,
2025-12-22 21:03:00 +01:00
& c - > rel_materializes_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: seed materializesAs failed " ) ;
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_ensure_alias ( c , store , cfg , dcfg , " ms.materializes_as " ,
2025-12-23 09:14:58 +01:00
c - > rel_materializes_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR ,
" concepts init: ensure ms.materializes_as failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_seed_relation ( store , cfg , dcfg , " represents " ,
2025-12-23 09:14:58 +01:00
& c - > rel_represents_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: seed represents failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_ensure_alias ( c , store , cfg , dcfg , " ms.represents " ,
2025-12-23 09:14:58 +01:00
c - > rel_represents_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: ensure ms.represents failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_seed_relation ( store , cfg , dcfg , " requiresKey " ,
2025-12-23 09:14:58 +01:00
& c - > rel_requires_key_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: seed requiresKey failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_ensure_alias ( c , store , cfg , dcfg , " ms.requires_key " ,
2025-12-23 09:14:58 +01:00
c - > rel_requires_key_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR ,
" concepts init: ensure ms.requires_key failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_seed_relation ( store , cfg , dcfg , " withinDomain " ,
2025-12-23 09:14:58 +01:00
& c - > rel_within_domain_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: seed withinDomain failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_ensure_alias ( c , store , cfg , dcfg , " ms.within_domain " ,
2025-12-23 09:14:58 +01:00
c - > rel_within_domain_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR ,
" concepts init: ensure ms.within_domain failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_seed_relation ( store , cfg , dcfg , " computedBy " ,
2025-12-23 09:14:58 +01:00
& c - > rel_computed_by_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: seed computedBy failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_ensure_alias ( c , store , cfg , dcfg , " ms.computed_by " ,
2025-12-23 09:14:58 +01:00
c - > rel_computed_by_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR ,
" concepts init: ensure ms.computed_by failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_seed_relation ( store , cfg , dcfg , " hasProvenance " ,
2025-12-23 09:14:58 +01:00
& c - > rel_has_provenance_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: seed hasProvenance failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_ensure_alias ( c , store , cfg , dcfg , " ms.has_provenance " ,
2025-12-23 09:14:58 +01:00
c - > rel_has_provenance_ref ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR ,
" concepts init: ensure ms.has_provenance failed " ) ;
2025-12-23 09:14:58 +01:00
return false ;
}
2026-01-23 20:55:41 +01:00
if ( ! amduatd_concepts_load_edges ( c , store ) ) {
2026-01-23 22:28:56 +01:00
amduat_log ( AMDUAT_LOG_ERROR , " concepts init: load edges failed " ) ;
2026-01-23 20:55:41 +01:00
return false ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 20:55:41 +01:00
if ( c - > edges . len = = 0u ) {
2026-01-23 22:28:56 +01:00
if ( dcfg ! = NULL & & dcfg - > space_enabled ) {
if ( dcfg - > migrate_unscoped_edges ) {
if ( ! amduatd_concepts_migrate_unscoped_edges (
c ,
store ,
AMDUATD_EDGE_COLLECTION ) ) {
return false ;
}
if ( ! amduatd_concepts_load_edges ( c , store ) ) {
return false ;
}
}
} else {
if ( ! amduatd_concepts_migrate_edges ( c , store ) ) {
return false ;
}
if ( ! amduatd_concepts_load_edges ( c , store ) ) {
return false ;
}
2025-12-22 21:03:00 +01:00
}
}
return true ;
}
static bool amduatd_concepts_derive_name_ref (
const amduat_asl_store_fs_config_t * cfg ,
const char * name ,
amduat_reference_t * out_ref ) {
uint8_t * bytes = NULL ;
size_t bytes_len = 0 ;
amduat_artifact_t artifact ;
if ( out_ref = = NULL ) {
return false ;
}
* out_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
if ( cfg = = NULL | | name = = NULL ) {
return false ;
}
if ( ! amduatd_build_prefixed_bytes ( " AMDUATD/NAME/1 " , name , & bytes ,
& bytes_len ) ) {
return false ;
}
artifact = amduat_artifact ( amduat_octets ( bytes , bytes_len ) ) ;
if ( ! amduat_asl_ref_derive ( artifact ,
cfg - > config . encoding_profile_id ,
cfg - > config . hash_id ,
out_ref ,
NULL ) ) {
free ( bytes ) ;
return false ;
}
free ( bytes ) ;
return true ;
}
static bool amduatd_concepts_put_name_artifact ( amduat_asl_store_t * store ,
const char * name ,
amduat_reference_t * out_ref ) {
uint8_t * bytes = NULL ;
size_t bytes_len = 0 ;
amduat_artifact_t artifact ;
if ( out_ref ! = NULL ) {
* out_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
}
if ( store = = NULL | | name = = NULL | | out_ref = = NULL ) {
return false ;
}
if ( ! amduatd_build_prefixed_bytes ( " AMDUATD/NAME/1 " , name , & bytes ,
& bytes_len ) ) {
return false ;
}
artifact = amduat_artifact ( amduat_octets ( bytes , bytes_len ) ) ;
if ( amduat_asl_store_put ( store , artifact , out_ref ) ! = AMDUAT_ASL_STORE_OK ) {
free ( bytes ) ;
return false ;
}
free ( bytes ) ;
return true ;
}
static bool amduatd_concepts_put_concept_id ( amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
amduat_reference_t * out_ref ) {
uint8_t rnd [ 16 ] ;
uint8_t * bytes = NULL ;
size_t bytes_len = 0 ;
amduat_artifact_t artifact ;
if ( out_ref ! = NULL ) {
* out_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
}
if ( store = = NULL | | cfg = = NULL | | out_ref = = NULL ) {
return false ;
}
if ( ! amduatd_read_urandom ( rnd , sizeof ( rnd ) ) ) {
return false ;
}
bytes_len = strlen ( " AMDUATD/CONCEPT-ID/1 " ) + 1u + sizeof ( rnd ) ;
bytes = ( uint8_t * ) malloc ( bytes_len ) ;
if ( bytes = = NULL ) {
return false ;
}
memcpy ( bytes , " AMDUATD/CONCEPT-ID/1 " , strlen ( " AMDUATD/CONCEPT-ID/1 " ) ) ;
bytes [ strlen ( " AMDUATD/CONCEPT-ID/1 " ) ] = 0 ;
memcpy ( bytes + strlen ( " AMDUATD/CONCEPT-ID/1 " ) + 1u , rnd , sizeof ( rnd ) ) ;
artifact = amduat_artifact ( amduat_octets ( bytes , bytes_len ) ) ;
if ( ! amduat_asl_ref_derive ( artifact ,
cfg - > config . encoding_profile_id ,
cfg - > config . hash_id ,
out_ref ,
NULL ) ) {
free ( bytes ) ;
return false ;
}
if ( amduat_asl_store_put ( store , artifact , out_ref ) ! = AMDUAT_ASL_STORE_OK ) {
free ( bytes ) ;
return false ;
}
free ( bytes ) ;
return true ;
}
static bool amduatd_concepts_put_edge ( amduat_asl_store_t * store ,
amduatd_concepts_t * c ,
amduat_reference_t from ,
amduat_reference_t to ,
amduat_reference_t relation_concept_ref ,
2026-01-23 20:55:41 +01:00
amduat_octets_t actor ,
2025-12-22 21:03:00 +01:00
amduat_reference_t * out_edge_ref ) {
2026-01-23 20:55:41 +01:00
const char * rel_name = NULL ;
2025-12-22 21:03:00 +01:00
if ( out_edge_ref ! = NULL ) {
* out_edge_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
}
if ( store = = NULL | | c = = NULL | | out_edge_ref = = NULL ) {
return false ;
}
2026-01-23 20:55:41 +01:00
rel_name = amduatd_relation_name_for_ref ( c , relation_concept_ref ) ;
if ( rel_name = = NULL ) {
2025-12-22 21:03:00 +01:00
return false ;
}
2026-01-23 20:55:41 +01:00
return amduatd_concepts_append_edge_record ( c ,
store ,
from ,
to ,
rel_name ,
actor ,
out_edge_ref ) ;
2025-12-22 21:03:00 +01:00
}
static bool amduatd_concepts_lookup_alias ( amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
const amduatd_concepts_t * c ,
const char * name ,
amduat_reference_t * out_concept_ref ) {
amduat_reference_t name_ref ;
size_t i ;
if ( out_concept_ref ! = NULL ) {
* out_concept_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
}
if ( store = = NULL | | cfg = = NULL | | c = = NULL | | name = = NULL | |
out_concept_ref = = NULL ) {
return false ;
}
if ( ! amduatd_concepts_derive_name_ref ( cfg , name , & name_ref ) ) {
return false ;
}
2026-01-23 20:55:41 +01:00
for ( i = c - > edges . len ; i > 0 ; - - i ) {
const amduatd_edge_entry_t * entry = & c - > edges . items [ i - 1u ] ;
if ( entry - > rel = = NULL ) {
2025-12-22 21:03:00 +01:00
continue ;
}
2026-01-23 20:55:41 +01:00
if ( strcmp ( entry - > rel , AMDUATD_REL_ALIAS ) ! = 0 ) {
2025-12-22 21:03:00 +01:00
continue ;
}
2026-01-23 20:55:41 +01:00
if ( amduat_reference_eq ( entry - > src_ref , name_ref ) ) {
amduat_reference_clone ( entry - > dst_ref , out_concept_ref ) ;
2025-12-22 21:03:00 +01:00
amduat_reference_free ( & name_ref ) ;
return true ;
}
}
amduat_reference_free ( & name_ref ) ;
return false ;
}
static bool amduatd_concepts_resolve_latest ( amduat_asl_store_t * store ,
const amduatd_concepts_t * c ,
amduat_reference_t concept_ref ,
amduat_reference_t * out_ref ) {
size_t i ;
if ( out_ref ! = NULL ) {
* out_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
}
if ( store = = NULL | | c = = NULL | | out_ref = = NULL ) {
return false ;
}
2026-01-23 20:55:41 +01:00
for ( i = c - > edges . len ; i > 0 ; - - i ) {
const amduatd_edge_entry_t * entry = & c - > edges . items [ i - 1u ] ;
if ( entry - > rel = = NULL ) {
2025-12-22 21:03:00 +01:00
continue ;
}
2026-01-23 20:55:41 +01:00
if ( strcmp ( entry - > rel , AMDUATD_REL_MATERIALIZES ) ! = 0 ) {
2025-12-22 21:03:00 +01:00
continue ;
}
2026-01-23 20:55:41 +01:00
if ( amduat_reference_eq ( entry - > src_ref , concept_ref ) ) {
amduat_reference_clone ( entry - > dst_ref , out_ref ) ;
2025-12-22 21:03:00 +01:00
return true ;
}
}
return false ;
}
2025-12-22 21:13:26 +01:00
static bool amduatd_parse_name_artifact ( amduat_artifact_t artifact ,
2026-01-23 22:28:56 +01:00
const amduatd_cfg_t * dcfg ,
2025-12-22 21:13:26 +01:00
char * out ,
size_t cap ) {
const uint8_t * bytes ;
size_t len ;
const char * prefix = " AMDUATD/NAME/1 " ;
size_t prefix_len ;
size_t i ;
size_t name_len ;
2026-01-23 22:28:56 +01:00
char scoped [ AMDUAT_ASL_POINTER_NAME_MAX + 1u ] ;
2025-12-22 21:13:26 +01:00
if ( out ! = NULL & & cap ! = 0 ) {
out [ 0 ] = ' \0 ' ;
}
if ( out = = NULL | | cap = = 0 ) {
return false ;
}
if ( artifact . bytes . len ! = 0 & & artifact . bytes . data = = NULL ) {
return false ;
}
bytes = artifact . bytes . data ;
len = artifact . bytes . len ;
prefix_len = strlen ( prefix ) ;
if ( len < prefix_len + 1u ) {
return false ;
}
if ( memcmp ( bytes , prefix , prefix_len ) ! = 0 | | bytes [ prefix_len ] ! = 0 ) {
return false ;
}
for ( i = prefix_len + 1u ; i < len ; + + i ) {
if ( bytes [ i ] = = 0 ) {
return false ;
}
}
name_len = len - ( prefix_len + 1u ) ;
2026-01-23 22:28:56 +01:00
if ( name_len = = 0 | | name_len > = sizeof ( scoped ) ) {
2025-12-22 21:13:26 +01:00
return false ;
}
2026-01-23 22:28:56 +01:00
memcpy ( scoped , bytes + prefix_len + 1u , name_len ) ;
scoped [ name_len ] = ' \0 ' ;
return amduatd_unscoped_name ( dcfg , scoped , out , cap ) ;
2025-12-22 21:13:26 +01:00
}
static bool amduatd_handle_get_concepts ( int fd ,
amduat_asl_store_t * store ,
2026-01-23 22:28:56 +01:00
const amduatd_concepts_t * concepts ,
const amduatd_cfg_t * dcfg ) {
2025-12-22 21:13:26 +01:00
amduatd_strbuf_t b ;
bool first = true ;
size_t i ;
if ( store = = NULL | | concepts = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
memset ( & b , 0 , sizeof ( b ) ) ;
if ( ! amduatd_strbuf_append_cstr ( & b , " { \" concepts \" :[ " ) ) {
amduatd_strbuf_free ( & b ) ;
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
}
2026-01-23 20:55:41 +01:00
for ( i = 0 ; i < concepts - > edges . len ; + + i ) {
const amduatd_edge_entry_t * entry = & concepts - > edges . items [ i ] ;
2025-12-22 21:13:26 +01:00
amduat_artifact_t artifact ;
amduat_asl_store_error_t err ;
2026-01-23 22:28:56 +01:00
char name [ AMDUAT_ASL_POINTER_NAME_MAX + 1u ] ;
2025-12-22 21:13:26 +01:00
char * concept_hex = NULL ;
2026-01-23 20:55:41 +01:00
if ( entry - > rel = = NULL | |
strcmp ( entry - > rel , AMDUATD_REL_ALIAS ) ! = 0 ) {
2025-12-22 21:13:26 +01:00
continue ;
}
memset ( & artifact , 0 , sizeof ( artifact ) ) ;
2026-01-23 20:55:41 +01:00
err = amduat_asl_store_get ( store , entry - > src_ref , & artifact ) ;
2025-12-22 21:13:26 +01:00
if ( err ! = AMDUAT_ASL_STORE_OK | |
2026-01-23 22:28:56 +01:00
! amduatd_parse_name_artifact ( artifact , dcfg , name , sizeof ( name ) ) ) {
2025-12-22 21:13:26 +01:00
amduat_asl_artifact_free ( & artifact ) ;
continue ;
}
amduat_asl_artifact_free ( & artifact ) ;
2026-01-23 20:55:41 +01:00
if ( ! amduat_asl_ref_encode_hex ( entry - > dst_ref , & concept_hex ) ) {
2025-12-22 21:13:26 +01:00
continue ;
}
if ( ! first ) {
( void ) amduatd_strbuf_append_char ( & b , ' , ' ) ;
}
first = false ;
( void ) amduatd_strbuf_append_cstr ( & b , " { \" name \" : \" " ) ;
( void ) amduatd_strbuf_append_cstr ( & b , name ) ;
( void ) amduatd_strbuf_append_cstr ( & b , " \" , \" concept_ref \" : \" " ) ;
( void ) amduatd_strbuf_append_cstr ( & b , concept_hex ) ;
( void ) amduatd_strbuf_append_cstr ( & b , " \" } " ) ;
free ( concept_hex ) ;
}
if ( ! amduatd_strbuf_append_cstr ( & b , " ]} \n " ) ) {
amduatd_strbuf_free ( & b ) ;
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
}
{
bool ok = amduatd_http_send_json ( fd , 200 , " OK " , b . data , false ) ;
amduatd_strbuf_free ( & b ) ;
return ok ;
}
}
2025-12-23 09:14:58 +01:00
static bool amduatd_append_relation_entry ( amduatd_strbuf_t * b ,
bool * first ,
const char * name ,
amduat_reference_t ref ) {
char * hex = NULL ;
if ( b = = NULL | | first = = NULL | | name = = NULL ) {
return false ;
}
if ( ! amduat_asl_ref_encode_hex ( ref , & hex ) ) {
return false ;
}
if ( ! * first ) {
( void ) amduatd_strbuf_append_char ( b , ' , ' ) ;
}
* first = false ;
( void ) amduatd_strbuf_append_cstr ( b , " { \" name \" : \" " ) ;
( void ) amduatd_strbuf_append_cstr ( b , name ) ;
( void ) amduatd_strbuf_append_cstr ( b , " \" , \" concept_ref \" : \" " ) ;
( void ) amduatd_strbuf_append_cstr ( b , hex ) ;
( void ) amduatd_strbuf_append_cstr ( b , " \" } " ) ;
free ( hex ) ;
return true ;
}
static bool amduatd_handle_get_relations ( int fd ,
const amduatd_concepts_t * concepts ) {
amduatd_strbuf_t b ;
bool first = true ;
if ( concepts = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
memset ( & b , 0 , sizeof ( b ) ) ;
if ( ! amduatd_strbuf_append_cstr ( & b , " { \" relations \" :[ " ) ) {
amduatd_strbuf_free ( & b ) ;
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
}
if ( ! amduatd_append_relation_entry ( & b , & first , " ms.aliases " ,
concepts - > rel_aliases_ref ) | |
! amduatd_append_relation_entry ( & b , & first , " ms.materializes_as " ,
concepts - > rel_materializes_ref ) | |
! amduatd_append_relation_entry ( & b , & first , " ms.represents " ,
concepts - > rel_represents_ref ) | |
! amduatd_append_relation_entry ( & b , & first , " ms.requires_key " ,
concepts - > rel_requires_key_ref ) | |
! amduatd_append_relation_entry ( & b , & first , " ms.within_domain " ,
concepts - > rel_within_domain_ref ) | |
! amduatd_append_relation_entry ( & b , & first , " ms.computed_by " ,
concepts - > rel_computed_by_ref ) | |
! amduatd_append_relation_entry ( & b , & first , " ms.has_provenance " ,
concepts - > rel_has_provenance_ref ) ) {
amduatd_strbuf_free ( & b ) ;
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
}
( void ) amduatd_strbuf_append_cstr ( & b , " ]} \n " ) ;
{
bool ok = amduatd_http_send_json ( fd , 200 , " OK " , b . data , false ) ;
amduatd_strbuf_free ( & b ) ;
return ok ;
}
}
2025-12-22 21:13:26 +01:00
static bool amduatd_handle_get_concept ( int fd ,
amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
const amduatd_concepts_t * concepts ,
2026-01-23 22:28:56 +01:00
const amduatd_cfg_t * dcfg ,
2025-12-22 21:13:26 +01:00
const char * name ) {
amduat_reference_t concept_ref ;
amduat_reference_t latest_ref ;
amduatd_strbuf_t b ;
char * concept_hex = NULL ;
char * latest_hex = NULL ;
2026-01-23 22:28:56 +01:00
char * scoped_name = NULL ;
2025-12-22 21:13:26 +01:00
bool have_latest = false ;
size_t i ;
size_t version_count = 0 ;
memset ( & concept_ref , 0 , sizeof ( concept_ref ) ) ;
memset ( & latest_ref , 0 , sizeof ( latest_ref ) ) ;
memset ( & b , 0 , sizeof ( b ) ) ;
if ( store = = NULL | | cfg = = NULL | | concepts = = NULL | | name = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_scope_name_cstr ( dcfg , name , & scoped_name ) ) {
2025-12-22 21:13:26 +01:00
return amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid name " ) ;
}
2026-01-23 22:28:56 +01:00
amduatd_log_scoped_name ( dcfg , name , scoped_name ) ;
if ( ! amduatd_concepts_lookup_alias ( store , cfg , concepts , scoped_name ,
& concept_ref ) ) {
free ( scoped_name ) ;
2025-12-22 21:13:26 +01:00
return amduatd_send_json_error ( fd , 404 , " Not Found " , " unknown concept " ) ;
}
2026-01-23 22:28:56 +01:00
free ( scoped_name ) ;
2025-12-22 21:13:26 +01:00
if ( amduatd_concepts_resolve_latest ( store , concepts , concept_ref ,
& latest_ref ) ) {
have_latest = true ;
}
if ( ! amduat_asl_ref_encode_hex ( concept_ref , & concept_hex ) ) {
amduat_reference_free ( & concept_ref ) ;
amduat_reference_free ( & latest_ref ) ;
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
}
if ( have_latest ) {
if ( ! amduat_asl_ref_encode_hex ( latest_ref , & latest_hex ) ) {
free ( concept_hex ) ;
amduat_reference_free ( & concept_ref ) ;
amduat_reference_free ( & latest_ref ) ;
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
}
}
if ( ! amduatd_strbuf_append_cstr ( & b , " { " ) ) {
free ( concept_hex ) ;
free ( latest_hex ) ;
amduat_reference_free ( & concept_ref ) ;
amduat_reference_free ( & latest_ref ) ;
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
}
( void ) amduatd_strbuf_append_cstr ( & b , " \" name \" : \" " ) ;
( void ) amduatd_strbuf_append_cstr ( & b , name ) ;
( void ) amduatd_strbuf_append_cstr ( & b , " \" , \" concept_ref \" : \" " ) ;
( void ) amduatd_strbuf_append_cstr ( & b , concept_hex ) ;
( void ) amduatd_strbuf_append_cstr ( & b , " \" , \" latest_ref \" : " ) ;
if ( latest_hex ! = NULL ) {
( void ) amduatd_strbuf_append_cstr ( & b , " \" " ) ;
( void ) amduatd_strbuf_append_cstr ( & b , latest_hex ) ;
( void ) amduatd_strbuf_append_cstr ( & b , " \" " ) ;
} else {
( void ) amduatd_strbuf_append_cstr ( & b , " null " ) ;
}
( void ) amduatd_strbuf_append_cstr ( & b , " , \" versions \" :[ " ) ;
2026-01-23 20:55:41 +01:00
for ( i = 0 ; i < concepts - > edges . len ; + + i ) {
const amduatd_edge_entry_t * entry = & concepts - > edges . items [ i ] ;
2025-12-22 21:13:26 +01:00
char * edge_hex = NULL ;
char * ref_hex = NULL ;
2026-01-23 20:55:41 +01:00
if ( entry - > rel = = NULL | |
strcmp ( entry - > rel , AMDUATD_REL_MATERIALIZES ) ! = 0 ) {
2025-12-22 21:13:26 +01:00
continue ;
}
2026-01-23 20:55:41 +01:00
if ( ! amduat_reference_eq ( entry - > src_ref , concept_ref ) ) {
2025-12-22 21:13:26 +01:00
continue ;
}
2026-01-23 20:55:41 +01:00
if ( ! amduat_asl_ref_encode_hex ( entry - > record_ref , & edge_hex ) | |
! amduat_asl_ref_encode_hex ( entry - > dst_ref , & ref_hex ) ) {
2025-12-22 21:13:26 +01:00
free ( edge_hex ) ;
free ( ref_hex ) ;
continue ;
}
if ( version_count ! = 0 ) {
( void ) amduatd_strbuf_append_char ( & b , ' , ' ) ;
}
version_count + + ;
( void ) amduatd_strbuf_append_cstr ( & b , " { \" edge_ref \" : \" " ) ;
( void ) amduatd_strbuf_append_cstr ( & b , edge_hex ) ;
( void ) amduatd_strbuf_append_cstr ( & b , " \" , \" ref \" : \" " ) ;
( void ) amduatd_strbuf_append_cstr ( & b , ref_hex ) ;
( void ) amduatd_strbuf_append_cstr ( & b , " \" } " ) ;
free ( edge_hex ) ;
free ( ref_hex ) ;
if ( version_count > = 64u ) {
break ;
}
}
( void ) amduatd_strbuf_append_cstr ( & b , " ]} \n " ) ;
free ( concept_hex ) ;
free ( latest_hex ) ;
amduat_reference_free ( & concept_ref ) ;
amduat_reference_free ( & latest_ref ) ;
{
bool ok = amduatd_http_send_json ( fd , 200 , " OK " , b . data , false ) ;
amduatd_strbuf_free ( & b ) ;
return ok ;
}
}
2025-12-22 15:23:54 +01:00
static bool amduatd_read_exact ( int fd , uint8_t * buf , size_t len ) {
size_t off = 0 ;
while ( off < len ) {
ssize_t n = read ( fd , buf + off , len - off ) ;
if ( n < 0 ) {
if ( errno = = EINTR ) {
continue ;
}
return false ;
}
if ( n = = 0 ) {
return false ;
}
off + = ( size_t ) n ;
}
return true ;
}
static bool amduatd_read_line ( int fd , char * buf , size_t cap , size_t * out_len ) {
size_t len = 0 ;
while ( len + 1 < cap ) {
char c = 0 ;
ssize_t n = read ( fd , & c , 1 ) ;
if ( n < 0 ) {
if ( errno = = EINTR ) {
continue ;
}
return false ;
}
if ( n = = 0 ) {
return false ;
}
if ( c = = ' \n ' ) {
break ;
}
buf [ len + + ] = c ;
}
if ( len = = 0 ) {
return false ;
}
if ( len > 0 & & buf [ len - 1 ] = = ' \r ' ) {
len - - ;
}
buf [ len ] = ' \0 ' ;
if ( out_len ! = NULL ) {
* out_len = len ;
}
return true ;
}
static void amduatd_http_req_init ( amduatd_http_req_t * req ) {
memset ( req , 0 , sizeof ( * req ) ) ;
}
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 15:23:54 +01:00
static bool amduatd_http_parse_request ( int fd , amduatd_http_req_t * out_req ) {
char line [ 2048 ] ;
size_t line_len = 0 ;
char * sp1 = NULL ;
char * sp2 = NULL ;
if ( out_req = = NULL ) {
return false ;
}
amduatd_http_req_init ( out_req ) ;
if ( ! amduatd_read_line ( fd , line , sizeof ( line ) , & line_len ) ) {
return false ;
}
sp1 = strchr ( line , ' ' ) ;
if ( sp1 = = NULL ) {
return false ;
}
* sp1 + + = ' \0 ' ;
sp2 = strchr ( sp1 , ' ' ) ;
if ( sp2 = = NULL ) {
return false ;
}
* sp2 + + = ' \0 ' ;
if ( strlen ( line ) > = sizeof ( out_req - > method ) ) {
return false ;
}
strncpy ( out_req - > method , line , sizeof ( out_req - > method ) - 1 ) ;
if ( strlen ( sp1 ) > = sizeof ( out_req - > path ) ) {
return false ;
}
strncpy ( out_req - > path , sp1 , sizeof ( out_req - > path ) - 1 ) ;
for ( ; ; ) {
if ( ! amduatd_read_line ( fd , line , sizeof ( line ) , & line_len ) ) {
return false ;
}
if ( line_len = = 0 ) {
break ;
}
if ( strncasecmp ( line , " Content-Length: " , 15 ) = = 0 ) {
const char * v = line + 15 ;
while ( * v = = ' ' | | * v = = ' \t ' ) {
v + + ;
}
out_req - > content_length = ( size_t ) strtoull ( v , NULL , 10 ) ;
} else if ( strncasecmp ( line , " Content-Type: " , 13 ) = = 0 ) {
const char * v = line + 13 ;
while ( * v = = ' ' | | * v = = ' \t ' ) {
v + + ;
}
strncpy ( out_req - > content_type , v , sizeof ( out_req - > content_type ) - 1 ) ;
} else if ( strncasecmp ( line , " Accept: " , 7 ) = = 0 ) {
const char * v = line + 7 ;
while ( * v = = ' ' | | * v = = ' \t ' ) {
v + + ;
}
strncpy ( out_req - > accept , v , sizeof ( out_req - > accept ) - 1 ) ;
} else if ( strncasecmp ( line , " X-Amduat-Type-Tag: " , 18 ) = = 0 ) {
const char * v = line + 18 ;
while ( * v = = ' ' | | * v = = ' \t ' ) {
v + + ;
}
strncpy ( out_req - > x_type_tag , v , sizeof ( out_req - > x_type_tag ) - 1 ) ;
2026-01-23 23:08:41 +01:00
} else if ( strncasecmp ( line , " X-Amduat-Capability: " , 20 ) = = 0 ) {
const char * v = line + 20 ;
while ( * v = = ' ' | | * v = = ' \t ' ) {
v + + ;
}
strncpy ( out_req - > x_capability , v , sizeof ( out_req - > x_capability ) - 1 ) ;
2025-12-22 15:23:54 +01:00
}
}
return true ;
}
2026-01-23 23:30:29 +01:00
bool amduatd_http_send_status ( int fd ,
int code ,
const char * reason ,
const char * content_type ,
const uint8_t * body ,
size_t body_len ,
bool head_only ) {
2025-12-22 15:23:54 +01:00
char hdr [ 512 ] ;
int n = snprintf ( hdr ,
sizeof ( hdr ) ,
" HTTP/1.1 %d %s \r \n "
" Content-Length: %zu \r \n "
" Content-Type: %s \r \n "
" Connection: close \r \n "
" \r \n " ,
code ,
reason ! = NULL ? reason : " " ,
body_len ,
content_type ! = NULL ? content_type : " text/plain " ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( hdr ) ) {
return false ;
}
if ( ! amduatd_write_all ( fd , ( const uint8_t * ) hdr , ( size_t ) n ) ) {
return false ;
}
if ( ! head_only & & body ! = NULL & & body_len ! = 0 ) {
return amduatd_write_all ( fd , body , body_len ) ;
}
return true ;
}
2026-01-23 23:30:29 +01:00
bool amduatd_http_send_text ( int fd ,
int code ,
const char * reason ,
const char * text ,
bool head_only ) {
2025-12-22 15:23:54 +01:00
const uint8_t * body = ( const uint8_t * ) ( text ! = NULL ? text : " " ) ;
size_t len = text ! = NULL ? strlen ( text ) : 0 ;
return amduatd_http_send_status (
fd , code , reason , " text/plain; charset=utf-8 " , body , len , head_only ) ;
}
static bool amduatd_http_send_json ( int fd ,
int code ,
const char * reason ,
const char * json ,
bool head_only ) {
const uint8_t * body = ( const uint8_t * ) ( json ! = NULL ? json : " {} " ) ;
size_t len = json ! = NULL ? strlen ( json ) : 2 ;
return amduatd_http_send_status (
fd , code , reason , " application/json " , body , len , head_only ) ;
}
2025-12-22 19:46:59 +01:00
static bool amduatd_http_req_wants_html ( const amduatd_http_req_t * req ) {
if ( req = = NULL ) {
return false ;
}
if ( req - > accept [ 0 ] = = ' \0 ' ) {
return false ;
}
return strstr ( req - > accept , " text/html " ) ! = NULL ;
}
static bool amduatd_http_send_not_found ( int fd , const amduatd_http_req_t * req ) {
if ( amduatd_http_req_wants_html ( req ) ) {
const char * path = ( req ! = NULL & & req - > path [ 0 ] ! = ' \0 ' ) ? req - > path : " / " ;
const char * method =
( req ! = NULL & & req - > method [ 0 ] ! = ' \0 ' ) ? req - > method : " GET " ;
char html [ 4096 ] ;
int n = snprintf (
html ,
sizeof ( html ) ,
" <!doctype html> \n "
" <html lang= \" en \" > \n "
" <head> \n "
" <meta charset= \" utf-8 \" /> \n "
" <meta name= \" viewport \" content= \" width=device-width, initial-scale=1 \" /> \n "
" <title>amduatd — Not Found</title> \n "
" <style> \n "
" :root{color-scheme:light dark;} \n "
" body{font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif; "
" margin:0;line-height:1.4;} \n "
" .wrap{max-width:860px;margin:0 auto;padding:40px 20px;} \n "
" .card{border:1px solid rgba(127,127,127,.35);border-radius:14px;padding:22px; "
" background:rgba(127,127,127,.06);} \n "
" h1{margin:0 0 6px;font-size:22px;} \n "
" p{margin:8px 0;opacity:.9;} \n "
" code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace; "
" font-size:13px;} \n "
" ul{margin:10px 0 0;padding-left:18px;} \n "
" a{color:inherit;} \n "
" .muted{opacity:.75;} \n "
" </style> \n "
" </head> \n "
" <body> \n "
" <div class= \" wrap \" > \n "
" <div class= \" card \" > \n "
" <h1>404 — Not Found</h1> \n "
" <p>amduatd didn’ t recognize <code>%s %s</code>.</p> \n "
" <p class= \" muted \" >Try one of these:</p> \n "
" <ul> \n "
" <li><a href= \" /v1/meta \" >/v1/meta</a></li> \n "
" <li><a href= \" /v1/contract \" >/v1/contract</a></li> \n "
" </ul> \n "
" <p class= \" muted \" >Artifact bytes: <code>/v1/artifacts/<ref></code></p> \n "
" </div> \n "
" </div> \n "
" </body> \n "
" </html> \n " ,
method ,
path ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( html ) ) {
return amduatd_http_send_text ( fd , 404 , " Not Found " , " not found \n " , false ) ;
}
return amduatd_http_send_status ( fd ,
404 ,
" Not Found " ,
" text/html; charset=utf-8 " ,
( const uint8_t * ) html ,
( size_t ) n ,
false ) ;
}
return amduatd_http_send_text ( fd , 404 , " Not Found " , " not found \n " , false ) ;
}
2025-12-22 15:23:54 +01:00
static const char * amduatd_query_param ( const char * path ,
const char * key ,
char * out_value ,
size_t out_cap ) {
const char * q = strchr ( path , ' ? ' ) ;
size_t key_len = 0 ;
if ( out_value ! = NULL & & out_cap ! = 0 ) {
out_value [ 0 ] = ' \0 ' ;
}
if ( q = = NULL | | key = = NULL | | out_value = = NULL | | out_cap = = 0 ) {
return NULL ;
}
q + + ;
key_len = strlen ( key ) ;
while ( * q ! = ' \0 ' ) {
const char * k = q ;
const char * eq = strchr ( k , ' = ' ) ;
const char * amp = strchr ( k , ' & ' ) ;
size_t klen = 0 ;
const char * v = NULL ;
size_t vlen = 0 ;
if ( amp = = NULL ) {
amp = q + strlen ( q ) ;
}
if ( eq = = NULL | | eq > amp ) {
q = ( * amp = = ' & ' ) ? ( amp + 1 ) : amp ;
continue ;
}
klen = ( size_t ) ( eq - k ) ;
if ( klen = = key_len & & strncmp ( k , key , key_len ) = = 0 ) {
v = eq + 1 ;
vlen = ( size_t ) ( amp - v ) ;
if ( vlen > = out_cap ) {
vlen = out_cap - 1 ;
}
memcpy ( out_value , v , vlen ) ;
out_value [ vlen ] = ' \0 ' ;
return out_value ;
}
q = ( * amp = = ' & ' ) ? ( amp + 1 ) : amp ;
}
return NULL ;
}
2025-12-23 09:14:58 +01:00
static bool amduatd_bytes_contains ( const uint8_t * data ,
size_t len ,
const char * needle ) {
size_t i ;
size_t nlen ;
if ( data = = NULL | | needle = = NULL ) {
return false ;
}
nlen = strlen ( needle ) ;
if ( nlen = = 0 | | nlen > len ) {
return false ;
}
for ( i = 0 ; i + nlen < = len ; + + i ) {
if ( memcmp ( data + i , needle , nlen ) = = 0 ) {
return true ;
}
}
return false ;
}
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 ) ;
}
static const char * amduatd_json_skip_ws ( const char * p , const char * end ) {
while ( p < end ) {
char c = * p ;
if ( c ! = ' ' & & c ! = ' \t ' & & c ! = ' \n ' & & c ! = ' \r ' ) {
break ;
}
p + + ;
}
return p ;
}
static bool amduatd_json_expect ( const char * * p ,
const char * end ,
char expected ) {
const char * cur ;
if ( p = = NULL | | * p = = NULL ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
if ( cur > = end | | * cur ! = expected ) {
return false ;
}
* p = cur + 1 ;
return true ;
}
static bool amduatd_json_parse_string_noesc ( const char * * p ,
const char * end ,
const char * * out_start ,
size_t * out_len ) {
const char * cur ;
const char * s ;
if ( out_start ! = NULL ) {
* out_start = NULL ;
}
if ( out_len ! = NULL ) {
* out_len = 0 ;
}
if ( p = = NULL | | * p = = NULL ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
if ( cur > = end | | * cur ! = ' " ' ) {
return false ;
}
cur + + ;
s = cur ;
while ( cur < end ) {
unsigned char c = ( unsigned char ) * cur ;
if ( c = = ' " ' ) {
if ( out_start ! = NULL ) {
* out_start = s ;
}
if ( out_len ! = NULL ) {
* out_len = ( size_t ) ( cur - s ) ;
}
* p = cur + 1 ;
return true ;
}
if ( c = = ' \\ ' | | c < 0x20u ) {
return false ;
}
cur + + ;
}
return false ;
}
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_u64 ( const char * * p ,
const char * end ,
uint64_t * out ) {
const char * cur ;
const char * start ;
unsigned long long v ;
char * tmp = NULL ;
char * endp = NULL ;
size_t n ;
if ( out ! = NULL ) {
* out = 0 ;
}
if ( p = = NULL | | * p = = NULL | | out = = NULL ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
start = cur ;
if ( cur > = end ) {
return false ;
}
if ( * cur < ' 0 ' | | * cur > ' 9 ' ) {
return false ;
}
cur + + ;
while ( cur < end & & * cur > = ' 0 ' & & * cur < = ' 9 ' ) {
cur + + ;
}
n = ( size_t ) ( cur - start ) ;
tmp = ( char * ) malloc ( n + 1u ) ;
if ( tmp = = NULL ) {
return false ;
}
memcpy ( tmp , start , n ) ;
tmp [ n ] = ' \0 ' ;
errno = 0 ;
v = strtoull ( tmp , & endp , 10 ) ;
free ( tmp ) ;
if ( errno ! = 0 | | endp = = NULL | | * endp ! = ' \0 ' ) {
return false ;
}
* out = ( uint64_t ) v ;
* p = cur ;
return true ;
}
static bool amduatd_json_parse_u32_loose ( const char * * p ,
const char * end ,
uint32_t * out ) {
const char * cur ;
const char * sv = NULL ;
size_t sv_len = 0 ;
uint64_t v = 0 ;
size_t i ;
if ( out ! = NULL ) {
* out = 0 ;
}
if ( p = = NULL | | * p = = NULL | | out = = NULL ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
if ( cur > = end ) {
return false ;
}
if ( * cur = = ' " ' ) {
if ( ! amduatd_json_parse_string_noesc ( p , end , & sv , & sv_len ) ) {
return false ;
}
if ( sv_len = = 0 ) {
return false ;
}
for ( i = 0 ; i < sv_len ; + + i ) {
unsigned char c = ( unsigned char ) sv [ i ] ;
if ( c < ' 0 ' | | c > ' 9 ' ) {
return false ;
}
v = ( v * 10u ) + ( uint64_t ) ( c - ' 0 ' ) ;
if ( v > 0xffffffffULL ) {
return false ;
}
}
* out = ( uint32_t ) v ;
return true ;
}
if ( * cur < ' 0 ' | | * cur > ' 9 ' ) {
return false ;
}
for ( ; ; ) {
unsigned char c = ( unsigned char ) * cur ;
if ( c < ' 0 ' | | c > ' 9 ' ) {
break ;
}
v = ( v * 10u ) + ( uint64_t ) ( c - ' 0 ' ) ;
if ( v > 0xffffffffULL ) {
return false ;
}
cur + + ;
if ( cur > = end ) {
break ;
}
}
* out = ( uint32_t ) v ;
* p = cur ;
return true ;
}
static void amduatd_json_peek_token ( const char * p ,
const char * end ,
char * out ,
size_t cap ) {
const char * cur ;
size_t n = 0 ;
if ( out ! = NULL & & cap ! = 0 ) {
out [ 0 ] = ' \0 ' ;
}
if ( out = = NULL | | cap = = 0 ) {
return ;
}
cur = amduatd_json_skip_ws ( p , end ) ;
while ( cur < end & & n + 1 < cap ) {
unsigned char c = ( unsigned char ) * cur ;
if ( c = = ' , ' | | c = = ' } ' | | c = = ' ] ' | | c = = ' \n ' | | c = = ' \r ' ) {
break ;
}
if ( c < 0x20u | | c > 0x7eu ) {
out [ n + + ] = ' ? ' ;
} else {
out [ n + + ] = ( char ) c ;
}
cur + + ;
if ( n > = 24u ) {
break ;
}
}
out [ n ] = ' \0 ' ;
}
2025-12-22 19:10:14 +01:00
static bool amduatd_json_skip_string ( const char * * p , const char * end ) {
const char * cur ;
if ( p = = NULL | | * p = = NULL ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
if ( cur > = end | | * cur ! = ' " ' ) {
return false ;
}
cur + + ;
while ( cur < end ) {
unsigned char c = ( unsigned char ) * cur + + ;
if ( c = = ' " ' ) {
* p = cur ;
return true ;
}
if ( c = = ' \\ ' ) {
if ( cur > = end ) {
return false ;
}
cur + + ;
} else if ( c < 0x20u ) {
return false ;
}
}
return false ;
}
static bool amduatd_json_skip_value ( const char * * p ,
const char * end ,
int depth ) ;
static bool amduatd_json_skip_array ( const char * * p ,
const char * end ,
int depth ) {
const char * cur ;
if ( ! amduatd_json_expect ( p , end , ' [ ' ) ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
if ( cur < end & & * cur = = ' ] ' ) {
* p = cur + 1 ;
return true ;
}
for ( ; ; ) {
if ( ! amduatd_json_skip_value ( p , end , depth + 1 ) ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
if ( cur > = end ) {
return false ;
}
if ( * cur = = ' , ' ) {
* p = cur + 1 ;
continue ;
}
if ( * cur = = ' ] ' ) {
* p = cur + 1 ;
return true ;
}
return false ;
}
}
static bool amduatd_json_skip_object ( const char * * p ,
const char * end ,
int depth ) {
const char * cur ;
if ( ! amduatd_json_expect ( p , end , ' { ' ) ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
if ( cur < end & & * cur = = ' } ' ) {
* p = cur + 1 ;
return true ;
}
for ( ; ; ) {
if ( ! amduatd_json_skip_string ( p , end ) ) {
return false ;
}
if ( ! amduatd_json_expect ( p , end , ' : ' ) ) {
return false ;
}
if ( ! amduatd_json_skip_value ( p , end , depth + 1 ) ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
if ( cur > = end ) {
return false ;
}
if ( * cur = = ' , ' ) {
* p = cur + 1 ;
continue ;
}
if ( * cur = = ' } ' ) {
* p = cur + 1 ;
return true ;
}
return false ;
}
}
static bool amduatd_json_skip_value ( const char * * p ,
const char * end ,
int depth ) {
const char * cur ;
if ( p = = NULL | | * p = = NULL ) {
return false ;
}
if ( depth > 32 ) {
return false ;
}
cur = amduatd_json_skip_ws ( * p , end ) ;
if ( cur > = end ) {
return false ;
}
if ( * cur = = ' " ' ) {
return amduatd_json_skip_string ( p , end ) ;
}
if ( * cur = = ' { ' ) {
return amduatd_json_skip_object ( p , end , depth ) ;
}
if ( * cur = = ' [ ' ) {
return amduatd_json_skip_array ( p , end , depth ) ;
}
if ( * cur = = ' t ' & & ( end - cur ) > = 4 & & memcmp ( cur , " true " , 4 ) = = 0 ) {
* p = cur + 4 ;
return true ;
}
if ( * cur = = ' f ' & & ( end - cur ) > = 5 & & memcmp ( cur , " false " , 5 ) = = 0 ) {
* p = cur + 5 ;
return true ;
}
if ( * cur = = ' n ' & & ( end - cur ) > = 4 & & memcmp ( cur , " null " , 4 ) = = 0 ) {
* p = cur + 4 ;
return true ;
}
if ( * cur = = ' - ' | | ( * cur > = ' 0 ' & & * cur < = ' 9 ' ) ) {
cur + + ;
while ( cur < end ) {
char c = * cur ;
if ( ( c > = ' 0 ' & & c < = ' 9 ' ) | | c = = ' . ' | | c = = ' e ' | | c = = ' E ' | |
c = = ' + ' | | c = = ' - ' ) {
cur + + ;
continue ;
}
break ;
}
* p = cur ;
return true ;
}
return false ;
}
static bool amduatd_copy_json_str ( const char * s ,
size_t len ,
char * * out ) {
char * buf ;
if ( out = = NULL ) {
return false ;
}
* out = NULL ;
if ( len > ( SIZE_MAX - 1u ) ) {
return false ;
}
buf = ( char * ) malloc ( len + 1u ) ;
if ( buf = = NULL ) {
return false ;
}
if ( len ! = 0 ) {
memcpy ( buf , s , len ) ;
}
buf [ len ] = ' \0 ' ;
* out = buf ;
return true ;
}
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 ;
}
typedef enum {
AMDUATD_REF_OK = 0 ,
AMDUATD_REF_ERR_INVALID = 1 ,
AMDUATD_REF_ERR_NOT_FOUND = 2 ,
AMDUATD_REF_ERR_INTERNAL = 3
} amduatd_ref_status_t ;
static 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 ,
2026-01-23 22:28:56 +01:00
const amduatd_cfg_t * dcfg ,
2025-12-22 21:03:00 +01:00
const char * s ,
size_t len ,
amduat_reference_t * out_ref ) {
amduat_reference_t concept_ref ;
char * tmp = NULL ;
2026-01-23 22:28:56 +01:00
char * scoped_name = NULL ;
2025-12-22 21:03:00 +01:00
if ( out_ref ! = NULL ) {
memset ( out_ref , 0 , sizeof ( * out_ref ) ) ;
}
if ( store = = NULL | | cfg = = NULL | | concepts = = NULL | | s = = NULL | |
out_ref = = NULL ) {
return AMDUATD_REF_ERR_INTERNAL ;
}
if ( amduatd_decode_ref_hex_str ( s , len , out_ref ) ) {
return AMDUATD_REF_OK ;
}
2025-12-22 19:10:14 +01:00
if ( ! amduatd_copy_json_str ( s , len , & tmp ) ) {
2025-12-22 21:03:00 +01:00
return AMDUATD_REF_ERR_INTERNAL ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_scope_name_cstr ( dcfg , tmp , & scoped_name ) ) {
2025-12-22 21:03:00 +01:00
free ( tmp ) ;
return AMDUATD_REF_ERR_INVALID ;
}
2026-01-23 22:28:56 +01:00
amduatd_log_scoped_name ( dcfg , tmp , scoped_name ) ;
2025-12-22 21:03:00 +01:00
memset ( & concept_ref , 0 , sizeof ( concept_ref ) ) ;
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_lookup_alias ( store ,
cfg ,
concepts ,
scoped_name ,
& concept_ref ) ) {
free ( scoped_name ) ;
2025-12-22 21:03:00 +01:00
free ( tmp ) ;
return AMDUATD_REF_ERR_NOT_FOUND ;
2025-12-22 19:10:14 +01:00
}
2026-01-23 22:28:56 +01:00
free ( scoped_name ) ;
2025-12-22 19:10:14 +01:00
free ( tmp ) ;
2025-12-22 21:03:00 +01:00
if ( ! amduatd_concepts_resolve_latest ( store , concepts , concept_ref , out_ref ) ) {
amduat_reference_free ( & concept_ref ) ;
return AMDUATD_REF_ERR_NOT_FOUND ;
}
amduat_reference_free ( & concept_ref ) ;
return AMDUATD_REF_OK ;
2025-12-22 19:10:14 +01:00
}
static bool amduatd_send_json_error ( int fd ,
int code ,
const char * reason ,
const char * msg ) {
amduatd_strbuf_t b ;
memset ( & b , 0 , sizeof ( b ) ) ;
if ( ! amduatd_strbuf_append_cstr ( & b , " { \" error \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & b , msg ! = NULL ? msg : " error " ) | |
! amduatd_strbuf_append_cstr ( & b , " \" } \n " ) ) {
amduatd_strbuf_free ( & b ) ;
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " , " error \n " ,
false ) ;
}
{
bool ok = amduatd_http_send_json ( fd , code , reason , b . data , false ) ;
amduatd_strbuf_free ( & b ) ;
return ok ;
}
}
2025-12-22 15:23:54 +01:00
static void amduatd_path_without_query ( const char * path ,
char * out ,
size_t cap ) {
const char * q = NULL ;
size_t n = 0 ;
if ( out = = NULL | | cap = = 0 ) {
return ;
}
out [ 0 ] = ' \0 ' ;
if ( path = = NULL ) {
return ;
}
q = strchr ( path , ' ? ' ) ;
n = q ! = NULL ? ( size_t ) ( q - path ) : strlen ( path ) ;
if ( n > = cap ) {
n = cap - 1 ;
}
memcpy ( out , path , n ) ;
out [ n ] = ' \0 ' ;
}
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 ) ;
if ( err ! = AMDUAT_ASL_STORE_OK ) {
return false ;
}
return true ;
}
2025-12-23 09:14:58 +01:00
typedef struct {
char * key_hex ;
char * type_hex ;
char * key_text ;
char * value_hex ;
} amduatd_seed_entry_t ;
static int amduatd_seed_entry_cmp ( const void * a , const void * b ) {
const amduatd_seed_entry_t * x = ( const amduatd_seed_entry_t * ) a ;
const amduatd_seed_entry_t * y = ( const amduatd_seed_entry_t * ) b ;
if ( x = = NULL | | y = = NULL ) {
return 0 ;
}
return strcmp ( x - > key_hex ! = NULL ? x - > key_hex : " " ,
y - > key_hex ! = NULL ? y - > key_hex : " " ) ;
}
2025-12-22 15:23:54 +01:00
2025-12-23 09:14:58 +01:00
static bool amduatd_seed_concept_if_missing (
amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
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 char * name ,
amduat_reference_t * out_concept_ref ) {
amduat_reference_t name_ref ;
amduat_reference_t concept_ref ;
amduat_reference_t edge_ref ;
2026-01-23 22:28:56 +01:00
char * scoped_name = NULL ;
bool ok = false ;
2025-12-22 15:23:54 +01:00
2025-12-23 09:14:58 +01:00
if ( out_concept_ref ! = NULL ) {
* out_concept_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
}
if ( store = = NULL | | cfg = = NULL | | concepts = = NULL | | name = = NULL | |
out_concept_ref = = NULL ) {
return false ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_scope_name_cstr ( dcfg , name , & scoped_name ) ) {
return false ;
}
if ( amduatd_concepts_lookup_alias ( store , cfg , concepts , scoped_name ,
2025-12-23 09:14:58 +01:00
out_concept_ref ) ) {
2026-01-23 22:28:56 +01:00
ok = true ;
goto seed_cleanup ;
2025-12-23 09:14:58 +01:00
}
memset ( & name_ref , 0 , sizeof ( name_ref ) ) ;
memset ( & concept_ref , 0 , sizeof ( concept_ref ) ) ;
memset ( & edge_ref , 0 , sizeof ( edge_ref ) ) ;
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_put_name_artifact ( store , scoped_name , & name_ref ) ) {
goto seed_cleanup ;
2025-12-23 09:14:58 +01:00
}
if ( ! amduatd_concepts_put_concept_id ( store , cfg , & concept_ref ) ) {
2026-01-23 22:28:56 +01:00
goto seed_cleanup ;
2025-12-23 09:14:58 +01:00
}
if ( ! amduatd_concepts_put_edge ( store ,
concepts ,
name_ref ,
concept_ref ,
concepts - > rel_aliases_ref ,
2026-01-23 20:55:41 +01:00
amduat_octets ( NULL , 0u ) ,
2025-12-23 09:14:58 +01:00
& edge_ref ) ) {
2026-01-23 22:28:56 +01:00
goto seed_cleanup ;
}
* out_concept_ref = concept_ref ;
ok = true ;
goto seed_cleanup ;
seed_cleanup :
if ( ! ok ) {
2025-12-23 09:14:58 +01:00
amduat_reference_free ( & concept_ref ) ;
}
amduat_reference_free ( & name_ref ) ;
amduat_reference_free ( & edge_ref ) ;
2026-01-23 22:28:56 +01:00
free ( scoped_name ) ;
return ok ;
2025-12-23 09:14:58 +01:00
}
static bool amduatd_seed_store_artifact ( amduat_asl_store_t * store ,
amduat_artifact_t artifact ,
amduat_reference_t * out_ref ) {
if ( out_ref ! = NULL ) {
* out_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
}
if ( store = = NULL | | out_ref = = NULL ) {
return false ;
}
if ( amduat_asl_store_put ( store , artifact , out_ref ) ! = AMDUAT_ASL_STORE_OK ) {
return false ;
}
return true ;
}
static bool amduatd_seed_pel_identity_program (
amduat_asl_store_t * store ,
amduat_reference_t * out_program_ref ) {
amduat_pel_program_t program ;
amduat_pel_node_t node ;
amduat_pel_dag_input_t inputs [ 1 ] ;
amduat_pel_root_ref_t root ;
amduat_octets_t program_bytes ;
amduat_artifact_t artifact ;
if ( out_program_ref ! = NULL ) {
* out_program_ref = amduat_reference ( 0u , amduat_octets ( NULL , 0u ) ) ;
}
if ( store = = NULL | | out_program_ref = = NULL ) {
return false ;
}
memset ( & program , 0 , sizeof ( program ) ) ;
memset ( & node , 0 , sizeof ( node ) ) ;
memset ( inputs , 0 , sizeof ( inputs ) ) ;
memset ( & root , 0 , sizeof ( root ) ) ;
program_bytes = amduat_octets ( NULL , 0u ) ;
inputs [ 0 ] . kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL ;
inputs [ 0 ] . value . external . input_index = 0 ;
node . id = 1 ;
node . op . name = amduat_octets ( " pel.bytes.concat " ,
strlen ( " pel.bytes.concat " ) ) ;
node . op . version = 1 ;
node . inputs = inputs ;
node . inputs_len = 1 ;
node . params = amduat_octets ( NULL , 0u ) ;
root . node_id = 1 ;
root . output_index = 0 ;
program . nodes = & node ;
program . nodes_len = 1 ;
program . roots = & root ;
program . roots_len = 1 ;
if ( ! amduat_enc_pel_program_dag_encode_v1 ( & program , & program_bytes ) ) {
return false ;
}
artifact = amduat_artifact_with_type ( program_bytes ,
amduat_type_tag ( 0x00000101u ) ) ;
if ( ! amduatd_seed_store_artifact ( store , artifact , out_program_ref ) ) {
free ( ( void * ) program_bytes . data ) ;
return false ;
}
free ( ( void * ) program_bytes . data ) ;
return true ;
}
static bool amduatd_seed_materializes_if_missing (
amduat_asl_store_t * store ,
amduatd_concepts_t * concepts ,
amduat_reference_t concept_ref ,
amduat_reference_t target_ref ) {
amduat_reference_t latest ;
amduat_reference_t edge_ref ;
memset ( & latest , 0 , sizeof ( latest ) ) ;
if ( amduatd_concepts_resolve_latest ( store , concepts , concept_ref , & latest ) ) {
amduat_reference_free ( & latest ) ;
return true ;
}
if ( ! amduatd_concepts_put_edge ( store ,
concepts ,
concept_ref ,
target_ref ,
concepts - > rel_materializes_ref ,
2026-01-23 20:55:41 +01:00
amduat_octets ( NULL , 0u ) ,
2025-12-23 09:14:58 +01:00
& edge_ref ) ) {
return false ;
}
amduat_reference_free ( & edge_ref ) ;
return true ;
}
static bool amduatd_seed_ms_ui_state ( amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
2026-01-23 22:28:56 +01:00
amduatd_concepts_t * concepts ,
const amduatd_cfg_t * dcfg ) {
2025-12-23 09:14:58 +01:00
amduat_reference_t type_string_ref ;
amduat_reference_t type_ref_ref ;
amduat_reference_t key_title_ref ;
amduat_reference_t key_status_ref ;
amduat_reference_t key_latest_ref ;
amduat_reference_t schema_concept_ref ;
amduat_reference_t registry_concept_ref ;
amduat_reference_t identity_program_ref ;
amduat_reference_t schema_input_ref ;
amduat_reference_t registry_input_ref ;
amduat_reference_t schema_output_ref ;
amduat_reference_t registry_output_ref ;
amduat_reference_t seed_manifest_ref ;
amduat_reference_t seed_environment_ref ;
amduat_reference_t seed_executor_ref ;
amduat_reference_t receipt_ref ;
amduat_pel_run_result_t run_result ;
amduatd_seed_entry_t fields [ 3 ] ;
amduatd_seed_entry_t entries [ 3 ] ;
amduatd_strbuf_t b ;
amduat_artifact_t artifact ;
char * seed_latest_hex = NULL ;
size_t i ;
bool ok = false ;
memset ( & type_string_ref , 0 , sizeof ( type_string_ref ) ) ;
memset ( & type_ref_ref , 0 , sizeof ( type_ref_ref ) ) ;
memset ( & key_title_ref , 0 , sizeof ( key_title_ref ) ) ;
memset ( & key_status_ref , 0 , sizeof ( key_status_ref ) ) ;
memset ( & key_latest_ref , 0 , sizeof ( key_latest_ref ) ) ;
memset ( & schema_concept_ref , 0 , sizeof ( schema_concept_ref ) ) ;
memset ( & registry_concept_ref , 0 , sizeof ( registry_concept_ref ) ) ;
memset ( & identity_program_ref , 0 , sizeof ( identity_program_ref ) ) ;
memset ( & schema_input_ref , 0 , sizeof ( schema_input_ref ) ) ;
memset ( & registry_input_ref , 0 , sizeof ( registry_input_ref ) ) ;
memset ( & schema_output_ref , 0 , sizeof ( schema_output_ref ) ) ;
memset ( & registry_output_ref , 0 , sizeof ( registry_output_ref ) ) ;
memset ( & seed_manifest_ref , 0 , sizeof ( seed_manifest_ref ) ) ;
memset ( & seed_environment_ref , 0 , sizeof ( seed_environment_ref ) ) ;
memset ( & seed_executor_ref , 0 , sizeof ( seed_executor_ref ) ) ;
memset ( & receipt_ref , 0 , sizeof ( receipt_ref ) ) ;
memset ( & run_result , 0 , sizeof ( run_result ) ) ;
memset ( fields , 0 , sizeof ( fields ) ) ;
memset ( entries , 0 , sizeof ( entries ) ) ;
memset ( & b , 0 , sizeof ( b ) ) ;
memset ( & artifact , 0 , sizeof ( artifact ) ) ;
2026-01-23 22:28:56 +01:00
if ( ! amduatd_seed_concept_if_missing ( store , cfg , concepts , dcfg ,
" ms.type.string " ,
2025-12-23 09:14:58 +01:00
& type_string_ref ) | |
2026-01-23 22:28:56 +01:00
! amduatd_seed_concept_if_missing ( store , cfg , concepts , dcfg ,
" ms.type.ref " ,
2025-12-23 09:14:58 +01:00
& type_ref_ref ) | |
2026-01-23 22:28:56 +01:00
! amduatd_seed_concept_if_missing ( store , cfg , concepts , dcfg ,
" ms.ui.field.title " ,
2025-12-23 09:14:58 +01:00
& key_title_ref ) | |
2026-01-23 22:28:56 +01:00
! amduatd_seed_concept_if_missing ( store , cfg , concepts , dcfg ,
" ms.ui.field.status " ,
2025-12-23 09:14:58 +01:00
& key_status_ref ) | |
2026-01-23 22:28:56 +01:00
! amduatd_seed_concept_if_missing ( store , cfg , concepts , dcfg ,
2025-12-23 09:14:58 +01:00
" ms.ui.field.latest_ref " ,
& key_latest_ref ) | |
2026-01-23 22:28:56 +01:00
! amduatd_seed_concept_if_missing ( store , cfg , concepts , dcfg ,
2025-12-23 09:14:58 +01:00
" ms.ui.state.schema.v1 " ,
& schema_concept_ref ) | |
2026-01-23 22:28:56 +01:00
! amduatd_seed_concept_if_missing ( store , cfg , concepts , dcfg ,
2025-12-23 09:14:58 +01:00
" ms.ui.state.registry.v1 " ,
& registry_concept_ref ) ) {
goto seed_cleanup ;
}
if ( ! amduatd_seed_pel_identity_program ( store , & identity_program_ref ) ) {
goto seed_cleanup ;
}
{
amduat_reference_t latest_schema ;
amduat_reference_t latest_registry ;
bool have_schema = false ;
bool have_registry = false ;
bool schema_ok = false ;
bool registry_ok = false ;
memset ( & latest_schema , 0 , sizeof ( latest_schema ) ) ;
memset ( & latest_registry , 0 , sizeof ( latest_registry ) ) ;
have_schema = amduatd_concepts_resolve_latest ( store ,
concepts ,
schema_concept_ref ,
& latest_schema ) ;
have_registry = amduatd_concepts_resolve_latest ( store ,
concepts ,
registry_concept_ref ,
& latest_registry ) ;
if ( have_schema & & have_registry ) {
amduat_artifact_t schema_artifact ;
amduat_artifact_t registry_artifact ;
amduat_asl_store_error_t err ;
memset ( & schema_artifact , 0 , sizeof ( schema_artifact ) ) ;
memset ( & registry_artifact , 0 , sizeof ( registry_artifact ) ) ;
err = amduat_asl_store_get ( store , latest_schema , & schema_artifact ) ;
if ( err = = AMDUAT_ASL_STORE_OK & &
schema_artifact . bytes . len ! = 0 & &
schema_artifact . bytes . data ! = NULL & &
amduatd_bytes_contains ( schema_artifact . bytes . data ,
schema_artifact . bytes . len ,
" \" key_text \" " ) ) {
schema_ok = true ;
}
err = amduat_asl_store_get ( store , latest_registry , & registry_artifact ) ;
if ( err = = AMDUAT_ASL_STORE_OK & &
registry_artifact . bytes . len ! = 0 & &
registry_artifact . bytes . data ! = NULL & &
amduatd_bytes_contains ( registry_artifact . bytes . data ,
registry_artifact . bytes . len ,
" \" value_text \" " ) ) {
registry_ok = true ;
}
amduat_asl_artifact_free ( & schema_artifact ) ;
amduat_asl_artifact_free ( & registry_artifact ) ;
}
amduat_reference_free ( & latest_schema ) ;
amduat_reference_free ( & latest_registry ) ;
if ( have_schema & & have_registry & & schema_ok & & registry_ok ) {
ok = true ;
goto seed_cleanup ;
}
}
{
const char * payload = " { \" seed \" : \" manifest \" } " ;
artifact = amduat_artifact ( amduat_octets ( payload , strlen ( payload ) ) ) ;
if ( ! amduatd_seed_store_artifact ( store , artifact , & seed_manifest_ref ) ) {
goto seed_cleanup ;
}
}
{
const char * payload = " { \" seed \" : \" environment \" } " ;
artifact = amduat_artifact ( amduat_octets ( payload , strlen ( payload ) ) ) ;
if ( ! amduatd_seed_store_artifact ( store , artifact , & seed_environment_ref ) ) {
goto seed_cleanup ;
}
}
{
const char * payload = " { \" seed \" : \" executor \" } " ;
artifact = amduat_artifact ( amduat_octets ( payload , strlen ( payload ) ) ) ;
if ( ! amduatd_seed_store_artifact ( store , artifact , & seed_executor_ref ) ) {
goto seed_cleanup ;
}
}
{
amduat_reference_t hello_ref ;
amduat_reference_t title_ref ;
amduat_reference_t status_ref ;
char * hello_hex = NULL ;
const char * payload = " hello " ;
memset ( & hello_ref , 0 , sizeof ( hello_ref ) ) ;
memset ( & title_ref , 0 , sizeof ( title_ref ) ) ;
memset ( & status_ref , 0 , sizeof ( status_ref ) ) ;
artifact = amduat_artifact ( amduat_octets ( payload , strlen ( payload ) ) ) ;
if ( ! amduatd_seed_store_artifact ( store , artifact , & hello_ref ) ) {
goto seed_cleanup ;
}
if ( ! amduat_asl_ref_encode_hex ( hello_ref , & hello_hex ) ) {
goto seed_cleanup ;
}
seed_latest_hex = hello_hex ;
artifact = amduat_artifact ( amduat_octets ( " Amduat UI " ,
strlen ( " Amduat UI " ) ) ) ;
if ( ! amduatd_seed_store_artifact ( store , artifact , & title_ref ) ) {
free ( hello_hex ) ;
goto seed_cleanup ;
}
artifact = amduat_artifact ( amduat_octets ( " ready " , strlen ( " ready " ) ) ) ;
if ( ! amduatd_seed_store_artifact ( store , artifact , & status_ref ) ) {
free ( hello_hex ) ;
goto seed_cleanup ;
}
fields [ 0 ] . key_hex = NULL ;
fields [ 1 ] . key_hex = NULL ;
fields [ 2 ] . key_hex = NULL ;
if ( ! amduat_asl_ref_encode_hex ( key_title_ref , & fields [ 0 ] . key_hex ) | |
! amduat_asl_ref_encode_hex ( key_status_ref , & fields [ 1 ] . key_hex ) | |
! amduat_asl_ref_encode_hex ( key_latest_ref , & fields [ 2 ] . key_hex ) ) {
goto seed_cleanup ;
}
fields [ 0 ] . type_hex = strdup ( " ms.type.string " ) ;
fields [ 1 ] . type_hex = strdup ( " ms.type.string " ) ;
fields [ 2 ] . type_hex = strdup ( " ms.type.ref " ) ;
if ( fields [ 0 ] . type_hex = = NULL | | fields [ 1 ] . type_hex = = NULL | |
fields [ 2 ] . type_hex = = NULL ) {
goto seed_cleanup ;
}
fields [ 0 ] . key_text = strdup ( " title " ) ;
fields [ 1 ] . key_text = strdup ( " status " ) ;
fields [ 2 ] . key_text = strdup ( " latest_ref " ) ;
if ( fields [ 0 ] . key_text = = NULL | | fields [ 1 ] . key_text = = NULL | |
fields [ 2 ] . key_text = = NULL ) {
goto seed_cleanup ;
}
entries [ 0 ] . key_hex = fields [ 0 ] . key_hex ;
entries [ 1 ] . key_hex = fields [ 1 ] . key_hex ;
entries [ 2 ] . key_hex = fields [ 2 ] . key_hex ;
entries [ 0 ] . value_hex = strdup ( " Amduat UI " ) ;
entries [ 1 ] . value_hex = strdup ( " ready " ) ;
entries [ 2 ] . value_hex = hello_hex ;
if ( entries [ 0 ] . value_hex = = NULL | | entries [ 1 ] . value_hex = = NULL | |
entries [ 2 ] . value_hex = = NULL ) {
goto seed_cleanup ;
}
hello_hex = NULL ;
seed_latest_hex = NULL ;
qsort ( fields , 3 , sizeof ( fields [ 0 ] ) , amduatd_seed_entry_cmp ) ;
qsort ( entries , 3 , sizeof ( entries [ 0 ] ) , amduatd_seed_entry_cmp ) ;
if ( ! amduatd_strbuf_append_cstr ( & b ,
" { \" schema_version \" :1, \" fields \" :[ " ) ) {
goto seed_cleanup ;
}
for ( i = 0 ; i < 3 ; + + i ) {
if ( i ! = 0 & & ! amduatd_strbuf_append_char ( & b , ' , ' ) ) {
goto seed_cleanup ;
}
if ( ! amduatd_strbuf_append_cstr ( & b , " { \" key_ref \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & b , fields [ i ] . key_hex ) | |
! amduatd_strbuf_append_cstr ( & b , " \" , \" key_text \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & b , fields [ i ] . key_text ) | |
! amduatd_strbuf_append_cstr ( & b , " \" , \" type_ref \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & b , fields [ i ] . type_hex ) | |
! amduatd_strbuf_append_cstr ( & b , " \" } " ) ) {
goto seed_cleanup ;
}
}
if ( ! amduatd_strbuf_append_cstr ( & b , " ]} " ) ) {
goto seed_cleanup ;
}
artifact = amduat_artifact ( amduat_octets ( b . data , b . len ) ) ;
if ( ! amduatd_seed_store_artifact ( store , artifact , & schema_input_ref ) ) {
goto seed_cleanup ;
}
amduatd_strbuf_free ( & b ) ;
memset ( & b , 0 , sizeof ( b ) ) ;
if ( ! amduatd_strbuf_append_cstr ( & b ,
" { \" registry_version \" :1, \" entries \" :[ " ) ) {
goto seed_cleanup ;
}
for ( i = 0 ; i < 3 ; + + i ) {
if ( i ! = 0 & & ! amduatd_strbuf_append_char ( & b , ' , ' ) ) {
goto seed_cleanup ;
}
if ( ! amduatd_strbuf_append_cstr ( & b , " { \" key_ref \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & b , entries [ i ] . key_hex ) | |
! amduatd_strbuf_append_cstr ( & b ,
i = = 2 ? " \" , \" value_ref \" : \" "
: " \" , \" value_text \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & b , entries [ i ] . value_hex ) | |
! amduatd_strbuf_append_cstr ( & b , " \" } " ) ) {
goto seed_cleanup ;
}
}
if ( ! amduatd_strbuf_append_cstr ( & b , " ]} " ) ) {
goto seed_cleanup ;
}
artifact = amduat_artifact ( amduat_octets ( b . data , b . len ) ) ;
if ( ! amduatd_seed_store_artifact ( store , artifact , & registry_input_ref ) ) {
goto seed_cleanup ;
}
}
{
amduat_reference_t inputs [ 1 ] ;
amduat_reference_t scheme_ref = amduat_pel_program_dag_scheme_ref ( ) ;
amduat_artifact_t receipt_artifact ;
amduat_reference_t edge_ref ;
amduat_octets_t evaluator_id = amduat_octets ( " amduatd-seed " ,
strlen ( " amduatd-seed " ) ) ;
inputs [ 0 ] = schema_input_ref ;
if ( ! amduat_pel_surf_run_with_result ( store ,
scheme_ref ,
identity_program_ref ,
inputs ,
1 ,
false ,
amduat_reference ( 0u ,
amduat_octets ( NULL , 0u ) ) ,
& run_result ) ) {
goto seed_cleanup ;
}
if ( ! run_result . has_result_value | | run_result . output_refs_len ! = 1 ) {
goto seed_cleanup ;
}
if ( ! amduat_reference_clone ( run_result . output_refs [ 0 ] ,
& schema_output_ref ) ) {
goto seed_cleanup ;
}
if ( ! amduat_fer1_receipt_from_pel_result ( & run_result . result_value ,
seed_manifest_ref ,
seed_environment_ref ,
evaluator_id ,
seed_executor_ref ,
false ,
amduat_reference ( 0u ,
amduat_octets ( NULL , 0u ) ) ,
amduat_octets ( NULL , 0u ) ,
0 ,
0 ,
& receipt_artifact ) ) {
goto seed_cleanup ;
}
if ( ! amduatd_seed_store_artifact ( store , receipt_artifact , & receipt_ref ) ) {
amduat_asl_artifact_free ( & receipt_artifact ) ;
goto seed_cleanup ;
}
amduat_asl_artifact_free ( & receipt_artifact ) ;
if ( ! amduatd_concepts_put_edge ( store ,
concepts ,
schema_output_ref ,
receipt_ref ,
concepts - > rel_has_provenance_ref ,
2026-01-23 20:55:41 +01:00
amduat_octets ( NULL , 0u ) ,
2025-12-23 09:14:58 +01:00
& edge_ref ) ) {
goto seed_cleanup ;
}
amduat_reference_free ( & edge_ref ) ;
amduat_reference_free ( & receipt_ref ) ;
amduat_enc_pel1_result_free ( & run_result . result_value ) ;
amduat_pel_surf_free_refs ( run_result . output_refs , run_result . output_refs_len ) ;
amduat_pel_surf_free_ref ( & run_result . result_ref ) ;
memset ( & run_result , 0 , sizeof ( run_result ) ) ;
}
{
amduat_reference_t inputs [ 1 ] ;
amduat_reference_t scheme_ref = amduat_pel_program_dag_scheme_ref ( ) ;
amduat_artifact_t receipt_artifact ;
amduat_reference_t edge_ref ;
amduat_octets_t evaluator_id = amduat_octets ( " amduatd-seed " ,
strlen ( " amduatd-seed " ) ) ;
inputs [ 0 ] = registry_input_ref ;
if ( ! amduat_pel_surf_run_with_result ( store ,
scheme_ref ,
identity_program_ref ,
inputs ,
1 ,
false ,
amduat_reference ( 0u ,
amduat_octets ( NULL , 0u ) ) ,
& run_result ) ) {
goto seed_cleanup ;
}
if ( ! run_result . has_result_value | | run_result . output_refs_len ! = 1 ) {
goto seed_cleanup ;
}
if ( ! amduat_reference_clone ( run_result . output_refs [ 0 ] ,
& registry_output_ref ) ) {
goto seed_cleanup ;
}
if ( ! amduat_fer1_receipt_from_pel_result ( & run_result . result_value ,
seed_manifest_ref ,
seed_environment_ref ,
evaluator_id ,
seed_executor_ref ,
false ,
amduat_reference ( 0u ,
amduat_octets ( NULL , 0u ) ) ,
amduat_octets ( NULL , 0u ) ,
0 ,
0 ,
& receipt_artifact ) ) {
goto seed_cleanup ;
}
if ( ! amduatd_seed_store_artifact ( store , receipt_artifact , & receipt_ref ) ) {
amduat_asl_artifact_free ( & receipt_artifact ) ;
goto seed_cleanup ;
}
amduat_asl_artifact_free ( & receipt_artifact ) ;
if ( ! amduatd_concepts_put_edge ( store ,
concepts ,
registry_output_ref ,
receipt_ref ,
concepts - > rel_has_provenance_ref ,
2026-01-23 20:55:41 +01:00
amduat_octets ( NULL , 0u ) ,
2025-12-23 09:14:58 +01:00
& edge_ref ) ) {
goto seed_cleanup ;
}
amduat_reference_free ( & edge_ref ) ;
amduat_reference_free ( & receipt_ref ) ;
amduat_enc_pel1_result_free ( & run_result . result_value ) ;
amduat_pel_surf_free_refs ( run_result . output_refs , run_result . output_refs_len ) ;
amduat_pel_surf_free_ref ( & run_result . result_ref ) ;
memset ( & run_result , 0 , sizeof ( run_result ) ) ;
}
if ( ! amduatd_seed_materializes_if_missing ( store , concepts ,
schema_concept_ref ,
schema_output_ref ) | |
! amduatd_seed_materializes_if_missing ( store , concepts ,
registry_concept_ref ,
registry_output_ref ) ) {
goto seed_cleanup ;
}
ok = true ;
seed_cleanup :
if ( run_result . has_result_value ) {
amduat_enc_pel1_result_free ( & run_result . result_value ) ;
}
if ( run_result . output_refs ! = NULL ) {
amduat_pel_surf_free_refs ( run_result . output_refs , run_result . output_refs_len ) ;
}
amduat_pel_surf_free_ref ( & run_result . result_ref ) ;
amduat_reference_free ( & type_string_ref ) ;
amduat_reference_free ( & type_ref_ref ) ;
amduat_reference_free ( & key_title_ref ) ;
amduat_reference_free ( & key_status_ref ) ;
amduat_reference_free ( & key_latest_ref ) ;
amduat_reference_free ( & schema_concept_ref ) ;
amduat_reference_free ( & registry_concept_ref ) ;
amduat_reference_free ( & identity_program_ref ) ;
amduat_reference_free ( & schema_input_ref ) ;
amduat_reference_free ( & registry_input_ref ) ;
amduat_reference_free ( & schema_output_ref ) ;
amduat_reference_free ( & registry_output_ref ) ;
amduat_reference_free ( & seed_manifest_ref ) ;
amduat_reference_free ( & seed_environment_ref ) ;
amduat_reference_free ( & seed_executor_ref ) ;
for ( i = 0 ; i < 3 ; + + i ) {
free ( fields [ i ] . key_hex ) ;
free ( fields [ i ] . type_hex ) ;
free ( fields [ i ] . key_text ) ;
free ( entries [ i ] . value_hex ) ;
}
amduatd_strbuf_free ( & b ) ;
free ( seed_latest_hex ) ;
return ok ;
}
static bool amduatd_handle_get_artifact ( int fd ,
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 ) ;
}
}
if ( ! amduat_asl_index_current_state ( store , & state ) ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" store error \n " , false ) ;
}
if ( amduat_asl_log_scan ( store , & records , & record_count ) ! =
AMDUAT_ASL_STORE_OK ) {
return amduatd_http_send_text ( fd , 500 , " Internal Server Error " ,
" log scan error \n " , false ) ;
}
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 ,
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 ;
}
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-22 21:03:00 +01:00
static bool amduatd_handle_post_concepts ( int fd ,
amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
amduatd_concepts_t * concepts ,
2026-01-23 22:28:56 +01:00
const amduatd_cfg_t * dcfg ,
2025-12-22 21:03:00 +01:00
const amduatd_http_req_t * req ) {
uint8_t * body = NULL ;
const char * p = NULL ;
const char * end = NULL ;
char * name = NULL ;
bool have_name = false ;
bool have_ref = false ;
amduat_reference_t target_ref ;
amduat_reference_t name_ref ;
amduat_reference_t concept_ref ;
amduat_reference_t edge_ref ;
char * concept_hex = NULL ;
2026-01-23 22:28:56 +01:00
char * scoped_name = NULL ;
2025-12-22 21:03:00 +01:00
char json [ 4096 ] ;
bool ok = false ;
2026-01-23 20:55:41 +01:00
amduat_octets_t actor = amduat_octets ( NULL , 0u ) ;
2025-12-22 21:03:00 +01:00
memset ( & target_ref , 0 , sizeof ( target_ref ) ) ;
memset ( & name_ref , 0 , sizeof ( name_ref ) ) ;
memset ( & concept_ref , 0 , sizeof ( concept_ref ) ) ;
memset ( & edge_ref , 0 , sizeof ( edge_ref ) ) ;
if ( store = = NULL | | cfg = = NULL | | concepts = = NULL | | req = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
2026-01-23 20:55:41 +01:00
if ( req - > has_actor ) {
actor = req - > actor ;
}
2025-12-22 21:03:00 +01:00
if ( req - > content_length = = 0 ) {
return amduatd_send_json_error ( fd , 400 , " Bad Request " , " missing body " ) ;
}
if ( req - > content_length > ( 256u * 1024u ) ) {
return amduatd_send_json_error ( fd , 413 , " Payload Too Large " ,
" payload too large " ) ;
}
body = ( uint8_t * ) malloc ( req - > content_length ) ;
if ( body = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
}
if ( ! amduatd_read_exact ( fd , body , req - > content_length ) ) {
free ( body ) ;
return false ;
}
p = ( const char * ) body ;
end = ( const char * ) body + req - > content_length ;
if ( ! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto concepts_cleanup ;
}
for ( ; ; ) {
const char * key = NULL ;
size_t key_len = 0 ;
const char * sv = NULL ;
size_t sv_len = 0 ;
const char * cur = NULL ;
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' } ' ) {
p = cur + 1 ;
break ;
}
if ( ! amduatd_json_parse_string_noesc ( & p , end , & key , & key_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto concepts_cleanup ;
}
if ( key_len = = strlen ( " name " ) & & memcmp ( key , " name " , key_len ) = = 0 ) {
if ( have_name | | ! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) | |
! amduatd_copy_json_str ( sv , sv_len , & name ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid name " ) ;
goto concepts_cleanup ;
}
have_name = true ;
} else if ( key_len = = strlen ( " ref " ) & & memcmp ( key , " ref " , key_len ) = = 0 ) {
if ( have_ref | | ! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) | |
! amduatd_decode_ref_hex_str ( sv , sv_len , & target_ref ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid ref " ) ;
goto concepts_cleanup ;
}
have_ref = true ;
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto concepts_cleanup ;
}
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' , ' ) {
p = cur + 1 ;
continue ;
}
if ( cur < end & & * cur = = ' } ' ) {
p = cur + 1 ;
break ;
}
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto concepts_cleanup ;
}
if ( ! have_name ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " missing name " ) ;
goto concepts_cleanup ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_scope_name_cstr ( dcfg , name , & scoped_name ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid name " ) ;
goto concepts_cleanup ;
}
amduatd_log_scoped_name ( dcfg , name , scoped_name ) ;
if ( ! amduatd_concepts_put_name_artifact ( store , scoped_name , & name_ref ) ) {
2025-12-22 21:03:00 +01:00
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" store error " ) ;
goto concepts_cleanup ;
}
{
amduat_reference_t existing ;
memset ( & existing , 0 , sizeof ( existing ) ) ;
2026-01-23 22:28:56 +01:00
if ( amduatd_concepts_lookup_alias ( store ,
cfg ,
concepts ,
scoped_name ,
& existing ) ) {
2025-12-22 21:03:00 +01:00
amduat_reference_free ( & existing ) ;
ok = amduatd_send_json_error ( fd , 409 , " Conflict " , " name exists " ) ;
goto concepts_cleanup ;
}
}
if ( ! amduatd_concepts_put_concept_id ( store , cfg , & concept_ref ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" store error " ) ;
goto concepts_cleanup ;
}
if ( ! amduatd_concepts_put_edge ( store ,
concepts ,
name_ref ,
concept_ref ,
concepts - > rel_aliases_ref ,
2026-01-23 20:55:41 +01:00
actor ,
2025-12-22 21:03:00 +01:00
& edge_ref ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" store error " ) ;
goto concepts_cleanup ;
}
if ( have_ref ) {
if ( ! amduatd_concepts_put_edge ( store ,
concepts ,
concept_ref ,
target_ref ,
concepts - > rel_materializes_ref ,
2026-01-23 20:55:41 +01:00
actor ,
2025-12-22 21:03:00 +01:00
& edge_ref ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" store error " ) ;
goto concepts_cleanup ;
}
}
if ( ! amduat_asl_ref_encode_hex ( concept_ref , & concept_hex ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
goto concepts_cleanup ;
}
{
int n = snprintf ( json ,
sizeof ( json ) ,
" { "
" \" name \" : \" %s \" , "
" \" concept_ref \" : \" %s \" "
" } \n " ,
name ,
concept_hex ) ;
free ( concept_hex ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( json ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " error " ) ;
goto concepts_cleanup ;
}
ok = amduatd_http_send_json ( fd , 200 , " OK " , json , false ) ;
}
concepts_cleanup :
free ( body ) ;
free ( name ) ;
2026-01-23 22:28:56 +01:00
free ( scoped_name ) ;
2025-12-22 21:03:00 +01:00
amduat_reference_free ( & target_ref ) ;
amduat_reference_free ( & name_ref ) ;
amduat_reference_free ( & concept_ref ) ;
amduat_reference_free ( & edge_ref ) ;
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 21:03:00 +01:00
static bool amduatd_path_extract_name ( const char * path ,
const char * prefix ,
char * out ,
size_t cap ) {
size_t plen ;
const char * p ;
size_t len ;
if ( out ! = NULL & & cap ! = 0 ) {
out [ 0 ] = ' \0 ' ;
}
if ( path = = NULL | | prefix = = NULL | | out = = NULL | | cap = = 0 ) {
return false ;
}
plen = strlen ( prefix ) ;
if ( strncmp ( path , prefix , plen ) ! = 0 ) {
return false ;
}
p = path + plen ;
if ( * p = = ' \0 ' ) {
return false ;
}
len = strlen ( p ) ;
if ( len > = cap ) {
len = cap - 1 ;
}
memcpy ( out , p , len ) ;
out [ len ] = ' \0 ' ;
return true ;
}
static bool amduatd_handle_post_concept_publish ( int fd ,
amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
amduatd_concepts_t * concepts ,
2026-01-23 22:28:56 +01:00
const amduatd_cfg_t * dcfg ,
2025-12-22 21:03:00 +01:00
const char * name ,
const amduatd_http_req_t * req ) {
uint8_t * body = NULL ;
const char * p = NULL ;
const char * end = NULL ;
amduat_reference_t target_ref ;
amduat_reference_t concept_ref ;
amduat_reference_t edge_ref ;
bool have_ref = false ;
bool ok = false ;
2026-01-23 20:55:41 +01:00
amduat_octets_t actor = amduat_octets ( NULL , 0u ) ;
2026-01-23 22:28:56 +01:00
char * scoped_name = NULL ;
2025-12-22 21:03:00 +01:00
memset ( & target_ref , 0 , sizeof ( target_ref ) ) ;
memset ( & concept_ref , 0 , sizeof ( concept_ref ) ) ;
memset ( & edge_ref , 0 , sizeof ( edge_ref ) ) ;
if ( store = = NULL | | cfg = = NULL | | concepts = = NULL | | name = = NULL | |
req = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
2026-01-23 20:55:41 +01:00
if ( req - > has_actor ) {
actor = req - > actor ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_scope_name_cstr ( dcfg , name , & scoped_name ) ) {
2025-12-22 21:03:00 +01:00
return amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid name " ) ;
}
2026-01-23 22:28:56 +01:00
amduatd_log_scoped_name ( dcfg , name , scoped_name ) ;
2025-12-22 21:03:00 +01:00
if ( req - > content_length = = 0 ) {
2026-01-23 22:28:56 +01:00
free ( scoped_name ) ;
2025-12-22 21:03:00 +01:00
return amduatd_send_json_error ( fd , 400 , " Bad Request " , " missing body " ) ;
}
if ( req - > content_length > ( 256u * 1024u ) ) {
2026-01-23 22:28:56 +01:00
free ( scoped_name ) ;
2025-12-22 21:03:00 +01:00
return amduatd_send_json_error ( fd , 413 , " Payload Too Large " ,
" payload too large " ) ;
}
2026-01-23 22:28:56 +01:00
if ( ! amduatd_concepts_lookup_alias ( store ,
cfg ,
concepts ,
scoped_name ,
& concept_ref ) ) {
free ( scoped_name ) ;
2025-12-22 21:03:00 +01:00
return amduatd_send_json_error ( fd , 404 , " Not Found " , " unknown concept " ) ;
}
2026-01-23 22:28:56 +01:00
free ( scoped_name ) ;
2025-12-22 21:03:00 +01:00
body = ( uint8_t * ) malloc ( req - > content_length ) ;
if ( body = = NULL ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto publish_cleanup ;
}
if ( ! amduatd_read_exact ( fd , body , req - > content_length ) ) {
free ( body ) ;
amduat_reference_free ( & concept_ref ) ;
return false ;
}
2026-01-23 23:08:41 +01:00
p = ( const char * ) body ;
end = ( const char * ) body + req - > content_length ;
if ( ! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto publish_cleanup ;
}
for ( ; ; ) {
const char * key = NULL ;
size_t key_len = 0 ;
const char * sv = NULL ;
size_t sv_len = 0 ;
const char * cur = NULL ;
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' } ' ) {
p = cur + 1 ;
break ;
}
if ( ! amduatd_json_parse_string_noesc ( & p , end , & key , & key_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto publish_cleanup ;
}
if ( key_len = = strlen ( " ref " ) & & memcmp ( key , " ref " , key_len ) = = 0 ) {
if ( have_ref | | ! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) | |
! amduatd_decode_ref_hex_str ( sv , sv_len , & target_ref ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid ref " ) ;
goto publish_cleanup ;
}
have_ref = true ;
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto publish_cleanup ;
}
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' , ' ) {
p = cur + 1 ;
continue ;
}
if ( cur < end & & * cur = = ' } ' ) {
p = cur + 1 ;
break ;
}
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
goto publish_cleanup ;
}
if ( ! have_ref ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " missing ref " ) ;
goto publish_cleanup ;
}
if ( ! amduatd_concepts_put_edge ( store ,
concepts ,
concept_ref ,
target_ref ,
concepts - > rel_materializes_ref ,
actor ,
& edge_ref ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" store error " ) ;
goto publish_cleanup ;
}
ok = amduatd_http_send_json ( fd , 200 , " OK " , " { \" ok \" :true} \n " , false ) ;
publish_cleanup :
free ( body ) ;
amduat_reference_free ( & target_ref ) ;
amduat_reference_free ( & concept_ref ) ;
amduat_reference_free ( & edge_ref ) ;
return ok ;
}
static bool amduatd_handle_get_resolve ( int fd ,
amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
const amduatd_concepts_t * concepts ,
const amduatd_cfg_t * dcfg ,
const char * name ) {
amduat_reference_t concept_ref ;
amduat_reference_t latest_ref ;
char * hex = NULL ;
char json [ 2048 ] ;
int n ;
char * scoped_name = NULL ;
memset ( & concept_ref , 0 , sizeof ( concept_ref ) ) ;
memset ( & latest_ref , 0 , sizeof ( latest_ref ) ) ;
if ( store = = NULL | | cfg = = NULL | | concepts = = NULL | | name = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
if ( ! amduatd_scope_name_cstr ( dcfg , name , & scoped_name ) ) {
return amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid name " ) ;
}
amduatd_log_scoped_name ( dcfg , name , scoped_name ) ;
if ( ! amduatd_concepts_lookup_alias ( store , cfg , concepts , scoped_name ,
& concept_ref ) ) {
free ( scoped_name ) ;
return amduatd_send_json_error ( fd , 404 , " Not Found " , " unknown concept " ) ;
}
free ( scoped_name ) ;
if ( ! amduatd_concepts_resolve_latest ( store , concepts , concept_ref ,
& latest_ref ) ) {
amduat_reference_free ( & concept_ref ) ;
return amduatd_send_json_error ( fd , 404 , " Not Found " , " no versions " ) ;
}
amduat_reference_free ( & concept_ref ) ;
if ( ! amduat_asl_ref_encode_hex ( latest_ref , & hex ) ) {
amduat_reference_free ( & latest_ref ) ;
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
}
amduat_reference_free ( & latest_ref ) ;
n = snprintf ( json , sizeof ( json ) , " { \" ref \" : \" %s \" } \n " , hex ) ;
free ( hex ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( json ) ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " error " ) ;
}
return amduatd_http_send_json ( fd , 200 , " OK " , json , false ) ;
}
static bool amduatd_handle_post_capabilities (
int fd ,
amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
const amduatd_concepts_t * concepts ,
const amduatd_cfg_t * dcfg ,
const amduatd_cap_state_t * cap ,
const amduatd_http_req_t * req ) {
uint8_t * body = NULL ;
const char * p = NULL ;
const char * end = NULL ;
bool have_kind = false ;
bool have_target = false ;
bool have_expiry = false ;
char * kind = NULL ;
uint64_t expiry_seconds = 0u ;
amduatd_cap_token_t token ;
bool target_have_ref = false ;
bool target_have_name = false ;
bool target_have_program_ref = false ;
bool target_have_inputs = false ;
bool target_have_params_ref = false ;
size_t input_refs_cap = 0u ;
uint8_t * token_bytes = NULL ;
size_t token_len = 0u ;
char * token_b64 = NULL ;
char * token_text = NULL ;
char token_hash [ 9 ] ;
char json [ 4096 ] ;
bool ok = false ;
const char * space = NULL ;
size_t space_len = 0u ;
time_t now ;
memset ( & token , 0 , sizeof ( token ) ) ;
memset ( token_hash , 0 , sizeof ( token_hash ) ) ;
if ( store = = NULL | | cfg = = NULL | | concepts = = NULL | | dcfg = = NULL | |
cap = = NULL | | req = = NULL ) {
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
if ( ! cap - > enabled ) {
return amduatd_send_json_error ( fd , 403 , " Forbidden " ,
" capabilities disabled " ) ;
}
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 cap_mint_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 cap_mint_cleanup ;
}
if ( key_len = = strlen ( " kind " ) & & memcmp ( key , " kind " , key_len ) = = 0 ) {
const char * sv = NULL ;
size_t sv_len = 0 ;
if ( have_kind | |
! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) | |
! amduatd_copy_json_str ( sv , sv_len , & kind ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid kind " ) ;
goto cap_mint_cleanup ;
}
have_kind = true ;
} else if ( key_len = = strlen ( " expiry_seconds " ) & &
memcmp ( key , " expiry_seconds " , key_len ) = = 0 ) {
if ( have_expiry | | ! amduatd_json_parse_u64 ( & p , end , & expiry_seconds ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid expiry_seconds " ) ;
goto cap_mint_cleanup ;
}
have_expiry = true ;
} else if ( key_len = = strlen ( " target " ) & &
memcmp ( key , " target " , key_len ) = = 0 ) {
if ( have_target | | ! amduatd_json_expect ( & p , end , ' { ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid target " ) ;
goto cap_mint_cleanup ;
}
for ( ; ; ) {
const char * tkey = NULL ;
size_t tkey_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 , & tkey , & tkey_len ) | |
! amduatd_json_expect ( & p , end , ' : ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid target " ) ;
goto cap_mint_cleanup ;
}
if ( tkey_len = = strlen ( " ref " ) & & memcmp ( tkey , " ref " , tkey_len ) = = 0 ) {
amduatd_ref_status_t st ;
if ( target_have_ref | |
! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid ref " ) ;
goto cap_mint_cleanup ;
}
st = amduatd_decode_ref_or_name_latest ( store ,
cfg ,
concepts ,
dcfg ,
sv ,
sv_len ,
& token . artifact_ref ) ;
if ( st ! = AMDUATD_REF_OK ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid ref " ) ;
goto cap_mint_cleanup ;
}
target_have_ref = true ;
} else if ( tkey_len = = strlen ( " name " ) & &
memcmp ( tkey , " name " , tkey_len ) = = 0 ) {
if ( target_have_name | |
! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) | |
! amduatd_copy_json_str ( sv , sv_len , & token . pointer_name ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid name " ) ;
goto cap_mint_cleanup ;
}
target_have_name = true ;
} else if ( tkey_len = = strlen ( " program_ref " ) & &
memcmp ( tkey , " program_ref " , tkey_len ) = = 0 ) {
amduatd_ref_status_t st ;
if ( target_have_program_ref | |
! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid program_ref " ) ;
goto cap_mint_cleanup ;
}
st = amduatd_decode_ref_or_name_latest ( store ,
cfg ,
concepts ,
dcfg ,
sv ,
sv_len ,
& token . program_ref ) ;
if ( st ! = AMDUATD_REF_OK ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid program_ref " ) ;
goto cap_mint_cleanup ;
}
target_have_program_ref = true ;
} else if ( tkey_len = = strlen ( " input_refs " ) & &
memcmp ( tkey , " input_refs " , tkey_len ) = = 0 ) {
if ( target_have_inputs | | ! amduatd_json_expect ( & p , end , ' [ ' ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input_refs " ) ;
goto cap_mint_cleanup ;
}
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
target_have_inputs = true ;
} else {
for ( ; ; ) {
amduat_reference_t ref ;
amduatd_ref_status_t st ;
if ( ! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input_refs " ) ;
goto cap_mint_cleanup ;
}
memset ( & ref , 0 , sizeof ( ref ) ) ;
st = amduatd_decode_ref_or_name_latest ( store ,
cfg ,
concepts ,
dcfg ,
sv ,
sv_len ,
& ref ) ;
if ( st ! = AMDUATD_REF_OK ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input_ref " ) ;
goto cap_mint_cleanup ;
}
if ( token . input_refs_len = = 0u ) {
input_refs_cap = 4u ;
token . input_refs = ( amduat_reference_t * ) calloc (
input_refs_cap , sizeof ( * token . input_refs ) ) ;
if ( token . input_refs = = NULL ) {
amduat_reference_free ( & ref ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" oom " ) ;
goto cap_mint_cleanup ;
}
token . input_refs_len = 0u ;
} else if ( token . input_refs_len = = input_refs_cap ) {
size_t next_cap = input_refs_cap * 2u ;
amduat_reference_t * next = ( amduat_reference_t * ) realloc (
token . input_refs , next_cap * sizeof ( * token . input_refs ) ) ;
if ( next = = NULL ) {
amduat_reference_free ( & ref ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" oom " ) ;
goto cap_mint_cleanup ;
}
token . input_refs = next ;
input_refs_cap = next_cap ;
}
token . input_refs [ token . input_refs_len + + ] = ref ;
target_have_inputs = true ;
cur = amduatd_json_skip_ws ( p , end ) ;
if ( cur < end & & * cur = = ' , ' ) {
p = cur + 1 ;
continue ;
}
if ( cur < end & & * cur = = ' ] ' ) {
p = cur + 1 ;
target_have_inputs = true ;
break ;
}
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid input_refs " ) ;
goto cap_mint_cleanup ;
}
}
} else if ( tkey_len = = strlen ( " params_ref " ) & &
memcmp ( tkey , " params_ref " , tkey_len ) = = 0 ) {
amduatd_ref_status_t st ;
if ( target_have_params_ref | |
! amduatd_json_parse_string_noesc ( & p , end , & sv , & sv_len ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid params_ref " ) ;
goto cap_mint_cleanup ;
}
st = amduatd_decode_ref_or_name_latest ( store ,
cfg ,
concepts ,
dcfg ,
sv ,
sv_len ,
& token . params_ref ) ;
if ( st ! = AMDUATD_REF_OK ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid params_ref " ) ;
goto cap_mint_cleanup ;
}
token . has_params_ref = true ;
target_have_params_ref = true ;
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid target " ) ;
goto cap_mint_cleanup ;
}
}
2025-12-22 21:03:00 +01:00
2026-01-23 23:08:41 +01:00
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 target " ) ;
goto cap_mint_cleanup ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 23:08:41 +01:00
have_target = true ;
2025-12-22 21:03:00 +01:00
} else {
if ( ! amduatd_json_skip_value ( & p , end , 0 ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid json " ) ;
2026-01-23 23:08:41 +01:00
goto cap_mint_cleanup ;
2025-12-22 21:03:00 +01:00
}
}
2026-01-23 23:08:41 +01:00
2025-12-22 21:03:00 +01:00
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 " ) ;
2026-01-23 23:08:41 +01:00
goto cap_mint_cleanup ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 23:08:41 +01:00
if ( ! have_kind | | ! have_target | | ! have_expiry ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing fields " ) ;
goto cap_mint_cleanup ;
}
if ( expiry_seconds = = 0u | | expiry_seconds > AMDUATD_CAP_MAX_EXPIRY_SECONDS ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" expiry_seconds out of range " ) ;
goto cap_mint_cleanup ;
}
now = time ( NULL ) ;
if ( now < 0 | |
expiry_seconds > ( uint64_t ) ( UINT64_MAX - ( uint64_t ) now ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" expiry_seconds out of range " ) ;
goto cap_mint_cleanup ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 23:08:41 +01:00
token . expiry_unix = ( uint64_t ) now + expiry_seconds ;
2025-12-22 21:03:00 +01:00
2026-01-23 23:08:41 +01:00
if ( strcmp ( kind , " artifact_ref " ) = = 0 ) {
token . kind = AMDUATD_CAP_KIND_ARTIFACT_REF ;
if ( token . artifact_ref . hash_id = = 0 | |
token . artifact_ref . digest . data = = NULL ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing ref " ) ;
goto cap_mint_cleanup ;
}
} else if ( strcmp ( kind , " pointer_name " ) = = 0 ) {
token . kind = AMDUATD_CAP_KIND_POINTER_NAME ;
if ( token . pointer_name = = NULL | |
! amduatd_pointer_name_scoped_for_space ( dcfg , token . pointer_name ) ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" invalid name " ) ;
goto cap_mint_cleanup ;
}
} else if ( strcmp ( kind , " pel_view " ) = = 0 ) {
token . kind = AMDUATD_CAP_KIND_PEL_VIEW ;
if ( token . program_ref . hash_id = = 0 | |
token . program_ref . digest . data = = NULL ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing program_ref " ) ;
goto cap_mint_cleanup ;
}
if ( ! target_have_inputs ) {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " ,
" missing input_refs " ) ;
goto cap_mint_cleanup ;
}
} else {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid kind " ) ;
goto cap_mint_cleanup ;
}
if ( dcfg - > space_enabled & & dcfg - > space_id ! = NULL ) {
space = dcfg - > space_id ;
space_len = strlen ( space ) ;
token . space = ( char * ) malloc ( space_len + 1u ) ;
if ( token . space = = NULL ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto cap_mint_cleanup ;
}
memcpy ( token . space , space , space_len ) ;
token . space [ space_len ] = ' \0 ' ;
token . space_len = space_len ;
}
if ( ! amduatd_cap_build_token ( & token ,
cap - > hmac_key ,
cap - > hmac_key_len ,
& token_bytes ,
& token_len ) ) {
2025-12-22 21:03:00 +01:00
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
2026-01-23 23:08:41 +01:00
" token encode failed " ) ;
goto cap_mint_cleanup ;
}
if ( ! amduatd_b64url_encode ( token_bytes , token_len , & token_b64 ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" token encode failed " ) ;
goto cap_mint_cleanup ;
}
token_text = ( char * ) malloc ( strlen ( token_b64 ) + 5u ) ;
if ( token_text = = NULL ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto cap_mint_cleanup ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 23:08:41 +01:00
snprintf ( token_text , strlen ( token_b64 ) + 5u , " cap_%s " , token_b64 ) ;
2025-12-22 21:03:00 +01:00
2026-01-23 23:08:41 +01:00
amduatd_cap_token_hash_prefix ( token_bytes , token_len , token_hash ) ;
{
char uid_buf [ 32 ] ;
if ( req - > has_uid ) {
snprintf ( uid_buf , sizeof ( uid_buf ) , " %u " , ( unsigned int ) req - > uid ) ;
} else {
strncpy ( uid_buf , " unknown " , sizeof ( uid_buf ) - 1u ) ;
uid_buf [ sizeof ( uid_buf ) - 1u ] = ' \0 ' ;
}
amduat_log ( AMDUAT_LOG_INFO ,
" cap mint uid=%s kind=%s expiry=%llu space=%s token=%s " ,
uid_buf ,
amduatd_cap_kind_name ( token . kind ) ,
( unsigned long long ) token . expiry_unix ,
token . space_len ! = 0u ? token . space : " none " ,
token_hash ) ;
}
2025-12-22 21:03:00 +01:00
2026-01-23 23:08:41 +01:00
{
int n = snprintf ( json , sizeof ( json ) , " { \" token \" : \" %s \" } \n " , token_text ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( json ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " error " ) ;
goto cap_mint_cleanup ;
}
ok = amduatd_http_send_json ( fd , 200 , " OK " , json , false ) ;
}
cap_mint_cleanup :
2025-12-22 21:03:00 +01:00
free ( body ) ;
2026-01-23 23:08:41 +01:00
free ( kind ) ;
free ( token_bytes ) ;
free ( token_b64 ) ;
free ( token_text ) ;
amduatd_cap_token_free ( & token ) ;
2025-12-22 21:03:00 +01:00
return ok ;
}
2026-01-23 23:08:41 +01:00
static bool amduatd_handle_get_cap_resolve (
int fd ,
amduat_asl_store_t * store ,
const amduat_asl_store_fs_config_t * cfg ,
const amduatd_concepts_t * concepts ,
const amduatd_cfg_t * dcfg ,
const amduatd_cap_state_t * cap ,
const char * root_path ,
const amduatd_http_req_t * req ) {
const char * cap_header = NULL ;
uint8_t * token_bytes = NULL ;
size_t token_len = 0u ;
amduatd_cap_token_t token ;
const char * reason = " invalid " ;
char token_hash [ 9 ] ;
bool ok = false ;
2025-12-22 21:03:00 +01:00
2026-01-23 23:08:41 +01:00
memset ( & token , 0 , sizeof ( token ) ) ;
memset ( token_hash , 0 , sizeof ( token_hash ) ) ;
2025-12-22 21:03:00 +01:00
2026-01-23 23:08:41 +01:00
if ( store = = NULL | | cfg = = NULL | | concepts = = NULL | | dcfg = = NULL | |
cap = = NULL | | req = = NULL | | root_path = = NULL ) {
2025-12-22 21:03:00 +01:00
return amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" internal error " ) ;
}
2026-01-23 23:08:41 +01:00
( void ) cfg ;
( void ) concepts ;
if ( ! cap - > enabled | | ! cap - > reads_enabled ) {
return amduatd_send_json_error ( fd , 403 , " Forbidden " ,
" cap reads disabled " ) ;
}
cap_header = req - > x_capability ;
if ( cap_header = = NULL | | cap_header [ 0 ] = = ' \0 ' ) {
amduat_log ( AMDUAT_LOG_INFO , " cap use fail reason=missing token=none " ) ;
return amduatd_send_json_error ( fd , 401 , " Unauthorized " ,
" missing capability " ) ;
}
if ( strncmp ( cap_header , " cap_ " , 4u ) ! = 0 ) {
amduat_log ( AMDUAT_LOG_INFO , " cap use fail reason=bad-prefix token=none " ) ;
return amduatd_send_json_error ( fd , 401 , " Unauthorized " ,
" invalid capability " ) ;
}
if ( ! amduatd_b64url_decode ( cap_header + 4u , & token_bytes , & token_len ) ) {
amduat_log ( AMDUAT_LOG_INFO , " cap use fail reason=bad-base64 token=none " ) ;
return amduatd_send_json_error ( fd , 401 , " Unauthorized " ,
" invalid capability " ) ;
}
amduatd_cap_token_hash_prefix ( token_bytes , token_len , token_hash ) ;
if ( ! amduatd_cap_verify_token ( cap , dcfg , token_bytes , token_len ,
& token , & reason ) ) {
amduat_log ( AMDUAT_LOG_INFO ,
" cap use fail reason=%s kind=%s space=%s token=%s " ,
reason ,
token . kind ! = 0u ? amduatd_cap_kind_name ( token . kind ) : " unknown " ,
token . space_len ! = 0u & & token . space ! = NULL ? token . space : " none " ,
token_hash ) ;
free ( token_bytes ) ;
amduatd_cap_token_free ( & token ) ;
return amduatd_send_json_error ( fd , 403 , " Forbidden " ,
" invalid capability " ) ;
}
amduat_log ( AMDUAT_LOG_INFO ,
" cap use ok kind=%s space=%s token=%s " ,
amduatd_cap_kind_name ( token . kind ) ,
token . space_len ! = 0u ? token . space : " none " ,
token_hash ) ;
if ( token . kind = = AMDUATD_CAP_KIND_ARTIFACT_REF ) {
char * ref_hex = NULL ;
char path [ 1024 ] ;
if ( ! amduat_asl_ref_encode_hex ( token . artifact_ref , & ref_hex ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
2025-12-22 21:03:00 +01:00
" encode error " ) ;
2026-01-23 23:08:41 +01:00
goto cap_resolve_cleanup ;
}
if ( snprintf ( path , sizeof ( path ) , " /v1/artifacts/%s " , ref_hex ) < = 0 | |
strlen ( path ) > = sizeof ( path ) ) {
free ( ref_hex ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " error " ) ;
goto cap_resolve_cleanup ;
}
ok = amduatd_handle_get_artifact ( fd , store , req , path , false ) ;
free ( ref_hex ) ;
goto cap_resolve_cleanup ;
} else if ( token . kind = = AMDUATD_CAP_KIND_POINTER_NAME ) {
amduat_asl_pointer_store_t pointer_store ;
amduat_reference_t ref ;
bool exists = false ;
char format [ 32 ] ;
memset ( & ref , 0 , sizeof ( ref ) ) ;
if ( ! amduat_asl_pointer_store_init ( & pointer_store , root_path ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" pointer store error " ) ;
goto cap_resolve_cleanup ;
}
if ( amduat_asl_pointer_get ( & pointer_store , token . pointer_name ,
& exists , & ref ) ! = AMDUAT_ASL_POINTER_OK ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" pointer read failed " ) ;
goto cap_resolve_cleanup ;
}
if ( ! exists ) {
ok = amduatd_send_json_error ( fd , 404 , " Not Found " , " not found " ) ;
goto cap_resolve_cleanup ;
}
memset ( format , 0 , sizeof ( format ) ) ;
if ( amduatd_query_param ( req - > path , " format " , format , sizeof ( format ) ) ! = NULL & &
strcmp ( format , " artifact " ) = = 0 ) {
char * ref_hex = NULL ;
char path [ 1024 ] ;
if ( ! amduat_asl_ref_encode_hex ( ref , & ref_hex ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
amduat_reference_free ( & ref ) ;
goto cap_resolve_cleanup ;
}
if ( snprintf ( path , sizeof ( path ) , " /v1/artifacts/%s " , ref_hex ) < = 0 | |
strlen ( path ) > = sizeof ( path ) ) {
free ( ref_hex ) ;
amduat_reference_free ( & ref ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" error " ) ;
goto cap_resolve_cleanup ;
}
ok = amduatd_handle_get_artifact ( fd , store , req , path , false ) ;
free ( ref_hex ) ;
amduat_reference_free ( & ref ) ;
goto cap_resolve_cleanup ;
} else {
char * ref_hex = NULL ;
char json_out [ 2048 ] ;
int n ;
if ( ! amduat_asl_ref_encode_hex ( ref , & ref_hex ) ) {
amduat_reference_free ( & ref ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
goto cap_resolve_cleanup ;
}
n = snprintf ( json_out , sizeof ( json_out ) ,
" { \" ref \" : \" %s \" } \n " , ref_hex ) ;
free ( ref_hex ) ;
amduat_reference_free ( & ref ) ;
if ( n < = 0 | | ( size_t ) n > = sizeof ( json_out ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" error " ) ;
goto cap_resolve_cleanup ;
}
ok = amduatd_http_send_json ( fd , 200 , " OK " , json_out , false ) ;
goto cap_resolve_cleanup ;
}
} else if ( token . kind = = AMDUATD_CAP_KIND_PEL_VIEW ) {
amduat_pel_run_result_t run_result ;
char * result_hex = NULL ;
const char * status = " UNKNOWN " ;
amduatd_strbuf_t resp ;
size_t i ;
memset ( & run_result , 0 , sizeof ( run_result ) ) ;
memset ( & resp , 0 , sizeof ( resp ) ) ;
if ( ! amduat_pel_surf_run_with_result (
store ,
amduat_pel_program_dag_scheme_ref ( ) ,
token . program_ref ,
token . input_refs ,
token . input_refs_len ,
token . has_params_ref ,
token . params_ref ,
& run_result ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" pel run failed " ) ;
goto cap_view_cleanup ;
}
if ( ! run_result . has_result_value ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" pel run failed " ) ;
goto cap_view_cleanup ;
}
status = amduat_format_pel_status_name (
run_result . result_value . core_result . status ) ;
if ( ! amduat_asl_ref_encode_hex ( run_result . result_ref , & result_hex ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
goto cap_view_cleanup ;
}
if ( ! amduatd_strbuf_append_cstr ( & resp , " { \" result_ref \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & resp , result_hex ) | |
! amduatd_strbuf_append_cstr ( & resp , " \" , \" output_refs \" :[ " ) ) {
free ( result_hex ) ;
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto cap_view_cleanup ;
}
free ( result_hex ) ;
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 ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " ,
" encode error " ) ;
goto cap_view_cleanup ;
}
if ( i ! = 0 ) {
( void ) amduatd_strbuf_append_char ( & resp , ' , ' ) ;
}
( void ) amduatd_strbuf_append_cstr ( & resp , " \" " ) ;
( void ) amduatd_strbuf_append_cstr ( & resp , out_hex ) ;
( void ) amduatd_strbuf_append_cstr ( & resp , " \" " ) ;
free ( out_hex ) ;
}
if ( ! amduatd_strbuf_append_cstr ( & resp , " ], \" status \" : \" " ) | |
! amduatd_strbuf_append_cstr ( & resp , status ) | |
! amduatd_strbuf_append_cstr ( & resp , " \" } \n " ) ) {
ok = amduatd_send_json_error ( fd , 500 , " Internal Server Error " , " oom " ) ;
goto cap_view_cleanup ;
}
ok = amduatd_http_send_json ( fd , 200 , " OK " , resp . data , false ) ;
goto cap_view_cleanup ;
cap_view_cleanup :
amduatd_strbuf_free ( & resp ) ;
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 ) ;
goto cap_resolve_cleanup ;
} else {
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid capability " ) ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 23:08:41 +01:00
cap_resolve_cleanup :
free ( token_bytes ) ;
amduatd_cap_token_free ( & token ) ;
return ok ;
2025-12-22 21:03:00 +01:00
}
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 ,
const amduatd_cap_state_t * cap_state ,
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 ;
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 ) ) ;
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 ;
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 ) {
ok = amduatd_handle_post_capabilities ( fd ,
store ,
cfg ,
concepts ,
dcfg ,
cap_state ,
& req ) ;
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-23 22:28:56 +01:00
ok = amduatd_handle_post_pel_run ( fd , store , cfg , concepts , dcfg , & 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-23 22:28:56 +01:00
ok = amduatd_handle_post_context_frames ( fd , store , cfg , concepts , dcfg ,
& req ) ;
2026-01-23 20:55:41 +01:00
goto conn_cleanup ;
2025-12-23 09:14:58 +01:00
}
2025-12-22 21:03:00 +01:00
if ( strcmp ( req . method , " POST " ) = = 0 & & strcmp ( no_query , " /v1/concepts " ) = = 0 ) {
2026-01-23 22:28:56 +01:00
ok = amduatd_handle_post_concepts ( fd , store , cfg , concepts , dcfg , & req ) ;
2026-01-23 20:55:41 +01:00
goto conn_cleanup ;
2025-12-22 21:03:00 +01:00
}
2025-12-22 21:13:26 +01:00
if ( strcmp ( req . method , " GET " ) = = 0 & & strcmp ( no_query , " /v1/concepts " ) = = 0 ) {
2026-01-23 22:28:56 +01:00
ok = amduatd_handle_get_concepts ( fd , store , concepts , dcfg ) ;
2026-01-23 20:55:41 +01:00
goto conn_cleanup ;
2025-12-22 21:13:26 +01:00
}
2025-12-23 09:14:58 +01:00
if ( strcmp ( req . method , " GET " ) = = 0 & & strcmp ( no_query , " /v1/relations " ) = = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_handle_get_relations ( fd , concepts ) ;
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
}
2025-12-22 21:13:26 +01:00
if ( strcmp ( req . method , " GET " ) = = 0 & & strncmp ( no_query , " /v1/concepts/ " , 13 ) = = 0 ) {
const char * name = no_query + 13 ;
if ( name [ 0 ] = = ' \0 ' ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " missing name " ) ;
goto conn_cleanup ;
2025-12-22 21:13:26 +01:00
}
2026-01-23 22:28:56 +01:00
ok = amduatd_handle_get_concept ( fd , store , cfg , concepts , dcfg , name ) ;
2026-01-23 20:55:41 +01:00
goto conn_cleanup ;
2025-12-22 21:13:26 +01:00
}
2025-12-22 21:03:00 +01:00
if ( strcmp ( req . method , " POST " ) = = 0 & &
strncmp ( no_query , " /v1/concepts/ " , 13 ) = = 0 & &
strstr ( no_query , " /publish " ) ! = NULL ) {
char name [ 256 ] ;
char * slash ;
if ( ! amduatd_path_extract_name ( no_query , " /v1/concepts/ " , name ,
sizeof ( name ) ) ) {
return amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid path " ) ;
}
slash = strstr ( name , " /publish " ) ;
if ( slash = = NULL | | strcmp ( slash , " /publish " ) ! = 0 ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " invalid path " ) ;
goto conn_cleanup ;
2025-12-22 21:03:00 +01:00
}
* slash = ' \0 ' ;
2026-01-23 22:28:56 +01:00
ok = amduatd_handle_post_concept_publish ( fd , store , cfg , concepts , dcfg ,
name ,
2026-01-23 20:55:41 +01:00
& req ) ;
goto conn_cleanup ;
2025-12-22 21:03:00 +01:00
}
if ( strcmp ( req . method , " GET " ) = = 0 & & strncmp ( no_query , " /v1/resolve/ " , 12 ) = = 0 ) {
const char * name = no_query + 12 ;
if ( name [ 0 ] = = ' \0 ' ) {
2026-01-23 20:55:41 +01:00
ok = amduatd_send_json_error ( fd , 400 , " Bad Request " , " missing name " ) ;
goto conn_cleanup ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 22:28:56 +01:00
ok = amduatd_handle_get_resolve ( fd , store , cfg , concepts , dcfg , name ) ;
2026-01-23 20:55:41 +01:00
goto conn_cleanup ;
2025-12-22 21:03:00 +01:00
}
2026-01-23 23:08:41 +01:00
if ( strcmp ( req . method , " GET " ) = = 0 & &
strcmp ( no_query , " /v1/cap/resolve " ) = = 0 ) {
ok = amduatd_handle_get_cap_resolve ( fd ,
store ,
cfg ,
concepts ,
dcfg ,
cap_state ,
root_path ,
& req ) ;
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 :
free ( ( void * ) req . actor . data ) ;
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-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 "
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-23 22:28:56 +01:00
amduatd_cfg_t dcfg ;
2026-01-23 23:08:41 +01:00
amduatd_cap_state_t cap_state ;
2025-12-22 15:23:54 +01:00
amduat_asl_store_fs_config_t cfg ;
amduat_asl_store_fs_t fs ;
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 ;
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-23 23:08:41 +01:00
memset ( & cap_state , 0 , sizeof ( cap_state ) ) ;
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 ] ;
if ( ! amduatd_space_id_valid ( space_id ) ) {
fprintf ( stderr , " error: invalid --space \n " ) ;
return 2 ;
}
} else if ( strcmp ( argv [ i ] , " --migrate-unscoped-edges " ) = = 0 ) {
migrate_unscoped_edges = true ;
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 ;
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-23 22:28:56 +01:00
dcfg . space_id = space_id ;
dcfg . space_enabled = ( space_id ! = NULL ) ;
dcfg . migrate_unscoped_edges = migrate_unscoped_edges ;
2025-12-22 15:23:54 +01:00
memset ( & cfg , 0 , sizeof ( cfg ) ) ;
if ( ! amduat_asl_store_fs_load_config ( root , & cfg ) ) {
fprintf ( stderr , " error: failed to load store config: %s \n " , root ) ;
return 8 ;
}
memset ( & fs , 0 , sizeof ( fs ) ) ;
if ( ! amduat_asl_store_fs_init ( & fs , cfg . config , root ) ) {
fprintf ( stderr , " error: failed to initialize store: %s \n " , root ) ;
return 8 ;
}
amduat_asl_store_init ( & store , cfg . config , amduat_asl_store_fs_ops ( ) , & fs ) ;
2026-01-23 23:08:41 +01:00
if ( ! amduatd_cap_init ( root , & cap_state ) ) {
amduat_log ( AMDUAT_LOG_WARN , " capabilities unavailable " ) ;
}
cap_state . reads_enabled = 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-23 22:28:56 +01:00
if ( ! amduatd_concepts_init ( & concepts , & store , & cfg , & dcfg , root ) ) {
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-23 22:28:56 +01:00
if ( ! amduatd_seed_ms_ui_state ( & store , & cfg , & 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 ,
& cap_state ,
root ) ;
2026-01-21 19:51:26 +01:00
( void ) close ( cfd ) ;
}
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 ;
}