Add password-only phx.gen.auth (argon2) with closed, admin-managed
registration: admins create accounts with a temporary password and users
must change it on first login. Strip email/magic-link flows and remove the
unused Swoosh mailer.
Add two roles (admin/user) enforced via on_mount guards, and per-user data
tenancy: scans, assets, and findings carry a user_id; vulnerabilities stay
global with visibility derived through findings via a correlated EXISTS.
Cross-tenant detail URLs 404; admins see all rows.
Merge the account password page into /settings. Add an admin user-management
dashboard, a seeded bootstrap admin (fixed dev creds via seeds.exs, random
password via the new seed-admin release task), and cross-tenant isolation
tests. Bundle the root layout noindex/theme-color SEO change.
The pinned Debian snapshot shipped libgnutls30 3.7.9-2+deb12u6, which Trivy
flagged for 5 fixed HIGH/CRITICAL CVEs (deb12u7 available), blocking the
publish gate. Add apt-get upgrade to the runtime stage so security fixes land
without chasing a newer base snapshot on every advisory.
Verified locally: Trivy HIGH/CRITICAL fixed-only scan reports 0 findings.
- Pin builder + runtime to a real, matching Debian snapshot (bookworm-20260518);
the previous 20250630 tag was never published for OTP 27.3.4, so buildx
couldn't resolve the builder image and CI failed
- Run mix compile BEFORE mix assets.deploy: the colocated-hooks extractor
generates phoenix-colocated/bulwark at compile time and esbuild resolves it
via NODE_PATH, so the prior order broke the asset build
- Production hardening: --no-install-recommends, RELEASE_DISTRIBUTION=none,
crash dumps to /dev/null, CMD is server-only (migrations run as an
initContainer per the deploy runbook)
Verified: image builds (153MB) and the release boots.
GitHub Actions workflow: mix precommit gate against a postgres:17 service
container on PRs and pushes; on pushes to main, build the release image,
gate on a Trivy HIGH/CRITICAL scan, emit an SBOM, push to Harbor, and sign
the pushed tags with Cosign.
- Image: harbor.icecoldchris.dev/bulwark/bulwark (tags: sha-<sha>, latest)
- Commit the Cosign public key (cosign.pub) for verification; gitignore the
private key (cosign.key / *.key)
- Multi-stage Dockerfile (mix release, embedded ERTS, runs as nobody) with
migrate/server release overlays for an initContainer migration flow
- docker-compose: standalone-friendly upstream images (postgres:17,
valkey/valkey:8) for the local dev loop. The cluster's operator images
(cloudnative-pg, hyperspike) don't run standalone — DSN-from-env already
gives local<->cluster parity, so image identity is local-only
- .env(.local).example templates + .envrc (direnv) loader
- .dockerignore; .gitignore fixups for release artifacts and env files
Cache (Bulwark.Cache): Redix-pooled Valkey facade that degrades gracefully
when VALKEY_URL is unset or the server is unreachable. Backs the dashboard
KPI cache (5m TTL, invalidated on triage/cancel/parse) and a fail-open
fixed-window rate limiter on uploads. Oban stays on durable Postgres.
Config: dev and test Repos honor DATABASE_URL (12-factor parity) with
zero-config localhost fallbacks. Test config reads TEST_DATABASE_URL and
TEST_VALKEY_URL so a dev .env.local loaded via direnv can't leak into the
hermetic suite.
Health: /health (liveness, always 200) and /ready (readiness, 503 if
Postgres is down; cache-down reported degraded but still 200).
Fixes:
- Detector no longer crashes on top-level JSON arrays (non-map catch-all)
- ParseJob isolates each finding in its own transaction; synthesizes stable
external IDs (SHA-256 fingerprint) when a tool omits one
- Vulnerability severity normalized centrally (aliases -> canonical levels)
- Upload path traversal closed (Path.basename); peer IP captured for limiter
- Add FilterBar JS hook for global cmd+K keyboard shortcut
- Add filter_bar component to CoreComponents with search input and pill slots
- Vulnerabilities: text search across CVE ID/title (Flop compound fields) + severity pills
- Findings: status quick-filter pills (open, acknowledged, resolved, false positive)
- Assets: text search on identifier + type pills (container image, package, etc)
- Add compound_fields to Vulnerability and Asset Flop.Schema derivations
- Filter state persisted in URL params, compatible with Flop pagination/sorting
- Filter bar accent glow on focus when accents enabled
- Active nav accent bar (2px sky left border on current page)
- Section card header gradient line (sky-to-transparent top border)
- Status bar connected dot glow
- Severity indicator dots (colored dot before badge label via CSS ::before)
- Processing scan badge pulse glow animation
- Focus ring accent (sky-500 outline on :focus-visible)
- Table row hover highlight
- Add sev-dot/data-sev attrs to severity_badge component
- Add scan-processing class to processing scan status badge
- Add status-dot class to status bar indicator
- Add card-header class to dashboard section headers
- All effects gated behind body[data-accents] toggle
Rewrite entire frontend to match the Auditor Professional design
reference. Dark-mode-only zinc palette with Inter/JetBrains Mono
typography, collapsible sidebar (52px/180px), persistent header
with breadcrumbs and system integrity pulse, status bar footer
with uptime and sync timers.
- app.css: dark-only base, @theme for fonts/text-xxs, dot-grid
background, sticky table headers with backdrop blur, custom
scrollbar, integrity-pulse animation
- root.html.heex: Google Fonts, remove theme toggle JS
- app.js: add Sidebar and StatusBar hooks, merge with colocated
- layouts.ex: collapsible sidebar, header with breadcrumbs,
status bar with JS-driven timers
- core_components.ex: dark-only palette, 3-letter severity
badges (CRT/HI/MED/LOW), short finding status labels
(Open/Ack/Fixed/FP), compact mono-font badges
- All 8 LiveViews: remove dark: variants, use dark palette
directly, monospace data classes, compact density
- CLAUDE.md: document new design system conventions
- Remove daisyUI plugin and vendor files (daisyui.js, daisyui-theme.js)
- Add custom zinc/slate palette with semantic colors for severity only
- Add data-table CSS class for dense Flop-paginated tables
- Rewrite sidebar from daisyUI drawer to flex-based peer-state layout
- Replace all daisyUI classes across 8 LiveViews and CoreComponents
- Extract finding_status_badge to CoreComponents (was duplicated in 4 files)
- Add JS dropdown with toggle/hide for triage actions (replaces daisyUI dropdown)
- Update theme JS to resolve system preference to concrete data-theme
- Track theme setting separately via data-theme-setting attribute
- Update pagination to use Flop.Phoenix class attr instead of removed opts
- Rewrite README with project overview and getting started guide
- Update CLAUDE.md to reflect design system changes