Follows the schema fold: the variant_content table and last_accessed_at column are now part of 001_initial_schema.sql, not a separate migration.
125 lines
5.8 KiB
Markdown
125 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 (a
|
|
new `variant_content` table 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 and periodic 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
|