Improve federation API docs and errors
This commit is contained in:
parent
5cc56b2ce8
commit
d8a6603ddc
14
AUDITS.md
14
AUDITS.md
|
|
@ -73,6 +73,8 @@ Status legend: ✅ implemented, 🟡 planned/in-progress, ⬜ not started.
|
||||||
| `ASL/INDEXES/1` | 🟡 | Taxonomy planned. |
|
| `ASL/INDEXES/1` | 🟡 | Taxonomy planned. |
|
||||||
| `ASL/TGK-EXEC-PLAN/1` | 🟡 | Encoding implemented; executor out of scope. |
|
| `ASL/TGK-EXEC-PLAN/1` | 🟡 | Encoding implemented; executor out of scope. |
|
||||||
| `ENC/ASL-TGK-EXEC-PLAN/1` | ✅ | Plan encoding implemented. |
|
| `ENC/ASL-TGK-EXEC-PLAN/1` | ✅ | Plan encoding implemented. |
|
||||||
|
| `ASL/FEDERATION/1` | ✅ | Core federation primitives implemented. |
|
||||||
|
| `ASL/FEDERATION-REPLAY/1` | ✅ | Deterministic replay and view construction implemented. |
|
||||||
| `ASL/SYSTEM/1` | 🟡 | Cross-cutting view planned. |
|
| `ASL/SYSTEM/1` | 🟡 | Cross-cutting view planned. |
|
||||||
| `TGK/1` | 🟡 | Semantic layer planned. |
|
| `TGK/1` | 🟡 | Semantic layer planned. |
|
||||||
|
|
||||||
|
|
@ -348,3 +350,15 @@ Status legend: ✅ completed, ⬜ pending.
|
||||||
embedded commit-message appendix; tightened wording throughout; bumped the
|
embedded commit-message appendix; tightened wording throughout; bumped the
|
||||||
document version/date.
|
document version/date.
|
||||||
- Tests: N/A (documentation-only change).
|
- Tests: N/A (documentation-only change).
|
||||||
|
|
||||||
|
## 2026-02-XX — Federation core (`tier1/asl-federation-1.md`, `tier1/asl-federation-replay-1.md`)
|
||||||
|
- Scope: core federation registry, ingest validation, deterministic replay, view
|
||||||
|
construction, and resolve semantics.
|
||||||
|
- Findings: missing record typing and identity coverage for PER/TGK/tombstones;
|
||||||
|
policy gating needed explicit per-domain + optional per-record handling; view
|
||||||
|
build and resolve error reporting needed explicit codes and tests.
|
||||||
|
- Resolution: added federation registry storage, ingest validation, replay/view
|
||||||
|
build, resolve APIs, and tests for ordering, tombstone scoping, conflicts,
|
||||||
|
bounds, and metadata preservation; documented middle-layer boundary and
|
||||||
|
ref-only remote fetch guidance.
|
||||||
|
- Tests: user reported “100% tests passed, 0 tests failed out of 29”.
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ typedef struct {
|
||||||
uint8_t policy_ok;
|
uint8_t policy_ok;
|
||||||
uint8_t reserved[6];
|
uint8_t reserved[6];
|
||||||
amduat_hash_id_t policy_hash_id;
|
amduat_hash_id_t policy_hash_id;
|
||||||
amduat_octets_t policy_hash; /* Empty when unknown. */
|
amduat_octets_t policy_hash; /* Empty when unknown; caller-owned bytes. */
|
||||||
} amduat_fed_domain_state_t;
|
} amduat_fed_domain_state_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -44,9 +44,11 @@ const amduat_fed_domain_state_t *amduat_fed_registry_value_lookup(
|
||||||
|
|
||||||
void amduat_fed_registry_value_free(amduat_fed_registry_value_t *value);
|
void amduat_fed_registry_value_free(amduat_fed_registry_value_t *value);
|
||||||
|
|
||||||
|
/* Encode/Decode allocate buffers; caller frees via amduat_octets_free. */
|
||||||
bool amduat_fed_registry_encode(const amduat_fed_registry_value_t *value,
|
bool amduat_fed_registry_encode(const amduat_fed_registry_value_t *value,
|
||||||
amduat_octets_t *out_bytes);
|
amduat_octets_t *out_bytes);
|
||||||
|
|
||||||
|
/* Decode allocates policy_hash bytes; free via amduat_fed_registry_value_free. */
|
||||||
bool amduat_fed_registry_decode(amduat_octets_t bytes,
|
bool amduat_fed_registry_decode(amduat_octets_t bytes,
|
||||||
amduat_fed_registry_value_t *out_value);
|
amduat_fed_registry_value_t *out_value);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ typedef struct {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
amduat_fed_record_type_t type;
|
amduat_fed_record_type_t type;
|
||||||
amduat_reference_t ref;
|
amduat_reference_t ref; /* PER/TGK identities are ASL references. */
|
||||||
} amduat_fed_record_id_t;
|
} amduat_fed_record_id_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -44,6 +44,7 @@ typedef struct {
|
||||||
size_t len;
|
size_t len;
|
||||||
} amduat_fed_replay_view_t;
|
} amduat_fed_replay_view_t;
|
||||||
|
|
||||||
|
/* Caller frees record ids with amduat_fed_replay_view_free. */
|
||||||
bool amduat_fed_record_validate(const amduat_fed_record_t *record);
|
bool amduat_fed_record_validate(const amduat_fed_record_t *record);
|
||||||
|
|
||||||
bool amduat_fed_replay_domain(const amduat_fed_record_t *records,
|
bool amduat_fed_replay_domain(const amduat_fed_record_t *records,
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,23 @@ typedef struct {
|
||||||
size_t denies_len;
|
size_t denies_len;
|
||||||
} amduat_fed_view_t;
|
} amduat_fed_view_t;
|
||||||
|
|
||||||
bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
typedef enum {
|
||||||
size_t count,
|
AMDUAT_FED_VIEW_OK = 0,
|
||||||
uint32_t local_domain_id,
|
AMDUAT_FED_VIEW_ERR_INVALID = 1,
|
||||||
const amduat_fed_view_bounds_t *bounds,
|
AMDUAT_FED_VIEW_ERR_CONFLICT = 2,
|
||||||
size_t bounds_len,
|
AMDUAT_FED_VIEW_ERR_OOM = 3
|
||||||
const amduat_fed_policy_deny_t *denies,
|
} amduat_fed_view_error_t;
|
||||||
size_t denies_len,
|
|
||||||
amduat_fed_view_t *out_view);
|
/* Caller frees records/denies with amduat_fed_view_free. */
|
||||||
|
amduat_fed_view_error_t amduat_fed_view_build(
|
||||||
|
const amduat_fed_record_t *records,
|
||||||
|
size_t count,
|
||||||
|
uint32_t local_domain_id,
|
||||||
|
const amduat_fed_view_bounds_t *bounds,
|
||||||
|
size_t bounds_len,
|
||||||
|
const amduat_fed_policy_deny_t *denies,
|
||||||
|
size_t denies_len,
|
||||||
|
amduat_fed_view_t *out_view);
|
||||||
|
|
||||||
void amduat_fed_view_free(amduat_fed_view_t *view);
|
void amduat_fed_view_free(amduat_fed_view_t *view);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,22 +75,23 @@ static void amduat_fed_policy_deny_free(amduat_fed_policy_deny_t *deny) {
|
||||||
memset(deny, 0, sizeof(*deny));
|
memset(deny, 0, sizeof(*deny));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool amduat_fed_view_append(amduat_fed_view_t *view,
|
static amduat_fed_view_error_t amduat_fed_view_append(
|
||||||
const amduat_fed_record_t *records,
|
amduat_fed_view_t *view,
|
||||||
size_t count) {
|
const amduat_fed_record_t *records,
|
||||||
|
size_t count) {
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t base_len;
|
size_t base_len;
|
||||||
|
|
||||||
if (count == 0u) {
|
if (count == 0u) {
|
||||||
return true;
|
return AMDUAT_FED_VIEW_OK;
|
||||||
}
|
}
|
||||||
if (view->len > SIZE_MAX - count) {
|
if (view->len > SIZE_MAX - count) {
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||||
}
|
}
|
||||||
if (view->records == NULL) {
|
if (view->records == NULL) {
|
||||||
view->records = (amduat_fed_record_t *)calloc(count, sizeof(*view->records));
|
view->records = (amduat_fed_record_t *)calloc(count, sizeof(*view->records));
|
||||||
if (view->records == NULL) {
|
if (view->records == NULL) {
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
amduat_fed_record_t *next =
|
amduat_fed_record_t *next =
|
||||||
|
|
@ -98,7 +99,7 @@ static bool amduat_fed_view_append(amduat_fed_view_t *view,
|
||||||
(view->len + count) *
|
(view->len + count) *
|
||||||
sizeof(*view->records));
|
sizeof(*view->records));
|
||||||
if (next == NULL) {
|
if (next == NULL) {
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||||
}
|
}
|
||||||
view->records = next;
|
view->records = next;
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +116,7 @@ static bool amduat_fed_view_append(amduat_fed_view_t *view,
|
||||||
amduat_fed_record_free(&view->records[j]);
|
amduat_fed_record_free(&view->records[j]);
|
||||||
}
|
}
|
||||||
view->len = base_len;
|
view->len = base_len;
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_CONFLICT;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -127,26 +128,27 @@ static bool amduat_fed_view_append(amduat_fed_view_t *view,
|
||||||
amduat_fed_record_free(&view->records[j]);
|
amduat_fed_record_free(&view->records[j]);
|
||||||
}
|
}
|
||||||
view->len = base_len;
|
view->len = base_len;
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||||
}
|
}
|
||||||
view->len++;
|
view->len++;
|
||||||
}
|
}
|
||||||
return true;
|
return AMDUAT_FED_VIEW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
amduat_fed_view_error_t amduat_fed_view_build(
|
||||||
size_t count,
|
const amduat_fed_record_t *records,
|
||||||
uint32_t local_domain_id,
|
size_t count,
|
||||||
const amduat_fed_view_bounds_t *bounds,
|
uint32_t local_domain_id,
|
||||||
size_t bounds_len,
|
const amduat_fed_view_bounds_t *bounds,
|
||||||
const amduat_fed_policy_deny_t *denies,
|
size_t bounds_len,
|
||||||
size_t denies_len,
|
const amduat_fed_policy_deny_t *denies,
|
||||||
amduat_fed_view_t *out_view) {
|
size_t denies_len,
|
||||||
|
amduat_fed_view_t *out_view) {
|
||||||
size_t i;
|
size_t i;
|
||||||
amduat_fed_view_t view;
|
amduat_fed_view_t view;
|
||||||
|
|
||||||
if (out_view == NULL) {
|
if (out_view == NULL) {
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_INVALID;
|
||||||
}
|
}
|
||||||
out_view->records = NULL;
|
out_view->records = NULL;
|
||||||
out_view->len = 0;
|
out_view->len = 0;
|
||||||
|
|
@ -155,7 +157,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
||||||
out_view->denies_len = 0;
|
out_view->denies_len = 0;
|
||||||
|
|
||||||
if (records == NULL && count != 0u) {
|
if (records == NULL && count != 0u) {
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
view.records = NULL;
|
view.records = NULL;
|
||||||
|
|
@ -165,13 +167,13 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
||||||
view.denies_len = 0;
|
view.denies_len = 0;
|
||||||
|
|
||||||
if (denies_len != 0u && denies == NULL) {
|
if (denies_len != 0u && denies == NULL) {
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_INVALID;
|
||||||
}
|
}
|
||||||
if (denies_len != 0u) {
|
if (denies_len != 0u) {
|
||||||
view.denies = (amduat_fed_policy_deny_t *)calloc(denies_len,
|
view.denies = (amduat_fed_policy_deny_t *)calloc(denies_len,
|
||||||
sizeof(*view.denies));
|
sizeof(*view.denies));
|
||||||
if (view.denies == NULL) {
|
if (view.denies == NULL) {
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||||
}
|
}
|
||||||
for (i = 0; i < denies_len; ++i) {
|
for (i = 0; i < denies_len; ++i) {
|
||||||
if (!amduat_fed_policy_deny_clone(&denies[i], &view.denies[i])) {
|
if (!amduat_fed_policy_deny_clone(&denies[i], &view.denies[i])) {
|
||||||
|
|
@ -180,7 +182,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
||||||
amduat_fed_policy_deny_free(&view.denies[j]);
|
amduat_fed_policy_deny_free(&view.denies[j]);
|
||||||
}
|
}
|
||||||
free(view.denies);
|
free(view.denies);
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view.denies_len = denies_len;
|
view.denies_len = denies_len;
|
||||||
|
|
@ -214,7 +216,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
||||||
(amduat_fed_record_t *)calloc(domain_count, sizeof(*domain_records));
|
(amduat_fed_record_t *)calloc(domain_count, sizeof(*domain_records));
|
||||||
if (domain_records == NULL) {
|
if (domain_records == NULL) {
|
||||||
amduat_fed_view_free(&view);
|
amduat_fed_view_free(&view);
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
domain_count = 0;
|
domain_count = 0;
|
||||||
|
|
@ -234,7 +236,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
||||||
}
|
}
|
||||||
free(domain_records);
|
free(domain_records);
|
||||||
amduat_fed_view_free(&view);
|
amduat_fed_view_free(&view);
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_OOM;
|
||||||
}
|
}
|
||||||
domain_count++;
|
domain_count++;
|
||||||
}
|
}
|
||||||
|
|
@ -251,20 +253,25 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records,
|
||||||
free(domain_records);
|
free(domain_records);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
amduat_fed_view_free(&view);
|
amduat_fed_view_free(&view);
|
||||||
return false;
|
return AMDUAT_FED_VIEW_ERR_INVALID;
|
||||||
}
|
}
|
||||||
if (!amduat_fed_view_append(&view,
|
{
|
||||||
domain_view.records,
|
amduat_fed_view_error_t append_err;
|
||||||
domain_view.len)) {
|
|
||||||
amduat_fed_replay_view_free(&domain_view);
|
append_err = amduat_fed_view_append(&view,
|
||||||
amduat_fed_view_free(&view);
|
domain_view.records,
|
||||||
return false;
|
domain_view.len);
|
||||||
|
if (append_err != AMDUAT_FED_VIEW_OK) {
|
||||||
|
amduat_fed_replay_view_free(&domain_view);
|
||||||
|
amduat_fed_view_free(&view);
|
||||||
|
return append_err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
amduat_fed_replay_view_free(&domain_view);
|
amduat_fed_replay_view_free(&domain_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_view = view;
|
*out_view = view;
|
||||||
return true;
|
return AMDUAT_FED_VIEW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void amduat_fed_view_free(amduat_fed_view_t *view) {
|
void amduat_fed_view_free(amduat_fed_view_t *view) {
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,8 @@ static int test_view_and_resolve(void) {
|
||||||
denies[0].id.ref = ref_d;
|
denies[0].id.ref = ref_d;
|
||||||
denies[0].reason_code = 0;
|
denies[0].reason_code = 0;
|
||||||
|
|
||||||
if (!amduat_fed_view_build(records, 4, 1, bounds, 2, denies, 1, &view)) {
|
if (amduat_fed_view_build(records, 4, 1, bounds, 2, denies, 1, &view) !=
|
||||||
|
AMDUAT_FED_VIEW_OK) {
|
||||||
fprintf(stderr, "view build failed\n");
|
fprintf(stderr, "view build failed\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +168,8 @@ static int test_view_conflict(void) {
|
||||||
bounds[1].snapshot_id = 1;
|
bounds[1].snapshot_id = 1;
|
||||||
bounds[1].log_prefix = 10;
|
bounds[1].log_prefix = 10;
|
||||||
|
|
||||||
if (amduat_fed_view_build(records, 2, 1, bounds, 2, NULL, 0, &view)) {
|
if (amduat_fed_view_build(records, 2, 1, bounds, 2, NULL, 0, &view) !=
|
||||||
|
AMDUAT_FED_VIEW_ERR_CONFLICT) {
|
||||||
fprintf(stderr, "expected conflict\n");
|
fprintf(stderr, "expected conflict\n");
|
||||||
amduat_fed_view_free(&view);
|
amduat_fed_view_free(&view);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -191,11 +193,13 @@ static int test_view_rebuild_metadata(void) {
|
||||||
bounds[0].snapshot_id = 1;
|
bounds[0].snapshot_id = 1;
|
||||||
bounds[0].log_prefix = 10;
|
bounds[0].log_prefix = 10;
|
||||||
|
|
||||||
if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_a)) {
|
if (amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_a) !=
|
||||||
|
AMDUAT_FED_VIEW_OK) {
|
||||||
fprintf(stderr, "view rebuild build A failed\n");
|
fprintf(stderr, "view rebuild build A failed\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_b)) {
|
if (amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_b) !=
|
||||||
|
AMDUAT_FED_VIEW_OK) {
|
||||||
fprintf(stderr, "view rebuild build B failed\n");
|
fprintf(stderr, "view rebuild build B failed\n");
|
||||||
amduat_fed_view_free(&view_a);
|
amduat_fed_view_free(&view_a);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue