All checks were successful
check / check (push) Successful in 1m40s
Add cache_max_bytes to config.example.yml and the README key settings list. TODO.md: move cache size management and eviction to Completed Steps, promote P1 blocked networks configuration into Next Step, and note in Status that the unbounded disk growth DoS vector is closed.
126 lines
5.8 KiB
Markdown
126 lines
5.8 KiB
Markdown
# Workflow
|
|
|
|
* branch (from `main`)
|
|
* do the work in Next Step
|
|
* move Next Step to the top of Completed Steps
|
|
* move the top item of Future Steps into Next Step
|
|
* commit (`TODO.md` changes in the same commit as the work)
|
|
* merge to `main` if the branch is not protected, otherwise open a PR
|
|
* push
|
|
|
|
# Status
|
|
|
|
pre-1.0. No git tags exist. Recent work extracted the internal/magic,
|
|
internal/allowlist, internal/httpfetcher, and internal/signature
|
|
packages. The gosec findings from the 2026-07-06 survey are resolved
|
|
and `make check` is green on main. The disk cache is now size-bounded
|
|
with LRU eviction (`cache_max_bytes`), closing the unbounded disk
|
|
growth DoS vector.
|
|
|
|
# Next Step
|
|
|
|
P1: implement blocked networks configuration to extend SSRF protection
|
|
|
|
# Completed Steps
|
|
|
|
- 2026-08-07 implement cache size management and eviction (closes
|
|
#51): new `cache_max_bytes` config key validated by the startup
|
|
framework (explicit values used exactly with no floor, `0` disables
|
|
the disk cache entirely, omitted defaults to max(75% of free space
|
|
on the filesystem containing `<state_dir>/cache/`, 500 MiB), logged
|
|
at startup); processed variants are now tracked in the database
|
|
(migration 002 adds `variant_content` and an LRU timestamp on
|
|
`source_content`) so total usage is two SUMs, never a directory scan
|
|
on the hot path; a background goroutine evicts globally
|
|
least-recently-used entries (variants and source blobs merged) to
|
|
the limit, woken by a periodic ticker and by write-pressure
|
|
notifications from stores; a source blob and ALL of its
|
|
`source_metadata` references are deleted in one transaction before
|
|
the file is unlinked, so multi-referenced blobs are never removed
|
|
while referenced and rows never point at deleted files; a startup
|
|
reconciliation pass adopts untracked variant files, drops rows for
|
|
missing files, removes unreachable source blobs, and sweeps stale
|
|
temp files
|
|
- 2026-08-07 validate configuration on startup, fail fast on bad
|
|
config (closes #52): a config value that is set but unparseable or
|
|
invalid aborts startup naming the key and value (defaults apply only
|
|
to omitted keys), unknown config keys abort startup, a malformed
|
|
config file aborts instead of being skipped, and `state_dir` is
|
|
verified creatable and writable before the listener binds
|
|
- 2026-08-07 manual test pass of the auth and encrypted URL flows
|
|
against a locally built and running `pixad` (built from `main` at
|
|
`6573b9d`, port 18099, local throwaway config); all six checks
|
|
passed, plus all nine tests in `scripts/manual-test.sh` (closes #49):
|
|
- [x] visit `/` and see the login form: HTTP 200, `Pixa - Login`
|
|
page with `name="key"` password form
|
|
- [x] wrong key shows an error: POST `/` with `key=wrong-key`
|
|
returned HTTP 200 login page containing "Invalid signing key"
|
|
- [x] correct signing key shows the generator form: POST `/`
|
|
returned HTTP 303 to `/` with
|
|
`Set-Cookie: pixa_session=...; HttpOnly; Secure; SameSite=Strict`;
|
|
GET `/` with that cookie rendered `Pixa - URL Generator` with the
|
|
`/generate` form and logout link
|
|
- [x] a generated encrypted URL serves the image: POST `/generate`
|
|
(ttl=3600) produced a `/v1/e/<token>/img.jpeg` URL that returned
|
|
HTTP 200, `Content-Type: image/jpeg`, an 800x600 baseline JPEG of
|
|
61706 bytes
|
|
- [x] an expired URL (short TTL) returns 410: a ttl=1 URL fetched
|
|
after 3 s returned HTTP 410 Gone with
|
|
`{"error":"URL has expired","status":410,...}`
|
|
- [x] logout redirects back to login: GET `/logout` returned HTTP
|
|
303 to `/` with `Set-Cookie: pixa_session=; Max-Age=0`;
|
|
subsequent GET `/` rendered the login form again
|
|
- 2026-08-07 fix the two remaining gosec findings (G124 in
|
|
internal/session): session cookies now always carry
|
|
Secure/HttpOnly/SameSite=Strict on both the set and clear paths;
|
|
`make check` green (closes #47)
|
|
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints,
|
|
Makefile shims, README Entrypoints section
|
|
- 2026-04-07 extract magic byte detection into internal/magic (#42)
|
|
- 2026-03-25 extract allowlist package from internal/imgcache (#41)
|
|
- 2026-03-25 move schema_migrations table creation into 000.sql (#36)
|
|
- 2026-03-20 enforce and document exact-match-only signature
|
|
verification (#40)
|
|
- 2026-03-20 bound imageprocessor.Process input read to prevent
|
|
unbounded memory use (#37); consolidate appname into an
|
|
internal/globals constant (#34)
|
|
- 2026-03-18 parse version prefix from migration filenames (#33)
|
|
- 2026-03-15 QA audit fixes for 1.0/MVP readiness (#25)
|
|
- 2026-03-02 split Dockerfile with pre-built golangci-lint stage for
|
|
faster CI (#23)
|
|
- 2026-02-25 repo policy compliance: CI workflow, hash-pinned images,
|
|
golangci-lint and gosec fixes of that date (#14); arm64 Docker build
|
|
fix (#16)
|
|
- 2026-01-08 WebP and AVIF encoding support via govips (both former P0
|
|
image processing items, now done)
|
|
|
|
# Future Steps
|
|
|
|
- P1: rate limit global concurrent upstream fetches to prevent
|
|
resource exhaustion
|
|
- P1: strip EXIF and other metadata from processed images (privacy)
|
|
- P2: security
|
|
- referer blacklist
|
|
- per-IP rate limiting
|
|
- per-origin rate limiting
|
|
- P2: HTTP response handling
|
|
- Last-Modified headers
|
|
- Vary header for content negotiation
|
|
- X-Request-ID propagation
|
|
- P2: auto format selection (format=auto based on Accept header)
|
|
- P2: configuration
|
|
- add all configuration options from README
|
|
- environment variable overrides
|
|
- YAML config file support
|
|
- P2: operational
|
|
- optional Sentry error reporting
|
|
- comprehensive request logging
|
|
- Prometheus performance metrics
|
|
- integration tests for the image proxy flow
|
|
- load tests to verify the 1k to 5k req/s target
|
|
- P2: documentation
|
|
- configuration options
|
|
- API endpoints
|
|
- deployment guide
|
|
- example nginx or caddy reverse proxy config
|