diff --git a/.clinerules b/.clinerules new file mode 100644 index 0000000..1ab64ad --- /dev/null +++ b/.clinerules @@ -0,0 +1,97 @@ +# Cline rules for the "beam" project + +## Role + +Act as an expert Go backend/web developer. This is a small, public-facing +HTTP file-transfer service (upload/download via curl or a web UI). Prioritize +idiomatic, stdlib-first Go, correctness, and security — anonymous users can +upload arbitrary files, so treat all input as untrusted. + +## Project architecture facts + +- Single-file `main.go` using only the Go standard library (`net/http`, + `html/template`, etc.) — no web framework, router, or third-party + dependencies. Keep `go.mod` dependency-free unless the user explicitly + approves adding one. +- `index.html.tmpl` is the only frontend asset, rendered via `html/template` + with inline CSS/JS. Keep it self-contained — no external asset build step, + no CDN dependencies. +- Configuration is via environment variables only (`UPLOAD_DIR`, `PORT`, + `FILE_DURATION`, `DRYRUN_CLEAN`, `MAX_UPLOAD_SIZE`, etc.). Follow this + pattern for any new configuration knob, validate it at startup with + `log.Fatalf` on invalid values, log the effective value, and document it in + `README.md`. +- The background cleanup goroutine (`cleanupProcedure`) follows a + dry-run-by-default, structured-logging pattern. Any new periodic + background job should follow the same style: reset per-cycle state, + dry-run support, and clear log messages for what was/would be done. + +## Go version + +- The toolchain version is pinned in `go.mod` (currently `go 1.27`). Match + `go.mod` and the Dockerfile's `golang:X-alpine` builder image tag whenever + bumping — never let the two drift apart. Don't use stdlib APIs newer than + the pinned version. + +## Kubernetes / TLS ingress deployment implications + +This service runs in Kubernetes behind a TLS-terminating ingress. + +- Never rely on `r.TLS != nil` to detect HTTPS. Always honor + `X-Forwarded-Proto` / `X-Forwarded-Host` / `Forwarded` headers for any URL + generation, matching the existing `getProtocol`/`getHost` helpers. +- `/healthz` is used for k8s liveness/readiness probes: keep it fast and + cheap (e.g. `os.Stat` on the upload dir is fine). Do not add slow checks, + external calls, or heavy I/O to it. +- The process receives SIGTERM on pod termination followed by SIGKILL after + the grace period. Any new long-running goroutine or in-flight request + handling must respect context cancellation / `srv.Shutdown` and not block + graceful shutdown. +- Assume horizontal scaling with multiple replicas: do not add in-memory + state that must be shared/consistent across pods (e.g. in-memory rate + limiting, sessions, counters) without explicitly calling out that it is + per-pod-only and confirming that's acceptable. +- `UPLOAD_DIR` may be backed by a PersistentVolume. Don't assume it's + ephemeral, and don't assume it's shared across replicas (RWX) unless + confirmed — flag this tradeoff if a feature depends on it. +- The container runs as a non-root user (see Dockerfile `USER beam`). New + code must not require root-only operations (binding privileged ports, + chown, etc.). + +## Coding conventions + +- Always run `go build ./...`, `go vet ./...`, and `gofmt -l .` before + considering a change complete; fix any findings. +- Prefer the standard library over new dependencies (e.g. use `log/slog` if + structured logging is ever introduced, rather than a third-party logger). +- All path handling derived from user input (upload/download IDs, + filenames) must be validated with `filepath.IsLocal` and/or + `filepath.Clean`/`filepath.Base` before use — never trust raw path + segments from the URL or multipart form. +- All upload code paths must enforce `maxUploadSize` via + `http.MaxBytesReader`, and clean up any partially written file/directory + on failure. +- New HTTP handlers must call `logRequest` with an accurate status code for + every response path. +- Keep the JSON error shape for `/upload` consistent with + `writeMultipartError` (`{"error": "..."}`). + +## Git workflow + +- One logical change per commit. +- Commit subject is a single line, no body. +- Keep `.gitignore` up to date rather than leaving stray build/test + artifacts untracked in the working tree. + +## Testing + +- There are currently no `*_test.go` files. When adding or changing logic + that is easy to regress (path safety, ID generation, header parsing, + upload size limits), add table-driven tests using the standard `testing` + package and `net/http/httptest` where applicable. + +## Documentation + +- Keep `README.md` and the in-page instructions/labels in + `index.html.tmpl` (e.g. max upload size, usage examples) in sync with + actual runtime behavior.