Skip to content

Scaffolding: Federation sync app creation#298

Open
klpoland wants to merge 15 commits into
masterfrom
feature-kpoland-federation-sync-scaffolding
Open

Scaffolding: Federation sync app creation#298
klpoland wants to merge 15 commits into
masterfrom
feature-kpoland-federation-sync-scaffolding

Conversation

@klpoland

@klpoland klpoland commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

In this PR:

  • [NEW] federation/ (v1) application with:
    • services:
      • bootstrap.py: for initialization of federated data from home site and peers (including when new peer is added)
      • fed_index.py: for handling (external peer) asset indexing on webhook events
      • local_events.py: for handling redis dispatches to fetch local data
      • peer registry.py: for registering new peers and holding site information from site hellos
      • peer_sync.py: for packaging and sending asset data to peer sites (through their webhook routes)
    • routes:
      • health.py: (stub for now) for pinging site health (is connection established?)
      • webhooks.py: for receiving payloads FROM peers to index into LOCAL federated asset indices as well as a site-hello hook to confirm federation between peers
    • schemas and models: for type checking and providing data structures to federation configs, events, and documents (how asset docs are represented by FederatedDatasetDoc, FederatedCaptureDoc may warrant further consideration re: anticipating schema flexibility)
    • a main app that subscribes to redis events for picking up signals from gateway (handled in PR Scaffolding: Federation gateway setup #299)

Note

Medium Risk
New cross-site data path and webhook surface affect federated search consistency; webhook auth is config allowlists only (no signing/mTLS on payloads yet), and startup bootstrap errors are logged but do not block the app.

Overview
Introduces a new federation/ FastAPI sync service that keeps federated dataset/capture metadata in shared OpenSearch fed-* indices and propagates changes between configured peer sites.

On startup it optionally runs bootstrap that currently skips gateway export pulls and only sends site-hello to peers; a background Redis subscriber on federation:events:{site} loads the local asset from OpenSearch (after gateway indexing) and POSTs asset-updated webhooks to peers. Inbound /sync/api/v1/webhook/* routes index peer payloads with allowlisted site_name checks and register peers via site-hello. /health reports Redis, OpenSearch, config, and subscriber status.

Repo wiring adds Docker/compose, justfile, example federation.toml, dev mTLS cert scripts, and pre-commit (ruff/pyrefly/deptry) scoped to federation/, plus a broad pytest suite (mesh, webhooks, Redis pipeline, bootstrap helpers).

Reviewed by Cursor Bugbot for commit 155b128. Bugbot is set up for automated code reviews on this repo. Configure here.

@klpoland klpoland self-assigned this Jun 19, 2026
@klpoland klpoland added feature New feature or request federation Federation related work (sync service, document storage, peer configuration, etc.) labels Jun 19, 2026
@semanticdiff-com

semanticdiff-com Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review changes with  SemanticDiff

Changed Files
File Status
  .pre-commit-config.yaml  4% smaller
  federation/.envs/example/sync.env Unsupported file format
  federation/.gitignore Unsupported file format
  federation/Dockerfile Unsupported file format
  federation/certs/.gitignore Unsupported file format
  federation/compose.local.yaml  0% smaller
  federation/compose.production.yaml  0% smaller
  federation/federation.example.toml Unsupported file format
  federation/justfile Unsupported file format
  federation/pyproject.toml Unsupported file format
  federation/scripts/env-selection.sh Unsupported file format
  federation/scripts/generate-dev-certs.sh Unsupported file format
  federation/scripts/simulate_redis_event.py  0% smaller
  federation/sds_federation/__init__.py  0% smaller
  federation/sds_federation/main.py  0% smaller
  federation/sds_federation/models.py  0% smaller
  federation/sds_federation/routes/__init__.py  0% smaller
  federation/sds_federation/routes/health.py  0% smaller
  federation/sds_federation/routes/webhooks.py  0% smaller
  federation/sds_federation/schemas/__init__.py  0% smaller
  federation/sds_federation/schemas/webhooks.py  0% smaller
  federation/sds_federation/services/__init__.py  0% smaller
  federation/sds_federation/services/bootstrap.py  0% smaller
  federation/sds_federation/services/fed_index.py  0% smaller
  federation/sds_federation/services/fed_search.py  0% smaller
  federation/sds_federation/services/local_events.py  0% smaller
  federation/sds_federation/services/operational.py  0% smaller
  federation/sds_federation/services/peer_registry.py  0% smaller
  federation/sds_federation/services/peer_sync.py  0% smaller
  federation/sds_federation/services/redis_channel.py  0% smaller
  federation/sds_federation/testing/__init__.py  0% smaller
  federation/sds_federation/testing/sample_data.py  0% smaller
  federation/tests/__init__.py  0% smaller
  federation/tests/conftest.py  0% smaller
  federation/tests/support/__init__.py  0% smaller
  federation/tests/support/federation_mesh.py  0% smaller
  federation/tests/support/gateway_export_mock.py  0% smaller
  federation/tests/support/mock_opensearch.py  0% smaller
  federation/tests/support/mock_peer_registry.py  0% smaller
  federation/tests/test_integration_bootstrap.py  0% smaller
  federation/tests/test_integration_mesh.py  0% smaller
  federation/tests/test_integration_pipeline.py  0% smaller
  federation/tests/test_integration_webhooks.py  0% smaller
  federation/tests/test_operational.py  0% smaller
  federation/tests/test_redis_event_pipeline.py  0% smaller
  federation/tests/test_regression_fed_search.py  0% smaller
  federation/tests/test_regression_indexer.py  0% smaller
  federation/tests/test_regression_redis_channel.py  0% smaller
  federation/tests/test_regression_schemas.py  0% smaller
  federation/uv.lock Unsupported file format

@klpoland klpoland changed the title Scaffolding: Federation Sync App Scaffolding: Federation sync app creation Jun 19, 2026
@klpoland klpoland marked this pull request as ready for review June 25, 2026 19:50
@klpoland klpoland requested a review from lucaspar June 25, 2026 19:50
Comment thread federation/sds_federation/services/local_events.py
Comment thread federation/sds_federation/services/local_events.py
Comment thread federation/sds_federation/services/bootstrap.py Outdated
Comment thread federation/compose.yaml Outdated
Comment thread federation/sds_federation/services/local_events.py Outdated
Comment thread federation/sds_federation/services/fed_index.py Outdated
Comment thread federation/pyproject.toml
Comment thread federation/pyproject.toml Outdated
@lucaspar

Copy link
Copy Markdown
Member

I'll add more comments after those are addressed to reduce the noise

Comment thread federation/sds_federation/services/local_events.py
body = payload.model_dump(mode="json")
path = payload.asset_type.webhook_path
for peer in config.peers:
url = peer_webhook_url(peer, path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outbound sync ignores peer registry

Medium Severity

push_asset_updated_to_peers always uses static config.peers URLs from federation.toml, while site-hello updates PeerRegistry with each peer’s declared sync_service_url. Outbound webhooks never read the registry, so post-hello URL data is unused.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d0608d2. Configure here.

Comment thread federation/sds_federation/services/fed_index.py
Comment thread federation/sds_federation/routes/webhooks.py
Comment thread federation/justfile Outdated
Comment thread federation/sds_federation/services/bootstrap.py
Comment thread federation/sds_federation/main.py
Comment thread federation/sds_federation/main.py
Comment thread federation/sds_federation/services/fed_index.py
asset_type.value,
uuid,
)
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redis race drops peer sync

Medium Severity

For non-delete Redis events, handle_redis_asset_event loads the asset from local fed-* OpenSearch and returns without notifying peers when the document is missing. If the gateway publishes Redis before the federated index write is visible, peer sites never receive that update and the event is not retried.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b0bc3f5. Configure here.

Comment thread federation/sds_federation/services/fed_search.py

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.

There are 9 total unresolved issues (including 5 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 155b128. Configure here.

retries: 3
networks:
- sds-network-local
- sds-network-local-opensearch

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong OpenSearch Docker network

High Severity

The sds-federation-local-sync service cannot resolve OPENSEARCH_HOST (sds-gateway-local-opensearch) because it's attached to sds-network-local-opensearch while the gateway's OpenSearch service is on sds-gateway-*-opensearch-net. This network isolation prevents communication, causing indexing, Redis fan-out, and OpenSearch health checks to fail.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 155b128. Configure here.

refresh="wait_for",
)

self._mark_applied(site_name, asset.uuid, event_at)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale dedupe lost on restart

Medium Severity

The stale-event check uses an in-memory map (_last_event) that is lost on process restart. This allows older webhooks to overwrite newer OpenSearch documents, even though federation_event_at is persisted in the document.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 155b128. Configure here.

indexer,
event_at=event_at,
)
return total

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused bootstrap helper functions

Low Severity

bootstrap_local_site and bootstrap_all_peers are never called; run_bootstrap only registers site-hellos and discards the indexer. These helpers add dead paths that diverge from the live startup behavior.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 155b128. Configure here.

http = build_gateway_http_client()
os_host = os.environ.get("OPENSEARCH_HOST", "opensearch")
os_port = os.environ.get("OPENSEARCH_PORT", "9200")
os_client = OpenSearch(hosts=[{"host": os_host, "port": int(os_port)}])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenSearch client lacks auth

High Severity

The federation OpenSearch client is created with only host and port. Gateway OpenSearch expects basic auth (and TLS in production), so pings, document reads, and webhook indexing will fail even if the container can reach the host.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 155b128. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request federation Federation related work (sync service, document storage, peer configuration, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants