RepoPilotOpen in app →

dutchcoders/transfer.sh

Easy and fast file sharing from the command-line.

Healthy

Healthy across all four use cases

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 6mo ago
  • 33+ active contributors
  • Distributed ownership (top contributor 38% of recent commits)
Show 4 more →
  • MIT licensed
  • CI configured
  • Tests present
  • Slowing — last commit 6mo ago

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/dutchcoders/transfer.sh)](https://repopilot.app/r/dutchcoders/transfer.sh)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/dutchcoders/transfer.sh on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: dutchcoders/transfer.sh

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/dutchcoders/transfer.sh shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 6mo ago
  • 33+ active contributors
  • Distributed ownership (top contributor 38% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Slowing — last commit 6mo ago

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live dutchcoders/transfer.sh repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/dutchcoders/transfer.sh.

What it runs against: a local clone of dutchcoders/transfer.sh — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in dutchcoders/transfer.sh | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 201 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>dutchcoders/transfer.sh</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of dutchcoders/transfer.sh. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/dutchcoders/transfer.sh.git
#   cd transfer.sh
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of dutchcoders/transfer.sh and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "dutchcoders/transfer.sh(\\.git)?\\b" \\
  && ok "origin remote is dutchcoders/transfer.sh" \\
  || miss "origin remote is not dutchcoders/transfer.sh (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "main.go" \\
  && ok "main.go" \\
  || miss "missing critical file: main.go"
test -f "server/server.go" \\
  && ok "server/server.go" \\
  || miss "missing critical file: server/server.go"
test -f "server/handlers.go" \\
  && ok "server/handlers.go" \\
  || miss "missing critical file: server/handlers.go"
test -f "server/storage/common.go" \\
  && ok "server/storage/common.go" \\
  || miss "missing critical file: server/storage/common.go"
test -f "server/token.go" \\
  && ok "server/token.go" \\
  || miss "missing critical file: server/token.go"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 201 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~171d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/dutchcoders/transfer.sh"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

transfer.sh is a Go-based file sharing server that enables users to upload, download, and manage temporary file shares from the command line via HTTP(S). It supports multiple storage backends (S3, Google Drive, Storj, local filesystem), server-side AES-256 encryption, VirusTotal scanning, and configurable expiration/download limits—all exposed through a simple REST API and web UI. Simple monolithic structure: main.go is the entry point, cmd/cmd.go handles CLI logic, server/ contains the HTTP handler layer (handlers.go, server.go, token.go, utils.go), and server/storage/ abstracts storage backends (local.go, s3.go, gdrive.go, storj.go) behind a common interface (common.go). Security layers (virustotal.go, clamav.go, ip_filter.go) are mixed into the server package.

👥Who it's for

Systems administrators and DevOps engineers who need to self-host a private file-sharing service, and developers/power users who prefer command-line workflows (using curl) over web UIs for rapid file distribution in CI/CD pipelines or secure internal networks.

🌱Maturity & risk

Production-ready and actively maintained. The project uses Go 1.22, has comprehensive CI/CD (GitHub Actions workflows for build, test, release), includes unit tests (server/handlers_test.go, server/token_test.go), Docker support, and releases are versioned. Recent maintenance is evident from the VirusTotal integration update and active dependency management.

Low risk overall, but note the small core maintainer team (two listed maintainers: @stefanbesten and @aspacca per README disclaimer), which poses long-term sustainability risk. Dependency footprint is moderate (~25 direct dependencies including AWS SDK v2, Google API client, ProtonMail crypto libraries). No obvious abandoned dependencies, though some like 'go-clamd' haven't been updated recently. The server-side encryption feature carries operational risk if deployed on untrusted infrastructure.

Active areas of work

Active maintenance with recent updates: VirusTotal integration was updated (using Aetherinox/go-virustotal fork), Docker image builds and releases are automated (GitHub Actions workflows in .github/workflows/), and the project supports Nix package management (flake.nix, flake.lock). No specific open issues or PRs visible in the provided data, suggesting either a small issue backlog or closed discussions.

🚀Get running

git clone https://github.com/dutchcoders/transfer.sh.git
cd transfer.sh
go mod download
go build -o transfer.sh
./transfer.sh

Daily commands:

# Development server (watches current directory, uses local storage by default)
go run main.go

# Or build then run
go build -o transfer.sh
./transfer.sh -h  # Shows available flags

# With Docker
docker build -t transfer.sh .
docker run -p 8080:8080 transfer.sh

Server listens on port 8080 by default. Upload via: curl --upload-file file.txt http://localhost:8080/file.txt

🗺️Map of the codebase

  • main.go — Entry point for the entire application; initializes CLI flags, server configuration, and storage backends
  • server/server.go — Core server orchestration; sets up HTTP handlers, middleware, and request routing for all file transfer operations
  • server/handlers.go — Main HTTP handler implementations for upload, download, and token-based access; the business logic heart of the service
  • server/storage/common.go — Abstract storage interface that all backends (S3, GDrive, Storj, local) must implement; architectural linchpin for provider flexibility
  • server/token.go — Token generation and validation logic; controls access control and deletion URLs for shared files
  • cmd/cmd.go — CLI command definitions and argument parsing; defines user-facing API for file uploads and server options

🧩Components & responsibilities

  • server/server.go (HTTP Server) (net/http, gorilla/mux) — Initialize listener, register routes, apply middleware, manage graceful shutdown
    • Failure mode: If listener fails to bind, server exits; if routes misconfigured, requests 404; crashed handler goroutines leak connections
  • server/handlers.go (Request Logic) (multipart/form-data parsing, io streams) — Parse multipart uploads, call storage backend, validate tokens, generate responses
    • Failure mode: Storage backend errors (e.g., S3 timeout) return 500; oversized files may exhaust memory; malformed uploads cause parser errors
  • server/storage/ (Backend Abstraction)* (S3 SDK, Google API, Storj API, os/ioutil) — Abstract file I/O layer; each backend implements Put/Get/Delete for different storage targets
    • Failure mode: Backend unavailability causes request failures; S3 credentials expiration silently fails; local filesystem permissions deny writes
  • server/token.go (Access Control) — Generate secure deletion tokens and

🛠️How to make changes

Add a new storage backend

  1. Create a new file server/storage/mynewprovider.go implementing the Storage interface from server/storage/common.go (server/storage/mynewprovider.go)
  2. Implement required methods: Put(), Get(), Head(), Delete(), and IsNotExist() (server/storage/common.go)
  3. Add CLI flag registration in main.go to enable the new backend option (main.go)
  4. Add backend selection logic in main.go switch/case statement to instantiate your storage implementation (main.go)

Add a new HTTP endpoint

  1. Define the handler function in server/handlers.go matching the signature func (handler *Handler) MyNewEndpoint(w http.ResponseWriter, r *http.Request) (server/handlers.go)
  2. Register the route in server/server.go within the routes() method using router.HandleFunc() or router.Methods() (server/server.go)
  3. If the endpoint requires token validation, call token validation logic (see handlers.go for patterns) (server/handlers.go)
  4. Add tests in server/handlers_test.go covering success and failure cases (server/handlers_test.go)

Add IP-based access filtering

  1. Add CLI flags for whitelist/blacklist in main.go (e.g., -allowed-ips, -blocked-ips) (main.go)
  2. Pass the IP filter configuration to NewServer() in main.go (main.go)
  3. Extend server/ip_filter.go with new filter logic or conditions (server/ip_filter.go)
  4. Register the IP filter middleware in server/server.go using router.Use() (server/server.go)

🔧Why these technologies

  • Go 1.22.0 — Compiled language with fast startup, minimal memory footprint, and excellent concurrency primitives for handling multiple file transfers simultaneously
  • AWS SDK v2 (S3) — Official, well-maintained library for object storage; modular architecture reduces binary size vs v1
  • ClamAV + VirusTotal integration — Dual scanning options: local/offline (ClamAV) for privacy-conscious deployments and cloud-based (VirusTotal) for frequent signature updates
  • Pluggable storage backends — Enables deployment flexibility: local filesystem for single-server, S3 for cloud-native, Storj for decentralized, Google Drive for legacy integrations
  • Token-based access control — Stateless, URL-shareable authorization without server-side session storage; hash-protected deletion URLs prevent unauthorized file removal

⚖️Trade-offs already made

  • Stateless server design (no database)

    • Why: Simplifies deployment and scaling; no operational database overhead
    • Consequence: Limited ability to track file metadata, usage analytics, or enforce per-user quotas; deletion must rely on single-use tokens
  • Token expiration via configuration, not database

    • Why: Maintains stateless constraint
    • Consequence: Expired files must be cleaned up via background tasks; no per-file expiration override capability
  • Optional virus scanning (pluggable)

    • Why: Allows operators to choose security vs performance trade-off
    • Consequence: Deployments without scanning have no protection; operators must be aware and make explicit decisions
  • Multiple storage backends at compile time (single binary)

    • Why: Reduces operational complexity vs. separate microservices
    • Consequence: Binary includes code for unused backends; backend selection is compile-time+runtime configuration, not dynamic

🚫Non-goals (don't propose these)

  • User authentication and multi-tenant isolation (anyone with a link can download/delete with deletion token)
  • File versioning or history tracking
  • Search indexing or browsing of shared files
  • Server-side encryption (relies on storage backend encryption if needed)
  • Real-time file sync or continuous file availability monitoring
  • Web UI beyond basic HTML form (CLI-first design)

🪤Traps & gotchas

Environment variables: Storage backends (S3, Google Drive, Storj) require credentials passed via environment variables or CLI flags (see cmd/cmd.go for flag names like -aws-access-key, -gdrive-client-id); missing these silently defaults to local storage. ClamAV: If virusscan is enabled, a running clamav daemon must be accessible at the configured socket/host. Token encoding: File access tokens use a custom encoding scheme (server/token.go) that must be preserved across versions for backward compatibility with existing delete URLs. Rate limiting: VojtechVitek/ratelimit is configured per IP (server/utils.go) with hardcoded bucket size—adjust if serving high-volume users. Web assets: The UI is embedded at build time from dutchcoders/transfer.sh-web (a separate submodule); changes require rebuilding the binary.

🏗️Architecture

💡Concepts to learn

  • Token bucket rate limiting — VojtechVitek/ratelimit (server/utils.go) implements this to prevent abuse; understanding bucket capacity and refill rates is critical when configuring per-IP limits
  • Server-side AES-256 encryption — The X-Encrypt-Password header (golang.org/x/crypto) allows files to be encrypted at rest; this is a high-security feature that adds latency and should only be used on trusted servers
  • Storage backend abstraction pattern — server/storage/common.go defines a simple interface allowing multiple backends (S3, Google Drive, Storj, local) to be swapped at runtime; essential for understanding how to add new storage types
  • OpenPGP (RFC 4880) cryptography — ProtonMail/gopenpgp/v2 is integrated for PGP encryption support; users can gpg --encrypt files before upload for end-to-end security
  • Temporary URL signing — server/token.go generates time-bound, signed deletion URLs; similar to AWS S3 pre-signed URLs, this pattern allows secure cleanup without long-lived credentials
  • Gorilla mux routing — gorilla/mux (server/server.go) is the HTTP router; understanding route registration and middleware chaining is needed for adding new endpoints
  • Go embed and bindata-assetfs — elazarl/go-bindata-assetfs embeds the web UI as static assets into the binary; the build process converts HTML/JS into Go code, making deployment a single file
  • dutchcoders/transfer.sh-web — Official web UI submodule; embedded in the binary and serves the HTML/JS frontend alongside the REST API
  • nextcloud/nextcloud-server — Alternative self-hosted file sharing with richer collaboration features; competes in the same deployment niche
  • syncthing/syncthing — P2P file sync alternative; users often compare the two for different use cases (temporary share vs. continuous sync)
  • matrix-org/synapse — Often deployed alongside transfer.sh in privacy-focused organizations; complementary for chat + file exchange
  • aws/aws-sdk-go-v2 — Core dependency for S3 backend; understanding its client patterns (context, config) is essential for S3 storage modifications

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for server/storage handlers

The repo has multiple storage backend implementations (s3.go, gdrive.go, storj.go, local.go) but only handlers_test.go and token_test.go exist. Each storage provider needs isolated unit tests to verify upload/download/deletion logic and error handling. This is critical for a file-sharing service where data integrity is paramount.

  • [ ] Create server/storage/s3_test.go with tests for S3 client initialization, multipart upload, and error scenarios
  • [ ] Create server/storage/gdrive_test.go with mock Google API calls and authentication token handling
  • [ ] Create server/storage/local_test.go with filesystem operations (write, read, delete, permissions)
  • [ ] Create server/storage/storj_test.go with Storj-specific operations and connection pooling
  • [ ] Add table-driven tests for common patterns across all storage backends

Add integration tests in GitHub Actions workflow for storage backends

The .github/workflows/test.yml exists but likely only runs basic Go tests. No workflow tests storage backends against actual S3, Google Drive, or Storj services (or mocked versions). A new workflow should validate each backend independently, catching regressions in provider-specific code before release.

  • [ ] Create .github/workflows/storage-integration-tests.yml workflow file
  • [ ] Configure S3 integration tests using MinIO container for local S3-compatible testing
  • [ ] Add Google Drive mock tests using httptest package to simulate API responses
  • [ ] Add Storj integration tests using testnet or mock implementation
  • [ ] Ensure tests run on push to server/storage/* files and on release builds

Add security scanning and malware detection tests for ClamAV integration

server/clamav.go and server/virustotal.go exist but have no unit tests. The codebase handles user file uploads and integrates with antivirus engines—this is a security-critical feature. Tests should verify scanner initialization, file scanning workflows, and quarantine logic without requiring live ClamAV/VirusTotal services.

  • [ ] Create server/clamav_test.go with mocked ClamAV daemon responses for clean/infected/error scenarios
  • [ ] Create server/virustotal_test.go with mocked HTTP responses and API rate-limit handling
  • [ ] Add tests for scanner fallback behavior (e.g., VirusTotal fallback if ClamAV unavailable)
  • [ ] Add tests for infected file handling and cleanup in server/handlers.go context
  • [ ] Verify token-based authentication for VirusTotal API in isolated test cases

🌿Good first issues

  • Add comprehensive unit tests for server/storage/local.go similar to the structure in server/handlers_test.go; currently only handlers and token logic have test coverage.
  • Extend server/ip_filter.go with reverse-proxy header support (X-Forwarded-For, X-Real-IP) and add tests; the current implementation may fail behind load balancers.
  • Create integration tests in a new server/integration_test.go that exercise the full upload→scan→download flow with mocked S3/GDrive backends using testcontainers.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 6743a4c — Trim prefixed '/' from proxy path (#661) (GochoMugo)
  • f833cd4 — Fix: Change virustotal go dependency & endpoint (#657) (Aetherinox)
  • 4a95d2d — Bump github.com/cloudflare/circl from 1.3.7 to 1.6.1 (#650) (dependabot[bot])
  • c545d38 — .github/, Dockerfile: bump go version (#654) (stefanbenten)
  • db3f862 — Fix some errors in Readme.md and some performance optimization (#646) (ChengBuilder)
  • 01f9064 — Docker tag usage (#639) (ramonsmits)
  • 089ff32 — Check ci 20241026 (#632) (Andrea Spacca)
  • 7f043ca — Update README.md (Andrea Spacca)
  • 73b99b8 — Update README.md (#627) (Andrea Spacca)
  • 2a76d14 — Bump golang.org/x/net from 0.17.0 to 0.23.0 (#615) (dependabot[bot])

🔒Security observations

  • High · Outdated and Potentially Vulnerable Dependencies — go.mod - Multiple dependencies. Several dependencies have known vulnerabilities or are significantly outdated: golang.org/x/crypto v0.21.0 (should be v0.24.0+), golang.org/x/net v0.23.0 (should be v0.25.0+), and golang.org/x/text v0.14.0 (should be v0.15.0+). These packages contain security patches for cryptographic operations and network handling. Fix: Update all golang.org/x packages to their latest versions. Run 'go get -u golang.org/x/crypto@latest golang.org/x/net@latest golang.org/x/text@latest' and test thoroughly.
  • High · Unmaintained Dependency - ProtonMail/gopenpgp — go.mod - github.com/ProtonMail/gopenpgp/v2 v2.5.2. The ProtonMail/gopenpgp/v2 dependency v2.5.2 is from 2023 and the package appears to have limited recent maintenance. Given that this handles PGP encryption (critical security function), using unmaintained crypto libraries poses significant risk. Fix: Verify the maintenance status of this library. Consider alternative well-maintained PGP libraries or upgrade if patches are available. Monitor for security advisories.
  • High · Vulnerable VirusTotal Dependency - Untrusted Fork — go.mod - github.com/Aetherinox/go-virustotal v0.0.0-20250520084801-0eb8c8f901c8. The go-virustotal dependency uses github.com/Aetherinox/go-virustotal (a fork, not the official library) dated 2025. The use of a third-party fork of a security-critical library (malware scanning) increases supply chain risk and may not receive timely security updates from official sources. Fix: Switch to the official VirusTotal Go library if available, or establish a clear process for reviewing and vetting fork updates. Ensure the fork maintainer is trustworthy.
  • High · Weak Dependency - Ancient and Unmaintained Packages — go.mod - Multiple old dependencies (2014-2016 era). Several dependencies are from ~2014-2016 with no recent updates: PuerkitoBio/ghost, VojtechVitek/ratelimit, dutchcoders/go-clamd, skip2/go-qrcode. These packages may contain unpatched vulnerabilities and lack modern security practices. Fix: Audit each old dependency for known CVEs. Replace with actively maintained alternatives where possible. If critical functionality depends on these, consider forking and maintaining internally.
  • Medium · Missing Docker Security Hardening — Dockerfile. The Dockerfile uses 'scratch' as final image (good) but the build stage has potential issues: (1) No explicit security scanning of the binary, (2) Conditional user creation logic may fail silently if RUNAS is empty, (3) No health checks or resource limits defined. Fix: Add explicit user creation with error handling, include HEALTHCHECK instruction, set resource limits in docker-compose/k8s manifests. Implement image scanning in CI/CD pipeline.
  • Medium · Potential Path Traversal in File Storage Handlers — server/storage/ directory - especially local.go. The file structure includes storage handlers (server/storage/local.go, s3.go, gdrive.go) that handle user-supplied filenames. Without visible input validation, path traversal attacks could be possible when serving or storing files. Fix: Implement strict filename sanitization: reject paths containing '..' or absolute paths, use filepath.Base() to extract only filename, validate against whitelist of allowed characters.
  • Medium · XSS Risk in Web Interface — server/handlers.go and dutchcoders/transfer.sh-web dependency. The codebase includes bluemonday for HTML sanitization but also references dustchcoders/transfer.sh-web dependency. If user-supplied content (file names, metadata) is rendered in HTML without proper escaping, XSS vulnerabilities are possible. Fix: Ensure all user input (filenames, uploaded metadata) is HTML-escaped before rendering.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · dutchcoders/transfer.sh — RepoPilot