# Go Vanity Import + Git Server Architecture This document describes a **simple, robust architecture** for hosting Go module source code and enabling `go get` on a **custom domain**, using: * A basic Git server (authoritative source) * Go vanity imports * Caddy for HTTPS with automatic TLS The design prioritizes: * Simplicity * Long-term correctness * Zero client-side configuration * Easy future migration to a module proxy if needed --- ## Goals * Host all authored Go modules under a single domain (e.g. `yourdomain.com/foo`) * Allow anyone to run: ```bash go get yourdomain.com/foo ``` with no environment variables or flags * Retain full control of source code and hosting * Avoid unnecessary complexity (S3 proxies, CI artifact pipelines) for a single author --- ## High-Level Architecture ``` yourdomain.com ├─ / → Vanity import discovery (?go-get=1) ├─ /.git → Git repository (HTTPS, read-only) └─ ssh://git@yourdomain.com/.git → Git writes (SSH) ``` Components: * **Caddy**: Fronts everything, provides HTTPS automatically * **Vanity Import Server**: Tiny Go HTTP server that serves `` tags * **Git Server**: Bare Git repositories served over HTTPS and SSH --- ## How `go get` Works in This Setup When a user runs: ```bash go get yourdomain.com/foo ``` Go performs the following steps: 1. **Vanity discovery** ``` GET https://yourdomain.com/foo?go-get=1 ``` 2. **Vanity server responds** with: ```html ``` 3. **Go clones the repository**: ``` git clone https://yourdomain.com/foo.git ``` 4. **Go reads `go.mod`**, checks tags (e.g. `v1.2.3`), and builds the module No Go module proxy is involved. --- ## Module Requirements Each module repository **must**: * Be located at `/srv/git/.git` * Have a `go.mod` file with: ```go module yourdomain.com/ ``` * Use semantic version tags: ``` v1.0.0 v1.2.3 ``` Tags must not be rewritten after publication. --- ## Vanity Import Server A single Go HTTP server can serve vanity imports for **all modules**. ### Responsibilities * Respond only to `?go-get=1` requests * Dynamically map paths to Git repositories * Serve static HTML with `` ### Behavior For a request to: ``` /foo?go-get=1 ``` The server returns: ```html ``` All non-`go-get` requests return `404` (or are handled elsewhere). --- ## Git Server ### Repository Layout ``` /srv/git/ ├─ foo.git ├─ bar.git └─ baz.git ``` Repositories are: * **Bare** * Read-only over HTTPS * Writable only via SSH ### Write Access * SSH only * Single `git` user * Access controlled via `authorized_keys` Example push: ```bash git push git@yourdomain.com:/srv/git/foo.git ``` --- ## Caddy Configuration Caddy is used as the front-facing server. ### Responsibilities * Automatic HTTPS (Let’s Encrypt) * Route `.git` paths to `git-http-backend` * Route all other paths to the vanity import server ### Conceptual Routing * `/*.git*` → Git HTTP backend * `/*` → Vanity import server This keeps TLS, routing, and process management simple and centralized. --- ## Why This Design Is “Bullet-Proof Simple” ### Advantages * No module proxy implementation required * No S3, CloudFront, or artifact format rules * No CI publishing pipeline needed * Fully compatible with Go tooling * Easy to debug (if `git clone` works, `go get` works) * Mirrors Go’s original and still-supported design ### Trade-offs * Git-based fetching is slower than a proxy * No immutable release guarantees beyond Git tags * Less suitable for very high traffic For a single-author, self-hosted setup, these trade-offs are acceptable. --- ## Future Migration Path (Optional) If needed later, this setup can evolve into a module proxy: * Keep vanity import paths unchanged * Switch `` from `git` → `mod` * Introduce an S3-backed module proxy * Users do not need to change imports or commands This design does not lock you into Git forever. --- ## Summary This architecture provides: * A single, authoritative domain for all Go modules * Simple Git-based source hosting * Zero-config `go get` for users * Minimal operational complexity * A clean upgrade path to a full module proxy It is the simplest solution that is still fully correct and future-proof for Go modules.