Skip to content

feat: Init RDS package#9438

Open
rasswanth-s wants to merge 25 commits into
devfrom
rasswanth/rds-port
Open

feat: Init RDS package#9438
rasswanth-s wants to merge 25 commits into
devfrom
rasswanth/rds-port

Conversation

@rasswanth-s

Copy link
Copy Markdown
Collaborator

Summary

Changes

Testing

Pending

Asana task

(https://app.asana.com/1/1185126988600652/project/1210542925864934/task/1216142561490291)


PR naming convention

Your PR title must follow this format or merging will be blocked:

type: short description in lowercase

Example titles:

  • feat: add retry logic to job approval
  • fix: handle timeout in notification sender
  • docs: update syft-bg README
  • chore: bump dependencies
  • refactor: split init flow into helpers
  • test: add criteria validation tests
  • ci: add release train workflow

Allowed types:

Type When to use Example
feat New feature or capability feat: add DS rejection emails
fix Bug fix fix: handle empty peer list
docs Documentation only docs: update syft-bg README
chore Maintenance, deps, config chore: bump dependencies
refactor Code restructuring (no behavior change) refactor: split init flow
test Adding or updating tests test: add approval criteria tests
ci CI/CD workflow changes ci: add release train workflow
perf Performance improvement perf: cache Drive API responses
build Build system or dependency changes build: pin syft-bg>=0.2.0

Just edit the PR title to fix any errors — the check re-runs automatically.

Auto-labeling

Labels are applied automatically — you don't need to add them manually:

  • Type labels from your PR title (e.g., feat: adds feature, fix: adds bugfix)
  • Package labels from which files you changed (e.g., editing packages/syft-bg/ adds pkg:syft-bg)

These labels are used to auto-generate categorized release notes.

@rasswanth-s rasswanth-s changed the title feat: [WIP] Init RDS package feat: Init RDS package Jul 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new syft-rds “product layer” that composes the domain-free syft-client sync engine with the jobs/datasets managers, while refactoring the sync stack to support generic “collection” syncing (prefix + local subpath + policy) instead of being dataset-specific.

Changes:

  • Add syft-rds package (client/config/login + job auto-approval) and migrate consumers (syft-bg, syft-enclave, tests) to use it.
  • Refactor syft-client sync to support prefix-parameterized collection APIs (transport/router/cache/syncers) and a new CollectionSyncSpec.
  • Adjust dependencies/entrypoints to reflect the split between generic sync core and RDS product functionality.

Reviewed changes

Copilot reviewed 49 out of 49 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/unit/syft_bg/test_email_auto_approve_flow.py Switch syft-bg email flow test to use SyftRDSClient (currently calls a non-existent helper).
tests/unit/syft_bg/test_email_approval_flow.py Switch syft-bg approval flow test to use SyftRDSClient (currently calls a non-existent helper).
syft_client/sync/sync/datasite_watcher_syncer.py Rename/retarget watcher sync-down from dataset files to generic collection files.
syft_client/sync/sync/datasite_owner_syncer.py Generalize owner initial restore/download logic to iterate CollectionSyncSpec entries.
syft_client/sync/sync/collection_spec.py New domain-agnostic collection sync contract (prefix/subpath + immutable/owner-only flags).
syft_client/sync/sync/caches/datasite_watcher_cache.py Generalize watcher cache from dataset collections to multi-spec collection syncing + hash cache.
syft_client/sync/sync/caches/datasite_owner_cache.py Relax collections folder requirement and allow returning None for collections-relative path.
syft_client/sync/syftbox_manager.py Strip job/dataset responsibilities from sync engine; add callback emission for peer lifecycle.
syft_client/sync/connections/drive/gdrive_transport.py Generalize Google Drive collection folder naming/parsing + CRUD/download APIs by prefix.
syft_client/sync/connections/connection_router.py Replace dataset-specific router methods with prefix-parameterized collection methods.
syft_client/sync/connections/collection_prefixes.py Add canonical wire-level collection folder prefixes.
syft_client/sync/connections/base_connection.py Update base connection interface to prefix-parameterized collection methods.
syft_client/sync/callback_mixin.py Add _emit and on() alias to support safe lifecycle callbacks.
scripts/auto_approve_jobs.py Update script entrypoint to use syft_rds.login_do and RDS job auto-approval.
pyproject.toml Remove several deps from the root project (jobs/datasets now owned by syft-rds).
packages/syft-rds/tests/test_load_dataset_code.py Update RDS product tests to use SyftRDSClient for pairing.
packages/syft-rds/tests/test_job_auto_approval.py Move auto-approval tests to RDS product client + module.
packages/syft-rds/tests/test_datasets_jobs_repr.py Update repr tests to use RDS pairing and shared test utils.
packages/syft-rds/tests/test_dataset_upload_private.py Update private dataset upload tests to use RDS client and generic collection APIs.
packages/syft-rds/tests/test_create_dataset_cleanup.py Update create_dataset cleanup tests to use RDS client and updated logging.
packages/syft-rds/tests/test_client.py Add coverage ensuring RDS injects collection specs into the composed sync engine.
packages/syft-rds/tests/dataset_test_utils.py Add uniquely-named shared helpers to avoid pytest module-name collisions.
packages/syft-rds/tests/conftest.py Add per-suite pytest config mirroring top-level behavior (disable PRE_SYNC).
packages/syft-rds/src/syft_rds/login.py Add RDS login entrypoints (login_do, login_ds) wrapping the sync-engine login flow.
packages/syft-rds/src/syft_rds/job_auto_approval.py Re-home job auto-approval to RDS client surface/types.
packages/syft-rds/src/syft_rds/config.py Add composed RDS config (sync + job + dataset) and dataset collection specs injection.
packages/syft-rds/src/syft_rds/client.py Introduce SyftRDSClient composing sync engine with job/dataset managers + callbacks.
packages/syft-rds/src/syft_rds/init.py Define public RDS API surface exports.
packages/syft-rds/pyproject.toml Add syft-rds package metadata and workspace dependencies.
packages/syft-enclave/tests/test_runner.py Update enclave runner tests to call client.delete_syftbox() directly.
packages/syft-enclave/tests/test_immutability.py Update enclave immutability tests to use nested RDS client surfaces.
packages/syft-enclave/tests/test_encryption.py Update encryption tests to access peer store via rds.sync_engine.
packages/syft-enclave/tests/test_enclave_datasets.py Update enclave dataset tests to use RDS client fields.
packages/syft-enclave/tests/test_enclave_client.py Update enclave client tests for new RDS-composed structure.
packages/syft-enclave/src/syft_enclaves/utils.py Switch enclave test config creation to SyftRDSClientConfig.
packages/syft-enclave/src/syft_enclaves/runner.py Route lifecycle actions through enclave client public methods / RDS peer manager.
packages/syft-enclave/src/syft_enclaves/login.py Use SyftRDSClientConfig and run sync-engine login init via nested sync engine.
packages/syft-enclave/src/syft_enclaves/client.py Refactor enclave client to wrap SyftRDSClient and delegate appropriately.
packages/syft-enclave/pyproject.toml Add dependency on syft-rds.
packages/syft-bg/src/syft_bg/sync/orchestrator.py Build orchestrator clients via SyftRDSClientConfig/SyftRDSClient.
packages/syft-bg/src/syft_bg/notify/handlers/job.py Import is_normal_syncable_path from syft-rds re-export.
packages/syft-bg/src/syft_bg/common/syft_bg_config.py Use syft-rds re-export for default syftbox root resolution.
packages/syft-bg/src/syft_bg/cli/init/flow.py Use syft-rds re-export for default syftbox root resolution.
packages/syft-bg/src/syft_bg/approve/orchestrator.py Build approval orchestrator client via SyftRDSClientConfig/SyftRDSClient.
packages/syft-bg/src/syft_bg/approve/monitors/peer.py Update type hints from SyftboxManager to SyftRDSClient.
packages/syft-bg/src/syft_bg/approve/monitors/job.py Update type hints from SyftboxManager to SyftRDSClient.
packages/syft-bg/src/syft_bg/approve/handlers/peer.py Update type hints from SyftboxManager to SyftRDSClient.
packages/syft-bg/src/syft_bg/approve/handlers/job.py Update type hints from SyftboxManager to SyftRDSClient.
packages/syft-bg/pyproject.toml Add dependency on syft-rds.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/unit/syft_bg/test_email_auto_approve_flow.py Outdated
Comment thread tests/unit/syft_bg/test_email_approval_flow.py Outdated
Comment thread packages/syft-rds/src/syft_rds/config.py Outdated
Comment thread syft_client/sync/connections/drive/gdrive_transport.py
Comment thread packages/syft-rds/src/syft_rds/client.py
# Generic sync-stack helpers re-exported so consumers that compose the RDS product
# (e.g. syft-bg) have a single integration surface and need not import from
# syft_client.sync internals directly.
from syft_client.sync.syftbox_manager import get_jupyter_default_syftbox_folder

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This pattern of importing syft-client imports at rds is temporary, will refactor this part shortly

@pjwerneck pjwerneck left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The intent to create a domain-free core and define a public API is clear, but there are still violations of that boundary. It's mostly clear for the happy path, but there's still coupling and private-reaching when handling others cases.

There are still broken tests and conflicts, so this review is not final.

PRIVATE_DATASET_COLLECTION_PREFIX,
)

# The RDS layer OWNS the dataset collection values: both the on-wire prefixes and

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The subpaths are defined below, but the prefixes are being imported from syft_datasets.dataset_manager. This comment seems contradictory.


``sync`` is built by the caller (it differs per environment); ``job``
and ``dataset`` are derived here from the SAME email/folder/role so all
paths line up with the sync engine.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The alignment is documented, but there's no validator enforcing it. Like a @model_validator(mode="after") asserting that sync.email and dataset.email match.

current_user_email=email,
has_do_role=has_do_role,
)
dataset = SyftBoxConfig(syftbox_folder=folder, email=email)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why are we taking folder and email as parameters here, instead of using attributes from sync? It looks like a potential source of drift or inconsistencies.

so explicit user sharing is added. Reads the cache populated during
``pull_initial_state()`` in the nested DatasiteOwnerSyncer.
"""
for tag, content_hash in self._sync.datasite_owner_syncer._any_shared_collections:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is one example of the encapsulation violations.

self._sync._connection_router.owner_share_collection(
DATASET_COLLECTION_PREFIX, tag, content_hash, [peer_email]
)
except Exception:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This doesn't look like an acceptable "ignore all errors" case as the comment suggests, since owner_share_collection is not a trivial method. If a network request fails, it's going to behave as if it's already shared. This could also potentially silently real bugs in owner_share_collection.

Also, using a bare except: or a blanket except Exception: to silently suppress all errors is a python anti-pattern. In legitimate cases, contextlib.suppress is the more idiomatic way of doing that.

private_folder_id = self._upload_private_dataset_to_collection(dataset)

if sync:
self.sync()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The word "sync" is carrying four different meanings in the file. That's a real readability issue. In most cases it refers to the SyftboxManager instance, but it's also used as a boolean flag meaning to run a full sync after the method, the sync() method itself, that delegates to self._sync.sync() , and config.sync referring to the sub-config.

I'd rename the boolean to something more explicit, like sync_after, and the private attribute to _sync_engine so it matches the public property sync_engine.

syftbox_folder = get_colab_default_syftbox_folder(email)
use_in_memory_cache = False
collections_folder = syftbox_folder / email / COLLECTION_SUBPATH
collections_folder = (

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This interpolation is repeated four times in the file. It should be extracted to a common function/method.

Also, it's not clear why it assumes collection_specs[0] is special. Is it because it's the public spec? What happens if they are reordered? It should have a clarifying comment here.

If it must always be the public, it should refer to it by an unambiguous value.

"""
cast(PeerManager, self.peer_manager).load_peers(force_download=force_download)
self._sync_peer_install_sources_to_job_client()
self._emit_peers_loaded()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The sync method calls load_peers on every invocation, so every sync emits peers_loaded. Is that intentional? Is there any idempotency handling?

self.peer_manager.add_peer(peer_email, force=force, verbose=verbose)
self._sync_peer_install_sources_to_job_client()
self._emit_peers_loaded()
if self.has_do_role:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does this mean a peer added by the DO is approved automatically? If that's the case, there should be a comment clarifying it.

self._emit_peers_loaded()
if self.has_do_role:
self._post_approve_peer_do(peer_email)
if sync:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is the same issue with the word "sync" mentioned in client.py. For clarity, the boolean could be renamed to sync_after.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants