Cordelia is a LocalSend-inspired file and message sharing tool for local networks. It is written in Go as a learning project focused on networking fundamentals. Every device runs the same binary, discovers peers automatically on the LAN, and exchanges messages over a simple HTTP API.
Current status: v1.0.0 -> device identity, LAN discovery, peer registry, text messaging, file transfer with progress and checksums, TLS with pinning, graceful shutdown, interactive peer picker, retry with backoff, and persistent config file. API frozen under /v1.
- Each instance generates a persistent identity (name and fingerprint) on first run and stores it under the OS config directory.
- Discovery uses UDP multicast on
239.255.77.77:47777. Instances announce themselves every 3 seconds and listen for announcements from others. - A peer registry keeps the list of recently seen devices. Entries expire after 10 seconds of silence and are swept every 3 seconds. The registry is protected by a mutex because it is accessed from multiple goroutines.
- The HTTP API runs on TCP
47777over TLS (self-signed cert,~/.config/cordelia/cert.pem) and exposes:GET /info-> returns the local identity as JSONGET /peers-> returns the current peer list withcert_fingerprintPOST /message-> receives a JSON message{from, text}POST /upload-> receives a file via multipart upload (filepart), streams to~/Downloads/cordeliaor./downloads
- Discovery announces the cert fingerprint for TLS pinning; clients verify the server cert matches the announced fingerprint
- Graceful shutdown on SIGINT/SIGTERM and sha256 checksums for file integrity (
X-Checksum-Sha256) - Persistent config at
~/.config/cordelia/config.json(port,out_dir,ttl), flags override file
All of it is implemented with the Go standard library only.
- Go 1.22 or newer (tested on Go 1.27)
- Linux, macOS, or Windows. The code is pure Go with
CGO_ENABLED=0, so it cross-compiles without extra toolchains. - For LAN discovery, UDP inbound on port
47777must be allowed. The HTTP API also needs TCP47777.
You do not need Go installed to use Cordelia. Download the prebuilt binary for your system.
- Go to the Releases page:
https://github.com/A7reus/Cordelia/releases - Download the file that matches your OS and CPU. Example names:
- Linux Intel/AMD:
cordelia-linux-amd64 - Linux ARM (Raspberry Pi):
cordelia-linux-arm64 - macOS Intel:
cordelia-darwin-amd64 - macOS Apple Silicon:
cordelia-darwin-arm64 - Windows Intel/AMD:
cordelia-windows-amd64.exe - Windows ARM:
cordelia-windows-arm64.exe
- Linux Intel/AMD:
- Optional but recommended: verify the download with
checksums.txtfrom the same release:sha256sum -c checksums.txt --ignore-missing
Linux and macOS:
chmod +x cordelia-linux-amd64
./cordelia-linux-amd64 version
./cordelia-linux-amd64You can rename it to cordelia and move it to a directory in your PATH for convenience:
mv cordelia-linux-amd64 cordelia
chmod +x cordelia
sudo mv cordelia /usr/local/bin/
cordelia versionOn macOS, the first run may show a Gatekeeper warning because the binary is not signed. Right-click the file and choose Open, or run xattr -d com.apple.quarantine cordelia if you trust it.
Windows:
Download cordelia-windows-amd64.exe. If your browser warns about an unsigned binary, keep it if you trust the source. Open PowerShell in the download folder:
.\cordelia-windows-amd64.exe version
.\cordelia-windows-amd64.exeYou can rename it to cordelia.exe for shorter commands.
First run:
When you run cordelia with no arguments, it creates an identity and starts the server:
./cordelia
# 2026/08/27 21:39:08 serving API on :47777Leave this running. Open another terminal on the same machine or another device on the same WiFi/LAN to send.
Common commands (no Go needed):
./cordelia probe localhost:47777
./cordelia peers
./cordelia send-text 192.168.1.5:47777 "hello world"
./cordelia send-file 192.168.1.5:47777 ./photo.jpg
./cordelia send-file 192.168.1.5:47777 file1.txt file2.pdf
./cordelia version
# without host, picks from discovered peers interactively
./cordelia send-text "hello via picker"
./cordelia send-file ./photo.jpgFiles you receive are saved to ~/Downloads/cordelia if that folder exists, otherwise to ./downloads next to the binary. If a file with the same name already exists, it is saved as photo (1).jpg etc. To choose a different folder:
./cordelia -out /tmp/my-downloads
./cordelia -out /tmp/my-downloads -port 47778Transfers retry up to 3 times with backoff on transient failures and are verified with sha256. Ctrl+C now shuts down gracefully.
Other flags:
./cordelia -h
# -port int TCP API port (default 47777)
# -data-dir string config directory override (for testing)
# -out string download directory (default ~/Downloads/cordelia)If peers do not appear, allow Cordelia through your firewall. On Ubuntu:
sudo ufw allow in 47777/udp
sudo ufw allow in 47777/tcpOn Windows, allow it in Windows Defender Firewall when prompted.
To update, download the newer release and replace the binary.
-
Clone the repository:
git clone https://github.com/A7reus/Cordelia.git cd Cordelia -
Run directly:
go run ./cmd/cordelia
On first run, this creates an identity and starts the API server. Logs show the address being served.
-
Common commands while developing:
go vet ./... # static checks go run -race . # run with the race detector go run ./cmd/cordelia probe localhost:47777 go run ./cmd/cordelia peers go run ./cmd/cordelia send-text localhost:47777 "hello world" go run ./cmd/cordelia send-file localhost:47777 ./README.md go run ./cmd/cordelia send-file localhost:47777 file1.txt file2.txt # multiple files go run ./cmd/cordelia send-text "hello via picker" # picks peer interactively go run ./cmd/cordelia send-file ./photo.jpg # picks peer interactively go run ./cmd/cordelia version # prints the embedded version, defaults to "dev"
-
Running two instances on one machine (for testing discovery):
# terminal A go run ./cmd/cordelia # terminal B -> different port and different config directory go run ./cmd/cordelia -port 47778 -data-dir /tmp/cordelia-b # custom download directory go run ./cmd/cordelia -out /tmp/my-downloads
The second instance gets its own fingerprint and announces on its own TCP port. Both discover each other over multicast loopback. If you use UFW or another firewall, allow the ports first:
sudo ufw allow in 47777/udp sudo ufw allow in 47777/tcp
-
Formatting: the project uses
gofmt. Rungofmt -w .before committing.
go build -trimpath -ldflags "-s -w -X main.version=v1.0.0" -o cordelia ./cmd/cordelia
./cordelia version
./cordeliaThe -ldflags flag embeds the release version into the binary. Without it, version reports dev. -trimpath and -s -w make the build reproducible and smaller.
Because the project has no cgo dependencies, cross-compilation is a single command per target:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w -X main.version=v1.0.0" -o dist/cordelia-linux-amd64 ./cmd/cordelia
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "-s -w -X main.version=v1.0.0" -o dist/cordelia-linux-arm64 ./cmd/cordelia
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags "-s -w -X main.version=v1.0.0" -o dist/cordelia-darwin-amd64 ./cmd/cordelia
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -ldflags "-s -w -X main.version=v1.0.0" -o dist/cordelia-darwin-arm64 ./cmd/cordelia
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags "-s -w -X main.version=v1.0.0" -o dist/cordelia-windows-amd64.exe ./cmd/cordelia
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -trimpath -ldflags "-s -w -X main.version=v1.0.0" -o dist/cordelia-windows-arm64.exe ./cmd/cordeliaReleases are automated. Pushing a tag matching v* triggers .github/workflows/release.yml, which builds all targets above and publishes them to the GitHub Releases page via gh release create. To cut a new release:
git tag v1.0.0
git push origin v1.0.0Artifacts are named cordelia-<os>-<arch> (with .exe on Windows) and a checksums.txt is attached.
.
├── cmd/
│ └── cordelia/ # main entry, flag parsing, wiring
├── internal/
│ ├── certs/ # self-signed cert generation and fingerprint
│ ├── client/ # probe, peers, send-text, send-file, progress, TLS pinning, retry
│ ├── config/ # persistent config file
│ ├── server/ # HTTP handlers, download dir, upload limits
│ ├── identity/ # fingerprint generation and persistence
│ ├── discovery/ # UDP multicast announce and listen (with cert fingerprint)
│ └── registry/ # peer registry with TTL and cert fingerprint
├── go.mod
└── README.md
- v0.1.0 -> identity, discovery, peer registry, text messaging
- v0.2.0 -> file transfer over multipart upload (
POST /upload,send-file) with streaming - v0.3.0 -> progress reporting, collision-safe naming, multi-file transfer, custom download dir (
--out) - v0.4.0 -> TLS with self-signed certificates, cert fingerprint in discovery, and pinning for
send-text/send-file - v0.5.0 -> graceful shutdown, sha256 checksums, interactive peer picker, and retry with backoff
- v1.0.0 (current) -> frozen API under
/v1, persistent config, docs (API.md,SECURITY.md,CHANGELOG.md1.1.0), andgo test -raceCI
MIT. See LICENSE.