Garrard.CloudShare.Receiver
0.1.8
See the version list below for details.
dotnet tool install --global Garrard.CloudShare.Receiver --version 0.1.8
dotnet new tool-manifest
dotnet tool install --local Garrard.CloudShare.Receiver --version 0.1.8
#tool dotnet:?package=Garrard.CloudShare.Receiver&version=0.1.8
nuke :add-package Garrard.CloudShare.Receiver --version 0.1.8
cloud-share
A beautiful, secure way to share files and text between computers using Microsoft Dev Tunnels. Install as .NET global tools and enjoy a gorgeous CLI experience powered by Spectre.Console!
โจ Features
- ๐จ Beautiful CLI with Spectre.Console - colors, tables, progress indicators
- ๐ Web UI โ both tools open a local browser UI (Bootstrap 5 + Alpine.js dark theme)
- ๐ Authenticated web UI โ sender UI gated by a one-time UI token + HttpOnly cookie; receiver UI protected by a local session token
- ๐ End-to-end encryption โ AES-256-GCM encrypts all content before it leaves the sender
- ๐ HKDF key derivation โ AES key derived via HKDF-SHA256 (RFC 5869 / NIST SP 800-56C), not a raw hash
- ๐งฎ Integrity verification โ SHA-512 hash shown on both sides; receiver must confirm match before accessing content
- ๐ก๏ธ Brute-force protection โ 10 failed auth attempts triggers a 60-second lockout with HTTP 429
- ๐ Security hardened โ Bearer auth, Anti-CSRF (Origin/Referer), security headers (CSP, CORS, X-Frame-Options), Kestrel size limits, constant-time comparisons, SRI hashes on all CDN dependencies (see full security features โ)
- ๐ก๏ธ Rate limiting โ receiver endpoints protected by a fixed-window rate limiter (30 req/10s) to prevent local abuse
- ๐
CLOUDSHARE_SECRETenv var โ pre-load the shared secret via environment variable on both sender and receiver to avoid process list exposure - ๐ File transfers โ upload from sender UI, download via explicit button after hash verification
- ๐ฆ Zip bundles โ bundle multiple files into a zip, with optional AES-256 zip password, and queue it as a single item
- ๐ Text sharing โ paste text in sender UI, copy to clipboard from receiver UI
- ๐ Microsoft Dev Tunnels integration (free tier)
- โก Fast and lightweight .NET 10 global tools
- ๐ No port forwarding or firewall configuration needed
- ๐ค Auto-installs dependencies (devtunnels CLI) with user confirmation
- ๐ Auto-login โ prompts for Microsoft or GitHub account, remembers your choice; automatically re-authenticates if the token expires
- ๐พ Session memory โ receiver remembers the last URL and secret across runs
- ๐ Cross-platform: Windows, macOS (including Apple Silicon), Linux (Ubuntu)
๐ Installation
Option 1: Install as .NET Global Tools (Recommended)
# Install the sender
dotnet tool install --global Garrard.CloudShare.Sender
# Install the receiver
dotnet tool install --global Garrard.CloudShare.Receiver
After installation, you can run the tools from anywhere:
cloud-share-sender [file-path]
cloud-share-receiver
Option 2: Build from Source
git clone https://github.com/garrardkitchen/cloud-share.git
cd cloud-share
dotnet build --configuration Release
๐ Prerequisites
.NET 10 SDK
Download and install from https://dotnet.microsoft.com/download/dotnet/10.0
Verify installation:
dotnet --version # Should be 10.0 or higher
DevTunnels CLI (Auto-installed)
The tools will automatically detect if DevTunnels CLI is missing and offer to install it for you! If you prefer manual installation:
Windows (via winget):
winget install Microsoft.devtunnel
macOS (via Homebrew):
brew install --cask devtunnel
Linux:
curl -sL https://aka.ms/DevTunnelCliInstall | bash
Authentication
On first run the sender will prompt you to choose a login method and launch a browser-based OAuth flow automatically:
? Choose your login method:
> Microsoft account
GitHub account (free tier)
Your choice is saved to ~/<AppData>/CloudShare/preferences.json and reused on every subsequent run โ no need to log in again.
If the login token expires, the sender detects this automatically, re-authenticates using your saved preference (no prompt), and retries the tunnel โ no intervention needed.
To switch accounts or re-authenticate manually, pass --reset:
cloud-share-sender --reset
๐ฏ Usage
Sender
Start the sender (text-only mode):
cloud-share-sender
Start with a pre-loaded secret via environment variable (avoids process list exposure):
export CLOUDSHARE_SECRET="your-secret-here"
cloud-share-sender
Start the sender with a file pre-loaded:
cloud-share-sender /path/to/document.pdf
Re-authenticate or switch accounts:
cloud-share-sender --reset
cloud-share-sender /path/to/file.pdf --reset
Check version:
cloud-share-sender --version
The sender starts a local web UI at http://localhost:5000 and opens it in your browser automatically. The UI lets you:
- Text tab โ paste any text and click Share Text to encrypt and queue it
- File tab โ drag-and-drop or browse for a single file and click Upload File
- Zip Bundle tab โ add multiple files to a bundle, set an optional AES-256 zip password, then click Create & Queue Zip to zip, encrypt, and queue the bundle as a single item
- Queue table โ see all queued items with their SHA-512 hash prefix and timestamp; password-protected zips show a ๐ icon; items are marked โ once received
The CLI also prints the tunnel URL and secret token to share with the receiver:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Cloud Share Sender v1.0.0
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ DevTunnels CLI is installed
โ Authenticated with DevTunnels
โญโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ Setting โ Value โ
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Secret Token โ aBc123XyZ456 โ
โ File to Shareโ none - text only mode โ
โ Local Port โ 5000 โ
โฐโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
Tunnel URL: https://abc123.uks1.devtunnels.ms:5000
Web UI: http://localhost:5000 (opening in browser...)
Press Ctrl+C to stop sharing.
Receiver
cloud-share-receiver
With a pre-loaded secret (skips the interactive secret prompt entirely):
export CLOUDSHARE_SECRET="your-secret-here"
cloud-share-receiver
Check version:
cloud-share-receiver --version
The receiver prompts for the tunnel URL and secret, then starts a local web UI at http://localhost:5001 and opens it in your browser automatically.
Session memory: The URL and secret are saved to ~/<AppData>/CloudShare/receiver-session.json as you type them. On the next run the receiver offers to reuse the previous connection:
โญโ Previous session โโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ URL: https://c84xl717.uks1.devtunnels.ms โ
โ Secret: ************ โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
? Use previous connection? [Y/n]
Answering Y skips the prompts entirely. Answering N overwrites the saved session with new values.
Receiving items:
- When the sender queues an item, it appears in the receiver's queue list in real time (via SSE).
- Click Receive on any queued item.
- The Hash Verification dialog opens showing both the sender's SHA-512 hash and the hash computed by the receiver. Confirm they match before proceeding.
- Mismatch โ the item is permanently deleted from the sender and the content is discarded.
- Text match โ the decrypted text is displayed with a Copy text button to copy it to your clipboard.
- File/Zip match โ a Download File button appears (bottom-left of the dialog); click it to save the file to your browser's download folder. Password-protected zips show a ๐ warning โ you will need the password (shared out-of-band) to extract the zip.
๐๏ธ Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ cloud-share-sender โ โ cloud-share-receiver โ
โ โ โ โ
โ CLI (Spectre.Console) โ โ CLI (Spectre.Console) โ
โ Web UI localhost:5000 โ โ Web UI localhost:5001 โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ AES-256-GCM encryptโ โ Microsoft Dev โ โ Proxy โ sender tunnelโ โ
โ โ SHA-512 hash โ โโโโโโTunnelsโโโโโโโโโโ โ AES-256-GCM decrypt โ โ
โ โ Queue (in-memory) โ โ โ โ SHA-512 verify โ โ
โ โ SSE broadcaster โ โ โ โ SSE proxy โ โ
โ โโโโโโโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โฒ
โโโโโโโโโโโโโ Share tunnel URL + secret โโโโโโโโโโโโโ
(out-of-band, e.g. chat)
๐ Security
Cloud Share has been subject to a full parallel security audit using OWASP Top 10, NIST SP 800-53, NIST CSF 2.0, STRIDE, and CWE/SANS Top 25. The controls below reflect the implemented findings.
๐ Full Security Features Reference โ
๐ Complete Audit Report โ
Transit & Cryptography
| Control | Detail |
|---|---|
| AES-256-GCM encryption | All content encrypted before leaving the sender (FIPS 197, NIST SP 800-38D) |
| HKDF-SHA256 key derivation | AES key derived via HKDF (RFC 5869 / NIST SP 800-56C) โ not a raw SHA-256 hash |
| SHA-512 integrity hash | Computed on plaintext before encryption; verified by the receiver before content is exposed โ mismatch permanently deletes the item (FIPS 198-1) |
| TLS 1.3 transport | All tunnel traffic over Microsoft DevTunnels HTTPS (NIST SP 800-52 Rev. 2) |
| Authorization: Bearer header | Shared secret transmitted via HTTP Authorization header only โ never in URL query strings |
| Plaintext zeroed in memory | CryptographicOperations.ZeroMemory() clears decrypted buffers immediately after use (NIST SP 800-53 SC-28) |
Authentication & Access Control
| Control | Detail |
|---|---|
| Sender UI token | Separate 128-bit random token gates the sender web UI independently of the encryption secret. Browser URL includes ?token=<uiToken>; first visit sets an HttpOnly SameSite=Strict cookie. Unauthenticated requests receive HTTP 401. |
| Receiver local session token | 128-bit token generated at startup; required on all local receiver endpoints via Bearer header or HttpOnly cookie (for EventSource compatibility). |
| Brute-force lockout | 10 consecutive auth failures โ 60-second lockout โ HTTP 429 with Retry-After header (NIST SP 800-53 AC-7, OWASP A07:2021) |
| Constant-time comparisons | All secret, token, and hash comparisons use CryptographicOperations.FixedTimeEquals() to prevent timing side-channel attacks (CWE-208) |
| HTTPS-only enforcement | Receiver rejects http:// tunnel URLs at startup |
HTTP Hardening
| Control | Detail |
|---|---|
| Content-Security-Policy | default-src 'self' cdn.jsdelivr.net with explicit allowlists on both UIs |
| X-Frame-Options: DENY | Prevents clickjacking via iframe embedding |
| X-Content-Type-Options: nosniff | Blocks MIME-sniffing attacks |
| Referrer-Policy: no-referrer | Suppresses Referer headers on all navigations |
| CORS โ localhost-only | Restricted to localhost:<port> only; blocks DNS-rebinding attacks |
| Anti-CSRF (Origin/Referer) | All sender POST/DELETE endpoints validate the Origin or Referer header; browser-originating cross-site requests return HTTP 403 (CWE-352) |
| Kestrel request body limit | 110 MB hard limit at Kestrel level, preventing chunked-encoding bypass of application checks |
| Subresource Integrity (SRI) | All CDN <script> and <link> tags carry integrity= + crossorigin="anonymous" attributes โ a CDN compromise cannot inject malicious JavaScript (OWASP A08:2021, NIST SP 800-53 SI-7) |
Resource & DoS Protection
| Control | Detail |
|---|---|
| SSE connection limit | Max 10 concurrent SSE connections; bounded channel (100 items) per client; excess returns HTTP 429 |
| Queue size limit | In-memory queue capped at 50 items |
| File size limits | Text: 10 MB ยท Files: 100 MB ยท Zips: 100 MB (aggregate input checked before MemoryStream allocation) |
| Response size caps | Receiver caps /queue responses at 1 MB and item responses at 150 MB โ prevents OOM from a malicious sender (CWE-770) |
| Rate limiting (receiver) | Fixed-window limiter: 30 requests per 10 seconds on all receiver endpoints |
| SSE message validation | SSE proxy parses and validates each data: line as JSON; unknown or malformed event types are dropped and logged as SECURITY_EVENT: before reaching the browser |
| API / SSE client separation | API HttpClient: 30-second timeout. SSE HttpClient: InfiniteTimeSpan with CancellationToken โ prevents slow-loris attacks on API calls |
Secret & Credential Hygiene
| Control | Detail |
|---|---|
| Secret masked in console | Displayed as ****<last-4> by default; --show-secret to reveal full value |
| Session file permissions | chmod 600 on receiver-session.json on Linux/macOS (NIST SP 800-53 AC-6) |
CLOUDSHARE_SECRET env var |
Both tools read CLOUDSHARE_SECRET at startup โ if set, the interactive secret prompt is skipped entirely, preventing the secret appearing in process listings (ps aux / /proc/<pid>/cmdline) |
| Key separation | UI access token and encryption secret are independent values โ never reused for each other's purpose |
Input Validation
| Control | Detail |
|---|---|
| Filename sanitisation | Path separators, null bytes, and oversized names stripped on both sender (upload) and receiver (download) |
| Item ID validation | Route {id} validated against ^[a-zA-Z0-9_-]{8,64}$ before forwarding to sender |
| Exception details suppressed | Proxy errors return HTTP 502 โ no stack traces or internal paths exposed to HTTP clients |
Supply Chain & CI/CD
| Control | Detail |
|---|---|
| Vulnerable package scan | dotnet list package --vulnerable --include-transitive on every CI build |
| Dependabot | Weekly NuGet dependency scanning with automated PRs (OWASP A06:2021) |
Security Event Logging
All security-relevant events are prefixed with SECURITY_EVENT: in the console output at warning level, making them straightforward to grep or pipe to a SIEM:
- Authentication failures and brute-force lockouts
- Hash mismatches (potential tampering)
- Decryption failures
- Oversized response rejections
- Unknown or malformed SSE event types
- Anti-CSRF Origin/Referer violations
Best Practices
- ๐ Always share the secret token through a secure, out-of-band channel (e.g. encrypted messaging)
- ๐ A new secret is generated on every sender start โ secrets are single-session
- โฑ๏ธ Stop the sender immediately after file transfer is complete
- ๐ฏ This tool is designed for one-time, temporary file sharing only
๐ Network Requirements
The following outbound connections are required. Share this list with your network administrator for firewall rule creation.
Sender machine
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
*.devtunnels.ms |
443 | HTTPS/WSS | Dev Tunnel relay (active while sender is running) |
global.rel.tunnels.api.visualstudio.com |
443 | HTTPS | Dev Tunnels management API (tunnel create/delete) |
login.microsoftonline.com |
443 | HTTPS | Microsoft account login (devtunnel user login) |
login.live.com |
443 | HTTPS | Microsoft account OAuth token exchange |
github.com |
443 | HTTPS | GitHub account login (devtunnel user login -g) |
aka.ms |
443 | HTTPS | DevTunnels CLI installer redirect |
cdn.jsdelivr.net |
443 | HTTPS | Bootstrap 5, Bootstrap Icons, Alpine.js (sender web UI CDN assets) |
Receiver machine
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
*.devtunnels.ms |
443 | HTTPS | Connecting to the sender's tunnel |
cdn.jsdelivr.net |
443 | HTTPS | Bootstrap 5, Bootstrap Icons, Alpine.js (receiver web UI CDN assets) |
Tool installation only (one-time)
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
api.nuget.org |
443 | HTTPS | dotnet tool install package download |
dotnetcli.blob.core.windows.net |
443 | HTTPS | .NET SDK download |
Exact CDN resources loaded by the web UI
Both the sender (localhost:5000) and receiver (localhost:5001) browsers load:
| Resource | URL | SRI Algorithm |
|---|---|---|
| Bootstrap 5.3.8 CSS | https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css |
SHA-384 |
| Bootstrap 5.3.8 JS | https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js |
SHA-384 |
| Bootstrap Icons 1.11.3 CSS | https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css |
SHA-256 |
| Bootstrap Icons fonts | https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/fonts/bootstrap-icons.woff2 |
(font, no SRI) |
| Alpine.js 3.15.8 | https://cdn.jsdelivr.net/npm/alpinejs@3.15.8/dist/cdn.min.js |
SHA-256 |
All <script> and <link> tags carry integrity= (SRI) and crossorigin="anonymous" attributes. Versions are pinned exactly โ no floating @3.x.x wildcards.
Air-gapped environments: If outbound access to
cdn.jsdelivr.netis blocked, the web UI will not render correctly. In that scenario the CDN<script>and<link>tags insender-ui.htmlandreceiver-ui.htmlcan be replaced with locally hosted copies of the same assets.
๐งช Development
Building
dotnet build --configuration Release
Running Tests
dotnet test --configuration Release
With coverage:
dotnet test --collect:"XPlat Code Coverage" --results-directory ./coverage
Project Structure
cloud-share/
โโโ cloud-share.sln # Solution file
โโโ README.md # This file
โโโ LICENSE # MIT license
โโโ global.json # .NET SDK version
โโโ .github/
โ โโโ workflows/
โ โโโ ci.yml # CI/CD pipeline with tests & coverage
โโโ CloudShare.Core/
โ โโโ CloudShare.Core.csproj # Shared library
โ โโโ CryptoHelper.cs # AES-256-GCM encryption & SHA-512 hashing
โ โโโ DependencyManager.cs # Auto-install, login flow & preferences
โ โโโ SpectreHelper.cs # Markup escaping helpers
โ โโโ VersionInfo.cs # Version information
โโโ TunnelSender/
โ โโโ TunnelSender.csproj # Sender global tool (uses SharpZipLib for zip bundles)
โ โโโ Program.cs # Sender application & web server
โ โโโ sender-ui.html # Embedded sender web UI
โโโ TunnelReceiver/
โ โโโ TunnelReceiver.csproj # Receiver global tool
โ โโโ Program.cs # Receiver application & proxy web server
โ โโโ receiver-ui.html # Embedded receiver web UI
โโโ CloudShare.Tests/
โโโ CloudShare.Tests.csproj # Test project
โโโ DependencyManagerTests.cs
โโโ VersionInfoTests.cs
๐ฆ NuGet Packages
- Garrard.CloudShare.Sender - The sender tool
- Garrard.CloudShare.Receiver - The receiver tool
Install globally:
dotnet tool install --global Garrard.CloudShare.Sender
dotnet tool install --global Garrard.CloudShare.Receiver
Update:
dotnet tool update --global Garrard.CloudShare.Sender
dotnet tool update --global Garrard.CloudShare.Receiver
Uninstall:
dotnet tool uninstall --global Garrard.CloudShare.Sender
dotnet tool uninstall --global Garrard.CloudShare.Receiver
๐ Troubleshooting
"DevTunnels CLI not found"
The tool will offer to install it automatically. If auto-install fails, install manually (see Prerequisites).
"Authentication required" / "Login token expired"
The sender automatically detects expired tokens and re-authenticates using your saved login preference. If the browser flow fails, run devtunnel user login -g (GitHub) or devtunnel user login (Microsoft) manually, then retry. To switch accounts use cloud-share-sender --reset.
"Connection refused"
- Ensure the sender is still running
- Verify you copied the complete tunnel URL
- Check your internet connection
"File not found"
Make sure you specified the file path when starting the sender:
cloud-share-sender /path/to/your/file.pdf
๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ค Author
Garrard Kitchen
- GitHub: @garrardkitchen
- NuGet: garrardkitchen
๐ Acknowledgments
- Built with .NET 10
- CLI powered by Spectre.Console
- Uses Microsoft Dev Tunnels for secure connectivity
- Tested with xUnit and Moq
๐ Resources
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.