Skip to content

Scaffolding: Federation gateway setup#299

Open
klpoland wants to merge 8 commits into
masterfrom
feature-kpoland-federation-gateway-scaffolding
Open

Scaffolding: Federation gateway setup#299
klpoland wants to merge 8 commits into
masterfrom
feature-kpoland-federation-gateway-scaffolding

Conversation

@klpoland

@klpoland klpoland commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Note

Medium Risk
Introduces a new internal export surface and global API-key permission behavior (sync keys scoped to federation only); misconfigured CIDRs or leaked sync keys could expose public metadata beyond the intended Docker network.

Overview
Adds gateway-side federation scaffolding so a local federation-sync service can pull public dataset/capture metadata and subscribe to change notifications.

Internal export API (/api/.../federation/export/...) lists and returns federation-shaped JSON for public finalized datasets and public captures, gated by FEDERATION_ENABLED, startup/periodic operational checks (sync health, Redis, FederationSync API key), CIDR allowlists, and a dedicated FederationSync API key (create_federation_sync_api_key). Export payloads use new DatasetFederationSerializer / CaptureFederationSerializer, with contract tests aligned to sds_federation Pydantic models.

Change propagation: post_save signals on datasets/captures publish created/updated/deleted events to a configurable Redis channel when federation is operational. Example env docs and settings cover site name, channel naming, health URL, and export CIDRs.

Auth hardening: APIKeyAuthentication now sets request.auth to the UserAPIKey object; DisallowFederationSyncKey is added to global DRF default permissions so sync keys cannot call normal asset APIs (inverse: export routes require IsFederationSyncKey). KeySources.FederationSync plus a users migration extend API key sources.

Smaller follow-ons: app ready() initializes federation operational state; fallow-cross-file-dupes.sh prefers local fallow; create_test_files import paths updated.

Reviewed by Cursor Bugbot for commit 751d03a. 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
  gateway/sds_gateway/api_methods/views/dataset_endpoints.py  42% smaller
  gateway/sds_gateway/api_methods/tests/test_authenticate.py  26% smaller
  gateway/config/settings/base.py  6% smaller
  gateway/.envs/example/django.env Unsupported file format
  gateway/config/api_router.py  0% smaller
  gateway/pyproject.toml Unsupported file format
  gateway/scripts/fallow-cross-file-dupes.sh Unsupported file format
  gateway/sds_gateway/api_methods/apps.py  0% smaller
  gateway/sds_gateway/api_methods/authentication.py  0% smaller
  gateway/sds_gateway/api_methods/federation/__init__.py  0% smaller
  gateway/sds_gateway/api_methods/federation/availability.py  0% smaller
  gateway/sds_gateway/api_methods/federation/events.py  0% smaller
  gateway/sds_gateway/api_methods/federation/export_contract.py  0% smaller
  gateway/sds_gateway/api_methods/federation/permissions.py  0% smaller
  gateway/sds_gateway/api_methods/federation/redis_channel.py  0% smaller
  gateway/sds_gateway/api_methods/federation/signals.py  0% smaller
  gateway/sds_gateway/api_methods/helpers/compile_federated_data.py  0% smaller
  gateway/sds_gateway/api_methods/models.py  0% smaller
  gateway/sds_gateway/api_methods/permissions.py  0% smaller
  gateway/sds_gateway/api_methods/serializers/capture_serializers.py  0% smaller
  gateway/sds_gateway/api_methods/serializers/dataset_serializers.py  0% smaller
  gateway/sds_gateway/api_methods/tests/test_federation_events.py  0% smaller
  gateway/sds_gateway/api_methods/tests/test_federation_export.py  0% smaller
  gateway/sds_gateway/api_methods/tests/test_federation_export_contract.py  0% smaller
  gateway/sds_gateway/api_methods/tests/test_federation_hardening.py  0% smaller
  gateway/sds_gateway/api_methods/views/capture_endpoints.py  0% smaller
  gateway/sds_gateway/api_methods/views/federation_endpoints.py  0% smaller
  gateway/sds_gateway/api_methods/views/file_endpoints.py  0% smaller
  gateway/sds_gateway/users/management/commands/create_federation_sync_api_key.py  0% smaller
  gateway/sds_gateway/users/management/commands/create_test_files.py  0% smaller
  gateway/sds_gateway/users/migrations/0012_alter_userapikey_source_federation_sync.py  0% smaller
  gateway/sds_gateway/users/migrations/max_migration.txt Unsupported file format
  gateway/sds_gateway/users/models.py  0% smaller

@klpoland klpoland changed the title gateway: federation export API, Redis events, sync API key Scaffolding: Federation gateway setup Jun 19, 2026
@klpoland klpoland added the gateway Gateway component label Jun 19, 2026
@klpoland

Copy link
Copy Markdown
Collaborator Author

Added some dedicated API endpoints, serializers, and signals for federation and configuration that defaults to disabling federation sync.

@klpoland klpoland force-pushed the feature-kpoland-federation-gateway-scaffolding branch 2 times, most recently from 9f94dfd to 83d51d0 Compare June 25, 2026 19:02
@klpoland klpoland marked this pull request as ready for review June 25, 2026 19:03
@klpoland klpoland requested a review from lucaspar June 25, 2026 19:03
Comment thread gateway/sds_gateway/api_methods/views/dataset_endpoints.py Outdated
Comment thread gateway/sds_gateway/api_methods/federation/availability.py
@klpoland klpoland force-pushed the feature-kpoland-federation-gateway-scaffolding branch from 83d51d0 to 4bbcbed Compare June 25, 2026 19:53
Comment on lines +11 to +25
# FEDERATION
# ------------------------------------------------------------------------------
# the below environment variables are used for site federation with peers.
# By default, federation events are disabled.
# If FEDERATION_ENABLED is TRUE, deployment will need to include
# federation sync service configuration.
# FEDERATION_ENABLED=true
# FEDERATION_EVENTS_ENABLED=true
# FEDERATION_EVENTS_CHANNEL=federation:events
# FEDERATION_SYNC_USER_EMAIL=
# FEDERATION_SITE_NAME=
# FEDERATION_SYNC_HEALTH_URL=http://federation-sync:8000/sync/health
# FEDERATION_EXPORT_INTERNAL_HEADER_SECRET=
# FEDERATION_EXPORT_TRUST_X_FORWARDED_FOR=false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

add a one line comment explaining each value; highlight that federation-sync is a docker service in the same network, defined in /federation

Comment thread gateway/config/settings/base.py
Comment thread gateway/sds_gateway/api_methods/federation/events.py Outdated
Comment thread gateway/sds_gateway/api_methods/federation/export_contract.py Outdated
Comment thread gateway/sds_gateway/api_methods/federation/availability.py Outdated
@lucaspar lucaspar added the migrations Code changes that require data or schema migrations in the database. label Jun 25, 2026

# Create files
minio_client = MinioClient()
minio_client = get_minio_client()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Test command calls missing upload method

Low Severity

This commit switches the dev command to get_minio_client(), which returns ObjectStoreFacade with MinIO-compatible methods like fput_object, but the upload loop still calls upload_file. That method is not defined on the facade or delegated MinIO client, so the command fails at runtime when uploading fixtures.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2c09fae. Configure here.

user=user,
source=KeySources.FederationSync,
description="Federation sync service (export endpoints only)",
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Command always rotates sync keys

Medium Severity

Each run of create_federation_sync_api_key deletes every existing FederationSync API key for the sync user before creating a new one. Re-running the command invalidates keys already configured in federation-sync without an explicit rotate flag.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3393c0f. Configure here.

Comment on lines +11 to +23
# FEDERATION
# ------------------------------------------------------------------------------
# Peer sync uses the federation-sync Docker service (same sds-network as gateway;
# service definition lives under /federation). Bootstrap: enable federation, run
# create_federation_sync_api_key, pass the key to federation-sync. Set FEDERATION_SITE_NAME
# (e.g. crc) when enabling federation; use SDS_SITE_FQDN for the public host (RFC [site].fqdn).
# FEDERATION_ENABLED=true # Master switch for export APIs and Redis federation events.
# FEDERATION_SITE_NAME=crc # RFC [site].name (short peer id); set SDS_SITE_FQDN separately for [site].fqdn.
# FEDERATION_EVENTS_CHANNEL=federation:events # Redis pub/sub channel federation-sync subscribes to.
# FEDERATION_SYNC_HEALTH_URL=http://federation-sync:8000/sync/health # Health probe target (federation-sync service).
# FEDERATION_SYNC_USER_EMAIL=federation-sync@internal.local # Service user email for create_federation_sync_api_key.
# FEDERATION_EXPORT_ALLOWED_CIDRS= # Comma-separated CIDRs allowed to call export (default: private Docker ranges).

@lucaspar lucaspar Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

comments need to be in a new line for .env files; otherwise the # ... will unintentionally be part of the values set:

cat .env | grep SSH_KEY
SSH_KEY= # this is not interpreted as a comment

docker exec -it ubuntu bash -c 'env | grep SSH_KEY'
SSH_KEY=# this is not interpreted as a comment

@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 6 total unresolved issues (including 2 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 751d03a. Configure here.

initialize_federation_operational_state,
)

initialize_federation_operational_state()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ready hook queries pre-migrate

High Severity

Calling initialize_federation_operational_state() from AppConfig.ready() runs federation checks during app load, including a UserAPIKey database query when FEDERATION_ENABLED is true. Django invokes ready() before migrations apply, so a fresh migrate with federation enabled can raise a missing-table error and abort bootstrap.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 751d03a. Configure here.

)

def _parse_cidrs(raw: list[str]) -> list[ipaddress.IPv4Network | ipaddress.IPv6Network]:
return [ipaddress.ip_network(item.strip(), strict=False) for item in raw]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Empty CIDR crashes startup

High Severity

_parse_cidrs parses every list entry with ipaddress.ip_network and does not drop blank tokens. An empty or malformed FEDERATION_EXPORT_ALLOWED_CIDRS value (for example a trailing comma or an empty assignment) can include "", which makes settings import raise ValueError and prevents Django from starting.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 751d03a. Configure here.

"192.168.0.0/16",
],
),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Empty CIDR list blocks export

Medium Severity

Setting FEDERATION_EXPORT_ALLOWED_CIDRS to an empty value bypasses the documented private-network defaults and yields an empty allowlist, so is_client_ip_allowed_for_federation_export always denies export clients even when federation is otherwise operational.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 751d03a. Configure here.

item_type=ItemType.DATASET,
uuid=instance.uuid,
timestamp=instance.updated_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.

Wrong federation event types

Medium Severity

_event_type maps any non-exportable save to deleted, including brand-new private or draft records that were never federated, so Redis can emit spurious delete notifications. When a record first becomes exportable on a later save, created is false, so the event is updated instead of created, which can confuse sync indexing.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 751d03a. 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.) gateway Gateway component migrations Code changes that require data or schema migrations in the database.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants