11 KiB
Federation Coordinator Middle Layer Spec
This document specifies the middle-layer coordinator that orchestrates federation above the core C substrate. It remains transport- and policy-aware while keeping core semantics unchanged.
Scope and Goals
- Maintain per-domain replay bounds and an admitted set.
- Fetch and ingest published records from remotes.
- Build federation views via core APIs.
- Resolve missing bytes by fetching artifacts into a cache store.
- Keep storage layout private (no extents or blocks exposed).
- Align with tier1 federation semantics and replay determinism.
- Federation view is the union of admitted domains regardless of topology.
Non-goals:
- Re-implement core semantics in this layer.
- Introduce a single global snapshot ID (federation is per-domain).
Core Dependencies
The coordinator calls into vendor core APIs (vendor/amduat/include/amduat/fed/*)
and MUST stay aligned with their signatures:
amduat_fed_registry_*(persist per-domain admission state)amduat_fed_ingest_validate(record validation + conflict detection)amduat_fed_replay_build(deterministic replay per domain)amduat_fed_view_build+amduat_fed_resolve(build view and resolve ref-only)
Core expects per-domain replay bounds {domain_id, snapshot_id, log_prefix} and
does not handle transport, auth, caching policy, or remote fetch.
Alignment note: the daemon-layer API in this repo still needs updates to match current vendor core types and signatures. This spec reflects vendor headers as the source of truth.
Tier1 alignment (normative):
ASL/FEDERATION/1ASL/FEDERATION-REPLAY/1ASL/DOMAIN-MODEL/1ASL/POLICY-HASH/1ENC/ASL-CORE-INDEX/1(canonical invendor/amduat/tier1/enc-asl-core-index-1.md)
Data Structures
Registry State (per domain, persisted)
struct amduat_fed_domain_state {
uint32_t domain_id;
uint64_t snapshot_id;
uint64_t log_prefix;
uint64_t last_logseq;
uint8_t admitted;
uint8_t policy_ok;
uint8_t reserved[6];
amduat_hash_id_t policy_hash_id;
amduat_octets_t policy_hash; /* Empty when unknown; caller-owned bytes. */
};
Registry bytes are stored via amduat_fed_registry_store_* in the local ASL
store; the coordinator owns admission workflows and policy compatibility checks.
Admitted Set (in-memory)
struct amduat_fed_admitted_set {
uint32_t *domain_ids; // sorted, unique
size_t len;
};
The admitted set is derived from registry entries with admitted != 0 and
policy_ok != 0.
Snapshot Vector (per view build)
struct amduat_fed_snapshot_vector {
amduat_fed_view_bounds_t *bounds;
size_t len;
uint64_t vector_epoch;
};
Record Staging (per fetch batch)
struct amduat_fed_record {
amduat_fed_record_meta_t meta;
amduat_fed_record_id_t id;
uint64_t logseq;
uint64_t snapshot_id;
uint64_t log_prefix;
};
Records MUST carry the fields required by ASL/FEDERATION-REPLAY/1, and replay
ordering MUST be deterministic (sort by logseq, then canonical identity).
Record metadata includes visibility and optional cross-domain source identity.
View and Policy Denies
struct amduat_fed_view_bounds {
uint32_t domain_id;
uint64_t snapshot_id;
uint64_t log_prefix;
};
struct amduat_fed_policy_deny {
amduat_fed_record_id_t id;
uint32_t reason_code;
};
Fetch Backlog / Retry State
struct amduat_fed_fetch_state {
uint32_t domain_id;
uint64_t next_snapshot_id;
uint64_t next_log_prefix;
uint64_t next_logseq;
uint64_t backoff_ms;
uint64_t last_attempt_ms;
uint32_t consecutive_failures;
};
Cache Store Metadata (optional)
struct amduat_fed_cache_policy {
bool enabled;
uint64_t max_bytes;
uint64_t used_bytes;
uint32_t ttl_seconds;
uint32_t prefetch_depth;
};
Coordinator Interfaces
Configuration
struct amduat_fed_coord_cfg {
const char *registry_path;
amduat_asl_store_t *authoritative_store;
amduat_asl_store_t *cache_store; // optional
amduat_asl_store_t *session_store; // optional
amduat_fed_transport transport;
amduat_fed_cache_policy cache_policy;
amduat_fed_policy_hooks policy_hooks;
};
Lifecycle and Operations
int amduat_fed_coord_open(
const struct amduat_fed_coord_cfg *cfg,
struct amduat_fed_coord **out);
int amduat_fed_coord_close(struct amduat_fed_coord *c);
int amduat_fed_coord_load_registry(struct amduat_fed_coord *c);
int amduat_fed_coord_set_admitted(
struct amduat_fed_coord *c,
uint32_t domain_id,
bool admitted);
int amduat_fed_coord_tick(struct amduat_fed_coord *c, uint64_t now_ms);
int amduat_fed_coord_resolve(
struct amduat_fed_coord *c,
amduat_reference_t ref,
amduat_artifact_t *out);
API Status
Planned coordinator surface (not yet implemented):
amduat_fed_coord_openamduat_fed_coord_closeamduat_fed_coord_load_registryamduat_fed_coord_set_admittedamduat_fed_coord_tickamduat_fed_coord_resolve
Implemented in core (already available):
amduat_fed_registry_*amduat_fed_ingest_validateamduat_fed_replay_buildamduat_fed_view_buildamduat_fed_resolve
Transport Abstraction
Minimal interface that hides protocol, auth, and topology:
struct amduat_fed_transport {
int (*get_records)(
void *ctx, uint32_t domain_id,
uint64_t snapshot_id, uint64_t log_prefix,
uint64_t from_logseq,
amduat_fed_record_iter *out_iter);
int (*get_artifact)(
void *ctx, amduat_reference_t ref,
amduat_sink *out_sink);
};
Transport MUST return records that can be validated with amduat_fed_record_validate
and MUST provide all fields required by ASL/FEDERATION-REPLAY/1.
Transport MUST NOT surface internal-only records from foreign domains.
Storage and Encodings
- The coordinator stores records/artifacts via ASL store APIs and does not touch segment layouts or extents directly.
- Federation metadata in index records is encoded by core per
ENC/ASL-CORE-INDEX/1; the coordinator must not override it. - Cache store semantics are best-effort and do not affect authoritative state.
Policies
- Admission is per-domain and controlled via registry entries and
policy_ok. - Policy compatibility uses
policy_hash_id+policy_hash(ASL/POLICY-HASH/1). policy_okis computed during admission by comparing local policy hash to the remote domain's published policy hash.- Admission and policy compatibility MUST be enforced before any foreign state is admitted into the federation view.
- Per-record filtering, if used, MUST be deterministic and SHOULD be expressed
as policy denies passed to
amduat_fed_view_buildrather than by dropping records before validation. - Cache write policy is middle-layer only (fetch-on-miss, optional prefetch).
- Eviction is local (LRU or segmented queues) and must not leak layout.
- Conflict policy: reject on identity collision with differing metadata and keep bounds stable until operator intervention (ASL/FEDERATION-REPLAY/1).
Sequencing and Consistency
- Deterministic views require a stable snapshot vector per build.
- Bounds advance only after successful ingest; they never move backwards.
- Before validation, records are ordered by
(logseq, canonical identity)and filtered tologseq <= log_prefix. - Tombstones and shadowing apply only within the source domain.
- Use
vector_epochto swap snapshot vectors atomically after a build. - Persist registry updates atomically via
amduat_fed_registry_store_putbefore swapping the snapshot vector. - If a remote retracts or regresses, keep local bounds and mark domain degraded.
Core Flow
Startup
- Load registry.
- Derive admitted set from
admitted != 0andpolicy_ok != 0. - Build initial snapshot vector.
Periodic Tick
- For each admitted domain, fetch records up to bound and validate.
- Update
last_logseq, and advance bounds if remote snapshot moves forward. - Build federation view using
amduat_fed_view_buildover the staged records, with optional policy denies derived from coordinator hooks. - Swap snapshot vector atomically after registry updates.
Resolve Loop
- Call
amduat_fed_resolveagainst the latest builtamduat_fed_view_t. - If
AMDUAT_FED_RESOLVE_FOUND_REMOTE_NO_BYTES, fetch bytes via transport into cache store. - Retry resolve after cache write completes.
Example Tick Pseudocode
int amduat_fed_coord_tick(struct amduat_fed_coord *c, uint64_t now_ms) {
amduat_fed_snapshot_vector vec = build_snapshot_vector(c);
clear(staged_records_all);
build_policy_denies(&policy_denies, &policy_denies_len);
for each domain in vec.bounds:
bound = &vec.bounds[i];
if !admitted(bound->domain_id) continue;
if backoff_active(bound->domain_id, now_ms) continue;
clear(staged_records);
iter = transport.get_records(
bound->domain_id,
bound->snapshot_id,
bound->log_prefix,
state.next_logseq);
while iter.next(record):
if !amduat_fed_record_validate(&record):
mark_domain_error(bound->domain_id);
break;
append(staged_records, record);
sort_by_logseq_then_id(staged_records);
clamp_to_log_prefix(staged_records, bound->log_prefix);
rc = amduat_fed_ingest_validate(
staged_records, staged_len, &err_index, &conflict_index);
if rc == AMDUAT_FED_INGEST_ERR_CONFLICT:
mark_domain_error(bound->domain_id);
continue;
if rc == AMDUAT_FED_INGEST_ERR_INVALID:
mark_domain_error(bound->domain_id);
continue;
update_registry_bounds(bound->domain_id, state);
append_all(staged_records_all, staged_records);
amduat_fed_view_build(
staged_records_all, staged_len_all,
local_domain_id, vec.bounds, vec.len,
policy_denies, policy_denies_len,
&view);
swap_snapshot_vector(c, build_snapshot_vector(c));
return 0;
}
Example Resolve Pseudocode
int amduat_fed_coord_resolve(
struct amduat_fed_coord *c,
amduat_reference_t ref,
amduat_artifact_t *out) {
view = c->last_view;
rc = amduat_fed_resolve(&view, c->authoritative_store, ref, out);
if (rc == AMDUAT_FED_RESOLVE_FOUND_REMOTE_NO_BYTES && c->cache_store) {
rc = transport.get_artifact(ref, sink_for_cache(c->cache_store));
if (rc == 0) {
rc = amduat_fed_resolve(&view, c->authoritative_store, ref, out);
}
}
return rc;
}
Coordinator Wiring Example
amduat_fed_transport_unix_t unix_transport;
amduat_fed_coord_cfg_t cfg;
amduat_fed_coord_t *coord = NULL;
amduat_fed_transport_unix_init(&unix_transport, "amduatd.sock");
memset(&cfg, 0, sizeof(cfg));
cfg.local_domain_id = 1;
cfg.authoritative_store = &store;
cfg.cache_store = &cache_store;
cfg.transport = amduat_fed_transport_unix_ops(&unix_transport);
if (amduat_fed_coord_open(&cfg, &coord) == AMDUAT_FED_COORD_OK) {
amduat_fed_coord_tick(coord, now_ms);
amduat_fed_coord_resolve(coord, some_ref, &artifact);
amduat_fed_coord_close(coord);
}