Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,24 @@

All notable changes to Sync360 will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Semantic versioning will begin when public releases begin.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions remain preview releases, so compatibility can change before `1.0.0`.

## [Unreleased]

## [0.4.0] - 2026-08-28

### Changed

- Replaced the file Accept/Decline screen and suspended HTTP decision with a temporary four-digit receive code generated once for each fresh application session.
- Displayed the same session receive code on both the Send and Receive screens so it is visible from the default screen.
- File offers now include the entered code and receive an immediate accepted, invalid-code, busy, or preparation-failed response.
- Removed file `UserDecision`, `CompletableDeferred`, incoming-offer state, decision timeout, and Accept/Cancel race while retaining operation IDs, TCP preparation timeout, cancellation, progress, framing, and cleanup.
- Changed the file-offer wire format, so matching builds are required; preview protocol metadata intentionally remains version `1` for now.

### Security

- Documented the receive code as a short-lived convenience against accidental or casual unwanted sends, not authentication; it has no attempt throttling, and local HTTP and raw TCP remain cleartext.

## [0.3.0] - 2026-08-27

### Added
Expand Down
8 changes: 4 additions & 4 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Sync360 Privacy

Last updated: August 3, 2026
Last updated: August 28, 2026

Sync360 sends text and files directly between nearby devices on the same reachable local network. It does not use a Sync360 account, cloud-storage service, analytics service, advertising service, or Sync360 transfer backend.

Expand All @@ -11,14 +11,14 @@ Sync360 sends text and files directly between nearby devices on the same reachab
- No analytics, advertising, tracking, or telemetry is included.
- A random installation identifier is stored locally so devices can identify each other.
- Nearby-device discovery information is exchanged only with devices on the reachable local network and is kept as runtime state.
- Text and selected files are sent directly to the receiver chosen by the user after the receiver approves the offer.
- Text is sent directly to the chosen receiver. Files are sent after the sender enters the receiver's temporary four-digit code.
- Received files remain on the receiving device in its platform Downloads location.
- Shared text and transfer state are temporary runtime state; Sync360 does not maintain chat or clipboard history.
- Sync360 does not send shared content to the developer.

## Network Security

Sync360 currently uses cleartext local HTTP for offers and text and raw TCP for file bytes. Sender authentication, session validation, request signing, replay protection, encryption, and cryptographic integrity verification are not implemented yet. Receiver approval exists in the UI but is not a complete security boundary.
Sync360 currently uses cleartext local HTTP for offers and text and raw TCP for file bytes. Sender authentication, session validation, request signing, replay protection, encryption, and cryptographic integrity verification are not implemented yet. The temporary four-digit file receive code is a convenience check, not authentication, and is transmitted over cleartext HTTP.

Do not treat the current code as production-secure file-transfer software. Use it only on private networks you control while testing.

Expand All @@ -28,7 +28,7 @@ Sync360 uses network access for local discovery and direct transfer. Android use

## Retention

Sync360 stores a local installation identifier. Discovery, offer, text, and transfer state are runtime state. Files successfully received remain in Downloads until the user removes them through the operating system. Incomplete current files are removed after receive failure or cancellation where the platform implementation supports it.
Sync360 stores a local installation identifier. The file receive code, discovery, offer, text, and transfer state are runtime state. The receive code is not persisted and a fresh application session generates another one. Files successfully received remain in Downloads until the user removes them through the operating system. Incomplete current files are removed after receive failure or cancellation where the platform implementation supports it.

## Contact

Expand Down
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,15 @@ In an initial Windows 11 Ethernet test, the native Windows DNS-SD backend discov
- Deliver text directly with one HTTP request when the receiver is idle.
- Enforce a 100,000-character text limit and show the sender name with Copy and Clear actions.
- Select images, videos, documents, and multiple files.
- Show file metadata to the receiver before any file bytes are sent.
- Generate a temporary four-digit file receive code for each fresh application session and show the same code on both Send and Receive.
- Check the receive code and file metadata before any file bytes are sent.
- Stream file bytes directly over raw TCP without loading an entire file into memory.
- Save received files into public Android Downloads through `MediaStore`, preserving the extension when duplicate names are resolved.
- Delete the incomplete current file if its receive operation fails or is cancelled.
- Stream each accepted file batch continuously, then confirm the batch with one final receiver result.
- Cancel a pending send or active file transfer on a best-effort basis.
- Show batch-wide byte percentage while files are being sent and received.
- Show clear offer, transfer, success, failure, and cancelled states on the sender, with incoming, receiving, and received states on the receiver.
- Show clear preparation, transfer, success, failure, and cancelled states on the sender, with receiving and received states on the receiver.
- Run the shared Send/Receive UI on Desktop, with an adaptive 50/50 two-pane layout in wider windows.
- Discover and advertise Windows devices through the operating system DNS-SD API, with JmDNS retained for macOS and Linux, using the same service as Android.
- Select multiple Desktop files with the native file dialog and send them through the same offer and TCP protocol.
Expand Down Expand Up @@ -92,14 +93,14 @@ The current progress UI tracks the exact bytes transferred across the accepted b

Sync360 uses two small networking paths with different jobs:

- **Ktor HTTP handles direct text delivery and the file control plane.** It carries text payloads, file offers, receiver decisions, and file metadata.
- **Ktor HTTP handles direct text delivery and the file control plane.** It carries text payloads and immediate code-checked file offers with metadata.
- **Raw TCP is the file data plane.** It streams the actual file bytes directly between devices.

```mermaid
flowchart LR
A["Sender device"] -->|"Android NSD or platform Desktop DNS-SD"| B["Receiver device"]
A -->|"Ktor: direct text delivery"| B
A -->|"Ktor: file offer + decision"| B
A -->|"Ktor: code-checked file offer"| B
A -->|"Raw TCP: streamed file bytes"| B
B -->|"Platform Downloads writer"| D["Downloads"]
```
Expand All @@ -126,14 +127,19 @@ Text uses one request and has no offer, receiver decision, operation ID, waiting
```text
Platform file picker
-> SelectedFileReader reads name, size, MIME type, and platform location
-> POST /sync360/file/offer sends metadata
-> receiver Accept/Decline
-> sender enters the receiver's temporary four-digit code
-> POST /sync360/file/offer sends metadata and code
-> idle receiver checks the code and prepares its TCP receiver immediately
-> platform FileTransferSender opens an InputStream
-> one raw TCP connection streams the accepted file batch
-> platform DownloadsWriter saves each file
-> receiver returns final success and completed-file count
```

The receive code is generated in memory when a fresh application session starts. The same code is shown on the Send and Receive screens, so it is available from the default screen without switching tabs. It is not persisted, advertised, or remembered by the sender. It is a convenience check, not authentication or encryption.

This changes the file-offer request and response format. Builds containing this flow are not file-transfer compatible with `0.3.0` or older builds, even though the advertised preview protocol version intentionally remains `1` for now. Use matching builds on both devices.

One TCP socket is opened for the complete accepted batch. It begins with the operation ID as 16 raw UUID bytes; each file then begins with its index and promised byte count, followed by exactly that many bytes. The receiver checks the operation ID, index, and size before saving. The sender writes every file sequentially, flushes once after the complete batch, then reads one final success flag and completed-file count from the receiver. The count increases only after the platform Downloads writer successfully returns. The current shared payload buffer is 512 KiB; exact byte counts define file boundaries, so correctness does not depend on `flush()` calls or matching sender and receiver read chunks.

Files are sent sequentially. If a later file fails, files that were already completed stay in Downloads; the incomplete current file is cleaned up. Android uses a pending `MediaStore` entry and resolves its MIME type from the filename extension so duplicate names remain in the form `file (1).ext`. Desktop writes a temporary `.part` file before moving a completed file into place without overwriting an existing name.
Expand Down Expand Up @@ -237,8 +243,8 @@ macOS/Linux:
3. Keep Sync360 open on both devices during the current foreground-only test flow.
4. On the Send screen, wait for the other device to appear.
5. For text, enter the content and select the nearby device; idle receivers show it immediately.
6. For files, select the files and nearby device, then accept the offer on the receiver.
7. Accepted files will be written to the platform's Downloads folder.
6. For files, read the target device's four-digit code from its Send or Receive screen, select the files and nearby device, then enter that code on the sender.
7. Code-accepted files will be written to the platform's Downloads folder.

Some routers enable client isolation and block local device-to-device traffic. If discovery or transfer does not work, try another trusted Wi-Fi network or a phone hotspot.

Expand All @@ -250,7 +256,7 @@ Reload is available only after the current discovery window has stopped while se

Sync360 is **not secure for untrusted networks yet**.

The current implementation uses cleartext local HTTP and raw TCP. File operation IDs correlate offers, cancellation, and file sockets for correctness, but they are not secret or authenticated. Direct text delivery has no receiver approval. Sync360 does not yet authenticate the sender, encrypt content, or verify file integrity with a cryptographic hash. File receiver approval exists in the UI, but it is not a complete security boundary.
The current implementation uses cleartext local HTTP and raw TCP. File operation IDs correlate offers, cancellation, and file sockets for correctness, but they are not secret or authenticated. Direct text delivery has no receiver approval. The four-digit file receive code reduces accidental or casual unwanted sends, but its small keyspace, cleartext transport, and current lack of attempt throttling do not make it authentication. Sync360 does not yet authenticate the sender, encrypt content, or verify file integrity with a cryptographic hash.

Use the current app only for development and testing on private networks you control. Please report security-sensitive findings according to [SECURITY.md](SECURITY.md), not in a public issue.

Expand All @@ -261,7 +267,6 @@ Use the current app only for development and testing on private networks you con
- Improve active-transfer feedback around the current byte percentage.
- Add integrity verification.
- Test cancellation and failure reporting across more network-loss and transfer stages.
- Close the narrow Accept/Cancel timing gap so an offer cannot report acceptance after its receiver state has already been cancelled.
- Strengthen lifecycle behavior and local-network reliability.
- Add Android 17 local-network permission handling and serialize Android 13 legacy NSD resolves.
- Validate Desktop discovery and transfer across more operating systems, network adapters, routers, and firewall configurations.
Expand All @@ -277,7 +282,7 @@ Use the current app only for development and testing on private networks you con
Sync360 is not trying to become a chat app, cloud-sync product, or permanent device manager. The product direction stays focused:

```text
find nearby -> send text or approve files -> transfer directly
find nearby -> send text or enter a file receive code -> transfer directly
```

## Why the rebuild is intentionally small
Expand Down
9 changes: 6 additions & 3 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Sync360 is an early local-network sharing app. It is not secure for untrusted networks yet.

The current rebuild implements local discovery, receiver-approved text sharing, and streamed file transfer before adding the final security model. Security work remains required before untrusted-network use.
The current rebuild implements local discovery, direct text delivery, and code-checked streamed file transfer before adding the final security model. Security work remains required before untrusted-network use.

## Supported versions

Expand All @@ -11,6 +11,8 @@ There are no stable supported releases yet.
| Version | Supported |
| ------- | --------- |
| Unreleased / main | Best effort |
| 0.4.x | Best effort |
| 0.3.x and older | No |

## Reporting a vulnerability

Expand Down Expand Up @@ -40,10 +42,11 @@ General bugs, crashes, UI issues, documentation problems, and non-sensitive arch
Current implementation:

- Android NSD, Windows system DNS-SD, macOS/Linux JmDNS, and an initial iOS Bonjour implementation exist.
- Ktor carries text/file offers, receiver decisions, metadata, and accepted text.
- Ktor carries direct text and code-checked file offers with metadata.
- Raw TCP streams accepted file batches to platform Downloads storage.
- File names and promised sizes are validated, but a file socket is not bound to its approved offer with a session token.
- File names and promised sizes are validated, but a file socket is not authenticated with a secret session token.
- Sender authentication, encryption, replay protection, and cryptographic integrity verification are not implemented.
- The temporary four-digit file receive code is not authentication and can be guessed or observed on the cleartext local connection. Attempts are not currently rate-limited.

Use current builds only on private local networks you control. Do not use the current code as a security model for production file transfer.

Expand Down
4 changes: 2 additions & 2 deletions STORE_LISTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ Share text and files directly between your Android and desktop devices over your

## Security Notice

Sync360 currently uses trusted-network mode. The receiver approves offers in the UI, but requests and file sockets are not authenticated and transferred content is not encrypted by Sync360.
Sync360 currently uses trusted-network mode. Files require the receiver's temporary four-digit code, but the code, requests, and file sockets are not authenticated and transferred content is not encrypted by Sync360.

Use Sync360 only on a private home network or personal hotspot controlled by you. Do not use it on public or shared networks such as cafes, hotels, airports, schools, or offices.

## Privacy Summary

- Direct local-network transfer; no transfer cloud.
- No account, ads, analytics, tracking, or telemetry.
- Offer decisions, transfer state, and shared text are temporary runtime state.
- Receive codes, transfer state, and shared text are temporary runtime state.
- Received files remain on the receiving device.

## Publishing Checklist
Expand Down
4 changes: 2 additions & 2 deletions androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ android {
applicationId = "com.liftley.sync360"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 3
versionName = "0.3.0"
versionCode = 4
versionName = "0.4.0"
}

buildFeatures {
Expand Down
6 changes: 3 additions & 3 deletions context.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The old AI-generated sync implementation was removed. The current app is being r
- Windows discovery/registration through the operating system `dnsapi.dll` DNS-SD API on all interfaces.
- Current macOS/Linux discovery/registration through JmDNS on eligible IPv4 and IPv6 LAN addresses.
- Application-lifetime network startup with separate discovery and registration lifecycle states.
- Ktor HTTP direct text delivery plus file offers, receiver decisions, and metadata.
- Ktor HTTP direct text delivery plus immediate code-checked file offers and metadata.
- Raw TCP streaming for file bytes.
- Multiple files sent sequentially over one accepted-batch connection.
- Android file access through `ContentResolver` and Downloads writing through `MediaStore`.
Expand Down Expand Up @@ -72,12 +72,12 @@ Current shared transfer constants use a 512 KiB payload buffer, 5-second connect
- Foreground/background lifecycle support.
- Broader Desktop adapter, firewall, router, and operating-system validation.
- Android 17 local-network permission-aware startup and serialized Android 13 legacy NSD resolution.
- Closing the narrow shared Accept/Cancel response race.
- Validating the temporary file receive-code flow across supported platforms.
- Session validation, authentication, encryption, and integrity verification.

## Important limitations

Sync360 currently uses cleartext local HTTP and raw TCP. Direct text has no receiver approval or operation ID. File operation IDs correlate protocol messages and sockets but do not authenticate a peer. File offers require receiver approval, but the app has no authentication, encryption, or checksum. The current target-SDK-37 Android build also lacks Android 17's required local-network runtime-permission flow. Windows receiving depends on Windows Firewall allowing the application. Use development builds only on private networks you control.
Sync360 currently uses cleartext local HTTP and raw TCP. Direct text has no receiver approval or operation ID. File operation IDs correlate protocol messages and sockets but do not authenticate a peer. A temporary four-digit receive code replaces file Accept/Decline, but it has no attempt throttling and is not authentication, encryption, or a checksum. The new file-offer format is incompatible with `0.3.0` and older builds while preview protocol metadata intentionally remains version `1`, so both devices must run matching builds. The current target-SDK-37 Android build also lacks Android 17's required local-network runtime-permission flow. Windows receiving depends on Windows Firewall allowing the application. Use development builds only on private networks you control.

For detailed and current information, read:

Expand Down
2 changes: 1 addition & 1 deletion desktopApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ compose.desktop {
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "Sync360"
packageVersion = "0.3.0"
packageVersion = "0.4.0"
appResourcesRootDir.set(
project.layout.projectDirectory.dir("packaging/app-resources")
)
Expand Down
Loading
Loading