5 attempts / 15 min per IP. Excess attempts get 429 + Retry-After.
Client IP read from X-Forwarded-For with loopback fallback.
In-memory; prune inline on each check.
Refactored app() into app_with_state() + thin wrapper.
Uses parking_lot::Mutex (no unwrap, no poisoning).
Injects X-Content-Type-Options, X-Frame-Options, Referrer-Policy,
Content-Security-Policy, and Permissions-Policy on every response.
Registered as outermost layer so it covers setup_guard 503s too.
No new dependencies.
modules/nixos/archivr-server.nix — services.archivr-server NixOS module:
- enable/bind/archives/user/group/openFirewall options
- generates archivr-server.toml from options via pkgs.writeText
- pins auth DB to /var/lib/archivr-server/ (StateDirectory)
- dedicated archivr system user + group
- hardened systemd unit: ProtectSystem=strict, NoNewPrivileges,
PrivateTmp, RestrictNamespaces, etc.; archive paths whitelisted
- openFirewall parses the port from the bind string automatically
flake.nix:
- add self to outputs args
- add aarch64-linux to systems (Raspberry Pi / ARM servers)
- expose nixosModules.archivr-server + nixosModules.default
.gitignore: whitelist modules/ directory
docs/README.md: add 'Hosting on NixOS' section with full example
- LoginPage/SetupPage: centered card layout with display font, styled fields and submit button
- Topbar: fix duplicate Settings nav item; add styled user-menu with username + logout button
- RunsView: format ISO timestamps to readable dates, add colored status badges (completed/failed/running)
- AdminView: view-tabs system, styled admin-table, admin-input, status-badge (active/disabled), btn-primary
- SettingsView: replace all inline styles with form-section/form-field/field-input/btn-primary/btn-danger
- CollectionsView: restructure create form with proper field labels and btn-primary
- TagsView: add Tags section heading with separator
- ContextRail: show visibility as human-readable label not raw number; fix assign-error class
- styles.css: add auth-loading, view-tabs, form utilities, btn variants, status badges,
run-status pills, token-banner/row, checkbox-row, coll-create-form, tag-tree-header CSS
Garnix CI was failing because openssl-sys couldn't find the OpenSSL
library. Added pkgs.openssl to buildInputs for both archivr_cli_unwrapped
and archivr_server_unwrapped to provide the necessary system dependency
and allow pkg-config to locate openssl.pc.
POST /api/archives/:id/captures now returns 202 Accepted immediately with
{ job_uid, status: "pending" }. The capture runs in a tokio::task::spawn_blocking
thread and updates the job status to running → completed/failed in the archive DB.
Core DB (database.rs):
- capture_jobs table: id, job_uid, archive_id, run_uid, status, error_text, created_at, updated_at
- create_capture_job, update_capture_job_status, get_capture_job, fail_stalled_capture_jobs
Core archive (archive.rs):
- CaptureJobSummary type + get_capture_job query
Server (routes.rs):
- capture_handler: creates job, spawns background capture, returns 202 immediately
- GET /api/archives/:id/capture_jobs/:job_uid: poll job status
- main.rs: on startup, marks any 'running' jobs from previous session as 'failed'
Frontend:
- api.js: submitCapture returns job object; pollCaptureJob added
- CaptureDialog.jsx: polls every 500ms after submit, shows 'Running…' state,
handles completed (close + refresh) and failed (show error), cleans up interval
163 tests green. Frontend builds cleanly.