Software Design Document
Introduction
This SDD describes the architecture and design of AirGap Transfer’s MVP.
Guiding document: Principles
Architecture Overview
System Context
┌────────────────────────────────────────────────────────┐
│ Pure Rust CLI Application │
├────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ CLI Parser │──────►│ Commands │ │
│ │ (clap) │ │ (pack/unpack)│ │
│ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌─────────────┐ ┌─────────┐ │
│ │ Chunker │ │ Verifier │ │ State │ │
│ │ (streaming) │ │ (pluggable) │ │ (JSON) │ │
│ └──────┬───────┘ └─────────────┘ └─────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ USB/Disk I/O │ │
│ └───────────────┘ │
└────────────────────────────────────────────────────────┘
Design Rationale
Decision |
Rationale |
|---|---|
Pure Rust |
Memory safety, cross-platform, minimal runtime |
CLI only |
Focus on functionality, defer GUI to post-MVP |
Streaming architecture |
Handle files larger than available RAM |
JSON manifest |
Human-readable, easy to inspect and debug |
Pluggable hash verify |
Trait-based interface; SHA-256 default, extensible to future algorithms |
No compression |
Simplicity, defer to post-MVP |
File Structure
Per principles.md: Flat structure, minimal modules
airgap-transfer/
├── src/
│ ├── main.rs # Entry point, CLI setup
│ ├── commands/
│ │ ├── pack.rs # Pack operation implementation
│ │ ├── unpack.rs # Unpack operation implementation
│ │ └── list.rs # List operation implementation
│ ├── chunker.rs # Streaming chunk creation/reconstruction
│ ├── verifier.rs # SHA-256 checksum operations
│ ├── manifest.rs # Manifest file handling (JSON)
│ └── usb.rs # USB detection and capacity checks
├── Cargo.toml
├── vendor/ # Vendored dependencies (for air-gap builds)
└── .cargo/
└── config.toml # Points to vendor directory
Data Design
Manifest Structure
Single JSON file per transfer operation
{
"version": "1.0",
"operation": "pack",
"source_path": "/path/to/source",
"total_size_bytes": 10737418240,
"chunk_size_bytes": 1073741824,
"hash_algorithm": "sha256",
"chunk_count": 10,
"chunks": [
{
"index": 0,
"filename": "chunk_000.tar",
"size_bytes": 1073741824,
"checksum": "sha256:abc123...",
"status": "completed"
}
],
"created_utc": "2026-01-04T12:00:00Z",
"last_updated_utc": "2026-01-04T12:15:00Z"
}
Chunk File Format
Format: tar archive (standard Unix format)
Naming:
chunk_XXX.tar(zero-padded, 3-digit index)Size: Fixed size (except final chunk which may be smaller)
Contents: Raw file data, preserving directory structure
State Persistence
Manifest file location:
Pack operation: Written to USB alongside chunks
Unpack operation: Read from USB chunk location
Resume: Manifest status field tracks completed chunks
Component Design
CLI Parser (main.rs)
Command structure:
airgap-transfer <command> [options]
Commands:
pack <source> <dest> Split files into chunks
unpack <source> <dest> Reconstruct from chunks
list <chunk-location> Show chunk inventory
Global options: --dry-run, --verbose, --no-verify, --chunk-size, --hash-algorithm
Chunker (chunker.rs)
Core responsibility: Streaming chunk creation and reconstruction
Pack behavior:
Stream source files into tar format
Write fixed-size chunks directly to USB
Calculate checksum during streaming (single-pass)
Update manifest progressively
Unpack behavior:
Verify chunk checksums before processing
Extract tar chunks sequentially to destination
Reconstruct original directory structure
Verifier (verifier.rs)
Core responsibility: Cryptographic integrity verification
Functions:
Generate checksum during streaming using configured algorithm
Verify chunk checksum matches manifest (algorithm-aware)
Report verification failures with details
The verifier implements a HashAlgorithm trait. New algorithms are added by implementing
this trait — no changes to the chunker, manifest, or CLI modules required.
Manifest (manifest.rs)
Core responsibility: Metadata persistence and state management
Functions:
Create manifest from pack operation parameters
Update chunk status as operations complete
Read and validate manifest during unpack
Support resume by tracking completion status
USB Handler (usb.rs)
Core responsibility: Removable media detection and capacity checks
Functions:
Detect USB mount points (platform-specific)
Query available capacity
Auto-determine optimal chunk size
Sync filesystem before USB removal prompt
Interaction Flows
Pack Operation
User App USB
│ │ │
│ pack source usb │ │
│───────────────────────►│ │
│ │ Detect USB capacity │
│ │───────────────────────►│
│ │ Calculate chunk count │
│ │ │
│ │ Stream chunk 0 to USB │
│ │───────────────────────►│
│ │ Update manifest │
│ │ │
│ "Insert next USB" │ │
│◄───────────────────────│ │
│ │ Stream chunk 1 to USB │
│ │───────────────────────►│
│ │ Sync and finish │
│ "Complete: 2 chunks" │ │
│◄───────────────────────│ │
Unpack Operation
User App USB
│ │ │
│ unpack usb dest │ │
│───────────────────────►│ │
│ │ Read manifest │
│ │───────────────────────►│
│ │ Verify all chunks │
│ │───────────────────────►│
│ │ Extract chunk 0 │
│ │ Extract chunk 1 │
│ │ Verify final output │
│ "Complete: verified" │ │
│◄───────────────────────│ │
Dependencies
Minimal crates: Target ≤10 direct dependencies
See Principles for dependency guidelines.
Expected dependencies:
clap - CLI argument parsing
serde / serde_json - Manifest serialization
sha2 - SHA-256 checksums (default backend); trait interface supports additional backends
tar - Tar archive creation/extraction
Platform-specific filesystem libs (stdlib where possible)
Security & Privacy
Privacy by architecture: No network code exists in the application.
Threat |
Mitigation |
|---|---|
Data exfiltration |
No network crates in dependency tree |
Path traversal |
Validate and sanitize all paths |
Checksum bypass |
Verification enabled by default (disable with –no-verify) |
Malicious chunks |
Verify checksums before extraction |
USB interception |
v1.2: Optional AEAD encryption at rest (ChaCha20-Poly1305) |
Manifest tamper |
v1.2: Keyed MAC authentication of manifest |
v1.2 Encryption Design (Planned)
The v1.0 checksum-based verification detects accidental corruption but does not protect against intentional tampering or unauthorized reading of intercepted USB media. v1.2 introduces optional AEAD encryption (see SRS v1.2 requirements) to address these threats.
Design principle: Encryption is opt-in. When no passphrase is provided, behavior is identical to v1.0. This preserves the simple default workflow while enabling encryption for users with higher-security threat models.
Key architecture decisions:
AEAD (not separate encrypt-then-MAC) to eliminate composition errors
Passphrase-based key derivation via Argon2id (no PKI infrastructure required)
Manifest remains human-readable JSON, authenticated via keyed MAC
Trait-based AEAD backend mirrors existing
HashAlgorithmpattern
Deployment
Air-Gap Support
The application supports deployment on air-gapped systems (no internet access).
Requirements:
Pure Rust, single binary
Vendored dependencies via
cargo vendorOffline build:
cargo build --release --offline
Platform Packages
Platform |
Format |
Notes |
|---|---|---|
macOS |
Binary |
Universal binary (x86_64 + ARM64) |
Windows |
.exe |
Standalone executable |
Linux |
Binary + .deb/.rpm |
Static binary preferred |
Platform Considerations
USB Detection
Platform |
Approach |
|---|---|
macOS |
|
Linux |
|
Windows |
DriveInfo API via WinAPI |
Filesystem Sync
Platform |
Command |
|---|---|
macOS/Linux |
|
Windows |
|