15 Commits

Author SHA1 Message Date
689d14202b Add README for main project and each example
Main README covers quick start, API overview, and links to example READMEs.
Each example (paste, filemanager, can-sync, canfs) gets its own README
with setup instructions, architecture, and configuration details.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 14:45:20 -06:00
e7def4b819 Merge branch 'sync-v2' into master
Adds P2P sync (protobuf API, iroh QUIC transport, gossip + pkarr
discovery), SSE live refresh, plain-English code comments across
all source files and examples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 14:36:29 -06:00
620966872e Add plain-English comments to all functions across src/ and examples/
Comments help non-Rust users understand what each function, struct, and
module does. Covers the core service (18 source files) and all four
example projects (can-sync, canfs, filemanager, paste).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 14:35:24 -06:00
e60c880232 Organize storage by type directories with human-readable filenames
- Files now stored in type subdirectories: images/, pdf/, documents/,
  video/, audio/, others/
- Filename format: YYYY-MM-DD_HH-MM_{hash8}.{ext} (human-readable)
- Added mime_to_type_dir() for MIME-to-directory classification
- write_asset() auto-creates type subdirectories
- Backward compatible: old flat filenames still work via DB lookup
- Filemanager: added TYPE/ virtual tree root for browsing by content type

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:11:56 -06:00
c77e6d4105 Add live SSE auto-refresh to paste app
- CAN service: public SSE endpoint at /api/v1/can/0/events broadcasts
  new_asset events on ingest and sync push (no auth required)
- Paste backend: SSE proxy at /paste/events streams from CAN service,
  with auto-reconnect on connection loss
- Paste frontend: EventSource subscribes to /paste/events and calls
  loadItems() on new_asset events for instant UI refresh
- When assets arrive via P2P sync, paste updates automatically

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 15:01:45 -06:00
69e4f13c22 Add internet peer discovery via pkarr relay rendezvous
Peers sharing the same sync_passphrase can now find each other
automatically over the internet without manual ticket exchange or
port forwarding. Uses n0's public pkarr relay servers as a
rendezvous point.

How it works:
- Derive 8 deterministic Ed25519 keypair "slots" from the passphrase
- Each peer claims a slot by publishing its EndpointId as a TXT record
- All peers scan all 8 slots every 15s to discover new peers
- Re-publish every 60s with 5min TTL to stay visible
- Discovered EndpointIds feed into the same peer channel as gossip

This runs alongside the existing gossip discovery (which still needs
bootstrap peers) and direct ticket-file connections (used by tests).

All 6 stress tests pass (102 assets, 63+ MB/s bidirectional).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:53:34 -06:00
71002939a1 Add bash version of go script for macOS/Linux
Same as go_example_1.ps1 but for bash — starts CAN service, Paste UI,
and sync agent. Uses trap for clean shutdown on Ctrl+C.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 17:18:36 -06:00
23e5e0712e Add sync agent to go_example_1.ps1 for automatic P2P replication
The go script now builds and starts the can-sync agent alongside CAN
service and Paste UI. Any machine that clones the repo and runs the
script will auto-discover other instances via iroh's relay network
using the shared passphrase "duke-canman-sync" — no port forwarding
or manual peer configuration needed.

Changes:
- Add sync_api_key to root config.yaml (enables sync API)
- Update can-sync config.yaml with matching key and shared passphrase
- Update go_example_1.ps1 to build and launch can-sync agent
- Script now manages 3 processes: CAN service, Paste UI, sync agent

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 17:16:42 -06:00
7b54bbe892 Add SSE events and incremental hash queries for live sync
Replace polling-based sync detection with SSE (Server-Sent Events) from
CAN service for instant push notifications on new asset ingests. Add
incremental hash queries via ?since=timestamp parameter to avoid
transferring full hash lists on every sync cycle.

CAN service changes:
- Add broadcast channel (SyncEventSender) in AppState for SSE events
- Add GET /sync/events SSE endpoint with auth via header or query param
- Fire broadcast events on both ingest and sync push
- Add db::get_assets_since() for incremental queries
- Support ?since= parameter on POST /sync/hashes

can-sync agent changes:
- Add SSE subscription with auto-reconnect in can_client
- Add get_hashes_since() for incremental catch-up
- Rewrite live push loop: SSE-driven with 30s fallback poll
- Remove poll_interval parameter from live sync functions

All 6 stress tests pass (102 assets, 63 MB/s bidirectional).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:48:26 -06:00
4dc0f8c12d Add stress tests: burst ingests, large files, simultaneous sync
Rewrite integration test with 6 scenarios:
- Single file each direction (basic sanity)
- Rapid burst 25 files A→B with mixed sizes (12MB+ large files)
- Rapid burst 25 files B→A with mixed sizes
- Simultaneous burst: 25 files on EACH side at the same time
- Final full-mirror verification (102 assets, perfect match)

Throughput measured at ~20 MB/s per direction, ~31 MB/s
bidirectional. All tests pass including simultaneous ingestion
of 162 MB across 50 files with zero conflicts or missing assets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:29:16 -06:00
ccce1474d4 Fix echo bounce and initial sync deadlock in peer protocol
Add mpsc channel between live receive and push loops so hashes
received from a peer aren't pushed right back (echo prevention).
Change initial reconciliation to use tokio::join! for concurrent
send/receive, avoiding QUIC flow-control deadlock when both sides
have large transfers. Update known_hashes to union-insert so
peer-received hashes persist across poll cycles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:11:47 -06:00
06c01baf44 Fix QUIC stream protocol bugs and add integration tests
- Fix bidirectional stream handling: responder uses accept_bi() instead
  of open_bi() so both sides communicate on the same stream
- Add live_receive_loop to accept incoming bi-streams during ongoing
  sync (peer's push loop opens new streams per batch)
- Split live_sync_loop into live_push_loop + live_receive_loop running
  concurrently via tokio::select in new run_live_sync()
- Update handle_incoming to run live sync after initial reconciliation
- Add direct peer connection via ticket files (EndpointAddr JSON
  exchange) for local testing without gossip bootstrap
- Add CAN_PORT env var override for running multiple CAN instances
- Add integration test binary (sync_test.rs): starts 2 CAN services +
  2 sync agents, ingests files on each side, verifies bidirectional
  sync with 4 test cases (A→B, B→A, batch, count match)
- Add PowerShell script (run-integration-test.ps1) for one-command test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:20:49 -06:00
a28fac6c9a Rewrite can-sync v2: simplified P2P full-mirror replication
Replace the over-engineered iroh-docs/libraries/filters architecture
with a simple peer-to-peer sync using:
- iroh 0.96 Endpoint for QUIC transport + NAT traversal
- iroh-gossip for peer discovery via shared passphrase
- Protobuf messages over QUIC streams for asset transfer
- CAN service's private /sync/* API for local data access

Deleted: announcer, fetcher, library, manifest, node, routes (2860 lines)
Added: discovery, peer, protocol (simplified ~600 lines)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 12:30:12 -06:00
1b8187a484 Add private protobuf sync API to CAN service
New /sync/* endpoints for peer-to-peer replication:
- POST /sync/hashes - list all asset digests for reconciliation
- POST /sync/pull - pull full assets (metadata + content) by hash
- POST /sync/push - push asset with explicit timestamp for deterministic hashing
- POST /sync/meta - update metadata (tags, description, trash state)

All endpoints use protobuf encoding (prost) and require X-Sync-Key header
matching config.sync_api_key. Sync API is disabled when no key is configured.

Also adds db::get_all_assets() for sync reconciliation (includes trashed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 11:06:27 -06:00
360ecbdad0 Initial commit: CAN Service + examples (can-sync v1, canfs, filemanager, paste)
CAN Service: content-addressable storage with HTTP API, SQLite metadata,
file-based blob storage, thumbnail generation, and integrity verification.

can-sync v1: P2P sync sidecar using iroh-docs for encrypted peer-to-peer
replication with library/filter-based selective sync. Fully builds but
being superseded by v2 (simplified full-mirror approach).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 10:32:04 -06:00