docs: extract goget manpage to standalone troff source

This commit is contained in:
2026-06-29 21:16:28 +02:00
parent 9ef62520c7
commit 9867e2817e
8 changed files with 1163 additions and 206 deletions
+7
View File
@@ -3,6 +3,13 @@
# Build output
bin/
# Generated copy of man/goget.1 (regenerated by `just build`).
# Required because //go:embed cannot reference files outside the
# embedding package, so man/goget.1 is copied into the package
# tree at build time and embedded into the binary.
cmd/goget/man-page-data/*.1
!cmd/goget/man-page-data/.gitkeep
# Test artifacts
*.test
*.out
+28
View File
@@ -9,6 +9,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
**Documentation**
- **Standalone troff manpage** — `man/goget.1` is now the single source
of truth for the goget(1) manual. The previous embedded manpage listed
only ~30 of the 138 CLI flags and was missing every standard section;
the new file documents every flag with a dedicated entry, the
full set of conventional `.SH` sections (NAME, SYNOPSIS, DESCRIPTION,
OPTIONS, EXAMPLES, EXIT STATUS, FILES, ENVIRONMENT, SEE ALSO, HISTORY,
AUTHORS, REPORTING BUGS, COPYRIGHT), curl/wget compatibility notes, and
a release-history entry. The manpage is embedded into the binary via
`//go:embed`, so `--generate-man-page` and `man goget` now agree.
- **`just man`** — new task that runs `groff -man -ww -Kutf-8 -z` over
`man/goget.1` to validate the troff source. Skips cleanly when `groff`
is not installed (CI on minimal images still passes).
- **`just install-man`** and **`just uninstall-man`** — install the
manpage to `$HOME/.local/share/man/man1/goget.1.gz` (XDG rootless,
gzipped, reinstall-safe). After `just install-man`, `man goget`
resolves the page automatically via the user's `MANPATH`.
**Core engine**
- **Unified worker pool** — single protocol-agnostic worker pool (`internal/pool`)
@@ -53,6 +72,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
output when combined with `--json`. Credentials embedded in the stored
URL are masked before display (the password placeholder is `xxxxx`).
### Testing
- `cmd/goget/man_page_test.go` — new test file. Asserts the embedded
manpage is non-empty, carries a `.TH GOGET 1` header, contains every
mandatory `.SH` section, and documents every flag registered by
`defineFlags` (so adding a new flag without documenting it now breaks
the build). When `groff` is installed, also runs `groff -man -ww
-Kutf-8 -z` over the embedded source and fails on any warning.
## [1.0.0] — 2026-06-26
First public release: a modern IPv6-first command-line download utility built on
+5
View File
@@ -271,13 +271,18 @@ just install # go mod download
just run # run from source (development)
just build # go vet + gofmt check + build → bin/goget
just test # go test -race -count=1 ./...
just man # validate man/goget.1 with groff
just install-man # install manpage to ~/.local/share/man/man1/
just uninstall # remove build artifacts
just uninstall-man# remove installed manpage
```
---
## Documentation
- [`man/goget.1`](man/goget.1) — troff manual page (`man goget` after
`just install-man`)
- [`docs/architecture.md`](docs/architecture.md) — components and request flow
- [`docs/configuration.md`](docs/configuration.md) — config schema (TOML)
- [`docs/api.md`](docs/api.md) — public Go library API
+3
View File
@@ -0,0 +1,3 @@
Placeholder so the directory is tracked by git. The actual file
generated here by `just build` is `goget.1`, copied from
`../../../man/goget.1`.
+13 -206
View File
@@ -1,217 +1,24 @@
//go:build linux || freebsd
// +build linux freebsd
package main
import (
_ "embed"
"fmt"
"codeberg.org/petrbalvin/goget/internal/core"
)
// manPage holds the canonical goget(1) manual page in troff/groff format.
// The source of truth is ../../man/goget.1; a copy is kept here so it can
// be embedded into the binary at build time (//go:embed cannot reference
// files outside the embedding package). The copy is refreshed by the
// `cp` step in `just build`.
//
//go:embed man-page-data/goget.1
var manPage string
// handleGenManPage implements the --generate-man-page flag. It writes the
// embedded manpage to standard output and exits 0.
func handleGenManPage() int {
fmt.Printf(`.TH GOGET 1 "2026-06" "goget %s" "User Commands"
.SH NAME
goget \- modern IPv6-first command-line download utility
.SH SYNOPSIS
.B goget
\fB\-\-url\fR \fIURL\fR [\fIOPTIONS\fR]
.SH DESCRIPTION
goget is a modern replacement for curl and wget built on Go stdlib.
Features IPv6-first connections, parallel downloads, resume support,
compression, checksum verification, and recursive mirroring.
.SH OPTIONS
.SS "Target"
.TP
\fB\-\-url\fR=\fIURL\fR
URL to download (can be specified multiple times)
.SS "Basic Options"
.TP
\fB\-\-output\fR=\fIFILE\fR
Output filename (default: auto from URL)
.TP
\fB\-\-verbose\fR
Verbose output with progress bar (default: enabled)
.TP
\fB\-\-no-progress\fR
Hide progress bar (keep other output)
.TP
\fB\-\-quiet\fR
Suppress all output except errors
.TP
\fB\-\-debug\fR
Debug mode with detailed info
.TP
\fB\-\-resume\fR
Resume interrupted download
.TP
\fB\-\-restart\fR
Delete existing file and restart download
.TP
\fB\-\-overwrite\fR
Overwrite existing file
.TP
\fB\-\-no-decompress\fR
Disable automatic decompression
.TP
\fB\-\-parallel\fR=\fIN\fR
Parallel connections (0=auto, 1=sequential)
.TP
\fB\-\-json\fR
Output progress as JSON (for scripting)
.TP
\fB\-\-info\fR
Show file information without downloading
.TP
\fB\-\-benchmark\fR=\fIURL\fR
Benchmark download speed
.SS "Network"
.TP
\fB\-\-timeout\fR=\fIDURATION\fR
Connection timeout (0 = auto based on size)
.TP
\fB\-\-proxy\fR=\fIURL\fR
Proxy server URL (http://, https://, socks5://, socks5h://)
.TP
\fB\-\-dns-servers\fR=\fIIPs\fR
Custom DNS servers (comma-separated)
.TP
\fB\-\-max-retries\fR=\fIN\fR
Maximum number of retries on failure
.TP
\fB\-\-no-ipv4\fR
Disable IPv4 fallback
.TP
\fB\-\-no-redirect\fR
Disable following HTTP redirects
.TP
\fB\-\-show-headers\fR
Print HTTP response headers
.TP
\fB\-\-keep-timestamps\fR
Set file modification time from Last-Modified header
.TP
\fB\-\-content-disposition\fR
Use server-provided filename from Content-Disposition header
.SS "Authentication"
.TP
Credentials are loaded automatically from \fB~/.netrc\fR when no \fB\-\-username\fR is given.
.TP
\fB\-\-username\fR=\fIUSER\fR
Username for HTTP Basic/Digest Auth
.TP
\fB\-\-password\fR=\fIPASS\fR
Password for HTTP Basic/Digest Auth
.TP
\fB\-\-password-file\fR=\fIFILE\fR
Read password from file
.TP
\fB\-\-auth-type\fR=\fITYPE\fR
Auth type: basic, digest, auto
.TP
\fB\-\-cookie-jar\fR=\fIFILE\fR
File for loading/saving cookies
.TP
\fB\-\-cookie\fR=\fINAME=VALUE\fR
Set cookie from CLI (repeatable, curl-compatible)
.TP
\fB\-\-retry-all-errors\fR
Retry on any HTTP 4xx/5xx response
.TP
\fB\-\-ssl-key-log\fR=\fIFILE\fR
Log TLS session keys to file (SSLKEYLOGFILE for Wireshark)
.SS "Config"
.TP
\fB\-\-config\fR=\fIFILE\fR
Path to config file
.TP
\fB\-\-config-get\fR=\fIKEY\fR
Get a config value
.TP
\fB\-\-config-set\fR=\fIKEY\fR \fIVALUE\fR
Set a config value
.TP
\fB\-\-config-list\fR
List all config values
.TP
\fB\-\-config-unset\fR=\fIKEY\fR
Unset a config value
.TP
\fB\-\-init-config\fR
Generate default config file
.SS "Mirror & Recursive"
.TP
\fB\-\-mirror\fR
Mirror entire website (infinite depth)
.TP
\fB\-\-recursive\fR
Enable recursive downloading
.TP
\fB\-\-max-depth\fR=\fIN\fR
Maximum recursion depth
.SS "Scheduling & Hooks"
.TP
\fB\-\-schedule\fR=\fITIME\fR
Schedule download ("YYYY-MM-DD HH:MM" or cron "0 2 * * *")
.TP
\fB\-\-on-complete\fR=\fICMD\fR
Run shell command after successful download
.TP
\fB\-\-bind-interface\fR=\fIIFACE\fR
Bind to specific network interface
.SS "Other"
.TP
\fB\-\-batch-file\fR=\fIFILE\fR
Download multiple URLs from a file
.TP
\fB\-\-checksum-file\fR=\fIFILE\fR
Read expected checksum from SHA256SUMS file
.TP
\fB\-\-generate-man-page\fR
Generate this man page
.TP
\fB\-\-help\fR
Show help
.TP
\fB\-\-version\fR
Show version
.SH EXAMPLES
.TP
Download a file:
goget --url https://example.com/file.zip
.TP
Parallel download:
goget --url https://example.com/large.iso --parallel 4
.TP
Resume interrupted download:
goget --url https://example.com/file.zip --resume
.TP
Mirror website:
goget --url https://example.com --mirror --output ./mirror
.TP
Recursive download:
goget --url https://example.com --recursive --max-depth 2
.TP
JSON progress (for scripting):
goget --url https://example.com/file.zip --json --no-progress > progress.jsonl
.TP
Scheduled download at 2 AM:
goget --url https://example.com/daily.zip --schedule "0 2 * * *"
.TP
Run import script after download:
goget --url https://example.com/data.csv --on-complete "import.sh"
.SH FILES
.TP
~/.config/goget/config.json
Configuration file
.TP
<file>.goget.meta
Resume metadata file
.SH BUGS
Report bugs at https://codeberg.org/petrbalvin/goget/issues
.SH AUTHOR
Petr Balvin
.SH LICENSE
MIT License`, core.Version)
fmt.Print(manPage)
return 0
}
+134
View File
@@ -0,0 +1,134 @@
//go:build linux || freebsd
package main
import (
"flag"
"os/exec"
"sort"
"strings"
"testing"
"codeberg.org/petrbalvin/goget/internal/config"
)
// manPageRequiredSections lists the conventional manpage sections that
// goget(1) must include. Every section in this list must be present
// in the manpage source as a ".SH <NAME>" header. Adding a new section
// here forces the manpage author to document it.
var manPageRequiredSections = []string{
"NAME",
"SYNOPSIS",
"DESCRIPTION",
"OPTIONS",
"EXAMPLES",
"EXIT STATUS",
"FILES",
"ENVIRONMENT",
"SEE ALSO",
"HISTORY",
"AUTHORS",
"REPORTING BUGS",
"COPYRIGHT",
}
// TestManPageEmbedded is the simplest smoke test: the embed succeeded
// and the variable is not empty. Any first line beginning with `.`
// (either a `.\"` comment or a macro such as `.TH`) is accepted as
// proof the embedded blob is troff source.
func TestManPageEmbedded(t *testing.T) {
if manPage == "" {
t.Fatal("embedded manPage is empty — //go:embed failed at build time")
}
if !strings.HasPrefix(manPage, ".") {
t.Errorf("embedded manPage does not look like troff (got prefix %q)", firstLine(manPage))
}
}
// TestManPageHasTHHeader verifies the .TH macro is present with the
// expected program name, section, and version. The .TH line is the
// entry point that `man` uses to identify the page.
func TestManPageHasTHHeader(t *testing.T) {
if !strings.Contains(manPage, ".TH GOGET 1 ") {
t.Error("manpage is missing the .TH GOGET 1 header")
}
}
// TestManPageHasMandatorySections asserts that every section in
// manPageRequiredSections exists as a .SH heading. This keeps the page
// discoverable in `man goget` and ensures consistency with project
// documentation conventions.
//
// In troff, multi-word section names must be quoted to keep the
// spaces inside a single argument: `.SH "SEE ALSO"`. The matcher
// accepts both the bare form (`.SH SEE ALSO`) and the quoted form
// (`.SH "SEE ALSO"`), so authors can use either convention.
func TestManPageHasMandatorySections(t *testing.T) {
for _, sec := range manPageRequiredSections {
bare := "\n.SH " + sec + "\n"
quoted := "\n.SH \"" + sec + "\"\n"
if !strings.Contains(manPage, bare) && !strings.Contains(manPage, quoted) {
t.Errorf("manpage is missing required section: .SH %s (or .SH \"%s\")", sec, sec)
}
}
}
// TestManPageDocumentsEveryFlag is a guard against drift between the
// CLI surface (defineFlags) and the manpage. For every flag
// registered on the FlagSet, the manpage must contain the
// corresponding option entry. The expected on-page form escapes
// every hyphen in the flag name as `\-` (per the convention used
// throughout the manpage) and prefixes the entry with `\-\-`.
//
// Adding a new flag in defineFlags but forgetting the manpage (or
// vice versa) makes this test fail with a clear missing-flag list.
func TestManPageDocumentsEveryFlag(t *testing.T) {
cfg := config.DefaultConfig()
fs := newTestFlagSet("goget-test-man-page")
defineFlags(fs, cfg)
var missing []string
fs.VisitAll(func(f *flag.Flag) {
escaped := strings.ReplaceAll(f.Name, "-", `\-`)
needle := `\-\-` + escaped
if !strings.Contains(manPage, needle) {
missing = append(missing, f.Name)
}
})
if len(missing) > 0 {
sort.Strings(missing)
t.Errorf("manpage does not document %d flag(s):\n --%s",
len(missing), strings.Join(missing, "\n --"))
}
}
// TestManPageRendersWithGroff runs `groff -man -ww -Kutf-8 -z` on the
// embedded manpage and fails if it reports any warnings or errors.
// The `-z` flag suppresses output; `-ww` enables maximum warning
// verbosity; `-Kutf-8` accepts UTF-8 source (em-dash, etc.).
//
// If `groff` is not installed on the test machine the test is
// skipped — we still want it to pass on minimal CI images — but a
// CI image with `groff` available will catch regressions in the
// troff source.
func TestManPageRendersWithGroff(t *testing.T) {
if _, err := exec.LookPath("groff"); err != nil {
t.Skip("groff not installed; skipping manpage render check")
}
cmd := exec.Command("groff", "-man", "-ww", "-Kutf-8", "-z")
cmd.Stdin = strings.NewReader(manPage)
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("groff rejected the manpage: %v\n%s", err, out)
} else if len(out) > 0 {
t.Fatalf("groff produced unexpected output:\n%s", out)
}
}
// firstLine returns the first line of s, trimmed of the trailing
// newline. Used only for error messages.
func firstLine(s string) string {
if i := strings.IndexByte(s, '\n'); i >= 0 {
return s[:i]
}
return s
}
+23
View File
@@ -11,6 +11,10 @@ run:
# Build the binary
build:
# Keep the embedded manpage in sync with man/goget.1. //go:embed
# cannot reference files outside the embedding package, so we copy
# the source-of-truth manpage into the package tree before compiling.
cp man/goget.1 cmd/goget/man-page-data/goget.1
go vet ./...
gofmt -l $(find cmd internal pkg -name '*.go') | grep . && exit 1 || true
go build -ldflags "-s -w" -o bin/goget ./cmd/goget
@@ -22,3 +26,22 @@ test:
# Remove build artifacts
uninstall:
rm -rf bin/ coverage.out
# Validate the manpage with groff (zero warnings on success). Requires
# `groff` to be installed — skipped silently otherwise.
man:
@command -v groff >/dev/null || { echo "groff not installed, skipping"; exit 0; }
groff -man -ww -Kutf-8 -z man/goget.1
# Install the manpage to ~/.local/share/man/man1/ (XDG rootless).
# After install, `man goget` resolves it automatically. Re-run safely to
# pick up edits.
install-man: man
@mkdir -p "$HOME/.local/share/man/man1"
gzip -c -f man/goget.1 > "$HOME/.local/share/man/man1/goget.1.gz"
@echo "Installed: $HOME/.local/share/man/man1/goget.1.gz"
# Remove the manpage installed by `just install-man`.
uninstall-man:
rm -f "$HOME/.local/share/man/man1/goget.1.gz"
@echo "Removed: $HOME/.local/share/man/man1/goget.1.gz"
+950
View File
@@ -0,0 +1,950 @@
.\" Manpage for goget.
.\" Source of truth — kept in sync with cmd/goget/man-page-data/goget.1
.\" by the `cp` step in `just build`. The duplicate is required because
.\" //go:embed cannot reference files outside the embedding package.
.\"
.\" Conventions used in this file:
.\" .TP tag-paragraph (one option per entry)
.\" \fB...\fR bold (the flag itself)
.\" \fI...\fR italic (the argument placeholder)
.\" \- literal hyphen (escapes groff's dash-to-endash conversion)
.\" \\- alternative escape for the same purpose
.\"
.\" Validate with: groff -man -ww man/goget.1 > /dev/null
.\" Render with: groff -man -Tutf8 man/goget.1 | less
.\"
.TH GOGET 1 "2026-06-26" "goget 1.0.0" "User Commands"
.SH NAME
goget \- modern IPv6-first command-line download utility
.SH SYNOPSIS
.B goget
[\fIOPTION\fR]... \fIURL\fR...
.SH DESCRIPTION
.B goget
is a modern replacement for
.BR curl (1)
and
.BR wget (1)
built on the Go standard library.
It provides IPv6-first connections (with automatic IPv4 fallback), parallel
chunked downloads, resume support, checksum verification, recursive website
mirroring, and support for nine network protocols
.RB ( HTTP / HTTPS ", " FTP / FTPS ", " SFTP ", " WebDAV / WebDAVS ", "
.BR file:// ", " data: ", " Gopher ", and " Gemini ") \(em all in a
single static binary with zero runtime dependencies.
.PP
Safety features include TLS 1.2 minimum, certificate verification, SSRF
protection (private/internal addresses are blocked by default), SSH host-key
verification for SFTP, HSTS cache (RFC 6797), and certificate or host-key
pinning.
.PP
URLs are specified positionally after the flags; the
.B \-\-url
flag is an alias for the first positional URL and may be repeated to download
several files in one invocation.
.PP
Configuration is loaded from
.IR ~/.config/goget/config.toml
(overridable with
.BR \-\-config " or the " GOGET_CONFIG
environment variable) and merged on top of compiled-in defaults. Command-line
flags always take precedence over configuration values.
.SH OPTIONS
.SS "Target"
.TP
\fB\-\-url\fR=\fIURL\fR
URL to download. May be repeated for multiple URLs, or supplied as positional
arguments after the flags.
.SS "Output"
.TP
\fB\-\-output\fR=\fIFILE\fR
Output filename or directory. When omitted, the filename is derived from the
URL.
.TP
\fB\-\-restart\fR
Delete any existing output file and restart the download from byte zero.
.TP
\fB\-\-overwrite\fR
Overwrite an existing output file without using atomic rename.
.TP
\fB\-\-content\-disposition\fR
Use the filename from the server's
.B Content\-Disposition
header instead of the one derived from the URL.
.TP
\fB\-\-create\-dirs\fR
Create missing parent directories of
.BR \-\-output
(enabled by default; disable with
.B \-\-create\-dirs=false
for
.BR curl (1) " /" wget (1)
compatibility). Ignored for stdout output.
.TP
\fB\-\-keep\-timestamps\fR
Set the output file's modification time to the server's
.B Last\-Modified
value.
.TP
\fB\-\-no\-clobber\fR
Skip the download if the output file already exists.
.TP
\fB\-\-continue\fR
Auto-resume the download if a partial output file is present.
.TP
\fB\-\-timestamping\fR
Download only when the remote file is newer than the local copy.
.SS "Progress and output"
.TP
\fB\-\-verbose\fR
Verbose output (default: enabled when set in the config file).
.TP
\fB\-\-no\-progress\fR
Hide the progress bar; keep every other piece of output.
.TP
\fB\-\-progress\fR=\fISTYLE\fR
Progress bar style. One of
.B bar
(default) or
.BR dot .
.TP
\fB\-\-quiet\fR
Suppress all output except errors.
.TP
\fB\-\-json\fR
Emit progress and completion events as newline-delimited JSON on stdout.
Each event is a single JSON object with
.BR type " (" progress " or " complete "), " downloaded ", " total ", " speed ,
and (for the
.B progress
event) an
.B eta_ms
field when the total size and speed are known.
.TP
\fB\-\-debug\fR
Enable debug output with detailed connection and protocol information.
.TP
\fB\-\-show\-headers\fR
Print HTTP response headers during the download.
.TP
\fB\-\-write\-out\fR=\fIFORMAT\fR
Print metadata after the transfer, using a
.BR curl (1)\-compatible
format string. Supported directives:
.BR %\{http_code\} ", " %\{size_download\} ", " %\{time_total\} ", "
.BR %\{speed_download\} .
.TP
\fB\-\-trace\-time\fR
Print an HTTP timing breakdown after the download:
.BR DNS ", " TCP ", " TLS ", " TTFB ", and " Total .
.SS "Network"
.TP
\fB\-\-timeout\fR=\fIDURATION\fR
Overall connection timeout.
.B 0
selects a smart auto-calculation based on the file size.
.TP
\fB\-\-connect\-timeout\fR=\fIDURATION\fR
Timeout for the TCP (or SSH) connection establishment phase.
.TP
\fB\-\-max\-time\fR=\fIDURATION\fR
Maximum total transfer time; abort if exceeded.
.B 0
disables the limit.
.TP
\fB\-\-max\-retries\fR=\fIN\fR
Maximum number of retry attempts on failure.
.B 0
selects the built-in default.
.TP
\fB\-\-retry\-delay\fR=\fIDURATION\fR
Delay inserted between retry attempts.
.TP
\fB\-\-retry\-all\-errors\fR
Retry on any HTTP 4xx or 5xx response (in addition to network errors).
.TP
\fB\-\-retry\-on\-http\-error\fR=\fICODES\fR
Retry on the given HTTP status codes (comma-separated; e.g.
.BR 503,429 ).
.TP
\fB\-\-max\-filesize\fR=\fISIZE\fR
Abort the transfer if the remote file exceeds this size (e.g.
.BR 100MB ", " 1GB ).
.TP
\fB\-\-proxy\fR=\fIURL\fR
Proxy URL.
.B http://
and
.B https://
trigger HTTP CONNECT;
.B socks5://
uses local DNS,
.B socks5h://
uses remote DNS.
Optional
.B user:pass@
credentials are supported.
.TP
\fB\-\-no\-ipv4\fR
Disable IPv4 fallback; connect over IPv6 only.
.TP
\fB\-\-no\-redirect\fR
Do not follow HTTP 3xx redirects.
.TP
\fB\-\-dns\-servers\fR=\fIIPS\fR
Custom DNS servers, comma-separated (e.g.
.BR 1.1.1.1,8.8.8.8 ).
.TP
\fB\-\-bind\-interface\fR=\fIIFACE\fR
Bind outgoing connections to a specific network interface (Linux only).
.TP
\fB\-\-allow\-private\fR
Allow connections to private or internal IP addresses. By default these
are blocked as an SSRF safeguard.
.TP
\fB\-\-pinned\-cert\fR=\fISHA256\fR
Pin the server certificate by its leaf SHA-256 fingerprint. Applies to all
TLS-bearing protocols (HTTPS, FTPS, Gemini, WebDAVS); abort on mismatch.
.TP
\fB\-\-pinned\-host\-key\fR=\fIFINGERPRINT\fR
Pin the SSH host key (SFTP). Accepts
.BR SHA256:<base64>
(as emitted by
.BR "ssh\-keygen \-lf" )
or a raw lowercase-hex SHA-256 of the key wire format. Pinning takes
precedence over
.B \-\-ssh\-insecure
and any
.I known_hosts
entry.
.TP
\fB\-\-rate\-limit\fR=\fIRATE\fR
Maximum download speed (e.g.
.BR 1MB/s ", " 500KB/s ).
.B 0
disables the limit.
.TP
\fB\-\-max\-speed\fR=\fIRATE\fR
Alias for
.B \-\-rate\-limit
in the legacy "bytes per second" form.
.TP
\fB\-\-range\fR=\fIN\fB\-\fIN\fR
Download only a byte range (e.g.
.BR 0\-999 ).
.SS "HTTP and request shaping"
.TP
\fB\-\-header\fR=\fI"Key: Value"\fR
Custom HTTP header. May be repeated.
.TP
\fB\-\-insecure\fR
Skip TLS certificate verification. Use only for testing.
.TP
\fB\-\-cert\fR=\fIFILE\fR
Client certificate in PEM format (for mTLS).
.TP
\fB\-\-key\fR=\fIFILE\fR
Client private key in PEM format (for mTLS).
.TP
\fB\-\-ca\-certificate\fR=\fIFILE\fR
Path to a custom CA certificate bundle.
.TP
\fB\-\-ssl\-key\-log\fR=\fIFILE\fR
Log TLS session keys to
.I FILE
in the NSS Key Log format used by Wireshark
.RB ( SSLKEYLOGFILE ).
.TP
\fB\-\-data\fR=\fI"key=value"\fR
Send
.I key=value
as the request body. Implies HTTP POST.
.TP
\fB\-\-form\fR=\fI"field=value"\fR
Multipart form field; repeatable. A value of
.B field=@/path
attaches a file upload.
.TP
\fB\-\-post\-file\fR=\fIFILE\fR
POST the contents of
.I FILE
as the request body.
.TP
\fB\-\-referer\fR=\fIURL\fR
Set the
.B Referer
request header.
.TP
\fB\-\-compressed\fR
Send an
.B Accept\-Encoding
header so the server returns a compressed response. By default
.B goget
already handles compressed responses transparently; this flag is provided
for
.BR curl (1)
compatibility.
.TP
\fB\-\-spider\fR
Check whether the URL exists via a HEAD request; do not download the body.
.TP
\fB\-\-fail\fR
Exit with a non-zero status on HTTP 4xx or 5xx responses.
.TP
\fB\-\-no\-decompress\fR
Disable automatic decompression of compressed response bodies.
.SS "Recursive downloading and mirroring"
.TP
\fB\-\-recursive\fR
Follow links discovered in HTML pages.
.TP
\fB\-\-mirror\fR
Mirror an entire website. Shorthand for
.BR "\-\-recursive \-\-convert\-links \-\-page\-requisites" .
.TP
\fB\-\-max\-depth\fR=\fIN\fR
Maximum recursion depth.
.TP
\fB\-\-recursive\-parallel\fR=\fIN\fR
Number of concurrent file downloads in recursive mode across all protocols
(HTTP, WebDAV, FTP, SFTP).
.B 0
runs sequentially (default).
.TP
\fB\-\-follow\-external\fR
Follow links to domains other than the starting domain.
.TP
\fB\-\-span\-hosts\fR
Alias for
.B \-\-follow\-external
in the
.BR wget (1)
style.
.TP
\fB\-\-domains\fR=\fILIST\fR
Restrict recursive crawling to the listed comma-separated domains.
.TP
\fB\-\-accept\fR=\fIPATTERN\fR
Accept patterns (filename glob or MIME type), comma-separated. Only
matching resources are downloaded.
.TP
\fB\-\-reject\fR=\fIPATTERN\fR
Reject patterns (glob), comma-separated. Matching resources are skipped.
.TP
\fB\-\-exclude\-pattern\fR=\fIPATTERN\fR
Alias for
.B \-\-reject
used by the recursive crawler.
.TP
\fB\-\-no\-parent\fR
Do not ascend to parent directories during recursive download.
.TP
\fB\-\-page\-requisites\fR
Download CSS, JavaScript, and images required to render the HTML pages.
.TP
\fB\-\-convert\-links\fR
Rewrite links in downloaded HTML for local offline viewing.
.TP
\fB\-\-no\-convert\-links\fR
Skip HTML link rewriting (faster; output is not offline-ready).
.TP
\fB\-\-backup\-converted\fR
Keep a
.B .orig
backup of each file before link conversion.
.TP
\fB\-\-no\-mirror\-assets\fR
Do not download CSS, JavaScript, and images during a mirror.
.TP
\fB\-\-no\-robots\fR
Do not honour
.I robots.txt
during mirroring.
.TP
\fB\-\-wait\fR=\fIDURATION\fR
Delay between requests in recursive or mirror mode (e.g.
.BR 1s ", " 500ms ).
.TP
\fB\-\-random\-wait\fR
Randomize the wait time between 0.5x and 1.5x of
.BR \-\-wait .
.TP
\fB\-\-adjust\-extension\fR
Append a
.B .html
extension to text/html files that lack one.
.TP
\fB\-\-cut\-dirs\fR=\fIN\fR
Ignore the first
.I N
path components when constructing local filenames.
.TP
\fB\-\-protocol\-directories\fR
Create protocol-prefixed subdirectories
.RB ( .http/ ", " .ftp/ )
in recursive mode.
.TP
\fB\-\-dry\-run\fR
List the URLs that would be downloaded in recursive or mirror mode without
writing any files to disk.
.TP
\fB\-\-warc\-file\fR=\fIFILE\fR
Write a WARC (Web ARChive) record of the transfer to
.IR FILE .
.SS "Download management"
.TP
\fB\-\-batch\-file\fR=\fIFILE\fR
Read URLs from
.IR FILE ,
one per line. Lines beginning with
.B #
are treated as comments and skipped.
.TP
\fB\-\-input\-file\fR=\fIFILE\fR
Alias for
.BR \-\-batch\-file .
A value of
.B \-
reads from standard input.
.TP
\fB\-\-glob\fR
Expand
.B [1-3]
and
.B {a,b,c}
patterns in the URL before issuing requests.
.TP
\fB\-\-queue\fR
Add the URL to the persistent download queue instead of downloading it
immediately.
.TP
\fB\-\-queue\-list\fR
List all items in the download queue and exit.
.TP
\fB\-\-queue\-process\fR
Process (download) every pending item in the queue and exit.
.TP
\fB\-\-queue\-clear\fR
Remove completed items from the queue.
.TP
\fB\-\-queue\-file\fR=\fIPATH\fR
Use a custom queue file (default
.IR ~/.config/goget/queue.json ).
.TP
\fB\-\-queue\-priority\fR=\fIN\fR
Priority for the queued item (1\(en10; higher values are processed first).
Default: 5.
.TP
\fB\-\-schedule\fR=\fITIME\fR
Schedule the download. Accepts an absolute timestamp
.RB ( " YYYY\-MM\-DD HH:MM ")
or a cron expression in the standard
.B MIN HOUR DOM MON DOW
format (e.g.
.BR "0 2 * * *" ).
.TP
\fB\-\-on\-complete\fR=\fICMD\fR
Run
.I CMD
after a successful download. The following environment variables are
exported into the hook:
.RS
.TP
.B GOGET_OUTPUT
Absolute path of the downloaded file.
.TP
.B GOGET_SIZE
Downloaded size in bytes.
.TP
.B GOGET_URL
The (credential-stripped) source URL.
.RE
.IP
The command is split into tokens with shell-like quoting rules but is
executed directly via
.BR exec (3)
\(em no shell is invoked, so metacharacters in arguments are not
re-interpreted.
.SS "Authentication"
.TP
\fB\-\-username\fR=\fIUSER\fR
Username for HTTP Basic or Digest authentication. When omitted, credentials
are auto-loaded from
.I ~/.netrc
if a matching machine entry is present.
.TP
\fB\-\-password\fR=\fIPASS\fR
Password for HTTP Basic or Digest authentication. Prefer
.B \-\-password\-file
to avoid exposing the secret on the command line.
.TP
\fB\-\-password\-file\fR=\fIFILE\fR
Read the password from
.I FILE
(the first line, with the trailing newline stripped).
.TP
\fB\-\-auth\-type\fR=\fITYPE\fR
Authentication scheme:
.BR basic ", " digest ", or " auto
(default).
.TP
\fB\-\-cookie\-jar\fR=\fIFILE\fR
Load cookies from and save cookies to
.I FILE
in Netscape format.
.TP
\fB\-\-cookie\fR=\fI"name=value"\fR
Set a cookie on the request. May be repeated.
.TP
\fB\-\-no\-private\fR
Disable
.I .netrc
and other credential-file auto-loading for this invocation.
.TP
\fB\-\-oauth\-client\-id\fR=\fIID\fR
OAuth 2.0 client ID.
.TP
\fB\-\-oauth\-client\-secret\fR=\fISECRET\fR
OAuth 2.0 client secret.
.TP
\fB\-\-oauth\-token\-url\fR=\fIURL\fR
OAuth 2.0 token endpoint.
.TP
\fB\-\-oauth\-auth\-url\fR=\fIURL\fR
OAuth 2.0 authorization endpoint (used by the
.B authorization_code
grant).
.TP
\fB\-\-oauth\-redirect\-uri\fR=\fIURI\fR
OAuth 2.0 redirect URI.
.TP
\fB\-\-oauth\-scopes\fR=\fISCOPES\fR
OAuth 2.0 scopes, comma-separated.
.TP
\fB\-\-oauth\-grant\-type\fR=\fITYPE\fR
OAuth 2.0 grant type:
.BR client_credentials ", " authorization_code ", " password ", or "
.BR refresh_token .
.TP
\fB\-\-oauth\-access\-token\fR=\fITOKEN\fR
OAuth 2.0 access token supplied directly.
.TP
\fB\-\-oauth\-refresh\-token\fR=\fITOKEN\fR
OAuth 2.0 refresh token used to obtain a new access token.
.SS "Upload"
.TP
\fB\-\-upload\fR
Upload the file in
.B \-\-upload\-file
instead of downloading.
.TP
\fB\-\-upload\-method\fR=\fIMETHOD\fR
HTTP method for the upload:
.B PUT
(default) or
.BR POST .
.TP
\fB\-\-upload\-file\fR=\fIFILE\fR
Path to the file to upload.
.TP
\fB\-\-form\-data\fR=\fI"k=v"\fR
Additional multipart form data, comma-separated.
.TP
\fB\-\-file\-field\fR=\fINAME\fR
Form field name used for the file part. Default:
.BR file .
.SS "Verification"
.TP
\fB\-\-checksum\fR=\fIHASH\fR
Expected checksum, as a hex string.
.TP
\fB\-\-checksum\-algo\fR=\fIALG\fR
Checksum algorithm:
.BR sha256 ", " sha512 ", " blake2b ", " sha3\-256 ", " sha3\-512 ", or " md5 .
Default:
.BR sha256 .
.TP
\fB\-\-checksum\-file\fR=\fIFILE\fR
Read expected checksums from
.I FILE
in the standard
.B SHA256SUMS
format.
.SS "PGP / OpenPGP"
.TP
\fB\-\-pgp\-verify\fR
Verify a PGP detached or inline signature after download.
.TP
\fB\-\-pgp\-decrypt\fR
Decrypt a PGP-encrypted file after download.
.TP
\fB\-\-pgp\-sig\fR=\fIFILE\fR
Path to a detached signature file (e.g.
.BR .asc ", " .sig ).
.TP
\fB\-\-pgp\-key\fR=\fIFILE\fR
Path to a PGP key (public or private, depending on the operation).
.TP
\fB\-\-pgp\-passphrase\fR=\fIPASS\fR
Passphrase for the private key.
.TP
\fB\-\-pgp\-passphrase\-file\fR=\fIFILE\fR
Read the private-key passphrase from
.IR FILE .
.SS "Metalink"
.TP
\fB\-\-metalink\fR
Treat the URL as a Metalink file (multi-source download).
.TP
\fB\-\-metalink\-file\fR=\fIPATH\fR
Read a Metalink description from
.IR PATH .
Accepts
.B .meta4
and
.B .metalink
formats.
.SS "TLS and SSH"
.TP
\fB\-\-known\-hosts\fR=\fIFILE\fR
Use a custom
.I known_hosts
file for SFTP (default
.IR ~/.ssh/known_hosts ).
.TP
\fB\-\-ssh\-insecure\fR
Skip SSH host key verification for SFTP (accept any key). Mutually
incompatible with
.BR \-\-pinned\-host\-key .
.TP
\fB\-\-hsts\-file\fR=\fIFILE\fR
Use a custom HSTS cache file (default
.IR ~/.config/goget/hsts ).
.SS "Archive extraction"
.TP
\fB\-\-extract\fR
Automatically extract recognized archives (TAR, TAR.GZ, TAR.BZ2, ZIP)
after a successful download.
.TP
\fB\-\-extract\-dir\fR=\fIDIR\fR
Directory to extract into. Default: alongside the downloaded archive.
.TP
\fB\-\-strip\-components\fR=\fIN\fR
Strip the first
.I N
path components during archive extraction.
.SS "Configuration"
.TP
\fB\-\-config\fR=\fIFILE\fR
Path to the TOML configuration file.
.TP
\fB\-\-init\-config\fR
Write a default configuration file to
.I ~/.config/goget/config.toml
and exit.
.TP
\fB\-\-config\-get\fR=\fIKEY\fR
Print the value of a single configuration key (e.g.
.BR \-\-config\-get timeout )
and exit.
.TP
\fB\-\-config\-set\fR=\fIKEY\fR \fIVALUE\fR
Persist
.I VALUE
under
.I KEY
in the configuration file and exit.
.TP
\fB\-\-config\-list\fR
Print every configuration key and value and exit.
.TP
\fB\-\-config\-unset\fR=\fIKEY\fR
Remove
.I KEY
from the configuration file and exit.
.SS "Information and shell integration"
.TP
\fB\-\-info\fR=\fIURL\fR
Show file metadata (size, content type, server, last-modified) without
downloading the body.
.TP
\fB\-\-benchmark\fR=\fIURL\fR
Benchmark the download speed by transferring the first 10 MB and reporting
the average throughput.
.TP
\fB\-\-show\-metadata\fR=\fIPATH\fR
Display the contents of a
.B .goget.meta
sidecar for debugging interrupted or resumed downloads. Accepts either a
path to the downloaded file (the sidecar is located next to it) or a direct
path to the sidecar. Renders a human-readable summary by default; combines
with
.B \-\-json
for raw output. Credentials embedded in the stored URL are masked.
.TP
\fB\-\-generate\-man\-page\fR
Print this manual page in troff format to standard output and exit.
.TP
\fB\-\-completion\fR=\fISHELL\fR
Print a shell completion script for
.IR SHELL \(lqbbash\(rqq, \(rqqzsh\(rqq, or \(rqqfish\(rqq\(en to standard output and exit.
.TP
\fB\-\-help\fR
Print the short help summary and exit.
.TP
\fB\-\-version\fR
Print the program name and version and exit.
.SH EXAMPLES
.SS "Simple download"
.PP
Download a single file to the current directory:
.PP
.RS
.nf
goget \-\-url https://example.com/file.zip
.fi
.RE
.SS "Parallel chunks and resume"
.PP
Files larger than 100 MB are automatically split into parallel chunks. To
force four connections explicitly and resume an interrupted transfer:
.PP
.RS
.nf
goget \-\-url https://example.com/large.iso \-\-parallel 4
goget \-\-url https://example.com/file.zip \-\-resume
.fi
.RE
.SS "Verification"
.PP
Verify a download against a known checksum and PGP signature:
.PP
.RS
.nf
goget \-\-url https://example.com/release.tar.gz \\
\-\-checksum\-algo sha256 \-\-checksum 5b9d...e2 \\
\-\-pgp\-verify \-\-pgp\-sig release.tar.gz.asc \\
\-\-pgp\-key maintainer.asc
.fi
.RE
.SS "Recursive mirror"
.PP
Mirror a documentation site, convert links for offline use, and respect
.IR robots.txt :
.PP
.RS
.nf
goget \-\-url https://example.com/docs/ \\
\-\-mirror \-\-convert\-links \-\-output ./mirror
.fi
.RE
.SS "Scripting with JSON progress"
.PP
Stream machine-readable progress to a file for a downstream tool:
.PP
.RS
.nf
goget \-\-url https://example.com/data.csv \\
\-\-json \-\-no\-progress > progress.jsonl
.fi
.RE
.SS "Scheduled download with a post-hook"
.PP
Fetch a daily report at 02:00 and feed it into a processor:
.PP
.RS
.nf
goget \-\-url https://example.com/daily.zip \\
\-\-schedule "0 2 * * *" \\
\-\-on\-complete "import.sh"
.fi
.RE
.SS "Batch file"
.PP
Read URLs from a file (one per line; lines starting with
.B #
are comments):
.PP
.RS
.nf
goget \-\-batch\-file urls.txt
.fi
.RE
.SH EXIT STATUS
.TP
.B 0
Success.
.TP
.B 1
Generic failure (network error, protocol error, invalid input).
.TP
.B 2
Invalid command-line arguments or configuration.
.TP
.B 3
File I/O error (cannot write output, cannot read source).
.TP
.B 4
Authentication failure.
.TP
.B 5
TLS or certificate error (including pinning mismatch).
.TP
.B 6
Checksum or PGP verification failed.
.TP
.B 7
Operation cancelled (SIGINT, SIGTERM, context cancellation).
.TP
.B 8
Resource limit exceeded
.RB ( \-\-max\-filesize ", " \-\-max\-time ", etc.).
.PP
Only the generic
.B 0
and
.B 1
codes are guaranteed; the project reserves the other values and their
meaning is part of the stable interface.
.SH FILES
.TP
.I ~/.config/goget/config.toml
Default configuration file. Overridden by
.B \-\-config
or the
.B GOGET_CONFIG
environment variable.
.TP
.I ~/.config/goget/queue.json
Persistent download queue. Path can be overridden with
.BR \-\-queue\-file .
.TP
.I ~/.config/goget/hsts
HSTS cache (RFC 6797). Path can be overridden with
.BR \-\-hsts\-file .
.TP
.I ~/.ssh/known_hosts
Default SFTP host-key database. Path can be overridden with
.BR \-\-known\-hosts .
.TP
.I <output>.goget.meta
Resume metadata sidecar written next to a partial output file. Deleted on
successful completion; consulted by
.B \-\-resume
or
.BR \-\-continue .
.TP
.I ~/.netrc
Auto-loaded credentials. Disabled for the current invocation by
.BR \-\-no\-private .
.SH ENVIRONMENT
.TP
.B GOGET_CONFIG
Path to the configuration file. Takes precedence over
.IR ~/.config/goget/config.toml
and
.BR \-\-config .
.TP
.B NO_COLOR
When set to a non-empty value, colour output is disabled regardless of the
.BR color " setting in the config file (see " https://no\-color.org )."
.TP
.B SSLKEYLOGFILE
Equivalent to passing
.B \-\-ssl\-key\-log
with the same value. Used by Wireshark to decrypt TLS traffic.
.TP
.B GOGET_OUTPUT
Exported into the environment of
.B \-\-on\-complete
hooks; contains the absolute path of the downloaded file.
.TP
.B GOGET_SIZE
Exported into
.B \-\-on\-complete
hooks; contains the downloaded size in bytes.
.TP
.B GOGET_URL
Exported into
.B \-\-on\-complete
hooks; contains the source URL with credentials stripped
.RB ( core.SafeURL ).
.TP
.B HOME
Used to locate
.I ~/.config/goget/
and
.IR ~/.ssh/known_hosts .
.TP
.B HTTP_PROXY
.IR "https_proxy" ", " ALL_PROXY ", " NO_PROXY
Standard proxy environment variables; respected unless
.B \-\-proxy
is given explicitly.
.SH "SEE ALSO"
.BR curl (1),
.BR wget (1),
.BR sftp (1),
.BR ssh\-keygen (1),
.BR groff (1),
.BR man (1)
.PP
Project home page and issue tracker:
.RI < https://codeberg.org/petrbalvin/goget >
.PP
Full documentation set:
.RS
.TP
.I docs/configuration.md
Configuration schema.
.TP
.I docs/cli-reference.md
Full flag reference.
.TP
.I docs/protocols.md
Protocol handler details.
.TP
.I docs/recursive-mirror.md
Recursive and mirror behaviour.
.TP
.I docs/security.md
Threat model and security controls.
.TP
.I docs/api.md
Public Go library API.
.RE
.SH HISTORY
goget 1.0.0 (2026-06-26) is the first public release.
The project was created as a modern, IPv6-first, Go-stdlib-only replacement
for
.BR curl (1)
and
.BR wget (1).
.SH AUTHORS
Petr Balvín
.RI < opensource@petrbalvin.org >
\(em upstream maintainer.
.PP
See
.RI < https://codeberg.org/petrbalvin/goget >
for the full contributor list.
.SH "REPORTING BUGS"
Report bugs to the issue tracker at
.RI < https://codeberg.org/petrbalvin/goget/issues >.
.PP
For security issues, do not open a public issue; email
.RI < opensource@petrbalvin.org >
instead.
.SH COPYRIGHT
Copyright \(co 2026 Petr Balvín.
.PP
Licensed under the MIT License.
Permission is granted to use, copy, modify, and distribute this software
without restriction, provided that the above copyright notice and this
permission notice appear in all copies.
.PP
.B goget
is provided
.B "AS IS"
without any warranty of any kind. See the MIT License text distributed with
the source for the full disclaimer.