# 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/1` - `ASL/FEDERATION-REPLAY/1` - `ASL/DOMAIN-MODEL/1` - `ASL/POLICY-HASH/1` - `ENC/ASL-CORE-INDEX/1` (canonical in `vendor/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_open` - `amduat_fed_coord_close` - `amduat_fed_coord_load_registry` - `amduat_fed_coord_set_admitted` - `amduat_fed_coord_tick` - `amduat_fed_coord_resolve` Implemented in core (already available): - `amduat_fed_registry_*` - `amduat_fed_ingest_validate` - `amduat_fed_replay_build` - `amduat_fed_view_build` - `amduat_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_ok` is 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_build` rather 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 to `logseq <= log_prefix`. - Tombstones and shadowing apply only within the source domain. - Use `vector_epoch` to swap snapshot vectors atomically after a build. - Persist registry updates atomically via `amduat_fed_registry_store_put` before 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 != 0` and `policy_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_build` over the staged records, with optional policy denies derived from coordinator hooks. - Swap snapshot vector atomically after registry updates. ### Resolve Loop - Call `amduat_fed_resolve` against the latest built `amduat_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); } ```