amduatd: add opt-in federation config, space scoping, and tests

This commit is contained in:
Carl Niklas Rydberg 2026-01-24 10:35:49 +01:00
parent db3c4b4c93
commit af11665a35
8 changed files with 649 additions and 17 deletions

View file

@ -26,7 +26,8 @@ target_link_libraries(amduat_federation
set(amduatd_sources src/amduatd.c src/amduatd_http.c src/amduatd_caps.c
src/amduatd_space.c src/amduatd_concepts.c
src/amduatd_store.c src/amduatd_derivation_index.c)
src/amduatd_store.c src/amduatd_derivation_index.c
src/amduatd_fed.c)
if(AMDUATD_ENABLE_UI)
list(APPEND amduatd_sources src/amduatd_ui.c)
endif()
@ -117,3 +118,21 @@ target_link_libraries(amduatd_test_derivation_index
)
add_test(NAME amduatd_derivation_index COMMAND amduatd_test_derivation_index)
add_executable(amduatd_test_fed_cfg
tests/test_amduatd_fed_cfg.c
src/amduatd_fed.c
src/amduatd_space.c
)
target_include_directories(amduatd_test_fed_cfg
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/amduat/include
)
target_link_libraries(amduatd_test_fed_cfg
PRIVATE amduat_asl amduat_enc amduat_util amduat_asl_pointer_fs
)
add_test(NAME amduatd_fed_cfg COMMAND amduatd_test_fed_cfg)

View file

@ -47,6 +47,30 @@ Run the daemon with the index-backed store:
Note: `/v1/fed/records` requires the index backend.
## Federation (dev)
Federation is opt-in and disabled by default. Enabling federation requires the
index-backed store and explicit flags:
```sh
./build/amduatd --root .amduat-asl --sock amduatd.sock --store-backend index \
--fed-enable --fed-transport unix --fed-unix-sock peer.sock \
--fed-domain-id 1 --fed-registry-ref <registry_ref_hex>
```
Flags:
- `--fed-enable` turns on the coordinator tick loop.
- `--fed-transport stub|unix` selects transport (`stub` by default).
- `--fed-unix-sock PATH` configures the unix transport socket path.
- `--fed-domain-id ID` sets the local domain id.
- `--fed-registry-ref REF` seeds the registry reference (hex ref).
- `--fed-require-space` rejects `/v1/fed/*` requests that do not resolve a space.
`X-Amduat-Space` is honored for `/v1/fed/*` requests the same way as other
endpoints. If `--space` is configured, unix transport requests will include the
same `X-Amduat-Space` header when contacting peers.
Run the daemon with derivation indexing enabled:
```sh

View file

@ -687,7 +687,9 @@ static int amduat_fed_transport_unix_get_records(void *ctx,
amduat_fed_record_t **out_records,
size_t *out_len) {
amduat_fed_transport_unix_t *transport = (amduat_fed_transport_unix_t *)ctx;
char req[512];
char req[2048];
char space_header[AMDUAT_ASL_POINTER_NAME_MAX + 32u];
const char *space_line = "";
int fd;
uint8_t *buf = NULL;
size_t buf_len = 0;
@ -703,13 +705,23 @@ static int amduat_fed_transport_unix_get_records(void *ctx,
*out_records = NULL;
*out_len = 0;
if (transport->has_space) {
snprintf(space_header,
sizeof(space_header),
"X-Amduat-Space: %s\r\n",
transport->space_id);
space_line = space_header;
}
snprintf(req, sizeof(req),
"GET /v1/fed/records?domain_id=%u&from_logseq=%llu HTTP/1.1\r\n"
"Host: localhost\r\n"
"%s"
"Connection: close\r\n"
"\r\n",
(unsigned int)domain_id,
(unsigned long long)from_logseq);
(unsigned long long)from_logseq,
space_line);
fd = amduat_fed_transport_unix_connect(transport->socket_path);
if (fd < 0) {
@ -767,7 +779,9 @@ static int amduat_fed_transport_unix_get_artifact(void *ctx,
amduat_octets_t *out_bytes) {
amduat_fed_transport_unix_t *transport = (amduat_fed_transport_unix_t *)ctx;
char *ref_hex = NULL;
char req[512];
char req[2048];
char space_header[AMDUAT_ASL_POINTER_NAME_MAX + 32u];
const char *space_line = "";
int fd;
uint8_t *buf = NULL;
size_t buf_len = 0;
@ -783,12 +797,21 @@ static int amduat_fed_transport_unix_get_artifact(void *ctx,
if (!amduat_asl_ref_encode_hex(ref, &ref_hex)) {
return -1;
}
if (transport->has_space) {
snprintf(space_header,
sizeof(space_header),
"X-Amduat-Space: %s\r\n",
transport->space_id);
space_line = space_header;
}
snprintf(req, sizeof(req),
"GET /v1/fed/artifacts/%s HTTP/1.1\r\n"
"Host: localhost\r\n"
"%s"
"Connection: close\r\n"
"\r\n",
ref_hex);
ref_hex,
space_line);
free(ref_hex);
fd = amduat_fed_transport_unix_connect(transport->socket_path);
@ -837,6 +860,34 @@ bool amduat_fed_transport_unix_init(amduat_fed_transport_unix_t *transport,
memset(transport->socket_path, 0, sizeof(transport->socket_path));
strncpy(transport->socket_path, socket_path,
AMDUAT_FED_TRANSPORT_UNIX_PATH_MAX - 1u);
memset(transport->space_id, 0, sizeof(transport->space_id));
transport->has_space = false;
return true;
}
bool amduat_fed_transport_unix_set_space(amduat_fed_transport_unix_t *transport,
const char *space_id) {
size_t len;
size_t i;
if (transport == NULL) {
return false;
}
memset(transport->space_id, 0, sizeof(transport->space_id));
transport->has_space = false;
if (space_id == NULL || space_id[0] == '\0') {
return true;
}
len = strlen(space_id);
if (len >= sizeof(transport->space_id)) {
return false;
}
for (i = 0; i < len; ++i) {
if (space_id[i] == '\r' || space_id[i] == '\n') {
return false;
}
}
memcpy(transport->space_id, space_id, len);
transport->has_space = true;
return true;
}

View file

@ -2,6 +2,7 @@
#define AMDUAT_FED_TRANSPORT_UNIX_H
#include "federation/coord.h"
#include "amduat/asl/asl_pointer_fs.h"
#ifdef __cplusplus
extern "C" {
@ -11,11 +12,16 @@ enum { AMDUAT_FED_TRANSPORT_UNIX_PATH_MAX = 1024 };
typedef struct {
char socket_path[AMDUAT_FED_TRANSPORT_UNIX_PATH_MAX];
char space_id[AMDUAT_ASL_POINTER_NAME_MAX + 1u];
bool has_space;
} amduat_fed_transport_unix_t;
bool amduat_fed_transport_unix_init(amduat_fed_transport_unix_t *transport,
const char *socket_path);
bool amduat_fed_transport_unix_set_space(amduat_fed_transport_unix_t *transport,
const char *space_id);
amduat_fed_transport_t amduat_fed_transport_unix_ops(
amduat_fed_transport_unix_t *transport);

View file

@ -26,6 +26,7 @@
#include "amduat/tgk/core.h"
#include "federation/coord.h"
#include "federation/transport_stub.h"
#include "federation/transport_unix.h"
#include "amduat/pel/program_dag.h"
#include "amduat/pel/program_dag_desc.h"
#include "amduat/pel/opreg_kernel.h"
@ -38,6 +39,7 @@
#include "amduatd_ui.h"
#include "amduatd_caps.h"
#include "amduatd_space.h"
#include "amduatd_fed.h"
#include "amduatd_store.h"
#include "amduatd_derivation_index.h"
@ -975,8 +977,37 @@ static bool amduatd_parse_u32_str(const char *s, uint32_t *out) {
return true;
}
static bool amduatd_fed_require_space(int fd,
const amduatd_fed_cfg_t *fed_cfg,
const amduatd_http_req_t *req) {
amduat_octets_t scoped = amduat_octets(NULL, 0u);
const char *err = NULL;
char msg[128];
if (fed_cfg == NULL || req == NULL) {
return amduatd_http_send_text(fd, 500, "Internal Server Error",
"internal error\n", false);
}
if (!amduatd_fed_scope_names(fed_cfg,
req->effective_space,
"fed",
&scoped,
&err)) {
const char *reason = err != NULL ? err : "invalid X-Amduat-Space";
int n = snprintf(msg, sizeof(msg), "%s\n", reason);
if (n <= 0 || (size_t)n >= sizeof(msg)) {
return amduatd_http_send_text(fd, 400, "Bad Request",
"invalid X-Amduat-Space\n", false);
}
return amduatd_http_send_text(fd, 400, "Bad Request", msg, false);
}
amduat_octets_free(&scoped);
return true;
}
static bool amduatd_handle_get_fed_records(int fd,
amduat_asl_store_t *store,
const amduatd_fed_cfg_t *fed_cfg,
const amduatd_http_req_t *req) {
char domain_buf[32];
char from_buf[32];
@ -997,6 +1028,9 @@ static bool amduatd_handle_get_fed_records(int fd,
return amduatd_http_send_text(fd, 500, "Internal Server Error",
"internal error\n", false);
}
if (!amduatd_fed_require_space(fd, fed_cfg, req)) {
return false;
}
if (amduatd_query_param(req->path, "domain_id",
domain_buf, sizeof(domain_buf)) == NULL ||
!amduatd_parse_u32_str(domain_buf, &domain_id)) {
@ -1179,6 +1213,7 @@ fed_records_oom:
static bool amduatd_handle_get_fed_artifact(int fd,
amduat_asl_store_t *store,
const amduatd_fed_cfg_t *fed_cfg,
const amduatd_http_req_t *req,
const char *path) {
char no_query[1024];
@ -1191,6 +1226,9 @@ static bool amduatd_handle_get_fed_artifact(int fd,
return amduatd_http_send_text(fd, 500, "Internal Server Error",
"internal error\n", false);
}
if (!amduatd_fed_require_space(fd, fed_cfg, req)) {
return 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);
@ -1228,13 +1266,20 @@ static bool amduatd_handle_get_fed_artifact(int fd,
static bool amduatd_handle_get_fed_status(int fd,
const amduat_fed_coord_t *coord,
const amduatd_fed_cfg_t *fed_cfg,
const amduatd_http_req_t *req) {
amduat_fed_coord_status_t status;
char *ref_hex = NULL;
char json[512];
int n;
(void)req;
if (!amduatd_fed_require_space(fd, fed_cfg, req)) {
return false;
}
if (coord == NULL || fed_cfg == NULL || !fed_cfg->enabled) {
return amduatd_http_send_text(fd, 503, "Service Unavailable",
"federation disabled\n", false);
}
amduat_fed_coord_get_status(coord, &status);
if (status.registry_ref.hash_id != 0 &&
status.registry_ref.digest.data != NULL) {
@ -3870,6 +3915,7 @@ static bool amduatd_handle_conn(int fd,
amduat_reference_t ui_ref,
amduatd_concepts_t *concepts,
const amduatd_cfg_t *dcfg,
const amduatd_fed_cfg_t *fed_cfg,
const amduat_fed_coord_t *coord,
const amduatd_allowlist_t *allowlist,
amduatd_caps_t *caps,
@ -3971,12 +4017,12 @@ static bool amduatd_handle_conn(int fd,
}
if (strcmp(req.method, "GET") == 0 &&
strcmp(no_query, "/v1/fed/records") == 0) {
ok = amduatd_handle_get_fed_records(fd, store, &req);
ok = amduatd_handle_get_fed_records(fd, store, fed_cfg, &req);
goto conn_cleanup;
}
if (strcmp(req.method, "GET") == 0 &&
strcmp(no_query, "/v1/fed/status") == 0) {
ok = amduatd_handle_get_fed_status(fd, coord, &req);
ok = amduatd_handle_get_fed_status(fd, coord, fed_cfg, &req);
goto conn_cleanup;
}
@ -4042,7 +4088,7 @@ static bool amduatd_handle_conn(int fd,
}
if (strcmp(req.method, "GET") == 0 &&
strncmp(no_query, "/v1/fed/artifacts/", 18) == 0) {
ok = amduatd_handle_get_fed_artifact(fd, store, &req, req.path);
ok = amduatd_handle_get_fed_artifact(fd, store, fed_cfg, &req, req.path);
goto conn_cleanup;
}
if (strcmp(req.method, "GET") == 0 &&
@ -4089,6 +4135,10 @@ static void amduatd_print_usage(FILE *stream) {
" [--space SPACE_ID] [--migrate-unscoped-edges]\n"
" [--edges-refresh-ms MS]\n"
" [--store-backend fs|index]\n"
" [--fed-enable] [--fed-transport stub|unix]\n"
" [--fed-unix-sock PATH]\n"
" [--fed-domain-id ID] [--fed-registry-ref REF]\n"
" [--fed-require-space]\n"
" [--allow-uid UID] [--allow-user NAME]\n"
" [--enable-cap-reads]\n"
" [--enable-derivation-index]\n"
@ -4116,14 +4166,17 @@ int main(int argc, char **argv) {
amduat_reference_t api_contract_ref;
amduat_reference_t ui_ref;
amduatd_concepts_t concepts;
amduatd_fed_cfg_t fed_cfg;
amduat_fed_transport_stub_t fed_stub;
amduat_fed_coord_cfg_t fed_cfg;
amduat_fed_transport_unix_t fed_unix;
amduat_fed_coord_cfg_t fed_coord_cfg;
amduat_fed_coord_t *fed_coord = NULL;
amduatd_allowlist_t allowlist;
int i;
int sfd = -1;
uint64_t last_tick_ms = 0;
uint64_t last_edges_refresh_ms = 0;
const char *fed_err = NULL;
memset(&api_contract_ref, 0, sizeof(api_contract_ref));
memset(&ui_ref, 0, sizeof(ui_ref));
@ -4131,8 +4184,18 @@ int main(int argc, char **argv) {
memset(&allowlist, 0, sizeof(allowlist));
memset(&dcfg, 0, sizeof(dcfg));
memset(&caps, 0, sizeof(caps));
amduatd_fed_cfg_init(&fed_cfg);
for (i = 1; i < argc; ++i) {
amduatd_fed_parse_result_t fed_rc;
fed_rc = amduatd_fed_cfg_parse_arg(&fed_cfg, argc, argv, &i, &fed_err);
if (fed_rc == AMDUATD_FED_PARSE_OK) {
continue;
}
if (fed_rc == AMDUATD_FED_PARSE_ERROR) {
fprintf(stderr, "error: %s\n", fed_err != NULL ? fed_err : "fed parse error");
return 2;
}
if (strcmp(argv[i], "--root") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --root requires a path\n");
@ -4231,6 +4294,13 @@ int main(int argc, char **argv) {
return 2;
}
if (!amduatd_fed_requirements_check(store_backend, &fed_cfg, &fed_err)) {
fprintf(stderr,
"error: %s\n",
fed_err != NULL ? fed_err : "invalid federation config");
return 2;
}
if (!amduatd_store_init(&store, &cfg, &store_ctx, root, store_backend)) {
fprintf(stderr, "error: failed to initialize store: %s\n", root);
return 8;
@ -4268,14 +4338,53 @@ int main(int argc, char **argv) {
}
#endif
{
char *registry_hex = NULL;
if (fed_cfg.registry_ref_set) {
if (!amduat_asl_ref_encode_hex(fed_cfg.registry_ref, &registry_hex)) {
registry_hex = NULL;
}
}
amduat_log(AMDUAT_LOG_INFO,
"federation enabled=%s transport=%s domain_id=%u registry_ref=%s require_space=%s",
fed_cfg.enabled ? "on" : "off",
amduatd_fed_transport_name(fed_cfg.transport_kind),
(unsigned int)fed_cfg.local_domain_id,
registry_hex != NULL ? registry_hex : "null",
fed_cfg.require_space ? "on" : "off");
free(registry_hex);
}
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;
memset(&fed_coord_cfg, 0, sizeof(fed_coord_cfg));
if (fed_cfg.enabled) {
fed_coord_cfg.local_domain_id = fed_cfg.local_domain_id;
fed_coord_cfg.authoritative_store = &store;
fed_coord_cfg.cache_store = NULL;
if (fed_cfg.registry_ref_set) {
fed_coord_cfg.registry_ref = fed_cfg.registry_ref;
} else {
fed_coord_cfg.registry_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (fed_cfg.transport_kind == AMDUATD_FED_TRANSPORT_UNIX) {
if (!amduat_fed_transport_unix_init(&fed_unix,
fed_cfg.unix_socket_path)) {
fprintf(stderr, "error: invalid --fed-unix-sock\n");
return 2;
}
if (dcfg.space.enabled) {
(void)amduat_fed_transport_unix_set_space(&fed_unix,
dcfg.space.space_id_buf);
}
fed_coord_cfg.transport = amduat_fed_transport_unix_ops(&fed_unix);
} else {
fed_coord_cfg.transport = amduat_fed_transport_stub_ops(&fed_stub);
}
if (amduat_fed_coord_open(&fed_coord_cfg, &fed_coord) !=
AMDUAT_FED_COORD_OK) {
fprintf(stderr, "error: failed to init federation coordinator\n");
return 8;
}
}
signal(SIGINT, amduatd_on_signal);
@ -4378,6 +4487,7 @@ int main(int argc, char **argv) {
ui_ref,
&concepts,
&dcfg,
&fed_cfg,
fed_coord,
&allowlist,
&caps,

236
src/amduatd_fed.c Normal file
View file

@ -0,0 +1,236 @@
#include "amduatd_fed.h"
#include "amduat/asl/ref_text.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
static bool amduatd_fed_parse_u32(const char *s, uint32_t *out) {
unsigned long val;
char *endp = NULL;
if (s == NULL || out == NULL) {
return false;
}
errno = 0;
val = strtoul(s, &endp, 10);
if (errno != 0 || endp == s || *endp != '\0' || val > UINT32_MAX) {
return false;
}
*out = (uint32_t)val;
return true;
}
void amduatd_fed_cfg_init(amduatd_fed_cfg_t *cfg) {
if (cfg == NULL) {
return;
}
memset(cfg, 0, sizeof(*cfg));
cfg->transport_kind = AMDUATD_FED_TRANSPORT_STUB;
cfg->registry_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
void amduatd_fed_cfg_free(amduatd_fed_cfg_t *cfg) {
if (cfg == NULL) {
return;
}
if (cfg->registry_ref_set) {
amduat_reference_free(&cfg->registry_ref);
cfg->registry_ref_set = false;
}
amduatd_fed_cfg_init(cfg);
}
const char *amduatd_fed_transport_name(amduatd_fed_transport_kind_t kind) {
switch (kind) {
case AMDUATD_FED_TRANSPORT_STUB:
return "stub";
case AMDUATD_FED_TRANSPORT_UNIX:
return "unix";
default:
return "unknown";
}
}
amduatd_fed_parse_result_t amduatd_fed_cfg_parse_arg(amduatd_fed_cfg_t *cfg,
int argc,
char **argv,
int *io_index,
const char **out_err) {
const char *arg = NULL;
const char *value = NULL;
if (out_err != NULL) {
*out_err = NULL;
}
if (cfg == NULL || argv == NULL || io_index == NULL || argc <= 0) {
if (out_err != NULL) {
*out_err = "invalid fed config parse inputs";
}
return AMDUATD_FED_PARSE_ERROR;
}
if (*io_index < 0 || *io_index >= argc) {
if (out_err != NULL) {
*out_err = "fed config parse index out of range";
}
return AMDUATD_FED_PARSE_ERROR;
}
arg = argv[*io_index];
if (strcmp(arg, "--fed-enable") == 0) {
cfg->enabled = true;
return AMDUATD_FED_PARSE_OK;
}
if (strcmp(arg, "--fed-require-space") == 0) {
cfg->require_space = true;
return AMDUATD_FED_PARSE_OK;
}
if (strcmp(arg, "--fed-transport") == 0) {
if (*io_index + 1 >= argc) {
if (out_err != NULL) {
*out_err = "--fed-transport requires a value";
}
return AMDUATD_FED_PARSE_ERROR;
}
value = argv[++(*io_index)];
if (strcmp(value, "stub") == 0) {
cfg->transport_kind = AMDUATD_FED_TRANSPORT_STUB;
return AMDUATD_FED_PARSE_OK;
}
if (strcmp(value, "unix") == 0) {
cfg->transport_kind = AMDUATD_FED_TRANSPORT_UNIX;
return AMDUATD_FED_PARSE_OK;
}
if (out_err != NULL) {
*out_err = "invalid --fed-transport";
}
return AMDUATD_FED_PARSE_ERROR;
}
if (strcmp(arg, "--fed-unix-sock") == 0) {
size_t len;
if (*io_index + 1 >= argc) {
if (out_err != NULL) {
*out_err = "--fed-unix-sock requires a path";
}
return AMDUATD_FED_PARSE_ERROR;
}
value = argv[++(*io_index)];
len = strlen(value);
if (len == 0 || len >= sizeof(cfg->unix_socket_path)) {
if (out_err != NULL) {
*out_err = "invalid --fed-unix-sock";
}
return AMDUATD_FED_PARSE_ERROR;
}
memset(cfg->unix_socket_path, 0, sizeof(cfg->unix_socket_path));
memcpy(cfg->unix_socket_path, value, len);
cfg->unix_socket_set = true;
return AMDUATD_FED_PARSE_OK;
}
if (strcmp(arg, "--fed-domain-id") == 0) {
uint32_t domain_id = 0;
if (*io_index + 1 >= argc) {
if (out_err != NULL) {
*out_err = "--fed-domain-id requires a value";
}
return AMDUATD_FED_PARSE_ERROR;
}
value = argv[++(*io_index)];
if (!amduatd_fed_parse_u32(value, &domain_id)) {
if (out_err != NULL) {
*out_err = "invalid --fed-domain-id";
}
return AMDUATD_FED_PARSE_ERROR;
}
cfg->local_domain_id = domain_id;
return AMDUATD_FED_PARSE_OK;
}
if (strcmp(arg, "--fed-registry-ref") == 0) {
amduat_reference_t ref;
if (*io_index + 1 >= argc) {
if (out_err != NULL) {
*out_err = "--fed-registry-ref requires a value";
}
return AMDUATD_FED_PARSE_ERROR;
}
value = argv[++(*io_index)];
memset(&ref, 0, sizeof(ref));
if (!amduat_asl_ref_decode_hex(value, &ref)) {
if (out_err != NULL) {
*out_err = "invalid --fed-registry-ref";
}
return AMDUATD_FED_PARSE_ERROR;
}
if (cfg->registry_ref_set) {
amduat_reference_free(&cfg->registry_ref);
}
cfg->registry_ref = ref;
cfg->registry_ref_set = true;
return AMDUATD_FED_PARSE_OK;
}
return AMDUATD_FED_PARSE_NOT_HANDLED;
}
bool amduatd_fed_requirements_check(amduatd_store_backend_t backend,
const amduatd_fed_cfg_t *cfg,
const char **out_err) {
if (out_err != NULL) {
*out_err = NULL;
}
if (cfg == NULL) {
if (out_err != NULL) {
*out_err = "missing fed config";
}
return false;
}
if (!cfg->enabled) {
return true;
}
if (backend != AMDUATD_STORE_BACKEND_INDEX) {
if (out_err != NULL) {
*out_err = "federation requires --store-backend index";
}
return false;
}
if (cfg->transport_kind == AMDUATD_FED_TRANSPORT_UNIX &&
!cfg->unix_socket_set) {
if (out_err != NULL) {
*out_err = "unix transport requires --fed-unix-sock";
}
return false;
}
return true;
}
bool amduatd_fed_scope_names(const amduatd_fed_cfg_t *cfg,
const amduatd_space_t *space,
const char *name,
amduat_octets_t *out_scoped,
const char **out_err) {
if (out_err != NULL) {
*out_err = NULL;
}
if (out_scoped != NULL) {
*out_scoped = amduat_octets(NULL, 0u);
}
if (cfg == NULL || name == NULL || out_scoped == NULL) {
if (out_err != NULL) {
*out_err = "invalid fed scope inputs";
}
return false;
}
if (cfg->require_space && (space == NULL || !space->enabled)) {
if (out_err != NULL) {
*out_err = "missing X-Amduat-Space";
}
return false;
}
if (!amduatd_space_scope_name(space, name, out_scoped)) {
if (out_err != NULL) {
*out_err = "failed to scope name";
}
return false;
}
return true;
}

65
src/amduatd_fed.h Normal file
View file

@ -0,0 +1,65 @@
#ifndef AMDUATD_FED_H
#define AMDUATD_FED_H
#include "amduatd_space.h"
#include "amduatd_store.h"
#include "federation/transport_unix.h"
#include "amduat/asl/core.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
AMDUATD_FED_TRANSPORT_STUB = 0,
AMDUATD_FED_TRANSPORT_UNIX = 1
} amduatd_fed_transport_kind_t;
typedef struct {
bool enabled;
bool require_space;
amduatd_fed_transport_kind_t transport_kind;
uint32_t local_domain_id;
bool registry_ref_set;
amduat_reference_t registry_ref;
bool unix_socket_set;
char unix_socket_path[AMDUAT_FED_TRANSPORT_UNIX_PATH_MAX];
} amduatd_fed_cfg_t;
typedef enum {
AMDUATD_FED_PARSE_OK = 0,
AMDUATD_FED_PARSE_NOT_HANDLED = 1,
AMDUATD_FED_PARSE_ERROR = 2
} amduatd_fed_parse_result_t;
void amduatd_fed_cfg_init(amduatd_fed_cfg_t *cfg);
void amduatd_fed_cfg_free(amduatd_fed_cfg_t *cfg);
const char *amduatd_fed_transport_name(amduatd_fed_transport_kind_t kind);
amduatd_fed_parse_result_t amduatd_fed_cfg_parse_arg(amduatd_fed_cfg_t *cfg,
int argc,
char **argv,
int *io_index,
const char **out_err);
bool amduatd_fed_requirements_check(amduatd_store_backend_t backend,
const amduatd_fed_cfg_t *cfg,
const char **out_err);
bool amduatd_fed_scope_names(const amduatd_fed_cfg_t *cfg,
const amduatd_space_t *space,
const char *name,
amduat_octets_t *out_scoped,
const char **out_err);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUATD_FED_H */

View file

@ -0,0 +1,121 @@
#include "amduatd_fed.h"
#include "amduat/asl/ref_text.h"
#include "amduat/hash/asl1.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int failures = 0;
static void expect(bool cond, const char *msg) {
if (!cond) {
fprintf(stderr, "FAIL: %s\n", msg);
failures++;
}
}
int main(void) {
amduatd_fed_cfg_t cfg;
amduatd_space_t space;
amduat_octets_t scoped = amduat_octets(NULL, 0u);
uint8_t digest_bytes[32];
amduat_octets_t digest;
amduat_reference_t ref;
char *ref_hex = NULL;
const char *err = NULL;
int i;
memset(digest_bytes, 0x2a, sizeof(digest_bytes));
if (!amduat_octets_clone(amduat_octets(digest_bytes, sizeof(digest_bytes)),
&digest)) {
fprintf(stderr, "FAIL: digest clone\n");
return 1;
}
ref = amduat_reference(AMDUAT_HASH_ASL1_ID_SHA256, digest);
if (!amduat_asl_ref_encode_hex(ref, &ref_hex)) {
fprintf(stderr, "FAIL: ref encode\n");
amduat_reference_free(&ref);
return 1;
}
amduatd_fed_cfg_init(&cfg);
expect(!cfg.enabled, "default disabled");
expect(cfg.transport_kind == AMDUATD_FED_TRANSPORT_STUB, "default transport");
expect(!cfg.require_space, "default require_space");
{
char *argv[] = {
"amduatd",
"--fed-enable",
"--fed-transport",
"unix",
"--fed-unix-sock",
"amduatd.sock",
"--fed-domain-id",
"42",
"--fed-registry-ref",
ref_hex,
"--fed-require-space",
};
int argc = (int)(sizeof(argv) / sizeof(argv[0]));
for (i = 1; i < argc; ++i) {
amduatd_fed_parse_result_t rc;
rc = amduatd_fed_cfg_parse_arg(&cfg, argc, argv, &i, &err);
expect(rc == AMDUATD_FED_PARSE_OK, "fed parse ok");
}
}
expect(cfg.enabled, "parsed enabled");
expect(cfg.transport_kind == AMDUATD_FED_TRANSPORT_UNIX,
"parsed transport");
expect(cfg.unix_socket_set, "parsed unix socket");
expect(cfg.local_domain_id == 42u, "parsed domain id");
expect(cfg.registry_ref_set, "parsed registry ref");
expect(cfg.require_space, "parsed require space");
expect(!amduatd_fed_requirements_check(AMDUATD_STORE_BACKEND_FS,
&cfg,
&err),
"requirements reject fs backend");
expect(amduatd_fed_requirements_check(AMDUATD_STORE_BACKEND_INDEX,
&cfg,
&err),
"requirements accept index backend");
if (!amduatd_space_init(&space, NULL, false)) {
fprintf(stderr, "FAIL: space init\n");
amduatd_fed_cfg_free(&cfg);
amduat_reference_free(&ref);
free(ref_hex);
return 1;
}
expect(!amduatd_fed_scope_names(&cfg, &space, "fed", &scoped, &err),
"scope requires space");
amduat_octets_free(&scoped);
if (!amduatd_space_init(&space, "alpha", false)) {
fprintf(stderr, "FAIL: space init alpha\n");
amduatd_fed_cfg_free(&cfg);
amduat_reference_free(&ref);
free(ref_hex);
return 1;
}
expect(amduatd_fed_scope_names(&cfg, &space, "fed", &scoped, &err),
"scope with space");
expect(scoped.data != NULL &&
scoped.len == strlen("space/alpha/fed") &&
memcmp(scoped.data,
"space/alpha/fed",
scoped.len) == 0,
"scoped name");
amduat_octets_free(&scoped);
amduatd_fed_cfg_free(&cfg);
amduat_reference_free(&ref);
free(ref_hex);
return failures == 0 ? 0 : 1;
}