Part 05 — Robust File / Blob Subsystem Architecture
Reusable P2P Communication Platform
Status: Architecture specification
Part: 05 of 24
Primary language: Rust
Primary goals: content-addressed storage, resumable transfer, integrity, encryption, low-copy streaming, deduplication, partial availability, multi-transport delivery, crash recovery, reuse across messaging/files/emergency/ERP/custom products
1. Purpose
The communication platform needs a dedicated binary-data subsystem that can safely and efficiently handle:
- chat attachments
- photos
- videos
- voice messages
- documents
- large files
- application packages
- emergency images
- diagnostic bundles
- ERP documents
- backups
- future product-specific binary payloads
The blob subsystem must be reusable independently from messaging.
The architectural rule is:
Messages reference blobs; messages do not own file-transfer mechanics.
This separation allows:
messaging-only application
file-only application
messaging + files
emergency application
ERP document transfer
headless relay
to reuse the same file/blob engine.
2. Core Principles
The subsystem must provide:
- content addressing
- deterministic integrity verification
- streaming I/O
- bounded memory usage
- chunked transfer
- resumable transfer
- partial availability
- optional parallel transfer
- encryption
- local staging
- deduplication
- storage quotas
- garbage collection
- reference tracking
- transport independence
- crash consistency
- DTN compatibility
- multi-device targeting
- diagnostics
- policy-driven retention
It must not depend on:
- Dioxus
- messenger screens
- Android UI
- a specific transport
- a specific database
- a central server
3. Architectural Position
Application
↓
Messaging / File Share / ERP / Emergency
↓
File Service
↓
Blob Manager
↓
Chunk Store / Manifest / Encryption
↓
Transfer Planner
↓
Routing Policy Engine
↓
Iroh / LAN / Wi-Fi / Bluetooth / DTN
The file subsystem owns file semantics.
The routing layer owns path selection.
The transport layer moves bytes.
The storage layer persists blobs/chunks.
4. Main Crates
Recommended split:
comm-files/
comm-blob-store/
comm-blob-types/
comm-transfer/
comm-transfer-protocol/
comm-storage-filesystem/
Potential workspace:
crates/
├── comm-files/
├── comm-blob-store/
├── comm-transfer/
├── comm-transfer-protocol/
└── comm-storage-filesystem/
Avoid one huge crate if the subsystem grows large.
5. Blob Concept
A blob is an immutable binary object.
#![allow(unused)] fn main() { pub struct BlobId([u8; 32]); }
A blob is identified by content-derived identity or a secure encrypted-object identity.
The blob does not inherently know:
conversation
sender
recipient
file name
UI
Metadata references the blob externally.
6. Content Addressing
Conceptually:
content bytes
↓
cryptographic hash
↓
BlobId
Use a strong cryptographic hash such as BLAKE3 or another explicitly selected algorithm.
Benefits:
- integrity verification
- deduplication
- immutable identity
- resumability
- peer caching
- partial transfer verification
- distributed lookup
7. Plaintext vs Ciphertext Addressing
This is a major design decision.
Option A — plaintext-addressed
hash(plaintext)
Pros:
- stronger deduplication
Cons:
- leaks equality of identical content
- problematic for privacy
Option B — ciphertext-addressed
encrypt
↓
hash(ciphertext)
Pros:
- better privacy
- avoids plaintext equality leakage
Cons:
- less cross-user deduplication
For a privacy-focused communication platform, prefer:
Encrypt first, then content-address the ciphertext.
8. Blob Identity Layers
Useful model:
LogicalAttachmentId
↓
EncryptedBlobId
↓
ChunkIds
A message may reference a logical attachment record.
The actual storage identity is the encrypted blob.
9. Blob Descriptor
#![allow(unused)] fn main() { pub struct BlobDescriptor { pub blob_id: BlobId, pub size: u64, pub chunking: ChunkingDescriptor, pub encryption: EncryptionDescriptor, pub media_type: Option<MediaType>, pub manifest_id: Option<ManifestId>, } }
Do not trust remote-declared size blindly.
10. File Metadata
User-facing metadata should be separate from blob identity.
#![allow(unused)] fn main() { pub struct FileMetadata { pub display_name: Option<FileName>, pub media_type: Option<MediaType>, pub logical_size: u64, pub created_at: Option<Timestamp>, } }
This metadata can be encrypted inside the application message if privacy requires.
11. Chunking
Large blobs must be chunked.
Blob
├── Chunk 0
├── Chunk 1
├── Chunk 2
└── ...
Chunking enables:
- resume
- parallelism
- corruption isolation
- partial transfer
- DTN carriage
- progress reporting
- cache reuse
12. Fixed vs Content-Defined Chunking
Fixed-size chunks
Example:
1 MiB
4 MiB
Pros:
- simple
- fast
- easy range requests
Content-defined chunking
Pros:
- better dedup across modified files
Cons:
- more CPU
- more metadata
- more complexity
For v1, prefer:
Fixed-size chunking with a configurable chunk size.
13. Chunk Size Policy
Chunk size should depend on workload.
Example ranges:
small file:
single chunk
medium:
256 KiB – 1 MiB chunks
large:
1 MiB – 4 MiB chunks
Do not hard-code one size for every transport.
The manifest records the chosen scheme.
14. Chunk Identity
Each chunk can have:
#![allow(unused)] fn main() { pub struct ChunkHash([u8; 32]); }
The manifest includes chunk hashes.
This allows verifying individual chunks before whole-object completion.
15. Blob Manifest
#![allow(unused)] fn main() { pub struct BlobManifest { pub version: u16, pub blob_id: BlobId, pub total_size: u64, pub chunk_size: u32, pub chunks: Vec<ChunkDescriptor>, } }
Chunk descriptor:
#![allow(unused)] fn main() { pub struct ChunkDescriptor { pub index: u32, pub offset: u64, pub size: u32, pub hash: ChunkHash, } }
16. Manifest Limits
Bound:
max chunks
max manifest size
max file size
Do not allow a tiny network frame to declare millions of chunks and force huge allocation.
17. Manifest Hierarchy
For extremely large files, consider hierarchical manifests later.
Example:
Root manifest
├── Segment manifest A
├── Segment manifest B
└── Segment manifest C
This avoids massive flat manifests.
Not required initially.
18. Encryption Model
Recommended:
plaintext file
↓
per-blob random key
↓
chunk encryption
↓
ciphertext chunks
↓
content addressing
Use authenticated encryption.
Each chunk must be independently verifiable/authenticated.
19. Per-Blob Key
#![allow(unused)] fn main() { pub struct BlobEncryptionKey(SecretBytes); }
The key is not stored in public blob metadata.
It is distributed through the E2EE application layer.
20. Chunk Nonces
Never reuse AEAD nonce/key pairs.
Derive chunk nonce safely from:
blob key
chunk index
domain separation
or use securely generated per-chunk nonces recorded in authenticated metadata.
The exact construction should use a vetted cryptographic design.
21. Encryption Metadata
#![allow(unused)] fn main() { pub struct EncryptionDescriptor { pub scheme: EncryptionSchemeId, pub version: u16, } }
Do not expose raw secret key.
22. Metadata Privacy
Sensitive metadata can include:
file name
MIME type
dimensions
duration
creation time
Decide which metadata is:
public transport metadata
encrypted application metadata
local-only metadata
Default should minimize exposure.
23. Message Attachment Reference
Messaging stores a reference:
#![allow(unused)] fn main() { pub struct AttachmentReference { pub blob: BlobDescriptor, pub metadata: EncryptedOrVisibleMetadata, } }
The message does not contain the file bytes.
24. File-Only Transfer
A file-sharing app can use:
FileOffer
FileAccept
Transfer
Blob
without creating a conversation.
This is a core reusability requirement.
25. Transfer Identity
#![allow(unused)] fn main() { pub struct TransferId([u8; 16]); }
A transfer is distinct from a blob.
Same blob may have:
multiple transfers
multiple recipients
multiple retries
26. Transfer State Machine
Created
↓
Offered
↓
Accepted
↓
Preparing
↓
Transferring
↓
Verifying
↓
Completed
Alternative states:
Paused
Deferred
Cancelled
Failed
Expired
27. Transfer Record
#![allow(unused)] fn main() { pub struct TransferRecord { pub transfer_id: TransferId, pub blob_id: BlobId, pub direction: TransferDirection, pub peer: DeviceId, pub state: TransferState, pub created_at: Timestamp, pub updated_at: Timestamp, } }
28. Transfer Journal
High-frequency operational state belongs in a transfer journal/projection.
Examples:
chunk bitmap
bytes verified
active path
retry count
Do not append a permanent event for every packet.
Part 04 event log stores meaningful semantic transitions.
29. Resume Bitmap
For fixed chunking:
received:
[1,1,1,0,0,1,...]
Store efficiently as:
bitset
ranges
On restart, receiver knows exactly what is missing.
30. Range-Based Resume
Alternative:
received ranges
Example:
0..4 MiB
8..12 MiB
Useful when transport supports ranges.
Choose one canonical internal form.
31. Resume Protocol
Receiver sends:
manifest known
missing chunks:
3,4,9,10
Sender transfers only missing chunks.
No full restart.
32. Partial Availability
Receiver should be able to access:
thumbnail
preview
first playable media segment
before full object completion where application allows.
The blob store exposes safe partial-read status.
33. Progressive Images
Image flow:
thumbnail blob
preview blob
original blob
Prefer separate objects rather than relying only on arbitrary partial decode.
This simplifies UI and transport policy.
34. Progressive Video/File Handling
For large video/files, partial transfer may be usable if format supports it.
The blob layer should not assume every file is stream-playable.
Media layer decides.
35. Transfer Planner
TransferPlanner
├── destination resolution
├── chunk scheduling
├── route request generation
├── concurrency limits
├── retry planning
└── policy evaluation
It consumes Part 03 routing decisions.
36. Transport Independence
Transfer engine must not contain:
Iroh-specific send()
Bluetooth-specific write()
Instead:
Transfer Engine
↓
Transport/Session abstraction
This allows transfer over:
Iroh
LAN
Wi-Fi Direct
Bluetooth
DTN
37. Streaming I/O
Never:
read entire 2 GB file into Vec<u8>
Use:
File
↓
bounded reader
↓
chunk buffer
↓
encrypt
↓
transport
Receiver:
transport
↓
chunk
↓
verify/decrypt
↓
temp/staging storage
38. Low-Copy Data Path
Use types like:
Bytes
BytesMut
Arc<[u8]>
where appropriate.
Avoid repeated:
Vec → clone → encrypt copy → clone → send
Do not sacrifice safety for theoretical zero-copy.
39. Buffer Pool
For high-throughput transfers, a reusable bounded buffer pool can reduce allocations.
BufferPool
├── max buffers
├── chunk size classes
└── backpressure
Do not create unbounded pools.
40. Backpressure
Pipeline:
disk reader
↓
bounded chunk queue
↓
encryptor
↓
bounded transport queue
↓
network
If network slows:
reader slows
Memory remains bounded.
41. Parallelism
Support configurable parallel chunk transfer.
Example:
4 concurrent chunks
But increase only when:
bandwidth
CPU
storage
battery
justify it.
42. Per-Transport Parallelism
Iroh/LAN:
higher parallelism
Bluetooth:
very low parallelism
DTN:
bundle-level scheduling
The transfer engine uses route capabilities.
43. Adaptive Concurrency
Future optimization:
increase concurrency
while throughput improves
reduce
when:
loss rises
latency rises
memory pressure
battery saver
Start with deterministic limits.
44. File Offer Protocol
Sender may send:
#![allow(unused)] fn main() { pub struct FileOffer { pub transfer_id: TransferId, pub blob: BlobDescriptor, pub encrypted_metadata: Bytes, pub expiry: Option<Timestamp>, } }
Receiver decides:
accept
reject
defer
metadata-only
45. Auto-Accept Policy
Applications can define:
contacts only
small files
Wi-Fi only
specific MIME types
emergency thumbnails
File subsystem enforces policy supplied by application.
46. Transfer Authorization
Before receiving:
peer identity
authorization
size policy
storage quota
content policy
must be checked.
Connection alone does not imply permission.
47. Quotas
Support:
max file size
max concurrent transfers
max per-peer bytes
max daily received bytes
max relay storage
max cache size
This protects resource usage.
48. Storage Reservation
Before accepting a large transfer:
reserve expected storage
or verify sufficient quota.
Avoid accepting 20 GB then failing at 99%.
49. Sparse Files
Where filesystem supports:
sparse allocation
can help partial/range downloads.
Use platform capability abstraction.
Not mandatory for correctness.
50. Temporary Staging
Incoming incomplete objects should live in:
staging/
not final blob namespace.
After full verification:
atomic move/commit
to final blob store.
51. Atomic Finalization
Flow:
receive chunks
↓
verify each
↓
verify full manifest/blob
↓
fsync as policy requires
↓
atomic rename/commit
↓
mark Complete
Never expose incomplete object as complete.
52. Crash Recovery
On restart:
load transfer journal
↓
inspect staging files
↓
verify recorded completed chunks
↓
resume missing chunks
Do not trust temp files blindly.
53. Temp File Naming
Use opaque safe IDs:
<TransferId>.partial
Do not use untrusted file names as filesystem paths.
54. Path Traversal Protection
Incoming display name:
../../etc/passwd
must remain just a display string.
Never construct storage paths directly from remote filenames.
55. Filename Sanitization
When exporting to user filesystem:
sanitize
normalize
resolve collision
Examples:
photo.jpg
photo (1).jpg
Keep original display name separately if useful.
56. MIME Type Validation
Remote MIME type is only a hint.
Where security matters:
sniff/inspect content safely
before opening.
Do not execute files based on declared MIME.
57. Executable Content
Downloads may contain:
APK
EXE
script
archive
The communication platform should not auto-execute received files.
Opening is an explicit application/user action.
58. Archive Safety
If application extracts archives:
zip-slip/path traversal
decompression bomb
symlink
must be handled in a separate safe extraction subsystem.
Blob layer itself stores opaque bytes.
59. Deduplication
If ciphertext-addressed:
same encrypted object
→ same blob
can deduplicate locally.
Different encryption keys generally prevent cross-message plaintext deduplication.
That is acceptable for privacy.
60. Reference Counting
Track logical references:
message attachment
file-share history
draft
cache pin
DTN bundle
backup
Blob becomes GC-eligible when no required references remain.
61. Pinning
#![allow(unused)] fn main() { pub enum PinReason { UserSaved, ActiveTransfer, MessageReference, DtnRequired, BackupPending, } }
Pinned blobs are not evicted.
62. Cache vs Durable Blob
Classify:
Durable
Cache
Temporary
Relay
Different retention rules apply.
63. Garbage Collection
GC flow:
scan unreferenced blobs
↓
respect grace period
↓
verify no active transfer
↓
delete atomically
Never delete a blob solely because it is old.
64. Grace Period
A short grace period protects against:
race between unreference/re-reference
projection lag
transaction timing
Use configurable policy.
65. Cache Eviction
Evict in order:
expired temp
unreferenced previews
old cache
completed relay blobs
large low-priority objects
Do not evict:
user-saved
active
critical DTN
unless emergency storage policy explicitly allows.
66. Storage Pressure
States:
#![allow(unused)] fn main() { pub enum StoragePressure { Normal, Elevated, Critical, Full, } }
Effects:
reduce prefetch
pause bulk receive
evict cache
reject new large files
preserve critical data
67. Relay Blob Storage
A DTN relay may store encrypted blobs/chunks for others.
Relay storage must be:
- quota-limited
- opaque
- expiring
- separately accounted
- lower trust
Relay node does not get content keys.
68. DTN Chunk Strategy
Large files should not be blindly copied through BLE mesh.
Policy may allow:
metadata only
thumbnail
selected chunks
full file only on capable relay
DTN subsystem uses file chunk identities and priorities.
69. Emergency Files
Emergency mode can classify:
SOS text → highest
location → highest
thumbnail → high
voice → high
full image → medium
video → low/bulk
Blob engine supplies objects; Part 17 decides priority.
70. Multi-Device File Delivery
Destination may be:
account
specific device
all devices
Example:
send 4 GB file to Bob's laptop only
Part 02 identity resolves target devices.
71. Own-Device Blob Sync
Policies:
metadata everywhere
thumbnail everywhere
full file on demand
full file to selected devices
Avoid replicating every large blob to every device automatically.
72. Peer Discovery of Blob Availability
Peers can advertise:
I have BlobId X
only where privacy policy permits.
Do not globally broadcast private content hashes.
Availability exchange should occur inside authorized sessions.
73. Swarming
Future capability:
download different chunks from multiple authorized peers
Example:
Alice laptop has chunks
Alice phone has chunks
recipient fetches from both
This can improve speed/resilience.
Not necessary for initial release.
74. Source Selection
For each chunk, potential sources may include:
original sender
sender's other device
authorized cache
relay
Routing chooses path/source according to policy.
75. Trust of Sources
Content addressing means untrusted transport/source can provide bytes, but receiver still verifies hashes and decryption/authentication.
Authorization still controls who can learn blob existence/keys.
76. Full Blob Verification
After all chunks:
reconstruct ordered ciphertext
↓
verify root/blob identity
Do not rely only on per-chunk hashes if the manifest/root binding is incomplete.
77. Manifest Authentication
The manifest must be authenticated.
Possible:
manifest covered by E2EE message signature/authentication
or separately signed/authenticated.
An attacker must not be able to reorder/replace chunk descriptors.
78. Merkle Tree Option
For large blobs:
Merkle root
can authenticate chunks efficiently.
Benefits:
- independent chunk verification
- partial proofs
- swarming
A flat chunk-hash manifest may be sufficient initially.
79. Chunk Compression
Do not compress already compressed media blindly.
Potential compression for:
text archive
structured data
should be content-aware and application-controlled.
Never compress encrypted ciphertext expecting useful savings.
80. File Compression Architecture
If compression is used:
plaintext
↓
optional compression
↓
encryption
↓
chunking/addressing
or chunking before encryption depending chosen format.
The order must be explicitly specified.
81. Compression Bomb Protection
If decompression occurs:
max output size
ratio limits
streaming decode
must be enforced.
82. Media Derivatives
Blob subsystem should support related blobs:
original
thumbnail
preview
transcoded derivative
waveform
Represent relationships in metadata, not inside storage identity.
83. Image Derivatives
For a photo:
original encrypted blob
thumbnail encrypted blob
preview encrypted blob
This provides faster conversation rendering and mesh transfer.
84. Voice Note Representation
Voice note:
Opus blob
waveform metadata
duration
The file/blob system stores it as ordinary binary content.
85. Video Attachment
Video file attachment is not the same as realtime call media.
Video attachment:
blob transfer
Call:
realtime media protocol
Keep these separate.
86. Blob Read API
#![allow(unused)] fn main() { pub trait BlobReader { async fn read_range( &self, range: ByteRange, ) -> Result<Bytes, BlobError>; async fn stream( &self, ) -> Result<BlobStream, BlobError>; } }
Avoid APIs that require materializing entire objects.
87. Blob Write API
#![allow(unused)] fn main() { pub trait BlobWriter { async fn write_chunk( &mut self, index: u32, data: Bytes, ) -> Result<(), BlobError>; async fn finalize( self, ) -> Result<BlobId, BlobError>; } }
Writer verifies expected size/hash.
88. Blob Store Trait
#![allow(unused)] fn main() { pub trait BlobStore: Send + Sync { async fn contains(&self, id: BlobId) -> Result<bool, BlobError>; async fn stat(&self, id: BlobId) -> Result<BlobStat, BlobError>; async fn open(&self, id: BlobId) -> Result<Box<dyn BlobReader>, BlobError>; async fn begin_write(&self, descriptor: BlobDescriptor) -> Result<Box<dyn BlobWriter>, BlobError>; async fn delete(&self, id: BlobId) -> Result<(), BlobError>; } }
89. Metadata Store
Blob bytes and metadata can be separated:
filesystem:
ciphertext blobs
SQLite:
descriptors
references
transfer state
quotas
This is a strong practical architecture.
90. Directory Layout
Example:
blob-root/
├── objects/
│ ├── ab/
│ │ └── <blob-id>
│ ├── cd/
│ └── ...
├── staging/
├── temp/
└── quarantine/
Prefix sharding avoids giant directories.
91. Quarantine
Malformed or suspicious received objects may be placed in:
quarantine
for diagnostics before deletion.
Do not expose them to normal applications.
92. Filesystem Permissions
Blob directories should use restrictive permissions.
On mobile, app sandbox provides additional isolation.
On desktop, set application-owned paths carefully.
93. Database Schema
Conceptual tables:
blobs
blob_references
blob_derivatives
transfers
transfer_chunks
transfer_sources
blob_pins
storage_quotas
94. Blob Table
Example fields:
blob_id
size
state
created_at
last_accessed
storage_class
reference_count
Do not store content path from untrusted metadata.
95. Transfer Chunk Table
transfer_id
chunk_index
state
verified
bytes_received
retry_count
Use compact representation for very large chunk counts where needed.
96. Chunk Bitmap Optimization
For thousands of chunks, storing one SQL row per chunk may be expensive.
Alternative:
compressed bitmap / roaring bitmap / bitset blob
Benchmark before choosing.
97. Durable Progress
Progress displayed to user should derive from:
verified bytes
not merely bytes received into memory.
This gives truthful resume semantics.
98. Transfer ACK
Sender should distinguish:
transport ACK
chunk accepted
blob verified
user/application accepted
These are different states.
99. Chunk ACK
Receiver can ACK:
chunk index verified
or use range/bitmap acknowledgements.
Avoid ACK per tiny frame if chunk is already large.
100. Completion ACK
Final:
BlobVerified
means receiver persisted and verified complete blob.
Only then should sender mark transfer complete according to protocol.
101. Transfer Cancellation
Cancellation must:
stop scheduling
cancel active streams
persist Cancelled
apply staging retention policy
User may optionally keep partial data for resume.
102. Pause
Pause differs from cancel.
Pause retains:
journal
partial chunks
metadata
and can resume later.
103. Deferred Transfer
A transfer may be:
WaitingForWiFi
WaitingForPeer
WaitingForStorage
WaitingForApproval
WaitingForBattery
This integrates with Part 03 routing policy.
104. Retry
Use exponential backoff where appropriate.
Retry at:
transfer/session level
not per raw network packet.
Transport handles lower-level retries.
105. Corrupt Chunk
On hash/authentication failure:
discard chunk
increment failure count
request retransmission
possibly penalize source
Repeated corruption may indicate malicious/broken source.
106. Corrupt Manifest
Reject entire transfer offer if manifest authentication fails.
Do not attempt "best effort" reconstruction.
107. Multiple Sources
If one source repeatedly sends corrupt chunks:
blacklist source for that transfer
while preserving other sources.
108. Rate Limiting
Per-peer limits:
offers/sec
active transfers
bytes/sec
stored partial bytes
Unknown peers get stricter limits.
109. Abuse Protection
Protect against:
huge fake size
millions of chunks
never-completing transfers
hash mismatch spam
storage exhaustion
offer spam
All allocations/storage reservations must be bounded.
110. Resource Policy
#![allow(unused)] fn main() { pub struct FileResourcePolicy { pub max_parallel_transfers: usize, pub max_parallel_chunks: usize, pub max_buffer_bytes: usize, pub max_staging_bytes: u64, pub max_file_size: u64, } }
Applications can tighten limits.
111. Battery Awareness
Part 13 will deepen this.
File subsystem should already expose knobs:
pause background bulk on low battery
reduce concurrency
prefer Wi-Fi
avoid expensive discovery
112. Memory Pressure
On low memory:
shrink buffer pool
reduce parallel chunks
release previews
pause background transfers
Do not lose verified progress.
113. Thermal Pressure
Large hashing/encryption/transfers can generate heat.
On thermal stress:
reduce concurrency
defer noncritical bulk
114. Hashing Performance
Hash incrementally while streaming.
Do not read file twice if pipeline can safely combine:
read
hash
encrypt
write/send
But ensure chosen ciphertext-addressing scheme computes the correct final identity.
115. Preprocessing Pipeline
Potential outgoing flow:
source file
↓
validate metadata
↓
optional media derivative generation
↓
optional compression
↓
encrypt
↓
chunk/hash
↓
stage blob
↓
transfer
Exact order must be fixed in protocol spec.
116. Staging Before Send
For durable resumability, consider preparing encrypted blob locally before transfer.
Pros:
- stable BlobId
- easier retry
- easier multi-recipient send
- no need to reread original after app restart
Cons:
- extra storage
Policy can allow:
stream-only
stage-before-send
depending on file size/use.
117. Stream-Only Mode
For very large data or limited storage:
read source
↓
encrypt/chunk
↓
send
without retaining full encrypted blob.
But resume after source changes becomes harder.
Use only when application semantics permit.
118. Source Mutation Detection
If transferring from a file path/handle:
file may change
Record:
size
mtime if useful
inode/file-id if available
content hash as built
If source changes mid-transfer, abort/restart.
Do not silently send mixed versions.
119. Immutable Source Snapshot
Best production approach where feasible:
copy/import source into managed blob staging
Then transfer immutable bytes.
This simplifies correctness.
120. User File Handles
On Android/iOS, source may be a platform URI/handle.
Platform adapter exposes a stream/descriptor.
Core never assumes POSIX path access.
121. Android Integration
Kotlin may handle:
Photo Picker
Storage Access Framework
content:// URI
persistable permissions
Rust owns:
metadata validation
blob import
encryption
chunking
transfer
122. iOS Integration
Platform adapter handles:
document picker
photo picker
security-scoped URLs where applicable
Rust core remains unchanged.
123. Desktop Integration
Desktop can use native file picker through Dioxus/platform layer.
Blob engine receives:
safe file source handle/path abstraction
124. File Source Abstraction
#![allow(unused)] fn main() { pub trait FileSource { async fn len(&self) -> Result<u64, FileError>; async fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, FileError>; } }
Avoid tying core to std::fs::File only.
125. File Sink Abstraction
For export:
#![allow(unused)] fn main() { pub trait FileSink { async fn write(&mut self, data: &[u8]) -> Result<(), FileError>; async fn commit(self: Box<Self>) -> Result<(), FileError>; } }
Platform implementations can handle Android/iOS document destinations.
126. Export vs Internal Blob
Keep internal encrypted blob separate from user-visible exported file.
Flow:
internal blob
↓
decrypt
↓
user-selected destination
This preserves app sandbox and content integrity.
127. Local Decryption Cache
Avoid keeping permanent decrypted copies unless explicitly required.
Options:
decrypt on demand
temporary cache
user export
Privacy-sensitive default should minimize plaintext persistence.
128. Thumbnail Cache
Thumbnails may be decrypted and cached.
Use:
bounded LRU
storage pressure eviction
Do not let thumbnail cache grow without limit.
129. Media Decode Isolation
Image/video decode should be separate from blob integrity.
Flow:
verified/decrypted blob
↓
media decoder
Never decode unverified arbitrary chunks as trusted content unless streaming decoder is hardened and sandboxed appropriately.
130. Iroh Blobs Integration
If using Iroh's blob capabilities, wrap them behind the platform's blob/transfer abstraction.
Do not let application code depend directly on Iroh-specific blob types.
This allows:
Iroh backend
filesystem backend
future backend
without changing message/file APIs.
131. Internal vs Iroh Content IDs
If Iroh provides its own content hash/address, define mapping clearly:
Platform BlobId
↔
Iroh blob identifier
Do not assume external IDs are identical forever.
132. Direct Transfer Protocol
A custom file control protocol may negotiate:
offer
manifest
missing chunks
pause
resume
cancel
complete
Bulk data can use optimized transport-specific streams.
133. Protocol Extension Integration
Part 01 extension:
files/1
Capabilities may include:
chunking
resume
parallel
content addressing
partial read
multi-source
134. Capability Negotiation
Receiver advertises:
max chunk size
parallelism
max object size
resume support
Sender chooses common compatible behavior.
135. Protocol Versioning
Stable:
files/1
Major incompatible changes become:
files/2
Internal storage schema can evolve independently.
136. Wire Types vs Domain Types
Do not serialize:
#![allow(unused)] fn main() { TransferRecord }
directly.
Use:
FileOfferV1
ChunkRequestV1
ChunkAckV1
TransferCompleteV1
137. Postcard Usage
Postcard is suitable for compact control metadata.
Do not put large chunk data inside one giant Postcard structure if streaming raw bytes is more efficient.
Use:
small Postcard header
+
streamed chunk bytes
138. Chunk Frame
Conceptually:
ChunkHeader
├── transfer_id
├── blob_id
├── chunk_index
├── size
└── hash/proof metadata
followed by:
chunk bytes
Validate before allocating.
139. Large-Length Safety
Use:
u64 for file sizes
u32 for bounded chunk size
but always compare against local limits before conversion/allocation.
Never cast untrusted u64 directly to usize.
140. Progress Model
#![allow(unused)] fn main() { pub struct TransferProgress { pub total_bytes: u64, pub verified_bytes: u64, pub active_bytes_per_sec: Option<u64>, pub state: TransferState, } }
UI update frequency should be throttled.
141. ETA
ETA is approximate.
Do not present false precision.
Use:
~2 min
or omit when unstable.
142. UI Independence
Blob subsystem emits state/events.
It does not call:
show progress
open file
display image
Dioxus or another UI consumes transfer state.
143. Notification Integration
Background transfer may emit:
completed
failed
action required
through notification abstraction.
Core does not directly call Android notification APIs.
144. File Acceptance UX
Application may show:
Document.pdf
12.4 MB
From Alice
[Accept] [Decline]
Blob engine only provides trusted metadata and state.
145. Automatic Download Policy
Policy examples:
images on Wi-Fi
voice always
video never
files manual
emergency thumbnail automatically
This belongs to application policy integrated with routing/file service.
146. Data Saver
When enabled:
thumbnail first
defer originals
pause background large files
Blob engine exposes dependency relationships between derivative and original.
147. Storage Classes
#![allow(unused)] fn main() { pub enum BlobStorageClass { Durable, Cached, Temporary, Relay, } }
Each has:
- retention
- quota
- eviction priority
148. Blob Reference Model
#![allow(unused)] fn main() { pub struct BlobReference { pub owner_namespace: NamespaceId, pub object_id: ObjectId, pub blob_id: BlobId, pub role: BlobRole, } }
Examples:
message attachment
avatar
thumbnail
transfer
backup
149. Reference Integrity
Reference updates should be transactional with corresponding domain state.
Example:
message deleted
↓
remove message reference
↓
blob becomes GC candidate
Do not update reference count loosely outside transactions.
150. Reference Count Recovery
Reference count is derived metadata.
If inconsistent:
rebuild by scanning authoritative references
Do not treat cached count as irreplaceable truth.
151. GC Mark-and-Sweep Option
For robustness:
mark referenced
↓
sweep unreferenced + grace-period
can periodically validate counts.
152. Orphan Detection
Crash may create staged/unreferenced blobs.
Periodic cleanup detects:
no transfer
no reference
older than grace period
then removes safely.
153. Partial Blob Retention
After failed transfer:
keep for resume
until:
expiry
user cancel
storage pressure
Policy-driven.
154. Expiry
Transfers and blobs can have:
offer expiry
transfer expiry
relay expiry
cache expiry
These are different.
Do not use one timestamp for all semantics.
155. Message Deletion
Deleting a message may remove a blob reference.
If same blob referenced elsewhere:
do not delete
GC handles it.
156. Disappearing Messages
Blob linked only to disappearing message becomes eligible after:
message expiry
+
retention grace
unless user explicitly saved it.
157. User Save
"Save to device" should create:
user-owned durable reference
or export plaintext to user-selected location.
Clarify semantics.
158. Backup
Backup policy determines whether to include:
all blobs
only referenced durable blobs
thumbnails
relay cache
Relay cache should normally be excluded.
159. Incremental Backup
Content addressing allows efficient:
backup only missing BlobIds
Useful for large datasets.
160. Restore
Restore:
manifest metadata
↓
blob bytes
↓
hash verification
↓
reference rebuild
Never trust backup bytes without integrity checks.
161. Cross-Device Backup
A user's second device may serve as backup source.
The file subsystem treats it as another authorized blob source.
162. Server/Object Storage Optional Backend
Enterprise/cloud products may use:
S3-compatible
object store
server cache
through BlobStore adapter.
The communication SDK should not require cloud object storage.
163. Server-Side Encryption
If server stores blobs, prefer:
already application-encrypted ciphertext
Server need not receive plaintext.
164. Signed URLs
If a deployment uses object-storage signed URLs, keep that mechanism outside core blob identity.
It is a transport/access adapter.
165. Public Sharing
If future product supports public share links:
share capability
+
blob identifier
+
decryption/access material
must be carefully designed.
Do not turn private BlobId into public URL by default.
166. Capability-Based File Access
Potential future design:
ReadCapability
WriteCapability
ForwardCapability
This integrates with later capability-security architecture.
167. Forwarding Permission
A file may be:
recipient-only
forwardable
relayable ciphertext
public
Do not conflate:
relay allowed
with:
recipient may re-share plaintext
168. Relay vs Recipient
Relay:
stores/forwards ciphertext
Recipient:
has decryption authorization
These are distinct roles.
169. Multi-Recipient Files
Avoid re-encrypting gigantic file data separately for every recipient if application cryptographic model can safely share a content key via per-recipient encrypted key wrapping.
Conceptually:
one encrypted blob
+
recipient-specific wrapped blob key
This can be much more efficient.
170. Group Attachment
Group message:
one encrypted blob
+
group-authorized key distribution
Exact group crypto depends on group architecture.
171. Key Revocation Limits
If a recipient already obtained:
blob key + ciphertext
later revocation cannot erase plaintext they could have copied.
Do not promise remote deletion.
172. Re-Keying
Future access changes may require:
new blob encryption key
and potentially re-encryption.
This is expensive for large files.
Design authorization carefully to avoid unnecessary re-keying.
173. Encryption Key Caching
Cache active keys only in protected memory/state as necessary.
Do not persist unwrapped keys casually.
174. Transfer Across Transport Changes
Example:
start on Iroh relay
↓
LAN appears
↓
pause scheduler
↓
new authenticated path
↓
resume missing chunks
Same TransferId and BlobId.
175. Bluetooth → Wi-Fi Upgrade
Nearby discovery via BLE can bootstrap:
Wi-Fi Direct/Aware
Then file transfer continues on fast path.
No restart.
176. Wi-Fi → DTN Downgrade
If link disappears:
critical small chunks/metadata may enter DTN
bulk waits
according to policy.
177. Multipath Readiness
Chunk architecture naturally supports Part 12 multipath.
Each path can carry distinct chunks.
No core file format change is needed.
178. Integrity Under Multipath
Every chunk verifies independently.
Source/path does not affect final blob correctness.
179. Route Scoring Inputs
File service tells Part 03:
estimated bytes
bulk class
deadline/expiry
metered policy
DTN allowed
multipath allowed
Routing returns plan.
180. Queue Priority
Transfer classes:
EmergencyCritical
InteractiveAttachment
UserRequested
BackgroundSync
Bulk
Relay
Part 08 will generalize resource fairness.
181. Concurrent Transfer Fairness
One 50 GB file must not block:
small image
voice note
SOS attachment
Scheduler should use weighted fairness.
182. Per-Peer Fairness
One peer should not monopolize all upload slots.
183. Per-Application Fairness
If SDK embedded by multiple product modules, resource quotas can be namespaced.
184. Performance Metrics
Track:
hash throughput
encryption throughput
disk read/write throughput
network throughput
verified bytes/sec
resume rate
dedup hit rate
GC reclaimed bytes
Do not expose content names in telemetry.
185. Diagnostics
Useful diagnostic snapshot:
active transfers
path
bytes verified
chunk counts
storage usage
cache usage
staging usage
last error
186. Error Model
#![allow(unused)] fn main() { pub enum FileError { NotFound, Unauthorized, InvalidManifest, HashMismatch, DecryptionFailed, StorageFull, QuotaExceeded, UnsupportedCapability, SourceChanged, Cancelled, Expired, Transport, Storage, } }
Use typed errors.
187. Recoverability Classification
Retryable
Resumable
Permanent
UserActionRequired
Example:
network timeout → resumable
hash mismatch → retryable/source penalty
unsupported capability → permanent/fallback
storage full → user action required
188. No anyhow in Public File API
Use typed errors in reusable crate boundaries.
anyhow is acceptable at application bootstrap/CLI.
189. Security Invariants
- Blob bytes are never trusted before integrity/authentication verification.
- Remote filenames never become storage paths directly.
- Large declared sizes never cause unchecked allocation.
- Receiver never marks complete before full verification.
- Encryption keys do not travel in public blob metadata.
- Relay nodes do not require plaintext keys.
- Duplicate chunks are idempotent.
- Restart does not lose verified progress.
- Cancellation does not corrupt completed blobs.
- A malicious peer cannot exceed configured storage/resource quotas.
190. Testing Layers
Unit:
manifest
chunk math
hashing
reference counting
state machine
Integration:
send/receive
pause/resume
crash/restart
multi-device
Network:
path switch
loss
duplicates
reordering
Security:
tampered chunk
tampered manifest
oversized metadata
path traversal
191. Property Tests
Invariants:
reassembled verified blob equals original
duplicate chunk does not increase verified bytes
completed transfer remains complete after restart
missing chunk bitmap round-trips
GC never deletes pinned referenced blob
192. Fuzz Targets
Fuzz:
manifest parser
file offer
chunk header
resume bitmap
metadata parser
archive-adjacent metadata if supported
All allocations bounded.
193. Crash Injection
Inject crash:
after offer
mid chunk
after chunk write before journal
after journal before ACK
during finalization
during GC
Verify safe recovery.
194. Corruption Tests
Modify:
chunk byte
manifest entry
blob root
metadata
Receiver must detect.
195. Large File Test
Test multi-gigabyte logical files without loading whole file into memory.
Measure:
peak memory
resume latency
throughput
CPU
196. Small File Test
Ensure architecture does not impose huge overhead on:
1 KB
10 KB
100 KB
Tiny files may use one chunk.
197. Bluetooth Test
Test:
small file over BLE
interruption
resume
Wi-Fi upgrade
Ensure bounded memory and no restart from zero.
198. DTN Test
Test:
thumbnail carried
full image deferred
gateway appears
remaining data completes
199. Multi-Source Test
Future:
two sources
same blob
different chunks
Receiver verifies one final object.
200. Storage Pressure Test
Fill storage until:
Elevated
Critical
Full
Verify:
- cache eviction
- bulk rejection
- critical preservation
- clear UI state
201. Suggested Crate Structure
crates/comm-files/
├── src/
│ ├── lib.rs
│ ├── file.rs
│ ├── offer.rs
│ ├── service.rs
│ ├── policy.rs
│ └── error.rs
crates/comm-blob-store/
├── src/
│ ├── lib.rs
│ ├── blob.rs
│ ├── descriptor.rs
│ ├── manifest.rs
│ ├── chunk.rs
│ ├── reader.rs
│ ├── writer.rs
│ ├── refs.rs
│ ├── gc.rs
│ ├── quota.rs
│ └── error.rs
crates/comm-transfer/
├── src/
│ ├── planner.rs
│ ├── scheduler.rs
│ ├── state.rs
│ ├── journal.rs
│ ├── resume.rs
│ ├── progress.rs
│ └── error.rs
crates/comm-transfer-protocol/
├── src/
│ ├── v1/
│ ├── codec.rs
│ └── limits.rs
202. Public API
Keep high-level API simple:
#![allow(unused)] fn main() { let transfer = files .send_file(peer, source, policy) .await?; transfer.pause().await?; transfer.resume().await?; transfer.cancel().await?; }
Receiving:
#![allow(unused)] fn main() { let offer = files.next_offer().await?; offer.accept(destination_policy).await?; }
The application does not manipulate chunk protocol directly.
203. Blob API
Applications needing lower-level access can use:
#![allow(unused)] fn main() { let blob = blobs.import(source).await?; let reader = blobs.open(blob.id()).await?; }
Keep low-level API separate from ordinary transfer API.
204. Messaging Integration API
Example:
#![allow(unused)] fn main() { let blob = files.import_attachment(source).await?; messaging .send( conversation, MessageContent::Attachment(blob.reference()) ) .await?; }
Actual file transfer can begin eagerly or on recipient request according to policy.
205. File-Only API
#![allow(unused)] fn main() { files .send_file( Destination::Device(device_id), source, FileSendPolicy::default(), ) .await?; }
No conversation required.
206. Initial Production Scope
Implement first:
immutable encrypted blobs
fixed-size chunking
chunk hashes
flat manifest
filesystem blob store
SQLite metadata
resumable transfer
pause/cancel
single-source transfer
bounded parallel chunks
routing integration
storage quotas
reference tracking
GC
crash recovery
Defer initially:
content-defined chunking
full swarming
hierarchical manifests
public share links
complex cloud object-store adapters
advanced multipath
207. Implementation Phases
Phase 1 — Blob primitives
BlobId
ChunkHash
BlobDescriptor
Manifest
Phase 2 — Local store
filesystem objects
staging
SQLite metadata
reference tracking
Phase 3 — Encryption and import
per-blob key
chunk encryption
hashing
atomic finalization
Phase 4 — Transfer protocol
offer
accept
manifest
chunk request
ACK
complete
Phase 5 — Resume
journal
bitmap/ranges
restart recovery
Phase 6 — Routing
Iroh/LAN/Bluetooth/DTN
path switching
Phase 7 — Resource management
quotas
GC
storage pressure
backpressure
Phase 8 — Hardening
fuzzing
crash injection
corruption tests
large-file benchmarks
208. Definition of Done
Part 05 is complete when:
- file transfer works independently from messaging
- messages reference blobs instead of embedding file bytes
- files can be imported without loading entire content into memory
- blobs are immutable and integrity-addressed
- privacy-sensitive mode addresses ciphertext rather than plaintext
- chunking supports resume
- individual chunks can be verified
- transfer state survives process death
- incomplete data remains in staging until verified
- completed blob finalization is atomic
- storage paths never use remote filenames directly
- quotas prevent storage exhaustion
- GC never deletes referenced/pinned blobs
- files can pause/resume/cancel
- large transfer can switch transport without starting from zero
- Bluetooth/DTN can carry policy-appropriate subsets
- multi-device destination targeting works
- relay nodes can store ciphertext without decryption keys
- Dioxus/Kotlin/iOS UI layers are not dependencies
- public API is small and typed
- fuzz, corruption, crash, resume, and large-file tests exist
209. Relationship to Earlier Parts
Part 05 builds on:
01 — Protocol Extension System
02 — Multi-Device Identity
03 — Transport & Routing Policy Engine
04 — Offline Event Log
It directly supports:
06 — DTN / Store-Carry-Forward
07 — Capability Negotiation Expansion
08 — Resource Limits & Backpressure
09 — Crash Recovery
10 — Protocol Fuzzing & Test Suite
11 — Relay / Self-Hosted Infrastructure
12 — Multipath Networking
13 — Battery-Aware Scheduling
14 — Proximity Abstraction
16 — Daemon & Headless Runtime
17 — Emergency Priority Architecture
18 — Network Diagnostics & Path Visualization
20 — Embedded Linux Node
23 — External Interoperability Suite
210. Final Principle
The file/blob subsystem should make this possible:
A 5 GB file
can be:
imported once
encrypted once
identified immutably
transferred in bounded chunks
paused
resumed
switched from relay to LAN
partially carried by DTN where policy allows
verified independently
referenced by a message
sent without messaging
cached safely
garbage-collected when no longer referenced
without any application layer needing to understand the raw transfer mechanics.
That is what makes the subsystem reusable, efficient, crash-safe, privacy-preserving, and suitable as a shared binary-data foundation for the entire communication platform.