RepoPilotOpen in app →

claudiodangelis/qrcp

:zap: Transfer files over wifi from your computer to your mobile device by scanning a QR code without leaving the terminal.

Healthy

Healthy across the board

weakest axis
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 5w ago
  • 22+ active contributors
  • MIT licensed
Show all 6 evidence items →
  • CI configured
  • Tests present
  • Concentrated ownership — top contributor handles 68% of recent commits

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/claudiodangelis/qrcp)](https://repopilot.app/r/claudiodangelis/qrcp)

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/claudiodangelis/qrcp on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: claudiodangelis/qrcp

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/claudiodangelis/qrcp 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 the board

  • Last commit 5w ago
  • 22+ active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Concentrated ownership — top contributor handles 68% of recent commits

<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 claudiodangelis/qrcp repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/claudiodangelis/qrcp.

What it runs against: a local clone of claudiodangelis/qrcp — 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 claudiodangelis/qrcp | 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 ≤ 64 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>claudiodangelis/qrcp</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of claudiodangelis/qrcp. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/claudiodangelis/qrcp.git
#   cd qrcp
#
# 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 claudiodangelis/qrcp and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "claudiodangelis/qrcp(\\.git)?\\b" \\
  && ok "origin remote is claudiodangelis/qrcp" \\
  || miss "origin remote is not claudiodangelis/qrcp (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 "cmd/qrcp.go" \\
  && ok "cmd/qrcp.go" \\
  || miss "missing critical file: cmd/qrcp.go"
test -f "application/application.go" \\
  && ok "application/application.go" \\
  || miss "missing critical file: application/application.go"
test -f "server/server.go" \\
  && ok "server/server.go" \\
  || miss "missing critical file: server/server.go"
test -f "config/config.go" \\
  && ok "config/config.go" \\
  || miss "missing critical file: config/config.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 64 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~34d)"
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/claudiodangelis/qrcp"
  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

qrcp is a Go CLI tool that transfers files over Wi-Fi between a computer and mobile device by encoding an HTTP endpoint in a QR code. The user scans the QR with their phone, and the file download (or upload page) begins automatically in the mobile browser without manual URL entry. It binds a temporary web server to the local network interface, generates a unique random path, and serves files or an upload handler. Monolithic single-binary layout: cmd/ contains CLI entry points (send.go, receive.go, config.go, completion.go), application/ houses the core transfer logic (application.go), server/ wraps the HTTP server and keep-alive logic (server.go, tcpkeepalivelistener.go), qr/ handles QR code generation (qr.go), config/ manages user settings and YAML serialization (config.go, migrate.go), and pages/ serves HTML templates for the upload UI (pages.go). The main.go ties it together into a single qrcp binary.

👥Who it's for

DevOps engineers, sysadmins, and power users who frequently move files between laptops and phones and want a frictionless, CLI-native workflow without cloud storage or email. Contributors are Go developers interested in small, focused networking tools.

🌱Maturity & risk

The project is actively maintained and production-ready. It has a stable release process (goreleaser configured in .goreleaser.yml), multi-platform distribution (Linux, macOS, Windows packages via AUR, Homebrew, Scoop, Chocolatey), and a clear DEVELOPMENT.md guide. The Go version requirement (1.21.0+) is modern, and CI/CD is set up in .github/workflows/main.yml, indicating ongoing active development.

Low risk: the dependency graph is lean (core dependencies are for QR generation via skip2/go-qrcode, CLI parsing via spf13/cobra, and config management via spf13/viper), all well-established packages. The single-maintainer model (claudiodangelis) is a potential concern, but the tool is simple enough that maintenance burden is low. No breaking changes evident in the recent structure.

Active areas of work

The project is actively maintained: Go version bumped to 1.24.1 in go.mod, Cobra CLI framework is current (v1.9.1), and viper config management is at v1.20.0. DEVELOPMENT.md and CONTRIBUTING.md are present, indicating the maintainer is organized and welcoming PRs. The .github/dependabot.yml is configured, showing automated dependency updates.

🚀Get running

Clone and build with Go 1.21+ installed:

git clone https://github.com/claudiodangelis/qrcp.git
cd qrcp
go install ./...

Or install the binary directly:

go install github.com/claudiodangelis/qrcp@latest

Verify:

qrcp --help

Daily commands: No dev server in the traditional sense. Build and run the binary directly:

go build -o qrcp ./cmd/qrcp.go
./qrcp send /path/to/file     # Send mode
./qrcp receive                 # Receive mode
./qrcp --help                  # Show all commands

For development with live reload, use go run ./cmd/qrcp.go send /path/to/file.

🗺️Map of the codebase

  • main.go — Entry point that initializes the application and orchestrates the CLI command execution; essential for understanding program startup flow.
  • cmd/qrcp.go — Root Cobra command definition that sets up the CLI framework and global flags; all subcommands branch from here.
  • application/application.go — Core Application struct and logic that manages file transfer lifecycle, server binding, and QR code generation.
  • server/server.go — HTTP server implementation handling both file distribution and upload endpoints; the network I/O backbone.
  • config/config.go — Configuration system managing user settings, persistence, and environment; all app behavior depends on config validation.
  • qr/qr.go — QR code generation wrapper around skip2/go-qrcode; essential for encoding transfer URLs.
  • util/net.go — Network utility functions for detecting local IP addresses and managing network interface binding; critical for cross-device connectivity.

🧩Components & responsibilities

  • CLI Entry Point (cmd/qrcp.go, cmd/send.go, cmd/receive.go) (Cobra, Viper) — Parses user input, validates flags, calls Application methods, and handles user feedback.
    • Failure mode: Exits with error on invalid flags or missing files; user sees error message.
  • Application (application/application.go) — Orchestrates entire transfer flow: det

🛠️How to make changes

Add a new send option/flag

  1. Define the flag in cmd/send.go using Cobra's Flags() method on the sendCmd command (cmd/send.go)
  2. Store the flag value in a local variable using viper.GetXXX() or cmd flags (cmd/send.go)
  3. Pass the option to Application.Send() method in application/application.go (application/application.go)
  4. Implement the logic in Application.Send() to use the new option (application/application.go)
  5. If server behavior changes, update server/server.go handler methods (server/server.go)

Add a new configuration option

  1. Define the field in the Config struct in config/config.go (config/config.go)
  2. Add YAML tag and default value to the struct definition (config/config.go)
  3. Update config/migrate.go to handle the new field in old config versions (config/migrate.go)
  4. Add a test case in config/config_test.go to verify parsing (config/config_test.go)
  5. Use the config value in cmd/ or application/ files where needed (application/application.go)

Add a new HTTP endpoint or page

  1. Design the HTML/response structure, add a template function in pages/pages.go (pages/pages.go)
  2. Define the request/response payloads in body/payload.go if needed (body/payload.go)
  3. Add a new HTTP handler method to the Server struct in server/server.go (server/server.go)
  4. Register the route in server.go's ListenAndServe or handler setup (server/server.go)
  5. Call the endpoint from Application.Send() or Application.Receive() as needed (application/application.go)

🔧Why these technologies

  • Cobra — Provides structured CLI framework with flag parsing, subcommands, and help generation; essential for professional CLI UX.
  • Viper — Handles configuration file parsing (YAML), environment variable binding, and defaults; decouples config from business logic.
  • skip2/go-qrcode — Lightweight QR code generation library; pure Go with no external dependencies simplifies distribution.
  • Go net/http — Standard library HTTP server avoids dependencies and provides TCP keep-alive control via custom listener for mobile reliability.
  • archivex — Provides ZIP archive creation when sending multiple files; simplifies client-side extraction.

⚖️Trade-offs already made

  • HTTP server vs HTTPS by default

    • Why: Simplicity and portability; HTTPS requires certificate management and may block QR scanning on mobile.
    • Consequence: Network transfers unencrypted on local Wi-Fi; users must manually enable TLS with mkcert if needed (see docs/tutorials/secure-transfers-with-mkcert.md).
  • Random port selection vs fixed port

    • Why: Avoids conflicts when multiple qrcp instances run; enables parallel transfers.
    • Consequence: QR code must be regenerated per session; no bookmarkable URLs.
  • File served directly vs chunked download

    • Why: Simpler implementation; browser handles streaming automatically.
    • Consequence: Large files may consume memory if buffered; no progress feedback to server (only client-side).
  • ZIP archive for multiple files vs directory listing

    • Why: Mobile browsers have limited folder download support; ZIP provides single-file convenience.
    • Consequence: Overhead of compression; no selective file download.

🚫Non-goals (don't propose these)

  • Persistent server or daemon mode; qrcp exits after each transfer
  • Authentication or encryption by default; operates on trusted local networks
  • Cloud storage integration or relay through public servers; air-gapped transfers only
  • Mobile app; relies on mobile browsers only
  • Cross-platform file sync; one-way transfer per invocation

🪤Traps & gotchas

TCP Keep-Alive Required: Mobile networks (LTE, 5G) aggressively close idle connections; tcpkeepalivelistener.go is essential and non-obvious. Random Path Routing: The server uses a random path component (e.g., /a1b2c3d/) to prevent accidental file serving to other clients; this is generated in application.go and hard to spot. XDG Config Paths: On Linux, config goes to ~/.config/qrcp/ per XDG standard (adrg/xdg), not ~/.qrcp; users unfamiliar with XDG will not find the config file. Embedded Pages: pages/pages.go contains hardcoded HTML/CSS; there is no separate static/ directory, so template edits require code changes. IP Detection: glendc/go-external-ip is used to detect public IP for WAN transfers; this requires an external call and may fail behind strict NANs—no fallback to mDNS is evident. No HTTPS by Default: Transfers are over plain HTTP; users must manually set up a reverse proxy or use mkcert (see docs/tutorials/secure-transfers-with-mkcert.md) for HTTPS.

🏗️Architecture

💡Concepts to learn

  • TCP Keep-Alive — Mobile networks (cellular, WiFi) terminate idle connections after 30–300 seconds; qrcp's custom tcpkeepalivelistener sets SO_KEEPALIVE socket options to prevent disconnects mid-transfer
  • QR Code Encoding — qrcp encodes an HTTP URL into a 2D barcode that mobile cameras can scan instantly, eliminating manual URL typing and making the tool frictionless for mobile users
  • Multipart Form Data — In receive mode, qrcp accepts file uploads via HTML form with multipart/form-data encoding; body/payload.go parses this to extract and save files
  • Random Path-Based Routing — Instead of a fixed /download endpoint, qrcp generates a random path (e.g., /abc123def/) for each session, allowing only the QR-scanning client to access the file while preventing enumeration attacks
  • XDG Base Directory Specification — qrcp uses adrg/xdg to store config in ~/.config/qrcp/ on Linux (and appropriate folders on macOS/Windows), following desktop standards that respect user preferences and container/sandboxing constraints
  • Cobra CLI Framework — qrcp uses Cobra subcommands (send, receive, config) with flags and help generation; understanding Cobra is necessary to add new commands or modify CLI behavior
  • Viper Configuration Management — qrcp uses Viper to abstract config from YAML files, environment variables, and CLI flags; config/config.go and config/migrate.go ensure backward compatibility and user overrides
  • schollz/croc — Alternative file-transfer CLI using PAKE and relay servers; solves similar cross-device file-sharing but with encryption and peer-to-peer focus rather than QR-based web serving
  • ipfs/go-ipfs — Distributed file sharing via IPFS; used for long-term or public file distribution, whereas qrcp is optimized for ad-hoc local network transfers
  • localsend/localsend — Cross-platform GUI app for local file sharing (similar use case); qrcp differs by being CLI-first and QR-code-driven rather than GUI-based discovery
  • spf13/cobra — The CLI framework qrcp uses; understanding Cobra command structure is essential for extending cmd/ subcommands
  • skip2/go-qrcode — The QR code generation library qrcp depends on; used in qr/qr.go to encode the transfer URL

🪄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 unit tests for server/server.go and server/util.go

The server package is critical to qrcp's core functionality (serving files over HTTP and handling file uploads), yet there are no test files in the server directory. This is a gap compared to config/config_test.go. Adding tests would improve reliability for file serving logic, error handling, and the TCP keepalive listener implementation.

  • [ ] Create server/server_test.go with tests for server initialization, handler registration, and port binding
  • [ ] Create server/tcpkeepalivelistener_test.go to test the custom TCP listener behavior
  • [ ] Add tests in server/util_test.go for any utility functions in server/util.go
  • [ ] Ensure tests cover both send and receive modes with mock HTTP clients
  • [ ] Run tests with go test ./server/... and add coverage reporting

Add integration tests in GitHub Actions workflow for cross-platform QR code generation and file transfer

The .github/workflows/main.yml currently lacks integration tests that validate the core qrcp functionality (QR generation, file serving, file upload) across Linux, macOS, and Windows. Given that qrcp is a user-facing CLI tool with platform-specific behavior, integration tests would catch regressions and platform-specific bugs early.

  • [ ] Create a new workflow file .github/workflows/integration-tests.yml or extend main.yml
  • [ ] Add test steps for Linux, macOS, and Windows runners that build qrcp and run functional tests
  • [ ] Implement basic integration test script in root or tests/ directory that: (1) starts qrcp send with a test file, (2) verifies QR code output, (3) simulates a download request
  • [ ] Test qrcp receive mode by uploading a test file to the served endpoint
  • [ ] Validate file integrity post-transfer using checksums

Add utility functions tests for util/net.go and util/util.go with comprehensive network/system edge cases

The util package contains critical network utilities (like IP detection via go-external-ip and likely network interface enumeration) that affect QR code generation and server binding. Currently there are no tests for util/net.go or util/util.go, making it risky to refactor or extend network detection logic without regressions.

  • [ ] Create util/net_test.go with tests for IP detection, network interface handling, and fallback scenarios
  • [ ] Create util/util_test.go for any standalone utility functions
  • [ ] Add test cases for edge cases: (1) no network interfaces available, (2) IPv6-only networks, (3) VPN/tunnel interfaces, (4) loopback-only fallback
  • [ ] Mock external IP lookup (go-external-ip dependency) to avoid flaky network-dependent tests
  • [ ] Validate that server binding uses correct resolved IP addresses in test scenarios

🌿Good first issues

  • Add unit tests to config/config_test.go: The config package has minimal test coverage. Write tests for migrate.go to verify backward-compatible config upgrades from older qrcp versions.
  • Document the TCP keep-alive behavior in DEVELOPMENT.md: The tcpkeepalivelistener.go is a critical but undocumented piece. Add a section explaining why keep-alive is needed on mobile networks and how to test it.
  • Add shell completion tests: cmd/completion.go generates bash/zsh/fish completion scripts, but there are no integration tests. Create a test that validates the generated completions actually work.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 3b17618 — docs(): remove link to tips platform (claudiodangelis)
  • 78605bf — docs(README): reorganize contents (#371) (claudiodangelis)
  • ec2ab28 — chore(go): run "go mod tidy" (claudiodangelis)
  • 5c158e3 — chore(deps): Bump github.com/spf13/viper from 1.19.0 to 1.20.0 (#370) (dependabot[bot])
  • 3e77f97 — fix(ci): strip version prefix from changelog (#366) (claudiodangelis)
  • 5688393 — chore(ci): add e2e tests (#369) (claudiodangelis)
  • 4ed5252 — chore(ci): add zip archive format for windows (#365) (claudiodangelis)
  • b977922 — chore(ci): update goreleaser (#364) (claudiodangelis)
  • 72c0654 — feat(ci): add arm64 releases for MacOS and Windows (#363) (claudiodangelis)
  • 696b2ac — feat(web): support file paste on the upload page (#344) (magik6k)

🔒Security observations

  • High · Outdated and Vulnerable Dependencies — go.mod, go.sum. Several dependencies have known security vulnerabilities and are significantly outdated. Notably: github.com/eiannone/keyboard (last updated 2020), github.com/jhoonb/archivex (unmaintained since 2018), gopkg.in/cheggaaa/pb.v1 (deprecated), and github.com/asaskevich/govalidator (last significant update 2020). These packages may contain unpatched security vulnerabilities. Fix: Audit and update all dependencies to their latest secure versions. Replace deprecated packages (gopkg.in/cheggaaa/pb.v1, gopkg.in/yaml.v2) with actively maintained alternatives. Perform a security audit using 'go list -json -m all' and 'nancy' or 'gosec' to identify known vulnerabilities.
  • High · Potential Path Traversal in File Transfer — server/server.go, body/payload.go, pages/pages.go. The application handles file uploads and downloads through web handlers. Without explicit validation visible in the file structure, there's a risk of path traversal attacks where users could upload/download files outside intended directories by using '../' sequences in filenames. Fix: Implement strict path validation in file handling. Use filepath.Clean() and verify that resolved paths remain within the intended directory. Sanitize all user-supplied filenames and reject any containing path traversal sequences. Use filepath.Base() to extract only the filename.
  • High · Lack of HTTPS Enforcement with Self-Signed Certificates — server/server.go, SECURITY.md, docs/tutorials/secure-transfers-with-mkcert.md. While the project mentions TLS/mkcert support in docs, there's no evidence of enforced HTTPS-only mode. File transfers over unencrypted HTTP expose sensitive data in transit on potentially untrusted Wi-Fi networks, which is the exact threat model this tool operates in. Fix: Implement mandatory HTTPS for all transfers. Provide easy setup for self-signed certificates (default behavior). Add warnings if running without TLS. Implement HSTS headers. Make certificate generation part of the default setup flow, not an optional tutorial.
  • Medium · Weak Random Path Generation Security — qr/qr.go, server/server.go, application/application.go. The application uses a 'random_path' in QR codes as the sole access control mechanism. If the random path generation is predictable or insufficiently random, attackers on the same network could intercept files without scanning the QR code. Fix: Ensure cryptographically secure random generation using crypto/rand instead of math/rand. Use sufficient entropy (minimum 32 bytes for path segment). Consider implementing additional authentication mechanisms like token validation or time-based expiration for the download/upload path.
  • Medium · Missing CORS and Security Headers — server/server.go. No evidence of CORS policies, X-Frame-Options, X-Content-Type-Options, Content-Security-Policy, or other defensive HTTP headers being set. This could allow various web-based attacks on clients accessing the transfer endpoint. Fix: Add comprehensive security headers to all HTTP responses: X-Frame-Options: DENY, X-Content-Type-Options: nosniff, X-XSS-Protection, Strict-Transport-Security (if HTTPS), and Content-Security-Policy. Implement proper CORS configuration to restrict cross-origin requests.
  • Medium · Unvalidated File Upload Size — server/server.go, body/payload.go. No evidence of file upload size limits in the visible codebase. Attackers could potentially perform denial-of-service by uploading extremely large files, consuming disk space and server resources during the transfer. Fix: Implement and enforce configurable file size limits using http.MaxBytesReader(). Add validation for total upload size. Provide clear user feedback on size restrictions. Store temporary uploads in isolated directory with quota management.
  • Medium · Archive Extraction Without Validation — server/server.go, body/payload.go. The project uses github.com/jhoonb/archivex for archive handling. Archive extraction operations could be vulnerable to zip bombs or path traversal attacks if not properly validated during extraction. Fix: When extracting archives, validate the extracted file paths to ensure they don't escape

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 · claudiodangelis/qrcp — RepoPilot