nfpm v2 does not expand ${VAR} in contents.src fields. The deb/rpm
targets now pipe nfpm.yaml through envsubst to resolve ARCH and
VERSION before passing the config to nfpm.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Containerfile referenced golang:1.23 but go.mod requires 1.26.1.
Verified end-to-end: image builds, health check works, all routes
respond, API login succeeds, version flag shows 0.1.0.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bugs fixed:
- Space key was hijacked in tag input when a suggestion was
highlighted, preventing users from typing spaces. Removed
Space as a selection key (Enter is sufficient per combobox
pattern).
- ArrowUp was clamped to index 0, making it impossible to
deselect all suggestions and return to free typing. Now
allows arrowing back to -1 which clears aria-activedescendant.
Cleanup:
- Remove dead inline onkeydown handlers from tag suggestion
<li> elements (tabindex="-1" means they never receive focus,
so the handlers never fire; the global keydown listener in
app.js handles keyboard navigation).
- Add outline to aria-selected="true" state for visual parity
with hover (keyboard users now see the same indicator).
- Announce "Ingen forslag" in live region when suggestions are
empty (screen readers previously got silence).
- Add responsive table wrapper to admin tags and admin requests
tables (was only on admin users).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tag autocomplete combobox pattern (WCAG 2.1.1, 4.1.2, 4.1.3):
- Add role="combobox", aria-expanded, aria-haspopup to tag input
- Implement arrow key navigation (up/down) through suggestions
- Add Space key support alongside Enter for selecting tags
- Manage aria-activedescendant to track highlighted option
- Add Escape to close suggestions
- Add aria-live="polite" status region announcing suggestion count
- Add aria-selected state on options
- Tag suggestions now have stable IDs for activedescendant
Focus visibility (WCAG 2.4.7):
- Remove outline:none on tag suggestions, replace with visible
2px solid outline on :focus-visible
Contrast (WCAG 1.4.3):
- Replace opacity:0.5 on disabled rows with muted text color
and strikethrough on username (maintains 4.5:1 ratio)
Structure and semantics (WCAG 1.3.1):
- Fix heading hierarchy H1→H3 skip in import.html (now H2)
- Replace <nav> misuse for fave actions with div[role="group"]
- Add aria-label="Administrasjonsmeny" to admin dashboard nav
- Wrap admin users table in responsive scrollable region
- Remove redundant "Bilde for:" prefix from image alt text
- Make error page H1 descriptive: "Feil 404: Ikke funnet"
- Add .sr-only utility class for screen-reader-only content
- Add hreflang="en" to English-language external link
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bugs fixed:
- Renderer.Error set WriteHeader before Content-Type, causing
the header to be silently dropped. Now sets Content-Type first.
- truncate template function operated on bytes, not runes — could
split multi-byte UTF-8 characters (Norwegian æøå). Now uses
[]rune for correct Unicode handling.
Performance:
- Skip session DB lookup (2 queries) on /static/ and /uploads/
requests — these never use user context.
UX consistency:
- Replace all http.NotFound and http.Error("Forbidden") in
handler layer with styled error pages via Renderer.Error.
- Add notFound/forbidden helper methods on Handler.
Deployment fixes:
- Remove false libc6/glibc deps from nfpm.yaml (binary is
statically linked with CGO_ENABLED=0).
- Add CGO_ENABLED=0 to Makefile build target for consistency.
- Add .dockerignore to exclude .git, dist/, data/ from build
context.
- Remove phantom 'lint' from Makefile .PHONY.
- Document ProtectSystem=strict constraint in systemd service.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers quick start (binary and container), all environment variables,
Caddy deployment examples (subdomain, subpath, remote proxy),
API usage with curl examples, complete route table, tech stack,
security features, and development instructions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Security fixes:
- Fix XSS in Atom feed: escape user-supplied URLs in HTML content
- Wrap signup request approval in a transaction to prevent
partial state on crash (user created but request still pending)
- Stop leaking internal error messages to admin UI
- Add request body size limit on API import endpoint
- Log SetMustResetPassword errors instead of silently discarding
Correctness fixes:
- Handle errors from API fave update/delete instead of returning
success on failure
- Use actual data timestamp for feed <updated> instead of
time.Now() (improves HTTP caching)
- Replace hardcoded 10000 export limit with named constant
(maxExportFaves = 100000)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 6 — JSON API:
- POST /api/v1/auth/login — returns session token
- POST /api/v1/auth/logout
- GET/POST /api/v1/faves — list own faves (paginated), create fave
- GET/PUT/DELETE /api/v1/faves/{id} — get, update, delete fave
- GET /api/v1/tags?q= — search tags
- GET /api/v1/users/{username} — public profile
- GET /api/v1/users/{username}/faves — public faves (paginated)
- GET /api/v1/export/json — export own faves
- POST /api/v1/import — import faves from JSON
All endpoints return JSON. Auth via session cookie (same as web UI).
Privacy-aware: private faves hidden from non-owners.
Respects profile visibility settings.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 5 — Feeds & Import/Export:
- Atom feeds: global (/feed.xml), per-user (/u/{name}/feed.xml),
per-tag (/tags/{name}/feed.xml). Uses gorilla/feeds.
- JSON export: all user's faves with tags, pretty-printed
- CSV export: standard format with header row
- JSON import: validates and creates faves with tags
- CSV import: flexible column mapping from header row
- Import/export pages with format documentation
- Feed items include enclosure for images, author info
- Limited-visibility profiles excluded from feeds
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 4 — Admin Panel:
- Admin dashboard with user/fave/pending-request counts
- User management: create with temp password, reset password,
enable/disable accounts (prevents self-disable)
- Tag management: rename and delete tags
- Signup request management: approve (creates user with
must-reset-password) and reject pending requests
- Site settings: site name, description, signup mode
(open/requests/closed)
- All admin routes require both login and admin role
- SignupRequest model and full store (create, list pending,
approve with user creation, reject)
- SetMustResetPassword method on UserStore for admin password resets
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>