From 29a87a91ceaf6acc566ea7427cb6a58449451bce Mon Sep 17 00:00:00 2001
From: Jamie Pine
Date: Sat, 6 Sep 2025 21:00:37 -0400
Subject: [PATCH] huge refactor
---
.prettierrc.js | 31 -
.../ACT-000-action-system.md | 0
.../ACT-001-action-manager-registry.md | 0
{core-new/.tasks => .tasks}/AI-000-ai-epic.md | 0
.../.tasks => .tasks}/AI-001-ai-agent.md | 0
.../AI-002-create-finetuning-dataset.md | 0
.../CLI-000-command-line-interface.md | 0
.../CLOUD-000-cloud-as-a-peer.md | 0
.../CLOUD-001-design-cloud-core-infra.md | 9 +-
.../CLOUD-002-relay-server.md | 0
.../CLOUD-003-cloud-volume.md | 0
.../.tasks => .tasks}/CORE-000-vdfs-core.md | 0
.../CORE-001-entry-centric-model.md | 0
.../CORE-002-sdpath-addressing.md | 0
.../CORE-003-content-identity.md | 0
.../CORE-004-closure-table.md | 0
.../CORE-005-file-type-system.md | 0
.../CORE-006-semantic-tagging-architecture.md | 0
...m-state-for-on-demand-state-computation.md | 0
.../CORE-008-virtual-sidecar-system.md | 4 +-
.../CORE-009-user-managed-collections.md | 0
.../CORE-010-file-ingestion-workflow.md | 0
.../DEV-000-development-validation.md | 0
.../DEV-001-multi-process-test-framework.md | 0
.../FILE-000-file-operations.md | 0
.../FILE-001-file-copy-job.md | 0
.../FILE-002-file-deletion-job.md | 0
.../INDEX-000-indexing-file-management.md | 0
.../INDEX-001-location-watcher-service.md | 0
...NDEX-002-stale-file-detection-algorithm.md | 0
.../.tasks => .tasks}/JOB-000-job-system.md | 0
.../.tasks => .tasks}/JOB-001-job-manager.md | 0
.../.tasks => .tasks}/JOB-002-job-logging.md | 0
.../LOC-000-location-operations.md | 0
.../LOC-001-location-management-actions.md | 0
...l-locations-via-pure-hierarchical-model.md | 0
.../LSYNC-000-library-sync.md | 0
.../LSYNC-001-design-library-sync-protocol.md | 0
.../LSYNC-002-metadata-sync.md | 0
.../LSYNC-003-file-op-sync.md | 0
...C-004-sync-relationship-database-schema.md | 0
.../.tasks => .tasks}/NET-000-networking.md | 0
.../NET-001-iroh-p2p-stack.md | 0
.../NET-002-device-pairing.md | 0
.../NET-003-spacedrop-protocol.md | 0
.../PLUG-000-wasm-plugin-system.md | 0
.../PLUG-001-integrate-wasm-runtime.md | 0
.../PLUG-002-define-vdfs-plugin-api.md | 0
.../PLUG-003-develop-twitter-agent-poc.md | 0
.../RES-000-resource-management.md | 0
.../RES-001-adaptive-throttling.md | 0
.../SEARCH-000-temporal-semantic-search.md | 0
.../SEARCH-001-async-searchjob.md | 0
...CH-002-two-stage-fts-semantic-reranking.md | 0
.../SEARCH-003-unified-vector-repositories.md | 0
.../SEC-000-security-and-privacy.md | 0
.../SEC-002-database-encryption.md | 0
.../SEC-003-cryptographic-audit-log.md | 0
.../.tasks => .tasks}/SEC-004-rbac-system.md | 0
.../SEC-005-secure-credential-vault.md | 0
.../SEC-006-certificate-pinning.md | 0
...-encryption-policies-for-public-sharing.md | 0
.../VOL-000-volume-operations.md | 0
...physicalclass-and-location-logicalclass.md | 0
...VOL-002-automatic-volume-classification.md | 0
...elligent-storage-tiering-warning-system.md | 0
...004-remote-volume-indexing-with-opendal.md | 0
...e-as-a-virtual-volume-for-direct-import.md | 0
...ference-sidecars-for-live-photo-support.md | 0
{core-new/.tasks => .tasks}/task.schema.json | 0
README.md | 54 +-
spacedrive-cloud => apps/cloud | 0
combine_paths.py | 72 -
core-new/Cargo.toml | 161 --
core-new/benchmarks/Cargo.toml | 39 -
core-new/build.rs | 13 -
core-new/examples/indexing_showcase.rs | 136 --
core-new/src/context.rs | 78 -
core-new/src/lib.rs | 499 -----
core-new/src/library/config.rs | 208 ---
core-new/src/library/mod.rs | 238 ---
core-new/src/location/mod.rs | 563 ------
core-new/src/volume/error.rs | 83 -
core-new/src/volume/mod.rs | 92 -
core-new/src/volume/speed.rs | 369 ----
core-new/src/volume/types.rs | 685 -------
{core-new => core}/.scripts/combine.sh | 0
{core-new => core/.scripts}/test_daemon.sh | 0
.../.scripts}/update_spacedrive.sh | 2 +-
{core-new => core}/AGENTS.md | 0
{core-new => core}/Cargo.lock | Bin 251354 -> 251346 bytes
core/Cargo.toml | 303 ++--
{core-new => core}/README.md | 2 +-
core/benchmarks/Cargo.toml | 37 +
.../benchmarks/recipes/shape_large.yaml | 0
.../benchmarks/recipes/shape_medium.yaml | 0
.../benchmarks/recipes/shape_small.yaml | 0
.../results/shape_large-aggregation-hdd.json | 0
.../results/shape_large-aggregation-ssd.json | 2 +-
...hape_large-content_identification-hdd.json | 0
...hape_large-content_identification-ssd.json | 2 +-
.../shape_large-indexing_discovery-hdd.json | 0
.../shape_large-indexing_discovery-ssd.json | 2 +-
.../results/shape_medium-aggregation-hdd.json | 0
.../results/shape_medium-aggregation-ssd.json | 2 +-
...ape_medium-content_identification-hdd.json | 0
...ape_medium-content_identification-ssd.json | 2 +-
.../shape_medium-indexing_discovery-hdd.json | 0
.../shape_medium-indexing_discovery-ssd.json | 2 +-
.../results/shape_small-aggregation-hdd.json | 0
.../results/shape_small-aggregation-ssd.json | 2 +-
...hape_small-content_identification-hdd.json | 0
...hape_small-content_identification-ssd.json | 2 +-
.../shape_small-indexing_discovery-hdd.json | 0
.../shape_small-indexing_discovery-ssd.json | 2 +-
.../benchmarks/results/whitepaper_metrics.csv | 0
.../benchmarks/src/bin/sd-bench-new.rs | 0
{core-new => core}/benchmarks/src/cli/args.rs | 0
.../benchmarks/src/cli/commands.rs | 0
{core-new => core}/benchmarks/src/cli/mod.rs | 0
.../benchmarks/src/config/mod.rs | 0
.../benchmarks/src/core_boot/mod.rs | 0
.../benchmarks/src/generator/filesystem.rs | 0
.../benchmarks/src/generator/mod.rs | 0
.../benchmarks/src/generator/noop.rs | 0
.../benchmarks/src/generator/registry.rs | 0
{core-new => core}/benchmarks/src/lib.rs | 0
.../benchmarks/src/metrics/mod.rs | 0
.../benchmarks/src/metrics/sources.rs | 0
.../benchmarks/src/recipe/mod.rs | 0
.../benchmarks/src/recipe/schema.rs | 0
.../benchmarks/src/reporting/csv.rs | 0
.../benchmarks/src/reporting/json_summary.rs | 0
.../benchmarks/src/reporting/mod.rs | 0
.../benchmarks/src/reporting/registry.rs | 0
.../benchmarks/src/runner/mod.rs | 0
.../benchmarks/src/runner/monitor.rs | 0
.../benchmarks/src/scenarios/common.rs | 0
.../src/scenarios/content_identification.rs | 0
.../benchmarks/src/scenarios/core_indexing.rs | 0
.../benchmarks/src/scenarios/mod.rs | 0
.../benchmarks/src/scenarios/registry.rs | 0
{core-new => core}/benchmarks/src/util/fs.rs | 0
{core-new => core}/benchmarks/src/util/mod.rs | 0
{core-new => core}/benchmarks/src/util/rng.rs | 0
.../benchmarks/src/util/time.rs | 0
core/build.rs | 20 +-
core/crates/cloud-services/Cargo.toml | 57 -
core/crates/cloud-services/src/client.rs | 358 ----
core/crates/cloud-services/src/error.rs | 216 ---
.../src/key_manager/key_store.rs | 331 ----
.../cloud-services/src/key_manager/mod.rs | 183 --
core/crates/cloud-services/src/lib.rs | 55 -
core/crates/cloud-services/src/p2p/mod.rs | 272 ---
.../src/p2p/new_sync_messages_notifier.rs | 154 --
core/crates/cloud-services/src/p2p/runner.rs | 1109 ------------
core/crates/cloud-services/src/sync/ingest.rs | 121 --
core/crates/cloud-services/src/sync/mod.rs | 136 --
.../crates/cloud-services/src/sync/receive.rs | 356 ----
core/crates/cloud-services/src/sync/send.rs | 337 ----
.../cloud-services/src/token_refresher.rs | 468 -----
core/crates/file-path-helper/Cargo.toml | 33 -
core/crates/file-path-helper/README.md | 3 -
.../src/isolated_file_path_data.rs | 799 --------
core/crates/file-path-helper/src/lib.rs | 438 -----
core/crates/heavy-lifting/Cargo.toml | 63 -
.../src/file_identifier/cas_id.rs | 80 -
.../heavy-lifting/src/file_identifier/job.rs | 1072 -----------
.../heavy-lifting/src/file_identifier/mod.rs | 290 ---
.../src/file_identifier/shallow.rs | 238 ---
.../src/file_identifier/tasks/identifier.rs | 578 ------
.../src/file_identifier/tasks/mod.rs | 187 --
.../file_identifier/tasks/object_processor.rs | 434 -----
core/crates/heavy-lifting/src/indexer/job.rs | 1163 ------------
core/crates/heavy-lifting/src/indexer/mod.rs | 566 ------
.../heavy-lifting/src/indexer/shallow.rs | 303 ----
.../heavy-lifting/src/indexer/tasks/mod.rs | 7 -
.../heavy-lifting/src/indexer/tasks/saver.rs | 309 ----
.../src/indexer/tasks/updater.rs | 307 ----
.../src/indexer/tasks/walker/entry.rs | 93 -
.../src/indexer/tasks/walker/metadata.rs | 64 -
.../src/indexer/tasks/walker/mod.rs | 1176 ------------
.../src/indexer/tasks/walker/rules.rs | 261 ---
.../src/indexer/tasks/walker/save_state.rs | 219 ---
.../heavy-lifting/src/job_system/error.rs | 76 -
.../heavy-lifting/src/job_system/job.rs | 1189 ------------
.../heavy-lifting/src/job_system/mod.rs | 378 ----
.../heavy-lifting/src/job_system/report.rs | 436 -----
.../heavy-lifting/src/job_system/runner.rs | 713 --------
.../heavy-lifting/src/job_system/store.rs | 234 ---
.../heavy-lifting/src/job_system/utils.rs | 35 -
core/crates/heavy-lifting/src/lib.rs | 114 --
.../helpers/exif_media_data.rs | 172 --
.../helpers/ffmpeg_media_data.rs | 789 --------
.../src/media_processor/helpers/mod.rs | 12 -
.../media_processor/helpers/thumbnailer.rs | 547 ------
.../heavy-lifting/src/media_processor/job.rs | 1096 -----------
.../heavy-lifting/src/media_processor/mod.rs | 193 --
.../src/media_processor/shallow.rs | 271 ---
.../tasks/media_data_extractor.rs | 604 -------
.../src/media_processor/tasks/mod.rs | 5 -
.../src/media_processor/tasks/thumbnailer.rs | 488 -----
core/crates/heavy-lifting/src/utils/mod.rs | 1 -
.../heavy-lifting/src/utils/sub_path.rs | 113 --
core/crates/indexer-rules/Cargo.toml | 33 -
core/crates/indexer-rules/src/lib.rs | 947 ----------
core/crates/indexer-rules/src/seed.rs | 346 ----
core/crates/indexer-rules/src/serde_impl.rs | 217 ---
core/crates/p2p/Cargo.toml | 70 -
core/crates/p2p/src/error.rs | 24 -
core/crates/p2p/src/lib.rs | 162 --
.../authorize_new_device_in_sync_group.rs | 22 -
.../p2p/src/schema/cloud_services/mod.rs | 25 -
.../notify_new_sync_messages.rs | 12 -
core/crates/p2p/src/schema/mod.rs | 36 -
core/crates/p2p/src/server/mod.rs | 191 --
.../p2p/src/server/router/cloud_services.rs | 47 -
core/crates/p2p/src/server/router/mod.rs | 19 -
core/crates/prisma-helpers/Cargo.toml | 20 -
core/crates/prisma-helpers/src/lib.rs | 576 ------
.../crates}/spacedrive-jobs-derive/Cargo.toml | 0
.../crates}/spacedrive-jobs-derive/src/lib.rs | 0
core/crates/sync/Cargo.toml | 39 -
core/crates/sync/README.md | 132 --
core/crates/sync/src/backfill.rs | 669 -------
core/crates/sync/src/db_operation.rs | 103 --
core/crates/sync/src/ingest_utils.rs | 517 ------
core/crates/sync/src/lib.rs | 153 --
core/crates/sync/src/manager.rs | 750 --------
{core-new => core}/crush.json | 0
{core-new => core}/examples/file_type_demo.rs | 0
{core-new => core}/examples/indexing_demo.rs | 0
core/examples/indexing_showcase.rs | 136 ++
.../examples/job_logging_test.rs | 0
{core-new => core}/examples/library_demo.rs | 0
.../examples/location_watcher_demo.rs | 0
.../examples/pause_resume_demo.rs | 0
{core-new => core}/examples/shutdown_demo.rs | 0
.../examples/simple_pause_resume.rs | 0
{core-new => core}/examples/test_migration.rs | 0
{core-new => core}/examples/volume_demo.rs | 0
.../20230616064440_init/migration.sql | 271 ---
.../migration.sql | 63 -
.../20230619032753_p2p/migration.sql | 17 -
.../migration.sql | 33 -
.../migration.sql | 2 -
.../20230711114013_preferences/migration.sql | 5 -
.../migration.sql | 95 -
.../migration.sql | 7 -
.../migration.sql | 15 -
.../20230812141757_added_albums/migration.sql | 23 -
.../20230828195811_media_data/migration.sql | 152 --
.../migration.sql | 2 -
.../migration.sql | 2 -
.../migration.sql | 40 -
.../migration.sql | 28 -
.../migration.sql | 15 -
.../migration.sql | 2 -
.../migration.sql | 24 -
.../migration.sql | 39 -
.../migration.sql | 40 -
.../migration.sql | 33 -
.../migration.sql | 35 -
.../migration.sql | 57 -
.../migration.sql | 2 -
.../migration.sql | 37 -
.../migration.sql | 29 -
.../migration.sql | 52 -
.../migration.sql | 128 --
.../migration.sql | 29 -
.../migration.sql | 12 -
.../migration.sql | 2 -
.../migration.sql | 6 -
.../migration.sql | 2 -
.../migration.sql | 15 -
.../migration.sql | 226 ---
core/prisma/migrations/migration_lock.toml | 3 -
core/prisma/schema.prisma | 741 --------
{core-new => core}/rust-toolchain.toml | 0
core/src/api/backups.rs | 499 -----
core/src/api/cloud/devices.rs | 397 ----
core/src/api/cloud/libraries.rs | 140 --
core/src/api/cloud/locations.rs | 112 --
core/src/api/cloud/mod.rs | 391 ----
core/src/api/cloud/sync_groups.rs | 404 -----
core/src/api/cloud/thumbnails.rs | 61 -
core/src/api/devices.rs | 64 -
core/src/api/ephemeral_files.rs | 661 -------
core/src/api/files.rs | 1053 -----------
core/src/api/jobs.rs | 444 -----
core/src/api/keys.rs | 447 -----
core/src/api/labels.rs | 175 --
core/src/api/libraries.rs | 667 -------
core/src/api/locations.rs | 561 ------
core/src/api/mod.rs | 258 ---
core/src/api/models.rs | 23 -
core/src/api/nodes.rs | 135 --
core/src/api/notifications.rs | 167 --
core/src/api/p2p.rs | 154 --
core/src/api/preferences.rs | 22 -
core/src/api/search/exif_data.rs | 29 -
core/src/api/search/file_path.rs | 296 ---
core/src/api/search/mod.rs | 424 -----
core/src/api/search/object.rs | 181 --
core/src/api/search/saved.rs | 232 ---
core/src/api/search/utils.rs | 125 --
core/src/api/sync.rs | 78 -
core/src/api/tags.rs | 396 ----
core/src/api/utils/invalidate.rs | 431 -----
core/src/api/utils/library.rs | 53 -
core/src/api/utils/mod.rs | 109 --
core/src/api/volumes.rs | 82 -
core/src/api/web_api.rs | 29 -
{core-new => core}/src/bin/cli.rs | 0
{core-new => core}/src/config/app_config.rs | 0
{core-new => core}/src/config/migration.rs | 0
{core-new => core}/src/config/mod.rs | 0
core/src/context.rs | 275 +--
core/src/custom_uri/mod.rs | 592 ------
core/src/custom_uri/mpsc_to_async_write.rs | 44 -
core/src/custom_uri/serve_file.rs | 150 --
core/src/custom_uri/utils.rs | 84 -
{core-new => core}/src/device/config.rs | 0
{core-new => core}/src/device/manager.rs | 0
{core-new => core}/src/device/mod.rs | 0
{core-new => core}/src/domain/addressing.rs | 0
.../src/domain/content_identity.rs | 0
{core-new => core}/src/domain/device.rs | 0
{core-new => core}/src/domain/entry.rs | 0
{core-new => core}/src/domain/location.rs | 0
{core-new => core}/src/domain/mod.rs | 0
.../src/domain/user_metadata.rs | 0
{core-new => core}/src/domain/volume.rs | 0
{core-new => core}/src/file_type/builtin.rs | 0
.../src/file_type/definitions/archives.toml | 0
.../src/file_type/definitions/audio.toml | 0
.../src/file_type/definitions/code.toml | 0
.../src/file_type/definitions/documents.toml | 0
.../src/file_type/definitions/images.toml | 0
.../src/file_type/definitions/misc.toml | 0
.../src/file_type/definitions/video.toml | 0
{core-new => core}/src/file_type/magic.rs | 0
{core-new => core}/src/file_type/mod.rs | 0
{core-new => core}/src/file_type/registry.rs | 0
.../actions/BUILDER_REFACTOR_PLAN.md | 0
.../src/infrastructure/actions/builder.rs | 0
.../src/infrastructure/actions/error.rs | 0
.../src/infrastructure/actions/handler.rs | 0
.../src/infrastructure/actions/manager.rs | 0
.../src/infrastructure/actions/mod.rs | 0
.../src/infrastructure/actions/output.rs | 0
.../src/infrastructure/actions/receipt.rs | 0
.../src/infrastructure/actions/registry.rs | 0
.../src/infrastructure/actions/tests.rs | 0
.../src/infrastructure/cli/README.md | 0
.../src/infrastructure/cli/adapters/copy.rs | 0
.../src/infrastructure/cli/adapters/mod.rs | 0
.../src/infrastructure/cli/commands/daemon.rs | 0
.../src/infrastructure/cli/commands/file.rs | 0
.../src/infrastructure/cli/commands/job.rs | 0
.../infrastructure/cli/commands/library.rs | 0
.../infrastructure/cli/commands/location.rs | 0
.../src/infrastructure/cli/commands/mod.rs | 0
.../infrastructure/cli/commands/network.rs | 0
.../src/infrastructure/cli/commands/system.rs | 0
.../src/infrastructure/cli/commands/volume.rs | 0
.../src/infrastructure/cli/daemon/client.rs | 0
.../src/infrastructure/cli/daemon/config.rs | 0
.../cli/daemon/handlers/core.rs | 0
.../cli/daemon/handlers/file.rs | 0
.../infrastructure/cli/daemon/handlers/job.rs | 0
.../cli/daemon/handlers/library.rs | 0
.../cli/daemon/handlers/location.rs | 0
.../infrastructure/cli/daemon/handlers/mod.rs | 0
.../cli/daemon/handlers/network.rs | 0
.../cli/daemon/handlers/system.rs | 0
.../cli/daemon/handlers/volume.rs | 0
.../src/infrastructure/cli/daemon/mod.rs | 0
.../cli/daemon/services/helpers.rs | 0
.../infrastructure/cli/daemon/services/mod.rs | 0
.../cli/daemon/services/state.rs | 0
.../cli/daemon/types/commands.rs | 0
.../infrastructure/cli/daemon/types/common.rs | 0
.../infrastructure/cli/daemon/types/mod.rs | 0
.../cli/daemon/types/responses.rs | 0
.../src/infrastructure/cli/mod.rs | 0
.../src/infrastructure/cli/output/context.rs | 0
.../infrastructure/cli/output/formatters.rs | 0
.../src/infrastructure/cli/output/messages.rs | 0
.../src/infrastructure/cli/output/mod.rs | 0
.../src/infrastructure/cli/output/section.rs | 0
.../src/infrastructure/cli/output/tests.rs | 0
.../src/infrastructure/cli/pairing_ui.rs | 0
.../src/infrastructure/cli/state.rs | 0
.../src/infrastructure/cli/tui.rs | 0
.../src/infrastructure/cli/utils.rs | 0
.../database/entities/audit_log.rs | 0
.../database/entities/collection.rs | 0
.../database/entities/collection_entry.rs | 0
.../database/entities/content_identity.rs | 0
.../database/entities/content_kind.rs | 0
.../database/entities/device.rs | 0
.../database/entities/directory_paths.rs | 0
.../infrastructure/database/entities/entry.rs | 0
.../database/entities/entry_closure.rs | 0
.../database/entities/indexer_rule.rs | 0
.../infrastructure/database/entities/label.rs | 0
.../database/entities/location.rs | 0
.../database/entities/metadata_label.rs | 0
.../database/entities/metadata_tag.rs | 0
.../database/entities/mime_type.rs | 0
.../infrastructure/database/entities/mod.rs | 0
.../database/entities/sidecar.rs | 0
.../database/entities/sidecar_availability.rs | 0
.../infrastructure/database/entities/tag.rs | 0
.../database/entities/user_metadata.rs | 0
.../database/entities/volume.rs | 0
.../m20240101_000001_initial_schema.rs | 0
.../m20240102_000001_populate_lookups.rs | 0
.../m20240107_000001_create_collections.rs | 0
.../m20250109_000001_create_sidecars.rs | 0
...m20250110_000001_refactor_volumes_table.rs | 0
.../m20250112_000001_create_indexer_rules.rs | 0
.../infrastructure/database/migration/mod.rs | 0
.../src/infrastructure/database/mod.rs | 0
.../src/infrastructure/events/mod.rs | 0
.../src/infrastructure/jobs/context.rs | 0
.../src/infrastructure/jobs/database.rs | 0
.../src/infrastructure/jobs/error.rs | 0
.../src/infrastructure/jobs/executor.rs | 0
.../infrastructure/jobs/generic_progress.rs | 0
.../src/infrastructure/jobs/handle.rs | 0
.../src/infrastructure/jobs/logger.rs | 0
.../src/infrastructure/jobs/manager.rs | 0
.../src/infrastructure/jobs/mod.rs | 0
.../src/infrastructure/jobs/output.rs | 0
.../src/infrastructure/jobs/progress.rs | 0
.../src/infrastructure/jobs/registry.rs | 0
.../src/infrastructure/jobs/traits.rs | 0
.../src/infrastructure/jobs/types.rs | 0
{core-new => core}/src/infrastructure/mod.rs | 0
.../src/keys/device_key_manager.rs | 0
.../src/keys/library_key_manager.rs | 0
{core-new => core}/src/keys/mod.rs | 0
core/src/lib.rs | 848 +++++----
core/src/library/config.rs | 647 ++-----
{core-new => core}/src/library/error.rs | 0
core/src/library/library.rs | 218 ---
{core-new => core}/src/library/lock.rs | 0
{core-new => core}/src/library/manager.rs | 0
core/src/library/manager/error.rs | 65 -
core/src/library/manager/mod.rs | 697 -------
core/src/library/manager/pragmas.rs | 109 --
core/src/library/mod.rs | 244 ++-
core/src/library/name.rs | 61 -
core/src/library/statistics.rs | 122 --
core/src/location/archive/archive_job.rs | 0
core/src/location/archive/mod.rs | 0
core/src/location/error.rs | 115 --
{core-new => core}/src/location/manager.rs | 0
core/src/location/manager/mod.rs | 451 -----
core/src/location/manager/runner.rs | 459 -----
core/src/location/manager/watcher/android.rs | 303 ----
core/src/location/manager/watcher/ios.rs | 452 -----
core/src/location/manager/watcher/linux.rs | 309 ----
core/src/location/manager/watcher/macos.rs | 478 -----
core/src/location/manager/watcher/mod.rs | 885 ---------
core/src/location/manager/watcher/utils.rs | 1241 -------------
core/src/location/manager/watcher/windows.rs | 419 -----
core/src/location/metadata.rs | 295 ---
core/src/location/mod.rs | 1602 ++++++-----------
core/src/location/non_indexed.rs | 414 -----
core/src/node/config.rs | 572 ------
core/src/node/hardware.rs | 210 ---
core/src/node/mod.rs | 6 -
core/src/node/platform.rs | 62 -
core/src/notifications.rs | 34 -
core/src/object/fs/archive.rs | 0
core/src/object/fs/convert.rs | 0
core/src/object/fs/error.rs | 51 -
core/src/object/fs/mod.rs | 222 ---
core/src/object/fs/old_copy.rs | 669 -------
core/src/object/fs/old_cut.rs | 142 --
core/src/object/fs/old_delete.rs | 122 --
core/src/object/fs/old_erase.rs | 205 ---
core/src/object/fs/sync.rs | 0
.../object/media/old_thumbnail/clean_up.rs | 183 --
.../object/media/old_thumbnail/directory.rs | 385 ----
core/src/object/mod.rs | 3 -
core/src/object/old_orphan_remover.rs | 106 --
core/src/object/tag/mod.rs | 47 -
core/src/object/tag/seed.rs | 34 -
core/src/object/validation/hash.rs | 25 -
core/src/object/validation/mod.rs | 23 -
.../object/validation/old_validator_job.rs | 203 ---
core/src/old_job/error.rs | 119 --
core/src/old_job/manager.rs | 416 -----
core/src/old_job/mod.rs | 1148 ------------
core/src/old_job/report.rs | 504 ------
core/src/old_job/worker.rs | 660 -------
core/src/old_p2p/events.rs | 159 --
core/src/old_p2p/libraries.rs | 139 --
core/src/old_p2p/manager.rs | 466 -----
core/src/old_p2p/metadata.rs | 132 --
core/src/old_p2p/mod.rs | 17 -
core/src/old_p2p/operations/library.rs | 134 --
core/src/old_p2p/operations/mod.rs | 8 -
core/src/old_p2p/operations/ping.rs | 37 -
core/src/old_p2p/operations/rspc.rs | 63 -
core/src/old_p2p/operations/spacedrop.rs | 382 ----
core/src/old_p2p/protocol.rs | 131 --
core/src/old_p2p/sync/mod.rs | 262 ---
core/src/old_p2p/sync/proto.rs | 43 -
.../src/operations/addressing.rs | 0
.../src/operations/content/action.rs | 0
.../src/operations/content/mod.rs | 0
.../src/operations/devices/mod.rs | 0
.../src/operations/devices/revoke/action.rs | 0
.../src/operations/devices/revoke/mod.rs | 0
.../src/operations/devices/revoke/output.rs | 0
.../src/operations/entries/mod.rs | 0
.../src/operations/entries/state.rs | 0
.../src/operations/files/copy/action.rs | 0
.../src/operations/files/copy/database.rs | 0
.../operations/files/copy/docs/ANALYSIS.md | 0
.../files/copy/docs/FILE_SYNC_OVERLAP.md | 0
.../copy/docs/PROGRESSIVE_COPY_DESIGN.md | 0
.../copy/docs/RESUME_VALIDATION_DESIGN.md | 0
.../src/operations/files/copy/input.rs | 0
.../src/operations/files/copy/job.rs | 0
.../src/operations/files/copy/mod.rs | 0
.../src/operations/files/copy/output.rs | 0
.../src/operations/files/copy/routing.rs | 0
.../src/operations/files/copy/strategy.rs | 0
.../src/operations/files/delete/action.rs | 0
.../src/operations/files/delete/job.rs | 0
.../src/operations/files/delete/mod.rs | 0
.../src/operations/files/delete/output.rs | 0
.../files/duplicate_detection/action.rs | 0
.../files/duplicate_detection/job.rs | 0
.../files/duplicate_detection/mod.rs | 0
.../src/operations/files/mod.rs | 0
.../src/operations/files/validation/action.rs | 0
.../src/operations/files/validation/job.rs | 0
.../src/operations/files/validation/mod.rs | 0
.../src/operations/indexing/action.rs | 0
.../indexing/change_detection/mod.rs | 0
.../src/operations/indexing/entry.rs | 0
.../src/operations/indexing/hierarchy.rs | 0
.../src/operations/indexing/job.rs | 0
.../src/operations/indexing/metrics.rs | 0
.../src/operations/indexing/mod.rs | 0
.../src/operations/indexing/path_resolver.rs | 0
.../src/operations/indexing/persistence.rs | 0
.../operations/indexing/phases/aggregation.rs | 0
.../src/operations/indexing/phases/content.rs | 0
.../operations/indexing/phases/discovery.rs | 0
.../src/operations/indexing/phases/mod.rs | 0
.../operations/indexing/phases/processing.rs | 0
.../src/operations/indexing/progress.rs | 0
.../src/operations/indexing/rules.rs | 0
.../src/operations/indexing/state.rs | 0
.../src/operations/indexing/tests/mod.rs | 0
.../src/operations/libraries/create/action.rs | 0
.../src/operations/libraries/create/mod.rs | 0
.../src/operations/libraries/create/output.rs | 0
.../src/operations/libraries/delete/action.rs | 0
.../src/operations/libraries/delete/mod.rs | 0
.../src/operations/libraries/delete/output.rs | 0
.../src/operations/libraries/export/action.rs | 0
.../src/operations/libraries/export/mod.rs | 0
.../src/operations/libraries/export/output.rs | 0
.../src/operations/libraries/mod.rs | 0
.../src/operations/libraries/rename/action.rs | 0
.../src/operations/libraries/rename/mod.rs | 0
.../src/operations/libraries/rename/output.rs | 0
.../src/operations/locations/add/action.rs | 0
.../src/operations/locations/add/mod.rs | 0
.../src/operations/locations/add/output.rs | 0
.../src/operations/locations/index/action.rs | 0
.../src/operations/locations/index/mod.rs | 0
.../src/operations/locations/mod.rs | 0
.../src/operations/locations/remove/action.rs | 0
.../src/operations/locations/remove/mod.rs | 0
.../src/operations/locations/remove/output.rs | 0
.../src/operations/locations/rescan/action.rs | 0
.../src/operations/locations/rescan/mod.rs | 0
.../src/operations/locations/rescan/output.rs | 0
.../src/operations/media/live_photo.rs | 0
.../src/operations/media/live_photo_query.rs | 0
.../src/operations/media/mod.rs | 0
.../src/operations/media/thumbnail/action.rs | 0
.../src/operations/media/thumbnail/error.rs | 0
.../operations/media/thumbnail/generator.rs | 0
.../src/operations/media/thumbnail/job.rs | 0
.../src/operations/media/thumbnail/mod.rs | 0
.../src/operations/media/thumbnail/state.rs | 0
.../src/operations/media/thumbnail/utils.rs | 0
.../src/operations/metadata/action.rs | 0
.../src/operations/metadata/mod.rs | 0
{core-new => core}/src/operations/mod.rs | 0
.../src/operations/sidecar/mod.rs | 0
.../src/operations/sidecar/path.rs | 0
.../src/operations/sidecar/types.rs | 0
.../src/operations/volumes/mod.rs | 0
.../operations/volumes/speed_test/action.rs | 0
.../operations/volumes/speed_test/handler.rs | 0
.../src/operations/volumes/speed_test/mod.rs | 0
.../src/operations/volumes/track/action.rs | 0
.../src/operations/volumes/track/handler.rs | 0
.../src/operations/volumes/track/mod.rs | 0
.../src/operations/volumes/untrack/action.rs | 0
.../src/operations/volumes/untrack/handler.rs | 0
.../src/operations/volumes/untrack/mod.rs | 0
core/src/preferences/kv.rs | 161 --
core/src/preferences/library.rs | 129 --
core/src/preferences/mod.rs | 56 -
core/src/search/mod.rs | 1 -
{core-new => core}/src/services/device.rs | 0
.../src/services/entry_state_service.rs | 0
.../src/services/file_sharing.rs | 0
.../location_watcher/event_handler.rs | 0
.../src/services/location_watcher/mod.rs | 0
.../location_watcher/platform/linux.rs | 0
.../location_watcher/platform/macos.rs | 0
.../services/location_watcher/platform/mod.rs | 0
.../location_watcher/platform/windows.rs | 0
.../src/services/location_watcher/utils.rs | 0
{core-new => core}/src/services/mod.rs | 0
.../services/networking/core/event_loop.rs | 0
.../src/services/networking/core/mod.rs | 0
.../services/networking/device/connection.rs | 0
.../src/services/networking/device/mod.rs | 0
.../services/networking/device/persistence.rs | 0
.../services/networking/device/registry.rs | 0
.../src/services/networking/mod.rs | 0
.../networking/protocols/file_transfer.rs | 0
.../networking/protocols/messaging.rs | 0
.../src/services/networking/protocols/mod.rs | 0
.../networking/protocols/pairing/initiator.rs | 0
.../networking/protocols/pairing/joiner.rs | 0
.../networking/protocols/pairing/messages.rs | 0
.../networking/protocols/pairing/mod.rs | 0
.../protocols/pairing/persistence.rs | 0
.../networking/protocols/pairing/security.rs | 0
.../networking/protocols/pairing/types.rs | 0
.../services/networking/protocols/registry.rs | 0
.../src/services/networking/utils/identity.rs | 0
.../src/services/networking/utils/logging.rs | 0
.../src/services/networking/utils/mod.rs | 0
.../src/services/sidecar_manager.rs | 0
.../src/services/volume_monitor.rs | 0
{core-new => core}/src/shared/errors.rs | 0
{core-new => core}/src/shared/mod.rs | 0
{core-new => core}/src/shared/types.rs | 0
{core-new => core}/src/shared/utils.rs | 0
{core-new => core}/src/test_framework/mod.rs | 0
.../src/test_framework/runner.rs | 0
core/src/util/abort_on_drop.rs | 39 -
core/src/util/batched_stream.rs | 67 -
core/src/util/debug_initializer.rs | 198 --
core/src/util/infallible_request.rs | 28 -
core/src/util/maybe_undefined.rs | 88 -
core/src/util/mod.rs | 17 -
core/src/util/mpscrr.rs | 326 ----
core/src/util/observable.rs | 101 --
core/src/util/unsafe_streamed_query.rs | 31 -
core/src/util/version_manager.rs | 224 ---
core/src/volume/actor.rs | 521 ------
.../src/volume/classification.rs | 0
core/src/volume/error.rs | 342 +---
{core-new => core}/src/volume/manager.rs | 0
core/src/volume/mod.rs | 113 +-
core/src/volume/os.rs | 502 ------
{core-new => core}/src/volume/os_detection.rs | 0
core/src/volume/speed.rs | 443 +++--
core/src/volume/state.rs | 237 ---
core/src/volume/types.rs | 755 +++++---
core/src/volume/volumes.rs | 130 --
core/src/volume/watcher.rs | 262 ---
{core-new => core}/tests/copy_action_test.rs | 0
.../tests/copy_progress_test.rs | 0
.../tests/cross_device_copy_test.rs | 0
.../tests/database_migration_test.rs | 0
.../tests/device_pairing_test.rs | 0
.../tests/device_persistence_test.rs | 0
{core-new => core}/tests/event_system_test.rs | 0
.../tests/file_transfer_test.rs | 0
{core-new => core}/tests/helpers/mod.rs | 0
.../tests/helpers/test_volumes.rs | 0
.../tests/indexing_rules_test.rs | 0
{core-new => core}/tests/indexing_test.rs | 0
.../tests/job_pause_resume_test.rs | 0
.../tests/job_registration_test.rs | 0
{core-new => core}/tests/job_shutdown_test.rs | 0
{core-new => core}/tests/library_test.rs | 0
.../tests/volume_tracking_test.rs | 0
.../volume_tracking_with_test_volumes.rs | 0
.../whitepaper/architecture.png | Bin
{core-new => core}/whitepaper/comment.cut | 0
{core-new => core}/whitepaper/grok-changes.md | 0
.../whitepaper/grok-critique.md | 0
.../whitepaper/grok-critique2.md | 0
.../whitepaper/proposed-changes-v2.md | 0
.../whitepaper/proposed-changes.md | 0
{core-new => core}/whitepaper/references.bib | 0
{core-new => core}/whitepaper/spacedrive.bbl | 0
{core-new => core}/whitepaper/spacedrive.blg | 0
{core-new => core}/whitepaper/spacedrive.out | 0
{core-new => core}/whitepaper/spacedrive.pdf | Bin
{core-new => core}/whitepaper/spacedrive.tex | 2 +-
crates/ai/Cargo.toml | 79 -
crates/ai/README.md | 3 -
crates/ai/src/lib.rs | 86 -
crates/ai/src/old_image_labeler/mod.rs | 56 -
crates/ai/src/old_image_labeler/model/mod.rs | 259 ---
.../ai/src/old_image_labeler/model/yolov8.rs | 168 --
crates/ai/src/old_image_labeler/old_actor.rs | 612 -------
crates/ai/src/old_image_labeler/process.rs | 507 ------
crates/ai/src/utils/mod.rs | 27 -
crates/file-ext/Cargo.toml | 20 -
crates/file-ext/src/extensions.rs | 566 ------
crates/file-ext/src/kind.rs | 62 -
crates/file-ext/src/lib.rs | 4 -
crates/file-ext/src/magic.rs | 232 ---
crates/file-ext/src/text.rs | 297 ---
crates/old-p2p/Cargo.toml | 51 -
crates/old-p2p/README.md | 9 -
crates/old-p2p/crates/block/Cargo.toml | 18 -
crates/old-p2p/crates/block/src/block.rs | 97 -
crates/old-p2p/crates/block/src/block_size.rs | 120 --
crates/old-p2p/crates/block/src/lib.rs | 469 -----
crates/old-p2p/crates/block/src/sb_request.rs | 268 ---
crates/old-p2p/crates/proto/Cargo.toml | 14 -
crates/old-p2p/crates/proto/src/lib.rs | 88 -
crates/old-p2p/crates/tunnel/Cargo.toml | 17 -
crates/old-p2p/crates/tunnel/src/lib.rs | 143 --
crates/old-p2p/src/hook.rs | 141 --
crates/old-p2p/src/hooks.rs | 9 -
crates/old-p2p/src/hooks/mdns.rs | 234 ---
crates/old-p2p/src/hooks/quic.rs | 10 -
crates/old-p2p/src/hooks/quic/handle.rs | 122 --
crates/old-p2p/src/hooks/quic/transport.rs | 928 ----------
crates/old-p2p/src/hooks/quic/utils.rs | 53 -
crates/old-p2p/src/identity.rs | 172 --
crates/old-p2p/src/lib.rs | 34 -
crates/old-p2p/src/p2p.rs | 407 -----
crates/old-p2p/src/peer.rs | 331 ----
crates/old-p2p/src/smart_guards.rs | 55 -
crates/old-p2p/src/stream.rs | 81 -
crates/prisma-cli/Cargo.toml | 18 -
crates/prisma-cli/src/bin/prisma.rs | 3 -
crates/prisma-cli/src/bin/sync.rs | 3 -
crates/prisma/.gitignore | 1 -
crates/prisma/Cargo.toml | 16 -
crates/prisma/src/lib.rs | 15 -
crates/sync-generator/Cargo.toml | 16 -
crates/sync-generator/src/attribute/mod.rs | 55 -
crates/sync-generator/src/attribute/parser.rs | 155 --
crates/sync-generator/src/lib.rs | 195 --
crates/sync-generator/src/model.rs | 214 ---
crates/sync-generator/src/sync_data.rs | 351 ----
crates/sync/Cargo.toml | 16 -
crates/sync/src/compressed.rs | 359 ----
crates/sync/src/crdt.rs | 76 -
crates/sync/src/factory.rs | 163 --
crates/sync/src/lib.rs | 44 -
crates/sync/src/model_traits.rs | 27 -
.../task-validator/Cargo.toml | 0
.../task-validator/src/main.rs | 0
crates/utils/Cargo.toml | 23 -
crates/utils/src/db.rs | 163 --
crates/utils/src/error.rs | 59 -
crates/utils/src/lib.rs | 135 --
{core-new/docs => docs}/README.md | 0
{core-new/docs => docs}/benchmarks.md | 0
{core-new/docs => docs/core}/architecture.md | 0
.../docs => docs/core}/cli-multi-instance.md | 0
{core-new/docs => docs/core}/cli.md | 0
{core-new/docs => docs/core}/database.md | 0
{core-new/docs => docs/core}/domain-models.md | 0
{core-new/docs => docs/core}/examples.md | 0
{core-new/docs => docs/core}/indexing.md | 0
{core-new/docs => docs/core}/job-system.md | 0
{core-new/docs => docs/core}/library.md | 0
{core-new/docs => docs/core}/locations.md | 0
{core-new/docs => docs/core}/networking.md | 0
{core-new/docs => docs/core}/pairing.md | 0
{core-new/docs => docs/core}/task-tracking.md | 0
{core-new/docs => docs/core}/testing.md | 0
.../docs => docs/core}/virtual_sidecars.md | 0
{core-new/docs => docs/core}/volume-system.md | 0
.../docs => docs}/design/ACTIONS_REFACTOR.md | 0
.../design/ACTION_SYSTEM_DESIGN.md | 0
.../design/AGENT_MANAGER_DESIGN.md | 0
.../docs => docs}/design/API_COMPARISON.md | 0
.../design/ARCHITECTURE_DECISIONS.md | 0
.../design/AT_REST_LIBRARY_ENCRYPTION.md | 0
.../design/BENCHMARKING_SUITE_DESIGN.md | 0
.../design/CLOSURE_TABLE_INDEXING_PROPOSAL.md | 0
...OSS_DEVICE_FILE_TRANSFER_IMPLEMENTATION.md | 0
...ROSS_PLATFORM_COPY_AND_VOLUME_AWARENESS.md | 0
.../docs => docs}/design/DAEMON_REFACTOR.md | 0
.../design/DESIGN_CORE_LIFECYCLE.md | 0
.../design/DESIGN_DEVICE_MANAGEMENT.md | 0
.../design/DESIGN_FILE_DATA_MODEL.md | 0
.../design/DESIGN_FILE_DATA_MODEL_VISUAL.md | 0
.../design/DESIGN_FILE_TYPE_SYSTEM.md | 0
.../design/DESIGN_LIBRARY_IMPLEMENTATION.md | 0
.../design/DESIGN_LIBRARY_ORGANIZATION.md | 0
.../DESIGN_LIBRARY_ORGANIZATION_VISUAL.md | 0
.../design/DESIGN_OPTIMIZED_STORAGE.md | 0
.../design/DESIGN_VDFS_INTEGRATION.md | 0
.../design/DEVICE_PAIRING_PROTOCOL.md | 0
.../design/DOMAIN_MODELS_README.md | 0
.../design/ENTITY_REFACTOR_DESIGN.md | 0
.../design/FFMPEG_BUNDLING_DESIGN.md | 57 +-
.../design/FILE_SHARING_DESIGN.md | 0
.../FUNCTION_BASED_TEST_FRAMEWORK_DESIGN.md | 0
.../design/IDEA_FOR_BETTER_INDEXING.md | 0
.../design/IMPLEMENTATION_STATUS.md | 0
.../docs => docs}/design/INDEXER_ANALYSIS.md | 0
.../design/INDEXER_ANALYSIS_2.md | 0
.../design/INDEXER_JOB_EXAMPLE.md | 0
.../docs => docs}/design/INDEXER_PROGRESS.md | 6 +-
.../design/INDEXER_RULES_SYSTEM.md | 71 +-
.../INDEXING_DISCOVERY_OPTIMIZATION_PLAN.md | 0
.../design/INTEGRATION_SYSTEM_DESIGN.md | 0
.../INTEGRATION_SYSTEM_DESIGN_GEMINI.md | 0
.../design/IPHONE_AS_VOLUME_DESIGN.md | 0
.../design/IROH_MIGRATION_DESIGN.md | 0
.../docs => docs}/design/JOB_SYSTEM_DESIGN.md | 0
.../design/JOB_SYSTEM_MACRO_EXAMPLE.md | 0
.../docs => docs}/design/JOB_SYSTEM_README.md | 0
.../design/LIBP2P_INTEGRATION_DESIGN.md | 0
.../design/LIBRARY_LEADERSHIP.md | 0
.../design/NETWORKING_SYSTEM_DESIGN.md | 228 +--
.../design/OLD_SPACEDRIVE_ANALYSIS.md | 0
.../design/OPERATIONS_REFACTOR_PLAN.md | 0
.../PERSISTENT_DEVICE_CONNECTIONS_DESIGN.md | 0
.../design/REFERENCE_SIDECARS.md | 0
.../docs => docs}/design/REWRITE_PLAN.MD | 2 +-
.../docs => docs}/design/SDPATH_REFACTOR.md | 0
.../design/SDPATH_REFACTOR_COVERAGE.md | 0
.../docs => docs}/design/SEARCH_DESIGN.md | 95 +-
.../design/SIMULATION_ENGINE_DESIGN.md | 0
.../design/SPACEDRIVE_COMPLETE_OVERVIEW.md | 143 +-
.../docs => docs}/design/SPACEDROP_DESIGN.md | 0
.../design/SPACEDROP_IMPLEMENTATION_PLAN.md | 24 +-
{core-new/docs => docs}/design/STRUCTURE.md | 6 +-
{core-new/docs => docs}/design/SYNC_DESIGN.md | 0
.../design/SYNC_DESIGN_2025_08_19.md | 0
.../design/SYNC_FIRST_DRAFT_DESIGN.md | 0
.../design/SYNC_INTEGRATION_NOTES.md | 0
.../design/THUMBNAIL_SYSTEM_DESIGN.md | 14 +-
{core-new/docs => docs}/design/UI_DESIGN.md | 0
.../docs => docs}/design/VDFS_MODEL_VISUAL.md | 0
.../design/VIRTUAL_LOCATIONS_DESIGN.md | 0
.../design/VIRTUAL_SIDECAR_SYSTEM.md | 0
.../design/VOLUME_CLASSIFICATION_DESIGN.md | 0
.../VOLUME_TRACKING_IMPLEMENTATION_PLAN.md | 0
.../design/WATCHER_VDFS_INTEGRATION.md | 190 +-
.../design/WHITEPAPER_IMPL_ROADMAP.md | 0
.../design/cli-output-refactor.md | 0
.../design/frontend_graphql_usage.tsx | 0
.../design/indexer-scope-upgrade.md | 0
.../docs => docs}/design/landing-page-idea.md | 0
.../networking_implementation_summary.md | 0
{core-new/docs => docs}/history.md | 0
{core-new/docs => docs}/philosophy.md | 0
{core-new/docs => docs}/roadmap.md | 0
{core-new/docs => docs}/whitepaper.md | 0
error.txt | 1024 -----------
interface/.eslintrc.js | 13 -
interface/.gitignore | 1 -
interface/ErrorFallback.tsx | 200 --
interface/RoutingContext.tsx | 30 -
interface/TabsContext.tsx | 18 -
interface/app/$libraryId/404.tsx | 29 -
interface/app/$libraryId/Explorer/Context.tsx | 29 -
.../ContextMenu/AssignTagMenuItems.tsx | 258 ---
.../Explorer/ContextMenu/ConditionalItem.tsx | 45 -
.../ContextMenu/FilePath/CutCopyItems.tsx | 56 -
.../Explorer/ContextMenu/FilePath/Items.tsx | 299 ---
.../Explorer/ContextMenu/Object/Items.tsx | 112 --
.../Explorer/ContextMenu/OpenWith.tsx | 143 --
.../Explorer/ContextMenu/SharedItems.tsx | 236 ---
.../Explorer/ContextMenu/context.tsx | 43 -
.../$libraryId/Explorer/ContextMenu/index.tsx | 95 -
.../app/$libraryId/Explorer/CopyAsPath.tsx | 36 -
.../$libraryId/Explorer/DismissibleNotice.tsx | 79 -
.../app/$libraryId/Explorer/DragOverlay.tsx | 96 -
.../$libraryId/Explorer/ExplorerDraggable.tsx | 28 -
.../$libraryId/Explorer/ExplorerDroppable.tsx | 36 -
.../$libraryId/Explorer/ExplorerPathBar.tsx | 257 ---
.../$libraryId/Explorer/ExplorerTagBar.tsx | 369 ----
.../Explorer/FilePath/DeleteDialog.tsx | 130 --
.../Explorer/FilePath/ErrorBarrier.tsx | 40 -
.../$libraryId/Explorer/FilePath/Image.tsx | 14 -
.../Explorer/FilePath/LayeredFileIcon.tsx | 62 -
.../$libraryId/Explorer/FilePath/Original.tsx | 263 ---
.../Explorer/FilePath/RenameTextBox.tsx | 328 ----
.../Explorer/FilePath/Thumb.module.scss | 25 -
.../$libraryId/Explorer/FilePath/Thumb.tsx | 439 -----
.../$libraryId/Explorer/FilePath/useFrame.tsx | 15 -
.../app/$libraryId/Explorer/FilePath/utils.ts | 107 --
.../Explorer/Inspector/FavoriteButton.tsx | 36 -
.../Explorer/Inspector/MediaData.tsx | 249 ---
.../$libraryId/Explorer/Inspector/Note.tsx | 62 -
.../$libraryId/Explorer/Inspector/index.tsx | 672 -------
.../$libraryId/Explorer/Inspector/store.tsx | 4 -
.../OptionsPanel/ListView/IconSize.tsx | 37 -
.../OptionsPanel/ListView/TextSize.tsx | 37 -
.../Explorer/OptionsPanel/ListView/index.tsx | 11 -
.../Explorer/OptionsPanel/ListView/util.ts | 18 -
.../Explorer/OptionsPanel/index.tsx | 236 ---
.../$libraryId/Explorer/ParentContextMenu.tsx | 297 ---
.../Explorer/QuickPreview/Context.tsx | 26 -
.../Explorer/QuickPreview/ImageSlider.tsx | 111 --
.../Explorer/QuickPreview/index.tsx | 778 --------
.../$libraryId/Explorer/QuickPreview/store.ts | 9 -
.../Explorer/RevealInNativeExplorer.tsx | 35 -
.../app/$libraryId/Explorer/TopBarOptions.tsx | 184 --
.../app/$libraryId/Explorer/View/Context.tsx | 26 -
.../Explorer/View/DragScrollable.tsx | 33 -
.../$libraryId/Explorer/View/EmptyNotice.tsx | 41 -
.../View/Grid/DragSelect/DragSelectable.tsx | 17 -
.../Explorer/View/Grid/DragSelect/context.tsx | 20 -
.../Explorer/View/Grid/DragSelect/index.tsx | 600 ------
.../Grid/DragSelect/useDragSelectable.tsx | 58 -
.../Grid/DragSelect/useSelectedTargets.tsx | 35 -
.../Explorer/View/Grid/DragSelect/util.ts | 16 -
.../$libraryId/Explorer/View/Grid/Item.tsx | 72 -
.../Explorer/View/Grid/useKeySelection.tsx | 151 --
.../Explorer/View/GridView/Item/Context.tsx | 13 -
.../Explorer/View/GridView/Item/index.tsx | 218 ---
.../Explorer/View/GridView/index.tsx | 83 -
.../Explorer/View/ListView/Item.tsx | 92 -
.../Explorer/View/ListView/TableRow.tsx | 57 -
.../Explorer/View/ListView/context.tsx | 16 -
.../Explorer/View/ListView/index.tsx | 1006 -----------
.../Explorer/View/ListView/useRanges.tsx | 158 --
.../Explorer/View/ListView/useTable.tsx | 236 ---
.../Explorer/View/MediaView/DateHeader.tsx | 60 -
.../Explorer/View/MediaView/Item.tsx | 68 -
.../Explorer/View/MediaView/index.tsx | 235 ---
.../Explorer/View/MediaView/util.ts | 41 -
.../Explorer/View/RenamableItemText.tsx | 180 --
.../app/$libraryId/Explorer/View/ViewItem.tsx | 244 ---
.../app/$libraryId/Explorer/View/index.tsx | 265 ---
.../Explorer/View/useActiveItem.tsx | 124 --
.../Explorer/View/useDragScrollable.tsx | 66 -
.../Explorer/hooks/useExplorerCopyPaste.tsx | 160 --
interface/app/$libraryId/Explorer/index.tsx | 158 --
interface/app/$libraryId/Explorer/store.ts | 196 --
.../app/$libraryId/Explorer/useExplorer.ts | 223 ---
.../$libraryId/Explorer/useExplorerDnd.tsx | 194 --
.../Explorer/useExplorerDraggable.tsx | 67 -
.../Explorer/useExplorerDroppable.tsx | 210 ---
.../Explorer/useExplorerItemData.tsx | 130 --
.../Explorer/useExplorerOperatingSystem.tsx | 29 -
.../Explorer/useExplorerPreferences.ts | 69 -
.../$libraryId/Explorer/useKeyRevealFinder.ts | 61 -
interface/app/$libraryId/Explorer/util.ts | 176 --
interface/app/$libraryId/KeyManager/Key.tsx | 251 ---
interface/app/$libraryId/KeyManager/List.tsx | 60 -
.../app/$libraryId/KeyManager/Mounter.tsx | 163 --
.../app/$libraryId/KeyManager/NotSetup.tsx | 136 --
.../app/$libraryId/KeyManager/NotUnlocked.tsx | 57 -
interface/app/$libraryId/KeyManager/index.tsx | 26 -
interface/app/$libraryId/Layout/CMDK/CMDK.css | 676 -------
.../app/$libraryId/Layout/CMDK/CMDK.scss | 68 -
.../app/$libraryId/Layout/CMDK/index.tsx | 279 ---
.../Layout/CMDK/pages/CMDKLocations.tsx | 46 -
.../$libraryId/Layout/CMDK/pages/CMDKTags.tsx | 32 -
interface/app/$libraryId/Layout/Context.tsx | 14 -
.../app/$libraryId/Layout/DndContext.tsx | 24 -
.../Layout/Sidebar/DebugPopover.tsx | 272 ---
.../Sidebar/JobManager/IsRunningJob.tsx | 8 -
.../Layout/Sidebar/JobManager/Job.module.scss | 46 -
.../Layout/Sidebar/JobManager/Job.tsx | 116 --
.../Sidebar/JobManager/JobContainer.tsx | 169 --
.../Layout/Sidebar/JobManager/JobGroup.tsx | 306 ----
.../Layout/Sidebar/JobManager/index.tsx | 184 --
.../Layout/Sidebar/SidebarLayout/Context.tsx | 18 -
.../Sidebar/SidebarLayout/FeedbackPopover.tsx | 130 --
.../Layout/Sidebar/SidebarLayout/Footer.tsx | 91 -
.../Layout/Sidebar/SidebarLayout/Icon.tsx | 5 -
.../SidebarLayout/JobManagerPopover.tsx | 75 -
.../SidebarLayout/LibrariesDropdown.tsx | 74 -
.../Layout/Sidebar/SidebarLayout/Link.tsx | 74 -
.../Layout/Sidebar/SidebarLayout/Section.tsx | 22 -
.../Layout/Sidebar/SidebarLayout/SeeMore.tsx | 29 -
.../Sidebar/SidebarLayout/WindowControls.tsx | 26 -
.../Layout/Sidebar/SidebarLayout/index.tsx | 276 ---
.../app/$libraryId/Layout/Sidebar/helpers.ts | 4 -
.../app/$libraryId/Layout/Sidebar/index.tsx | 40 -
.../Sidebar/sections/Categories/index.tsx | 69 -
.../Layout/Sidebar/sections/Debug/index.tsx | 23 -
.../sections/Devices/AddDeviceDialog.tsx | 46 -
.../Layout/Sidebar/sections/Devices/index.tsx | 49 -
.../Layout/Sidebar/sections/Library/index.tsx | 42 -
.../Layout/Sidebar/sections/Local/index.tsx | 218 ---
.../sections/Locations/ContextMenu.tsx | 80 -
.../Sidebar/sections/Locations/index.tsx | 163 --
.../Layout/Sidebar/sections/Peers/index.tsx | 40 -
.../Sidebar/sections/SavedSearches/index.tsx | 106 --
.../Sidebar/sections/Tags/ContextMenu.tsx | 56 -
.../Layout/Sidebar/sections/Tags/index.tsx | 70 -
.../Layout/Sidebar/sections/Tools/index.tsx | 79 -
.../app/$libraryId/Layout/Sidebar/store.ts | 11 -
interface/app/$libraryId/Layout/auth.tsx | 97 -
interface/app/$libraryId/Layout/index.tsx | 170 --
interface/app/$libraryId/Layout/store.ts | 10 -
.../app/$libraryId/PageLayout/Context.tsx | 14 -
interface/app/$libraryId/PageLayout/index.tsx | 31 -
interface/app/$libraryId/Spacedrop/index.tsx | 190 --
interface/app/$libraryId/Spacedrop/toast.tsx | 118 --
interface/app/$libraryId/TopBar/Context.tsx | 36 -
interface/app/$libraryId/TopBar/Layout.tsx | 24 -
.../$libraryId/TopBar/NavigationButtons.tsx | 92 -
interface/app/$libraryId/TopBar/Portal.tsx | 22 -
.../app/$libraryId/TopBar/TopBarButton.tsx | 53 -
.../app/$libraryId/TopBar/TopBarMobile.tsx | 86 -
.../app/$libraryId/TopBar/TopBarOptions.tsx | 232 ---
interface/app/$libraryId/TopBar/index.tsx | 207 ---
interface/app/$libraryId/debug/dnd.tsx | 34 -
interface/app/$libraryId/ephemeral.tsx | 266 ---
interface/app/$libraryId/favorites.tsx | 83 -
interface/app/$libraryId/index.tsx | 81 -
interface/app/$libraryId/labels.tsx | 75 -
interface/app/$libraryId/location/$id.tsx | 269 ---
.../$libraryId/location/LocationOptions.tsx | 146 --
interface/app/$libraryId/media.tsx | 7 -
interface/app/$libraryId/network.tsx | 334 ----
interface/app/$libraryId/node/$id.tsx | 61 -
.../app/$libraryId/overview/FileKindStats.tsx | 320 ----
.../overview/Layout/HorizontalScroll.tsx | 98 -
.../$libraryId/overview/Layout/Section.tsx | 41 -
.../app/$libraryId/overview/LibraryStats.tsx | 243 ---
.../app/$libraryId/overview/LocationCard.tsx | 49 -
interface/app/$libraryId/overview/NewCard.tsx | 71 -
.../app/$libraryId/overview/StatCard.tsx | 114 --
.../app/$libraryId/overview/StorageBar.tsx | 85 -
interface/app/$libraryId/overview/index.tsx | 138 --
interface/app/$libraryId/peer/$id.tsx | 55 -
.../app/$libraryId/peer/StarfieldEffect.tsx | 335 ----
interface/app/$libraryId/people.tsx | 7 -
interface/app/$libraryId/recents.tsx | 81 -
interface/app/$libraryId/saved-search/$id.tsx | 148 --
.../search/Filters/FilterRegistry.tsx | 31 -
.../Filters/components/AppliedFilters.tsx | 191 --
.../components/FilterOptionBoolean.tsx | 43 -
.../components/FilterOptionDateRange.tsx | 45 -
.../Filters/components/FilterOptionList.tsx | 51 -
.../Filters/factories/createBooleanFilter.ts | 43 -
.../factories/createDateRangeFilter.ts | 93 -
.../factories/createInOrNotInFilter.ts | 81 -
.../factories/createTextMatchFilter.ts | 59 -
.../Filters/hooks/useToggleOptionSelected.tsx | 36 -
.../app/$libraryId/search/Filters/index.tsx | 112 --
.../Filters/registry/BooleanFilters.tsx | 31 -
.../search/Filters/registry/DateFilters.tsx | 169 --
.../search/Filters/registry/KindFilter.tsx | 47 -
.../Filters/registry/LocationFilter.tsx | 45 -
.../search/Filters/registry/TagsFilter.tsx | 56 -
.../search/Filters/registry/TextFilters.tsx | 33 -
.../app/$libraryId/search/Filters/store.ts | 115 --
.../$libraryId/search/Filters/typeGuards.ts | 78 -
.../app/$libraryId/search/FiltersOld.tsx | 890 ---------
interface/app/$libraryId/search/SearchBar.tsx | 147 --
.../app/$libraryId/search/SearchOptions.tsx | 385 ----
interface/app/$libraryId/search/context.tsx | 22 -
interface/app/$libraryId/search/index.tsx | 81 -
interface/app/$libraryId/search/store.tsx | 27 -
interface/app/$libraryId/search/useSearch.ts | 209 ---
.../search/useSearchExplorerQuery.ts | 36 -
interface/app/$libraryId/search/util.tsx | 54 -
interface/app/$libraryId/settings/Layout.tsx | 56 -
.../app/$libraryId/settings/ModalLayout.tsx | 45 -
.../$libraryId/settings/OverviewLayout.tsx | 10 -
interface/app/$libraryId/settings/Setting.tsx | 51 -
interface/app/$libraryId/settings/Sidebar.tsx | 152 --
.../settings/client/SpacedriveAccount.tsx | 31 -
.../settings/client/account/Profile.tsx | 204 ---
.../settings/client/account/ShowPassword.tsx | 27 -
.../client/account/handlers/cookieHandler.ts | 148 --
.../client/account/handlers/windowHandler.ts | 54 -
.../settings/client/account/index.tsx | 184 --
.../$libraryId/settings/client/appearance.tsx | 350 ----
.../$libraryId/settings/client/backups.tsx | 88 -
.../$libraryId/settings/client/extensions.tsx | 80 -
.../$libraryId/settings/client/general.tsx | 292 ---
.../app/$libraryId/settings/client/index.ts | 14 -
.../settings/client/keybindings.tsx | 233 ---
.../settings/client/network/debug.tsx | 36 -
.../settings/client/network/index.tsx | 502 ------
.../$libraryId/settings/client/privacy.tsx | 48 -
.../app/$libraryId/settings/client/usage.tsx | 149 --
interface/app/$libraryId/settings/index.tsx | 28 -
.../$libraryId/settings/library/contacts.tsx | 13 -
.../$libraryId/settings/library/general.tsx | 146 --
.../app/$libraryId/settings/library/index.tsx | 19 -
.../settings/library/locations/$id.tsx | 250 ---
.../library/locations/AddLocationButton.tsx | 100 -
.../library/locations/AddLocationDialog.tsx | 276 ---
.../library/locations/DeleteDialog.tsx | 38 -
.../IndexerRuleEditor/RuleButton.tsx | 79 -
.../locations/IndexerRuleEditor/RuleInput.tsx | 161 --
.../locations/IndexerRuleEditor/RulesForm.tsx | 295 ---
.../locations/IndexerRuleEditor/index.tsx | 150 --
.../settings/library/locations/ListItem.tsx | 144 --
.../settings/library/locations/PathInput.tsx | 37 -
.../settings/library/locations/index.tsx | 51 -
.../locations/openDirectoryPickerDialog.ts | 13 -
.../settings/library/saved-searches/index.tsx | 133 --
.../$libraryId/settings/library/security.tsx | 12 -
.../$libraryId/settings/library/sharing.tsx | 12 -
.../settings/library/tags/CreateDialog.tsx | 103 --
.../settings/library/tags/DeleteDialog.tsx | 36 -
.../settings/library/tags/EditForm.tsx | 92 -
.../settings/library/tags/index.tsx | 82 -
.../app/$libraryId/settings/node/index.tsx | 6 -
.../settings/node/libraries/CreateDialog.tsx | 73 -
.../node/libraries/DeleteDeviceDialog.tsx | 91 -
.../settings/node/libraries/DeleteDialog.tsx | 56 -
.../settings/node/libraries/DeviceItem.tsx | 68 -
.../settings/node/libraries/ListItem.tsx | 92 -
.../settings/node/libraries/index.tsx | 55 -
.../app/$libraryId/settings/node/p2p.tsx | 39 -
.../$libraryId/settings/resources/about.tsx | 91 -
.../settings/resources/changelog.tsx | 63 -
.../$libraryId/settings/resources/index.tsx | 7 -
interface/app/$libraryId/spaces.tsx | 3 -
interface/app/$libraryId/tag/$id.tsx | 114 --
interface/app/I18n.ts | 29 -
interface/app/RootContext.tsx | 18 -
interface/app/demo.react.tsx | 73 -
interface/app/demo.solid.tsx | 84 -
interface/app/index.tsx | 349 ----
interface/app/onboarding/Layout.tsx | 87 -
interface/app/onboarding/Progress.tsx | 52 -
interface/app/onboarding/components.tsx | 6 -
interface/app/onboarding/context.tsx | 153 --
interface/app/onboarding/creating-library.tsx | 17 -
interface/app/onboarding/full-disk.tsx | 65 -
interface/app/onboarding/index.tsx | 44 -
interface/app/onboarding/joining-library.tsx | 14 -
interface/app/onboarding/locations.tsx | 190 --
interface/app/onboarding/login.tsx | 101 --
interface/app/onboarding/new-library.tsx | 74 -
interface/app/onboarding/prerelease.tsx | 49 -
interface/app/onboarding/privacy.tsx | 54 -
interface/app/p2p/index.tsx | 76 -
interface/app/route-schemas.ts | 47 -
interface/app/style.scss | 425 -----
interface/components/Accordion.tsx | 64 -
interface/components/AlertDialog.tsx | 58 -
interface/components/AuthCheck.tsx | 10 -
interface/components/AuthRequiredOverlay.tsx | 20 -
interface/components/Authentication.tsx | 170 --
interface/components/Codeblock.tsx | 22 -
interface/components/ColorPicker.tsx | 33 -
interface/components/Devtools.tsx | 14 -
interface/components/DismissibleNotice.tsx | 76 -
interface/components/DragRegion.tsx | 19 -
interface/components/Folder.tsx | 45 -
interface/components/Icon.tsx | 25 -
interface/components/Loader.tsx | 21 -
interface/components/Login.tsx | 352 ----
interface/components/LoginButton.tsx | 49 -
interface/components/Menu.tsx | 48 -
interface/components/MultiCheckbox.tsx | 109 --
interface/components/PDFViewer.tsx | 65 -
interface/components/PasswordMeter.tsx | 62 -
interface/components/Register.tsx | 222 ---
interface/components/RequestAddDialog.tsx | 120 --
interface/components/ShowPassword.tsx | 27 -
interface/components/Sparkles.tsx | 100 -
interface/components/SubtleButton.tsx | 15 -
interface/components/TextViewer/index.tsx | 160 --
interface/components/TextViewer/one-dark.scss | 445 -----
.../components/TextViewer/one-light.scss | 433 -----
interface/components/TextViewer/prism-lazy.ts | 62 -
interface/components/TextViewer/prism.tsx | 42 -
interface/components/TrafficLights.tsx | 78 -
interface/components/TruncatedText.tsx | 21 -
interface/components/index.ts | 16 -
interface/hooks/index.ts | 36 -
interface/hooks/useAccessToken.ts | 8 -
interface/hooks/useCallbackToWatchForm.ts | 65 -
interface/hooks/useCallbackToWatchResize.ts | 77 -
interface/hooks/useClickOutside.ts | 35 -
interface/hooks/useCounter.ts | 90 -
interface/hooks/useDebouncedForm.ts | 19 -
interface/hooks/useDeeplinkEventHandler.ts | 37 -
interface/hooks/useDismissibleNoticeStore.tsx | 18 -
interface/hooks/useDragAndDropState.ts | 180 --
interface/hooks/useFileDropEventHandler.ts | 44 -
interface/hooks/useFocusState.tsx | 17 -
interface/hooks/useHomeDir.ts | 14 -
interface/hooks/useInputState.tsx | 10 -
interface/hooks/useIsDark.ts | 25 -
interface/hooks/useIsLocationIndexing.ts | 36 -
interface/hooks/useIsTextTruncated.ts | 23 -
interface/hooks/useKeyDeleteFile.tsx | 45 -
interface/hooks/useKeyMatcher.ts | 86 -
interface/hooks/useKeybind.ts | 35 -
interface/hooks/useKeybindEventHandler.ts | 73 -
interface/hooks/useKeybindFactory.ts | 5 -
interface/hooks/useLocale.ts | 22 -
interface/hooks/useMouseItemResize.ts | 65 -
interface/hooks/useOperatingSystem.ts | 31 -
interface/hooks/usePrefersReducedMotion.ts | 37 -
interface/hooks/useQuickRescan.ts | 48 -
interface/hooks/useRandomInterval.ts | 38 -
interface/hooks/useRedirectToNewLocation.ts | 46 -
interface/hooks/useRouteTitle.ts | 19 -
interface/hooks/useScrolled.tsx | 24 -
interface/hooks/useShortcut.ts | 207 ---
interface/hooks/useShowControls.ts | 11 -
interface/hooks/useTheme.ts | 57 -
interface/hooks/useWindowSize.ts | 39 -
interface/hooks/useWindowState.tsx | 7 -
interface/hooks/useZodParams.ts | 10 -
interface/hooks/useZodRouteParams.ts | 29 -
interface/hooks/useZodSearchParams.ts | 170 --
interface/index.tsx | 126 --
interface/locales/README.md | 42 -
interface/locales/ar/common.json | 774 --------
interface/locales/be/common.json | 848 ---------
interface/locales/cs/common.json | 813 ---------
interface/locales/de/common.json | 748 --------
interface/locales/en/common.json | 819 ---------
interface/locales/es/common.json | 750 --------
interface/locales/fr/common.json | 749 --------
interface/locales/i18nnext.d.ts | 1 -
interface/locales/it/common.json | 749 --------
interface/locales/ja/common.json | 744 --------
interface/locales/nl/common.json | 748 --------
interface/locales/ru/common.json | 848 ---------
interface/locales/tr/common.json | 748 --------
interface/locales/uk/common.json | 881 ---------
interface/locales/zh-CN/common.json | 740 --------
interface/locales/zh-TW/common.json | 740 --------
interface/package.json | 90 -
interface/tailwind.config.js | 1 -
interface/tsconfig.json | 13 -
interface/types/declarations.d.ts | 7 -
interface/util/Platform.tsx | 108 --
interface/util/events.ts | 37 -
interface/util/hardware.ts | 22 -
interface/util/index.tsx | 32 -
interface/util/keybinds.ts | 45 -
interface/util/pdfViewer.tsx | 24 -
interface/util/useTraceUpdate.tsx | 28 -
interface/util/uuid.ts | 1 -
tsconfig.json | 6 +-
1258 files changed, 3195 insertions(+), 123076 deletions(-)
delete mode 100644 .prettierrc.js
rename {core-new/.tasks => .tasks}/ACT-000-action-system.md (100%)
rename {core-new/.tasks => .tasks}/ACT-001-action-manager-registry.md (100%)
rename {core-new/.tasks => .tasks}/AI-000-ai-epic.md (100%)
rename {core-new/.tasks => .tasks}/AI-001-ai-agent.md (100%)
rename {core-new/.tasks => .tasks}/AI-002-create-finetuning-dataset.md (100%)
rename {core-new/.tasks => .tasks}/CLI-000-command-line-interface.md (100%)
rename {core-new/.tasks => .tasks}/CLOUD-000-cloud-as-a-peer.md (100%)
rename {core-new/.tasks => .tasks}/CLOUD-001-design-cloud-core-infra.md (63%)
rename {core-new/.tasks => .tasks}/CLOUD-002-relay-server.md (100%)
rename {core-new/.tasks => .tasks}/CLOUD-003-cloud-volume.md (100%)
rename {core-new/.tasks => .tasks}/CORE-000-vdfs-core.md (100%)
rename {core-new/.tasks => .tasks}/CORE-001-entry-centric-model.md (100%)
rename {core-new/.tasks => .tasks}/CORE-002-sdpath-addressing.md (100%)
rename {core-new/.tasks => .tasks}/CORE-003-content-identity.md (100%)
rename {core-new/.tasks => .tasks}/CORE-004-closure-table.md (100%)
rename {core-new/.tasks => .tasks}/CORE-005-file-type-system.md (100%)
rename {core-new/.tasks => .tasks}/CORE-006-semantic-tagging-architecture.md (100%)
rename {core-new/.tasks => .tasks}/CORE-007-quantum-state-for-on-demand-state-computation.md (100%)
rename {core-new/.tasks => .tasks}/CORE-008-virtual-sidecar-system.md (96%)
rename {core-new/.tasks => .tasks}/CORE-009-user-managed-collections.md (100%)
rename {core-new/.tasks => .tasks}/CORE-010-file-ingestion-workflow.md (100%)
rename {core-new/.tasks => .tasks}/DEV-000-development-validation.md (100%)
rename {core-new/.tasks => .tasks}/DEV-001-multi-process-test-framework.md (100%)
rename {core-new/.tasks => .tasks}/FILE-000-file-operations.md (100%)
rename {core-new/.tasks => .tasks}/FILE-001-file-copy-job.md (100%)
rename {core-new/.tasks => .tasks}/FILE-002-file-deletion-job.md (100%)
rename {core-new/.tasks => .tasks}/INDEX-000-indexing-file-management.md (100%)
rename {core-new/.tasks => .tasks}/INDEX-001-location-watcher-service.md (100%)
rename {core-new/.tasks => .tasks}/INDEX-002-stale-file-detection-algorithm.md (100%)
rename {core-new/.tasks => .tasks}/JOB-000-job-system.md (100%)
rename {core-new/.tasks => .tasks}/JOB-001-job-manager.md (100%)
rename {core-new/.tasks => .tasks}/JOB-002-job-logging.md (100%)
rename {core-new/.tasks => .tasks}/LOC-000-location-operations.md (100%)
rename {core-new/.tasks => .tasks}/LOC-001-location-management-actions.md (100%)
rename {core-new/.tasks => .tasks}/LOC-005-virtual-locations-via-pure-hierarchical-model.md (100%)
rename {core-new/.tasks => .tasks}/LSYNC-000-library-sync.md (100%)
rename {core-new/.tasks => .tasks}/LSYNC-001-design-library-sync-protocol.md (100%)
rename {core-new/.tasks => .tasks}/LSYNC-002-metadata-sync.md (100%)
rename {core-new/.tasks => .tasks}/LSYNC-003-file-op-sync.md (100%)
rename {core-new/.tasks => .tasks}/LSYNC-004-sync-relationship-database-schema.md (100%)
rename {core-new/.tasks => .tasks}/NET-000-networking.md (100%)
rename {core-new/.tasks => .tasks}/NET-001-iroh-p2p-stack.md (100%)
rename {core-new/.tasks => .tasks}/NET-002-device-pairing.md (100%)
rename {core-new/.tasks => .tasks}/NET-003-spacedrop-protocol.md (100%)
rename {core-new/.tasks => .tasks}/PLUG-000-wasm-plugin-system.md (100%)
rename {core-new/.tasks => .tasks}/PLUG-001-integrate-wasm-runtime.md (100%)
rename {core-new/.tasks => .tasks}/PLUG-002-define-vdfs-plugin-api.md (100%)
rename {core-new/.tasks => .tasks}/PLUG-003-develop-twitter-agent-poc.md (100%)
rename {core-new/.tasks => .tasks}/RES-000-resource-management.md (100%)
rename {core-new/.tasks => .tasks}/RES-001-adaptive-throttling.md (100%)
rename {core-new/.tasks => .tasks}/SEARCH-000-temporal-semantic-search.md (100%)
rename {core-new/.tasks => .tasks}/SEARCH-001-async-searchjob.md (100%)
rename {core-new/.tasks => .tasks}/SEARCH-002-two-stage-fts-semantic-reranking.md (100%)
rename {core-new/.tasks => .tasks}/SEARCH-003-unified-vector-repositories.md (100%)
rename {core-new/.tasks => .tasks}/SEC-000-security-and-privacy.md (100%)
rename {core-new/.tasks => .tasks}/SEC-002-database-encryption.md (100%)
rename {core-new/.tasks => .tasks}/SEC-003-cryptographic-audit-log.md (100%)
rename {core-new/.tasks => .tasks}/SEC-004-rbac-system.md (100%)
rename {core-new/.tasks => .tasks}/SEC-005-secure-credential-vault.md (100%)
rename {core-new/.tasks => .tasks}/SEC-006-certificate-pinning.md (100%)
rename {core-new/.tasks => .tasks}/SEC-007-per-library-encryption-policies-for-public-sharing.md (100%)
rename {core-new/.tasks => .tasks}/VOL-000-volume-operations.md (100%)
rename {core-new/.tasks => .tasks}/VOL-001-volume-physicalclass-and-location-logicalclass.md (100%)
rename {core-new/.tasks => .tasks}/VOL-002-automatic-volume-classification.md (100%)
rename {core-new/.tasks => .tasks}/VOL-003-intelligent-storage-tiering-warning-system.md (100%)
rename {core-new/.tasks => .tasks}/VOL-004-remote-volume-indexing-with-opendal.md (100%)
rename {core-new/.tasks => .tasks}/VOL-005-treat-connected-iphone-as-a-virtual-volume-for-direct-import.md (100%)
rename {core-new/.tasks => .tasks}/VSS-003-reference-sidecars-for-live-photo-support.md (100%)
rename {core-new/.tasks => .tasks}/task.schema.json (100%)
rename spacedrive-cloud => apps/cloud (100%)
delete mode 100755 combine_paths.py
delete mode 100644 core-new/Cargo.toml
delete mode 100644 core-new/benchmarks/Cargo.toml
delete mode 100644 core-new/build.rs
delete mode 100644 core-new/examples/indexing_showcase.rs
delete mode 100644 core-new/src/context.rs
delete mode 100644 core-new/src/lib.rs
delete mode 100644 core-new/src/library/config.rs
delete mode 100644 core-new/src/library/mod.rs
delete mode 100644 core-new/src/location/mod.rs
delete mode 100644 core-new/src/volume/error.rs
delete mode 100644 core-new/src/volume/mod.rs
delete mode 100644 core-new/src/volume/speed.rs
delete mode 100644 core-new/src/volume/types.rs
rename {core-new => core}/.scripts/combine.sh (100%)
rename {core-new => core/.scripts}/test_daemon.sh (100%)
rename {core-new => core/.scripts}/update_spacedrive.sh (95%)
rename {core-new => core}/AGENTS.md (100%)
rename {core-new => core}/Cargo.lock (99%)
rename {core-new => core}/README.md (99%)
create mode 100644 core/benchmarks/Cargo.toml
rename {core-new => core}/benchmarks/recipes/shape_large.yaml (100%)
rename {core-new => core}/benchmarks/recipes/shape_medium.yaml (100%)
rename {core-new => core}/benchmarks/recipes/shape_small.yaml (100%)
rename {core-new => core}/benchmarks/results/shape_large-aggregation-hdd.json (100%)
rename {core-new => core}/benchmarks/results/shape_large-aggregation-ssd.json (90%)
rename {core-new => core}/benchmarks/results/shape_large-content_identification-hdd.json (100%)
rename {core-new => core}/benchmarks/results/shape_large-content_identification-ssd.json (90%)
rename {core-new => core}/benchmarks/results/shape_large-indexing_discovery-hdd.json (100%)
rename {core-new => core}/benchmarks/results/shape_large-indexing_discovery-ssd.json (90%)
rename {core-new => core}/benchmarks/results/shape_medium-aggregation-hdd.json (100%)
rename {core-new => core}/benchmarks/results/shape_medium-aggregation-ssd.json (90%)
rename {core-new => core}/benchmarks/results/shape_medium-content_identification-hdd.json (100%)
rename {core-new => core}/benchmarks/results/shape_medium-content_identification-ssd.json (90%)
rename {core-new => core}/benchmarks/results/shape_medium-indexing_discovery-hdd.json (100%)
rename {core-new => core}/benchmarks/results/shape_medium-indexing_discovery-ssd.json (90%)
rename {core-new => core}/benchmarks/results/shape_small-aggregation-hdd.json (100%)
rename {core-new => core}/benchmarks/results/shape_small-aggregation-ssd.json (90%)
rename {core-new => core}/benchmarks/results/shape_small-content_identification-hdd.json (100%)
rename {core-new => core}/benchmarks/results/shape_small-content_identification-ssd.json (90%)
rename {core-new => core}/benchmarks/results/shape_small-indexing_discovery-hdd.json (100%)
rename {core-new => core}/benchmarks/results/shape_small-indexing_discovery-ssd.json (90%)
rename {core-new => core}/benchmarks/results/whitepaper_metrics.csv (100%)
rename {core-new => core}/benchmarks/src/bin/sd-bench-new.rs (100%)
rename {core-new => core}/benchmarks/src/cli/args.rs (100%)
rename {core-new => core}/benchmarks/src/cli/commands.rs (100%)
rename {core-new => core}/benchmarks/src/cli/mod.rs (100%)
rename {core-new => core}/benchmarks/src/config/mod.rs (100%)
rename {core-new => core}/benchmarks/src/core_boot/mod.rs (100%)
rename {core-new => core}/benchmarks/src/generator/filesystem.rs (100%)
rename {core-new => core}/benchmarks/src/generator/mod.rs (100%)
rename {core-new => core}/benchmarks/src/generator/noop.rs (100%)
rename {core-new => core}/benchmarks/src/generator/registry.rs (100%)
rename {core-new => core}/benchmarks/src/lib.rs (100%)
rename {core-new => core}/benchmarks/src/metrics/mod.rs (100%)
rename {core-new => core}/benchmarks/src/metrics/sources.rs (100%)
rename {core-new => core}/benchmarks/src/recipe/mod.rs (100%)
rename {core-new => core}/benchmarks/src/recipe/schema.rs (100%)
rename {core-new => core}/benchmarks/src/reporting/csv.rs (100%)
rename {core-new => core}/benchmarks/src/reporting/json_summary.rs (100%)
rename {core-new => core}/benchmarks/src/reporting/mod.rs (100%)
rename {core-new => core}/benchmarks/src/reporting/registry.rs (100%)
rename {core-new => core}/benchmarks/src/runner/mod.rs (100%)
rename {core-new => core}/benchmarks/src/runner/monitor.rs (100%)
rename {core-new => core}/benchmarks/src/scenarios/common.rs (100%)
rename {core-new => core}/benchmarks/src/scenarios/content_identification.rs (100%)
rename {core-new => core}/benchmarks/src/scenarios/core_indexing.rs (100%)
rename {core-new => core}/benchmarks/src/scenarios/mod.rs (100%)
rename {core-new => core}/benchmarks/src/scenarios/registry.rs (100%)
rename {core-new => core}/benchmarks/src/util/fs.rs (100%)
rename {core-new => core}/benchmarks/src/util/mod.rs (100%)
rename {core-new => core}/benchmarks/src/util/rng.rs (100%)
rename {core-new => core}/benchmarks/src/util/time.rs (100%)
delete mode 100644 core/crates/cloud-services/Cargo.toml
delete mode 100644 core/crates/cloud-services/src/client.rs
delete mode 100644 core/crates/cloud-services/src/error.rs
delete mode 100644 core/crates/cloud-services/src/key_manager/key_store.rs
delete mode 100644 core/crates/cloud-services/src/key_manager/mod.rs
delete mode 100644 core/crates/cloud-services/src/lib.rs
delete mode 100644 core/crates/cloud-services/src/p2p/mod.rs
delete mode 100644 core/crates/cloud-services/src/p2p/new_sync_messages_notifier.rs
delete mode 100644 core/crates/cloud-services/src/p2p/runner.rs
delete mode 100644 core/crates/cloud-services/src/sync/ingest.rs
delete mode 100644 core/crates/cloud-services/src/sync/mod.rs
delete mode 100644 core/crates/cloud-services/src/sync/receive.rs
delete mode 100644 core/crates/cloud-services/src/sync/send.rs
delete mode 100644 core/crates/cloud-services/src/token_refresher.rs
delete mode 100644 core/crates/file-path-helper/Cargo.toml
delete mode 100644 core/crates/file-path-helper/README.md
delete mode 100644 core/crates/file-path-helper/src/isolated_file_path_data.rs
delete mode 100644 core/crates/file-path-helper/src/lib.rs
delete mode 100644 core/crates/heavy-lifting/Cargo.toml
delete mode 100644 core/crates/heavy-lifting/src/file_identifier/cas_id.rs
delete mode 100644 core/crates/heavy-lifting/src/file_identifier/job.rs
delete mode 100644 core/crates/heavy-lifting/src/file_identifier/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/file_identifier/shallow.rs
delete mode 100644 core/crates/heavy-lifting/src/file_identifier/tasks/identifier.rs
delete mode 100644 core/crates/heavy-lifting/src/file_identifier/tasks/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/file_identifier/tasks/object_processor.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/job.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/shallow.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/tasks/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/tasks/saver.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/tasks/updater.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/tasks/walker/entry.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/tasks/walker/metadata.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/tasks/walker/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/tasks/walker/rules.rs
delete mode 100644 core/crates/heavy-lifting/src/indexer/tasks/walker/save_state.rs
delete mode 100644 core/crates/heavy-lifting/src/job_system/error.rs
delete mode 100644 core/crates/heavy-lifting/src/job_system/job.rs
delete mode 100644 core/crates/heavy-lifting/src/job_system/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/job_system/report.rs
delete mode 100644 core/crates/heavy-lifting/src/job_system/runner.rs
delete mode 100644 core/crates/heavy-lifting/src/job_system/store.rs
delete mode 100644 core/crates/heavy-lifting/src/job_system/utils.rs
delete mode 100644 core/crates/heavy-lifting/src/lib.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/helpers/exif_media_data.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/helpers/ffmpeg_media_data.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/helpers/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/helpers/thumbnailer.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/job.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/shallow.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/tasks/media_data_extractor.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/tasks/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/media_processor/tasks/thumbnailer.rs
delete mode 100644 core/crates/heavy-lifting/src/utils/mod.rs
delete mode 100644 core/crates/heavy-lifting/src/utils/sub_path.rs
delete mode 100644 core/crates/indexer-rules/Cargo.toml
delete mode 100644 core/crates/indexer-rules/src/lib.rs
delete mode 100644 core/crates/indexer-rules/src/seed.rs
delete mode 100644 core/crates/indexer-rules/src/serde_impl.rs
delete mode 100644 core/crates/p2p/Cargo.toml
delete mode 100644 core/crates/p2p/src/error.rs
delete mode 100644 core/crates/p2p/src/lib.rs
delete mode 100644 core/crates/p2p/src/schema/cloud_services/authorize_new_device_in_sync_group.rs
delete mode 100644 core/crates/p2p/src/schema/cloud_services/mod.rs
delete mode 100644 core/crates/p2p/src/schema/cloud_services/notify_new_sync_messages.rs
delete mode 100644 core/crates/p2p/src/schema/mod.rs
delete mode 100644 core/crates/p2p/src/server/mod.rs
delete mode 100644 core/crates/p2p/src/server/router/cloud_services.rs
delete mode 100644 core/crates/p2p/src/server/router/mod.rs
delete mode 100644 core/crates/prisma-helpers/Cargo.toml
delete mode 100644 core/crates/prisma-helpers/src/lib.rs
rename {core-new => core/crates}/spacedrive-jobs-derive/Cargo.toml (100%)
rename {core-new => core/crates}/spacedrive-jobs-derive/src/lib.rs (100%)
delete mode 100644 core/crates/sync/Cargo.toml
delete mode 100644 core/crates/sync/README.md
delete mode 100644 core/crates/sync/src/backfill.rs
delete mode 100644 core/crates/sync/src/db_operation.rs
delete mode 100644 core/crates/sync/src/ingest_utils.rs
delete mode 100644 core/crates/sync/src/lib.rs
delete mode 100644 core/crates/sync/src/manager.rs
rename {core-new => core}/crush.json (100%)
rename {core-new => core}/examples/file_type_demo.rs (100%)
rename {core-new => core}/examples/indexing_demo.rs (100%)
create mode 100644 core/examples/indexing_showcase.rs
rename {core-new => core}/examples/job_logging_test.rs (100%)
rename {core-new => core}/examples/library_demo.rs (100%)
rename {core-new => core}/examples/location_watcher_demo.rs (100%)
rename {core-new => core}/examples/pause_resume_demo.rs (100%)
rename {core-new => core}/examples/shutdown_demo.rs (100%)
rename {core-new => core}/examples/simple_pause_resume.rs (100%)
rename {core-new => core}/examples/test_migration.rs (100%)
rename {core-new => core}/examples/volume_demo.rs (100%)
delete mode 100644 core/prisma/migrations/20230616064440_init/migration.sql
delete mode 100644 core/prisma/migrations/20230618234117_remove_owned_operations/migration.sql
delete mode 100644 core/prisma/migrations/20230619032753_p2p/migration.sql
delete mode 100644 core/prisma/migrations/20230619144125_collate_nocase/migration.sql
delete mode 100644 core/prisma/migrations/20230621173906_size_in_bytes_bytes/migration.sql
delete mode 100644 core/prisma/migrations/20230711114013_preferences/migration.sql
delete mode 100644 core/prisma/migrations/20230712050046_library_instance/migration.sql
delete mode 100644 core/prisma/migrations/20230712063345_notifications/migration.sql
delete mode 100644 core/prisma/migrations/20230724131659_relation_operation/migration.sql
delete mode 100644 core/prisma/migrations/20230812141757_added_albums/migration.sql
delete mode 100644 core/prisma/migrations/20230828195811_media_data/migration.sql
delete mode 100644 core/prisma/migrations/20230912065026_file_path_hidden/migration.sql
delete mode 100644 core/prisma/migrations/20230921203938_add_size_bytes_in_location_table/migration.sql
delete mode 100644 core/prisma/migrations/20230926180932_remove_device_from_file_path_table/migration.sql
delete mode 100644 core/prisma/migrations/20231005202254_sort_by_image_taken/migration.sql
delete mode 100644 core/prisma/migrations/20231022013532_saved_searches/migration.sql
delete mode 100644 core/prisma/migrations/20231113140411_tag_date_created/migration.sql
delete mode 100644 core/prisma/migrations/20231121173834_filters_string/migration.sql
delete mode 100644 core/prisma/migrations/20231204174640_update_tags_and_labels/migration.sql
delete mode 100644 core/prisma/migrations/20231219105608_crdt_operation/migration.sql
delete mode 100644 core/prisma/migrations/20240221044741_drop_node_peer_id/migration.sql
delete mode 100644 core/prisma/migrations/20240314064009_cache_instance_metadata/migration.sql
delete mode 100644 core/prisma/migrations/20240319065147_remove_remoteidentity/migration.sql
delete mode 100644 core/prisma/migrations/20240405185748_add_target_to_saved_search/migration.sql
delete mode 100644 core/prisma/migrations/20240408081222_numeric_sync_model_id/migration.sql
delete mode 100644 core/prisma/migrations/20240408215355_add_location_scan_state/migration.sql
delete mode 100644 core/prisma/migrations/20240409202941_rename_media_data_to_exif_data/migration.sql
delete mode 100644 core/prisma/migrations/20240508074615_ffmpeg_media_data/migration.sql
delete mode 100644 core/prisma/migrations/20240512011221_better_statistics/migration.sql
delete mode 100644 core/prisma/migrations/20240517045456_change_indexer_rule_name_to_unique/migration.sql
delete mode 100644 core/prisma/migrations/20240531034707_add_node_remote_identity_to_instance/migration.sql
delete mode 100644 core/prisma/migrations/20240705205916_create_object_kind_statistics_table/migration.sql
delete mode 100644 core/prisma/migrations/20240710015044_add_info_colum_to_job/migration.sql
delete mode 100644 core/prisma/migrations/20240712155557_create_storage_statistics_table/migration.sql
delete mode 100644 core/prisma/migrations/20250130041311_adding_devices/migration.sql
delete mode 100644 core/prisma/migrations/migration_lock.toml
delete mode 100644 core/prisma/schema.prisma
rename {core-new => core}/rust-toolchain.toml (100%)
delete mode 100644 core/src/api/backups.rs
delete mode 100644 core/src/api/cloud/devices.rs
delete mode 100644 core/src/api/cloud/libraries.rs
delete mode 100644 core/src/api/cloud/locations.rs
delete mode 100644 core/src/api/cloud/mod.rs
delete mode 100644 core/src/api/cloud/sync_groups.rs
delete mode 100644 core/src/api/cloud/thumbnails.rs
delete mode 100644 core/src/api/devices.rs
delete mode 100644 core/src/api/ephemeral_files.rs
delete mode 100644 core/src/api/files.rs
delete mode 100644 core/src/api/jobs.rs
delete mode 100644 core/src/api/keys.rs
delete mode 100644 core/src/api/labels.rs
delete mode 100644 core/src/api/libraries.rs
delete mode 100644 core/src/api/locations.rs
delete mode 100644 core/src/api/mod.rs
delete mode 100644 core/src/api/models.rs
delete mode 100644 core/src/api/nodes.rs
delete mode 100644 core/src/api/notifications.rs
delete mode 100644 core/src/api/p2p.rs
delete mode 100644 core/src/api/preferences.rs
delete mode 100644 core/src/api/search/exif_data.rs
delete mode 100644 core/src/api/search/file_path.rs
delete mode 100644 core/src/api/search/mod.rs
delete mode 100644 core/src/api/search/object.rs
delete mode 100644 core/src/api/search/saved.rs
delete mode 100644 core/src/api/search/utils.rs
delete mode 100644 core/src/api/sync.rs
delete mode 100644 core/src/api/tags.rs
delete mode 100644 core/src/api/utils/invalidate.rs
delete mode 100644 core/src/api/utils/library.rs
delete mode 100644 core/src/api/utils/mod.rs
delete mode 100644 core/src/api/volumes.rs
delete mode 100644 core/src/api/web_api.rs
rename {core-new => core}/src/bin/cli.rs (100%)
rename {core-new => core}/src/config/app_config.rs (100%)
rename {core-new => core}/src/config/migration.rs (100%)
rename {core-new => core}/src/config/mod.rs (100%)
delete mode 100644 core/src/custom_uri/mod.rs
delete mode 100644 core/src/custom_uri/mpsc_to_async_write.rs
delete mode 100644 core/src/custom_uri/serve_file.rs
delete mode 100644 core/src/custom_uri/utils.rs
rename {core-new => core}/src/device/config.rs (100%)
rename {core-new => core}/src/device/manager.rs (100%)
rename {core-new => core}/src/device/mod.rs (100%)
rename {core-new => core}/src/domain/addressing.rs (100%)
rename {core-new => core}/src/domain/content_identity.rs (100%)
rename {core-new => core}/src/domain/device.rs (100%)
rename {core-new => core}/src/domain/entry.rs (100%)
rename {core-new => core}/src/domain/location.rs (100%)
rename {core-new => core}/src/domain/mod.rs (100%)
rename {core-new => core}/src/domain/user_metadata.rs (100%)
rename {core-new => core}/src/domain/volume.rs (100%)
rename {core-new => core}/src/file_type/builtin.rs (100%)
rename {core-new => core}/src/file_type/definitions/archives.toml (100%)
rename {core-new => core}/src/file_type/definitions/audio.toml (100%)
rename {core-new => core}/src/file_type/definitions/code.toml (100%)
rename {core-new => core}/src/file_type/definitions/documents.toml (100%)
rename {core-new => core}/src/file_type/definitions/images.toml (100%)
rename {core-new => core}/src/file_type/definitions/misc.toml (100%)
rename {core-new => core}/src/file_type/definitions/video.toml (100%)
rename {core-new => core}/src/file_type/magic.rs (100%)
rename {core-new => core}/src/file_type/mod.rs (100%)
rename {core-new => core}/src/file_type/registry.rs (100%)
rename {core-new => core}/src/infrastructure/actions/BUILDER_REFACTOR_PLAN.md (100%)
rename {core-new => core}/src/infrastructure/actions/builder.rs (100%)
rename {core-new => core}/src/infrastructure/actions/error.rs (100%)
rename {core-new => core}/src/infrastructure/actions/handler.rs (100%)
rename {core-new => core}/src/infrastructure/actions/manager.rs (100%)
rename {core-new => core}/src/infrastructure/actions/mod.rs (100%)
rename {core-new => core}/src/infrastructure/actions/output.rs (100%)
rename {core-new => core}/src/infrastructure/actions/receipt.rs (100%)
rename {core-new => core}/src/infrastructure/actions/registry.rs (100%)
rename {core-new => core}/src/infrastructure/actions/tests.rs (100%)
rename {core-new => core}/src/infrastructure/cli/README.md (100%)
rename {core-new => core}/src/infrastructure/cli/adapters/copy.rs (100%)
rename {core-new => core}/src/infrastructure/cli/adapters/mod.rs (100%)
rename {core-new => core}/src/infrastructure/cli/commands/daemon.rs (100%)
rename {core-new => core}/src/infrastructure/cli/commands/file.rs (100%)
rename {core-new => core}/src/infrastructure/cli/commands/job.rs (100%)
rename {core-new => core}/src/infrastructure/cli/commands/library.rs (100%)
rename {core-new => core}/src/infrastructure/cli/commands/location.rs (100%)
rename {core-new => core}/src/infrastructure/cli/commands/mod.rs (100%)
rename {core-new => core}/src/infrastructure/cli/commands/network.rs (100%)
rename {core-new => core}/src/infrastructure/cli/commands/system.rs (100%)
rename {core-new => core}/src/infrastructure/cli/commands/volume.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/client.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/config.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/handlers/core.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/handlers/file.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/handlers/job.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/handlers/library.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/handlers/location.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/handlers/mod.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/handlers/network.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/handlers/system.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/handlers/volume.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/mod.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/services/helpers.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/services/mod.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/services/state.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/types/commands.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/types/common.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/types/mod.rs (100%)
rename {core-new => core}/src/infrastructure/cli/daemon/types/responses.rs (100%)
rename {core-new => core}/src/infrastructure/cli/mod.rs (100%)
rename {core-new => core}/src/infrastructure/cli/output/context.rs (100%)
rename {core-new => core}/src/infrastructure/cli/output/formatters.rs (100%)
rename {core-new => core}/src/infrastructure/cli/output/messages.rs (100%)
rename {core-new => core}/src/infrastructure/cli/output/mod.rs (100%)
rename {core-new => core}/src/infrastructure/cli/output/section.rs (100%)
rename {core-new => core}/src/infrastructure/cli/output/tests.rs (100%)
rename {core-new => core}/src/infrastructure/cli/pairing_ui.rs (100%)
rename {core-new => core}/src/infrastructure/cli/state.rs (100%)
rename {core-new => core}/src/infrastructure/cli/tui.rs (100%)
rename {core-new => core}/src/infrastructure/cli/utils.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/audit_log.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/collection.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/collection_entry.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/content_identity.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/content_kind.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/device.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/directory_paths.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/entry.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/entry_closure.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/indexer_rule.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/label.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/location.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/metadata_label.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/metadata_tag.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/mime_type.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/mod.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/sidecar.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/sidecar_availability.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/tag.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/user_metadata.rs (100%)
rename {core-new => core}/src/infrastructure/database/entities/volume.rs (100%)
rename {core-new => core}/src/infrastructure/database/migration/m20240101_000001_initial_schema.rs (100%)
rename {core-new => core}/src/infrastructure/database/migration/m20240102_000001_populate_lookups.rs (100%)
rename {core-new => core}/src/infrastructure/database/migration/m20240107_000001_create_collections.rs (100%)
rename {core-new => core}/src/infrastructure/database/migration/m20250109_000001_create_sidecars.rs (100%)
rename {core-new => core}/src/infrastructure/database/migration/m20250110_000001_refactor_volumes_table.rs (100%)
rename {core-new => core}/src/infrastructure/database/migration/m20250112_000001_create_indexer_rules.rs (100%)
rename {core-new => core}/src/infrastructure/database/migration/mod.rs (100%)
rename {core-new => core}/src/infrastructure/database/mod.rs (100%)
rename {core-new => core}/src/infrastructure/events/mod.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/context.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/database.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/error.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/executor.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/generic_progress.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/handle.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/logger.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/manager.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/mod.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/output.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/progress.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/registry.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/traits.rs (100%)
rename {core-new => core}/src/infrastructure/jobs/types.rs (100%)
rename {core-new => core}/src/infrastructure/mod.rs (100%)
rename {core-new => core}/src/keys/device_key_manager.rs (100%)
rename {core-new => core}/src/keys/library_key_manager.rs (100%)
rename {core-new => core}/src/keys/mod.rs (100%)
rename {core-new => core}/src/library/error.rs (100%)
delete mode 100644 core/src/library/library.rs
rename {core-new => core}/src/library/lock.rs (100%)
rename {core-new => core}/src/library/manager.rs (100%)
delete mode 100644 core/src/library/manager/error.rs
delete mode 100644 core/src/library/manager/mod.rs
delete mode 100644 core/src/library/manager/pragmas.rs
delete mode 100644 core/src/library/name.rs
delete mode 100644 core/src/library/statistics.rs
delete mode 100644 core/src/location/archive/archive_job.rs
delete mode 100644 core/src/location/archive/mod.rs
delete mode 100644 core/src/location/error.rs
rename {core-new => core}/src/location/manager.rs (100%)
delete mode 100644 core/src/location/manager/mod.rs
delete mode 100644 core/src/location/manager/runner.rs
delete mode 100644 core/src/location/manager/watcher/android.rs
delete mode 100644 core/src/location/manager/watcher/ios.rs
delete mode 100644 core/src/location/manager/watcher/linux.rs
delete mode 100644 core/src/location/manager/watcher/macos.rs
delete mode 100644 core/src/location/manager/watcher/mod.rs
delete mode 100644 core/src/location/manager/watcher/utils.rs
delete mode 100644 core/src/location/manager/watcher/windows.rs
delete mode 100644 core/src/location/metadata.rs
delete mode 100644 core/src/location/non_indexed.rs
delete mode 100644 core/src/node/config.rs
delete mode 100644 core/src/node/hardware.rs
delete mode 100644 core/src/node/mod.rs
delete mode 100644 core/src/node/platform.rs
delete mode 100644 core/src/notifications.rs
delete mode 100644 core/src/object/fs/archive.rs
delete mode 100644 core/src/object/fs/convert.rs
delete mode 100644 core/src/object/fs/error.rs
delete mode 100644 core/src/object/fs/mod.rs
delete mode 100644 core/src/object/fs/old_copy.rs
delete mode 100644 core/src/object/fs/old_cut.rs
delete mode 100644 core/src/object/fs/old_delete.rs
delete mode 100644 core/src/object/fs/old_erase.rs
delete mode 100644 core/src/object/fs/sync.rs
delete mode 100644 core/src/object/media/old_thumbnail/clean_up.rs
delete mode 100644 core/src/object/media/old_thumbnail/directory.rs
delete mode 100644 core/src/object/mod.rs
delete mode 100644 core/src/object/old_orphan_remover.rs
delete mode 100644 core/src/object/tag/mod.rs
delete mode 100644 core/src/object/tag/seed.rs
delete mode 100644 core/src/object/validation/hash.rs
delete mode 100644 core/src/object/validation/mod.rs
delete mode 100644 core/src/object/validation/old_validator_job.rs
delete mode 100644 core/src/old_job/error.rs
delete mode 100644 core/src/old_job/manager.rs
delete mode 100644 core/src/old_job/mod.rs
delete mode 100644 core/src/old_job/report.rs
delete mode 100644 core/src/old_job/worker.rs
delete mode 100644 core/src/old_p2p/events.rs
delete mode 100644 core/src/old_p2p/libraries.rs
delete mode 100644 core/src/old_p2p/manager.rs
delete mode 100644 core/src/old_p2p/metadata.rs
delete mode 100644 core/src/old_p2p/mod.rs
delete mode 100644 core/src/old_p2p/operations/library.rs
delete mode 100644 core/src/old_p2p/operations/mod.rs
delete mode 100644 core/src/old_p2p/operations/ping.rs
delete mode 100644 core/src/old_p2p/operations/rspc.rs
delete mode 100644 core/src/old_p2p/operations/spacedrop.rs
delete mode 100644 core/src/old_p2p/protocol.rs
delete mode 100644 core/src/old_p2p/sync/mod.rs
delete mode 100644 core/src/old_p2p/sync/proto.rs
rename {core-new => core}/src/operations/addressing.rs (100%)
rename {core-new => core}/src/operations/content/action.rs (100%)
rename {core-new => core}/src/operations/content/mod.rs (100%)
rename {core-new => core}/src/operations/devices/mod.rs (100%)
rename {core-new => core}/src/operations/devices/revoke/action.rs (100%)
rename {core-new => core}/src/operations/devices/revoke/mod.rs (100%)
rename {core-new => core}/src/operations/devices/revoke/output.rs (100%)
rename {core-new => core}/src/operations/entries/mod.rs (100%)
rename {core-new => core}/src/operations/entries/state.rs (100%)
rename {core-new => core}/src/operations/files/copy/action.rs (100%)
rename {core-new => core}/src/operations/files/copy/database.rs (100%)
rename {core-new => core}/src/operations/files/copy/docs/ANALYSIS.md (100%)
rename {core-new => core}/src/operations/files/copy/docs/FILE_SYNC_OVERLAP.md (100%)
rename {core-new => core}/src/operations/files/copy/docs/PROGRESSIVE_COPY_DESIGN.md (100%)
rename {core-new => core}/src/operations/files/copy/docs/RESUME_VALIDATION_DESIGN.md (100%)
rename {core-new => core}/src/operations/files/copy/input.rs (100%)
rename {core-new => core}/src/operations/files/copy/job.rs (100%)
rename {core-new => core}/src/operations/files/copy/mod.rs (100%)
rename {core-new => core}/src/operations/files/copy/output.rs (100%)
rename {core-new => core}/src/operations/files/copy/routing.rs (100%)
rename {core-new => core}/src/operations/files/copy/strategy.rs (100%)
rename {core-new => core}/src/operations/files/delete/action.rs (100%)
rename {core-new => core}/src/operations/files/delete/job.rs (100%)
rename {core-new => core}/src/operations/files/delete/mod.rs (100%)
rename {core-new => core}/src/operations/files/delete/output.rs (100%)
rename {core-new => core}/src/operations/files/duplicate_detection/action.rs (100%)
rename {core-new => core}/src/operations/files/duplicate_detection/job.rs (100%)
rename {core-new => core}/src/operations/files/duplicate_detection/mod.rs (100%)
rename {core-new => core}/src/operations/files/mod.rs (100%)
rename {core-new => core}/src/operations/files/validation/action.rs (100%)
rename {core-new => core}/src/operations/files/validation/job.rs (100%)
rename {core-new => core}/src/operations/files/validation/mod.rs (100%)
rename {core-new => core}/src/operations/indexing/action.rs (100%)
rename {core-new => core}/src/operations/indexing/change_detection/mod.rs (100%)
rename {core-new => core}/src/operations/indexing/entry.rs (100%)
rename {core-new => core}/src/operations/indexing/hierarchy.rs (100%)
rename {core-new => core}/src/operations/indexing/job.rs (100%)
rename {core-new => core}/src/operations/indexing/metrics.rs (100%)
rename {core-new => core}/src/operations/indexing/mod.rs (100%)
rename {core-new => core}/src/operations/indexing/path_resolver.rs (100%)
rename {core-new => core}/src/operations/indexing/persistence.rs (100%)
rename {core-new => core}/src/operations/indexing/phases/aggregation.rs (100%)
rename {core-new => core}/src/operations/indexing/phases/content.rs (100%)
rename {core-new => core}/src/operations/indexing/phases/discovery.rs (100%)
rename {core-new => core}/src/operations/indexing/phases/mod.rs (100%)
rename {core-new => core}/src/operations/indexing/phases/processing.rs (100%)
rename {core-new => core}/src/operations/indexing/progress.rs (100%)
rename {core-new => core}/src/operations/indexing/rules.rs (100%)
rename {core-new => core}/src/operations/indexing/state.rs (100%)
rename {core-new => core}/src/operations/indexing/tests/mod.rs (100%)
rename {core-new => core}/src/operations/libraries/create/action.rs (100%)
rename {core-new => core}/src/operations/libraries/create/mod.rs (100%)
rename {core-new => core}/src/operations/libraries/create/output.rs (100%)
rename {core-new => core}/src/operations/libraries/delete/action.rs (100%)
rename {core-new => core}/src/operations/libraries/delete/mod.rs (100%)
rename {core-new => core}/src/operations/libraries/delete/output.rs (100%)
rename {core-new => core}/src/operations/libraries/export/action.rs (100%)
rename {core-new => core}/src/operations/libraries/export/mod.rs (100%)
rename {core-new => core}/src/operations/libraries/export/output.rs (100%)
rename {core-new => core}/src/operations/libraries/mod.rs (100%)
rename {core-new => core}/src/operations/libraries/rename/action.rs (100%)
rename {core-new => core}/src/operations/libraries/rename/mod.rs (100%)
rename {core-new => core}/src/operations/libraries/rename/output.rs (100%)
rename {core-new => core}/src/operations/locations/add/action.rs (100%)
rename {core-new => core}/src/operations/locations/add/mod.rs (100%)
rename {core-new => core}/src/operations/locations/add/output.rs (100%)
rename {core-new => core}/src/operations/locations/index/action.rs (100%)
rename {core-new => core}/src/operations/locations/index/mod.rs (100%)
rename {core-new => core}/src/operations/locations/mod.rs (100%)
rename {core-new => core}/src/operations/locations/remove/action.rs (100%)
rename {core-new => core}/src/operations/locations/remove/mod.rs (100%)
rename {core-new => core}/src/operations/locations/remove/output.rs (100%)
rename {core-new => core}/src/operations/locations/rescan/action.rs (100%)
rename {core-new => core}/src/operations/locations/rescan/mod.rs (100%)
rename {core-new => core}/src/operations/locations/rescan/output.rs (100%)
rename {core-new => core}/src/operations/media/live_photo.rs (100%)
rename {core-new => core}/src/operations/media/live_photo_query.rs (100%)
rename {core-new => core}/src/operations/media/mod.rs (100%)
rename {core-new => core}/src/operations/media/thumbnail/action.rs (100%)
rename {core-new => core}/src/operations/media/thumbnail/error.rs (100%)
rename {core-new => core}/src/operations/media/thumbnail/generator.rs (100%)
rename {core-new => core}/src/operations/media/thumbnail/job.rs (100%)
rename {core-new => core}/src/operations/media/thumbnail/mod.rs (100%)
rename {core-new => core}/src/operations/media/thumbnail/state.rs (100%)
rename {core-new => core}/src/operations/media/thumbnail/utils.rs (100%)
rename {core-new => core}/src/operations/metadata/action.rs (100%)
rename {core-new => core}/src/operations/metadata/mod.rs (100%)
rename {core-new => core}/src/operations/mod.rs (100%)
rename {core-new => core}/src/operations/sidecar/mod.rs (100%)
rename {core-new => core}/src/operations/sidecar/path.rs (100%)
rename {core-new => core}/src/operations/sidecar/types.rs (100%)
rename {core-new => core}/src/operations/volumes/mod.rs (100%)
rename {core-new => core}/src/operations/volumes/speed_test/action.rs (100%)
rename {core-new => core}/src/operations/volumes/speed_test/handler.rs (100%)
rename {core-new => core}/src/operations/volumes/speed_test/mod.rs (100%)
rename {core-new => core}/src/operations/volumes/track/action.rs (100%)
rename {core-new => core}/src/operations/volumes/track/handler.rs (100%)
rename {core-new => core}/src/operations/volumes/track/mod.rs (100%)
rename {core-new => core}/src/operations/volumes/untrack/action.rs (100%)
rename {core-new => core}/src/operations/volumes/untrack/handler.rs (100%)
rename {core-new => core}/src/operations/volumes/untrack/mod.rs (100%)
delete mode 100644 core/src/preferences/kv.rs
delete mode 100644 core/src/preferences/library.rs
delete mode 100644 core/src/preferences/mod.rs
delete mode 100644 core/src/search/mod.rs
rename {core-new => core}/src/services/device.rs (100%)
rename {core-new => core}/src/services/entry_state_service.rs (100%)
rename {core-new => core}/src/services/file_sharing.rs (100%)
rename {core-new => core}/src/services/location_watcher/event_handler.rs (100%)
rename {core-new => core}/src/services/location_watcher/mod.rs (100%)
rename {core-new => core}/src/services/location_watcher/platform/linux.rs (100%)
rename {core-new => core}/src/services/location_watcher/platform/macos.rs (100%)
rename {core-new => core}/src/services/location_watcher/platform/mod.rs (100%)
rename {core-new => core}/src/services/location_watcher/platform/windows.rs (100%)
rename {core-new => core}/src/services/location_watcher/utils.rs (100%)
rename {core-new => core}/src/services/mod.rs (100%)
rename {core-new => core}/src/services/networking/core/event_loop.rs (100%)
rename {core-new => core}/src/services/networking/core/mod.rs (100%)
rename {core-new => core}/src/services/networking/device/connection.rs (100%)
rename {core-new => core}/src/services/networking/device/mod.rs (100%)
rename {core-new => core}/src/services/networking/device/persistence.rs (100%)
rename {core-new => core}/src/services/networking/device/registry.rs (100%)
rename {core-new => core}/src/services/networking/mod.rs (100%)
rename {core-new => core}/src/services/networking/protocols/file_transfer.rs (100%)
rename {core-new => core}/src/services/networking/protocols/messaging.rs (100%)
rename {core-new => core}/src/services/networking/protocols/mod.rs (100%)
rename {core-new => core}/src/services/networking/protocols/pairing/initiator.rs (100%)
rename {core-new => core}/src/services/networking/protocols/pairing/joiner.rs (100%)
rename {core-new => core}/src/services/networking/protocols/pairing/messages.rs (100%)
rename {core-new => core}/src/services/networking/protocols/pairing/mod.rs (100%)
rename {core-new => core}/src/services/networking/protocols/pairing/persistence.rs (100%)
rename {core-new => core}/src/services/networking/protocols/pairing/security.rs (100%)
rename {core-new => core}/src/services/networking/protocols/pairing/types.rs (100%)
rename {core-new => core}/src/services/networking/protocols/registry.rs (100%)
rename {core-new => core}/src/services/networking/utils/identity.rs (100%)
rename {core-new => core}/src/services/networking/utils/logging.rs (100%)
rename {core-new => core}/src/services/networking/utils/mod.rs (100%)
rename {core-new => core}/src/services/sidecar_manager.rs (100%)
rename {core-new => core}/src/services/volume_monitor.rs (100%)
rename {core-new => core}/src/shared/errors.rs (100%)
rename {core-new => core}/src/shared/mod.rs (100%)
rename {core-new => core}/src/shared/types.rs (100%)
rename {core-new => core}/src/shared/utils.rs (100%)
rename {core-new => core}/src/test_framework/mod.rs (100%)
rename {core-new => core}/src/test_framework/runner.rs (100%)
delete mode 100644 core/src/util/abort_on_drop.rs
delete mode 100644 core/src/util/batched_stream.rs
delete mode 100644 core/src/util/debug_initializer.rs
delete mode 100644 core/src/util/infallible_request.rs
delete mode 100644 core/src/util/maybe_undefined.rs
delete mode 100644 core/src/util/mod.rs
delete mode 100644 core/src/util/mpscrr.rs
delete mode 100644 core/src/util/observable.rs
delete mode 100644 core/src/util/unsafe_streamed_query.rs
delete mode 100644 core/src/util/version_manager.rs
delete mode 100644 core/src/volume/actor.rs
rename {core-new => core}/src/volume/classification.rs (100%)
rename {core-new => core}/src/volume/manager.rs (100%)
delete mode 100644 core/src/volume/os.rs
rename {core-new => core}/src/volume/os_detection.rs (100%)
delete mode 100644 core/src/volume/state.rs
delete mode 100644 core/src/volume/volumes.rs
delete mode 100644 core/src/volume/watcher.rs
rename {core-new => core}/tests/copy_action_test.rs (100%)
rename {core-new => core}/tests/copy_progress_test.rs (100%)
rename {core-new => core}/tests/cross_device_copy_test.rs (100%)
rename {core-new => core}/tests/database_migration_test.rs (100%)
rename {core-new => core}/tests/device_pairing_test.rs (100%)
rename {core-new => core}/tests/device_persistence_test.rs (100%)
rename {core-new => core}/tests/event_system_test.rs (100%)
rename {core-new => core}/tests/file_transfer_test.rs (100%)
rename {core-new => core}/tests/helpers/mod.rs (100%)
rename {core-new => core}/tests/helpers/test_volumes.rs (100%)
rename {core-new => core}/tests/indexing_rules_test.rs (100%)
rename {core-new => core}/tests/indexing_test.rs (100%)
rename {core-new => core}/tests/job_pause_resume_test.rs (100%)
rename {core-new => core}/tests/job_registration_test.rs (100%)
rename {core-new => core}/tests/job_shutdown_test.rs (100%)
rename {core-new => core}/tests/library_test.rs (100%)
rename {core-new => core}/tests/volume_tracking_test.rs (100%)
rename {core-new => core}/tests/volume_tracking_with_test_volumes.rs (100%)
rename {core-new => core}/whitepaper/architecture.png (100%)
rename {core-new => core}/whitepaper/comment.cut (100%)
rename {core-new => core}/whitepaper/grok-changes.md (100%)
rename {core-new => core}/whitepaper/grok-critique.md (100%)
rename {core-new => core}/whitepaper/grok-critique2.md (100%)
rename {core-new => core}/whitepaper/proposed-changes-v2.md (100%)
rename {core-new => core}/whitepaper/proposed-changes.md (100%)
rename {core-new => core}/whitepaper/references.bib (100%)
rename {core-new => core}/whitepaper/spacedrive.bbl (100%)
rename {core-new => core}/whitepaper/spacedrive.blg (100%)
rename {core-new => core}/whitepaper/spacedrive.out (100%)
rename {core-new => core}/whitepaper/spacedrive.pdf (100%)
rename {core-new => core}/whitepaper/spacedrive.tex (99%)
delete mode 100644 crates/ai/Cargo.toml
delete mode 100644 crates/ai/README.md
delete mode 100644 crates/ai/src/lib.rs
delete mode 100644 crates/ai/src/old_image_labeler/mod.rs
delete mode 100644 crates/ai/src/old_image_labeler/model/mod.rs
delete mode 100644 crates/ai/src/old_image_labeler/model/yolov8.rs
delete mode 100644 crates/ai/src/old_image_labeler/old_actor.rs
delete mode 100644 crates/ai/src/old_image_labeler/process.rs
delete mode 100644 crates/ai/src/utils/mod.rs
delete mode 100644 crates/file-ext/Cargo.toml
delete mode 100644 crates/file-ext/src/extensions.rs
delete mode 100644 crates/file-ext/src/kind.rs
delete mode 100644 crates/file-ext/src/lib.rs
delete mode 100644 crates/file-ext/src/magic.rs
delete mode 100644 crates/file-ext/src/text.rs
delete mode 100644 crates/old-p2p/Cargo.toml
delete mode 100644 crates/old-p2p/README.md
delete mode 100644 crates/old-p2p/crates/block/Cargo.toml
delete mode 100644 crates/old-p2p/crates/block/src/block.rs
delete mode 100644 crates/old-p2p/crates/block/src/block_size.rs
delete mode 100644 crates/old-p2p/crates/block/src/lib.rs
delete mode 100644 crates/old-p2p/crates/block/src/sb_request.rs
delete mode 100644 crates/old-p2p/crates/proto/Cargo.toml
delete mode 100644 crates/old-p2p/crates/proto/src/lib.rs
delete mode 100644 crates/old-p2p/crates/tunnel/Cargo.toml
delete mode 100644 crates/old-p2p/crates/tunnel/src/lib.rs
delete mode 100644 crates/old-p2p/src/hook.rs
delete mode 100644 crates/old-p2p/src/hooks.rs
delete mode 100644 crates/old-p2p/src/hooks/mdns.rs
delete mode 100644 crates/old-p2p/src/hooks/quic.rs
delete mode 100644 crates/old-p2p/src/hooks/quic/handle.rs
delete mode 100644 crates/old-p2p/src/hooks/quic/transport.rs
delete mode 100644 crates/old-p2p/src/hooks/quic/utils.rs
delete mode 100644 crates/old-p2p/src/identity.rs
delete mode 100644 crates/old-p2p/src/lib.rs
delete mode 100644 crates/old-p2p/src/p2p.rs
delete mode 100644 crates/old-p2p/src/peer.rs
delete mode 100644 crates/old-p2p/src/smart_guards.rs
delete mode 100644 crates/old-p2p/src/stream.rs
delete mode 100644 crates/prisma-cli/Cargo.toml
delete mode 100644 crates/prisma-cli/src/bin/prisma.rs
delete mode 100644 crates/prisma-cli/src/bin/sync.rs
delete mode 100644 crates/prisma/.gitignore
delete mode 100644 crates/prisma/Cargo.toml
delete mode 100644 crates/prisma/src/lib.rs
delete mode 100644 crates/sync-generator/Cargo.toml
delete mode 100644 crates/sync-generator/src/attribute/mod.rs
delete mode 100644 crates/sync-generator/src/attribute/parser.rs
delete mode 100644 crates/sync-generator/src/lib.rs
delete mode 100644 crates/sync-generator/src/model.rs
delete mode 100644 crates/sync-generator/src/sync_data.rs
delete mode 100644 crates/sync/Cargo.toml
delete mode 100644 crates/sync/src/compressed.rs
delete mode 100644 crates/sync/src/crdt.rs
delete mode 100644 crates/sync/src/factory.rs
delete mode 100644 crates/sync/src/lib.rs
delete mode 100644 crates/sync/src/model_traits.rs
rename {core-new => crates}/task-validator/Cargo.toml (100%)
rename {core-new => crates}/task-validator/src/main.rs (100%)
delete mode 100644 crates/utils/Cargo.toml
delete mode 100644 crates/utils/src/db.rs
delete mode 100644 crates/utils/src/error.rs
delete mode 100644 crates/utils/src/lib.rs
rename {core-new/docs => docs}/README.md (100%)
rename {core-new/docs => docs}/benchmarks.md (100%)
rename {core-new/docs => docs/core}/architecture.md (100%)
rename {core-new/docs => docs/core}/cli-multi-instance.md (100%)
rename {core-new/docs => docs/core}/cli.md (100%)
rename {core-new/docs => docs/core}/database.md (100%)
rename {core-new/docs => docs/core}/domain-models.md (100%)
rename {core-new/docs => docs/core}/examples.md (100%)
rename {core-new/docs => docs/core}/indexing.md (100%)
rename {core-new/docs => docs/core}/job-system.md (100%)
rename {core-new/docs => docs/core}/library.md (100%)
rename {core-new/docs => docs/core}/locations.md (100%)
rename {core-new/docs => docs/core}/networking.md (100%)
rename {core-new/docs => docs/core}/pairing.md (100%)
rename {core-new/docs => docs/core}/task-tracking.md (100%)
rename {core-new/docs => docs/core}/testing.md (100%)
rename {core-new/docs => docs/core}/virtual_sidecars.md (100%)
rename {core-new/docs => docs/core}/volume-system.md (100%)
rename {core-new/docs => docs}/design/ACTIONS_REFACTOR.md (100%)
rename {core-new/docs => docs}/design/ACTION_SYSTEM_DESIGN.md (100%)
rename {core-new/docs => docs}/design/AGENT_MANAGER_DESIGN.md (100%)
rename {core-new/docs => docs}/design/API_COMPARISON.md (100%)
rename {core-new/docs => docs}/design/ARCHITECTURE_DECISIONS.md (100%)
rename {core-new/docs => docs}/design/AT_REST_LIBRARY_ENCRYPTION.md (100%)
rename {core-new/docs => docs}/design/BENCHMARKING_SUITE_DESIGN.md (100%)
rename {core-new/docs => docs}/design/CLOSURE_TABLE_INDEXING_PROPOSAL.md (100%)
rename {core-new/docs => docs}/design/CROSS_DEVICE_FILE_TRANSFER_IMPLEMENTATION.md (100%)
rename {core-new/docs => docs}/design/CROSS_PLATFORM_COPY_AND_VOLUME_AWARENESS.md (100%)
rename {core-new/docs => docs}/design/DAEMON_REFACTOR.md (100%)
rename {core-new/docs => docs}/design/DESIGN_CORE_LIFECYCLE.md (100%)
rename {core-new/docs => docs}/design/DESIGN_DEVICE_MANAGEMENT.md (100%)
rename {core-new/docs => docs}/design/DESIGN_FILE_DATA_MODEL.md (100%)
rename {core-new/docs => docs}/design/DESIGN_FILE_DATA_MODEL_VISUAL.md (100%)
rename {core-new/docs => docs}/design/DESIGN_FILE_TYPE_SYSTEM.md (100%)
rename {core-new/docs => docs}/design/DESIGN_LIBRARY_IMPLEMENTATION.md (100%)
rename {core-new/docs => docs}/design/DESIGN_LIBRARY_ORGANIZATION.md (100%)
rename {core-new/docs => docs}/design/DESIGN_LIBRARY_ORGANIZATION_VISUAL.md (100%)
rename {core-new/docs => docs}/design/DESIGN_OPTIMIZED_STORAGE.md (100%)
rename {core-new/docs => docs}/design/DESIGN_VDFS_INTEGRATION.md (100%)
rename {core-new/docs => docs}/design/DEVICE_PAIRING_PROTOCOL.md (100%)
rename {core-new/docs => docs}/design/DOMAIN_MODELS_README.md (100%)
rename {core-new/docs => docs}/design/ENTITY_REFACTOR_DESIGN.md (100%)
rename {core-new/docs => docs}/design/FFMPEG_BUNDLING_DESIGN.md (94%)
rename {core-new/docs => docs}/design/FILE_SHARING_DESIGN.md (100%)
rename {core-new/docs => docs}/design/FUNCTION_BASED_TEST_FRAMEWORK_DESIGN.md (100%)
rename {core-new/docs => docs}/design/IDEA_FOR_BETTER_INDEXING.md (100%)
rename {core-new/docs => docs}/design/IMPLEMENTATION_STATUS.md (100%)
rename {core-new/docs => docs}/design/INDEXER_ANALYSIS.md (100%)
rename {core-new/docs => docs}/design/INDEXER_ANALYSIS_2.md (100%)
rename {core-new/docs => docs}/design/INDEXER_JOB_EXAMPLE.md (100%)
rename {core-new/docs => docs}/design/INDEXER_PROGRESS.md (97%)
rename {core-new/docs => docs}/design/INDEXER_RULES_SYSTEM.md (90%)
rename {core-new/docs => docs}/design/INDEXING_DISCOVERY_OPTIMIZATION_PLAN.md (100%)
rename {core-new/docs => docs}/design/INTEGRATION_SYSTEM_DESIGN.md (100%)
rename {core-new/docs => docs}/design/INTEGRATION_SYSTEM_DESIGN_GEMINI.md (100%)
rename {core-new/docs => docs}/design/IPHONE_AS_VOLUME_DESIGN.md (100%)
rename {core-new/docs => docs}/design/IROH_MIGRATION_DESIGN.md (100%)
rename {core-new/docs => docs}/design/JOB_SYSTEM_DESIGN.md (100%)
rename {core-new/docs => docs}/design/JOB_SYSTEM_MACRO_EXAMPLE.md (100%)
rename {core-new/docs => docs}/design/JOB_SYSTEM_README.md (100%)
rename {core-new/docs => docs}/design/LIBP2P_INTEGRATION_DESIGN.md (100%)
rename {core-new/docs => docs}/design/LIBRARY_LEADERSHIP.md (100%)
rename {core-new/docs => docs}/design/NETWORKING_SYSTEM_DESIGN.md (87%)
rename {core-new/docs => docs}/design/OLD_SPACEDRIVE_ANALYSIS.md (100%)
rename {core-new/docs => docs}/design/OPERATIONS_REFACTOR_PLAN.md (100%)
rename {core-new/docs => docs}/design/PERSISTENT_DEVICE_CONNECTIONS_DESIGN.md (100%)
rename {core-new/docs => docs}/design/REFERENCE_SIDECARS.md (100%)
rename {core-new/docs => docs}/design/REWRITE_PLAN.MD (99%)
rename {core-new/docs => docs}/design/SDPATH_REFACTOR.md (100%)
rename {core-new/docs => docs}/design/SDPATH_REFACTOR_COVERAGE.md (100%)
rename {core-new/docs => docs}/design/SEARCH_DESIGN.md (93%)
rename {core-new/docs => docs}/design/SIMULATION_ENGINE_DESIGN.md (100%)
rename SPACEDRIVE_COMPLETE_OVERVIEW.md => docs/design/SPACEDRIVE_COMPLETE_OVERVIEW.md (89%)
rename {core-new/docs => docs}/design/SPACEDROP_DESIGN.md (100%)
rename {core-new/docs => docs}/design/SPACEDROP_IMPLEMENTATION_PLAN.md (98%)
rename {core-new/docs => docs}/design/STRUCTURE.md (97%)
rename {core-new/docs => docs}/design/SYNC_DESIGN.md (100%)
rename {core-new/docs => docs}/design/SYNC_DESIGN_2025_08_19.md (100%)
rename {core-new/docs => docs}/design/SYNC_FIRST_DRAFT_DESIGN.md (100%)
rename {core-new/docs => docs}/design/SYNC_INTEGRATION_NOTES.md (100%)
rename {core-new/docs => docs}/design/THUMBNAIL_SYSTEM_DESIGN.md (95%)
rename {core-new/docs => docs}/design/UI_DESIGN.md (100%)
rename {core-new/docs => docs}/design/VDFS_MODEL_VISUAL.md (100%)
rename {core-new/docs => docs}/design/VIRTUAL_LOCATIONS_DESIGN.md (100%)
rename {core-new/docs => docs}/design/VIRTUAL_SIDECAR_SYSTEM.md (100%)
rename {core-new/docs => docs}/design/VOLUME_CLASSIFICATION_DESIGN.md (100%)
rename {core-new/docs => docs}/design/VOLUME_TRACKING_IMPLEMENTATION_PLAN.md (100%)
rename {core-new/docs => docs}/design/WATCHER_VDFS_INTEGRATION.md (93%)
rename {core-new/docs => docs}/design/WHITEPAPER_IMPL_ROADMAP.md (100%)
rename {core-new/docs => docs}/design/cli-output-refactor.md (100%)
rename {core-new/docs => docs}/design/frontend_graphql_usage.tsx (100%)
rename {core-new/docs => docs}/design/indexer-scope-upgrade.md (100%)
rename {core-new/docs => docs}/design/landing-page-idea.md (100%)
rename {core-new/docs => docs}/design/networking_implementation_summary.md (100%)
rename {core-new/docs => docs}/history.md (100%)
rename {core-new/docs => docs}/philosophy.md (100%)
rename {core-new/docs => docs}/roadmap.md (100%)
rename {core-new/docs => docs}/whitepaper.md (100%)
delete mode 100644 error.txt
delete mode 100644 interface/.eslintrc.js
delete mode 100644 interface/.gitignore
delete mode 100644 interface/ErrorFallback.tsx
delete mode 100644 interface/RoutingContext.tsx
delete mode 100644 interface/TabsContext.tsx
delete mode 100644 interface/app/$libraryId/404.tsx
delete mode 100644 interface/app/$libraryId/Explorer/Context.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ContextMenu/AssignTagMenuItems.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ContextMenu/ConditionalItem.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ContextMenu/FilePath/CutCopyItems.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ContextMenu/FilePath/Items.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ContextMenu/Object/Items.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ContextMenu/OpenWith.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ContextMenu/SharedItems.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ContextMenu/context.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ContextMenu/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/CopyAsPath.tsx
delete mode 100644 interface/app/$libraryId/Explorer/DismissibleNotice.tsx
delete mode 100644 interface/app/$libraryId/Explorer/DragOverlay.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ExplorerDraggable.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ExplorerDroppable.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ExplorerPathBar.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ExplorerTagBar.tsx
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/DeleteDialog.tsx
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/ErrorBarrier.tsx
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/Image.tsx
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/LayeredFileIcon.tsx
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/Original.tsx
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/RenameTextBox.tsx
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/Thumb.module.scss
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/Thumb.tsx
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/useFrame.tsx
delete mode 100644 interface/app/$libraryId/Explorer/FilePath/utils.ts
delete mode 100644 interface/app/$libraryId/Explorer/Inspector/FavoriteButton.tsx
delete mode 100644 interface/app/$libraryId/Explorer/Inspector/MediaData.tsx
delete mode 100644 interface/app/$libraryId/Explorer/Inspector/Note.tsx
delete mode 100644 interface/app/$libraryId/Explorer/Inspector/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/Inspector/store.tsx
delete mode 100644 interface/app/$libraryId/Explorer/OptionsPanel/ListView/IconSize.tsx
delete mode 100644 interface/app/$libraryId/Explorer/OptionsPanel/ListView/TextSize.tsx
delete mode 100644 interface/app/$libraryId/Explorer/OptionsPanel/ListView/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/OptionsPanel/ListView/util.ts
delete mode 100644 interface/app/$libraryId/Explorer/OptionsPanel/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/ParentContextMenu.tsx
delete mode 100644 interface/app/$libraryId/Explorer/QuickPreview/Context.tsx
delete mode 100644 interface/app/$libraryId/Explorer/QuickPreview/ImageSlider.tsx
delete mode 100644 interface/app/$libraryId/Explorer/QuickPreview/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/QuickPreview/store.ts
delete mode 100644 interface/app/$libraryId/Explorer/RevealInNativeExplorer.tsx
delete mode 100644 interface/app/$libraryId/Explorer/TopBarOptions.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/Context.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/DragScrollable.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/EmptyNotice.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/Grid/DragSelect/DragSelectable.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/Grid/DragSelect/context.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/Grid/DragSelect/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/Grid/DragSelect/useDragSelectable.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/Grid/DragSelect/useSelectedTargets.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/Grid/DragSelect/util.ts
delete mode 100644 interface/app/$libraryId/Explorer/View/Grid/Item.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/Grid/useKeySelection.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/GridView/Item/Context.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/GridView/Item/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/GridView/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/ListView/Item.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/ListView/TableRow.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/ListView/context.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/ListView/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/ListView/useRanges.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/ListView/useTable.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/MediaView/DateHeader.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/MediaView/Item.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/MediaView/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/MediaView/util.ts
delete mode 100644 interface/app/$libraryId/Explorer/View/RenamableItemText.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/ViewItem.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/useActiveItem.tsx
delete mode 100644 interface/app/$libraryId/Explorer/View/useDragScrollable.tsx
delete mode 100644 interface/app/$libraryId/Explorer/hooks/useExplorerCopyPaste.tsx
delete mode 100644 interface/app/$libraryId/Explorer/index.tsx
delete mode 100644 interface/app/$libraryId/Explorer/store.ts
delete mode 100644 interface/app/$libraryId/Explorer/useExplorer.ts
delete mode 100644 interface/app/$libraryId/Explorer/useExplorerDnd.tsx
delete mode 100644 interface/app/$libraryId/Explorer/useExplorerDraggable.tsx
delete mode 100644 interface/app/$libraryId/Explorer/useExplorerDroppable.tsx
delete mode 100644 interface/app/$libraryId/Explorer/useExplorerItemData.tsx
delete mode 100644 interface/app/$libraryId/Explorer/useExplorerOperatingSystem.tsx
delete mode 100644 interface/app/$libraryId/Explorer/useExplorerPreferences.ts
delete mode 100644 interface/app/$libraryId/Explorer/useKeyRevealFinder.ts
delete mode 100644 interface/app/$libraryId/Explorer/util.ts
delete mode 100644 interface/app/$libraryId/KeyManager/Key.tsx
delete mode 100644 interface/app/$libraryId/KeyManager/List.tsx
delete mode 100644 interface/app/$libraryId/KeyManager/Mounter.tsx
delete mode 100644 interface/app/$libraryId/KeyManager/NotSetup.tsx
delete mode 100644 interface/app/$libraryId/KeyManager/NotUnlocked.tsx
delete mode 100644 interface/app/$libraryId/KeyManager/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/CMDK/CMDK.css
delete mode 100644 interface/app/$libraryId/Layout/CMDK/CMDK.scss
delete mode 100644 interface/app/$libraryId/Layout/CMDK/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/CMDK/pages/CMDKLocations.tsx
delete mode 100644 interface/app/$libraryId/Layout/CMDK/pages/CMDKTags.tsx
delete mode 100644 interface/app/$libraryId/Layout/Context.tsx
delete mode 100644 interface/app/$libraryId/Layout/DndContext.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/DebugPopover.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/JobManager/IsRunningJob.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/JobManager/Job.module.scss
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/JobManager/Job.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/JobManager/JobContainer.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/JobManager/JobGroup.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/JobManager/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/Context.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/FeedbackPopover.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/Footer.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/Icon.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/JobManagerPopover.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/LibrariesDropdown.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/Link.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/Section.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/SeeMore.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/WindowControls.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/SidebarLayout/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/helpers.ts
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Categories/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Debug/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Devices/AddDeviceDialog.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Devices/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Library/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Local/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Locations/ContextMenu.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Locations/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Peers/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/SavedSearches/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Tags/ContextMenu.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Tags/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/sections/Tools/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/Sidebar/store.ts
delete mode 100644 interface/app/$libraryId/Layout/auth.tsx
delete mode 100644 interface/app/$libraryId/Layout/index.tsx
delete mode 100644 interface/app/$libraryId/Layout/store.ts
delete mode 100644 interface/app/$libraryId/PageLayout/Context.tsx
delete mode 100644 interface/app/$libraryId/PageLayout/index.tsx
delete mode 100644 interface/app/$libraryId/Spacedrop/index.tsx
delete mode 100644 interface/app/$libraryId/Spacedrop/toast.tsx
delete mode 100644 interface/app/$libraryId/TopBar/Context.tsx
delete mode 100644 interface/app/$libraryId/TopBar/Layout.tsx
delete mode 100644 interface/app/$libraryId/TopBar/NavigationButtons.tsx
delete mode 100644 interface/app/$libraryId/TopBar/Portal.tsx
delete mode 100644 interface/app/$libraryId/TopBar/TopBarButton.tsx
delete mode 100644 interface/app/$libraryId/TopBar/TopBarMobile.tsx
delete mode 100644 interface/app/$libraryId/TopBar/TopBarOptions.tsx
delete mode 100644 interface/app/$libraryId/TopBar/index.tsx
delete mode 100644 interface/app/$libraryId/debug/dnd.tsx
delete mode 100644 interface/app/$libraryId/ephemeral.tsx
delete mode 100644 interface/app/$libraryId/favorites.tsx
delete mode 100644 interface/app/$libraryId/index.tsx
delete mode 100644 interface/app/$libraryId/labels.tsx
delete mode 100644 interface/app/$libraryId/location/$id.tsx
delete mode 100644 interface/app/$libraryId/location/LocationOptions.tsx
delete mode 100644 interface/app/$libraryId/media.tsx
delete mode 100644 interface/app/$libraryId/network.tsx
delete mode 100644 interface/app/$libraryId/node/$id.tsx
delete mode 100644 interface/app/$libraryId/overview/FileKindStats.tsx
delete mode 100644 interface/app/$libraryId/overview/Layout/HorizontalScroll.tsx
delete mode 100644 interface/app/$libraryId/overview/Layout/Section.tsx
delete mode 100644 interface/app/$libraryId/overview/LibraryStats.tsx
delete mode 100644 interface/app/$libraryId/overview/LocationCard.tsx
delete mode 100644 interface/app/$libraryId/overview/NewCard.tsx
delete mode 100644 interface/app/$libraryId/overview/StatCard.tsx
delete mode 100644 interface/app/$libraryId/overview/StorageBar.tsx
delete mode 100644 interface/app/$libraryId/overview/index.tsx
delete mode 100644 interface/app/$libraryId/peer/$id.tsx
delete mode 100644 interface/app/$libraryId/peer/StarfieldEffect.tsx
delete mode 100644 interface/app/$libraryId/people.tsx
delete mode 100644 interface/app/$libraryId/recents.tsx
delete mode 100644 interface/app/$libraryId/saved-search/$id.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/FilterRegistry.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/components/AppliedFilters.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/components/FilterOptionBoolean.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/components/FilterOptionDateRange.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/components/FilterOptionList.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/factories/createBooleanFilter.ts
delete mode 100644 interface/app/$libraryId/search/Filters/factories/createDateRangeFilter.ts
delete mode 100644 interface/app/$libraryId/search/Filters/factories/createInOrNotInFilter.ts
delete mode 100644 interface/app/$libraryId/search/Filters/factories/createTextMatchFilter.ts
delete mode 100644 interface/app/$libraryId/search/Filters/hooks/useToggleOptionSelected.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/index.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/registry/BooleanFilters.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/registry/DateFilters.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/registry/KindFilter.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/registry/LocationFilter.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/registry/TagsFilter.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/registry/TextFilters.tsx
delete mode 100644 interface/app/$libraryId/search/Filters/store.ts
delete mode 100644 interface/app/$libraryId/search/Filters/typeGuards.ts
delete mode 100644 interface/app/$libraryId/search/FiltersOld.tsx
delete mode 100644 interface/app/$libraryId/search/SearchBar.tsx
delete mode 100644 interface/app/$libraryId/search/SearchOptions.tsx
delete mode 100644 interface/app/$libraryId/search/context.tsx
delete mode 100644 interface/app/$libraryId/search/index.tsx
delete mode 100644 interface/app/$libraryId/search/store.tsx
delete mode 100644 interface/app/$libraryId/search/useSearch.ts
delete mode 100644 interface/app/$libraryId/search/useSearchExplorerQuery.ts
delete mode 100644 interface/app/$libraryId/search/util.tsx
delete mode 100644 interface/app/$libraryId/settings/Layout.tsx
delete mode 100644 interface/app/$libraryId/settings/ModalLayout.tsx
delete mode 100644 interface/app/$libraryId/settings/OverviewLayout.tsx
delete mode 100644 interface/app/$libraryId/settings/Setting.tsx
delete mode 100644 interface/app/$libraryId/settings/Sidebar.tsx
delete mode 100644 interface/app/$libraryId/settings/client/SpacedriveAccount.tsx
delete mode 100644 interface/app/$libraryId/settings/client/account/Profile.tsx
delete mode 100644 interface/app/$libraryId/settings/client/account/ShowPassword.tsx
delete mode 100644 interface/app/$libraryId/settings/client/account/handlers/cookieHandler.ts
delete mode 100644 interface/app/$libraryId/settings/client/account/handlers/windowHandler.ts
delete mode 100644 interface/app/$libraryId/settings/client/account/index.tsx
delete mode 100644 interface/app/$libraryId/settings/client/appearance.tsx
delete mode 100644 interface/app/$libraryId/settings/client/backups.tsx
delete mode 100644 interface/app/$libraryId/settings/client/extensions.tsx
delete mode 100644 interface/app/$libraryId/settings/client/general.tsx
delete mode 100644 interface/app/$libraryId/settings/client/index.ts
delete mode 100644 interface/app/$libraryId/settings/client/keybindings.tsx
delete mode 100644 interface/app/$libraryId/settings/client/network/debug.tsx
delete mode 100644 interface/app/$libraryId/settings/client/network/index.tsx
delete mode 100644 interface/app/$libraryId/settings/client/privacy.tsx
delete mode 100644 interface/app/$libraryId/settings/client/usage.tsx
delete mode 100644 interface/app/$libraryId/settings/index.tsx
delete mode 100644 interface/app/$libraryId/settings/library/contacts.tsx
delete mode 100644 interface/app/$libraryId/settings/library/general.tsx
delete mode 100644 interface/app/$libraryId/settings/library/index.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/$id.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/AddLocationButton.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/AddLocationDialog.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/DeleteDialog.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/RuleButton.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/RuleInput.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/RulesForm.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/IndexerRuleEditor/index.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/ListItem.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/PathInput.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/index.tsx
delete mode 100644 interface/app/$libraryId/settings/library/locations/openDirectoryPickerDialog.ts
delete mode 100644 interface/app/$libraryId/settings/library/saved-searches/index.tsx
delete mode 100644 interface/app/$libraryId/settings/library/security.tsx
delete mode 100644 interface/app/$libraryId/settings/library/sharing.tsx
delete mode 100644 interface/app/$libraryId/settings/library/tags/CreateDialog.tsx
delete mode 100644 interface/app/$libraryId/settings/library/tags/DeleteDialog.tsx
delete mode 100644 interface/app/$libraryId/settings/library/tags/EditForm.tsx
delete mode 100644 interface/app/$libraryId/settings/library/tags/index.tsx
delete mode 100644 interface/app/$libraryId/settings/node/index.tsx
delete mode 100644 interface/app/$libraryId/settings/node/libraries/CreateDialog.tsx
delete mode 100644 interface/app/$libraryId/settings/node/libraries/DeleteDeviceDialog.tsx
delete mode 100644 interface/app/$libraryId/settings/node/libraries/DeleteDialog.tsx
delete mode 100644 interface/app/$libraryId/settings/node/libraries/DeviceItem.tsx
delete mode 100644 interface/app/$libraryId/settings/node/libraries/ListItem.tsx
delete mode 100644 interface/app/$libraryId/settings/node/libraries/index.tsx
delete mode 100644 interface/app/$libraryId/settings/node/p2p.tsx
delete mode 100644 interface/app/$libraryId/settings/resources/about.tsx
delete mode 100644 interface/app/$libraryId/settings/resources/changelog.tsx
delete mode 100644 interface/app/$libraryId/settings/resources/index.tsx
delete mode 100644 interface/app/$libraryId/spaces.tsx
delete mode 100644 interface/app/$libraryId/tag/$id.tsx
delete mode 100644 interface/app/I18n.ts
delete mode 100644 interface/app/RootContext.tsx
delete mode 100644 interface/app/demo.react.tsx
delete mode 100644 interface/app/demo.solid.tsx
delete mode 100644 interface/app/index.tsx
delete mode 100644 interface/app/onboarding/Layout.tsx
delete mode 100644 interface/app/onboarding/Progress.tsx
delete mode 100644 interface/app/onboarding/components.tsx
delete mode 100644 interface/app/onboarding/context.tsx
delete mode 100644 interface/app/onboarding/creating-library.tsx
delete mode 100644 interface/app/onboarding/full-disk.tsx
delete mode 100644 interface/app/onboarding/index.tsx
delete mode 100644 interface/app/onboarding/joining-library.tsx
delete mode 100644 interface/app/onboarding/locations.tsx
delete mode 100644 interface/app/onboarding/login.tsx
delete mode 100644 interface/app/onboarding/new-library.tsx
delete mode 100644 interface/app/onboarding/prerelease.tsx
delete mode 100644 interface/app/onboarding/privacy.tsx
delete mode 100644 interface/app/p2p/index.tsx
delete mode 100644 interface/app/route-schemas.ts
delete mode 100644 interface/app/style.scss
delete mode 100644 interface/components/Accordion.tsx
delete mode 100644 interface/components/AlertDialog.tsx
delete mode 100644 interface/components/AuthCheck.tsx
delete mode 100644 interface/components/AuthRequiredOverlay.tsx
delete mode 100644 interface/components/Authentication.tsx
delete mode 100644 interface/components/Codeblock.tsx
delete mode 100644 interface/components/ColorPicker.tsx
delete mode 100644 interface/components/Devtools.tsx
delete mode 100644 interface/components/DismissibleNotice.tsx
delete mode 100644 interface/components/DragRegion.tsx
delete mode 100644 interface/components/Folder.tsx
delete mode 100644 interface/components/Icon.tsx
delete mode 100644 interface/components/Loader.tsx
delete mode 100644 interface/components/Login.tsx
delete mode 100644 interface/components/LoginButton.tsx
delete mode 100644 interface/components/Menu.tsx
delete mode 100644 interface/components/MultiCheckbox.tsx
delete mode 100644 interface/components/PDFViewer.tsx
delete mode 100644 interface/components/PasswordMeter.tsx
delete mode 100644 interface/components/Register.tsx
delete mode 100644 interface/components/RequestAddDialog.tsx
delete mode 100644 interface/components/ShowPassword.tsx
delete mode 100644 interface/components/Sparkles.tsx
delete mode 100644 interface/components/SubtleButton.tsx
delete mode 100644 interface/components/TextViewer/index.tsx
delete mode 100644 interface/components/TextViewer/one-dark.scss
delete mode 100644 interface/components/TextViewer/one-light.scss
delete mode 100644 interface/components/TextViewer/prism-lazy.ts
delete mode 100644 interface/components/TextViewer/prism.tsx
delete mode 100644 interface/components/TrafficLights.tsx
delete mode 100644 interface/components/TruncatedText.tsx
delete mode 100644 interface/components/index.ts
delete mode 100644 interface/hooks/index.ts
delete mode 100644 interface/hooks/useAccessToken.ts
delete mode 100644 interface/hooks/useCallbackToWatchForm.ts
delete mode 100644 interface/hooks/useCallbackToWatchResize.ts
delete mode 100644 interface/hooks/useClickOutside.ts
delete mode 100644 interface/hooks/useCounter.ts
delete mode 100644 interface/hooks/useDebouncedForm.ts
delete mode 100644 interface/hooks/useDeeplinkEventHandler.ts
delete mode 100644 interface/hooks/useDismissibleNoticeStore.tsx
delete mode 100644 interface/hooks/useDragAndDropState.ts
delete mode 100644 interface/hooks/useFileDropEventHandler.ts
delete mode 100644 interface/hooks/useFocusState.tsx
delete mode 100644 interface/hooks/useHomeDir.ts
delete mode 100644 interface/hooks/useInputState.tsx
delete mode 100644 interface/hooks/useIsDark.ts
delete mode 100644 interface/hooks/useIsLocationIndexing.ts
delete mode 100644 interface/hooks/useIsTextTruncated.ts
delete mode 100644 interface/hooks/useKeyDeleteFile.tsx
delete mode 100644 interface/hooks/useKeyMatcher.ts
delete mode 100644 interface/hooks/useKeybind.ts
delete mode 100644 interface/hooks/useKeybindEventHandler.ts
delete mode 100644 interface/hooks/useKeybindFactory.ts
delete mode 100644 interface/hooks/useLocale.ts
delete mode 100644 interface/hooks/useMouseItemResize.ts
delete mode 100644 interface/hooks/useOperatingSystem.ts
delete mode 100644 interface/hooks/usePrefersReducedMotion.ts
delete mode 100644 interface/hooks/useQuickRescan.ts
delete mode 100644 interface/hooks/useRandomInterval.ts
delete mode 100644 interface/hooks/useRedirectToNewLocation.ts
delete mode 100644 interface/hooks/useRouteTitle.ts
delete mode 100644 interface/hooks/useScrolled.tsx
delete mode 100644 interface/hooks/useShortcut.ts
delete mode 100644 interface/hooks/useShowControls.ts
delete mode 100644 interface/hooks/useTheme.ts
delete mode 100644 interface/hooks/useWindowSize.ts
delete mode 100644 interface/hooks/useWindowState.tsx
delete mode 100644 interface/hooks/useZodParams.ts
delete mode 100644 interface/hooks/useZodRouteParams.ts
delete mode 100644 interface/hooks/useZodSearchParams.ts
delete mode 100644 interface/index.tsx
delete mode 100644 interface/locales/README.md
delete mode 100644 interface/locales/ar/common.json
delete mode 100644 interface/locales/be/common.json
delete mode 100644 interface/locales/cs/common.json
delete mode 100644 interface/locales/de/common.json
delete mode 100644 interface/locales/en/common.json
delete mode 100644 interface/locales/es/common.json
delete mode 100644 interface/locales/fr/common.json
delete mode 100644 interface/locales/i18nnext.d.ts
delete mode 100644 interface/locales/it/common.json
delete mode 100644 interface/locales/ja/common.json
delete mode 100644 interface/locales/nl/common.json
delete mode 100644 interface/locales/ru/common.json
delete mode 100644 interface/locales/tr/common.json
delete mode 100644 interface/locales/uk/common.json
delete mode 100644 interface/locales/zh-CN/common.json
delete mode 100644 interface/locales/zh-TW/common.json
delete mode 100644 interface/package.json
delete mode 100644 interface/tailwind.config.js
delete mode 100644 interface/tsconfig.json
delete mode 100644 interface/types/declarations.d.ts
delete mode 100644 interface/util/Platform.tsx
delete mode 100644 interface/util/events.ts
delete mode 100644 interface/util/hardware.ts
delete mode 100644 interface/util/index.tsx
delete mode 100644 interface/util/keybinds.ts
delete mode 100644 interface/util/pdfViewer.tsx
delete mode 100644 interface/util/useTraceUpdate.tsx
delete mode 100644 interface/util/uuid.ts
diff --git a/.prettierrc.js b/.prettierrc.js
deleted file mode 100644
index f57e12019..000000000
--- a/.prettierrc.js
+++ /dev/null
@@ -1,31 +0,0 @@
-const path = require('node:path');
-
-/**
- * {@type require('prettier').Config}
- */
-module.exports = {
- useTabs: true,
- printWidth: 100,
- singleQuote: true,
- trailingComma: 'none',
- bracketSameLine: false,
- semi: true,
- quoteProps: 'consistent',
- importOrder: [
- // external packages
- '',
- // spacedrive packages
- '^@sd/(interface|client|ui)(/.*)?$',
- // internal packages
- '^@/',
- '^~/',
- '',
- // relative
- '^[../]',
- '^[./]'
- ],
- importOrderParserPlugins: ['typescript', 'jsx', 'decorators'],
- importOrderTypeScriptVersion: '5.0.0',
- tailwindConfig: path.resolve(path.join(__dirname, 'packages/ui/tailwind.config.js')),
- plugins: ['@ianvs/prettier-plugin-sort-imports', 'prettier-plugin-tailwindcss']
-};
diff --git a/core-new/.tasks/ACT-000-action-system.md b/.tasks/ACT-000-action-system.md
similarity index 100%
rename from core-new/.tasks/ACT-000-action-system.md
rename to .tasks/ACT-000-action-system.md
diff --git a/core-new/.tasks/ACT-001-action-manager-registry.md b/.tasks/ACT-001-action-manager-registry.md
similarity index 100%
rename from core-new/.tasks/ACT-001-action-manager-registry.md
rename to .tasks/ACT-001-action-manager-registry.md
diff --git a/core-new/.tasks/AI-000-ai-epic.md b/.tasks/AI-000-ai-epic.md
similarity index 100%
rename from core-new/.tasks/AI-000-ai-epic.md
rename to .tasks/AI-000-ai-epic.md
diff --git a/core-new/.tasks/AI-001-ai-agent.md b/.tasks/AI-001-ai-agent.md
similarity index 100%
rename from core-new/.tasks/AI-001-ai-agent.md
rename to .tasks/AI-001-ai-agent.md
diff --git a/core-new/.tasks/AI-002-create-finetuning-dataset.md b/.tasks/AI-002-create-finetuning-dataset.md
similarity index 100%
rename from core-new/.tasks/AI-002-create-finetuning-dataset.md
rename to .tasks/AI-002-create-finetuning-dataset.md
diff --git a/core-new/.tasks/CLI-000-command-line-interface.md b/.tasks/CLI-000-command-line-interface.md
similarity index 100%
rename from core-new/.tasks/CLI-000-command-line-interface.md
rename to .tasks/CLI-000-command-line-interface.md
diff --git a/core-new/.tasks/CLOUD-000-cloud-as-a-peer.md b/.tasks/CLOUD-000-cloud-as-a-peer.md
similarity index 100%
rename from core-new/.tasks/CLOUD-000-cloud-as-a-peer.md
rename to .tasks/CLOUD-000-cloud-as-a-peer.md
diff --git a/core-new/.tasks/CLOUD-001-design-cloud-core-infra.md b/.tasks/CLOUD-001-design-cloud-core-infra.md
similarity index 63%
rename from core-new/.tasks/CLOUD-001-design-cloud-core-infra.md
rename to .tasks/CLOUD-001-design-cloud-core-infra.md
index 1ca33c95d..d159add50 100644
--- a/core-new/.tasks/CLOUD-001-design-cloud-core-infra.md
+++ b/.tasks/CLOUD-001-design-cloud-core-infra.md
@@ -11,7 +11,7 @@ whitepaper: Section 5.1
## Description
-Design the infrastructure for provisioning and running isolated `sd-core-new` instances for users in a cloud environment. This involves creating a scalable and secure architecture, likely using containerization and orchestration technologies like Kubernetes.
+Design the infrastructure for provisioning and running isolated `sd-core` instances for users in a cloud environment. This involves creating a scalable and secure architecture, likely using containerization and orchestration technologies like Kubernetes.
## Implementation Steps
@@ -21,6 +21,7 @@ Design the infrastructure for provisioning and running isolated `sd-core-new` in
4. Specify the security and networking policies for the cloud environment.
## Acceptance Criteria
-- [ ] A detailed architecture document is created.
-- [ ] The design addresses scalability, security, and cost-effectiveness.
-- [ ] The design is approved and ready for implementation.
+
+- [ ] A detailed architecture document is created.
+- [ ] The design addresses scalability, security, and cost-effectiveness.
+- [ ] The design is approved and ready for implementation.
diff --git a/core-new/.tasks/CLOUD-002-relay-server.md b/.tasks/CLOUD-002-relay-server.md
similarity index 100%
rename from core-new/.tasks/CLOUD-002-relay-server.md
rename to .tasks/CLOUD-002-relay-server.md
diff --git a/core-new/.tasks/CLOUD-003-cloud-volume.md b/.tasks/CLOUD-003-cloud-volume.md
similarity index 100%
rename from core-new/.tasks/CLOUD-003-cloud-volume.md
rename to .tasks/CLOUD-003-cloud-volume.md
diff --git a/core-new/.tasks/CORE-000-vdfs-core.md b/.tasks/CORE-000-vdfs-core.md
similarity index 100%
rename from core-new/.tasks/CORE-000-vdfs-core.md
rename to .tasks/CORE-000-vdfs-core.md
diff --git a/core-new/.tasks/CORE-001-entry-centric-model.md b/.tasks/CORE-001-entry-centric-model.md
similarity index 100%
rename from core-new/.tasks/CORE-001-entry-centric-model.md
rename to .tasks/CORE-001-entry-centric-model.md
diff --git a/core-new/.tasks/CORE-002-sdpath-addressing.md b/.tasks/CORE-002-sdpath-addressing.md
similarity index 100%
rename from core-new/.tasks/CORE-002-sdpath-addressing.md
rename to .tasks/CORE-002-sdpath-addressing.md
diff --git a/core-new/.tasks/CORE-003-content-identity.md b/.tasks/CORE-003-content-identity.md
similarity index 100%
rename from core-new/.tasks/CORE-003-content-identity.md
rename to .tasks/CORE-003-content-identity.md
diff --git a/core-new/.tasks/CORE-004-closure-table.md b/.tasks/CORE-004-closure-table.md
similarity index 100%
rename from core-new/.tasks/CORE-004-closure-table.md
rename to .tasks/CORE-004-closure-table.md
diff --git a/core-new/.tasks/CORE-005-file-type-system.md b/.tasks/CORE-005-file-type-system.md
similarity index 100%
rename from core-new/.tasks/CORE-005-file-type-system.md
rename to .tasks/CORE-005-file-type-system.md
diff --git a/core-new/.tasks/CORE-006-semantic-tagging-architecture.md b/.tasks/CORE-006-semantic-tagging-architecture.md
similarity index 100%
rename from core-new/.tasks/CORE-006-semantic-tagging-architecture.md
rename to .tasks/CORE-006-semantic-tagging-architecture.md
diff --git a/core-new/.tasks/CORE-007-quantum-state-for-on-demand-state-computation.md b/.tasks/CORE-007-quantum-state-for-on-demand-state-computation.md
similarity index 100%
rename from core-new/.tasks/CORE-007-quantum-state-for-on-demand-state-computation.md
rename to .tasks/CORE-007-quantum-state-for-on-demand-state-computation.md
diff --git a/core-new/.tasks/CORE-008-virtual-sidecar-system.md b/.tasks/CORE-008-virtual-sidecar-system.md
similarity index 96%
rename from core-new/.tasks/CORE-008-virtual-sidecar-system.md
rename to .tasks/CORE-008-virtual-sidecar-system.md
index 9249810a2..79b269057 100644
--- a/core-new/.tasks/CORE-008-virtual-sidecar-system.md
+++ b/.tasks/CORE-008-virtual-sidecar-system.md
@@ -1,8 +1,8 @@
---
id: CORE-008
title: Virtual Sidecar System
-status: To Do
-assignee: unassigned
+status: In Progress
+assignee: james
parent: CORE-000
priority: High
tags: [core, vdfs, sidecars, derivatives]
diff --git a/core-new/.tasks/CORE-009-user-managed-collections.md b/.tasks/CORE-009-user-managed-collections.md
similarity index 100%
rename from core-new/.tasks/CORE-009-user-managed-collections.md
rename to .tasks/CORE-009-user-managed-collections.md
diff --git a/core-new/.tasks/CORE-010-file-ingestion-workflow.md b/.tasks/CORE-010-file-ingestion-workflow.md
similarity index 100%
rename from core-new/.tasks/CORE-010-file-ingestion-workflow.md
rename to .tasks/CORE-010-file-ingestion-workflow.md
diff --git a/core-new/.tasks/DEV-000-development-validation.md b/.tasks/DEV-000-development-validation.md
similarity index 100%
rename from core-new/.tasks/DEV-000-development-validation.md
rename to .tasks/DEV-000-development-validation.md
diff --git a/core-new/.tasks/DEV-001-multi-process-test-framework.md b/.tasks/DEV-001-multi-process-test-framework.md
similarity index 100%
rename from core-new/.tasks/DEV-001-multi-process-test-framework.md
rename to .tasks/DEV-001-multi-process-test-framework.md
diff --git a/core-new/.tasks/FILE-000-file-operations.md b/.tasks/FILE-000-file-operations.md
similarity index 100%
rename from core-new/.tasks/FILE-000-file-operations.md
rename to .tasks/FILE-000-file-operations.md
diff --git a/core-new/.tasks/FILE-001-file-copy-job.md b/.tasks/FILE-001-file-copy-job.md
similarity index 100%
rename from core-new/.tasks/FILE-001-file-copy-job.md
rename to .tasks/FILE-001-file-copy-job.md
diff --git a/core-new/.tasks/FILE-002-file-deletion-job.md b/.tasks/FILE-002-file-deletion-job.md
similarity index 100%
rename from core-new/.tasks/FILE-002-file-deletion-job.md
rename to .tasks/FILE-002-file-deletion-job.md
diff --git a/core-new/.tasks/INDEX-000-indexing-file-management.md b/.tasks/INDEX-000-indexing-file-management.md
similarity index 100%
rename from core-new/.tasks/INDEX-000-indexing-file-management.md
rename to .tasks/INDEX-000-indexing-file-management.md
diff --git a/core-new/.tasks/INDEX-001-location-watcher-service.md b/.tasks/INDEX-001-location-watcher-service.md
similarity index 100%
rename from core-new/.tasks/INDEX-001-location-watcher-service.md
rename to .tasks/INDEX-001-location-watcher-service.md
diff --git a/core-new/.tasks/INDEX-002-stale-file-detection-algorithm.md b/.tasks/INDEX-002-stale-file-detection-algorithm.md
similarity index 100%
rename from core-new/.tasks/INDEX-002-stale-file-detection-algorithm.md
rename to .tasks/INDEX-002-stale-file-detection-algorithm.md
diff --git a/core-new/.tasks/JOB-000-job-system.md b/.tasks/JOB-000-job-system.md
similarity index 100%
rename from core-new/.tasks/JOB-000-job-system.md
rename to .tasks/JOB-000-job-system.md
diff --git a/core-new/.tasks/JOB-001-job-manager.md b/.tasks/JOB-001-job-manager.md
similarity index 100%
rename from core-new/.tasks/JOB-001-job-manager.md
rename to .tasks/JOB-001-job-manager.md
diff --git a/core-new/.tasks/JOB-002-job-logging.md b/.tasks/JOB-002-job-logging.md
similarity index 100%
rename from core-new/.tasks/JOB-002-job-logging.md
rename to .tasks/JOB-002-job-logging.md
diff --git a/core-new/.tasks/LOC-000-location-operations.md b/.tasks/LOC-000-location-operations.md
similarity index 100%
rename from core-new/.tasks/LOC-000-location-operations.md
rename to .tasks/LOC-000-location-operations.md
diff --git a/core-new/.tasks/LOC-001-location-management-actions.md b/.tasks/LOC-001-location-management-actions.md
similarity index 100%
rename from core-new/.tasks/LOC-001-location-management-actions.md
rename to .tasks/LOC-001-location-management-actions.md
diff --git a/core-new/.tasks/LOC-005-virtual-locations-via-pure-hierarchical-model.md b/.tasks/LOC-005-virtual-locations-via-pure-hierarchical-model.md
similarity index 100%
rename from core-new/.tasks/LOC-005-virtual-locations-via-pure-hierarchical-model.md
rename to .tasks/LOC-005-virtual-locations-via-pure-hierarchical-model.md
diff --git a/core-new/.tasks/LSYNC-000-library-sync.md b/.tasks/LSYNC-000-library-sync.md
similarity index 100%
rename from core-new/.tasks/LSYNC-000-library-sync.md
rename to .tasks/LSYNC-000-library-sync.md
diff --git a/core-new/.tasks/LSYNC-001-design-library-sync-protocol.md b/.tasks/LSYNC-001-design-library-sync-protocol.md
similarity index 100%
rename from core-new/.tasks/LSYNC-001-design-library-sync-protocol.md
rename to .tasks/LSYNC-001-design-library-sync-protocol.md
diff --git a/core-new/.tasks/LSYNC-002-metadata-sync.md b/.tasks/LSYNC-002-metadata-sync.md
similarity index 100%
rename from core-new/.tasks/LSYNC-002-metadata-sync.md
rename to .tasks/LSYNC-002-metadata-sync.md
diff --git a/core-new/.tasks/LSYNC-003-file-op-sync.md b/.tasks/LSYNC-003-file-op-sync.md
similarity index 100%
rename from core-new/.tasks/LSYNC-003-file-op-sync.md
rename to .tasks/LSYNC-003-file-op-sync.md
diff --git a/core-new/.tasks/LSYNC-004-sync-relationship-database-schema.md b/.tasks/LSYNC-004-sync-relationship-database-schema.md
similarity index 100%
rename from core-new/.tasks/LSYNC-004-sync-relationship-database-schema.md
rename to .tasks/LSYNC-004-sync-relationship-database-schema.md
diff --git a/core-new/.tasks/NET-000-networking.md b/.tasks/NET-000-networking.md
similarity index 100%
rename from core-new/.tasks/NET-000-networking.md
rename to .tasks/NET-000-networking.md
diff --git a/core-new/.tasks/NET-001-iroh-p2p-stack.md b/.tasks/NET-001-iroh-p2p-stack.md
similarity index 100%
rename from core-new/.tasks/NET-001-iroh-p2p-stack.md
rename to .tasks/NET-001-iroh-p2p-stack.md
diff --git a/core-new/.tasks/NET-002-device-pairing.md b/.tasks/NET-002-device-pairing.md
similarity index 100%
rename from core-new/.tasks/NET-002-device-pairing.md
rename to .tasks/NET-002-device-pairing.md
diff --git a/core-new/.tasks/NET-003-spacedrop-protocol.md b/.tasks/NET-003-spacedrop-protocol.md
similarity index 100%
rename from core-new/.tasks/NET-003-spacedrop-protocol.md
rename to .tasks/NET-003-spacedrop-protocol.md
diff --git a/core-new/.tasks/PLUG-000-wasm-plugin-system.md b/.tasks/PLUG-000-wasm-plugin-system.md
similarity index 100%
rename from core-new/.tasks/PLUG-000-wasm-plugin-system.md
rename to .tasks/PLUG-000-wasm-plugin-system.md
diff --git a/core-new/.tasks/PLUG-001-integrate-wasm-runtime.md b/.tasks/PLUG-001-integrate-wasm-runtime.md
similarity index 100%
rename from core-new/.tasks/PLUG-001-integrate-wasm-runtime.md
rename to .tasks/PLUG-001-integrate-wasm-runtime.md
diff --git a/core-new/.tasks/PLUG-002-define-vdfs-plugin-api.md b/.tasks/PLUG-002-define-vdfs-plugin-api.md
similarity index 100%
rename from core-new/.tasks/PLUG-002-define-vdfs-plugin-api.md
rename to .tasks/PLUG-002-define-vdfs-plugin-api.md
diff --git a/core-new/.tasks/PLUG-003-develop-twitter-agent-poc.md b/.tasks/PLUG-003-develop-twitter-agent-poc.md
similarity index 100%
rename from core-new/.tasks/PLUG-003-develop-twitter-agent-poc.md
rename to .tasks/PLUG-003-develop-twitter-agent-poc.md
diff --git a/core-new/.tasks/RES-000-resource-management.md b/.tasks/RES-000-resource-management.md
similarity index 100%
rename from core-new/.tasks/RES-000-resource-management.md
rename to .tasks/RES-000-resource-management.md
diff --git a/core-new/.tasks/RES-001-adaptive-throttling.md b/.tasks/RES-001-adaptive-throttling.md
similarity index 100%
rename from core-new/.tasks/RES-001-adaptive-throttling.md
rename to .tasks/RES-001-adaptive-throttling.md
diff --git a/core-new/.tasks/SEARCH-000-temporal-semantic-search.md b/.tasks/SEARCH-000-temporal-semantic-search.md
similarity index 100%
rename from core-new/.tasks/SEARCH-000-temporal-semantic-search.md
rename to .tasks/SEARCH-000-temporal-semantic-search.md
diff --git a/core-new/.tasks/SEARCH-001-async-searchjob.md b/.tasks/SEARCH-001-async-searchjob.md
similarity index 100%
rename from core-new/.tasks/SEARCH-001-async-searchjob.md
rename to .tasks/SEARCH-001-async-searchjob.md
diff --git a/core-new/.tasks/SEARCH-002-two-stage-fts-semantic-reranking.md b/.tasks/SEARCH-002-two-stage-fts-semantic-reranking.md
similarity index 100%
rename from core-new/.tasks/SEARCH-002-two-stage-fts-semantic-reranking.md
rename to .tasks/SEARCH-002-two-stage-fts-semantic-reranking.md
diff --git a/core-new/.tasks/SEARCH-003-unified-vector-repositories.md b/.tasks/SEARCH-003-unified-vector-repositories.md
similarity index 100%
rename from core-new/.tasks/SEARCH-003-unified-vector-repositories.md
rename to .tasks/SEARCH-003-unified-vector-repositories.md
diff --git a/core-new/.tasks/SEC-000-security-and-privacy.md b/.tasks/SEC-000-security-and-privacy.md
similarity index 100%
rename from core-new/.tasks/SEC-000-security-and-privacy.md
rename to .tasks/SEC-000-security-and-privacy.md
diff --git a/core-new/.tasks/SEC-002-database-encryption.md b/.tasks/SEC-002-database-encryption.md
similarity index 100%
rename from core-new/.tasks/SEC-002-database-encryption.md
rename to .tasks/SEC-002-database-encryption.md
diff --git a/core-new/.tasks/SEC-003-cryptographic-audit-log.md b/.tasks/SEC-003-cryptographic-audit-log.md
similarity index 100%
rename from core-new/.tasks/SEC-003-cryptographic-audit-log.md
rename to .tasks/SEC-003-cryptographic-audit-log.md
diff --git a/core-new/.tasks/SEC-004-rbac-system.md b/.tasks/SEC-004-rbac-system.md
similarity index 100%
rename from core-new/.tasks/SEC-004-rbac-system.md
rename to .tasks/SEC-004-rbac-system.md
diff --git a/core-new/.tasks/SEC-005-secure-credential-vault.md b/.tasks/SEC-005-secure-credential-vault.md
similarity index 100%
rename from core-new/.tasks/SEC-005-secure-credential-vault.md
rename to .tasks/SEC-005-secure-credential-vault.md
diff --git a/core-new/.tasks/SEC-006-certificate-pinning.md b/.tasks/SEC-006-certificate-pinning.md
similarity index 100%
rename from core-new/.tasks/SEC-006-certificate-pinning.md
rename to .tasks/SEC-006-certificate-pinning.md
diff --git a/core-new/.tasks/SEC-007-per-library-encryption-policies-for-public-sharing.md b/.tasks/SEC-007-per-library-encryption-policies-for-public-sharing.md
similarity index 100%
rename from core-new/.tasks/SEC-007-per-library-encryption-policies-for-public-sharing.md
rename to .tasks/SEC-007-per-library-encryption-policies-for-public-sharing.md
diff --git a/core-new/.tasks/VOL-000-volume-operations.md b/.tasks/VOL-000-volume-operations.md
similarity index 100%
rename from core-new/.tasks/VOL-000-volume-operations.md
rename to .tasks/VOL-000-volume-operations.md
diff --git a/core-new/.tasks/VOL-001-volume-physicalclass-and-location-logicalclass.md b/.tasks/VOL-001-volume-physicalclass-and-location-logicalclass.md
similarity index 100%
rename from core-new/.tasks/VOL-001-volume-physicalclass-and-location-logicalclass.md
rename to .tasks/VOL-001-volume-physicalclass-and-location-logicalclass.md
diff --git a/core-new/.tasks/VOL-002-automatic-volume-classification.md b/.tasks/VOL-002-automatic-volume-classification.md
similarity index 100%
rename from core-new/.tasks/VOL-002-automatic-volume-classification.md
rename to .tasks/VOL-002-automatic-volume-classification.md
diff --git a/core-new/.tasks/VOL-003-intelligent-storage-tiering-warning-system.md b/.tasks/VOL-003-intelligent-storage-tiering-warning-system.md
similarity index 100%
rename from core-new/.tasks/VOL-003-intelligent-storage-tiering-warning-system.md
rename to .tasks/VOL-003-intelligent-storage-tiering-warning-system.md
diff --git a/core-new/.tasks/VOL-004-remote-volume-indexing-with-opendal.md b/.tasks/VOL-004-remote-volume-indexing-with-opendal.md
similarity index 100%
rename from core-new/.tasks/VOL-004-remote-volume-indexing-with-opendal.md
rename to .tasks/VOL-004-remote-volume-indexing-with-opendal.md
diff --git a/core-new/.tasks/VOL-005-treat-connected-iphone-as-a-virtual-volume-for-direct-import.md b/.tasks/VOL-005-treat-connected-iphone-as-a-virtual-volume-for-direct-import.md
similarity index 100%
rename from core-new/.tasks/VOL-005-treat-connected-iphone-as-a-virtual-volume-for-direct-import.md
rename to .tasks/VOL-005-treat-connected-iphone-as-a-virtual-volume-for-direct-import.md
diff --git a/core-new/.tasks/VSS-003-reference-sidecars-for-live-photo-support.md b/.tasks/VSS-003-reference-sidecars-for-live-photo-support.md
similarity index 100%
rename from core-new/.tasks/VSS-003-reference-sidecars-for-live-photo-support.md
rename to .tasks/VSS-003-reference-sidecars-for-live-photo-support.md
diff --git a/core-new/.tasks/task.schema.json b/.tasks/task.schema.json
similarity index 100%
rename from core-new/.tasks/task.schema.json
rename to .tasks/task.schema.json
diff --git a/README.md b/README.md
index c986b5f21..259bb00bb 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ What started as an ambitious vision became an engineering lesson. Now we're ship
> **The Revolution**
->
+>
> Copy files between your iPhone and MacBook as easily as moving between folders. Search across all your devices with a single query. Organize photos that live anywhere. **Device boundaries disappear.**
@@ -50,9 +50,9 @@ What started as an ambitious vision became an engineering lesson. Now we're ship
## The Vision Realized
-**Copy iPhone video to MacBook storage?** Done.
-**Search across all devices instantly?** Built-in.
-**Organize files that live everywhere?** Native.
+**Copy iPhone video to MacBook storage?** Done.
+**Search across all devices instantly?** Built-in.
+**Organize files that live everywhere?** Native.
**Keep it private and lightning fast?** Always.
The original Spacedrive captured imaginations with a bold promise: the **Virtual Distributed File System**. Manage all your files across all your devices as if they were one giant drive. We delivered impressive file management, but the revolutionary cross-device magic remained just out of reach.
@@ -64,21 +64,25 @@ The original Spacedrive captured imaginations with a bold promise: the **Virtual
Your files are scattered across devices, cloud services, and external drives. Traditional file managers trap you in local boundaries. Spacedrive makes those boundaries disappear:
**🌐 Universal File Access**
+
- Browse files on any device from any device
- External drives, cloud storage, remote servers - all unified
- Offline files show up with cached metadata
**⚡ Lightning Search**
+
- Find files across all locations with a single search
- Content search inside documents, PDFs, and media
- AI-powered semantic search: "find sunset photos from vacation"
**🔄 Seamless Operations**
+
- Copy, move, and organize files between any devices
- Drag and drop across device boundaries
- Batch operations on distributed collections
**🔒 Privacy First**
+
- Your data stays on your devices
- Optional cloud sync, never required
- End-to-end encryption for all transfers
@@ -88,12 +92,14 @@ Your files are scattered across devices, cloud services, and external drives. Tr
The original Spacedrive got 500,000 installs because the vision was right. Development paused because the execution was flawed:
### The Problems (2022-2024)
+
- **Split personality**: Couldn't copy between different location types
- **Search limitations**: Basic filename matching, not true content discovery
- **Technical debt**: Built on foundations that couldn't scale
- **Feature paralysis**: Perfect became the enemy of good
### The Breakthrough (2024-2025)
+
- **Unified experience**: Every operation works everywhere
- **Real search**: Content indexing, semantic understanding, instant results
- **Modern foundation**: Built for performance and extensibility
@@ -118,6 +124,7 @@ We kept the revolutionary vision. We rebuilt the foundation to deliver it.
```
**Cross-device operations made simple:**
+
- Drag photos from your iPhone to external storage
- Search finds files regardless of which device they're on
- Organize distributed media collections as if they were local
@@ -138,8 +145,9 @@ spacedrive server --host 0.0.0.0 --port 8080
```
**Perfect for:**
+
- **Creators**: Manage media across multiple workstations
-- **Developers**: Sync projects between dev environments
+- **Developers**: Sync projects between dev environments
- **Families**: Shared photo organization across devices
- **Self-hosters**: Private cloud with true file management
@@ -150,6 +158,7 @@ Access your files from any browser, anywhere. Full Spacedrive functionality with
## Architecture: Built to Last
### Self-Contained Libraries
+
```
My Photos.sdlibrary/
├── library.json # Configuration & device registry
@@ -159,11 +168,13 @@ My Photos.sdlibrary/
```
**Portable by design:**
+
- **Backup** = copy the folder
-- **Share** = send the folder
+- **Share** = send the folder
- **Migrate** = move the folder
### Unified Operations
+
No more confusion between "indexed" and "direct" files. Every file operation works the same way:
- **Indexed locations**: Rich metadata, lightning search, smart organization
@@ -171,16 +182,18 @@ No more confusion between "indexed" and "direct" files. Every file operation wor
- **Hybrid mode**: Best of both worlds automatically
### Real Search Engine
+
```
🔍 Search: "sunset photos from vacation"
Results across all devices:
📱 iPhone/Photos/Vacation2024/sunset_beach.jpg
-💾 External/Backup/2024/vacation_sunset.mov
+💾 External/Backup/2024/vacation_sunset.mov
☁️ iCloud/Memories/golden_hour_sunset.heic
```
**Beyond filename matching:**
+
- Full-text content search in documents
- Image recognition and scene detection
- Vector search for semantic queries
@@ -189,24 +202,28 @@ Results across all devices:
## What's Shipping: The VDFS Roadmap
### Q1 2025: Foundation
+
- ✅ **Core rewrite** with unified file system
- ✅ **Working CLI** with daemon architecture
- 🚧 **Desktop app** rebuilt on new foundation
- 🚧 **Real search** with content indexing
-### Q2 2025: Device Communication
+### Q2 2025: Device Communication
+
- 🔄 **P2P discovery** and secure connections
- 🔄 **Cross-device operations** (copy, move, sync)
- 🔄 **Mobile apps** with desktop feature parity
- 🔄 **Web interface** for universal access
### Q3 2025: Intelligence
+
- 🎯 **AI-powered organization** with local models
- 🎯 **Smart collections** and auto-tagging
- 🎯 **Cloud integrations** (iCloud, Google Drive, etc.)
- 🎯 **Advanced media analysis**
### Q4 2025: Ecosystem
+
- 🚀 **Extension system** for community features
- 🚀 **Professional tools** for creators and teams
- 🚀 **Enterprise features** and compliance
@@ -236,6 +253,7 @@ spacedrive job monitor
```
**Working today:**
+
- ✅ Multi-location management
- ✅ Smart indexing with progress tracking
- ✅ Content-aware search
@@ -245,18 +263,21 @@ spacedrive job monitor
## Sustainable Open Source
### Always Free & Open
+
- **Core file management** and VDFS operations
- **Local search** and organization features
- **P2P sync** between your own devices
- **Privacy-first** architecture
### Premium Value-Adds
+
- **Spacedrive Cloud**: Cross-internet sync and backup
- **Advanced AI**: Professional media analysis and organization
- **Team features**: Shared libraries and collaboration
- **Enterprise**: SSO, compliance, and enterprise deployment
### Community First
+
- **Weekly dev streams** showing real progress
- **Open roadmap** with community voting
- **Contributor rewards** and recognition program
@@ -265,20 +286,26 @@ spacedrive job monitor
## Why It Will Work This Time
### Technical Maturity
+
From 500k installs and 34k stars, we learned what users actually need:
+
- **Performance first**: Sub-second search, responsive UI, efficient sync
- **Reliability**: Robust error handling, data integrity, graceful failures
- **Simplicity**: Complex features with simple interfaces
### Market Reality
+
The world has changed since 2022:
+
- **Privacy concerns** have intensified with cloud services
- **AI expectations** for semantic search and smart organization
- **Multi-device life** is now universal, not niche
- **Creator economy** needs professional file management tools
### Execution Discipline
+
No more feature paralysis:
+
- **Ship working features**, enhance over time
- **Measure real usage**, not just code metrics
- **Community feedback** drives priority decisions
@@ -287,18 +314,21 @@ No more feature paralysis:
## Get Involved
### For Users
+
- ⭐ **Star the repo** to follow development
- 💬 **Join Discord** for updates and early access
- 🐛 **Report issues** and request features
- 📖 **Beta testing** as features ship
### For Developers
+
- 🔧 **Contribute code** to the core rewrite
- 📚 **Improve docs** and tutorials
- 🧪 **Write tests** and benchmarks
- 🎨 **Design interfaces** for new features
### For Organizations
+
- 💼 **Early access** to enterprise features
- 🤝 **Partnership** opportunities
- 💰 **Sponsorship** and development funding
@@ -320,12 +350,12 @@ The future of file management isn't about better folder hierarchies or cloud sto
Follow the comeback
- Website ·
- Discord ·
+ Website ·
+ Discord ·
Twitter ·
- Core Development
+ Core Development
The file manager that should exist. Finally being built right.
-
\ No newline at end of file
+
diff --git a/spacedrive-cloud b/apps/cloud
similarity index 100%
rename from spacedrive-cloud
rename to apps/cloud
diff --git a/combine_paths.py b/combine_paths.py
deleted file mode 100755
index 9f8e32b3c..000000000
--- a/combine_paths.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env python3
-import os
-import sys
-import argparse
-from pathlib import Path
-
-def combine_paths(paths, output_file):
- """Combine multiple paths into a single text file."""
- with open(output_file, 'w', encoding='utf-8') as out:
- for path_str in paths:
- path = Path(path_str)
-
- if not path.exists():
- print(f"Warning: {path} does not exist, skipping...")
- continue
-
- out.write(f"\n{'='*80}\n")
- out.write(f"PATH: {path}\n")
- out.write(f"{'='*80}\n\n")
-
- if path.is_file():
- try:
- with open(path, 'r', encoding='utf-8') as f:
- out.write(f.read())
- out.write('\n\n')
- except UnicodeDecodeError:
- out.write(f"[Binary file - skipped]\n\n")
- except Exception as e:
- out.write(f"[Error reading file: {e}]\n\n")
-
- elif path.is_dir():
- for root, dirs, files in os.walk(path):
- # Skip common ignored directories
- dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['node_modules', '__pycache__', 'target', 'dist', 'build']]
-
- for file in files:
- if file.startswith('.'):
- continue
-
- file_path = Path(root) / file
- out.write(f"\n{'-'*60}\n")
- out.write(f"FILE: {file_path}\n")
- out.write(f"{'-'*60}\n\n")
-
- try:
- with open(file_path, 'r', encoding='utf-8') as f:
- out.write(f.read())
- out.write('\n\n')
- except UnicodeDecodeError:
- out.write(f"[Binary file - skipped]\n\n")
- except Exception as e:
- out.write(f"[Error reading file: {e}]\n\n")
-
-def main():
- parser = argparse.ArgumentParser(description='Combine multiple paths into a single text file')
- parser.add_argument('paths', nargs='+', help='Paths to combine')
- parser.add_argument('-o', '--output', help='Output file name (default: foldername.txt based on first path)')
-
- args = parser.parse_args()
-
- if args.output:
- output_file = args.output
- else:
- first_path = Path(args.paths[0])
- folder_name = first_path.name if first_path.is_dir() else first_path.stem
- output_file = f"{folder_name}.txt"
-
- combine_paths(args.paths, output_file)
- print(f"Combined {len(args.paths)} paths into {output_file}")
-
-if __name__ == "__main__":
- main()
\ No newline at end of file
diff --git a/core-new/Cargo.toml b/core-new/Cargo.toml
deleted file mode 100644
index 817954111..000000000
--- a/core-new/Cargo.toml
+++ /dev/null
@@ -1,161 +0,0 @@
-[package]
-name = "sd-core-new"
-version = "0.1.0"
-edition = "2021"
-
-[features]
-default = []
-# FFmpeg support for video thumbnails
-ffmpeg = ["dep:sd-ffmpeg"]
-
-[workspace]
-members = [
- "benchmarks",
- "task-validator",
-]
-
-[dependencies]
-# Async runtime
-tokio = { version = "1.40", features = ["full"] }
-futures = "0.3"
-async-trait = "0.1"
-
-# Database
-sea-orm = { version = "1.1", features = ["sqlx-sqlite", "runtime-tokio-rustls", "macros", "uuid", "with-json", "with-chrono"] }
-sea-orm-migration = { version = "1.1", features = ["runtime-tokio-rustls", "sqlx-sqlite"] }
-sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite"] }
-
-# API (temporarily disabled)
-# axum = "0.7"
-# async-graphql = "7.0"
-# async-graphql-axum = "7.0"
-
-# Serialization
-serde = { version = "1.0", features = ["derive"] }
-serde_json = "1.0"
-toml = "0.8"
-int-enum = "1.1"
-strum = { version = "0.26", features = ["derive"] }
-
-# Error handling
-thiserror = "1.0"
-anyhow = "1.0"
-
-
-# File operations
-notify = "6.1" # File system watching
-blake3 = "1.5" # Content addressing
-sha2 = "0.10" # SHA-256 hashing for CAS IDs
-hex = "0.4" # Hex encoding for volume fingerprints
-
-# Logging
-tracing = "0.1"
-tracing-subscriber = { version = "0.3", features = ["env-filter"] }
-
-# Indexer rules engine
-globset = { version = "0.4", features = ["serde1"] }
-gix-ignore = { version = "0.11", features = ["serde"] }
-futures-concurrency = "7.6"
-
-# Job system dependencies
-rmp = "0.8" # MessagePack core types
-rmp-serde = "1.3" # MessagePack serialization for job state
-inventory = "0.3" # Automatic job registration
-sd-task-system = { path = "../crates/task-system" }
-spacedrive-jobs-derive = { path = "spacedrive-jobs-derive" } # Job derive macros
-
-# Media processing dependencies
-sd-images = { path = "../crates/images" }
-sd-ffmpeg = { path = "../crates/ffmpeg", optional = true }
-webp = "0.3"
-image = "0.25"
-tokio-rustls = "0.26"
-
-# Networking
-# Iroh P2P networking
-iroh = "0.28"
-iroh-net = "0.28"
-iroh-blobs = "0.28"
-iroh-gossip = "0.28"
-
-# Serialization for protocols
-serde_cbor = "0.11"
-
-# Cryptography for signing (backward compatibility)
-ed25519-dalek = "2.1"
-
-# Legacy networking (kept for compatibility during transition)
-mdns-sd = "0.13" # mDNS service discovery (DEPRECATED - use libp2p DHT)
-snow = "0.9" # Noise Protocol encryption (DEPRECATED - use libp2p noise)
-ring = "0.16" # Crypto primitives
-argon2 = "0.5" # Password derivation
-aes-gcm = "0.10" # AES-GCM encryption for secure storage
-async-stream = "0.3" # File streaming
-backoff = "0.4" # Retry logic
-bincode = "2.0.0-rc.3" # Efficient encoding
-# futures-util = "0.3" # WebSocket utilities (disabled for now)
-rustls = { version = "0.23", features = ["aws_lc_rs"] } # TLS implementation (DEPRECATED - use libp2p noise)
-rcgen = "0.11" # Certificate generation (DEPRECATED - use libp2p noise)
-tokio-stream = "0.1" # Async streams
-
-# BIP39 wordlist support
-bip39 = "2.0"
-
-# Additional cryptography
-chacha20poly1305 = "0.10" # Authenticated encryption for chunk-level security
-hkdf = "0.12" # Key derivation function for session keys
-x25519-dalek = "2.0"
-hmac = "0.12"
-
-# Network utilities
-if-watch = "3.0"
-local-ip-address = "0.5"
-# colored already defined above
-
-# Utils
-uuid = { version = "1.11", features = ["v4", "v5", "v7", "serde"] }
-chrono = { version = "0.4", features = ["serde"] }
-once_cell = "1.20"
-dirs = "5.0"
-whoami = "1.5"
-rand = "0.8" # Random number generation for secure delete
-tempfile = "3.14" # Temporary directories for testing
-
-# Secure storage
-keyring = "3.6"
-
-# CLI dependencies
-clap = { version = "4.5", features = ["derive", "env"] }
-comfy-table = "7.1"
-dialoguer = "0.11"
-indicatif = "0.17"
-owo-colors = "4.1"
-supports-color = "3.0"
-console = "0.15"
-colored = "2.1"
-
-[build-dependencies]
-vergen = { version = "8", features = ["git", "gitcl", "cargo"] }
-ratatui = "0.29"
-crossterm = "0.28"
-indicatif = "0.17"
-console = "0.15"
-dialoguer = "0.11"
-colored = "2.1"
-comfy-table = "7.1"
-owo-colors = "4.1"
-supports-color = "3.0"
-
-# Platform specific
-[target.'cfg(unix)'.dependencies]
-libc = "0.2"
-
-[[bin]]
-name = "spacedrive"
-path = "src/bin/cli.rs"
-
-
-
-[dev-dependencies]
-tempfile = "3.14"
-pretty_assertions = "1.4"
diff --git a/core-new/benchmarks/Cargo.toml b/core-new/benchmarks/Cargo.toml
deleted file mode 100644
index 86a11c859..000000000
--- a/core-new/benchmarks/Cargo.toml
+++ /dev/null
@@ -1,39 +0,0 @@
-[package]
-name = "sd-bench"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-anyhow = "1.0"
-serde = { version = "1.0", features = ["derive"] }
-serde_yaml = "0.9"
-serde_json = "1.0"
-clap = { version = "4.5", features = ["derive", "env"] }
-tokio = { version = "1.40", features = ["full"] }
-tracing = "0.1"
-tracing-subscriber = { version = "0.3", features = ["env-filter"] }
-rand = "0.8"
-regex = "1.10"
-walkdir = "2.5"
-indicatif = "0.17"
-humantime-serde = "1.1"
-humantime = "2.1"
-chrono = { version = "0.4", features = ["serde"] }
-dirs = "5.0"
-uuid = { version = "1.11", features = ["v4", "serde"] }
-tempfile = "3.14"
-sd-core-new = { path = ".." }
-serde_with = { version = "3.9", features = ["json"] }
-async-trait = "0.1"
-blake3 = "1.5"
-sysinfo = { version = "0.30", default-features = false, features = ["multithread"] }
-
-[lib]
-name = "sd_bench"
-path = "src/lib.rs"
-
-[[bin]]
-name = "sd-bench"
-path = "src/bin/sd-bench-new.rs"
-
-
diff --git a/core-new/build.rs b/core-new/build.rs
deleted file mode 100644
index 4b32dbe9b..000000000
--- a/core-new/build.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use vergen::EmitBuilder;
-
-fn main() -> Result<(), Box> {
- // Emit the instructions
- EmitBuilder::builder()
- .git_sha(true)
- .git_commit_timestamp()
- .git_branch()
- .cargo_opt_level()
- .cargo_target_triple()
- .emit()?;
- Ok(())
-}
diff --git a/core-new/examples/indexing_showcase.rs b/core-new/examples/indexing_showcase.rs
deleted file mode 100644
index 18a6e0218..000000000
--- a/core-new/examples/indexing_showcase.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-//! Showcase of the production-ready indexer implementation
-//!
-//! This example demonstrates the sophisticated features of our new indexer:
-//! - Multi-phase processing (Discovery → Processing → Content)
-//! - Hardcoded filtering with should_skip_path
-//! - Incremental indexing with inode tracking
-//! - Performance metrics and reporting
-//! - Full resumability with checkpoints
-
-use std::path::Path;
-
-fn main() {
- println!("🚀 Spacedrive Production Indexer Showcase\n");
-
- // Demonstrate the filtering system
- showcase_filtering();
-
- // Show the modular architecture
- showcase_architecture();
-
- // Display sample metrics output
- showcase_metrics();
-}
-
-fn showcase_filtering() {
- println!("📁 Smart Filtering System");
- println!("========================\n");
-
- // Import the actual function from our implementation
- use sd_core_new::operations::indexing::filters::should_skip_path;
-
- let test_paths = vec![
- // Files that should be skipped
- (".DS_Store", true, "macOS system file"),
- ("Thumbs.db", true, "Windows thumbnail cache"),
- ("node_modules", true, "npm packages directory"),
- (".git", true, "Git repository data"),
- ("target", true, "Rust build directory"),
- ("__pycache__", true, "Python cache"),
- (".mypy_cache", true, "Python type checker cache"),
-
- // Files that should NOT be skipped
- ("document.pdf", false, "Regular document"),
- ("photo.jpg", false, "Image file"),
- ("src", false, "Source code directory"),
- (".config", false, "User config directory (allowed)"),
- ("project.rs", false, "Rust source file"),
- ];
-
- println!("Testing path filtering:");
- for (path_str, should_skip, description) in test_paths {
- let path = Path::new(path_str);
- let skipped = should_skip_path(path);
- let result = if skipped == should_skip { "✅" } else { "❌" };
- println!(" {} {:20} -> {:8} ({})",
- result,
- path_str,
- if skipped { "SKIP" } else { "INDEX" },
- description
- );
- }
-
- println!("\n💡 Note: This is where the future IndexerRuleEngine will integrate!");
- println!(" The should_skip_path function has a clear TODO marker for rules system.\n");
-}
-
-fn showcase_architecture() {
- println!("🏗️ Modular Architecture");
- println!("=======================\n");
-
- println!("core-new/src/operations/indexing/");
- println!("├── mod.rs # Module exports and documentation");
- println!("├── job.rs # Main IndexerJob with state machine");
- println!("├── state.rs # Resumable state management");
- println!("├── entry.rs # Entry processing with inode support");
- println!("├── filters.rs # Hardcoded filtering (→ future rules)");
- println!("├── metrics.rs # Performance tracking");
- println!("├── change_detection/ # Incremental indexing");
- println!("│ └── mod.rs # Inode-based change detection");
- println!("└── phases/ # Multi-phase processing");
- println!(" ├── discovery.rs # Directory walking");
- println!(" ├── processing.rs # Database operations");
- println!(" └── content.rs # CAS ID generation\n");
-
- println!("Key Features:");
- println!("✅ Full resumability with checkpoint system");
- println!("✅ Inode tracking for move/rename detection");
- println!("✅ Batch processing (1000 items per batch)");
- println!("✅ Non-critical error collection");
- println!("✅ Path prefix optimization");
- println!("✅ Content deduplication ready\n");
-}
-
-fn showcase_metrics() {
- println!("📊 Performance Metrics");
- println!("=====================\n");
-
- // Show what metrics output looks like
- let sample_output = r#"Indexing completed in 12.5s:
-- Files: 10,234 (818.7/s)
-- Directories: 1,523 (121.8/s)
-- Total size: 2.34 GB (191.23 MB/s)
-- Database writes: 10,234 in 11 batches (avg 930.4 items/batch)
-- Errors: 5 (skipped 1,523 paths)
-- Phase timing: discovery 5.2s, processing 6.1s, content 1.2s"#;
-
- println!("Sample metrics output:");
- println!("{}\n", sample_output);
-
- // Show the indexer progress phases
- println!("Progress Tracking Phases:");
- println!("1️⃣ Discovery: 'Found 245 entries in /Users/demo/Documents'");
- println!("2️⃣ Processing: 'Batch 3/11' (database operations)");
- println!("3️⃣ Content: 'Generating content identities (456/1234)'");
- println!("4️⃣ Finalizing: 'Cleaning up and saving final state'\n");
-
- // Show change detection in action
- println!("🔄 Incremental Indexing Example:");
- println!("First run: Indexed 5,000 files");
- println!("Second run: Detected 3 new, 5 modified, 2 moved files");
- println!(" Only processed 10 files instead of 5,000!");
- println!(" Used inode tracking to detect moves efficiently\n");
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_showcase_runs() {
- // Just verify our showcase compiles and runs
- showcase_filtering();
- showcase_architecture();
- showcase_metrics();
- }
-}
\ No newline at end of file
diff --git a/core-new/src/context.rs b/core-new/src/context.rs
deleted file mode 100644
index bb656e2a0..000000000
--- a/core-new/src/context.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-//! Shared context providing access to core application components.
-
-//! Shared context providing access to core application components.
-
-use crate::{
- config::JobLoggingConfig,
- device::DeviceManager, infrastructure::events::EventBus,
- keys::library_key_manager::LibraryKeyManager, library::LibraryManager,
- infrastructure::actions::manager::ActionManager,
- services::networking::NetworkingService, volume::VolumeManager,
-};
-use std::{path::PathBuf, sync::Arc};
-use tokio::sync::RwLock;
-
-/// Shared context providing access to core application components.
-#[derive(Clone)]
-pub struct CoreContext {
- pub events: Arc,
- pub device_manager: Arc,
- pub library_manager: Arc,
- pub volume_manager: Arc,
- pub library_key_manager: Arc,
- // This is wrapped in an RwLock to allow it to be set after initialization
- pub action_manager: Arc>>>,
- pub networking: Arc>>>,
- // Job logging configuration
- pub job_logging_config: Option,
- pub job_logs_dir: Option,
-}
-
-impl CoreContext {
- /// Create a new context with the given components
- pub fn new(
- events: Arc,
- device_manager: Arc,
- library_manager: Arc,
- volume_manager: Arc,
- library_key_manager: Arc,
- ) -> Self {
- Self {
- events,
- device_manager,
- library_manager,
- volume_manager,
- library_key_manager,
- action_manager: Arc::new(RwLock::new(None)),
- networking: Arc::new(RwLock::new(None)),
- job_logging_config: None,
- job_logs_dir: None,
- }
- }
-
- /// Set job logging configuration
- pub fn set_job_logging(&mut self, config: JobLoggingConfig, logs_dir: PathBuf) {
- self.job_logging_config = Some(config);
- self.job_logs_dir = Some(logs_dir);
- }
-
- /// Helper method for services to get the networking service
- pub async fn get_networking(&self) -> Option> {
- self.networking.read().await.clone()
- }
-
- /// Method for Core to set networking after it's initialized
- pub async fn set_networking(&self, networking: Arc) {
- *self.networking.write().await = Some(networking);
- }
-
- /// Helper method to get the action manager
- pub async fn get_action_manager(&self) -> Option> {
- self.action_manager.read().await.clone()
- }
-
- /// Method for Core to set action manager after it's initialized
- pub async fn set_action_manager(&self, action_manager: Arc) {
- *self.action_manager.write().await = Some(action_manager);
- }
-}
diff --git a/core-new/src/lib.rs b/core-new/src/lib.rs
deleted file mode 100644
index 649adfe4d..000000000
--- a/core-new/src/lib.rs
+++ /dev/null
@@ -1,499 +0,0 @@
-#![allow(warnings)]
-//! Spacedrive Core v2
-//!
-//! A unified, simplified architecture for cross-platform file management.
-
-pub mod config;
-pub mod context;
-pub mod device;
-pub mod domain;
-pub mod file_type;
-pub mod infrastructure;
-pub mod keys;
-pub mod library;
-pub mod location;
-pub mod operations;
-pub mod services;
-pub mod shared;
-pub mod test_framework;
-pub mod volume;
-
-use services::networking::protocols::PairingProtocolHandler;
-use services::networking::utils::logging::NetworkLogger;
-
-// Compatibility module for legacy networking references
-pub mod networking {
- pub use crate::services::networking::*;
-}
-
-use crate::config::AppConfig;
-use crate::context::CoreContext;
-use crate::device::DeviceManager;
-use crate::infrastructure::actions::manager::ActionManager;
-use crate::infrastructure::events::{Event, EventBus};
-use crate::library::LibraryManager;
-use crate::services::Services;
-use crate::volume::{VolumeDetectionConfig, VolumeManager};
-use std::path::PathBuf;
-use std::sync::Arc;
-use tokio::sync::{mpsc, RwLock};
-use tracing::{error, info};
-
-/// Pending pairing request information
-#[derive(Debug, Clone)]
-pub struct PendingPairingRequest {
- pub request_id: uuid::Uuid,
- pub device_id: uuid::Uuid,
- pub device_name: String,
- pub received_at: chrono::DateTime,
-}
-
-/// Spacedrop request message
-#[derive(serde::Serialize, serde::Deserialize)]
-struct SpacedropRequest {
- transfer_id: uuid::Uuid,
- file_path: String,
- sender_name: String,
- message: Option,
- file_size: u64,
-}
-
-// NOTE: SimplePairingUI has been moved to CLI infrastructure
-// See: src/infrastructure/cli/pairing_ui.rs for CLI-specific implementations
-
-/// Bridge between networking events and core events
-pub struct NetworkEventBridge {
- network_events: mpsc::UnboundedReceiver,
- core_events: Arc,
-}
-
-impl NetworkEventBridge {
- pub fn new(
- network_events: mpsc::UnboundedReceiver,
- core_events: Arc,
- ) -> Self {
- Self {
- network_events,
- core_events,
- }
- }
-
- pub async fn run(mut self) {
- while let Some(event) = self.network_events.recv().await {
- if let Some(core_event) = self.translate_event(event) {
- self.core_events.emit(core_event);
- }
- }
- }
-
- fn translate_event(&self, event: networking::NetworkEvent) -> Option {
- match event {
- networking::NetworkEvent::ConnectionEstablished { device_id, .. } => {
- Some(Event::DeviceConnected {
- device_id,
- device_name: "Connected Device".to_string(),
- })
- }
- networking::NetworkEvent::ConnectionLost { device_id, .. } => {
- Some(Event::DeviceDisconnected { device_id })
- }
- networking::NetworkEvent::PairingCompleted {
- device_id,
- device_info,
- } => Some(Event::DeviceConnected {
- device_id,
- device_name: device_info.device_name,
- }),
- _ => None, // Some events don't map to core events
- }
- }
-}
-
-/// The main context for all core operations
-pub struct Core {
- /// Application configuration
- pub config: Arc>,
-
- /// Device manager
- pub device: Arc,
-
- /// Library manager
- pub libraries: Arc,
-
- /// Volume manager
- pub volumes: Arc,
-
- /// Event bus for state changes
- pub events: Arc,
-
- /// Container for high-level services
- pub services: Services,
-
- /// Shared context for core components
- pub context: Arc,
-}
-
-impl Core {
- /// Initialize a new Core instance with default data directory
- pub async fn new() -> Result> {
- let data_dir = crate::config::default_data_dir()?;
- Self::new_with_config(data_dir).await
- }
-
- /// Initialize a new Core instance with custom data directory
- pub async fn new_with_config(data_dir: PathBuf) -> Result> {
- info!("Initializing Spacedrive Core at {:?}", data_dir);
-
- // 1. Load or create app config
- let config = AppConfig::load_or_create(&data_dir)?;
- config.ensure_directories()?;
- let config = Arc::new(RwLock::new(config));
-
- // 2. Initialize device manager
- let device = Arc::new(DeviceManager::init_with_path(&data_dir)?);
- // Set the global device ID for legacy compatibility
- shared::utils::set_current_device_id(device.device_id()?);
-
- // 3. Create event bus
- let events = Arc::new(EventBus::default());
-
- // 4. Initialize volume manager
- let volume_config = VolumeDetectionConfig::default();
- let device_id = device.device_id()?;
- let volumes = Arc::new(VolumeManager::new(device_id, volume_config, events.clone()));
-
- // 5. Initialize volume detection
- // info!("Initializing volume detection...");
- // match volumes.initialize().await {
- // Ok(()) => info!("Volume manager initialized"),
- // Err(e) => error!("Failed to initialize volume manager: {}", e),
- // }
-
- // 6. Initialize library manager with libraries directory
- let libraries_dir = config.read().await.libraries_dir();
- let libraries = Arc::new(LibraryManager::new_with_dir(libraries_dir, events.clone()));
-
- // 7. Initialize library key manager
- let library_key_manager =
- Arc::new(crate::keys::library_key_manager::LibraryKeyManager::new()?);
-
- // 8. Register all job types
- info!("Registering job types...");
- crate::operations::register_all_jobs();
- info!("Job types registered");
-
- // 9. Create the context that will be shared with services
- let mut context_inner = CoreContext::new(
- events.clone(),
- device.clone(),
- libraries.clone(),
- volumes.clone(),
- library_key_manager.clone(),
- );
-
- // Set job logging configuration if enabled
- let app_config = config.read().await;
- if app_config.job_logging.enabled {
- context_inner
- .set_job_logging(app_config.job_logging.clone(), app_config.job_logs_dir());
- }
- drop(app_config);
-
- let context = Arc::new(context_inner);
-
- // 10. Initialize services first, passing them the context
- let services = Services::new(context.clone());
-
- // 11. Auto-load all libraries with context for job manager initialization
- info!("Loading existing libraries...");
- let loaded_libraries: Vec> =
- match libraries.load_all_with_context(context.clone()).await {
- Ok(count) => {
- info!("Loaded {} libraries", count);
- libraries.list().await
- }
- Err(e) => {
- error!("Failed to load libraries: {}", e);
- vec![]
- }
- };
-
- // Initialize sidecar manager for each loaded library
- for library in &loaded_libraries {
- info!("Initializing sidecar manager for library {}", library.id());
- if let Err(e) = services.sidecar_manager.init_library(&library).await {
- error!(
- "Failed to initialize sidecar manager for library {}: {}",
- library.id(),
- e
- );
- } else {
- // Run bootstrap scan
- if let Err(e) = services.sidecar_manager.bootstrap_scan(&library).await {
- error!(
- "Failed to run sidecar bootstrap scan for library {}: {}",
- library.id(),
- e
- );
- }
- }
- }
-
- info!("Starting background services...");
- match services.start_all().await {
- Ok(()) => info!("Background services started"),
- Err(e) => error!("Failed to start services: {}", e),
- }
-
- // 12. Initialize ActionManager and set it in context
- let action_manager = Arc::new(crate::infrastructure::actions::manager::ActionManager::new(
- context.clone(),
- ));
- context.set_action_manager(action_manager).await;
-
- // 13. Emit startup event
- events.emit(Event::CoreStarted);
-
- Ok(Self {
- config,
- device,
- libraries,
- volumes,
- events,
- services,
- context,
- })
- }
-
- /// Get the application configuration
- pub fn config(&self) -> Arc> {
- self.config.clone()
- }
-
- /// Initialize networking using master key
- pub async fn init_networking(&mut self) -> Result<(), Box> {
- self.init_networking_with_logger(Arc::new(networking::SilentLogger))
- .await
- }
-
- /// Initialize networking with custom logger
- pub async fn init_networking_with_logger(
- &mut self,
- logger: Arc,
- ) -> Result<(), Box> {
- logger.info("Initializing networking...").await;
-
- // Initialize networking service through the services container
- let data_dir = self.config.read().await.data_dir.clone();
- self.services
- .init_networking(
- self.device.clone(),
- self.services.library_key_manager.clone(),
- data_dir,
- )
- .await?;
-
- // Start the networking service
- self.services.start_networking().await?;
-
- // Get the networking service for protocol registration
- if let Some(networking_service) = self.services.networking() {
- // Register default protocol handlers
- self.register_default_protocols(&networking_service).await?;
-
- // Set up event bridge to integrate with core event system
- let event_bridge = NetworkEventBridge::new(
- networking_service
- .subscribe_events()
- .await
- .unwrap_or_else(|| {
- let (_, rx) = tokio::sync::mpsc::unbounded_channel();
- rx
- }),
- self.events.clone(),
- );
- tokio::spawn(event_bridge.run());
-
- // Make networking service available to the context for other services
- self.context.set_networking(networking_service).await;
- }
-
- logger.info("Networking initialized successfully").await;
- Ok(())
- }
-
- /// Register default protocol handlers
- async fn register_default_protocols(
- &self,
- networking: &networking::NetworkingService,
- ) -> Result<(), Box> {
- let logger = std::sync::Arc::new(networking::utils::logging::ConsoleLogger);
-
- // Get command sender for the pairing handler's state machine
- let command_sender = networking
- .command_sender()
- .ok_or("NetworkingEventLoop command sender not available")?
- .clone();
-
- // Get data directory from config
- let data_dir = {
- let config = self.config.read().await;
- config.data_dir.clone()
- };
-
- let pairing_handler = Arc::new(networking::protocols::PairingProtocolHandler::new_with_persistence(
- networking.identity().clone(),
- networking.device_registry(),
- logger.clone(),
- command_sender,
- data_dir,
- ));
-
- // Try to load persisted sessions, but don't fail if there's an error
- if let Err(e) = pairing_handler.load_persisted_sessions().await {
- logger.warn(&format!("Failed to load persisted pairing sessions: {}. Starting with empty sessions.", e)).await;
- }
-
- // Start the state machine task for pairing
- networking::protocols::PairingProtocolHandler::start_state_machine_task(
- pairing_handler.clone(),
- );
-
- // Start cleanup task for expired sessions
- networking::protocols::PairingProtocolHandler::start_cleanup_task(pairing_handler.clone());
-
- let messaging_handler = networking::protocols::MessagingProtocolHandler::new();
- let mut file_transfer_handler =
- networking::protocols::FileTransferProtocolHandler::new_default(logger.clone());
-
- // Inject device registry into file transfer handler for encryption
- file_transfer_handler.set_device_registry(networking.device_registry());
-
- let protocol_registry = networking.protocol_registry();
- {
- let mut registry = protocol_registry.write().await;
- registry.register_handler(pairing_handler)?;
- registry.register_handler(Arc::new(messaging_handler))?;
- registry.register_handler(Arc::new(file_transfer_handler))?;
- }
-
- Ok(())
- }
-
- /// Initialize networking from Arc - for daemon use
- pub async fn init_networking_shared(
- core: Arc,
- ) -> Result, Box> {
- info!("Initializing networking for shared core...");
-
- // Create a new Core with networking enabled
- let mut new_core =
- Core::new_with_config(core.config().read().await.data_dir.clone()).await?;
-
- // Initialize networking on the new core
- new_core.init_networking().await?;
-
- info!("Networking initialized successfully for shared core");
- Ok(Arc::new(new_core))
- }
-
- /// Get the networking service (if initialized)
- pub fn networking(&self) -> Option> {
- self.services.networking()
- }
-
- /// Get list of connected devices
- pub async fn get_connected_devices(
- &self,
- ) -> Result, Box> {
- Ok(self.services.device.get_connected_devices().await?)
- }
-
- /// Get detailed information about connected devices
- pub async fn get_connected_devices_info(
- &self,
- ) -> Result, Box> {
- Ok(self.services.device.get_connected_devices_info().await?)
- }
-
- /// Add a location to the file system watcher
- pub async fn add_watched_location(
- &self,
- location_id: uuid::Uuid,
- library_id: uuid::Uuid,
- path: std::path::PathBuf,
- enabled: bool,
- ) -> Result<(), Box> {
- use crate::services::location_watcher::WatchedLocation;
-
- let watched_location = WatchedLocation {
- id: location_id,
- library_id,
- path,
- enabled,
- };
-
- Ok(self
- .services
- .location_watcher
- .add_location(watched_location)
- .await?)
- }
-
- /// Remove a location from the file system watcher
- pub async fn remove_watched_location(
- &self,
- location_id: uuid::Uuid,
- ) -> Result<(), Box> {
- Ok(self
- .services
- .location_watcher
- .remove_location(location_id)
- .await?)
- }
-
- /// Update file watching settings for a location
- pub async fn update_watched_location(
- &self,
- location_id: uuid::Uuid,
- enabled: bool,
- ) -> Result<(), Box> {
- Ok(self
- .services
- .location_watcher
- .update_location(location_id, enabled)
- .await?)
- }
-
- /// Get all currently watched locations
- pub async fn get_watched_locations(
- &self,
- ) -> Vec {
- self.services.location_watcher.get_watched_locations().await
- }
-
- /// Shutdown the core gracefully
- pub async fn shutdown(&self) -> Result<(), Box> {
- info!("Shutting down Spacedrive Core...");
-
- // Networking service is stopped by services.stop_all()
-
- // Stop all services
- self.services.stop_all().await?;
-
- // Stop volume monitoring
- self.volumes.stop_monitoring().await;
-
- // Close all libraries
- self.libraries.close_all().await?;
-
- // Save configuration
- self.config.write().await.save()?;
-
- // Emit shutdown event
- self.events.emit(Event::CoreShutdown);
-
- info!("Spacedrive Core shutdown complete");
- Ok(())
- }
-}
diff --git a/core-new/src/library/config.rs b/core-new/src/library/config.rs
deleted file mode 100644
index 207483d25..000000000
--- a/core-new/src/library/config.rs
+++ /dev/null
@@ -1,208 +0,0 @@
-//! Library configuration types
-
-use chrono::{DateTime, Utc};
-use serde::{Deserialize, Serialize};
-use uuid::Uuid;
-
-/// Library configuration stored in library.json
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct LibraryConfig {
- /// Version of the configuration format
- pub version: u32,
-
- /// Unique identifier for this library
- pub id: Uuid,
-
- /// Human-readable name
- pub name: String,
-
- /// Optional description
- pub description: Option,
-
- /// When the library was created
- pub created_at: DateTime,
-
- /// When the library was last modified
- pub updated_at: DateTime,
-
- /// Library-specific settings
- pub settings: LibrarySettings,
-
- /// Library statistics
- pub statistics: LibraryStatistics,
-}
-
-/// Library-specific settings
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct LibrarySettings {
- /// Whether to generate thumbnails for media files
- pub generate_thumbnails: bool,
-
- /// Thumbnail quality (0-100)
- pub thumbnail_quality: u8,
-
- /// Whether to enable AI-powered tagging
- pub enable_ai_tagging: bool,
-
- /// Whether sync is enabled for this library
- pub sync_enabled: bool,
-
- /// Whether the library is encrypted at rest
- pub encryption_enabled: bool,
-
- /// Custom thumbnail sizes to generate
- pub thumbnail_sizes: Vec,
-
- /// File extensions to ignore during indexing
- pub ignored_extensions: Vec,
-
- /// Maximum file size to index (in bytes)
- pub max_file_size: Option,
-
- /// Whether to automatically track system volumes
- pub auto_track_system_volumes: bool,
-
- /// Whether to automatically track external volumes when connected
- pub auto_track_external_volumes: bool,
-
- /// Indexer settings (rule toggles and related)
- #[serde(default)]
- pub indexer: IndexerSettings,
-}
-
-impl LibraryConfig {
- /// Load library configuration from a JSON file
- pub async fn load(path: &std::path::Path) -> Result {
- let config_data = tokio::fs::read_to_string(path)
- .await
- .map_err(|e| super::error::LibraryError::IoError(e))?;
- let config: LibraryConfig = serde_json::from_str(&config_data)
- .map_err(|e| super::error::LibraryError::JsonError(e))?;
- Ok(config)
- }
-}
-
-impl Default for LibrarySettings {
- fn default() -> Self {
- Self {
- generate_thumbnails: true,
- thumbnail_quality: 85,
- enable_ai_tagging: false,
- sync_enabled: false,
- encryption_enabled: false,
- thumbnail_sizes: vec![128, 256, 512],
- ignored_extensions: vec![
- ".tmp".to_string(),
- ".temp".to_string(),
- ".cache".to_string(),
- ".part".to_string(),
- ],
- max_file_size: Some(100 * 1024 * 1024 * 1024), // 100GB
- auto_track_system_volumes: true, // Default to true for user convenience
- auto_track_external_volumes: false, // Default to false for privacy
- indexer: IndexerSettings::default(),
- }
- }
-}
-
-/// Indexer settings controlling rule toggles
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct IndexerSettings {
- #[serde(default = "IndexerSettings::default_true")]
- pub no_system_files: bool,
- #[serde(default = "IndexerSettings::default_true")]
- pub no_git: bool,
- #[serde(default = "IndexerSettings::default_true")]
- pub no_dev_dirs: bool,
- #[serde(default)]
- pub no_hidden: bool,
- #[serde(default = "IndexerSettings::default_true")]
- pub gitignore: bool,
- #[serde(default)]
- pub only_images: bool,
-}
-
-impl IndexerSettings {
- fn default_true() -> bool {
- true
- }
-}
-
-impl Default for IndexerSettings {
- fn default() -> Self {
- Self {
- no_system_files: true,
- no_git: true,
- no_dev_dirs: true,
- no_hidden: false,
- gitignore: true,
- only_images: false,
- }
- }
-}
-
-/// Library statistics
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct LibraryStatistics {
- /// Total number of files indexed
- pub total_files: u64,
-
- /// Total size of all files in bytes
- pub total_size: u64,
-
- /// Number of locations in this library
- pub location_count: u32,
-
- /// Number of tags created
- pub tag_count: u32,
-
- /// Number of thumbnails generated
- pub thumbnail_count: u64,
-
- /// Last time the library was fully indexed
- pub last_indexed: Option>,
-
- /// When these statistics were last updated
- pub updated_at: DateTime,
-}
-
-impl Default for LibraryStatistics {
- fn default() -> Self {
- Self {
- total_files: 0,
- total_size: 0,
- location_count: 0,
- tag_count: 0,
- thumbnail_count: 0,
- last_indexed: None,
- updated_at: Utc::now(),
- }
- }
-}
-
-/// Thumbnail generation metadata
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct ThumbnailMetadata {
- /// Version of the thumbnail format
- pub version: u32,
-
- /// Quality setting used for generation
- pub quality: u8,
-
- /// Sizes that were generated
- pub sizes: Vec,
-
- /// When this metadata was created
- pub created_at: DateTime,
-}
-
-impl Default for ThumbnailMetadata {
- fn default() -> Self {
- Self {
- version: 1,
- quality: 85,
- sizes: vec![128, 256, 512],
- created_at: Utc::now(),
- }
- }
-}
diff --git a/core-new/src/library/mod.rs b/core-new/src/library/mod.rs
deleted file mode 100644
index 6e8740e21..000000000
--- a/core-new/src/library/mod.rs
+++ /dev/null
@@ -1,238 +0,0 @@
-//! Library management system
-//!
-//! This module provides the core library functionality for Spacedrive.
-//! Each library is a self-contained directory with its own database,
-//! thumbnails, and other data.
-
-mod config;
-mod error;
-mod lock;
-mod manager;
-
-pub use config::{LibraryConfig, LibrarySettings, LibraryStatistics};
-pub use error::{LibraryError, Result};
-pub use lock::LibraryLock;
-pub use manager::{LibraryManager, DiscoveredLibrary};
-
-use crate::infrastructure::{
- database::Database,
- jobs::manager::JobManager,
-};
-use std::path::{Path, PathBuf};
-use std::sync::Arc;
-use tokio::sync::RwLock;
-use uuid::Uuid;
-
-/// Represents an open Spacedrive library
-pub struct Library {
- /// Root directory of the library (the .sdlibrary folder)
- path: PathBuf,
-
- /// Library configuration
- config: RwLock,
-
- /// Database connection
- db: Arc,
-
- /// Job manager for this library
- jobs: Arc,
-
- /// Lock preventing concurrent access
- _lock: LibraryLock,
-}
-
-impl Library {
- /// Get the library ID
- pub fn id(&self) -> Uuid {
- // Config is immutable for ID, so we can use try_read
- self.config.try_read().map(|c| c.id).unwrap_or_else(|_| {
- // This should never happen in practice
- panic!("Failed to read library config for ID")
- })
- }
-
- /// Get the library name
- pub async fn name(&self) -> String {
- self.config.read().await.name.clone()
- }
-
- /// Get the library path
- pub fn path(&self) -> &Path {
- &self.path
- }
-
- /// Get the database
- pub fn db(&self) -> &Arc {
- &self.db
- }
-
- /// Get the job manager
- pub fn jobs(&self) -> &Arc {
- &self.jobs
- }
-
- /// Get a copy of the current configuration
- pub async fn config(&self) -> LibraryConfig {
- self.config.read().await.clone()
- }
-
- /// Update library configuration
- pub async fn update_config(&self, f: F) -> Result<()>
- where
- F: FnOnce(&mut LibraryConfig),
- {
- let mut config = self.config.write().await;
- f(&mut config);
- config.updated_at = chrono::Utc::now();
-
- // Save to disk
- let config_path = self.path.join("library.json");
- let json = serde_json::to_string_pretty(&*config)?;
- tokio::fs::write(config_path, json).await?;
-
- Ok(())
- }
-
- /// Save library configuration to disk
- pub async fn save_config(&self, config: &LibraryConfig) -> Result<()> {
- let config_path = self.path.join("library.json");
- let json = serde_json::to_string_pretty(config)?;
- tokio::fs::write(config_path, json).await?;
- Ok(())
- }
-
- /// Get the thumbnail directory for this library
- pub fn thumbnails_dir(&self) -> PathBuf {
- self.path.join("thumbnails")
- }
-
- /// Get the path for a specific thumbnail with size
- pub fn thumbnail_path(&self, cas_id: &str, size: u32) -> PathBuf {
- if cas_id.len() < 4 {
- // Fallback for short IDs
- return self.thumbnails_dir().join(format!("{}_{}.webp", cas_id, size));
- }
-
- // Two-level sharding based on first four characters
- let shard1 = &cas_id[0..2];
- let shard2 = &cas_id[2..4];
-
- self.thumbnails_dir()
- .join(shard1)
- .join(shard2)
- .join(format!("{}_{}.webp", cas_id, size))
- }
-
- /// Get the path for any thumbnail size (legacy compatibility)
- pub fn thumbnail_path_legacy(&self, cas_id: &str) -> PathBuf {
- self.thumbnail_path(cas_id, 256) // Default to 256px
- }
-
- /// Save a thumbnail with specific size
- pub async fn save_thumbnail(&self, cas_id: &str, size: u32, data: &[u8]) -> Result<()> {
- let path = self.thumbnail_path(cas_id, size);
-
- // Ensure parent directory exists
- if let Some(parent) = path.parent() {
- tokio::fs::create_dir_all(parent).await?;
- }
-
- // Write thumbnail
- tokio::fs::write(path, data).await?;
-
- Ok(())
- }
-
- /// Check if a thumbnail exists for a specific size
- pub async fn has_thumbnail(&self, cas_id: &str, size: u32) -> bool {
- tokio::fs::metadata(self.thumbnail_path(cas_id, size))
- .await
- .is_ok()
- }
-
- /// Shutdown the library, gracefully stopping all jobs
- pub async fn shutdown(&self) -> Result<()> {
- // Shutdown the job manager, which will pause all running jobs
- self.jobs.shutdown().await?;
-
- // Save config to ensure any updates are persisted
- let config = self.config.read().await;
- self.save_config(&*config).await?;
-
- Ok(())
- }
-
- /// Check if thumbnails exist for all specified sizes
- pub async fn has_all_thumbnails(&self, cas_id: &str, sizes: &[u32]) -> bool {
- for &size in sizes {
- if !self.has_thumbnail(cas_id, size).await {
- return false;
- }
- }
- true
- }
-
- /// Get thumbnail data for specific size
- pub async fn get_thumbnail(&self, cas_id: &str, size: u32) -> Result> {
- let path = self.thumbnail_path(cas_id, size);
- Ok(tokio::fs::read(path).await?)
- }
-
- /// Get the best available thumbnail (largest size available)
- pub async fn get_best_thumbnail(&self, cas_id: &str, preferred_sizes: &[u32]) -> Result)>> {
- // Try sizes in descending order
- let mut sizes = preferred_sizes.to_vec();
- sizes.sort_by(|a, b| b.cmp(a));
-
- for &size in &sizes {
- if self.has_thumbnail(cas_id, size).await {
- let data = self.get_thumbnail(cas_id, size).await?;
- return Ok(Some((size, data)));
- }
- }
-
- Ok(None)
- }
-
- /// Start thumbnail generation job
- pub async fn generate_thumbnails(&self, entry_ids: Option>) -> Result {
- use crate::operations::media::thumbnail::{ThumbnailJob, ThumbnailJobConfig};
-
- let config = ThumbnailJobConfig {
- sizes: self.config().await.settings.thumbnail_sizes.clone(),
- quality: self.config().await.settings.thumbnail_quality,
- regenerate: false,
- batch_size: 50,
- max_concurrent: 4,
- };
-
- let job = if let Some(ids) = entry_ids {
- ThumbnailJob::for_entries(ids, config)
- } else {
- ThumbnailJob::new(config)
- };
-
- self.jobs().dispatch(job).await
- .map_err(|e| LibraryError::JobError(e))
- }
-
- /// Update library statistics
- pub async fn update_statistics(&self, f: F) -> Result<()>
- where
- F: FnOnce(&mut LibraryStatistics),
- {
- self.update_config(|config| {
- f(&mut config.statistics);
- config.statistics.updated_at = chrono::Utc::now();
- }).await
- }
-}
-
-// Note: Library does not implement Clone due to the exclusive lock
-// Use Arc when you need shared access
-
-/// Current library configuration version
-pub const LIBRARY_CONFIG_VERSION: u32 = 2;
-
-/// Library directory extension
-pub const LIBRARY_EXTENSION: &str = "sdlibrary";
\ No newline at end of file
diff --git a/core-new/src/location/mod.rs b/core-new/src/location/mod.rs
deleted file mode 100644
index 983d53c72..000000000
--- a/core-new/src/location/mod.rs
+++ /dev/null
@@ -1,563 +0,0 @@
-//! Location management - simplified implementation matching core patterns
-
-pub mod manager;
-
-use crate::{
- infrastructure::{
- database::entities::{self, entry::EntryKind},
- events::{Event, EventBus},
- jobs::{handle::JobHandle, output::IndexedOutput, types::JobStatus},
- },
- library::Library,
- operations::indexing::{IndexMode as JobIndexMode, IndexerJob, IndexerJobConfig, PathResolver, rules::RuleToggles},
- domain::addressing::SdPath,
-};
-
-use sea_orm::{ActiveModelTrait, ActiveValue::Set, ColumnTrait, EntityTrait, QueryFilter, TransactionTrait};
-use serde::{Deserialize, Serialize};
-use std::{path::PathBuf, sync::Arc};
-use tokio::fs;
-use tracing::{error, info, warn};
-use uuid::Uuid;
-
-pub use manager::LocationManager;
-
-/// Location creation arguments (simplified from production version)
-#[derive(Debug, Serialize, Deserialize)]
-pub struct LocationCreateArgs {
- pub path: PathBuf,
- pub name: Option,
- pub index_mode: IndexMode,
-}
-
-/// Location indexing mode
-#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
-pub enum IndexMode {
- /// Only scan file/directory structure
- Shallow,
- /// Quick scan (metadata only)
- Quick,
- /// Include content hashing for deduplication
- Content,
- /// Full indexing with content analysis and metadata
- Deep,
- /// Full indexing with all features
- Full,
-}
-
-impl From for JobIndexMode {
- fn from(mode: IndexMode) -> Self {
- match mode {
- IndexMode::Shallow => JobIndexMode::Shallow,
- IndexMode::Quick => JobIndexMode::Content,
- IndexMode::Content => JobIndexMode::Content,
- IndexMode::Deep => JobIndexMode::Deep,
- IndexMode::Full => JobIndexMode::Deep,
- }
- }
-}
-
-impl From<&str> for IndexMode {
- fn from(s: &str) -> Self {
- match s.to_lowercase().as_str() {
- "shallow" => IndexMode::Shallow,
- "quick" => IndexMode::Quick,
- "content" => IndexMode::Content,
- "deep" => IndexMode::Deep,
- "full" => IndexMode::Full,
- _ => IndexMode::Full,
- }
- }
-}
-
-impl std::fmt::Display for IndexMode {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- IndexMode::Shallow => write!(f, "shallow"),
- IndexMode::Quick => write!(f, "quick"),
- IndexMode::Content => write!(f, "content"),
- IndexMode::Deep => write!(f, "deep"),
- IndexMode::Full => write!(f, "full"),
- }
- }
-}
-
-/// Managed location representation
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct ManagedLocation {
- pub id: Uuid,
- pub name: String,
- pub path: PathBuf,
- pub device_id: i32,
- pub library_id: Uuid,
- pub indexing_enabled: bool,
- pub index_mode: IndexMode,
- pub watch_enabled: bool,
-}
-
-/// Location management errors
-#[derive(Debug, thiserror::Error)]
-pub enum LocationError {
- #[error("Database error: {0}")]
- Database(#[from] sea_orm::DbErr),
- #[error("Database error: {0}")]
- DatabaseError(String),
- #[error("Path does not exist: {path}")]
- PathNotFound { path: PathBuf },
- #[error("Path not accessible: {path}")]
- PathNotAccessible { path: PathBuf },
- #[error("Location already exists: {path}")]
- LocationExists { path: PathBuf },
- #[error("Location not found: {id}")]
- LocationNotFound { id: Uuid },
- #[error("IO error: {0}")]
- Io(#[from] std::io::Error),
- #[error("Invalid path: {0}")]
- InvalidPath(String),
- #[error("Job error: {0}")]
- Job(#[from] crate::infrastructure::jobs::error::JobError),
- #[error("Other error: {0}")]
- Other(String),
-}
-
-pub type LocationResult = Result;
-
-/// Create a new location and start indexing (production pattern)
-pub async fn create_location(
- library: Arc,
- events: &EventBus,
- args: LocationCreateArgs,
- device_id: i32,
-) -> LocationResult {
- let path_str = args
- .path
- .to_str()
- .ok_or_else(|| LocationError::InvalidPath("Non-UTF8 path".to_string()))?;
-
- // Validate path exists
- if !args.path.exists() {
- return Err(LocationError::PathNotFound { path: args.path });
- }
-
- if !args.path.is_dir() {
- return Err(LocationError::InvalidPath(
- "Path must be a directory".to_string(),
- ));
- }
-
- // Begin transaction to ensure atomicity
- let txn = library.db().conn().begin().await
- .map_err(|e| LocationError::DatabaseError(e.to_string()))?;
-
- // First, check if an entry already exists for this path
- // We need to create a root entry for the location directory
- let directory_name = args.path
- .file_name()
- .and_then(|n| n.to_str())
- .unwrap_or("Unknown")
- .to_string();
-
- // Create entry for the location directory
- let entry_model = entities::entry::ActiveModel {
- uuid: Set(Some(Uuid::new_v4())),
- name: Set(directory_name.clone()),
- kind: Set(EntryKind::Directory as i32),
- extension: Set(None),
- metadata_id: Set(None),
- content_id: Set(None),
- size: Set(0),
- aggregate_size: Set(0),
- child_count: Set(0),
- file_count: Set(0),
- created_at: Set(chrono::Utc::now()),
- modified_at: Set(chrono::Utc::now()),
- accessed_at: Set(None),
- permissions: Set(None),
- inode: Set(None),
- parent_id: Set(None), // Location root has no parent
- ..Default::default()
- };
-
- let entry_record = entry_model.insert(&txn).await
- .map_err(|e| LocationError::DatabaseError(e.to_string()))?;
- let entry_id = entry_record.id;
-
- // Add self-reference to closure table
- let self_closure = entities::entry_closure::ActiveModel {
- ancestor_id: Set(entry_id),
- descendant_id: Set(entry_id),
- depth: Set(0),
- ..Default::default()
- };
- self_closure.insert(&txn).await
- .map_err(|e| LocationError::DatabaseError(e.to_string()))?;
-
- // Add to directory_paths table
- let dir_path_entry = entities::directory_paths::ActiveModel {
- entry_id: Set(entry_id),
- path: Set(path_str.to_string()),
- ..Default::default()
- };
- dir_path_entry.insert(&txn).await
- .map_err(|e| LocationError::DatabaseError(e.to_string()))?;
-
- // Check if a location already exists for this entry
- let existing = entities::location::Entity::find()
- .filter(entities::location::Column::EntryId.eq(entry_id))
- .one(&txn)
- .await
- .map_err(|e| LocationError::DatabaseError(e.to_string()))?;
-
- if existing.is_some() {
- // Rollback transaction
- txn.rollback().await
- .map_err(|e| LocationError::DatabaseError(e.to_string()))?;
- return Err(LocationError::LocationExists { path: args.path });
- }
-
- // Create location record
- let location_id = Uuid::new_v4();
- let name = args.name.unwrap_or_else(|| {
- args.path
- .file_name()
- .and_then(|n| n.to_str())
- .unwrap_or("Unknown")
- .to_string()
- });
-
- let location_model = entities::location::ActiveModel {
- id: Set(0), // Auto-increment
- uuid: Set(location_id),
- device_id: Set(device_id),
- entry_id: Set(entry_id),
- name: Set(Some(name.clone())),
- index_mode: Set(args.index_mode.to_string()),
- scan_state: Set("pending".to_string()),
- last_scan_at: Set(None),
- error_message: Set(None),
- total_file_count: Set(0),
- total_byte_size: Set(0),
- created_at: Set(chrono::Utc::now()),
- updated_at: Set(chrono::Utc::now()),
- };
-
- let location_record = location_model.insert(&txn).await
- .map_err(|e| LocationError::DatabaseError(e.to_string()))?;
- let location_db_id = location_record.id;
-
- // Commit transaction
- txn.commit().await
- .map_err(|e| LocationError::DatabaseError(e.to_string()))?;
-
- info!("Created location '{}' with ID: {}", name, location_db_id);
-
- // Emit location added event
- events.emit(Event::LocationAdded {
- library_id: library.id(),
- location_id,
- path: args.path.clone(),
- });
-
- // Start indexing (simplified - in production this goes through proper job manager)
- start_location_indexing(
- library.clone(),
- events,
- location_db_id,
- location_id,
- args.path,
- args.index_mode,
- )
- .await?;
-
- Ok(location_db_id)
-}
-
-/// Start indexing for a location (production implementation)
-async fn start_location_indexing(
- library: Arc,
- events: &EventBus,
- location_db_id: i32,
- location_uuid: Uuid,
- path: PathBuf,
- index_mode: IndexMode,
-) -> LocationResult<()> {
- info!("Starting indexing for location: {}", path.display());
-
- // Update scan state to "running"
- update_location_scan_state(library.clone(), location_db_id, "running", None).await?;
-
- // Emit indexing started event
- events.emit(Event::IndexingStarted {
- location_id: location_uuid,
- });
-
- // Get device UUID for SdPath
- let device_uuid = get_device_uuid(library.clone()).await?;
- let location_sd_path = SdPath::new(device_uuid, path.clone());
-
- // Create and dispatch indexer job through the proper job manager
- let lib_cfg = library.config().await;
- let idx_cfg = lib_cfg.settings.indexer;
- let mut config = IndexerJobConfig::new(location_uuid, location_sd_path, index_mode.into());
- config.rule_toggles = RuleToggles {
- no_system_files: idx_cfg.no_system_files,
- no_hidden: idx_cfg.no_hidden,
- no_git: idx_cfg.no_git,
- gitignore: idx_cfg.gitignore,
- only_images: idx_cfg.only_images,
- no_dev_dirs: idx_cfg.no_dev_dirs,
- };
- let indexer_job = IndexerJob::new(config);
-
- match library.jobs().dispatch(indexer_job).await {
- Ok(job_handle) => {
- info!(
- "Successfully dispatched indexer job {} for location: {}",
- job_handle.id(),
- path.display()
- );
-
- // Monitor job progress asynchronously
- let events_clone = events.clone();
- let library_clone = library.clone();
- let handle_clone = job_handle.clone();
-
- tokio::spawn(async move {
- monitor_indexing_job(
- handle_clone,
- events_clone,
- library_clone,
- location_db_id,
- location_uuid,
- path,
- )
- .await;
- });
- }
- Err(e) => {
- error!(
- "Failed to dispatch indexer job for {}: {}",
- path.display(),
- e
- );
-
- // Update scan state to failed
- if let Err(update_err) = update_location_scan_state(
- library.clone(),
- location_db_id,
- "failed",
- Some(e.to_string()),
- )
- .await
- {
- error!("Failed to update scan state: {}", update_err);
- }
-
- events.emit(Event::IndexingFailed {
- location_id: location_uuid,
- error: e.to_string(),
- });
-
- return Err(LocationError::Other(format!(
- "Failed to start indexing: {}",
- e
- )));
- }
- }
-
- Ok(())
-}
-
-/// Monitor indexing job progress and update location state accordingly
-async fn monitor_indexing_job(
- job_handle: JobHandle,
- events: EventBus,
- library: Arc,
- location_db_id: i32,
- location_uuid: Uuid,
- path: PathBuf,
-) {
- info!(
- "Monitoring indexer job {} for location: {}",
- job_handle.id(),
- path.display()
- );
-
- // Wait for job completion
- let job_result = job_handle.wait().await;
-
- match job_result {
- Ok(output) => {
- info!(
- "Indexing completed successfully for location: {}",
- path.display()
- );
-
- // Parse output to get statistics
- if let Some(indexer_output) = output.as_indexed() {
- // Update location stats
- if let Err(e) = update_location_stats(
- library.clone(),
- location_db_id,
- indexer_output.total_files,
- indexer_output.total_bytes,
- )
- .await
- {
- error!("Failed to update location stats: {}", e);
- }
-
- // Update scan state to completed
- if let Err(e) =
- update_location_scan_state(library.clone(), location_db_id, "completed", None)
- .await
- {
- error!("Failed to update scan state: {}", e);
- }
-
- // Emit completion events
- events.emit(Event::IndexingCompleted {
- location_id: location_uuid,
- total_files: indexer_output.total_files,
- total_dirs: indexer_output.total_dirs,
- });
-
- events.emit(Event::FilesIndexed {
- library_id: library.id(),
- location_id: location_uuid,
- count: indexer_output.total_files as usize,
- });
-
- info!(
- "Location indexing completed: {} ({} files, {} dirs, {} bytes)",
- path.display(),
- indexer_output.total_files,
- indexer_output.total_dirs,
- indexer_output.total_bytes
- );
- } else {
- warn!("Job completed but output format was unexpected");
-
- // Update scan state to completed anyway
- if let Err(e) =
- update_location_scan_state(library.clone(), location_db_id, "completed", None)
- .await
- {
- error!("Failed to update scan state: {}", e);
- }
- }
- }
- Err(e) => {
- error!("Indexing failed for {}: {}", path.display(), e);
-
- // Update scan state to failed
- if let Err(update_err) = update_location_scan_state(
- library.clone(),
- location_db_id,
- "failed",
- Some(e.to_string()),
- )
- .await
- {
- error!("Failed to update scan state: {}", update_err);
- }
-
- events.emit(Event::IndexingFailed {
- location_id: location_uuid,
- error: e.to_string(),
- });
- }
- }
-}
-
-/// Scan directory to get basic stats
-async fn scan_directory_stats(path: &PathBuf) -> Result<(u64, u64), std::io::Error> {
- let mut file_count = 0u64;
- let mut total_size = 0u64;
-
- let mut stack = vec![path.clone()];
-
- while let Some(current_path) = stack.pop() {
- if let Ok(mut entries) = fs::read_dir(¤t_path).await {
- while let Ok(Some(entry)) = entries.next_entry().await {
- if let Ok(metadata) = entry.metadata().await {
- if metadata.is_file() {
- file_count += 1;
- total_size += metadata.len();
- } else if metadata.is_dir() {
- stack.push(entry.path());
- }
- }
- }
- }
- }
-
- Ok((file_count, total_size))
-}
-
-/// Update location scan state
-async fn update_location_scan_state(
- library: Arc,
- location_id: i32,
- state: &str,
- error_message: Option,
-) -> LocationResult<()> {
- let location = entities::location::Entity::find_by_id(location_id)
- .one(library.db().conn())
- .await?
- .ok_or_else(|| LocationError::LocationNotFound { id: Uuid::nil() })?;
-
- let mut active_location: entities::location::ActiveModel = location.into();
- active_location.scan_state = Set(state.to_string());
- active_location.error_message = Set(error_message);
- active_location.updated_at = Set(chrono::Utc::now());
-
- if state == "running" {
- active_location.last_scan_at = Set(Some(chrono::Utc::now()));
- }
-
- active_location.update(library.db().conn()).await?;
- Ok(())
-}
-
-/// Update location statistics
-async fn update_location_stats(
- library: Arc,
- location_id: i32,
- file_count: u64,
- total_size: u64,
-) -> LocationResult<()> {
- let location = entities::location::Entity::find_by_id(location_id)
- .one(library.db().conn())
- .await?
- .ok_or_else(|| LocationError::LocationNotFound { id: Uuid::nil() })?;
-
- let mut active_location: entities::location::ActiveModel = location.into();
- active_location.total_file_count = Set(file_count as i64);
- active_location.total_byte_size = Set(total_size as i64);
- active_location.updated_at = Set(chrono::Utc::now());
-
- active_location.update(library.db().conn()).await?;
- Ok(())
-}
-
-/// Get device UUID for current device
-async fn get_device_uuid(_library: Arc) -> LocationResult {
- // Get the current device ID from the global state
- let device_uuid = crate::shared::utils::get_current_device_id();
-
- if device_uuid.is_nil() {
- return Err(LocationError::InvalidPath("Current device ID not initialized".to_string()));
- }
-
- Ok(device_uuid)
-}
-
-/// List all locations for a library
-pub async fn list_locations(
- library: Arc,
-) -> LocationResult> {
- Ok(entities::location::Entity::find()
- .all(library.db().conn())
- .await?)
-}
diff --git a/core-new/src/volume/error.rs b/core-new/src/volume/error.rs
deleted file mode 100644
index cc2564ff0..000000000
--- a/core-new/src/volume/error.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-//! Volume-related error types
-
-use thiserror::Error;
-
-/// Errors that can occur during volume operations
-#[derive(Error, Debug)]
-pub enum VolumeError {
- /// IO error during volume operations
- #[error("IO error: {0}")]
- Io(#[from] std::io::Error),
-
- /// Platform-specific error
- #[error("Platform error: {0}")]
- Platform(String),
-
- /// Volume not found
- #[error("Volume not found: {0}")]
- NotFound(String),
-
- /// Volume is not mounted
- #[error("Volume is not mounted: {0}")]
- NotMounted(String),
-
- /// Volume is read-only
- #[error("Volume is read-only: {0}")]
- ReadOnly(String),
-
- /// Insufficient space on volume
- #[error("Insufficient space on volume: required {required}, available {available}")]
- InsufficientSpace { required: u64, available: u64 },
-
- /// Speed test was cancelled or failed
- #[error("Speed test cancelled or failed")]
- SpeedTestFailed,
-
- /// Volume detection failed
- #[error("Volume detection failed: {0}")]
- DetectionFailed(String),
-
- /// Permission denied
- #[error("Permission denied: {0}")]
- PermissionDenied(String),
-
- /// Operation timed out
- #[error("Operation timed out")]
- Timeout,
-
- /// Invalid volume data
- #[error("Invalid volume data: {0}")]
- InvalidData(String),
-
- /// Database operation failed
- #[error("Database error: {0}")]
- Database(String),
-
- /// Volume is already tracked
- #[error("Volume is already tracked: {0}")]
- AlreadyTracked(String),
-
- /// Volume is not tracked
- #[error("Volume is not tracked: {0}")]
- NotTracked(String),
-}
-
-impl VolumeError {
- /// Create a platform-specific error
- pub fn platform(msg: impl Into) -> Self {
- Self::Platform(msg.into())
- }
-
- /// Create a detection failed error
- pub fn detection_failed(msg: impl Into) -> Self {
- Self::DetectionFailed(msg.into())
- }
-
- /// Create an insufficient space error
- pub fn insufficient_space(required: u64, available: u64) -> Self {
- Self::InsufficientSpace { required, available }
- }
-}
-
-/// Result type for volume operations
-pub type VolumeResult = Result;
\ No newline at end of file
diff --git a/core-new/src/volume/mod.rs b/core-new/src/volume/mod.rs
deleted file mode 100644
index 5464bad8c..000000000
--- a/core-new/src/volume/mod.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-//! Volume management for Spacedrive Core v2
-//!
-//! This module provides functionality for detecting, monitoring, and managing storage volumes
-//! across different platforms. It's designed to integrate with the copy system for optimal
-//! file operation routing.
-
-pub mod classification;
-pub mod error;
-pub mod manager;
-pub mod os_detection;
-pub mod speed;
-pub mod types;
-
-pub use error::VolumeError;
-pub use manager::VolumeManager;
-pub use types::{
- DiskType, FileSystem, MountType, Volume, VolumeDetectionConfig, VolumeEvent, VolumeFingerprint,
- VolumeInfo,
-};
-
-// Re-export platform-specific detection
-pub use os_detection::detect_volumes;
-
-/// Extension trait for Volume operations
-pub trait VolumeExt {
- /// Checks if volume is mounted and accessible
- async fn is_available(&self) -> bool;
-
- /// Checks if volume has enough free space
- fn has_space(&self, required_bytes: u64) -> bool;
-
- /// Check if path is on this volume
- fn contains_path(&self, path: &std::path::Path) -> bool;
-}
-
-impl VolumeExt for Volume {
- async fn is_available(&self) -> bool {
- self.is_mounted && tokio::fs::metadata(&self.mount_point).await.is_ok()
- }
-
- fn has_space(&self, required_bytes: u64) -> bool {
- self.total_bytes_available >= required_bytes
- }
-
- fn contains_path(&self, path: &std::path::Path) -> bool {
- // Check primary mount point
- if path.starts_with(&self.mount_point) {
- return true;
- }
-
- // Check additional mount points (for APFS volumes)
- self.mount_points.iter().any(|mp| path.starts_with(mp))
- }
-}
-
-/// Utilities for volume operations
-pub mod util {
- use super::*;
- use std::path::Path;
-
- /// Check if a path is on the specified volume
- pub fn is_path_on_volume(path: &Path, volume: &Volume) -> bool {
- volume.contains_path(&path.to_path_buf())
- }
-
- /// Calculate relative path from volume mount point
- pub fn relative_path_on_volume(path: &Path, volume: &Volume) -> Option {
- // Try primary mount point first
- if let Ok(relative) = path.strip_prefix(&volume.mount_point) {
- return Some(relative.to_path_buf());
- }
-
- // Try additional mount points
- for mount_point in &volume.mount_points {
- if let Ok(relative) = path.strip_prefix(mount_point) {
- return Some(relative.to_path_buf());
- }
- }
-
- None
- }
-
- /// Find the volume that contains the given path
- pub fn find_volume_for_path<'a>(
- path: &Path,
- volumes: impl Iterator- ,
- ) -> Option<&'a Volume> {
- volumes
- .filter(|vol| vol.contains_path(&path.to_path_buf()))
- .max_by_key(|vol| vol.mount_point.as_os_str().len()) // Prefer most specific mount
- }
-}
diff --git a/core-new/src/volume/speed.rs b/core-new/src/volume/speed.rs
deleted file mode 100644
index 670fc2ece..000000000
--- a/core-new/src/volume/speed.rs
+++ /dev/null
@@ -1,369 +0,0 @@
-//! Volume speed testing functionality
-
-use crate::volume::{
- error::{VolumeError, VolumeResult},
- types::{MountType, Volume, VolumeType},
-};
-use std::time::Instant;
-use tokio::{
- fs::{File, OpenOptions},
- io::{AsyncReadExt, AsyncWriteExt},
- time::{timeout, Duration},
-};
-use tracing::{debug, instrument, warn};
-
-/// Configuration for speed tests
-#[derive(Debug, Clone)]
-pub struct SpeedTestConfig {
- /// Size of the test file in megabytes
- pub file_size_mb: usize,
- /// Timeout for the test in seconds
- pub timeout_secs: u64,
- /// Number of test iterations for averaging
- pub iterations: usize,
-}
-
-impl Default for SpeedTestConfig {
- fn default() -> Self {
- Self {
- file_size_mb: 10,
- timeout_secs: 30,
- iterations: 1,
- }
- }
-}
-
-/// Result of a speed test
-#[derive(Debug, Clone)]
-pub struct SpeedTestResult {
- /// Write speed in MB/s
- pub write_speed_mbps: f64,
- /// Read speed in MB/s
- pub read_speed_mbps: f64,
- /// Total time taken for the test
- pub duration_secs: f64,
-}
-
-/// Run a speed test on the given volume
-#[instrument(skip(volume), fields(volume_name = %volume.name))]
-pub async fn run_speed_test(volume: &Volume) -> VolumeResult<(u64, u64)> {
- run_speed_test_with_config(volume, SpeedTestConfig::default()).await
-}
-
-/// Run a speed test with custom configuration
-#[instrument(skip(volume, config), fields(volume_name = %volume.name))]
-pub async fn run_speed_test_with_config(
- volume: &Volume,
- config: SpeedTestConfig,
-) -> VolumeResult<(u64, u64)> {
- if !volume.is_mounted {
- return Err(VolumeError::NotMounted(volume.name.clone()));
- }
-
- if volume.read_only {
- return Err(VolumeError::ReadOnly(volume.name.clone()));
- }
-
- debug!("Starting speed test with config: {:?}", config);
-
- let test_location = TestLocation::new(&volume.mount_point, &volume.mount_type).await?;
- let result = perform_speed_test(&test_location, &config).await?;
-
- // Cleanup
- test_location.cleanup().await?;
-
- debug!(
- "Speed test completed: {:.2} MB/s write, {:.2} MB/s read",
- result.write_speed_mbps, result.read_speed_mbps
- );
-
- Ok((
- result.read_speed_mbps as u64,
- result.write_speed_mbps as u64,
- ))
-}
-
-/// Helper for managing test files and directories
-struct TestLocation {
- test_file: std::path::PathBuf,
- created_dir: Option
,
-}
-
-impl TestLocation {
- /// Create a new test location
- async fn new(volume_path: &std::path::Path, mount_type: &MountType) -> VolumeResult {
- let (dir, created_dir) = get_writable_directory(volume_path, mount_type).await?;
- let test_file = dir.join("spacedrive_speed_test.tmp");
-
- Ok(Self {
- test_file,
- created_dir,
- })
- }
-
- /// Clean up test files and directories
- async fn cleanup(&self) -> VolumeResult<()> {
- // Remove test file
- if self.test_file.exists() {
- if let Err(e) = tokio::fs::remove_file(&self.test_file).await {
- warn!("Failed to remove test file: {}", e);
- }
- }
-
- // Remove created directory if we created it
- if let Some(ref dir) = self.created_dir {
- if let Err(e) = tokio::fs::remove_dir_all(dir).await {
- warn!("Failed to remove test directory: {}", e);
- }
- }
-
- Ok(())
- }
-}
-
-/// Perform the actual speed test
-async fn perform_speed_test(
- location: &TestLocation,
- config: &SpeedTestConfig,
-) -> VolumeResult {
- let test_data = generate_test_data(config.file_size_mb);
- let timeout_duration = Duration::from_secs(config.timeout_secs);
-
- let mut write_speeds = Vec::new();
- let mut read_speeds = Vec::new();
- let overall_start = Instant::now();
-
- for iteration in 0..config.iterations {
- debug!(
- "Speed test iteration {}/{}",
- iteration + 1,
- config.iterations
- );
-
- // Write test
- let write_speed = timeout(
- timeout_duration,
- perform_write_test(&location.test_file, &test_data),
- )
- .await
- .map_err(|_| VolumeError::Timeout)??;
-
- write_speeds.push(write_speed);
-
- // Read test
- let read_speed = timeout(
- timeout_duration,
- perform_read_test(&location.test_file, test_data.len()),
- )
- .await
- .map_err(|_| VolumeError::Timeout)??;
-
- read_speeds.push(read_speed);
-
- // Clean up test file between iterations
- if iteration < config.iterations - 1 {
- let _ = tokio::fs::remove_file(&location.test_file).await;
- }
- }
-
- let avg_write_speed = write_speeds.iter().sum::() / write_speeds.len() as f64;
- let avg_read_speed = read_speeds.iter().sum::() / read_speeds.len() as f64;
-
- Ok(SpeedTestResult {
- write_speed_mbps: avg_write_speed,
- read_speed_mbps: avg_read_speed,
- duration_secs: overall_start.elapsed().as_secs_f64(),
- })
-}
-
-/// Generate test data for speed testing
-fn generate_test_data(size_mb: usize) -> Vec {
- let size_bytes = size_mb * 1024 * 1024;
-
- // Use a pattern instead of zeros to avoid compression optimizations
- let pattern = b"SpacedriveSpeedTest0123456789ABCDEF";
- let mut data = Vec::with_capacity(size_bytes);
-
- for i in 0..size_bytes {
- data.push(pattern[i % pattern.len()]);
- }
-
- data
-}
-
-/// Perform write speed test
-async fn perform_write_test(file_path: &std::path::Path, data: &[u8]) -> VolumeResult {
- let start = Instant::now();
-
- let mut file = OpenOptions::new()
- .write(true)
- .create(true)
- .truncate(true)
- .open(file_path)
- .await?;
-
- file.write_all(data).await?;
- file.sync_all().await?; // Ensure data is written to disk
-
- let duration = start.elapsed();
- let speed_mbps = (data.len() as f64 / 1024.0 / 1024.0) / duration.as_secs_f64();
-
- Ok(speed_mbps)
-}
-
-/// Perform read speed test
-async fn perform_read_test(file_path: &std::path::Path, expected_size: usize) -> VolumeResult {
- let start = Instant::now();
-
- let mut file = File::open(file_path).await?;
- let mut buffer = Vec::with_capacity(expected_size);
- file.read_to_end(&mut buffer).await?;
-
- let duration = start.elapsed();
- let speed_mbps = (buffer.len() as f64 / 1024.0 / 1024.0) / duration.as_secs_f64();
-
- Ok(speed_mbps)
-}
-
-/// Get a writable directory within the volume
-async fn get_writable_directory(
- volume_path: &std::path::Path,
- mount_type: &MountType,
-) -> VolumeResult<(std::path::PathBuf, Option)> {
- match mount_type {
- MountType::System => {
- // For system volumes, prefer using temp directory
- let temp_dir = std::env::temp_dir();
- Ok((temp_dir, None))
- }
- _ => {
- // For external volumes, try to write in the root or create a temp directory
- let candidates = [
- volume_path.join("tmp"),
- volume_path.join(".spacedrive_temp"),
- volume_path.to_path_buf(),
- ];
-
- for candidate in &candidates {
- // Try to create the directory
- if let Ok(()) = tokio::fs::create_dir_all(candidate).await {
- // Test if we can write to it
- let test_file = candidate.join("test_write_permissions");
- if tokio::fs::write(&test_file, b"test").await.is_ok() {
- let _ = tokio::fs::remove_file(&test_file).await;
-
- // If we created a directory specifically for this test, mark it for cleanup
- let created_dir = if candidate
- .file_name()
- .map_or(false, |name| name == "tmp" || name == ".spacedrive_temp")
- {
- Some(candidate.clone())
- } else {
- None
- };
-
- return Ok((candidate.clone(), created_dir));
- }
- }
- }
-
- Err(VolumeError::PermissionDenied(format!(
- "No writable directory found in volume: {}",
- volume_path.display()
- )))
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::volume::{
- types::{DiskType, FileSystem},
- VolumeFingerprint,
- };
- use tempfile::TempDir;
-
- #[tokio::test]
- async fn test_speed_test_config() {
- let config = SpeedTestConfig::default();
- assert_eq!(config.file_size_mb, 10);
- assert_eq!(config.timeout_secs, 30);
- assert_eq!(config.iterations, 1);
- }
-
- #[tokio::test]
- async fn test_generate_test_data() {
- let data = generate_test_data(1); // 1MB
- assert_eq!(data.len(), 1024 * 1024);
-
- // Verify pattern is not all zeros
- assert!(data.iter().any(|&b| b != 0));
- }
-
- #[tokio::test]
- async fn test_writable_directory_external() {
- let temp_dir = TempDir::new().unwrap();
- let volume_path = temp_dir.path();
-
- let (writable_dir, created_dir) = get_writable_directory(volume_path, &MountType::External)
- .await
- .unwrap();
-
- assert!(writable_dir.exists());
-
- // Cleanup if we created a directory
- if let Some(dir) = created_dir {
- let _ = tokio::fs::remove_dir_all(dir).await;
- }
- }
-
- #[tokio::test]
- async fn test_writable_directory_system() {
- let (writable_dir, created_dir) =
- get_writable_directory(&std::path::PathBuf::from("/"), &MountType::System)
- .await
- .unwrap();
-
- assert!(writable_dir.exists());
- assert!(created_dir.is_none()); // Should use system temp, not create new dir
- }
-
- #[tokio::test]
- async fn test_full_speed_test() {
- let temp_dir = TempDir::new().unwrap();
-
- let volume = Volume::new(
- uuid::Uuid::new_v4(), // Test device ID
- "Test Volume".to_string(),
- MountType::External,
- VolumeType::External,
- temp_dir.path().to_path_buf(),
- vec![],
- DiskType::Unknown,
- FileSystem::Other("test".to_string()),
- 1000000000, // 1GB capacity
- 500000000, // 500MB available
- false, // Not read-only
- None,
- VolumeFingerprint::new(
- "Test Volume",
- 1000000000,
- "test",
- ),
- );
-
- let config = SpeedTestConfig {
- file_size_mb: 1, // Small test file
- timeout_secs: 10,
- iterations: 1,
- };
-
- let result = run_speed_test_with_config(&volume, config).await;
- assert!(result.is_ok());
-
- let (read_speed, write_speed) = result.unwrap();
- assert!(read_speed > 0);
- assert!(write_speed > 0);
- }
-}
diff --git a/core-new/src/volume/types.rs b/core-new/src/volume/types.rs
deleted file mode 100644
index f4b26fbbe..000000000
--- a/core-new/src/volume/types.rs
+++ /dev/null
@@ -1,685 +0,0 @@
-//! Volume type definitions
-
-use serde::{Deserialize, Serialize};
-use std::fmt;
-use std::path::PathBuf;
-use uuid::Uuid;
-
-/// Spacedrive volume identifier file content
-/// This file is created in the root of writable volumes for persistent identification
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct SpacedriveVolumeId {
- /// Unique identifier for this volume
- pub id: Uuid,
- /// When this identifier was created
- pub created: chrono::DateTime,
- /// Name of the device that created this identifier
- pub device_name: Option,
- /// Original volume name when identifier was created
- pub volume_name: String,
- /// Device ID that created this identifier
- pub device_id: Uuid,
-}
-
-/// Unique fingerprint for a storage volume
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
-pub struct VolumeFingerprint(pub String);
-
-impl VolumeFingerprint {
- /// Create a new volume fingerprint from volume properties
- /// Uses intrinsic volume characteristics for cross-device portable identification
- pub fn new(name: &str, total_bytes: u64, file_system: &str) -> Self {
- let mut hasher = blake3::Hasher::new();
- hasher.update(b"content_based:");
- hasher.update(name.as_bytes());
- hasher.update(&total_bytes.to_be_bytes());
- hasher.update(file_system.as_bytes());
- hasher.update(&(name.len() as u64).to_be_bytes());
-
- Self(hasher.finalize().to_hex().to_string())
- }
-
- /// Create a fingerprint from a Spacedrive identifier UUID (preferred method)
- /// This provides stable identification across devices, renames and remounts
- pub fn from_spacedrive_id(spacedrive_id: Uuid) -> Self {
- let mut hasher = blake3::Hasher::new();
- hasher.update(b"spacedrive_id:");
- hasher.update(spacedrive_id.as_bytes());
-
- Self(hasher.finalize().to_hex().to_string())
- }
-
- /// Generate 8-character short ID for CLI display and commands
- pub fn short_id(&self) -> String {
- self.0.chars().take(8).collect()
- }
-
- /// Generate 12-character medium ID for disambiguation
- pub fn medium_id(&self) -> String {
- self.0.chars().take(12).collect()
- }
-
- /// Create fingerprint from hex string
- pub fn from_hex(hex: impl Into) -> Self {
- Self(hex.into())
- }
-
- /// Create fingerprint from string (alias for from_hex)
- pub fn from_string(s: &str) -> Result {
- Ok(Self(s.to_string()))
- }
-
- /// Check if a string could be a short ID (8 chars, hex)
- pub fn is_short_id(s: &str) -> bool {
- s.len() == 8 && s.chars().all(|c| c.is_ascii_hexdigit())
- }
-
- /// Check if a string could be a medium ID (12 chars, hex)
- pub fn is_medium_id(s: &str) -> bool {
- s.len() == 12 && s.chars().all(|c| c.is_ascii_hexdigit())
- }
-
- /// Check if this fingerprint matches a short or medium ID
- pub fn matches_short_id(&self, short_id: &str) -> bool {
- if Self::is_short_id(short_id) {
- self.short_id() == short_id
- } else if Self::is_medium_id(short_id) {
- self.medium_id() == short_id
- } else {
- false
- }
- }
-}
-
-impl fmt::Display for VolumeFingerprint {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-/// Classification of volume types for UX and auto-tracking decisions
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
-pub enum VolumeType {
- /// Primary system drive containing OS and user data
- /// Examples: C:\ on Windows, / on Linux, Macintosh HD on macOS
- Primary,
-
- /// Dedicated user data volumes (separate from OS)
- /// Examples: /System/Volumes/Data on macOS, separate /home on Linux
- UserData,
-
- /// External or removable storage devices
- /// Examples: USB drives, external HDDs, /Volumes/* on macOS
- External,
-
- /// Secondary internal storage (additional drives/partitions)
- /// Examples: D:, E: drives on Windows, additional mounted drives
- Secondary,
-
- /// System/OS internal volumes (hidden from normal view)
- /// Examples: /System/Volumes/* on macOS, Recovery partitions
- System,
-
- /// Network attached storage
- /// Examples: SMB mounts, NFS, cloud storage
- Network,
-
- /// Unknown or unclassified volumes
- Unknown,
-}
-
-impl VolumeType {
- /// Should this volume type be auto-tracked by default?
- pub fn auto_track_by_default(&self) -> bool {
- match self {
- // Only auto-track the primary system volume
- // Users should explicitly choose to track other volumes
- VolumeType::Primary => true,
- VolumeType::UserData
- | VolumeType::External
- | VolumeType::Secondary
- | VolumeType::Network
- | VolumeType::System
- | VolumeType::Unknown => false,
- }
- }
-
- /// Should this volume be shown in the default UI view?
- pub fn show_by_default(&self) -> bool {
- !matches!(self, VolumeType::System | VolumeType::Unknown)
- }
-
- /// User-friendly display name for the volume type
- pub fn display_name(&self) -> &'static str {
- match self {
- VolumeType::Primary => "Primary Drive",
- VolumeType::UserData => "User Data",
- VolumeType::External => "External Drive",
- VolumeType::Secondary => "Secondary Drive",
- VolumeType::System => "System Volume",
- VolumeType::Network => "Network Drive",
- VolumeType::Unknown => "Unknown",
- }
- }
-
- /// Icon/indicator for CLI display
- pub fn icon(&self) -> &'static str {
- match self {
- VolumeType::Primary => "[PRI]",
- VolumeType::UserData => "[USR]",
- VolumeType::External => "[EXT]",
- VolumeType::Secondary => "[SEC]",
- VolumeType::System => "[SYS]",
- VolumeType::Network => "[NET]",
- VolumeType::Unknown => "[UNK]",
- }
- }
-}
-
-/// Events emitted by the Volume Manager when volume state changes
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub enum VolumeEvent {
- /// Emitted when a new volume is discovered
- VolumeAdded(Volume),
- /// Emitted when a volume is removed/unmounted
- VolumeRemoved { fingerprint: VolumeFingerprint },
- /// Emitted when a volume's properties are updated
- VolumeUpdated {
- fingerprint: VolumeFingerprint,
- old: VolumeInfo,
- new: VolumeInfo,
- },
- /// Emitted when a volume's speed test completes
- VolumeSpeedTested {
- fingerprint: VolumeFingerprint,
- read_speed_mbps: u64,
- write_speed_mbps: u64,
- },
- /// Emitted when a volume's mount status changes
- VolumeMountChanged {
- fingerprint: VolumeFingerprint,
- is_mounted: bool,
- },
- /// Emitted when a volume encounters an error
- VolumeError {
- fingerprint: VolumeFingerprint,
- error: String,
- },
-}
-
-/// Represents a physical or virtual storage volume in the system
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct Volume {
- /// Unique fingerprint for this volume
- pub fingerprint: VolumeFingerprint,
-
- /// Device this volume belongs to
- pub device_id: uuid::Uuid,
-
- /// Human-readable volume name
- pub name: String,
- /// Type of mount (system, external, etc)
- pub mount_type: MountType,
- /// Classification of this volume for UX decisions
- pub volume_type: VolumeType,
- /// Primary path where the volume is mounted
- pub mount_point: PathBuf,
- /// Additional mount points (for APFS volumes, etc.)
- pub mount_points: Vec,
- /// Whether the volume is currently mounted
- pub is_mounted: bool,
-
- /// Type of storage device (SSD, HDD, etc)
- pub disk_type: DiskType,
- /// Filesystem type (NTFS, EXT4, etc)
- pub file_system: FileSystem,
- /// Whether the volume is mounted read-only
- pub read_only: bool,
-
- /// Hardware identifier (platform-specific)
- pub hardware_id: Option,
- /// Current error status if any
- pub error_status: Option,
-
- // Storage information
- /// Total storage capacity in bytes
- pub total_bytes_capacity: u64,
- /// Available storage space in bytes
- pub total_bytes_available: u64,
-
- // Performance metrics (populated by speed tests)
- /// Read speed in megabytes per second
- pub read_speed_mbps: Option,
- /// Write speed in megabytes per second
- pub write_speed_mbps: Option,
-
- /// Whether this volume should be visible in default views
- pub is_user_visible: bool,
-
- /// Whether this volume should be auto-tracked
- pub auto_track_eligible: bool,
-
- /// When this volume information was last updated
- pub last_updated: chrono::DateTime,
-}
-
-/// Summary information about a volume (for updates and caching)
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct VolumeInfo {
- pub is_mounted: bool,
- pub total_bytes_available: u64,
- pub read_speed_mbps: Option,
- pub write_speed_mbps: Option,
- pub error_status: Option,
-}
-
-/// Information about a tracked volume in the database
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct TrackedVolume {
- pub id: i32,
- pub uuid: uuid::Uuid,
- pub device_id: uuid::Uuid,
- pub fingerprint: VolumeFingerprint,
- pub display_name: Option,
- pub tracked_at: chrono::DateTime,
- pub last_seen_at: chrono::DateTime,
- pub is_online: bool,
- pub total_capacity: Option,
- pub available_capacity: Option,
- pub read_speed_mbps: Option,
- pub write_speed_mbps: Option,
- pub last_speed_test_at: Option>,
- pub file_system: Option,
- pub mount_point: Option,
- pub is_removable: Option,
- pub is_network_drive: Option,
- pub device_model: Option,
- pub volume_type: String,
- pub is_user_visible: Option,
- pub auto_track_eligible: Option,
-}
-
-impl From<&Volume> for VolumeInfo {
- fn from(volume: &Volume) -> Self {
- Self {
- is_mounted: volume.is_mounted,
- total_bytes_available: volume.total_bytes_available,
- read_speed_mbps: volume.read_speed_mbps,
- write_speed_mbps: volume.write_speed_mbps,
- error_status: volume.error_status.clone(),
- }
- }
-}
-
-impl TrackedVolume {
- /// Convert a TrackedVolume back to a Volume for display purposes
- /// This is used for offline volumes that aren't currently detected
- pub fn to_offline_volume(&self) -> Volume {
- use std::path::PathBuf;
-
- Volume {
- fingerprint: self.fingerprint.clone(),
- device_id: self.device_id,
- name: self
- .display_name
- .clone()
- .unwrap_or_else(|| "Unknown".to_string()),
- mount_type: crate::volume::types::MountType::External, // Default for tracked volumes
- volume_type: match self.volume_type.as_str() {
- "Primary" => VolumeType::Primary,
- "UserData" => VolumeType::UserData,
- "External" => VolumeType::External,
- "Secondary" => VolumeType::Secondary,
- "System" => VolumeType::System,
- "Network" => VolumeType::Network,
- _ => VolumeType::Unknown,
- },
- mount_point: PathBuf::from(
- self.mount_point
- .clone()
- .unwrap_or_else(|| "Not connected".to_string()),
- ),
- mount_points: vec![], // Not available for offline volumes
- disk_type: crate::volume::types::DiskType::Unknown,
- file_system: crate::volume::types::FileSystem::from_string(
- &self
- .file_system
- .clone()
- .unwrap_or_else(|| "Unknown".to_string()),
- ),
- total_bytes_capacity: self.total_capacity.unwrap_or(0),
- total_bytes_available: self.available_capacity.unwrap_or(0),
- read_only: false, // Assume not read-only for tracked volumes
- hardware_id: self.device_model.clone(),
- is_mounted: false, // Offline volumes are not mounted
- error_status: None,
- read_speed_mbps: self.read_speed_mbps.map(|s| s as u64),
- write_speed_mbps: self.write_speed_mbps.map(|s| s as u64),
- last_updated: self.last_seen_at,
- is_user_visible: self.is_user_visible.unwrap_or(true),
- auto_track_eligible: self.auto_track_eligible.unwrap_or(false),
- }
- }
-}
-
-impl Volume {
- /// Create a new Volume instance
- pub fn new(
- device_id: uuid::Uuid,
- name: String,
- mount_type: MountType,
- volume_type: VolumeType,
- mount_point: PathBuf,
- additional_mount_points: Vec,
- disk_type: DiskType,
- file_system: FileSystem,
- total_bytes_capacity: u64,
- total_bytes_available: u64,
- read_only: bool,
- hardware_id: Option,
- fingerprint: VolumeFingerprint, // Accept pre-computed fingerprint
- ) -> Self {
- Self {
- fingerprint,
- device_id,
- name,
- mount_type,
- volume_type,
- mount_point,
- mount_points: additional_mount_points,
- is_mounted: true,
- disk_type,
- file_system,
- total_bytes_capacity,
- total_bytes_available,
- read_only,
- hardware_id,
- error_status: None,
- read_speed_mbps: None,
- write_speed_mbps: None,
- auto_track_eligible: volume_type.auto_track_by_default(),
- is_user_visible: volume_type.show_by_default(),
- last_updated: chrono::Utc::now(),
- }
- }
-
- /// Update volume information
- pub fn update_info(&mut self, info: VolumeInfo) {
- self.is_mounted = info.is_mounted;
- self.total_bytes_available = info.total_bytes_available;
- self.read_speed_mbps = info.read_speed_mbps;
- self.write_speed_mbps = info.write_speed_mbps;
- self.error_status = info.error_status;
- self.last_updated = chrono::Utc::now();
- }
-
- /// Check if this volume supports fast copy operations (CoW)
- pub fn supports_fast_copy(&self) -> bool {
- matches!(
- self.file_system,
- FileSystem::APFS | FileSystem::Btrfs | FileSystem::ZFS | FileSystem::ReFS
- )
- }
-
- /// Get the optimal chunk size for copying to/from this volume
- pub fn optimal_chunk_size(&self) -> usize {
- match self.disk_type {
- DiskType::SSD => 1024 * 1024, // 1MB for SSDs
- DiskType::HDD => 256 * 1024, // 256KB for HDDs
- DiskType::Unknown => 64 * 1024, // 64KB default
- }
- }
-
- /// Estimate copy speed between this and another volume
- pub fn estimate_copy_speed(&self, other: &Volume) -> Option {
- let self_read = self.read_speed_mbps?;
- let other_write = other.write_speed_mbps?;
-
- // Bottleneck is the slower of read or write speed
- Some(self_read.min(other_write))
- }
-
- /// Check if a path is contained within this volume
- pub fn contains_path(&self, path: &PathBuf) -> bool {
- // Check primary mount point
- if path.starts_with(&self.mount_point) {
- return true;
- }
-
- // Check additional mount points
- for mount_point in &self.mount_points {
- if path.starts_with(mount_point) {
- return true;
- }
- }
-
- false
- }
-}
-
-/// Represents the type of physical storage device
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
-pub enum DiskType {
- /// Solid State Drive
- SSD,
- /// Hard Disk Drive
- HDD,
- /// Unknown or virtual disk type
- Unknown,
-}
-
-impl fmt::Display for DiskType {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- DiskType::SSD => write!(f, "SSD"),
- DiskType::HDD => write!(f, "HDD"),
- DiskType::Unknown => write!(f, "Unknown"),
- }
- }
-}
-
-impl DiskType {
- pub fn from_string(disk_type: &str) -> Self {
- match disk_type.to_uppercase().as_str() {
- "SSD" => Self::SSD,
- "HDD" => Self::HDD,
- _ => Self::Unknown,
- }
- }
-}
-
-/// Represents the filesystem type of the volume
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
-pub enum FileSystem {
- /// Windows NTFS filesystem
- NTFS,
- /// FAT32 filesystem
- FAT32,
- /// Linux EXT4 filesystem
- EXT4,
- /// Apple APFS filesystem
- APFS,
- /// ExFAT filesystem
- ExFAT,
- /// Btrfs filesystem (Linux)
- Btrfs,
- /// ZFS filesystem
- ZFS,
- /// Windows ReFS filesystem
- ReFS,
- /// Other/unknown filesystem type
- Other(String),
-}
-
-impl fmt::Display for FileSystem {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- FileSystem::NTFS => write!(f, "NTFS"),
- FileSystem::FAT32 => write!(f, "FAT32"),
- FileSystem::EXT4 => write!(f, "EXT4"),
- FileSystem::APFS => write!(f, "APFS"),
- FileSystem::ExFAT => write!(f, "ExFAT"),
- FileSystem::Btrfs => write!(f, "Btrfs"),
- FileSystem::ZFS => write!(f, "ZFS"),
- FileSystem::ReFS => write!(f, "ReFS"),
- FileSystem::Other(name) => write!(f, "{}", name),
- }
- }
-}
-
-impl FileSystem {
- pub fn from_string(fs: &str) -> Self {
- match fs.to_uppercase().as_str() {
- "NTFS" => Self::NTFS,
- "FAT32" => Self::FAT32,
- "EXT4" => Self::EXT4,
- "APFS" => Self::APFS,
- "EXFAT" => Self::ExFAT,
- "BTRFS" => Self::Btrfs,
- "ZFS" => Self::ZFS,
- "REFS" => Self::ReFS,
- other => Self::Other(other.to_string()),
- }
- }
-
- /// Check if this filesystem supports reflinks/clones
- pub fn supports_reflink(&self) -> bool {
- matches!(self, Self::APFS | Self::Btrfs | Self::ZFS | Self::ReFS)
- }
-
- /// Check if this filesystem supports sendfile optimization
- pub fn supports_sendfile(&self) -> bool {
- matches!(self, Self::EXT4 | Self::Btrfs | Self::ZFS | Self::NTFS)
- }
-}
-
-/// Represents how the volume is mounted in the system
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
-pub enum MountType {
- /// System/boot volume
- System,
- /// External/removable volume
- External,
- /// Network-attached volume
- Network,
- /// Virtual/container volume
- Virtual,
-}
-
-impl fmt::Display for MountType {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- MountType::System => write!(f, "System"),
- MountType::External => write!(f, "External"),
- MountType::Network => write!(f, "Network"),
- MountType::Virtual => write!(f, "Virtual"),
- }
- }
-}
-
-impl MountType {
- pub fn from_string(mount_type: &str) -> Self {
- match mount_type.to_uppercase().as_str() {
- "SYSTEM" => Self::System,
- "EXTERNAL" => Self::External,
- "NETWORK" => Self::Network,
- "VIRTUAL" => Self::Virtual,
- _ => Self::System,
- }
- }
-}
-
-/// Configuration for volume detection and monitoring
-#[derive(Debug, Clone)]
-pub struct VolumeDetectionConfig {
- /// Whether to include system volumes
- pub include_system: bool,
- /// Whether to include virtual volumes
- pub include_virtual: bool,
- /// Whether to run speed tests on discovery
- pub run_speed_test: bool,
- /// How often to refresh volume information (in seconds)
- pub refresh_interval_secs: u64,
-}
-
-impl Default for VolumeDetectionConfig {
- fn default() -> Self {
- Self {
- include_system: true,
- include_virtual: false,
- run_speed_test: false, // Expensive operation, off by default
- refresh_interval_secs: 30,
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_volume_fingerprint() {
- let volume = Volume::new(
- uuid::Uuid::new_v4(),
- "Test Volume".to_string(),
- MountType::External,
- VolumeType::External,
- PathBuf::from("/mnt/test"),
- vec![],
- DiskType::SSD,
- FileSystem::EXT4,
- 1000000000,
- 500000000,
- false,
- Some("test-hw-id".to_string()),
- VolumeFingerprint::new("Test", 500000000, "ext4"),
- );
-
- // Test basic fingerprint creation
- let fingerprint = VolumeFingerprint::new(
- "Test Volume",
- 1000000000, // 1GB
- "ext4",
- );
- assert!(!fingerprint.0.is_empty());
-
- // Test Spacedrive ID fingerprint
- let spacedrive_id = Uuid::new_v4();
- let spacedrive_fingerprint = VolumeFingerprint::from_spacedrive_id(spacedrive_id);
- assert!(!spacedrive_fingerprint.0.is_empty());
- assert_ne!(fingerprint, spacedrive_fingerprint);
- }
-
- #[test]
- fn test_volume_contains_path() {
- let volume = Volume::new(
- uuid::Uuid::new_v4(),
- "Test".to_string(),
- MountType::System,
- VolumeType::System,
- PathBuf::from("/home"),
- vec![PathBuf::from("/home"), PathBuf::from("/mnt/home")],
- DiskType::SSD,
- FileSystem::EXT4,
- 1000000,
- 500000,
- false,
- None,
- VolumeFingerprint::new("Test", 1000000, "ext4"),
- );
-
- assert!(volume.contains_path(&PathBuf::from("/home/user/file.txt")));
- assert!(volume.contains_path(&PathBuf::from("/mnt/home/user/file.txt")));
- assert!(!volume.contains_path(&PathBuf::from("/var/log/file.txt")));
- }
-
- #[test]
- fn test_filesystem_capabilities() {
- assert!(FileSystem::APFS.supports_reflink());
- assert!(FileSystem::Btrfs.supports_reflink());
- assert!(!FileSystem::FAT32.supports_reflink());
-
- assert!(FileSystem::EXT4.supports_sendfile());
- assert!(!FileSystem::FAT32.supports_sendfile());
- }
-}
diff --git a/core-new/.scripts/combine.sh b/core/.scripts/combine.sh
similarity index 100%
rename from core-new/.scripts/combine.sh
rename to core/.scripts/combine.sh
diff --git a/core-new/test_daemon.sh b/core/.scripts/test_daemon.sh
similarity index 100%
rename from core-new/test_daemon.sh
rename to core/.scripts/test_daemon.sh
diff --git a/core-new/update_spacedrive.sh b/core/.scripts/update_spacedrive.sh
similarity index 95%
rename from core-new/update_spacedrive.sh
rename to core/.scripts/update_spacedrive.sh
index 74a51920a..fe97a921e 100644
--- a/core-new/update_spacedrive.sh
+++ b/core/.scripts/update_spacedrive.sh
@@ -1,7 +1,7 @@
#!/bin/bash
# --- Configuration ---
-PROJECT_ROOT="/Users/jamespine/Projects/spacedrive/core-new"
+PROJECT_ROOT="/Users/jamespine/Projects/spacedrive/core"
INSTALL_DIR="/usr/local/bin"
BINARY_NAME="spacedrive"
diff --git a/core-new/AGENTS.md b/core/AGENTS.md
similarity index 100%
rename from core-new/AGENTS.md
rename to core/AGENTS.md
diff --git a/core-new/Cargo.lock b/core/Cargo.lock
similarity index 99%
rename from core-new/Cargo.lock
rename to core/Cargo.lock
index 0576dfabd14dabab381d2dd43182349b9cac0d7d..fa1f43e960374f5fbee9c0fce478e5f6f6ec6eac 100644
GIT binary patch
delta 48
zcmV-00MGy0s}Itv4}gRLv;u{dmrD;0S}k0l>(2KVVeRcmrB+G42N@;0=ILP
G17|<<(Gy?*
delta 53
zcmcb#n*Y{n{)QIDEljObS#
diff --git a/core/Cargo.toml b/core/Cargo.toml
index d40d2d121..cbb8fc0ef 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -1,161 +1,166 @@
[package]
+edition = "2021"
name = "sd-core"
-version = "0.5.0"
-
-authors = ["Spacedrive Technology Inc "]
-description = "Virtual distributed filesystem engine that powers Spacedrive."
-edition.workspace = true
-license.workspace = true
-repository.workspace = true
-rust-version.workspace = true
+version = "0.1.0"
[features]
default = []
-# This feature allows features to be disabled when the Core is running on mobile.
-mobile = []
-# This feature controls whether the Spacedrive Core contains functionality which requires FFmpeg.
-ai = ["dep:sd-ai"]
-ffmpeg = ["sd-core-heavy-lifting/ffmpeg", "sd-media-metadata/ffmpeg"]
-heif = ["sd-images/heif"]
+# FFmpeg support for video thumbnails
+ffmpeg = ["dep:sd-ffmpeg"]
+
+[workspace]
+members = ["benchmarks", "crates/*"]
[dependencies]
-# Inner Core Sub-crates
-sd-core-cloud-services = { path = "./crates/cloud-services" }
-sd-core-file-path-helper = { path = "./crates/file-path-helper" }
-sd-core-heavy-lifting = { path = "./crates/heavy-lifting" }
-sd-core-indexer-rules = { path = "./crates/indexer-rules" }
-sd-core-prisma-helpers = { path = "./crates/prisma-helpers" }
-sd-core-sync = { path = "./crates/sync" }
+# Async runtime
+async-trait = "0.1"
+futures = "0.3"
+tokio = { version = "1.40", features = ["full"] }
-# Spacedrive Sub-crates
-sd-actors = { path = "../crates/actors" }
-sd-ai = { path = "../crates/ai", optional = true }
-sd-crypto = { path = "../crates/crypto" }
-sd-ffmpeg = { path = "../crates/ffmpeg", optional = true }
-sd-file-ext = { path = "../crates/file-ext" }
-sd-images = { path = "../crates/images", features = ["rspc", "serde", "specta"] }
-sd-media-metadata = { path = "../crates/media-metadata" }
-sd-old-p2p = { path = "../crates/old-p2p", features = ["specta"] }
-sd-old-p2p-block = { path = "../crates/old-p2p/crates/block" }
-sd-old-p2p-proto = { path = "../crates/old-p2p/crates/proto" }
-sd-old-p2p-tunnel = { path = "../crates/old-p2p/crates/tunnel" }
-sd-prisma = { path = "../crates/prisma" }
-sd-sync = { path = "../crates/sync" }
-sd-task-system = { path = "../crates/task-system" }
-sd-utils = { path = "../crates/utils" }
-
-# Workspace dependencies
-async-channel = { workspace = true }
-async-stream = { workspace = true }
-async-trait = { workspace = true }
-axum = { workspace = true, features = ["ws"] }
-base64 = { workspace = true }
-blake3 = { workspace = true }
-bytes = { workspace = true }
-chrono = { workspace = true, features = ["serde"] }
-futures = { workspace = true }
-futures-concurrency = { workspace = true }
-hyper = { workspace = true, features = ["client", "http1", "server"] }
-image = { workspace = true }
-itertools = { workspace = true }
-libc = { workspace = true }
-normpath = { workspace = true, features = ["localization"] }
-pin-project-lite = { workspace = true }
-prisma-client-rust = { workspace = true, features = ["rspc"] }
-regex = { workspace = true }
-reqwest = { workspace = true, features = ["json", "native-tls-vendored"] }
-rmp-serde = { workspace = true }
-rmpv = { workspace = true }
-rspc = { workspace = true, features = ["alpha", "axum", "chrono", "unstable", "uuid"] }
-sd-cloud-schema = { workspace = true }
-serde = { workspace = true, features = ["derive", "rc"] }
-serde_json = { workspace = true }
-specta = { workspace = true }
-strum = { workspace = true, features = ["derive"] }
-strum_macros = { workspace = true }
-tempfile = { workspace = true }
-thiserror = { workspace = true }
-tokio-stream = { workspace = true, features = ["fs"] }
-tokio-util = { workspace = true, features = ["io"] }
-tracing = { workspace = true }
-tracing-subscriber = { workspace = true, features = ["env-filter"] }
-uuid = { workspace = true, features = ["serde", "v4", "v7"] }
-
-# Specific Core dependencies
-async-recursion = "1.1"
-base91 = "0.1.0"
-ctor = "0.2.8"
-directories = "5.0"
-flate2 = "1.0"
-fsevent = "2.1.2"
-hex = "0.4.3"
-hostname = "0.4.0"
-http-body = "1.0"
-http-range = "0.1.5"
-hyper-util = { version = "0.1.9", features = ["tokio"] }
-int-enum = "0.5" # Update blocked due to API breaking changes
-mini-moka = "0.10.3"
-once_cell = "1.19.0"
-serde-hashkey = "0.4.5"
-serde_repr = "0.1.19"
-serde_with = "3.8"
-slotmap = "1.0"
-sysinfo = "0.29.11" # Update blocked due to API breaking changes
-tar = "0.4.41"
-tower-service = "0.3.2"
-tracing-appender = "0.2.3"
-whoami = "1.5.2"
-
-[dependencies.tokio]
-features = ["io-util", "macros", "process", "rt-multi-thread", "sync", "time"]
-workspace = true
-
-[dependencies.notify]
-default-features = false
-features = ["macos_fsevent"]
-git = "https://github.com/notify-rs/notify.git"
-rev = "c3929ed114"
-
-# Override features of transitive dependencies
-[dependencies.openssl]
-features = ["vendored"]
-version = "0.10.66"
-[dependencies.openssl-sys]
-features = ["vendored"]
-version = "0.9.103"
-
-# Platform-specific dependencies
-[target.'cfg(target_os = "macos")'.dependencies]
-plist = "1.6"
-trash = "5.1"
-
-[target.'cfg(target_os = "linux")'.dependencies]
-inotify = "0.11.0"
-trash = "5.1"
-
-[target.'cfg(target_os = "windows")'.dependencies]
-trash = "5.1"
-windows = { features = [
- "Win32_Storage_FileSystem",
- "Win32_System_IO",
- "Win32_System_Ioctl",
- "Win32_System_WindowsProgramming"
-], version = "0.58" }
-
-[target.'cfg(target_os = "ios")'.dependencies]
-icrate = { version = "0.1.2", features = [
- "Foundation",
- "Foundation_NSFileManager",
- "Foundation_NSNumber",
- "Foundation_NSString"
+# Database
+sea-orm = { version = "1.1", features = [
+ "macros",
+ "runtime-tokio-rustls",
+ "sqlx-sqlite",
+ "uuid",
+ "with-chrono",
+ "with-json"
] }
+sea-orm-migration = { version = "1.1", features = ["runtime-tokio-rustls", "sqlx-sqlite"] }
+sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite"] }
+
+# API (temporarily disabled)
+# axum = "0.7"
+# async-graphql = "7.0"
+# async-graphql-axum = "7.0"
+
+# Serialization
+int-enum = "1.1"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+strum = { version = "0.26", features = ["derive"] }
+toml = "0.8"
+
+# Error handling
+anyhow = "1.0"
+thiserror = "1.0"
+
+
+# File operations
+blake3 = "1.5" # Content addressing
+hex = "0.4" # Hex encoding for volume fingerprints
+notify = "6.1" # File system watching
+sha2 = "0.10" # SHA-256 hashing for CAS IDs
+
+# Logging
+tracing = "0.1"
+tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+
+# Indexer rules engine
+futures-concurrency = "7.6"
+gix-ignore = { version = "0.11", features = ["serde"] }
+globset = { version = "0.4", features = ["serde1"] }
+
+# Job system dependencies
+inventory = "0.3" # Automatic job registration
+rmp = "0.8" # MessagePack core types
+rmp-serde = "1.3" # MessagePack serialization for job state
+sd-task-system = { path = "crates/task-system" }
+spacedrive-jobs-derive = { path = "crates/spacedrive-jobs-derive" } # Job derive macros
+
+# Media processing dependencies
+image = "0.25"
+sd-ffmpeg = { path = "crates/ffmpeg", optional = true }
+sd-images = { path = "crates/images" }
+tokio-rustls = "0.26"
+webp = "0.3"
+
+# Networking
+# Iroh P2P networking
+iroh = "0.28"
+iroh-blobs = "0.28"
+iroh-gossip = "0.28"
+iroh-net = "0.28"
+
+# Serialization for protocols
+serde_cbor = "0.11"
+
+# Cryptography for signing (backward compatibility)
+ed25519-dalek = "2.1"
+
+# Legacy networking (kept for compatibility during transition)
+aes-gcm = "0.10" # AES-GCM encryption for secure storage
+argon2 = "0.5" # Password derivation
+async-stream = "0.3" # File streaming
+backoff = "0.4" # Retry logic
+bincode = "2.0.0-rc.3" # Efficient encoding
+mdns-sd = "0.13" # mDNS service discovery (DEPRECATED - use libp2p DHT)
+ring = "0.16" # Crypto primitives
+snow = "0.9" # Noise Protocol encryption (DEPRECATED - use libp2p noise)
+# futures-util = "0.3" # WebSocket utilities (disabled for now)
+rcgen = "0.11" # Certificate generation (DEPRECATED - use libp2p noise)
+rustls = { version = "0.23", features = [
+ "aws_lc_rs"
+] } # TLS implementation (DEPRECATED - use libp2p noise)
+tokio-stream = "0.1" # Async streams
+
+# BIP39 wordlist support
+bip39 = "2.0"
+
+# Additional cryptography
+chacha20poly1305 = "0.10" # Authenticated encryption for chunk-level security
+hkdf = "0.12" # Key derivation function for session keys
+hmac = "0.12"
+x25519-dalek = "2.0"
+
+# Network utilities
+if-watch = "3.0"
+local-ip-address = "0.5"
+# colored already defined above
+
+# Utils
+chrono = { version = "0.4", features = ["serde"] }
+dirs = "5.0"
+once_cell = "1.20"
+rand = "0.8" # Random number generation for secure delete
+tempfile = "3.14" # Temporary directories for testing
+uuid = { version = "1.11", features = ["serde", "v4", "v5", "v7"] }
+whoami = "1.5"
+
+# Secure storage
+keyring = "3.6"
+
+# CLI dependencies
+clap = { version = "4.5", features = ["derive", "env"] }
+colored = "2.1"
+comfy-table = "7.1"
+console = "0.15"
+dialoguer = "0.11"
+indicatif = "0.17"
+owo-colors = "4.1"
+supports-color = "3.0"
+
+[build-dependencies]
+colored = "2.1"
+comfy-table = "7.1"
+console = "0.15"
+crossterm = "0.28"
+dialoguer = "0.11"
+indicatif = "0.17"
+owo-colors = "4.1"
+ratatui = "0.29"
+supports-color = "3.0"
+vergen = { version = "8", features = ["cargo", "git", "gitcl"] }
+
+# Platform specific
+[target.'cfg(unix)'.dependencies]
+libc = "0.2"
+
+[[bin]]
+name = "spacedrive"
+path = "src/bin/cli.rs"
-[target.'cfg(target_os = "android")'.dependencies]
-tracing-android = "0.2.0"
[dev-dependencies]
-# Workspace dependencies
-tracing-test = { workspace = true }
-
-# Specific Core dependencies
-boxcar = "0.2.5"
+pretty_assertions = "1.4"
+tempfile = "3.14"
diff --git a/core-new/README.md b/core/README.md
similarity index 99%
rename from core-new/README.md
rename to core/README.md
index c14b61e47..2898235e3 100644
--- a/core-new/README.md
+++ b/core/README.md
@@ -293,7 +293,7 @@ Currently working features:
```bash
# Clone and build
git clone https://github.com/spacedriveapp/spacedrive
-cd spacedrive/core-new
+cd spacedrive/core
cargo build --release
# Try the CLI
diff --git a/core/benchmarks/Cargo.toml b/core/benchmarks/Cargo.toml
new file mode 100644
index 000000000..1fe95233b
--- /dev/null
+++ b/core/benchmarks/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+edition = "2021"
+name = "sd-bench"
+version = "0.1.0"
+
+[dependencies]
+anyhow = "1.0"
+async-trait = "0.1"
+blake3 = "1.5"
+chrono = { version = "0.4", features = ["serde"] }
+clap = { version = "4.5", features = ["derive", "env"] }
+dirs = "5.0"
+humantime = "2.1"
+humantime-serde = "1.1"
+indicatif = "0.17"
+rand = "0.8"
+regex = "1.10"
+sd-core = { path = ".." }
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+serde_with = { version = "3.9", features = ["json"] }
+serde_yaml = "0.9"
+sysinfo = { version = "0.30", default-features = false, features = ["multithread"] }
+tempfile = "3.14"
+tokio = { version = "1.40", features = ["full"] }
+tracing = "0.1"
+tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+uuid = { version = "1.11", features = ["serde", "v4"] }
+walkdir = "2.5"
+
+[lib]
+name = "sd_bench"
+path = "src/lib.rs"
+
+[[bin]]
+name = "sd-bench"
+path = "src/bin/sd-bench-new.rs"
diff --git a/core-new/benchmarks/recipes/shape_large.yaml b/core/benchmarks/recipes/shape_large.yaml
similarity index 100%
rename from core-new/benchmarks/recipes/shape_large.yaml
rename to core/benchmarks/recipes/shape_large.yaml
diff --git a/core-new/benchmarks/recipes/shape_medium.yaml b/core/benchmarks/recipes/shape_medium.yaml
similarity index 100%
rename from core-new/benchmarks/recipes/shape_medium.yaml
rename to core/benchmarks/recipes/shape_medium.yaml
diff --git a/core-new/benchmarks/recipes/shape_small.yaml b/core/benchmarks/recipes/shape_small.yaml
similarity index 100%
rename from core-new/benchmarks/recipes/shape_small.yaml
rename to core/benchmarks/recipes/shape_small.yaml
diff --git a/core-new/benchmarks/results/shape_large-aggregation-hdd.json b/core/benchmarks/results/shape_large-aggregation-hdd.json
similarity index 100%
rename from core-new/benchmarks/results/shape_large-aggregation-hdd.json
rename to core/benchmarks/results/shape_large-aggregation-hdd.json
diff --git a/core-new/benchmarks/results/shape_large-aggregation-ssd.json b/core/benchmarks/results/shape_large-aggregation-ssd.json
similarity index 90%
rename from core-new/benchmarks/results/shape_large-aggregation-ssd.json
rename to core/benchmarks/results/shape_large-aggregation-ssd.json
index 51e98565f..7cb6dc483 100644
--- a/core-new/benchmarks/results/shape_large-aggregation-ssd.json
+++ b/core/benchmarks/results/shape_large-aggregation-ssd.json
@@ -21,7 +21,7 @@
},
"id": "6a501e72-da12-43bc-a4f6-e336ec7a1d56",
"location_paths": [
- "/Users/jamespine/Projects/spacedrive/core-new/benchdata/shape_large"
+ "/Users/jamespine/Projects/spacedrive/core/benchdata/shape_large"
],
"recipe_name": "shape_large",
"timestamp_utc": "2025-08-10T09:12:02.347142+00:00"
diff --git a/core-new/benchmarks/results/shape_large-content_identification-hdd.json b/core/benchmarks/results/shape_large-content_identification-hdd.json
similarity index 100%
rename from core-new/benchmarks/results/shape_large-content_identification-hdd.json
rename to core/benchmarks/results/shape_large-content_identification-hdd.json
diff --git a/core-new/benchmarks/results/shape_large-content_identification-ssd.json b/core/benchmarks/results/shape_large-content_identification-ssd.json
similarity index 90%
rename from core-new/benchmarks/results/shape_large-content_identification-ssd.json
rename to core/benchmarks/results/shape_large-content_identification-ssd.json
index e5bec83d3..5a9156209 100644
--- a/core-new/benchmarks/results/shape_large-content_identification-ssd.json
+++ b/core/benchmarks/results/shape_large-content_identification-ssd.json
@@ -21,7 +21,7 @@
},
"id": "8d825ffa-d866-4fe7-b8fb-52559e63191e",
"location_paths": [
- "/Users/jamespine/Projects/spacedrive/core-new/benchdata/shape_large"
+ "/Users/jamespine/Projects/spacedrive/core/benchdata/shape_large"
],
"recipe_name": "shape_large",
"timestamp_utc": "2025-08-10T09:35:52.292540+00:00"
diff --git a/core-new/benchmarks/results/shape_large-indexing_discovery-hdd.json b/core/benchmarks/results/shape_large-indexing_discovery-hdd.json
similarity index 100%
rename from core-new/benchmarks/results/shape_large-indexing_discovery-hdd.json
rename to core/benchmarks/results/shape_large-indexing_discovery-hdd.json
diff --git a/core-new/benchmarks/results/shape_large-indexing_discovery-ssd.json b/core/benchmarks/results/shape_large-indexing_discovery-ssd.json
similarity index 90%
rename from core-new/benchmarks/results/shape_large-indexing_discovery-ssd.json
rename to core/benchmarks/results/shape_large-indexing_discovery-ssd.json
index a82d6d74d..489cdf5d9 100644
--- a/core-new/benchmarks/results/shape_large-indexing_discovery-ssd.json
+++ b/core/benchmarks/results/shape_large-indexing_discovery-ssd.json
@@ -21,7 +21,7 @@
},
"id": "942a9485-bfc7-4af6-9e98-9544f55b9642",
"location_paths": [
- "/Users/jamespine/Projects/spacedrive/core-new/benchdata/shape_large"
+ "/Users/jamespine/Projects/spacedrive/core/benchdata/shape_large"
],
"recipe_name": "shape_large",
"timestamp_utc": "2025-08-10T09:10:43.892804+00:00"
diff --git a/core-new/benchmarks/results/shape_medium-aggregation-hdd.json b/core/benchmarks/results/shape_medium-aggregation-hdd.json
similarity index 100%
rename from core-new/benchmarks/results/shape_medium-aggregation-hdd.json
rename to core/benchmarks/results/shape_medium-aggregation-hdd.json
diff --git a/core-new/benchmarks/results/shape_medium-aggregation-ssd.json b/core/benchmarks/results/shape_medium-aggregation-ssd.json
similarity index 90%
rename from core-new/benchmarks/results/shape_medium-aggregation-ssd.json
rename to core/benchmarks/results/shape_medium-aggregation-ssd.json
index ee3adf3fc..34001b9ba 100644
--- a/core-new/benchmarks/results/shape_medium-aggregation-ssd.json
+++ b/core/benchmarks/results/shape_medium-aggregation-ssd.json
@@ -21,7 +21,7 @@
},
"id": "7cb7172d-a51a-4783-a3c8-ff5c82024ba3",
"location_paths": [
- "/Users/jamespine/Projects/spacedrive/core-new/benchdata/shape_medium"
+ "/Users/jamespine/Projects/spacedrive/core/benchdata/shape_medium"
],
"recipe_name": "shape_medium",
"timestamp_utc": "2025-08-10T09:12:12.291950+00:00"
diff --git a/core-new/benchmarks/results/shape_medium-content_identification-hdd.json b/core/benchmarks/results/shape_medium-content_identification-hdd.json
similarity index 100%
rename from core-new/benchmarks/results/shape_medium-content_identification-hdd.json
rename to core/benchmarks/results/shape_medium-content_identification-hdd.json
diff --git a/core-new/benchmarks/results/shape_medium-content_identification-ssd.json b/core/benchmarks/results/shape_medium-content_identification-ssd.json
similarity index 90%
rename from core-new/benchmarks/results/shape_medium-content_identification-ssd.json
rename to core/benchmarks/results/shape_medium-content_identification-ssd.json
index adaa98a52..4afc7e824 100644
--- a/core-new/benchmarks/results/shape_medium-content_identification-ssd.json
+++ b/core/benchmarks/results/shape_medium-content_identification-ssd.json
@@ -21,7 +21,7 @@
},
"id": "62872b39-d91b-4195-89e5-d63986c516ea",
"location_paths": [
- "/Users/jamespine/Projects/spacedrive/core-new/benchdata/shape_medium"
+ "/Users/jamespine/Projects/spacedrive/core/benchdata/shape_medium"
],
"recipe_name": "shape_medium",
"timestamp_utc": "2025-08-10T09:40:28.247578+00:00"
diff --git a/core-new/benchmarks/results/shape_medium-indexing_discovery-hdd.json b/core/benchmarks/results/shape_medium-indexing_discovery-hdd.json
similarity index 100%
rename from core-new/benchmarks/results/shape_medium-indexing_discovery-hdd.json
rename to core/benchmarks/results/shape_medium-indexing_discovery-hdd.json
diff --git a/core-new/benchmarks/results/shape_medium-indexing_discovery-ssd.json b/core/benchmarks/results/shape_medium-indexing_discovery-ssd.json
similarity index 90%
rename from core-new/benchmarks/results/shape_medium-indexing_discovery-ssd.json
rename to core/benchmarks/results/shape_medium-indexing_discovery-ssd.json
index 836a8b12b..5dca3979c 100644
--- a/core-new/benchmarks/results/shape_medium-indexing_discovery-ssd.json
+++ b/core/benchmarks/results/shape_medium-indexing_discovery-ssd.json
@@ -21,7 +21,7 @@
},
"id": "e3a3c71e-1850-4f71-9d4f-5ef8b9a94df5",
"location_paths": [
- "/Users/jamespine/Projects/spacedrive/core-new/benchdata/shape_medium"
+ "/Users/jamespine/Projects/spacedrive/core/benchdata/shape_medium"
],
"recipe_name": "shape_medium",
"timestamp_utc": "2025-08-10T09:10:54.220721+00:00"
diff --git a/core-new/benchmarks/results/shape_small-aggregation-hdd.json b/core/benchmarks/results/shape_small-aggregation-hdd.json
similarity index 100%
rename from core-new/benchmarks/results/shape_small-aggregation-hdd.json
rename to core/benchmarks/results/shape_small-aggregation-hdd.json
diff --git a/core-new/benchmarks/results/shape_small-aggregation-ssd.json b/core/benchmarks/results/shape_small-aggregation-ssd.json
similarity index 90%
rename from core-new/benchmarks/results/shape_small-aggregation-ssd.json
rename to core/benchmarks/results/shape_small-aggregation-ssd.json
index 109e072c6..cb16c99c2 100644
--- a/core-new/benchmarks/results/shape_small-aggregation-ssd.json
+++ b/core/benchmarks/results/shape_small-aggregation-ssd.json
@@ -21,7 +21,7 @@
},
"id": "28911e81-983c-4680-9cfd-287b91589650",
"location_paths": [
- "/Users/jamespine/Projects/spacedrive/core-new/benchdata/shape_small"
+ "/Users/jamespine/Projects/spacedrive/core/benchdata/shape_small"
],
"recipe_name": "shape_small",
"timestamp_utc": "2025-08-10T09:12:16.491885+00:00"
diff --git a/core-new/benchmarks/results/shape_small-content_identification-hdd.json b/core/benchmarks/results/shape_small-content_identification-hdd.json
similarity index 100%
rename from core-new/benchmarks/results/shape_small-content_identification-hdd.json
rename to core/benchmarks/results/shape_small-content_identification-hdd.json
diff --git a/core-new/benchmarks/results/shape_small-content_identification-ssd.json b/core/benchmarks/results/shape_small-content_identification-ssd.json
similarity index 90%
rename from core-new/benchmarks/results/shape_small-content_identification-ssd.json
rename to core/benchmarks/results/shape_small-content_identification-ssd.json
index 82301139a..d9cc4b67e 100644
--- a/core-new/benchmarks/results/shape_small-content_identification-ssd.json
+++ b/core/benchmarks/results/shape_small-content_identification-ssd.json
@@ -21,7 +21,7 @@
},
"id": "cfe4988b-bfa4-4b9f-9632-4bc02d9738c7",
"location_paths": [
- "/Users/jamespine/Projects/spacedrive/core-new/benchdata/shape_small"
+ "/Users/jamespine/Projects/spacedrive/core/benchdata/shape_small"
],
"recipe_name": "shape_small",
"timestamp_utc": "2025-08-10T09:41:41.304584+00:00"
diff --git a/core-new/benchmarks/results/shape_small-indexing_discovery-hdd.json b/core/benchmarks/results/shape_small-indexing_discovery-hdd.json
similarity index 100%
rename from core-new/benchmarks/results/shape_small-indexing_discovery-hdd.json
rename to core/benchmarks/results/shape_small-indexing_discovery-hdd.json
diff --git a/core-new/benchmarks/results/shape_small-indexing_discovery-ssd.json b/core/benchmarks/results/shape_small-indexing_discovery-ssd.json
similarity index 90%
rename from core-new/benchmarks/results/shape_small-indexing_discovery-ssd.json
rename to core/benchmarks/results/shape_small-indexing_discovery-ssd.json
index 1199a0474..cc8416da0 100644
--- a/core-new/benchmarks/results/shape_small-indexing_discovery-ssd.json
+++ b/core/benchmarks/results/shape_small-indexing_discovery-ssd.json
@@ -21,7 +21,7 @@
},
"id": "1c95ba05-cc82-4947-966a-b4944cc030d8",
"location_paths": [
- "/Users/jamespine/Projects/spacedrive/core-new/benchdata/shape_small"
+ "/Users/jamespine/Projects/spacedrive/core/benchdata/shape_small"
],
"recipe_name": "shape_small",
"timestamp_utc": "2025-08-10T09:10:58.068059+00:00"
diff --git a/core-new/benchmarks/results/whitepaper_metrics.csv b/core/benchmarks/results/whitepaper_metrics.csv
similarity index 100%
rename from core-new/benchmarks/results/whitepaper_metrics.csv
rename to core/benchmarks/results/whitepaper_metrics.csv
diff --git a/core-new/benchmarks/src/bin/sd-bench-new.rs b/core/benchmarks/src/bin/sd-bench-new.rs
similarity index 100%
rename from core-new/benchmarks/src/bin/sd-bench-new.rs
rename to core/benchmarks/src/bin/sd-bench-new.rs
diff --git a/core-new/benchmarks/src/cli/args.rs b/core/benchmarks/src/cli/args.rs
similarity index 100%
rename from core-new/benchmarks/src/cli/args.rs
rename to core/benchmarks/src/cli/args.rs
diff --git a/core-new/benchmarks/src/cli/commands.rs b/core/benchmarks/src/cli/commands.rs
similarity index 100%
rename from core-new/benchmarks/src/cli/commands.rs
rename to core/benchmarks/src/cli/commands.rs
diff --git a/core-new/benchmarks/src/cli/mod.rs b/core/benchmarks/src/cli/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/cli/mod.rs
rename to core/benchmarks/src/cli/mod.rs
diff --git a/core-new/benchmarks/src/config/mod.rs b/core/benchmarks/src/config/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/config/mod.rs
rename to core/benchmarks/src/config/mod.rs
diff --git a/core-new/benchmarks/src/core_boot/mod.rs b/core/benchmarks/src/core_boot/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/core_boot/mod.rs
rename to core/benchmarks/src/core_boot/mod.rs
diff --git a/core-new/benchmarks/src/generator/filesystem.rs b/core/benchmarks/src/generator/filesystem.rs
similarity index 100%
rename from core-new/benchmarks/src/generator/filesystem.rs
rename to core/benchmarks/src/generator/filesystem.rs
diff --git a/core-new/benchmarks/src/generator/mod.rs b/core/benchmarks/src/generator/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/generator/mod.rs
rename to core/benchmarks/src/generator/mod.rs
diff --git a/core-new/benchmarks/src/generator/noop.rs b/core/benchmarks/src/generator/noop.rs
similarity index 100%
rename from core-new/benchmarks/src/generator/noop.rs
rename to core/benchmarks/src/generator/noop.rs
diff --git a/core-new/benchmarks/src/generator/registry.rs b/core/benchmarks/src/generator/registry.rs
similarity index 100%
rename from core-new/benchmarks/src/generator/registry.rs
rename to core/benchmarks/src/generator/registry.rs
diff --git a/core-new/benchmarks/src/lib.rs b/core/benchmarks/src/lib.rs
similarity index 100%
rename from core-new/benchmarks/src/lib.rs
rename to core/benchmarks/src/lib.rs
diff --git a/core-new/benchmarks/src/metrics/mod.rs b/core/benchmarks/src/metrics/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/metrics/mod.rs
rename to core/benchmarks/src/metrics/mod.rs
diff --git a/core-new/benchmarks/src/metrics/sources.rs b/core/benchmarks/src/metrics/sources.rs
similarity index 100%
rename from core-new/benchmarks/src/metrics/sources.rs
rename to core/benchmarks/src/metrics/sources.rs
diff --git a/core-new/benchmarks/src/recipe/mod.rs b/core/benchmarks/src/recipe/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/recipe/mod.rs
rename to core/benchmarks/src/recipe/mod.rs
diff --git a/core-new/benchmarks/src/recipe/schema.rs b/core/benchmarks/src/recipe/schema.rs
similarity index 100%
rename from core-new/benchmarks/src/recipe/schema.rs
rename to core/benchmarks/src/recipe/schema.rs
diff --git a/core-new/benchmarks/src/reporting/csv.rs b/core/benchmarks/src/reporting/csv.rs
similarity index 100%
rename from core-new/benchmarks/src/reporting/csv.rs
rename to core/benchmarks/src/reporting/csv.rs
diff --git a/core-new/benchmarks/src/reporting/json_summary.rs b/core/benchmarks/src/reporting/json_summary.rs
similarity index 100%
rename from core-new/benchmarks/src/reporting/json_summary.rs
rename to core/benchmarks/src/reporting/json_summary.rs
diff --git a/core-new/benchmarks/src/reporting/mod.rs b/core/benchmarks/src/reporting/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/reporting/mod.rs
rename to core/benchmarks/src/reporting/mod.rs
diff --git a/core-new/benchmarks/src/reporting/registry.rs b/core/benchmarks/src/reporting/registry.rs
similarity index 100%
rename from core-new/benchmarks/src/reporting/registry.rs
rename to core/benchmarks/src/reporting/registry.rs
diff --git a/core-new/benchmarks/src/runner/mod.rs b/core/benchmarks/src/runner/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/runner/mod.rs
rename to core/benchmarks/src/runner/mod.rs
diff --git a/core-new/benchmarks/src/runner/monitor.rs b/core/benchmarks/src/runner/monitor.rs
similarity index 100%
rename from core-new/benchmarks/src/runner/monitor.rs
rename to core/benchmarks/src/runner/monitor.rs
diff --git a/core-new/benchmarks/src/scenarios/common.rs b/core/benchmarks/src/scenarios/common.rs
similarity index 100%
rename from core-new/benchmarks/src/scenarios/common.rs
rename to core/benchmarks/src/scenarios/common.rs
diff --git a/core-new/benchmarks/src/scenarios/content_identification.rs b/core/benchmarks/src/scenarios/content_identification.rs
similarity index 100%
rename from core-new/benchmarks/src/scenarios/content_identification.rs
rename to core/benchmarks/src/scenarios/content_identification.rs
diff --git a/core-new/benchmarks/src/scenarios/core_indexing.rs b/core/benchmarks/src/scenarios/core_indexing.rs
similarity index 100%
rename from core-new/benchmarks/src/scenarios/core_indexing.rs
rename to core/benchmarks/src/scenarios/core_indexing.rs
diff --git a/core-new/benchmarks/src/scenarios/mod.rs b/core/benchmarks/src/scenarios/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/scenarios/mod.rs
rename to core/benchmarks/src/scenarios/mod.rs
diff --git a/core-new/benchmarks/src/scenarios/registry.rs b/core/benchmarks/src/scenarios/registry.rs
similarity index 100%
rename from core-new/benchmarks/src/scenarios/registry.rs
rename to core/benchmarks/src/scenarios/registry.rs
diff --git a/core-new/benchmarks/src/util/fs.rs b/core/benchmarks/src/util/fs.rs
similarity index 100%
rename from core-new/benchmarks/src/util/fs.rs
rename to core/benchmarks/src/util/fs.rs
diff --git a/core-new/benchmarks/src/util/mod.rs b/core/benchmarks/src/util/mod.rs
similarity index 100%
rename from core-new/benchmarks/src/util/mod.rs
rename to core/benchmarks/src/util/mod.rs
diff --git a/core-new/benchmarks/src/util/rng.rs b/core/benchmarks/src/util/rng.rs
similarity index 100%
rename from core-new/benchmarks/src/util/rng.rs
rename to core/benchmarks/src/util/rng.rs
diff --git a/core-new/benchmarks/src/util/time.rs b/core/benchmarks/src/util/time.rs
similarity index 100%
rename from core-new/benchmarks/src/util/time.rs
rename to core/benchmarks/src/util/time.rs
diff --git a/core/build.rs b/core/build.rs
index af19d274e..4b32dbe9b 100644
--- a/core/build.rs
+++ b/core/build.rs
@@ -1,11 +1,13 @@
-use std::process::Command;
+use vergen::EmitBuilder;
-fn main() {
- let output = Command::new("git")
- .args(["rev-parse", "--short", "HEAD"])
- .output()
- .expect("error getting git hash. Does `git rev-parse --short HEAD` work for you?");
- let git_hash = String::from_utf8(output.stdout)
- .expect("Error passing output of `git rev-parse --short HEAD`");
- println!("cargo:rustc-env=GIT_HASH={git_hash}");
+fn main() -> Result<(), Box> {
+ // Emit the instructions
+ EmitBuilder::builder()
+ .git_sha(true)
+ .git_commit_timestamp()
+ .git_branch()
+ .cargo_opt_level()
+ .cargo_target_triple()
+ .emit()?;
+ Ok(())
}
diff --git a/core/crates/cloud-services/Cargo.toml b/core/crates/cloud-services/Cargo.toml
deleted file mode 100644
index 1dc8b6d71..000000000
--- a/core/crates/cloud-services/Cargo.toml
+++ /dev/null
@@ -1,57 +0,0 @@
-[package]
-name = "sd-core-cloud-services"
-version = "0.1.0"
-
-edition = "2021"
-
-[dependencies]
-# Core Spacedrive Sub-crates
-sd-core-heavy-lifting = { path = "../heavy-lifting" }
-sd-core-prisma-helpers = { path = "../prisma-helpers" }
-sd-core-sync = { path = "../sync" }
-
-# Spacedrive Sub-crates
-sd-actors = { path = "../../../crates/actors" }
-sd-cloud-schema = { workspace = true }
-sd-crypto = { path = "../../../crates/crypto" }
-sd-prisma = { path = "../../../crates/prisma" }
-sd-utils = { path = "../../../crates/utils" }
-
-# Workspace dependencies
-anyhow = { workspace = true }
-async-stream = { workspace = true }
-base64 = { workspace = true }
-blake3 = { workspace = true }
-chrono = { workspace = true, features = ["serde"] }
-flume = { workspace = true }
-futures = { workspace = true }
-futures-concurrency = { workspace = true }
-iroh = { workspace = true, features = ["discovery-local-network"] }
-quic-rpc = { workspace = true, features = ["iroh-transport", "quinn-transport"] }
-rmp-serde = { workspace = true }
-rspc = { workspace = true }
-serde = { workspace = true, features = ["derive"] }
-serde_json = { workspace = true }
-specta = { workspace = true }
-thiserror = { workspace = true }
-tokio = { workspace = true, features = ["sync", "time"] }
-tokio-stream = { workspace = true }
-tokio-util = { workspace = true }
-tracing = { workspace = true }
-uuid = { workspace = true, features = ["serde"] }
-zeroize = { workspace = true }
-
-# External dependencies
-dashmap = "6.1.0"
-paste = "=1.0.15"
-quinn = { package = "iroh-quinn", version = "0.12" }
-# Using whatever version of reqwest that reqwest-middleware uses, just putting here to enable some features
-reqwest = { version = "0.12", features = ["json", "native-tls-vendored", "stream"] }
-reqwest-middleware = { version = "0.4", features = ["json"] }
-reqwest-retry = "0.7"
-rustls = { version = "=0.23.19", default-features = false, features = ["brotli", "ring", "std"] }
-rustls-platform-verifier = "0.4.0"
-
-
-[dev-dependencies]
-tokio = { workspace = true, features = ["rt", "sync", "time"] }
diff --git a/core/crates/cloud-services/src/client.rs b/core/crates/cloud-services/src/client.rs
deleted file mode 100644
index d28037357..000000000
--- a/core/crates/cloud-services/src/client.rs
+++ /dev/null
@@ -1,358 +0,0 @@
-use crate::p2p::{NotifyUser, UserResponse};
-
-use sd_cloud_schema::{Client, Service, ServicesALPN};
-
-use std::{net::SocketAddr, sync::Arc, time::Duration};
-
-use futures::Stream;
-use iroh::relay::RelayUrl;
-use quic_rpc::{client::QuinnConnector, RpcClient};
-use quinn::{crypto::rustls::QuicClientConfig, ClientConfig, Endpoint};
-use reqwest::{IntoUrl, Url};
-use reqwest_middleware::{reqwest, ClientBuilder, ClientWithMiddleware};
-// use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
-use tokio::sync::{Mutex, RwLock};
-use tracing::warn;
-
-use super::{
- error::Error, key_manager::KeyManager, p2p::CloudP2P, token_refresher::TokenRefresher,
-};
-
-pub type CloudServicesClient = Client>;
-
-#[derive(Debug, Default, Clone)]
-enum ClientState {
- #[default]
- NotConnected,
- Connected(CloudServicesClient),
-}
-
-/// Cloud services are a optional feature that allows you to interact with the cloud services
-/// of Spacedrive.
-/// They're optional in two different ways:
-/// - The cloud services depends on a user being logged in with our server.
-/// - The user being connected to the internet to begin with.
-///
-/// As we don't want to force the user to be connected to the internet, we have to make sure
-/// that core can always operate without the cloud services.
-#[derive(Debug)]
-pub struct CloudServices {
- client_state: Arc>,
- get_cloud_api_address: Url,
- http_client: ClientWithMiddleware,
- domain_name: String,
- pub cloud_p2p_dns_origin_name: String,
- pub cloud_p2p_relay_url: RelayUrl,
- pub cloud_p2p_dns_pkarr_url: Url,
- pub token_refresher: TokenRefresher,
- key_manager: Arc>>>,
- cloud_p2p: Arc>>>,
- pub(crate) notify_user_tx: flume::Sender,
- notify_user_rx: flume::Receiver,
- user_response_tx: flume::Sender,
- pub(crate) user_response_rx: flume::Receiver,
- pub has_bootstrapped: Arc>,
-}
-
-impl CloudServices {
- /// Creates a new cloud services client that can be used to interact with the cloud services.
- /// The client will try to connect to the cloud services on a best effort basis, as the user
- /// might not be connected to the internet.
- /// If the client fails to connect, it will try again the next time it's used.
- pub async fn new(
- get_cloud_api_address: impl IntoUrl + Send,
- cloud_p2p_relay_url: impl IntoUrl + Send,
- cloud_p2p_dns_pkarr_url: impl IntoUrl + Send,
- cloud_p2p_dns_origin_name: String,
- domain_name: String,
- ) -> Result {
- let mut http_client_builder = reqwest::Client::builder().timeout(Duration::from_secs(3));
-
- #[cfg(not(debug_assertions))]
- {
- http_client_builder = http_client_builder.https_only(true);
- }
-
- let cloud_p2p_relay_url = cloud_p2p_relay_url
- .into_url()
- .map_err(Error::InvalidUrl)?
- .into();
-
- let cloud_p2p_dns_pkarr_url = cloud_p2p_dns_pkarr_url
- .into_url()
- .map_err(Error::InvalidUrl)?;
-
- let http_client =
- ClientBuilder::new(http_client_builder.build().map_err(Error::HttpClientInit)?)
- // TODO: Re-enable retry middleware. It's currently disabled because it's causing blocking issues on mobile core initialization.
- // .with(RetryTransientMiddleware::new_with_policy(
- // ExponentialBackoff::builder().build_with_max_retries(3),
- // ))
- .build();
- let get_cloud_api_address = get_cloud_api_address
- .into_url()
- .map_err(Error::InvalidUrl)?;
-
- let client_state = match Self::init_client(
- &http_client,
- get_cloud_api_address.clone(),
- domain_name.clone(),
- )
- .await
- {
- Ok(client) => Arc::new(RwLock::new(ClientState::Connected(client))),
- Err(e) => {
- warn!(
- ?e,
- "Failed to initialize cloud services client; \
- This is a best effort and we will continue in Not Connected mode"
- );
- Arc::new(RwLock::new(ClientState::NotConnected))
- }
- };
-
- let (notify_user_tx, notify_user_rx) = flume::bounded(16);
- let (user_response_tx, user_response_rx) = flume::bounded(16);
-
- Ok(Self {
- client_state,
- token_refresher: TokenRefresher::new(
- http_client.clone(),
- get_cloud_api_address.clone(),
- ),
- get_cloud_api_address,
- http_client,
- cloud_p2p_dns_origin_name,
- cloud_p2p_relay_url,
- cloud_p2p_dns_pkarr_url,
- domain_name,
- key_manager: Arc::default(),
- cloud_p2p: Arc::default(),
- notify_user_tx,
- notify_user_rx,
- user_response_tx,
- user_response_rx,
- has_bootstrapped: Arc::default(),
- })
- }
-
- pub fn stream_user_notifications(&self) -> impl Stream- + '_ {
- self.notify_user_rx.stream()
- }
-
- #[must_use]
- pub const fn http_client(&self) -> &ClientWithMiddleware {
- &self.http_client
- }
-
- /// Send back a user response to the Cloud P2P actor
- ///
- /// # Panics
- /// Will panic if the channel is closed, which should never happen
- pub async fn send_user_response(&self, response: UserResponse) {
- self.user_response_tx
- .send_async(response)
- .await
- .expect("user response channel must never close");
- }
-
- async fn init_client(
- http_client: &ClientWithMiddleware,
- get_cloud_api_address: Url,
- domain_name: String,
- ) -> Result
{
- let cloud_api_address = http_client
- .get(get_cloud_api_address)
- .send()
- .await
- .map_err(Error::FailedToRequestApiAddress)?
- .error_for_status()
- .map_err(Error::AuthServerError)?
- .text()
- .await
- .map_err(Error::FailedToExtractApiAddress)?
- .parse::()?;
-
- let mut crypto_config = {
- #[cfg(debug_assertions)]
- {
- #[derive(Debug)]
- struct SkipServerVerification;
- impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
- fn verify_server_cert(
- &self,
- _end_entity: &rustls::pki_types::CertificateDer<'_>,
- _intermediates: &[rustls::pki_types::CertificateDer<'_>],
- _server_name: &rustls::pki_types::ServerName<'_>,
- _ocsp_response: &[u8],
- _now: rustls::pki_types::UnixTime,
- ) -> Result {
- Ok(rustls::client::danger::ServerCertVerified::assertion())
- }
-
- fn verify_tls12_signature(
- &self,
- _message: &[u8],
- _cert: &rustls::pki_types::CertificateDer<'_>,
- _dss: &rustls::DigitallySignedStruct,
- ) -> Result
- {
- Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
- }
-
- fn verify_tls13_signature(
- &self,
- _message: &[u8],
- _cert: &rustls::pki_types::CertificateDer<'_>,
- _dss: &rustls::DigitallySignedStruct,
- ) -> Result
- {
- Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
- }
-
- fn supported_verify_schemes(&self) -> Vec {
- vec![
- rustls::SignatureScheme::RSA_PKCS1_SHA1,
- rustls::SignatureScheme::ECDSA_SHA1_Legacy,
- rustls::SignatureScheme::RSA_PKCS1_SHA256,
- rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
- rustls::SignatureScheme::RSA_PKCS1_SHA384,
- rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
- rustls::SignatureScheme::RSA_PKCS1_SHA512,
- rustls::SignatureScheme::ECDSA_NISTP521_SHA512,
- rustls::SignatureScheme::RSA_PSS_SHA256,
- rustls::SignatureScheme::RSA_PSS_SHA384,
- rustls::SignatureScheme::RSA_PSS_SHA512,
- rustls::SignatureScheme::ED25519,
- rustls::SignatureScheme::ED448,
- ]
- }
- }
-
- rustls::ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13])
- .dangerous()
- .with_custom_certificate_verifier(Arc::new(SkipServerVerification))
- .with_no_client_auth()
- }
-
- #[cfg(not(debug_assertions))]
- {
- rustls::ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS13])
- .dangerous()
- .with_custom_certificate_verifier(Arc::new(
- rustls_platform_verifier::Verifier::new(),
- ))
- .with_no_client_auth()
- }
- };
-
- crypto_config
- .alpn_protocols
- .extend([ServicesALPN::LATEST.to_vec()]);
-
- let client_config = ClientConfig::new(Arc::new(
- QuicClientConfig::try_from(crypto_config)
- .expect("misconfigured TLS client config, this is a bug and should crash"),
- ));
-
- let mut endpoint = Endpoint::client("[::]:0".parse().expect("hardcoded address"))
- .map_err(Error::FailedToCreateEndpoint)?;
- endpoint.set_default_client_config(client_config);
-
- Ok(Client::new(RpcClient::new(QuinnConnector::::new(
- endpoint,
- cloud_api_address,
- domain_name,
- ))))
- }
-
- /// Returns a client to the cloud services.
- ///
- /// If the client is not connected, it will try to connect to the cloud services.
- /// Available routes documented in
- /// [`sd_cloud_schema::Service`](https://github.com/spacedriveapp/cloud-services-schema).
- pub async fn client(&self) -> Result {
- if let ClientState::Connected(client) = { &*self.client_state.read().await } {
- return Ok(client.clone());
- }
-
- // If we're not connected, we need to try to connect.
- let client = Self::init_client(
- &self.http_client,
- self.get_cloud_api_address.clone(),
- self.domain_name.clone(),
- )
- .await?;
- *self.client_state.write().await = ClientState::Connected(client.clone());
-
- Ok(client)
- }
-
- pub async fn set_key_manager(&self, key_manager: KeyManager) {
- self.key_manager
- .write()
- .await
- .replace(Arc::new(key_manager));
- }
-
- pub async fn key_manager(&self) -> Result, Error> {
- self.key_manager
- .read()
- .await
- .as_ref()
- .map_or(Err(Error::KeyManagerNotInitialized), |key_manager| {
- Ok(Arc::clone(key_manager))
- })
- }
-
- pub async fn set_cloud_p2p(&self, cloud_p2p: CloudP2P) {
- self.cloud_p2p.write().await.replace(Arc::new(cloud_p2p));
- }
-
- pub async fn cloud_p2p(&self) -> Result, Error> {
- self.cloud_p2p
- .read()
- .await
- .as_ref()
- .map_or(Err(Error::CloudP2PNotInitialized), |cloud_p2p| {
- Ok(Arc::clone(cloud_p2p))
- })
- }
-}
-
-#[cfg(test)]
-mod tests {
- use sd_cloud_schema::{auth, devices};
-
- use super::*;
-
- #[ignore]
- #[tokio::test]
- async fn test_client() {
- let response = CloudServices::new(
- "http://localhost:9420/cloud-api-address",
- "http://relay.localhost:9999/",
- "http://pkarr.localhost:9999/",
- "dns.localhost:9999".to_string(),
- "localhost".to_string(),
- )
- .await
- .unwrap()
- .client()
- .await
- .unwrap()
- .devices()
- .list(devices::list::Request {
- access_token: auth::AccessToken("invalid".to_string()),
- })
- .await
- .unwrap();
-
- assert!(matches!(
- response,
- Err(sd_cloud_schema::Error::Client(
- sd_cloud_schema::error::ClientSideError::Unauthorized
- ))
- ));
- }
-}
diff --git a/core/crates/cloud-services/src/error.rs b/core/crates/cloud-services/src/error.rs
deleted file mode 100644
index 8fe175a3a..000000000
--- a/core/crates/cloud-services/src/error.rs
+++ /dev/null
@@ -1,216 +0,0 @@
-use sd_cloud_schema::{
- cloud_p2p,
- sync::{self, groups},
- Request, Response,
-};
-use sd_utils::error::FileIOError;
-
-use std::{io, net::AddrParseError};
-
-use quic_rpc::{
- pattern::{bidi_streaming, rpc, server_streaming},
- transport::{mapped::MappedConnector, quinn::QuinnConnector},
-};
-
-#[derive(thiserror::Error, Debug)]
-pub enum Error {
- // Setup errors
- #[error("Couldn't parse Cloud Services API address URL: {0}")]
- InvalidUrl(reqwest::Error),
- #[error("Failed to parse Cloud Services API address URL")]
- FailedToParseRelayUrl,
- #[error("Failed to initialize http client: {0}")]
- HttpClientInit(reqwest::Error),
- #[error("Failed to request Cloud Services API address from Auth Server route: {0}")]
- FailedToRequestApiAddress(reqwest_middleware::Error),
- #[error("Auth Server's Cloud Services API address route returned an error: {0}")]
- AuthServerError(reqwest::Error),
- #[error(
- "Failed to extract response body from Auth Server's Cloud Services API address route: {0}"
- )]
- FailedToExtractApiAddress(reqwest::Error),
- #[error("Failed to parse auth server's Cloud Services API address: {0}")]
- FailedToParseApiAddress(#[from] AddrParseError),
- #[error("Failed to create endpoint: {0}")]
- FailedToCreateEndpoint(io::Error),
-
- // Token refresher errors
- #[error("Invalid token format, missing claims")]
- MissingClaims,
- #[error("Failed to decode access token data: {0}")]
- DecodeAccessTokenData(#[from] base64::DecodeError),
- #[error("Failed to deserialize access token json data: {0}")]
- DeserializeAccessTokenData(#[from] serde_json::Error),
- #[error("Token expired")]
- TokenExpired,
- #[error("Failed to request refresh token: {0}")]
- RefreshTokenRequest(reqwest_middleware::Error),
- #[error("Missing tokens on refresh response")]
- MissingTokensOnRefreshResponse,
- #[error("Failed to parse token header value to string: {0}")]
- FailedToParseTokenHeaderValueToString(#[from] reqwest::header::ToStrError),
-
- // Key Manager errors
- #[error("Failed to handle File on KeyManager: {0}")]
- FileIO(#[from] FileIOError),
- #[error("Failed to handle key store serialization: {0}")]
- KeyStoreSerialization(rmp_serde::encode::Error),
- #[error("Failed to handle key store deserialization: {0}")]
- KeyStoreDeserialization(rmp_serde::decode::Error),
- #[error("Key store encryption related error: {{context: \"{context}\", source: {source}}}")]
- KeyStoreCrypto {
- #[source]
- source: sd_crypto::Error,
- context: &'static str,
- },
- #[error("Key manager not initialized")]
- KeyManagerNotInitialized,
-
- // Cloud P2P errors
- #[error("Failed to create Cloud P2P endpoint: {0}")]
- CreateCloudP2PEndpoint(anyhow::Error),
- #[error("Failed to connect to Cloud P2P node: {0}")]
- ConnectToCloudP2PNode(anyhow::Error),
- #[error("Communication error with Cloud P2P node: {0}")]
- CloudP2PRpcCommunication(
- #[from] rpc::Error>,
- ),
- #[error("Cloud P2P not initialized")]
- CloudP2PNotInitialized,
- #[error("Failed to initialize LocalSwarmDiscovery: {0}")]
- LocalSwarmDiscoveryInit(anyhow::Error),
- #[error("Failed to initialize DhtDiscovery: {0}")]
- DhtDiscoveryInit(anyhow::Error),
-
- // Communication errors
- #[error("Failed to communicate with RPC backend: {0}")]
- RpcCommunication(#[from] rpc::Error>),
- #[error("Failed to communicate with RPC sync backend: {0}")]
- RpcSyncCommunication(
- #[from]
- rpc::Error<
- MappedConnector>,
- >,
- ),
- #[error("Failed to communicate with Server Streaming RPC backend: {0}")]
- ServerStreamCommunication(#[from] server_streaming::Error>),
- #[error("Failed to communicate with Server Streaming RPC sync backend: {0}")]
- ServerStreamSyncCommunication(
- #[from]
- server_streaming::Error<
- MappedConnector>,
- >,
- ),
- #[error("Failed to receive next response from Server Streaming RPC backend: {0}")]
- ServerStreamRecv(#[from] server_streaming::ItemError>),
- #[error("Failed to receive next response from Server Streaming RPC sync backend: {0}")]
- ServerStreamSyncRecv(
- #[from]
- server_streaming::ItemError<
- MappedConnector>,
- >,
- ),
- #[error("Failed to communicate with Bidi Streaming RPC backend: {0}")]
- BidiStreamCommunication(#[from] bidi_streaming::Error>),
- #[error("Failed to communicate with Bidi Streaming RPC sync backend: {0}")]
- BidiStreamSyncCommunication(
- #[from]
- bidi_streaming::Error<
- MappedConnector>,
- >,
- ),
- #[error("Failed to receive next response from Bidi Streaming RPC backend: {0}")]
- BidiStreamRecv(#[from] bidi_streaming::ItemError>),
- #[error("Error from backend: {0}")]
- Backend(#[from] sd_cloud_schema::Error),
- #[error("Failed to get access token from refresher: {0}")]
- GetToken(#[from] GetTokenError),
- #[error("Unexpected empty response from backend, context: {0}")]
- EmptyResponse(&'static str),
- #[error("Unexpected response from backend, context: {0}")]
- UnexpectedResponse(&'static str),
-
- // Sync error
- #[error("Sync error: {0}")]
- Sync(#[from] sd_core_sync::Error),
- #[error("Tried to sync messages with a group without having needed key")]
- MissingSyncGroupKey(groups::PubId),
- #[error("Failed to encrypt sync messages: {0}")]
- Encrypt(sd_crypto::Error),
- #[error("Failed to decrypt sync messages: {0}")]
- Decrypt(sd_crypto::Error),
- #[error("Failed to upload sync messages: {0}")]
- UploadSyncMessages(reqwest_middleware::Error),
- #[error("Failed to download sync messages: {0}")]
- DownloadSyncMessages(reqwest_middleware::Error),
- #[error("Received an error response from uploading sync messages: {0}")]
- ErrorResponseUploadSyncMessages(reqwest::Error),
- #[error("Received an error response from downloading sync messages: {0}")]
- ErrorResponseDownloadSyncMessages(reqwest::Error),
- #[error(
- "Received an error response from downloading sync messages while reading its bytes: {0}"
- )]
- ErrorResponseDownloadReadBytesSyncMessages(reqwest::Error),
- #[error("Critical error while uploading sync messages")]
- CriticalErrorWhileUploadingSyncMessages,
- #[error("Failed to send End update to push sync messages")]
- EndUpdatePushSyncMessages(io::Error),
- #[error("Unexpected end of stream while encrypting sync messages")]
- UnexpectedEndOfStream,
- #[error("Failed to create directory to store timestamp keeper files")]
- FailedToCreateTimestampKeepersDirectory(io::Error),
- #[error("Failed to read last timestamp keeper for pulling sync messages: {0}")]
- FailedToReadLastTimestampKeeper(io::Error),
- #[error("Failed to handle last timestamp keeper serialization: {0}")]
- LastTimestampKeeperSerialization(rmp_serde::encode::Error),
- #[error("Failed to handle last timestamp keeper deserialization: {0}")]
- LastTimestampKeeperDeserialization(rmp_serde::decode::Error),
- #[error("Failed to write last timestamp keeper for pulling sync messages: {0}")]
- FailedToWriteLastTimestampKeeper(io::Error),
- #[error("Sync messages download and decrypt task panicked")]
- SyncMessagesDownloadAndDecryptTaskPanicked,
- #[error("Serialization failure to push sync messages: {0}")]
- SerializationFailureToPushSyncMessages(rmp_serde::encode::Error),
- #[error("Deserialization failure to pull sync messages: {0}")]
- DeserializationFailureToPullSyncMessages(rmp_serde::decode::Error),
- #[error("Read nonce stream decryption: {0}")]
- ReadNonceStreamDecryption(io::Error),
- #[error("Incomplete download bytes sync messages")]
- IncompleteDownloadBytesSyncMessages,
- #[error("Timed out while waiting to recive thumbnail data")]
- ThumbnailRequestTimeout,
-
- // Temporary errors
- #[error("Device missing secret key for decrypting sync messages")]
- MissingKeyHash,
- #[error("Not Implemented yet")]
- NotImplemented,
- #[error("Device not found")]
- DeviceNotFound,
- #[error("Invalid CAS ID")]
- InvalidCasId,
- #[error("Internal Error")]
- InternalError,
- #[error("Remote Device Error")]
- RemoteDeviceError,
-}
-
-#[derive(thiserror::Error, Debug)]
-pub enum GetTokenError {
- #[error("Token refresher not initialized")]
- RefresherNotInitialized,
- #[error("Token refresher failed to refresh and need to be initialized again")]
- FailedToRefresh,
-}
-
-impl From for rspc::Error {
- fn from(e: Error) -> Self {
- Self::with_cause(rspc::ErrorCode::InternalServerError, e.to_string(), e)
- }
-}
-
-impl From for rspc::Error {
- fn from(e: GetTokenError) -> Self {
- Self::with_cause(rspc::ErrorCode::InternalServerError, e.to_string(), e)
- }
-}
diff --git a/core/crates/cloud-services/src/key_manager/key_store.rs b/core/crates/cloud-services/src/key_manager/key_store.rs
deleted file mode 100644
index acf97dad9..000000000
--- a/core/crates/cloud-services/src/key_manager/key_store.rs
+++ /dev/null
@@ -1,331 +0,0 @@
-use crate::Error;
-
-use sd_cloud_schema::{
- sync::{groups, KeyHash},
- NodeId, SecretKey as IrohSecretKey,
-};
-use sd_crypto::{
- cloud::{decrypt, encrypt, secret_key::SecretKey},
- primitives::{EncryptedBlock, OneShotNonce, StreamNonce},
- CryptoRng,
-};
-use sd_utils::error::FileIOError;
-use tracing::debug;
-
-use std::{
- collections::{BTreeMap, VecDeque},
- fs::Metadata,
- path::PathBuf,
- pin::pin,
-};
-
-use futures::StreamExt;
-use serde::{Deserialize, Serialize};
-use tokio::{
- fs,
- io::{AsyncReadExt, AsyncWriteExt, BufWriter},
-};
-use zeroize::{Zeroize, ZeroizeOnDrop};
-
-type KeyStack = VecDeque<(KeyHash, SecretKey)>;
-
-#[derive(Serialize, Deserialize)]
-pub struct KeyStore {
- iroh_secret_key: IrohSecretKey,
- keys: BTreeMap,
-}
-
-impl KeyStore {
- pub const fn new(iroh_secret_key: IrohSecretKey) -> Self {
- Self {
- iroh_secret_key,
- keys: BTreeMap::new(),
- }
- }
-
- pub fn add_key(&mut self, group_pub_id: groups::PubId, key: SecretKey) {
- self.keys.entry(group_pub_id).or_default().push_front((
- KeyHash(blake3::hash(key.as_ref()).to_hex().to_string()),
- key,
- ));
- }
-
- pub fn add_key_with_hash(
- &mut self,
- group_pub_id: groups::PubId,
- key: SecretKey,
- key_hash: KeyHash,
- ) {
- debug!(
- key_hash = key_hash.0,
- ?group_pub_id,
- "Added single cloud sync key to key manager"
- );
-
- self.keys
- .entry(group_pub_id)
- .or_default()
- .push_front((key_hash, key));
- }
-
- pub fn add_many_keys(
- &mut self,
- group_pub_id: groups::PubId,
- keys: impl IntoIterator- >,
- ) {
- let group_entry = self.keys.entry(group_pub_id).or_default();
-
- // We reverse the secret keys as a implementation detail to
- // keep the keys in the same order as they were added as a stack
- for key in keys.into_iter().rev() {
- let key_hash = blake3::hash(key.as_ref()).to_hex().to_string();
-
- debug!(
- key_hash,
- ?group_pub_id,
- "Added cloud sync key to key manager"
- );
-
- group_entry.push_front((KeyHash(key_hash), key));
- }
- }
-
- pub fn remove_group(&mut self, group_pub_id: groups::PubId) {
- self.keys.remove(&group_pub_id);
- }
-
- pub fn iroh_secret_key(&self) -> IrohSecretKey {
- self.iroh_secret_key.clone()
- }
-
- pub fn node_id(&self) -> NodeId {
- self.iroh_secret_key.public()
- }
-
- pub fn get_key(&self, group_pub_id: groups::PubId, hash: &KeyHash) -> Option
{
- self.keys.get(&group_pub_id).and_then(|group| {
- group
- .iter()
- .find_map(|(key_hash, key)| (key_hash == hash).then(|| key.clone()))
- })
- }
-
- pub fn get_latest_key(&self, group_pub_id: groups::PubId) -> Option<(KeyHash, SecretKey)> {
- self.keys
- .get(&group_pub_id)
- .and_then(|group| group.front().cloned())
- }
-
- pub fn get_group_keys(&self, group_pub_id: groups::PubId) -> Vec {
- self.keys
- .get(&group_pub_id)
- .map(|group| group.iter().map(|(_key_hash, key)| key.clone()).collect())
- .unwrap_or_default()
- }
-
- pub async fn encrypt(
- &self,
- key: &SecretKey,
- rng: &mut CryptoRng,
- keys_file_path: &PathBuf,
- ) -> Result<(), Error> {
- let plain_text_bytes =
- rmp_serde::to_vec_named(self).map_err(Error::KeyStoreSerialization)?;
- let mut file = BufWriter::with_capacity(
- EncryptedBlock::CIPHER_TEXT_SIZE,
- fs::OpenOptions::new()
- .create(true)
- .write(true)
- .truncate(true)
- .open(&keys_file_path)
- .await
- .map_err(|e| {
- FileIOError::from((
- &keys_file_path,
- e,
- "Failed to open space keys file to encrypt",
- ))
- })?,
- );
-
- if plain_text_bytes.len() < EncryptedBlock::PLAIN_TEXT_SIZE {
- use encrypt::OneShotEncryption;
-
- let EncryptedBlock { nonce, cipher_text } = key
- .encrypt(&plain_text_bytes, rng)
- .map_err(|e| Error::KeyStoreCrypto {
- source: e,
- context: "Failed to oneshot encrypt key store",
- })?;
-
- file.write_all(nonce.as_slice()).await.map_err(|e| {
- FileIOError::from((
- &keys_file_path,
- e,
- "Failed to write space keys file oneshot nonce",
- ))
- })?;
-
- file.write_all(cipher_text.as_slice()).await.map_err(|e| {
- FileIOError::from((
- &keys_file_path,
- e,
- "Failed to write space keys file oneshot cipher text",
- ))
- })?;
- } else {
- use encrypt::StreamEncryption;
-
- let (nonce, stream) = key.encrypt(plain_text_bytes.as_slice(), rng);
-
- file.write_all(nonce.as_slice()).await.map_err(|e| {
- FileIOError::from((
- &keys_file_path,
- e,
- "Failed to write space keys file stream nonce",
- ))
- })?;
-
- let mut stream = pin!(stream);
- while let Some(res) = stream.next().await {
- file.write_all(&res.map_err(|e| Error::KeyStoreCrypto {
- source: e,
- context: "Failed to stream encrypt key store",
- })?)
- .await
- .map_err(|e| {
- FileIOError::from((
- &keys_file_path,
- e,
- "Failed to write space keys file stream cipher text",
- ))
- })?;
- }
- };
-
- file.flush().await.map_err(|e| {
- FileIOError::from((&keys_file_path, e, "Failed to flush space keys file")).into()
- })
- }
-
- pub async fn decrypt(
- key: &SecretKey,
- metadata: Metadata,
- keys_file_path: &PathBuf,
- ) -> Result {
- let mut file = fs::File::open(&keys_file_path).await.map_err(|e| {
- FileIOError::from((
- keys_file_path,
- e,
- "Failed to open space keys file to decrypt",
- ))
- })?;
-
- let usize_file_len =
- usize::try_from(metadata.len()).expect("Failed to convert metadata length to usize");
-
- let key_store_bytes =
- if usize_file_len <= EncryptedBlock::CIPHER_TEXT_SIZE + size_of::() {
- use decrypt::OneShotDecryption;
-
- let mut nonce = OneShotNonce::default();
-
- file.read_exact(&mut nonce).await.map_err(|e| {
- FileIOError::from((
- keys_file_path,
- e,
- "Failed to read space keys file oneshot nonce",
- ))
- })?;
-
- let mut cipher_text = vec![0u8; usize_file_len - size_of::()];
-
- file.read_exact(&mut cipher_text).await.map_err(|e| {
- FileIOError::from((
- keys_file_path,
- e,
- "Failed to read space keys file oneshot cipher text",
- ))
- })?;
-
- key.decrypt_owned(&EncryptedBlock { nonce, cipher_text })
- .map_err(|e| Error::KeyStoreCrypto {
- source: e,
- context: "Failed to oneshot decrypt space keys file",
- })?
- } else {
- use decrypt::StreamDecryption;
-
- let mut nonce = StreamNonce::default();
-
- let mut key_store_bytes = Vec::with_capacity(
- (usize_file_len - size_of::()) / EncryptedBlock::CIPHER_TEXT_SIZE
- * EncryptedBlock::PLAIN_TEXT_SIZE,
- );
-
- file.read_exact(&mut nonce).await.map_err(|e| {
- FileIOError::from((
- keys_file_path,
- e,
- "Failed to read space keys file stream nonce",
- ))
- })?;
-
- key.decrypt(&nonce, &mut file, &mut key_store_bytes)
- .await
- .map_err(|e| Error::KeyStoreCrypto {
- source: e,
- context: "Failed to stream decrypt space keys file",
- })?;
-
- key_store_bytes
- };
-
- let this = rmp_serde::from_slice::(&key_store_bytes)
- .map_err(Error::KeyStoreDeserialization)?;
-
- #[cfg(debug_assertions)]
- {
- use std::fmt::Write;
- let mut key_hashes_log = String::new();
-
- this.keys.iter().for_each(|(group_pub_id, key_stack)| {
- writeln!(
- key_hashes_log,
- "Group: {group_pub_id:?} => KeyHashes: {:?}",
- key_stack
- .iter()
- .map(|(KeyHash(key_hash), _)| key_hash)
- .collect::>()
- )
- .expect("Failed to write to key hashes log");
- });
-
- tracing::info!("Loaded key hashes: {key_hashes_log}");
- }
-
- Ok(this)
- }
-}
-
-/// Zeroize our secret keys and scrambles up iroh's secret key that doesn't implement zeroize
-impl Zeroize for KeyStore {
- fn zeroize(&mut self) {
- self.iroh_secret_key = IrohSecretKey::generate();
- self.keys.values_mut().for_each(|group| {
- group
- .iter_mut()
- .map(|(_key_hash, key)| key)
- .for_each(Zeroize::zeroize);
- });
- self.keys = BTreeMap::new();
- }
-}
-
-impl Drop for KeyStore {
- fn drop(&mut self) {
- self.zeroize();
- }
-}
-
-impl ZeroizeOnDrop for KeyStore {}
diff --git a/core/crates/cloud-services/src/key_manager/mod.rs b/core/crates/cloud-services/src/key_manager/mod.rs
deleted file mode 100644
index 64007a190..000000000
--- a/core/crates/cloud-services/src/key_manager/mod.rs
+++ /dev/null
@@ -1,183 +0,0 @@
-use crate::Error;
-
-use sd_cloud_schema::{
- sync::{groups, KeyHash},
- NodeId, SecretKey as IrohSecretKey,
-};
-use sd_crypto::{cloud::secret_key::SecretKey, CryptoRng};
-use sd_utils::error::FileIOError;
-
-use std::{
- fmt,
- path::{Path, PathBuf},
-};
-
-use tokio::{fs, sync::RwLock};
-
-mod key_store;
-
-use key_store::KeyStore;
-
-const KEY_FILE_NAME: &str = "space.keys";
-
-pub struct KeyManager {
- master_key: SecretKey,
- keys_file_path: PathBuf,
- store: RwLock,
-}
-
-impl KeyManager {
- pub async fn new(
- master_key: SecretKey,
- iroh_secret_key: IrohSecretKey,
- data_directory: impl AsRef + Send,
- rng: &mut CryptoRng,
- ) -> Result {
- async fn inner(
- master_key: SecretKey,
- iroh_secret_key: IrohSecretKey,
- keys_file_path: PathBuf,
- rng: &mut CryptoRng,
- ) -> Result {
- let store = KeyStore::new(iroh_secret_key);
- store.encrypt(&master_key, rng, &keys_file_path).await?;
-
- Ok(KeyManager {
- master_key,
- keys_file_path,
- store: RwLock::new(store),
- })
- }
-
- inner(
- master_key,
- iroh_secret_key,
- data_directory.as_ref().join(KEY_FILE_NAME),
- rng,
- )
- .await
- }
-
- pub async fn load(
- master_key: SecretKey,
- data_directory: impl AsRef + Send,
- ) -> Result {
- async fn inner(
- master_key: SecretKey,
- keys_file_path: PathBuf,
- ) -> Result {
- Ok(KeyManager {
- store: RwLock::new(
- KeyStore::decrypt(
- &master_key,
- fs::metadata(&keys_file_path).await.map_err(|e| {
- FileIOError::from((
- &keys_file_path,
- e,
- "Failed to read space keys file",
- ))
- })?,
- &keys_file_path,
- )
- .await?,
- ),
- master_key,
- keys_file_path,
- })
- }
-
- inner(master_key, data_directory.as_ref().join(KEY_FILE_NAME)).await
- }
-
- pub async fn iroh_secret_key(&self) -> IrohSecretKey {
- self.store.read().await.iroh_secret_key()
- }
-
- pub async fn node_id(&self) -> NodeId {
- self.store.read().await.node_id()
- }
-
- pub async fn add_key(
- &self,
- group_pub_id: groups::PubId,
- key: SecretKey,
- rng: &mut CryptoRng,
- ) -> Result<(), Error> {
- let mut store = self.store.write().await;
- store.add_key(group_pub_id, key);
- // Keeping the write lock here, this way we ensure that we can't corrupt the file
- store
- .encrypt(&self.master_key, rng, &self.keys_file_path)
- .await
- }
-
- pub async fn add_key_with_hash(
- &self,
- group_pub_id: groups::PubId,
- key: SecretKey,
- key_hash: KeyHash,
- rng: &mut CryptoRng,
- ) -> Result<(), Error> {
- let mut store = self.store.write().await;
- store.add_key_with_hash(group_pub_id, key, key_hash);
- // Keeping the write lock here, this way we ensure that we can't corrupt the file
- store
- .encrypt(&self.master_key, rng, &self.keys_file_path)
- .await
- }
-
- pub async fn remove_group(
- &self,
- group_pub_id: groups::PubId,
- rng: &mut CryptoRng,
- ) -> Result<(), Error> {
- let mut store = self.store.write().await;
- store.remove_group(group_pub_id);
- // Keeping the write lock here, this way we ensure that we can't corrupt the file
- store
- .encrypt(&self.master_key, rng, &self.keys_file_path)
- .await
- }
-
- pub async fn add_many_keys(
- &self,
- group_pub_id: groups::PubId,
- keys: impl IntoIterator<
- Item = SecretKey,
- IntoIter = impl DoubleEndedIterator- + Send,
- > + Send,
- rng: &mut CryptoRng,
- ) -> Result<(), Error> {
- let mut store = self.store.write().await;
- store.add_many_keys(group_pub_id, keys);
- // Keeping the write lock here, this way we ensure that we can't corrupt the file
- store
- .encrypt(&self.master_key, rng, &self.keys_file_path)
- .await
- }
-
- pub async fn get_latest_key(
- &self,
- group_pub_id: groups::PubId,
- ) -> Option<(KeyHash, SecretKey)> {
- self.store.read().await.get_latest_key(group_pub_id)
- }
-
- pub async fn get_key(&self, group_pub_id: groups::PubId, hash: &KeyHash) -> Option
{
- self.store.read().await.get_key(group_pub_id, hash)
- }
-
- pub async fn get_group_keys(&self, group_pub_id: groups::PubId) -> Vec {
- self.store.read().await.get_group_keys(group_pub_id)
- }
-}
-
-impl fmt::Debug for KeyManager {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("KeyManager")
- .field("master_key", &"[REDACTED]")
- .field("keys_file_path", &self.keys_file_path)
- .field("store", &"[REDACTED]")
- .finish()
- }
-}
diff --git a/core/crates/cloud-services/src/lib.rs b/core/crates/cloud-services/src/lib.rs
deleted file mode 100644
index 064634fac..000000000
--- a/core/crates/cloud-services/src/lib.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-#![recursion_limit = "256"]
-#![warn(
- clippy::all,
- clippy::pedantic,
- clippy::correctness,
- clippy::perf,
- clippy::style,
- clippy::suspicious,
- clippy::complexity,
- clippy::nursery,
- clippy::unwrap_used,
- unused_qualifications,
- rust_2018_idioms,
- trivial_casts,
- trivial_numeric_casts,
- unused_allocation,
- clippy::unnecessary_cast,
- clippy::cast_lossless,
- clippy::cast_possible_truncation,
- clippy::cast_possible_wrap,
- clippy::cast_precision_loss,
- clippy::cast_sign_loss,
- clippy::dbg_macro,
- clippy::deprecated_cfg_attr,
- clippy::separated_literal_suffix,
- deprecated
-)]
-#![forbid(deprecated_in_future)]
-#![allow(clippy::missing_errors_doc, clippy::module_name_repetitions)]
-
-mod error;
-
-mod client;
-mod key_manager;
-mod p2p;
-mod sync;
-mod token_refresher;
-
-pub use client::CloudServices;
-pub use error::{Error, GetTokenError};
-pub use key_manager::KeyManager;
-pub use p2p::{
- CloudP2P, JoinSyncGroupResponse, JoinedLibraryCreateArgs, NotifyUser, Ticket, UserResponse,
-};
-pub use sync::{
- declare_actors as declare_cloud_sync, SyncActors as CloudSyncActors,
- SyncActorsState as CloudSyncActorsState,
-};
-
-// Re-exports
-pub use quic_rpc::transport::quinn::QuinnConnector;
-
-// Export URL for the auth server
-pub const AUTH_SERVER_URL: &str = "https://auth.spacedrive.com";
-// pub const AUTH_SERVER_URL: &str = "http://localhost:9420";
diff --git a/core/crates/cloud-services/src/p2p/mod.rs b/core/crates/cloud-services/src/p2p/mod.rs
deleted file mode 100644
index 1b390205d..000000000
--- a/core/crates/cloud-services/src/p2p/mod.rs
+++ /dev/null
@@ -1,272 +0,0 @@
-use crate::{sync::ReceiveAndIngestNotifiers, CloudServices, Error};
-
-use sd_cloud_schema::{
- cloud_p2p::{authorize_new_device_in_sync_group, CloudP2PALPN, CloudP2PError},
- devices::{self, Device},
- libraries,
- sync::groups::{self, GroupWithDevices},
- SecretKey as IrohSecretKey,
-};
-use sd_crypto::{CryptoRng, SeedableRng};
-use sd_prisma::prisma::file_path::cas_id;
-
-use std::{path::PathBuf, sync::Arc, time::Duration};
-
-use iroh::{
- discovery::{
- dns::DnsDiscovery, local_swarm_discovery::LocalSwarmDiscovery, pkarr::dht::DhtDiscovery,
- ConcurrentDiscovery, Discovery,
- },
- Endpoint, NodeId, RelayMap, RelayMode, RelayUrl,
-};
-use reqwest::Url;
-use serde::{Deserialize, Serialize};
-use tokio::{spawn, sync::oneshot, time::sleep};
-use tracing::{debug, error, warn};
-
-mod new_sync_messages_notifier;
-mod runner;
-
-use runner::Runner;
-
-#[derive(Debug)]
-pub struct JoinedLibraryCreateArgs {
- pub pub_id: libraries::PubId,
- pub name: String,
- pub description: Option,
-}
-
-#[derive(Debug)]
-pub struct RecivedGetThumbnailArgs {
- pub cas_id: cas_id::Type,
- pub error: Option,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, specta::Type)]
-#[serde(transparent)]
-#[repr(transparent)]
-#[specta(rename = "CloudP2PTicket")]
-pub struct Ticket(u64);
-
-#[derive(Debug, Serialize, specta::Type)]
-#[serde(tag = "kind", content = "data")]
-#[specta(rename = "CloudP2PNotifyUser")]
-pub enum NotifyUser {
- ReceivedJoinSyncGroupRequest {
- ticket: Ticket,
- asking_device: Device,
- sync_group: GroupWithDevices,
- },
- ReceivedJoinSyncGroupResponse {
- response: JoinSyncGroupResponse,
- sync_group: GroupWithDevices,
- },
- SendingJoinSyncGroupResponseError {
- error: JoinSyncGroupError,
- sync_group: GroupWithDevices,
- },
- TimedOutJoinRequest {
- device: Device,
- succeeded: bool,
- },
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, specta::Type)]
-pub enum JoinSyncGroupError {
- Communication,
- InternalServer,
- Auth,
-}
-
-#[derive(Debug, Serialize, specta::Type)]
-pub enum JoinSyncGroupResponse {
- Accepted { authorizor_device: Device },
- Failed(CloudP2PError),
- CriticalError,
-}
-
-#[derive(Debug, Clone, Serialize, Deserialize, specta::Type)]
-pub struct BasicLibraryCreationArgs {
- pub id: libraries::PubId,
- pub name: String,
- pub description: Option,
-}
-
-#[derive(Debug, Deserialize, specta::Type)]
-#[serde(tag = "kind", content = "data")]
-#[specta(rename = "CloudP2PUserResponse")]
-pub enum UserResponse {
- AcceptDeviceInSyncGroup {
- ticket: Ticket,
- accepted: Option,
- },
-}
-#[derive(Debug, Clone)]
-pub struct CloudP2P {
- msgs_tx: flume::Sender,
-}
-
-impl CloudP2P {
- pub async fn new(
- current_device_pub_id: devices::PubId,
- cloud_services: &CloudServices,
- mut rng: CryptoRng,
- iroh_secret_key: IrohSecretKey,
- dns_origin_domain: String,
- dns_pkarr_url: Url,
- relay_url: RelayUrl,
- data_directory: PathBuf,
- ) -> Result {
- let dht_discovery = DhtDiscovery::builder()
- .secret_key(iroh_secret_key.clone())
- .pkarr_relay(dns_pkarr_url)
- .build()
- .map_err(Error::DhtDiscoveryInit)?;
-
- let endpoint = Endpoint::builder()
- .alpns(vec![CloudP2PALPN::LATEST.to_vec()])
- .discovery(Box::new(ConcurrentDiscovery::from_services(vec![
- Box::new(DnsDiscovery::new(dns_origin_domain)),
- Box::new(
- LocalSwarmDiscovery::new(iroh_secret_key.public())
- .map_err(Error::LocalSwarmDiscoveryInit)?,
- ),
- Box::new(dht_discovery.clone()),
- ])))
- .secret_key(iroh_secret_key)
- .relay_mode(RelayMode::Custom(RelayMap::from_url(relay_url)))
- .bind()
- .await
- .map_err(Error::CreateCloudP2PEndpoint)?;
-
- spawn({
- let endpoint = endpoint.clone();
- async move {
- loop {
- let Ok(node_addr) = endpoint.node_addr().await.map_err(|e| {
- warn!(?e, "Failed to get direct addresses to force publish on DHT");
- }) else {
- sleep(Duration::from_secs(5)).await;
- continue;
- };
-
- debug!("Force publishing peer on DHT");
- return dht_discovery.publish(&node_addr.info);
- }
- }
- });
-
- let (msgs_tx, msgs_rx) = flume::bounded(16);
-
- spawn({
- let runner = Runner::new(
- current_device_pub_id,
- cloud_services,
- msgs_tx.clone(),
- endpoint,
- data_directory,
- )
- .await?;
- let user_response_rx = cloud_services.user_response_rx.clone();
-
- async move {
- // All cloned runners share a single state with internal mutability
- while let Err(e) = spawn(runner.clone().run(
- msgs_rx.clone(),
- user_response_rx.clone(),
- CryptoRng::from_seed(rng.generate_fixed()),
- ))
- .await
- {
- if e.is_panic() {
- error!("Cloud P2P runner panicked");
- } else {
- break;
- }
- }
- }
- });
-
- Ok(Self { msgs_tx })
- }
-
- /// Requests the device with the given connection ID asking for permission to the current device
- /// to join the sync group
- ///
- /// # Panics
- /// Will panic if the actor channel is closed, which should never happen
- pub async fn request_join_sync_group(
- &self,
- devices_in_group: Vec<(devices::PubId, NodeId)>,
- req: authorize_new_device_in_sync_group::Request,
- tx: oneshot::Sender,
- ) {
- self.msgs_tx
- .send_async(runner::Message::Request(runner::Request::JoinSyncGroup {
- req,
- devices_in_group,
- tx,
- }))
- .await
- .expect("Channel closed");
- }
-
- /// Register a notifier for the desired sync group, which will notify the receiver actor when
- /// new sync messages arrive through cloud p2p notification requests.
- ///
- /// # Panics
- /// Will panic if the actor channel is closed, which should never happen
- pub async fn register_sync_messages_receiver_notifier(
- &self,
- sync_group_pub_id: groups::PubId,
- notifier: Arc,
- ) {
- self.msgs_tx
- .send_async(runner::Message::RegisterSyncMessageNotifier((
- sync_group_pub_id,
- notifier,
- )))
- .await
- .expect("Channel closed");
- }
-
- /// Emit a notification that new sync messages were sent to cloud, so other devices should pull
- /// them as soon as possible.
- ///
- /// # Panics
- /// Will panic if the actor channel is closed, which should never happen
- pub async fn notify_new_sync_messages(&self, group_pub_id: groups::PubId) {
- self.msgs_tx
- .send_async(runner::Message::NotifyPeersSyncMessages(group_pub_id))
- .await
- .expect("Channel closed");
- }
-
- /// Requests the binary of a thumbnail from a specific device endpoint
- ///
- /// # Panics
- /// Will panic if the actor channel is closed, which should never happen
- pub async fn request_thumbnail_data(
- &self,
- device_pub_id: devices::PubId,
- cas_id: cas_id::Type,
- library_pub_id: libraries::PubId,
- tx: oneshot::Sender,
- ) {
- self.msgs_tx
- .send_async(runner::Message::Request(runner::Request::GetThumbnail {
- device_pub_id,
- cas_id,
- library_pub_id,
- tx,
- }))
- .await
- .expect("Channel closed");
- }
-}
-
-impl Drop for CloudP2P {
- fn drop(&mut self) {
- self.msgs_tx.send(runner::Message::Stop).ok();
- }
-}
diff --git a/core/crates/cloud-services/src/p2p/new_sync_messages_notifier.rs b/core/crates/cloud-services/src/p2p/new_sync_messages_notifier.rs
deleted file mode 100644
index 1d754cc73..000000000
--- a/core/crates/cloud-services/src/p2p/new_sync_messages_notifier.rs
+++ /dev/null
@@ -1,154 +0,0 @@
-use crate::{token_refresher::TokenRefresher, Error};
-
-use sd_cloud_schema::{
- cloud_p2p::{Client, CloudP2PALPN},
- devices,
- sync::groups,
-};
-
-use std::time::Duration;
-
-use futures_concurrency::future::Join;
-use iroh::{Endpoint, NodeId};
-use quic_rpc::{transport::quinn::QuinnConnector, RpcClient};
-use tokio::time::Instant;
-use tracing::{debug, error, instrument, warn};
-
-use super::runner::Message;
-
-const CACHED_MAX_DURATION: Duration = Duration::from_secs(60 * 5);
-
-pub async fn dispatch_notifier(
- group_pub_id: groups::PubId,
- device_pub_id: devices::PubId,
- devices: Option<(Instant, Vec<(devices::PubId, NodeId)>)>,
- msgs_tx: flume::Sender,
- cloud_services: sd_cloud_schema::Client<
- QuinnConnector,
- >,
- token_refresher: TokenRefresher,
- endpoint: Endpoint,
-) {
- match notify_peers(
- group_pub_id,
- device_pub_id,
- devices,
- cloud_services,
- token_refresher,
- endpoint,
- )
- .await
- {
- Ok((true, devices)) => {
- if msgs_tx
- .send_async(Message::UpdateCachedDevices((group_pub_id, devices)))
- .await
- .is_err()
- {
- warn!("Failed to send update cached devices message to update cached devices");
- }
- }
-
- Ok((false, _)) => {}
-
- Err(e) => {
- error!(?e, "Failed to notify peers");
- }
- }
-}
-
-#[instrument(skip(cloud_services, token_refresher, endpoint))]
-async fn notify_peers(
- group_pub_id: groups::PubId,
- device_pub_id: devices::PubId,
- devices: Option<(Instant, Vec<(devices::PubId, NodeId)>)>,
- cloud_services: sd_cloud_schema::Client<
- QuinnConnector,
- >,
- token_refresher: TokenRefresher,
- endpoint: Endpoint,
-) -> Result<(bool, Vec<(devices::PubId, NodeId)>), Error> {
- let (devices, update_cache) = match devices {
- Some((when, devices)) if when.elapsed() < CACHED_MAX_DURATION => (devices, false),
- _ => {
- debug!("Fetching devices connection ids for group");
- let groups::get::Response(groups::get::ResponseKind::DevicesConnectionIds(devices)) =
- cloud_services
- .sync()
- .groups()
- .get(groups::get::Request {
- access_token: token_refresher.get_access_token().await?,
- pub_id: group_pub_id,
- kind: groups::get::RequestKind::DevicesConnectionIds,
- })
- .await??
- else {
- unreachable!("Only DevicesConnectionIds response is expected, as we requested it");
- };
-
- (devices, true)
- }
- };
-
- send_notifications(group_pub_id, device_pub_id, &devices, &endpoint).await;
-
- Ok((update_cache, devices))
-}
-
-async fn send_notifications(
- group_pub_id: groups::PubId,
- device_pub_id: devices::PubId,
- devices: &[(devices::PubId, NodeId)],
- endpoint: &Endpoint,
-) {
- devices
- .iter()
- .filter(|(peer_device_pub_id, _)| *peer_device_pub_id != device_pub_id)
- .map(|(peer_device_pub_id, connection_id)| async move {
- if let Err(e) =
- connect_and_send_notification(group_pub_id, device_pub_id, connection_id, endpoint)
- .await
- {
- // Using just a debug log here because we don't want to spam the logs with
- // every single notification failure, as this is more a nice to have feature than a
- // critical one
- debug!(?e, %peer_device_pub_id, "Failed to send new sync messages notification to peer");
- } else {
- debug!(%peer_device_pub_id, "Sent new sync messages notification to peer");
- }
- })
- .collect::>()
- .join()
- .await;
-}
-
-async fn connect_and_send_notification(
- group_pub_id: groups::PubId,
- device_pub_id: devices::PubId,
- connection_id: &NodeId,
- endpoint: &Endpoint,
-) -> Result<(), Error> {
- let client = Client::new(RpcClient::new(QuinnConnector::from_connection(
- endpoint
- .connect(*connection_id, CloudP2PALPN::LATEST)
- .await
- .map_err(Error::ConnectToCloudP2PNode)?,
- )));
-
- if let Err(e) = client
- .notify_new_sync_messages(
- sd_cloud_schema::cloud_p2p::notify_new_sync_messages::Request {
- sync_group_pub_id: group_pub_id,
- device_pub_id,
- },
- )
- .await?
- {
- warn!(
- ?e,
- "This route shouldn't return an error, it's just a notification",
- );
- };
-
- Ok(())
-}
diff --git a/core/crates/cloud-services/src/p2p/runner.rs b/core/crates/cloud-services/src/p2p/runner.rs
deleted file mode 100644
index b7f0b5c07..000000000
--- a/core/crates/cloud-services/src/p2p/runner.rs
+++ /dev/null
@@ -1,1109 +0,0 @@
-use crate::{
- p2p::JoinSyncGroupError, sync::ReceiveAndIngestNotifiers, token_refresher::TokenRefresher,
- CloudServices, Error, KeyManager,
-};
-
-use sd_cloud_schema::{
- cloud_p2p::{
- self, authorize_new_device_in_sync_group, notify_new_sync_messages, Client, CloudP2PALPN,
- CloudP2PError, Service,
- },
- devices::{self, Device},
- libraries::{self},
- sync::groups,
-};
-use sd_crypto::{CryptoRng, SeedableRng};
-use sd_prisma::prisma::file_path::cas_id;
-
-use std::{
- collections::HashMap,
- path::PathBuf,
- pin::pin,
- sync::{
- atomic::{AtomicU64, Ordering},
- Arc,
- },
- time::Duration,
-};
-
-use dashmap::DashMap;
-use flume::SendError;
-use futures::StreamExt;
-use futures_concurrency::stream::Merge;
-use iroh::{key::PublicKey, Endpoint, NodeId};
-use quic_rpc::{
- server::{Accepting, RpcChannel, RpcServerError},
- transport::quinn::{QuinnConnector, QuinnListener},
- RpcClient, RpcServer,
-};
-use tokio::{
- spawn,
- sync::{oneshot, Mutex},
- task::JoinHandle,
- time::{interval, Instant, MissedTickBehavior},
-};
-use tokio_stream::wrappers::IntervalStream;
-use tracing::{debug, error, warn};
-
-use super::{
- new_sync_messages_notifier::dispatch_notifier, BasicLibraryCreationArgs, JoinSyncGroupResponse,
- JoinedLibraryCreateArgs, NotifyUser, RecivedGetThumbnailArgs, Ticket, UserResponse,
-};
-
-const TEN_SECONDS: Duration = Duration::from_secs(10);
-const FIVE_MINUTES: Duration = Duration::from_secs(60 * 5);
-
-#[allow(clippy::large_enum_variant)] // Ignoring because the enum Stop variant will only happen a single time ever
-pub enum Message {
- Request(Request),
- RegisterSyncMessageNotifier((groups::PubId, Arc)),
- NotifyPeersSyncMessages(groups::PubId),
- UpdateCachedDevices((groups::PubId, Vec<(devices::PubId, NodeId)>)),
- Stop,
-}
-
-pub enum Request {
- JoinSyncGroup {
- req: authorize_new_device_in_sync_group::Request,
- devices_in_group: Vec<(devices::PubId, NodeId)>,
- tx: oneshot::Sender,
- },
- GetThumbnail {
- device_pub_id: devices::PubId,
- cas_id: cas_id::Type,
- library_pub_id: libraries::PubId,
- tx: oneshot::Sender,
- },
-}
-
-/// We use internal mutability here, but don't worry because there will always be a single
-/// [`Runner`] running at a time, so the lock is never contended
-pub struct Runner {
- current_device_pub_id: devices::PubId,
- token_refresher: TokenRefresher,
- cloud_services: sd_cloud_schema::Client<
- QuinnConnector,
- >,
- msgs_tx: flume::Sender,
- endpoint: Endpoint,
- key_manager: Arc,
- ticketer: Arc,
- notify_user_tx: flume::Sender,
- sync_messages_receiver_notifiers_map:
- Arc>>,
- pending_sync_group_join_requests: Arc>>,
- cached_devices_per_group: HashMap)>,
- timeout_checker_buffer: Vec<(Ticket, PendingSyncGroupJoin)>,
- data_directory: PathBuf,
-}
-
-impl Clone for Runner {
- fn clone(&self) -> Self {
- Self {
- current_device_pub_id: self.current_device_pub_id,
- token_refresher: self.token_refresher.clone(),
- cloud_services: self.cloud_services.clone(),
- msgs_tx: self.msgs_tx.clone(),
- endpoint: self.endpoint.clone(),
- key_manager: Arc::clone(&self.key_manager),
- ticketer: Arc::clone(&self.ticketer),
- notify_user_tx: self.notify_user_tx.clone(),
- sync_messages_receiver_notifiers_map: Arc::clone(
- &self.sync_messages_receiver_notifiers_map,
- ),
- pending_sync_group_join_requests: Arc::clone(&self.pending_sync_group_join_requests),
- // Just cache the devices and their node_ids per group
- cached_devices_per_group: HashMap::new(),
- // This one is a temporary buffer only used for timeout checker
- timeout_checker_buffer: vec![],
- data_directory: self.data_directory.clone(),
- }
- }
-}
-
-struct PendingSyncGroupJoin {
- channel: RpcChannel>,
- request: authorize_new_device_in_sync_group::Request,
- this_device: Device,
- since: Instant,
-}
-
-type P2PServerEndpoint = QuinnListener;
-
-impl Runner {
- pub async fn new(
- current_device_pub_id: devices::PubId,
- cloud_services: &CloudServices,
- msgs_tx: flume::Sender,
- endpoint: Endpoint,
- data_directory: PathBuf,
- ) -> Result {
- Ok(Self {
- current_device_pub_id,
- token_refresher: cloud_services.token_refresher.clone(),
- cloud_services: cloud_services.client().await?,
- msgs_tx,
- endpoint,
- key_manager: cloud_services.key_manager().await?,
- ticketer: Arc::default(),
- notify_user_tx: cloud_services.notify_user_tx.clone(),
- sync_messages_receiver_notifiers_map: Arc::default(),
- pending_sync_group_join_requests: Arc::default(),
- cached_devices_per_group: HashMap::new(),
- timeout_checker_buffer: vec![],
- data_directory,
- })
- }
-
- pub async fn run(
- mut self,
- msgs_rx: flume::Receiver,
- user_response_rx: flume::Receiver,
- mut rng: CryptoRng,
- ) {
- // Ignoring because this is only used internally and I think that boxing will be more expensive than wasting
- // some extra bytes for smaller variants
- #[allow(clippy::large_enum_variant)]
- enum StreamMessage {
- AcceptResult(
- Result, RpcServerError>,
- ),
- Message(Message),
- UserResponse(UserResponse),
- Tick,
- }
-
- let mut ticker = interval(TEN_SECONDS);
- ticker.set_missed_tick_behavior(MissedTickBehavior::Skip);
-
- // FIXME(@fogodev): Update this function to use iroh-net transport instead of quinn
- // when it's implemented
- let (server, server_handle) = setup_server_endpoint(self.endpoint.clone());
-
- let mut msg_stream = pin!((
- async_stream::stream! {
- loop {
- yield StreamMessage::AcceptResult(server.accept().await);
- }
- },
- msgs_rx.stream().map(StreamMessage::Message),
- user_response_rx.stream().map(StreamMessage::UserResponse),
- IntervalStream::new(ticker).map(|_| StreamMessage::Tick),
- )
- .merge());
-
- while let Some(msg) = msg_stream.next().await {
- match msg {
- StreamMessage::AcceptResult(Ok(accepting)) => {
- let Ok((request, channel)) = accepting.read_first().await.map_err(|e| {
- error!(?e, "Failed to read first request from a new connection;");
- }) else {
- continue;
- };
-
- self.handle_request(request, channel).await;
- }
-
- StreamMessage::AcceptResult(Err(e)) => {
- // TODO(@fogodev): Maybe report this error to the user on a toast?
- error!(?e, "Error accepting connection;");
- }
-
- StreamMessage::Message(Message::Request(Request::JoinSyncGroup {
- req,
- devices_in_group,
- tx,
- })) => self.dispatch_join_requests(req, devices_in_group, &mut rng, tx),
-
- StreamMessage::Message(Message::Request(Request::GetThumbnail {
- device_pub_id,
- cas_id,
- library_pub_id,
- tx,
- })) => self.dispatch_get_thumbnail(device_pub_id, cas_id, library_pub_id, tx),
-
- StreamMessage::Message(Message::RegisterSyncMessageNotifier((
- group_pub_id,
- notifier,
- ))) => {
- self.sync_messages_receiver_notifiers_map
- .insert(group_pub_id, notifier);
- }
-
- StreamMessage::Message(Message::NotifyPeersSyncMessages(group_pub_id)) => {
- spawn(dispatch_notifier(
- group_pub_id,
- self.current_device_pub_id,
- self.cached_devices_per_group.get(&group_pub_id).cloned(),
- self.msgs_tx.clone(),
- self.cloud_services.clone(),
- self.token_refresher.clone(),
- self.endpoint.clone(),
- ));
- }
-
- StreamMessage::Message(Message::UpdateCachedDevices((
- group_pub_id,
- devices_connections_ids,
- ))) => {
- self.cached_devices_per_group
- .insert(group_pub_id, (Instant::now(), devices_connections_ids));
- }
-
- StreamMessage::UserResponse(UserResponse::AcceptDeviceInSyncGroup {
- ticket,
- accepted,
- }) => {
- self.handle_join_response(ticket, accepted).await;
- }
-
- StreamMessage::Tick => self.tick().await,
-
- StreamMessage::Message(Message::Stop) => {
- server_handle.abort();
- break;
- }
- }
- }
- }
-
- fn dispatch_join_requests(
- &self,
- req: authorize_new_device_in_sync_group::Request,
- devices_in_group: Vec<(devices::PubId, NodeId)>,
- rng: &mut CryptoRng,
- tx: oneshot::Sender,
- ) {
- async fn inner(
- key_manager: Arc,
- endpoint: Endpoint,
- mut rng: CryptoRng,
- req: authorize_new_device_in_sync_group::Request,
- devices_in_group: Vec<(devices::PubId, NodeId)>,
- tx: oneshot::Sender,
- ) -> Result {
- let group_pub_id = req.sync_group.pub_id;
- loop {
- let client =
- match connect_to_first_available_client(&endpoint, &devices_in_group).await {
- Ok(client) => client,
- Err(e) => {
- return Ok(JoinSyncGroupResponse::Failed(e));
- }
- };
-
- match client
- .authorize_new_device_in_sync_group(req.clone())
- .await?
- {
- Ok(authorize_new_device_in_sync_group::Response {
- authorizor_device,
- keys,
- library_pub_id,
- library_name,
- library_description,
- }) => {
- debug!(
- device_pub_id = %authorizor_device.pub_id,
- %group_pub_id,
- keys_count = keys.len(),
- %library_pub_id,
- library_name,
- "Received join sync group response"
- );
-
- key_manager
- .add_many_keys(
- group_pub_id,
- keys.into_iter().map(|key| {
- key.as_slice()
- .try_into()
- .expect("critical error, backend has invalid secret keys")
- }),
- &mut rng,
- )
- .await?;
-
- if tx
- .send(JoinedLibraryCreateArgs {
- pub_id: library_pub_id,
- name: library_name,
- description: library_description,
- })
- .is_err()
- {
- error!("Failed to handle library creation locally from received library data");
- return Ok(JoinSyncGroupResponse::CriticalError);
- }
-
- return Ok(JoinSyncGroupResponse::Accepted { authorizor_device });
- }
-
- // In case of timeout, we will try again
- Err(CloudP2PError::TimedOut) => continue,
-
- Err(e) => return Ok(JoinSyncGroupResponse::Failed(e)),
- }
- }
- }
-
- spawn({
- let endpoint = self.endpoint.clone();
- let notify_user_tx = self.notify_user_tx.clone();
- let key_manager = Arc::clone(&self.key_manager);
- let rng = CryptoRng::from_seed(rng.generate_fixed());
- async move {
- let sync_group = req.sync_group.clone();
-
- if let Err(SendError(response)) = notify_user_tx
- .send_async(NotifyUser::ReceivedJoinSyncGroupResponse {
- response: inner(key_manager, endpoint, rng, req, devices_in_group, tx)
- .await
- .unwrap_or_else(|e| {
- error!(
- ?e,
- "Failed to issue authorize new device in sync group request;"
- );
- JoinSyncGroupResponse::CriticalError
- }),
- sync_group,
- })
- .await
- {
- error!(?response, "Failed to send response to user;");
- }
- }
- });
- }
-
- #[allow(clippy::too_many_lines)]
- fn dispatch_get_thumbnail(
- &self,
- device_pub_id: devices::PubId,
- cas_id: cas_id::Type,
- library_pub_id: libraries::PubId,
- tx: oneshot::Sender,
- ) {
- debug!(?device_pub_id, ?cas_id, "Received request for thumbnail");
- let current_device_pub_id = self.current_device_pub_id;
- let cas_id_clone = cas_id.clone();
-
- // Put tx in an Arc to allow multiple references to it
- let tx = Arc::new(Mutex::new(Some(tx)));
-
- let device_connection = self
- .cached_devices_per_group
- .values()
- .find(|(_, devices)| devices.iter().any(|(pub_id, _)| pub_id == &device_pub_id))
- .and_then(|(_, devices)| devices.iter().find(|(pub_id, _)| pub_id == &device_pub_id))
- .ok_or_else(|| {
- error!("Failed to find device in the cached devices list");
-
- // Use a clone of the channel to send the error response
- let tx_clone = tx.clone();
- spawn(async move {
- if let Some(tx) = tx_clone.lock().await.take() {
- if tx
- .send(RecivedGetThumbnailArgs {
- cas_id: cas_id_clone.clone(),
- error: Some(Error::DeviceNotFound),
- })
- .is_err()
- {
- error!("Failed to send response to user;");
- }
- }
- });
- })
- .expect("Device must be in the cached devices list");
-
- let (_, device_connection_id) = device_connection;
-
- debug!("Device Connection ID: {:?}", device_connection_id);
- let data_dir_clone = self.data_directory.clone();
-
- // Spawn a separate task to avoid blocking the runner
- spawn({
- let endpoint = self.endpoint.clone();
- let device_connection_id = *device_connection_id;
- let tx = tx.clone();
- let cas_id_clone_clone = cas_id.clone();
-
- async move {
- // Connect to the device
- let client =
- match connect_to_specific_client(&endpoint, &device_connection_id).await {
- Ok(client) => client,
- Err(e) => {
- error!(?e, "Failed to connect to device");
- // Send the error through the channel
- if let Some(tx) = tx.lock().await.take() {
- if tx
- .send(RecivedGetThumbnailArgs {
- cas_id: cas_id_clone_clone,
- error: Some(Error::DeviceNotFound),
- })
- .is_err()
- {
- error!("Failed to send response to user;");
- }
- }
- return;
- }
- };
-
- // Create the request
- let request = cloud_p2p::get_thumbnail::Request {
- cas_id: cas_id_clone_clone.clone().unwrap_or_default(),
- device_pub_id: current_device_pub_id,
- library_pub_id,
- };
-
- // Send the request
- match client.get_thumbnail(request).await {
- Ok(Ok(cloud_p2p::get_thumbnail::Response { thumbnail })) => {
- debug!(?cas_id, "Successfully received thumbnail");
-
- // Convert cas_id to a string
- let cas_id_str = cas_id_clone_clone.clone().unwrap_or_default();
-
- // If we received a thumbnail, try to save it locally
- if let Some(thumbnail_data) = &thumbnail {
- // Try to save the thumbnail, but don't fail if saving fails
- if let Err(e) = save_remote_thumbnail(
- &cas_id_str,
- thumbnail_data,
- data_dir_clone,
- library_pub_id,
- )
- .await
- {
- error!(?e, "Failed to save remote thumbnail locally, but continuing with response");
- }
- }
-
- // Send the response via the oneshot channel
- if let Some(tx) = tx.lock().await.take() {
- if tx
- .send(RecivedGetThumbnailArgs {
- cas_id: cas_id_clone_clone.clone(),
- error: None,
- })
- .is_err()
- {
- error!("Failed to send thumbnail response to user");
- }
- }
- }
- Ok(Err(e)) => {
- error!(?e, "Remote device returned error for thumbnail request");
- // Send the error through the channel
- if let Some(tx) = tx.lock().await.take() {
- if tx
- .send(RecivedGetThumbnailArgs {
- cas_id: cas_id_clone_clone.clone(),
- error: Some(Error::RemoteDeviceError),
- })
- .is_err()
- {
- error!("Failed to send response to user;");
- }
- }
- }
- Err(e) => {
- error!(?e, "Failed to send thumbnail request to remote device");
- // Send the error through the channel
- if let Some(tx) = tx.lock().await.take() {
- if tx
- .send(RecivedGetThumbnailArgs {
- cas_id: cas_id_clone_clone.clone(),
- error: Some(Error::InternalError),
- })
- .is_err()
- {
- error!("Failed to send response to user;");
- }
- }
- }
- }
- }
- });
- }
-
- #[allow(clippy::too_many_lines)]
- async fn handle_request(
- &self,
- request: cloud_p2p::Request,
- channel: RpcChannel,
- ) {
- match request {
- cloud_p2p::Request::AuthorizeNewDeviceInSyncGroup(
- authorize_new_device_in_sync_group::Request {
- sync_group,
- asking_device,
- },
- ) => {
- let ticket = Ticket(self.ticketer.fetch_add(1, Ordering::Relaxed));
- let this_device = sync_group
- .devices
- .iter()
- .find(|device| device.pub_id == self.current_device_pub_id)
- .expect(
- "current device must be in the sync group, otherwise we wouldn't be here",
- )
- .clone();
-
- self.notify_user_tx
- .send_async(NotifyUser::ReceivedJoinSyncGroupRequest {
- ticket,
- asking_device: asking_device.clone(),
- sync_group: sync_group.clone(),
- })
- .await
- .expect("notify_user_tx must never closes!");
-
- self.pending_sync_group_join_requests.lock().await.insert(
- ticket,
- PendingSyncGroupJoin {
- channel,
- request: authorize_new_device_in_sync_group::Request {
- sync_group,
- asking_device,
- },
- this_device,
- since: Instant::now(),
- },
- );
- }
-
- cloud_p2p::Request::NotifyNewSyncMessages(req) => {
- if let Err(e) = channel
- .rpc(
- req,
- (),
- |(),
- notify_new_sync_messages::Request {
- sync_group_pub_id,
- device_pub_id,
- }| async move {
- debug!(%sync_group_pub_id, %device_pub_id, "Received new sync messages notification");
- if let Some(notifier) = self
- .sync_messages_receiver_notifiers_map
- .get(&sync_group_pub_id)
- {
- notifier.notify_receiver();
- } else {
- warn!("Received new sync messages notification for unknown sync group");
- }
-
- Ok(notify_new_sync_messages::Response)
- },
- )
- .await
- {
- error!(
- ?e,
- "Failed to reply to new sync messages notification request"
- );
- }
- }
-
- cloud_p2p::Request::GetThumbnail(req) => {
- if let Err(e) = channel
- .rpc(
- req,
- (),
- |(),
- cloud_p2p::get_thumbnail::Request {
- cas_id,
- device_pub_id,
- library_pub_id,
- }| async move {
- debug!(
- ?cas_id,
- "Received thumbnail request from device {:?}", device_pub_id
- );
-
- match fetch_local_thumbnail(
- Some(cas_id.clone()),
- self.data_directory.clone(),
- library_pub_id,
- )
- .await
- {
- Ok(Some(thumbnail_data)) => {
- debug!(?cas_id, "Found thumbnail locally");
- Ok(cloud_p2p::get_thumbnail::Response {
- thumbnail: Some(thumbnail_data),
- })
- }
- Ok(None) => {
- debug!(?cas_id, "Thumbnail not found locally");
- Err(CloudP2PError::Rejected)
- }
- Err(e) => {
- error!(?e, ?cas_id, "Error fetching thumbnail");
- Err(CloudP2PError::Rejected)
- }
- }
- },
- )
- .await
- {
- error!(?e, "Failed to send get thumbnail response;");
- }
- }
- }
- }
-
- async fn handle_join_response(
- &self,
- ticket: Ticket,
- accepted: Option,
- ) {
- let Some(PendingSyncGroupJoin {
- channel,
- request,
- this_device,
- ..
- }) = self
- .pending_sync_group_join_requests
- .lock()
- .await
- .remove(&ticket)
- else {
- warn!("Received join response for unknown ticket; We probably timed out this request already");
- return;
- };
-
- let sync_group = request.sync_group.clone();
- let asking_device_pub_id = request.asking_device.pub_id;
-
- let was_accepted = accepted.is_some();
-
- let response = if let Some(BasicLibraryCreationArgs {
- id: library_pub_id,
- name: library_name,
- description: library_description,
- }) = accepted
- {
- Ok(authorize_new_device_in_sync_group::Response {
- authorizor_device: this_device,
- keys: self
- .key_manager
- .get_group_keys(request.sync_group.pub_id)
- .await
- .into_iter()
- .map(Into::into)
- .collect(),
- library_pub_id,
- library_name,
- library_description,
- })
- } else {
- Err(CloudP2PError::Rejected)
- };
-
- if let Err(e) = channel
- .rpc(request, (), |(), _req| async move { response })
- .await
- {
- error!(?e, "Failed to send response to user;");
- self.notify_join_error(sync_group, JoinSyncGroupError::Communication)
- .await;
-
- return;
- }
-
- if was_accepted {
- let Ok(access_token) = self
- .token_refresher
- .get_access_token()
- .await
- .map_err(|e| error!(?e, "Failed to get access token;"))
- else {
- self.notify_join_error(sync_group, JoinSyncGroupError::Auth)
- .await;
- return;
- };
-
- match self
- .cloud_services
- .sync()
- .groups()
- .reply_join_request(groups::reply_join_request::Request {
- access_token,
- group_pub_id: sync_group.pub_id,
- authorized_device_pub_id: asking_device_pub_id,
- authorizor_device_pub_id: self.current_device_pub_id,
- })
- .await
- {
- Ok(Ok(groups::reply_join_request::Response)) => {
- // Everything is Awesome!
- }
- Ok(Err(e)) => {
- error!(?e, "Failed to reply to join request");
- self.notify_join_error(sync_group, JoinSyncGroupError::InternalServer)
- .await;
- }
- Err(e) => {
- error!(?e, "Failed to send reply to join request");
- self.notify_join_error(sync_group, JoinSyncGroupError::Communication)
- .await;
- }
- }
- }
- }
-
- async fn notify_join_error(
- &self,
- sync_group: groups::GroupWithDevices,
- error: JoinSyncGroupError,
- ) {
- self.notify_user_tx
- .send_async(NotifyUser::SendingJoinSyncGroupResponseError { error, sync_group })
- .await
- .expect("notify_user_tx must never closes!");
- }
-
- async fn tick(&mut self) {
- self.timeout_checker_buffer.clear();
-
- let mut pending_sync_group_join_requests =
- self.pending_sync_group_join_requests.lock().await;
-
- for (ticket, pending_sync_group_join) in pending_sync_group_join_requests.drain() {
- if pending_sync_group_join.since.elapsed() > FIVE_MINUTES {
- let PendingSyncGroupJoin {
- channel, request, ..
- } = pending_sync_group_join;
-
- let asking_device = request.asking_device.clone();
-
- let notify_message = match channel
- .rpc(request, (), |(), _req| async move {
- Err(CloudP2PError::TimedOut)
- })
- .await
- {
- Ok(()) => NotifyUser::TimedOutJoinRequest {
- device: asking_device,
- succeeded: true,
- },
- Err(e) => {
- error!(?e, "Failed to send timed out response to user;");
- NotifyUser::TimedOutJoinRequest {
- device: asking_device,
- succeeded: false,
- }
- }
- };
-
- self.notify_user_tx
- .send_async(notify_message)
- .await
- .expect("notify_user_tx must never closes!");
- } else {
- self.timeout_checker_buffer
- .push((ticket, pending_sync_group_join));
- }
- }
-
- pending_sync_group_join_requests.extend(self.timeout_checker_buffer.drain(..));
- }
-}
-
-async fn connect_to_first_available_client(
- endpoint: &Endpoint,
- devices_in_group: &[(devices::PubId, NodeId)],
-) -> Result>, CloudP2PError> {
- for (device_pub_id, device_connection_id) in devices_in_group {
- if let Ok(connection) = endpoint
- .connect(*device_connection_id, CloudP2PALPN::LATEST)
- .await
- .map_err(
- |e| error!(?e, %device_pub_id, "Failed to connect to authorizor device candidate"),
- ) {
- debug!(%device_pub_id, "Connected to authorizor device candidate");
-
- return Ok(Client::new(RpcClient::new(
- QuinnConnector::from_connection(connection),
- )));
- }
- }
-
- Err(CloudP2PError::UnableToConnect)
-}
-
-async fn connect_to_specific_client(
- endpoint: &Endpoint,
- device_connection_id: &PublicKey,
-) -> Result>, CloudP2PError> {
- // Get the connection id by fetching using the device pub id
- let connection = endpoint
- .connect(*device_connection_id, CloudP2PALPN::LATEST)
- .await
- .map_err(|e| {
- error!(?e, "Failed to connect to authorizor device candidate");
- CloudP2PError::UnableToConnect
- })?;
- debug!(%device_connection_id, "Connected to authorizor device candidate");
- Ok(Client::new(RpcClient::new(
- QuinnConnector::from_connection(connection),
- )))
-}
-
-fn setup_server_endpoint(
- endpoint: Endpoint,
-) -> (RpcServer, JoinHandle<()>) {
- let local_addr = {
- let (ipv4_addr, maybe_ipv6_addr) = endpoint.bound_sockets();
- // Trying to give preference to IPv6 addresses because it's 2024
- maybe_ipv6_addr.unwrap_or(ipv4_addr)
- };
-
- let (connections_tx, connections_rx) = flume::bounded(16);
-
- (
- RpcServer::new(QuinnListener::handle_connections(
- connections_rx,
- local_addr,
- )),
- spawn(async move {
- while let Some(connecting) = endpoint.accept().await {
- if let Ok(connection) = connecting.await.map_err(|e| {
- warn!(?e, "Cloud P2P failed to accept connection");
- }) {
- if connections_tx.send_async(connection).await.is_err() {
- warn!("Connection receiver dropped");
- break;
- }
- }
- }
- }),
- )
-}
-
-async fn fetch_local_thumbnail(
- cas_id: cas_id::Type,
- data_directory: PathBuf,
- library_pub_id: libraries::PubId,
-) -> Result>, Error> {
- use tokio::fs;
- use tracing::{debug, error};
-
- debug!(?cas_id, "Fetching thumbnail from local storage");
-
- // Convert cas_id to a string
- let cas_id = cas_id.unwrap_or_default();
-
- let cas_id = sd_core_prisma_helpers::CasId::from(cas_id);
-
- let thumbnails_directory =
- sd_core_heavy_lifting::media_processor::get_thumbnails_directory(data_directory);
-
- // Get the shard hex for the cas_id
- let shard_hex = sd_core_heavy_lifting::media_processor::get_shard_hex(&cas_id);
-
- // First try to find the thumbnail in the specific library folder
- let library_path = thumbnails_directory.join(library_pub_id.to_string());
- let shard_path = library_path.join(shard_hex);
- let thumbnail_path = shard_path.join(format!("{}.webp", cas_id.as_str()));
-
- debug!("Checking for thumbnail at {:?}", thumbnail_path);
-
- // If the thumbnail exists in the specific library folder, read it
- if fs::metadata(&thumbnail_path).await.is_ok() {
- match fs::read(&thumbnail_path).await {
- Ok(data) => {
- debug!("Found thumbnail at {:?}", thumbnail_path);
- return Ok(Some(data));
- }
- Err(e) => {
- error!(?e, "Failed to read thumbnail file");
- return Err(Error::InternalError);
- }
- }
- }
-
- // If not found in the specific library, try the ephemeral directory
- let ephemeral_dir = thumbnails_directory.join("ephemeral");
- let ephemeral_shard_path = ephemeral_dir.join(shard_hex);
- let ephemeral_thumbnail_path = ephemeral_shard_path.join(format!("{}.webp", cas_id.as_str()));
-
- debug!(
- "Checking for thumbnail in ephemeral at {:?}",
- ephemeral_thumbnail_path
- );
-
- // If the thumbnail exists in ephemeral, read it
- if fs::metadata(&ephemeral_thumbnail_path).await.is_ok() {
- match fs::read(&ephemeral_thumbnail_path).await {
- Ok(data) => {
- debug!("Found thumbnail at {:?}", ephemeral_thumbnail_path);
- return Ok(Some(data));
- }
- Err(e) => {
- error!(?e, "Failed to read thumbnail file");
- return Err(Error::InternalError);
- }
- }
- }
-
- // If we still don't have the thumbnail, search all library folders as a fallback
- // This is to handle cases where the library ID might have changed
- let Ok(mut directories) = fs::read_dir(&thumbnails_directory).await else {
- debug!("No thumbnails directory found");
- return Ok(None);
- };
-
- // Try to find the thumbnail in any other library directories
- while let Ok(Some(entry)) = directories.next_entry().await {
- let dir_path = entry.path();
-
- // Skip files and already checked directories
- if !dir_path.is_dir() || dir_path == library_path || dir_path == ephemeral_dir {
- continue;
- }
-
- // Check if thumbnail exists in this directory
- let other_shard_path = dir_path.join(shard_hex);
- let other_thumbnail_path = other_shard_path.join(format!("{}.webp", cas_id.as_str()));
-
- debug!("Checking for thumbnail at {:?}", other_thumbnail_path);
-
- if fs::metadata(&other_thumbnail_path).await.is_ok() {
- match fs::read(&other_thumbnail_path).await {
- Ok(data) => {
- debug!("Found thumbnail at {:?}", other_thumbnail_path);
- return Ok(Some(data));
- }
- Err(e) => {
- error!(?e, "Failed to read thumbnail file");
- return Err(Error::InternalError);
- }
- }
- }
- }
-
- // If we get here, the thumbnail doesn't exist anywhere
- debug!("Thumbnail not found for {}", cas_id.as_str());
- Ok(None)
-}
-
-async fn save_remote_thumbnail(
- cas_id: &str,
- thumbnail_data: &[u8],
- data_directory: PathBuf,
- library_pub_id: libraries::PubId,
-) -> Result {
- use tokio::fs;
- use tracing::{debug, error};
-
- debug!(?cas_id, "Saving remote thumbnail to local storage");
-
- // Convert to CasId for path computation
- let cas_id = sd_core_prisma_helpers::CasId::from(cas_id);
-
- // Get the thumbnails directory
- let thumbnails_directory =
- sd_core_heavy_lifting::media_processor::get_thumbnails_directory(data_directory);
- let library_dir = thumbnails_directory.join(library_pub_id.to_string());
-
- // Get the shard hex for organizing thumbnails
- let shard_hex = sd_core_heavy_lifting::media_processor::get_shard_hex(&cas_id);
-
- // Create the full directory path
- let shard_dir = library_dir.join(shard_hex);
-
- // Create the directories if they don't exist
- if let Err(e) = fs::create_dir_all(&shard_dir).await {
- error!(?e, "Failed to create thumbnail directory structure in library folder, falling back to ephemeral");
-
- // If we can't create in library folder, fall back to ephemeral
- let ephemeral_dir = thumbnails_directory.join("ephemeral");
- let ephemeral_shard_dir = ephemeral_dir.join(shard_hex);
-
- if let Err(e) = fs::create_dir_all(&ephemeral_shard_dir).await {
- error!(
- ?e,
- "Failed to create thumbnail directory structure in ephemeral folder"
- );
- return Err(Error::InternalError);
- }
-
- // Create the full path for the thumbnail in ephemeral
- let thumbnail_path = ephemeral_shard_dir.join(format!("{}.webp", cas_id.as_str()));
-
- // Write the thumbnail data to disk
- match fs::write(&thumbnail_path, thumbnail_data).await {
- Ok(()) => {
- debug!(
- "Successfully saved remote thumbnail to ephemeral: {:?}",
- thumbnail_path
- );
- return Ok(thumbnail_path);
- }
- Err(e) => {
- error!(
- ?e,
- "Failed to write remote thumbnail to disk in ephemeral folder"
- );
- return Err(Error::InternalError);
- }
- }
- }
-
- // Create the full path for the thumbnail in the library folder
- let thumbnail_path = shard_dir.join(format!("{}.webp", cas_id.as_str()));
-
- // Write the thumbnail data to disk
- match fs::write(&thumbnail_path, thumbnail_data).await {
- Ok(()) => {
- debug!(
- "Successfully saved remote thumbnail to library folder: {:?}",
- thumbnail_path
- );
- Ok(thumbnail_path)
- }
- Err(e) => {
- error!(
- ?e,
- "Failed to write remote thumbnail to disk in library folder"
- );
-
- // If writing to library folder fails, try ephemeral as a fallback
- let ephemeral_dir = thumbnails_directory.join("ephemeral");
- let ephemeral_shard_dir = ephemeral_dir.join(shard_hex);
-
- if let Err(e) = fs::create_dir_all(&ephemeral_shard_dir).await {
- error!(
- ?e,
- "Failed to create thumbnail directory structure in ephemeral folder"
- );
- return Err(Error::InternalError);
- }
-
- let ephemeral_thumbnail_path =
- ephemeral_shard_dir.join(format!("{}.webp", cas_id.as_str()));
-
- match fs::write(&ephemeral_thumbnail_path, thumbnail_data).await {
- Ok(()) => {
- debug!(
- "Successfully saved remote thumbnail to ephemeral fallback: {:?}",
- ephemeral_thumbnail_path
- );
- Ok(ephemeral_thumbnail_path)
- }
- Err(e) => {
- error!(
- ?e,
- "Failed to write remote thumbnail to disk in ephemeral fallback folder"
- );
- Err(Error::InternalError)
- }
- }
- }
- }
-}
diff --git a/core/crates/cloud-services/src/sync/ingest.rs b/core/crates/cloud-services/src/sync/ingest.rs
deleted file mode 100644
index a7dd65af3..000000000
--- a/core/crates/cloud-services/src/sync/ingest.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-use crate::Error;
-
-use sd_core_sync::SyncManager;
-
-use sd_actors::{Actor, Stopper};
-
-use std::{
- future::IntoFuture,
- sync::{
- atomic::{AtomicBool, Ordering},
- Arc,
- },
-};
-
-use futures::FutureExt;
-use futures_concurrency::future::Race;
-use tokio::{
- sync::Notify,
- time::{sleep, Instant},
-};
-use tracing::{debug, error};
-
-use super::{ReceiveAndIngestNotifiers, SyncActors, ONE_MINUTE};
-
-/// Responsible for taking sync operations received from the cloud,
-/// and applying them to the local database via the sync system's ingest actor.
-
-pub struct Ingester {
- sync: SyncManager,
- notifiers: Arc,
- active: Arc,
- active_notify: Arc,
-}
-
-impl Actor for Ingester {
- const IDENTIFIER: SyncActors = SyncActors::Ingester;
-
- async fn run(&mut self, stop: Stopper) {
- enum Race {
- Notified,
- Stopped,
- }
-
- loop {
- self.active.store(true, Ordering::Relaxed);
- self.active_notify.notify_waiters();
-
- if let Err(e) = self.run_loop_iteration().await {
- error!(?e, "Error during cloud sync ingester actor iteration");
- sleep(ONE_MINUTE).await;
- continue;
- }
-
- self.active.store(false, Ordering::Relaxed);
- self.active_notify.notify_waiters();
-
- if matches!(
- (
- self.notifiers
- .wait_notification_to_ingest()
- .map(|()| Race::Notified),
- stop.into_future().map(|()| Race::Stopped),
- )
- .race()
- .await,
- Race::Stopped
- ) {
- break;
- }
- }
- }
-}
-
-impl Ingester {
- pub const fn new(
- sync: SyncManager,
- notifiers: Arc,
- active: Arc,
- active_notify: Arc,
- ) -> Self {
- Self {
- sync,
- notifiers,
- active,
- active_notify,
- }
- }
-
- async fn run_loop_iteration(&self) -> Result<(), Error> {
- let start = Instant::now();
-
- let operations_to_ingest_count = self
- .sync
- .db
- .cloud_crdt_operation()
- .count(vec![])
- .exec()
- .await
- .map_err(sd_core_sync::Error::from)?;
-
- if operations_to_ingest_count == 0 {
- debug!("Nothing to ingest, early finishing ingester loop");
- return Ok(());
- }
-
- debug!(
- operations_to_ingest_count,
- "Starting sync messages cloud ingestion loop"
- );
-
- let ingested_count = self.sync.ingest_ops().await?;
-
- debug!(
- ingested_count,
- "Finished sync messages cloud ingestion loop in {:?}",
- start.elapsed()
- );
-
- Ok(())
- }
-}
diff --git a/core/crates/cloud-services/src/sync/mod.rs b/core/crates/cloud-services/src/sync/mod.rs
deleted file mode 100644
index b694befb4..000000000
--- a/core/crates/cloud-services/src/sync/mod.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use crate::{CloudServices, Error};
-
-use sd_core_sync::SyncManager;
-
-use sd_actors::{ActorsCollection, IntoActor};
-use sd_cloud_schema::sync::groups;
-use sd_crypto::CryptoRng;
-
-use std::{
- fmt,
- path::Path,
- sync::{atomic::AtomicBool, Arc},
- time::Duration,
-};
-
-use futures_concurrency::future::TryJoin;
-use tokio::sync::Notify;
-
-mod ingest;
-mod receive;
-mod send;
-
-use ingest::Ingester;
-use receive::Receiver;
-use send::Sender;
-
-const ONE_MINUTE: Duration = Duration::from_secs(60);
-
-#[derive(Default)]
-pub struct SyncActorsState {
- pub send_active: Arc,
- pub receive_active: Arc,
- pub ingest_active: Arc,
- pub state_change_notifier: Arc,
- receiver_and_ingester_notifiers: Arc,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, specta::Type)]
-#[specta(rename = "CloudSyncActors")]
-pub enum SyncActors {
- Ingester,
- Sender,
- Receiver,
-}
-
-impl fmt::Display for SyncActors {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Ingester => write!(f, "Cloud Sync Ingester"),
- Self::Sender => write!(f, "Cloud Sync Sender"),
- Self::Receiver => write!(f, "Cloud Sync Receiver"),
- }
- }
-}
-
-#[derive(Debug, Default)]
-pub struct ReceiveAndIngestNotifiers {
- ingester: Notify,
- receiver: Notify,
-}
-
-impl ReceiveAndIngestNotifiers {
- pub fn notify_receiver(&self) {
- self.receiver.notify_one();
- }
-
- async fn wait_notification_to_receive(&self) {
- self.receiver.notified().await;
- }
-
- fn notify_ingester(&self) {
- self.ingester.notify_one();
- }
-
- async fn wait_notification_to_ingest(&self) {
- self.ingester.notified().await;
- }
-}
-
-pub async fn declare_actors(
- data_dir: Box,
- cloud_services: Arc,
- actors: &ActorsCollection