- cooler.SiteCachePurger moved from the Journal plugin: loopback-only http,
32-character token, 2 s detached request, no token in logs or errors
- NewSiteCachePurgerFor names caller keys (for plugin alias keys)
- PurgeSiteCacheAfterCommit through lagoon.AfterCommit, one POST per save
- SiteCacheFromApp for hooks outside serve; Install publishes the purger
unless a plugin already did, and fails boot on an invalid config
- caching docs section, cooler README, configuration row
- OpenBucket validates public_url (absolute http/https, host, no user info, query
or fragment; the error names the key) and records it next to public_path_prefix
- a set public_url logs one start-up line: the bucket behind it must hold only
public files, since it bypasses the is_public check
- attach.PublicURLBase; PublicURL, File.URL and thumbnail URLs use the base when
set and keep the public_path_prefix proxy URLs otherwise
- TestPublicURLSmoke; storage Public URL base section, attachments, configuration
and lagoon README
- Manifest.Tags from build.tags (^[a-z0-9_]+$, duplicates dropped), round-tripped by SaveManifest
- summer build passes -tags=<list> through goBuildArgs and prints the build tags line
- attach: s3blob imported only by s3_on.go (//go:build summercms_s3); an untagged binary
refuses an s3:// bucket_url naming the tag, build.tags and -tags
- go mod tidy adds the AWS SDK v2 modules as indirect requirements
- storage, modules, scaffolding, configuration and scaling docs plus the lagoon README
- Cache: Get, Set, Has, Forget, Remember (degrades on cache errors), Increment, Flush
with per-entry TTLs under <prefix>cache:
- memory driver on otter v2.3.0 bounded by cache.memory.max_entries; Redis driver
with SET PX, INCRBY and a Flush limited to SCAN MATCH <prefix>cache:* plus UNLINK
- Install publishes the Cache (sharing one Redis client with the rate limiter);
FromApp serves jobs and commands without a dial
- new docs/services/caching.md, configuration rows, cooler README
- new module cooler: Install, Handle, OpenRedis (no URL in errors), RateLimitStore
(atomic fixed-window Lua script with MemoryStore semantics, per-instance fallback)
- serve installs cooler and publishes the Redis surf.Store before BuildRouter;
BuildRouter looks up a published Store, route:list keeps the memory store
- config keys http.rate_limit.store and redis.url; go-redis v9.23.0
- cooler README, root table row, surf README and rate-limiting, configuration
and scaling docs
The route answered with the blob's stored Content-Type, which attach.Store
sniffs from the bytes, so a public text upload starting with <html> was
served as text/html on the application origin; nosniff does not help when
the declared type is already HTML. Every response now carries a sandboxing
Content-Security-Policy, and a successful response whose type is not a
raster image (JPEG, PNG, GIF, WebP, AVIF) gets Content-Disposition:
attachment, on GET and HEAD. Covers stay inline.
- one Info record "http request" per request with method, matched route
pattern, status and duration_ms; path without the query only when no
route matched
- the matched route records its pattern through a request-scoped entry,
so locale prefix rewrites do not hide it
- logs through the published *slog.Logger or slog.Default; off by default
- surf README, logging and configuration docs describe the key
- surf mounts GET {storage.uploads.public_path_prefix}/{path...} on
attach.StaticHandlerPublic when storage.uploads.serve is true
- bucket and database resolved per request; X-Content-Type-Options: nosniff
- surf README, storage and configuration docs describe the key
- setup: introduction, installation rewritten from install to serve,
configuration with the keys an application sets
- console: introduction, setup and maintenance, scaffolding, writing
commands, utilities; every command name is checker-verified
- bonfire ExampleCatalog shows arguments, bare and repeatable flags
- index links the section introductions; TestDocsRequiredPages lists
the seven new pages