diff --git a/README.md b/README.md new file mode 100644 index 0000000..4871701 --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +# repo-delivery-middleware + +HTTP middleware that serves encrypted GitLab repo bundles to customers. + +## Building + +```sh +nix build # Go binary -> ./result/bin/repo-delivery-middleware +nix build .#docker-image # streaming Docker image script -> ./result +``` + +## Running (binary) + +Directly from the flake: + +```sh +nix run . -- --config config.toml +nix run . -- --config config.toml --listen :9090 +``` + +Or after `nix build`: + +```sh +./result/bin/repo-delivery-middleware --config config.toml +``` + +| Flag | Default | Description | +|------------|---------|--------------------| +| `--config` | | Path to TOML config file | +| `--listen` | `:8080` | Address to listen on | + +The `CONFIG_PATH` environment variable can be used instead of `--config`. +The flag takes precedence if both are set. + +## Running (Docker) + +The flake produces a `streamLayeredImage` script rather than a tarball, so +building and loading is a two-step pipe: + +```sh +nix build .#docker-image +./result | docker load +``` + +This streams and loads the image as `repo-delivery-middleware:latest`. + +```sh +docker run --rm -p 8080:8080 \ + -e GITLAB_API_KEY=glpat-xxxx \ + -v /path/to/config.toml:/config.toml \ + repo-delivery-middleware:latest --config /config.toml +``` + +The image has no shell. The entrypoint is the binary; extra arguments are +passed directly as flags. + +## Configuration + +```toml +schema = "v1" + +[config] +gitlab_url = "https://gitlab.example.com" +# Supports environment variable expansion ($VAR or ${VAR}) +gitlab_api_key = "${GITLAB_API_KEY}" + +[customers.acme] +# AES-256 key: exactly 64 hex characters (32 bytes) +key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +repos = [ + "group/project-a", + "group/subgroup/project-b", +] +``` + +- `schema` must be `"v1"`. +- At least one customer is required. +- Each customer key must be exactly 64 hex characters. +- Each customer must list at least one repo. + +## API + +``` +GET /getPayload +X-API-Key: +``` + +Success response: + +``` +200 OK +Content-Type: application/octet-stream +Content-Disposition: attachment; filename="payload.bin" +``` + +Error codes: + +| Code | Meaning | +|------|---------| +| 401 | Missing or unknown `X-API-Key` header | +| 405 | HTTP method other than GET | +| 500 | Failed to build or encrypt payload | +| 502 | Failed to fetch repositories from GitLab | + +## Payload format + +The response body is a single binary blob: + +``` +nonce (12 bytes) || ciphertext || GCM tag (16 bytes) +``` + +- Encrypted with AES-256-GCM using the customer's key. +- Plaintext is a tar.gz archive containing one directory per repo, named + after the repo basename (GitLab's internal prefix is stripped). +- Repos are sorted alphabetically for deterministic output. + +## Development + +```sh +nix develop # shell with go and gopls +```