Distributed Replicas #8
syntropicagent opened 3 weeks ago

Migrated from Linear KKN-197 (https://linear.app/kokonut/issue/KKN-197/distributed-replicas)

Based on your choices — geographic edge + data residency, sharded by location_id, and extend the current Compose stacks — the most suitable approach is a Federated Edge-Replica topology: each replica is a full, self-contained Kokonut stack deployed near a region/community, owning a disjoint set of locations, with a lightweight control plane for instance discovery/trust and eventual, aggregate-only cross-replica sync built on the existing federation layer. This reuses location_id partitioning and the "configurable container" precedent instead of introducing distributed-write consistency.

Below is the plan. (Per plan mode I've made no changes — this is a proposal.)

Principles

  1. A replica = one copyable Compose stack (the unit we scale by adding more of). Reuse docker-compose*.yml overlays; parameterize, don't rewrite.
  2. location_id is the shard key. Each location has exactly one home replica; its writes happen only there. No multi-master.
  3. Cross-replica is eventual & aggregate-only. Global/DAO/federation views are built from published aggregates (the federation_share/federation_query model), never from cross-instance transactions.
  4. Per-replica strong consistency, inter-replica eventual consistency. This is what makes it tractable.

Phased Plan

Phase 0 — Instance identity & routing (foundation)

  • Add instance_registry table (or extend federation_node in schemas/postgres/122_federation.sql) with instance_id, node_url, region, trust_level, status, last_heartbeat, sync_interval_seconds, capabilities JSONB. Reuse the SyncEngine.get_nodes_due_for_sync() heartbeat pattern (services/federation/sync.py:25).
  • Add location.home_instance_id FK → instance_registry (and a source_instance_id audit column) in schemas/postgres/001_locations.sql. Default to the bootstrap instance.
  • Add instance_id resolution to services/core/ so gateway + CLIs know which replica owns a location.
  • Extend gateway capability tokens to optionally carry instance_id scope, building on the existing per-location_id check in services/security/capabilities.py:136 and services/gateway/auth.py (constant-time KOKONUT_API_KEY_SCOPES).

Phase 1 — Make a replica a parameterized, copyable unit

  • Parameterize the stack via env: INSTANCE_ID, INSTANCE_REGION, PUBLIC_BASE_URL, DB names. Add a .env.instance template.
  • Add scripts/provision-replica.sh that renders a region stack (Compose + Caddy + worker + Traefik overlay) reusing docs/deployment.md.
  • Ensure seed.sh/seed-pilot.sh seed only the locations assigned to this instance (filter by home_instance_id), or global schema + instance-scoped pilot data.
  • Base images are already @sha256-pinned (KI-49), so replicas are reproducible.

Phase 2 — Control plane: discovery, heartbeat, trust

  • Extend services/federation/ (or new services/instances/) with InstanceRegistry.register/list/heartbeat/set_trust, reusing node.py:26 patterns.
  • CLI: python3 -m services.instances register --id ... --url ... --region ..., plus list/heartbeat/discover.

Phase 3 — Cross-replica sync (eventual, aggregate/query)

  • Build on federation_share / federation_query / aggregate_regional() (services/federation/sync.py:75). Add a location-scoped outbox (replica_outbox) capturing per-location summaries/metric rollups that home replicas publish to peers on sync_interval_seconds.
  • Cross-replica reads: extend federation_query to route "query location X owned by instance Y" via the registry.
  • Governance/DAO/federation views (federation_mutual_aid, state_of_kokonut) become the cross-replica aggregates — already location-aggregated.

Phase 4 — Routing & external surface

  • Thin global ingress (external Caddy/Traefik) resolves location_id → home instance (via registry) and forwards; Directus/Metabase admin stay per-replica, public read APIs are a federation read layer that unions per-instance v_public_* views.
  • Gateway per replica with name:resource:action:instance scope format.

Phase 5 — Store-level HA within a replica (optional, per-replica)

  • Postgres streaming replica (primary + standby); make services/ingestion/base.py get_db() (currently single-host, :43) multi-host/read-replica aware.
  • ClickHouse: replicated_merge_tree + Keeper cluster; add <remote_servers> to config/clickhouse/config.d/network.xml (currently empty).
  • Redis Sentinel if needed.

Phase 6 — Observability, backup, DR

  • Per-replica backups already in config/worker/crontab; add a global registry-health dashboard.
  • Gate provisioning on replication_readiness_assessment (docs/regenerative-outcomes.md).

Recommended MVP slice (do this first)

Phase 0 + Phase 1 + minimal Phase 2: add instance_registry + location.home_instance_id, parameterize the Compose stack into a copyable unit, add provision-replica.sh, and a basic instances register/list/heartbeat CLI. This delivers "scale by standing up another self-contained replica that owns a set of locations," with sync added incrementally later.

Deliberately avoided: active-active multi-master sync, a Kubernetes migration, and per-tenant schema rewrites — the sharded-by-location model makes all three unnecessary for the stated goal.

Risks / open questions

  • Registry is a shared dependency: keep it lightweight; bootstrap instance holds the canonical registry, peers cache + replicate it.
  • Location migration between instances (rare): handled by a supervised move job, not live sync.
  • Network egress between edge replicas for federation sync must be permitted.
  • Cross-replica transactions: none by design — acceptable since global views are aggregate/governance, not operational.

Distributed Replicas Implementation Plan

Target Architecture

Use federated regional replicas, each running a complete Kokonut stack:

Global control plane
  ├── Instance registry
  ├── Location ownership directory
  ├── Trust and routing metadata
  └── Global health view
Regional Replica A
  ├── PostgreSQL primary + standby
  ├── ClickHouse
  ├── Directus
  ├── Gateway
  ├── Workers
  └── Owns locations A1...An
Regional Replica B
  ├── PostgreSQL primary + standby
  ├── ClickHouse
  ├── Directus
  ├── Gateway
  ├── Workers
  └── Owns locations B1...Bn

Core rules:

  • Every location has exactly one home_instance_id.
  • Operational writes occur only on the home replica.
  • Cross-replica data is eventually consistent.
  • Only approved aggregates and metadata are exchanged by default.
  • No cross-replica transaction spans multiple PostgreSQL instances.
  • A location migration is an explicit, human-approved operation.

A new platform_instance registry is preferable to overloading federation_node: federation_node represents data-sharing peers, while platform_instance represents infrastructure ownership and routing.


Phase 0: Instance Identity and Location Routing

Objective

Introduce platform-instance identity and assign every location to a home replica without changing deployment behavior.

Schema

Add a new migration, using the next available migration number:

platform_instance

Suggested fields:

  • id UUID PRIMARY KEY
  • instance_code TEXT UNIQUE
  • display_name TEXT
  • region TEXT
  • node_url TEXT
  • public_key TEXT
  • trust_level
  • status: provisioning, active, draining, suspended, offline
  • capabilities JSONB
  • last_heartbeat_at
  • created_at, updated_at

Extend location with:

  • home_instance_id UUID
  • ownership_status
  • ownership_version
  • ownership_changed_at
  • ownership_changed_by

Initially allow home_instance_id to be nullable for migration compatibility. Backfill all existing locations to the bootstrap instance, then add the NOT NULL constraint.

Add indexes:

location(home_instance_id)
platform_instance(status, region)
platform_instance(last_heartbeat_at)

Do not add instance_id to every business table initially. The ownership relationship should be inherited through location_id.

Services

Add services/instances/:

  • registry.py
  • ownership.py
  • heartbeat.py
  • models.py
  • cli.py

Core operations:

  • Register instance
  • Activate/suspend instance
  • Record heartbeat
  • List instances by region/status
  • Assign location ownership
  • Resolve home instance for a location
  • Begin/complete ownership transfer

Add a bootstrap instance seed that is idempotent.

Gateway

Extend capability validation so capabilities may optionally include:

{
  \"resource\": \"harvest_event\",
  \"action\": \"write\",
  \"location_id\": \"LOCATION_UUID\",
  \"instance_id\": \"INSTANCE_UUID\"
}

The gateway must reject a request when:

  • The token is scoped to another instance.
  • The location belongs to another instance.
  • The instance is not active.
  • The request is routed to a non-home replica.

Tests

Add:

  • Instance registration tests
  • Duplicate instance code tests
  • Location ownership assignment tests
  • Ownership transfer state tests
  • Gateway instance/location mismatch tests
  • Bootstrap backfill migration test
  • Capability-token instance scoping tests

Completion Gate

Phase 0 is complete when:

  • All existing locations have a home instance.
  • A request can resolve location_id → home_instance_id.
  • The current single-stack deployment works as the bootstrap instance.
  • No existing CLI or service silently writes to a non-home location.

Phase 1: Parameterized, Copyable Compose Replica

Objective

Make the existing Compose stack deployable multiple times with different instance identity and storage.

Configuration

Add instance-level environment variables:

KOKONUT_INSTANCE_ID
KOKONUT_INSTANCE_CODE
KOKONUT_INSTANCE_REGION
KOKONUT_INSTANCE_PUBLIC_URL
KOKONUT_CONTROL_PLANE_URL
KOKONUT_FEDERATION_SIGNING_KEY
KOKONUT_FEDERATION_TRUST_BUNDLE

Keep database credentials and private keys instance-specific.

Add an instance environment template, for example:

.env.instance.example

Never include real secrets or private keys.

Compose Changes

Parameterize:

  • Project name
  • Container names
  • Volume names
  • Caddy hostnames
  • Database names
  • Worker identity
  • MQTT broker identity
  • Public URLs
  • Health-check labels

Preserve the existing two-network model:

  • databases
  • apps

Keep PostgreSQL, ClickHouse, Directus, and Redis private.

Add an explicit replica label to services:

com.kokonut.instance=${KOKONUT_INSTANCE_CODE}

Provisioning

Add:

scripts/provision-replica.sh
scripts/check-replica-config.sh

Provisioning should:

  1. Validate required instance variables.
  2. Validate region and instance code.
  3. Verify image digest pinning.
  4. Generate or validate the instance trust bundle.
  5. Start the stack.
  6. Run migrations.
  7. Register the instance.
  8. Wait for health checks.
  9. Confirm database ownership metadata.
  10. Emit a machine-readable deployment result.

Do not make provisioning automatically assign locations. Ownership assignment must be explicit.

Seed Behavior

Update seed behavior so:

  • Base schemas are applied globally.
  • Instance metadata is seeded locally.
  • Pilot/location data is filtered by assignment.
  • Re-running seeds remains idempotent.
  • A replica cannot accidentally seed another replica's private location data.

Tests

Add:

  • Compose configuration validation
  • Per-instance volume-name isolation
  • Missing-variable failures
  • Duplicate instance-code detection
  • Provisioning dry-run tests
  • Seed isolation tests
  • Health-check tests

Completion Gate

Phase 1 is complete when two isolated Compose stacks can run simultaneously on separate hosts using:

  • Separate database volumes
  • Separate ClickHouse volumes
  • Separate Caddy endpoints
  • Separate instance identities
  • No port or container-name collisions

Phase 2: Instance Registry, Discovery, Heartbeats, and Trust

Objective

Create a reliable control-plane protocol for replica membership and trust.

Registry Model

Implement instance lifecycle:

provisioning → active → draining → offline
                         ↘ suspended

Only active instances may receive writes or publish synchronization data.

Use platform_instance as the authoritative infrastructure registry. Link a platform instance to federation_node only where it participates in data federation.

Protocol

Add signed instance-to-instance messages with:

  • Sender instance ID
  • Recipient instance ID
  • Message ID
  • Message type
  • Created timestamp
  • Expiry timestamp
  • Nonce
  • Payload hash
  • Signature

Message types:

  • heartbeat
  • ownership_manifest
  • aggregate_share
  • query_request
  • query_response
  • sync_ack
  • ownership_transfer

Use replay protection:

  • Unique message ID
  • Nonce tracking
  • Expiry validation
  • Signature verification
  • Idempotent processing

Do not rely on the existing node_public_key field alone without defining key rotation and trust semantics.

API

Expose authenticated internal endpoints through the gateway or a dedicated internal service:

POST /internal/instances/heartbeat
POST /internal/instances/sync
POST /internal/instances/query
GET  /internal/instances/{id}/ownership

These routes must:

  • Require mutual authentication or signed capability tokens.
  • Be private by default.
  • Use explicit route policies.
  • Log all accepted and rejected requests.

CLI

Add:

python3 -m services.instances register
python3 -m services.instances list
python3 -m services.instances heartbeat
python3 -m services.instances activate
python3 -m services.instances suspend
python3 -m services.instances assign-location
python3 -m services.instances ownership

Tests

Add:

  • Signature verification tests
  • Expired-message tests
  • Replay tests
  • Trust-level authorization tests
  • Heartbeat timeout tests
  • Suspended-instance routing tests
  • Ownership manifest validation tests
  • Internal route authorization tests

Completion Gate

Phase 2 is complete when:

  • A new replica can register itself.
  • The control plane can detect stale/offline replicas.
  • Two replicas can authenticate each other.
  • Trust changes are explicit and auditable.
  • Location ownership can be queried from either replica.

Phase 3: Eventual Cross-Replica Synchronization

Objective

Exchange approved data between replicas without creating distributed transactions or leaking private data.

The current federation_share stores aggregate JSONB and federation_query stores query state. Retain those concepts, but add a durable outbox and delivery protocol.

Outbox Schema

Add:

replica_outbox

Suggested fields:

  • id UUID
  • source_instance_id
  • destination_instance_id
  • entity_type
  • entity_id
  • location_id
  • event_type
  • aggregate_version
  • payload JSONB
  • payload_hash
  • idempotency_key
  • status: pending, leased, delivered, failed, dead_letter
  • attempt_count
  • next_attempt_at
  • leased_until
  • last_error
  • created_at
  • delivered_at

Add:

replica_inbox

to provide durable deduplication and inbound message tracking.

Data Classification

Define explicit synchronization classes:

| Class | Default behavior | | -- | -- | | Private operational records | Never replicated by default | | Verified public aggregates | Replicated | | Governance summaries | Replicated if consented | | Location metadata | Replicated selectively | | Raw sensor data | Kept local unless explicitly approved | | Evidence hashes/CIDs | Replicated | | Credentials/secrets | Never replicated | | Draft/agent records | Local unless explicitly requested |

The platform's existing governed lifecycle and privacy rules remain authoritative.

Aggregate Export

Create an export layer that produces approved, deterministic payloads:

services/federation/exports/
  metrics.py
  governance.py
  environmental.py
  financial.py
  registry.py

Every export should include:

  • Source instance
  • Location IDs
  • Reporting period
  • Evidence maturity
  • Verification/publication state
  • Calculation version
  • Payload hash
  • Export timestamp

Do not replicate arbitrary table rows as the first implementation.

Delivery Engine

Extend SyncEngine with:

  • Batch claiming using leases
  • Exponential retry
  • Maximum retry count
  • Dead-letter disposition
  • Idempotent delivery
  • Delivery acknowledgements
  • Per-destination backpressure
  • Sync lag measurement

Use PostgreSQL durable state for scheduler claims and leases, consistent with repository integrity requirements.

Query Federation

Extend federation_query to support:

  • Target instance
  • Requested location IDs
  • Allowed data types
  • Consent scope
  • Query expiry
  • Result hash
  • Requester identity

Queries must be allowlisted. Do not permit arbitrary SQL over the network.

Location Ownership Transfer

Implement a controlled transfer workflow:

requested
→ approved
→ exporting
→ transferred
→ verified
→ retired

The transfer must:

  1. Freeze writes to the location.
  2. Produce a signed export package.
  3. Verify destination capacity.
  4. Import into the destination.
  5. Validate hashes and row counts.
  6. Update home_instance_id.
  7. Resume writes.
  8. Retain an audit record.

Tests

Add:

  • Outbox idempotency tests
  • Lease and retry tests
  • Dead-letter tests
  • Payload hash tests
  • Consent filtering tests
  • Export determinism tests
  • Duplicate inbound message tests
  • Sync lag tests
  • Ownership transfer tests
  • Cross-replica query authorization tests

Completion Gate

Phase 3 is complete when:

  • Two replicas exchange verified aggregate data.
  • Duplicate delivery does not duplicate state.
  • Failed deliveries retry safely.
  • Private data is not exported accidentally.
  • Operators can replay or dispose of dead letters.
  • Cross-replica queries are allowlisted and auditable.

Phase 4: Global Routing and External API Surface

Objective

Route users and services to the correct home replica while preserving locality and fail-closed authorization.

Routing Model

Introduce a global routing layer that resolves:

location_id → home_instance_id → instance_url

Use a cached signed ownership manifest to avoid making every request depend on a live central database.

The routing layer should:

  • Cache ownership with a short TTL.
  • Reject stale ownership when safety requires it.
  • Refresh after ownership changes.
  • Route public reads to the home replica.
  • Route writes only to the home replica.
  • Return a clear 409/421-style ownership error when misrouted.

Gateway Changes

Extend gateway route context with:

instance_id
location_id
request_origin
routing_version

Add:

  • Instance-aware API scopes
  • Location ownership enforcement
  • Signed inter-instance requests
  • Request correlation IDs
  • Forwarded-request audit metadata
  • Rate limits per instance and tenant/location

Do not expose database connections or internal service URLs.

Public Read Layer

For global reports:

  • Query approved aggregate shares.
  • Do not synchronously fan out to every replica for every request.
  • Cache aggregate results.
  • Include freshness and source-instance metadata.
  • Expose uncertainty when one or more replicas are offline.

For operational location views:

  • Route directly to the home replica.
  • Include replica health/freshness metadata.

Caddy/Traefik

Use the current Caddy/Traefik deployment patterns for:

  • Regional hostnames
  • Internal federation endpoints
  • Public API ingress
  • TLS termination
  • Security headers
  • Per-instance routing

Do not make Caddy directly responsible for database ownership logic. Put ownership-aware routing in the gateway or dedicated routing service.

Tests

Add:

  • Correct-home routing tests
  • Misroute rejection tests
  • Stale-manifest tests
  • Offline-replica behavior tests
  • Public aggregate freshness tests
  • API-key instance-scope tests
  • Forwarded-request signature tests
  • Rate-limit isolation tests

Completion Gate

Phase 4 is complete when:

  • A client can use one global endpoint.
  • Location requests route to the correct replica.
  • Global reports remain available when one replica is offline, with freshness warnings.
  • Writes never silently land on a non-home replica.
  • Directus and database services remain private.

Phase 5: Stateful High Availability Within Each Replica

Objective

Prevent a single database failure from taking down an entire regional replica.

This phase should be implemented per replica, not as cross-region multi-master replication.

PostgreSQL

Topology:

PostgreSQL primary
  └── PostgreSQL streaming standby

Implement:

  • Streaming replication
  • Replication slots
  • WAL retention policy
  • Standby health checks
  • Automatic or operator-approved failover
  • Backup verification
  • Restore drills
  • PgBouncer or equivalent connection endpoint if needed

Update connection configuration in services/ingestion/base.py and shared database helpers to support:

  • Primary host
  • Read host
  • Failover host
  • Connection timeout
  • Retry policy
  • target_session_attrs=read-write for write connections

Keep writes primary-only.

For analytics workloads, use a read-only connection pool/endpoint where safe. Do not route governance or lifecycle writes to read replicas.

PostgreSQL Failure Handling

Define explicit states:

primary healthy
standby catching up
standby ready
failover requested
failover completed
replication degraded

Add operator commands:

python3 -m services.ha postgres status
python3 -m services.ha postgres promote
python3 -m services.ha postgres rejoin
python3 -m services.ha postgres verify

Promotion should require an explicit operator or approved automation policy. Avoid split-brain.

ClickHouse

Start with one ClickHouse node per replica plus verified backups. Add replicated storage only when the operational SLO requires it.

For replicated ClickHouse:

  • Add ClickHouse Keeper.
  • Define remote_servers.
  • Use ReplicatedMergeTree tables.
  • Add shard/replica macros.
  • Define distributed tables only where query semantics are clear.
  • Preserve PostgreSQL as the canonical governed store.
  • Ensure analytical replays are idempotent.

Do not introduce ClickHouse replication before defining:

  • Table engine conversion strategy
  • Keeper backup/restore
  • Replica lag monitoring
  • Query behavior during partial availability

Redis

For Redis:

  • Start with persistence and restore validation.
  • Add Sentinel for failover if Redis state becomes operationally critical.
  • Keep durable scheduler/event state in PostgreSQL, not Redis.

HA Tests

Add:

  • PostgreSQL standby lag tests
  • Primary failure and promotion drill
  • Read/write routing tests
  • Split-brain prevention tests
  • Backup restore tests
  • ClickHouse replica lag tests
  • Keeper failure tests
  • Redis failover tests
  • Application reconnect tests

Completion Gate

Phase 5 is complete when each production replica has:

  • Tested database failover
  • Verified backups
  • Documented RTO/RPO
  • No unsafe write routing to standby
  • Alerting for replication lag
  • A repeatable recovery procedure

Phase 6: Observability, Backup, Disaster Recovery, and Operations

Objective

Make the distributed system operable at scale, with visibility into ownership, sync, routing, storage, and recovery.

Metrics

Add metrics for:

Instance health

  • Heartbeat age
  • Instance status
  • Region
  • Version
  • Capacity
  • Active location count

Routing

  • Requests by home instance
  • Misrouted requests
  • Routing-manifest age
  • Ownership lookup latency
  • Offline-instance requests

Synchronization

  • Outbox depth
  • Sync lag
  • Delivery success rate
  • Retry count
  • Dead-letter count
  • Inbound deduplication count
  • Aggregate freshness

Database HA

  • PostgreSQL replication lag
  • WAL retention
  • Standby state
  • ClickHouse replica lag
  • Keeper health
  • Backup age

Data governance

  • Exported records by consent level
  • Failed privacy filters
  • Unverified aggregate rejection count
  • Ownership transfer status

Use the repository's existing logging conventions and health checks rather than adding unstructured print() calls.

Health Endpoints

Extend health reporting with:

/services
/instances
/routing
/sync
/storage
/backups

Health responses should distinguish:

  • Healthy
  • Degraded
  • Stale
  • Offline
  • Unsafe for writes

A replica with stale ownership or failed sync may remain available for local reads but must not automatically accept new ownership or transfer operations.

Backups

Each replica needs:

  • PostgreSQL base backups
  • WAL archiving
  • ClickHouse backups
  • Redis backups where relevant
  • Configuration/trust-bundle backups
  • Encrypted offsite copy
  • Retention policy
  • Restore verification

Never back up secrets in plaintext or publish private evidence payloads.

Disaster Recovery

Document and test:

  1. Complete regional replica loss.
  2. Bootstrap recovery.
  3. Temporary location reassignment.
  4. Federation registry recovery.
  5. Outbox replay.
  6. Duplicate message handling.
  7. Ownership manifest reconstruction.
  8. Return of the recovered region.
  9. Controlled ownership transfer back.

Operator Tooling

Add:

python3 -m services.distributed status
python3 -m services.distributed instances
python3 -m services.distributed ownership
python3 -m services.distributed sync-lag
python3 -m services.distributed dead-letters
python3 -m services.distributed backup-status
python3 -m services.distributed recovery-check

All replay, disposal, failover, and ownership changes should require an explicit operator identity and produce an audit record.

Dashboards and Alerts

Create dashboards for:

  • Regional replica health
  • Location ownership
  • Federation sync
  • Database replication
  • Backup status
  • Routing errors
  • Data freshness
  • Capacity by region

Alert on:

  • Missed heartbeats
  • Ownership lookup failure
  • Sync lag above threshold
  • Outbox growth
  • Dead-letter creation
  • PostgreSQL replication lag
  • Backup age beyond policy
  • Unexpected location writes
  • Trust-level changes
  • Repeated signature failures

Completion Gate

Phase 6 is complete when:

  • Operators can identify the health and ownership state of every replica.
  • Backups are automatically verified.
  • A full regional recovery drill succeeds.
  • Sync failures are replayable or explicitly disposable.
  • RTO/RPO targets are measured rather than assumed.
  • Audit records exist for all privileged distributed-system actions.

Cross-Phase Delivery Order

Use separate branches and PRs, following the repository workflow:

  1. feat/distributed-instance-identity
  2. feat/distributed-compose-provisioning
  3. feat/distributed-instance-registry
  4. feat/distributed-replica-sync
  5. feat/distributed-global-routing
  6. feat/distributed-replica-ha
  7. feat/distributed-operations

Each phase should merge only after its migration, focused tests, security review, and rollback procedure are complete.

Phases can be partially parallelized:

  • Phase 1 can begin after the Phase 0 schema contract is agreed.
  • Phase 2 can begin while Phase 1 provisioning is implemented.
  • Phase 3 depends on Phase 0 ownership and Phase 2 identity/trust.
  • Phase 4 depends on Phase 0 routing metadata and Phase 2 registry.
  • Phase 5 can begin independently for the bootstrap replica, but should not be treated as cross-replica consistency.
  • Phase 6 should begin during Phase 2 and mature through every later phase.

Milestone A: Two isolated replicas

  • Phase 0 complete
  • Phase 1 complete
  • Two Compose stacks running
  • Locations explicitly assigned
  • No cross-replica writes

Milestone B: Trusted federation

  • Phase 2 complete
  • Signed heartbeats
  • Instance trust lifecycle
  • Ownership manifests

Milestone C: Aggregate synchronization

  • Phase 3 complete
  • Durable outbox/inbox
  • Approved aggregate exchange
  • Replay/dead-letter handling

Milestone D: Global access

  • Phase 4 complete
  • Location-aware routing
  • Global aggregate read API
  • Fail-closed misrouting behavior

Milestone E: Production resilience

  • Phase 5 complete
  • Per-replica failover
  • Backup/restore drills

Milestone F: Operable federation

  • Phase 6 complete
  • Dashboards, alerts, recovery procedures, and measured RTO/RPO

Key Decisions to Preserve

  • Do not implement multi-master PostgreSQL.
  • Do not replicate raw private records by default.
  • Do not use arbitrary remote SQL queries.
  • Do not make federation availability a prerequisite for local writes.
  • Do not silently transfer location ownership.
  • Do not route writes to a stale or non-home replica.
  • Do not introduce Kubernetes until Compose-based replica semantics are proven.
  • Keep PostgreSQL/Directus canonical and ClickHouse analytical.
  • Preserve human approval boundaries for ownership transfer, failover, replay, and disposal.
1/1
Type
New Feature
Priority
Major
Assignees
Not assigned
Iterations
Issue Votes (0)
Watchers (1)
Reference
KI-8
Please wait...
Connection lost or session expired, reload to recover
Page is in error, reload to recover