- Go 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
test / test (push) Successful in 40s
ci-images consolidated its per-stack images into a single debian-ci image with every toolchain pre-installed; debian-latest is its label. The old debian-trixie-<stack> labels are deprecated aliases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
| .forgejo/workflows | ||
| testdata/sigv4_vectors | ||
| .gitignore | ||
| client.go | ||
| client_test.go | ||
| errors.go | ||
| errors_test.go | ||
| go.mod | ||
| LICENSE | ||
| live_test.go | ||
| presign.go | ||
| presign_test.go | ||
| README.md | ||
| signer.go | ||
| signer_test.go | ||
| validate_test.go | ||
s3lite
Minimal, stdlib-only Go client for S3-compatible object storage.
Why this exists
The official AWS SDK works fine against non-AWS S3-compatible endpoints (MinIO, Hetzner Object Storage, R2, Backblaze, Spaces) but it drags in an AWS-flavoured configuration model and a non-trivial dependency tree. s3lite is the inverse: one package, no dependencies, a handful of operations. Suitable when you need basic object get/put/delete/list and nothing else.
Install
import "git.kristofferopsahl.com/kristofferopsahl/s3lite"
go get git.kristofferopsahl.com/kristofferopsahl/s3lite@latest
Quickstart
client, err := s3lite.New(s3lite.Config{
Endpoint: "https://hel1.your-objectstorage.com",
Region: "hel1",
AccessKey: os.Getenv("S3_ACCESS_KEY"),
SecretKey: os.Getenv("S3_SECRET_KEY"),
})
if err != nil { panic(err) }
body, hdr, err := client.GetObject(ctx, "my-bucket", "path/to/object.jpg")
if errors.Is(err, s3lite.ErrNotFound) {
// 404 handling
}
if err != nil { panic(err) }
defer body.Close()
io.Copy(os.Stdout, body)
Supported endpoints
Tested against:
- Hetzner Object Storage
By construction (path-style + SigV4) it should work against any S3-compatible service that supports path-style addressing — MinIO, Backblaze B2 (S3 API), Cloudflare R2 in path mode, DigitalOcean Spaces, Wasabi. Open an issue if it doesn't.
Scope
GetObject(ctx, bucket, key)returns the response body (caller closes) and headers.ErrNotFoundfor HTTP 404; other non-2xx responses return a*ResponseError.PutObject(ctx, bucket, key, body io.Reader, opts ...PutOption)uploadsbody. It signsX-Amz-Content-Sha256as the literalUNSIGNED-PAYLOADrather than hashing the payload up front, sobodystreams straight through instead of being buffered in memory first — this requires an HTTPS endpoint, since TLS (not SigV4) is what protects the bytes in transit.WithContentType(ct)andWithACL(acl)set the corresponding headers, both included in the signed-headers set.DeleteObject(ctx, bucket, key)removes an object. S3 answers 204 whether or not the key existed; some S3-compatible backends answer 200 or 404 instead — all three are treated as success, since an idempotent erasure re-run must not fail on a key it already removed.ListObjects(ctx, bucket, prefix, continuationToken)returns one page (≤ 1000 keys) of a bucket's keys underprefix, in lexical order, and the continuation token for the next page ("" once there is no next page).PresignGetObject(bucket, key, signAt, ttl)mints a query-string-signed GET URL (noAuthorizationheader, so it works from a browser or an<img>tag) that is byte-identical for the samesignAt+ttl— callers that need a stable, cacheable URL pass a fixedsignAtrather thantime.Now().
Not yet supported (open an issue if you need any of these): CopyObject, HeadObject, multipart uploads, virtual-hosted-style addressing, retries, server-side encryption.
SigV4
The library implements AWS Signature Version 4 in signer.go. It is exercised against AWS's published GET-only test vectors (in testdata/sigv4_vectors/). Path-style addressing only. The signer accepts any service name (the AWS-supplied vectors verify against the literal service string); Client always passes s3 in the credential scope. The region comes from Config.Region.
Tests
go test ./... # unit tests; stdlib + httptest only
S3LITE_TEST_*=... go test -tags=live ./... # opt-in real-endpoint test
License
MIT. See LICENSE.