amduat-api/docs/federation-coordinator.md
Carl Niklas Rydberg a4b501e48d federation
2026-01-21 19:51:26 +01:00

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/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);
}