The embedded UI was served without any cache header, so a browser or a
proxy could keep an old app.js after an update (seen with the API token
display). Each file now has an ETag (hash of its content, computed at
startup); the page, app.js and style.css are sent with "no-cache", so
each load costs a 304 without body until the next version. Fonts are
cached for 30 days.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
GET /api/v1/logs?query=<LogsQL>&since=<cursor|time|duration>&limit=<n>
returns the logs after a cursor, oldest first, with next_cursor and
has_more. The cursor is a position on _time (the reception time) plus
the number of rows already returned among those sharing that exact
time, ordered by a hash of their content, so following the cursors
gives every row once even within groups cut by a page. Rows count once
older than the lag (30 s), and nothing is returned while the disk
spool still holds logs.
API tokens (Settings > API) give read-only access to /api/v1/ as
"Authorization: Bearer lst_...", in every auth mode; only their SHA-256
is kept in /data/api_tokens.json. README (EN/FR) documents the route and
an n8n workflow keeping the cursor in $getWorkflowStaticData.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Files source (source_type=file), declared in Settings > Log sources > Log
files (/data/files.json, 50 at most): name (= app), path as mounted in the
container (file or glob, rotated copies .1/.gz left out, 200 files per
glob), format, optional host, switch.
- formats: plain text (level detected, leading timestamp read and removed),
JSON lines (msg/time/level keys, numeric pino levels, other keys as
fields, nested ones dotted), logfmt, access log CLF (nginx, Apache,
Traefik CLF with router/backend/duration), Traefik access log in JSON
(summary "GET host/path 200 · 12.5 ms · router · client"), regular
expression with named groups time/level/msg/host/app/status; 5xx = err,
4xx = warning; fields named like LogStream's own renamed field_<name>
- indented lines (text) and non-matching lines (regex) added to the
previous entry, which waits 2 s for them
- inode + offset per file in /data/files-state.json, rotation (end of
.1 read first), copytruncate, file renamed within the glob; last
FILES_BACKFILL (1h) of dated lines read when turned on, new files read
from their start
- status per file (files followed, entries read, not found / permission)
UI: rows with switch, name, path, format, host (regex field when needed),
status line; green edge, tint and FILE badge in the list, "Files" in the
source filter, HTTP fields first in the details, FR/EN. docker-compose,
.env.example and README (EN/FR): mounts, formats, FILES_BACKFILL.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Audit source (source_type=audit), off until enabled in Settings > Sources:
- follows /var/log/audit/audit.log (path configurable, AUDIT_LOG), with
rotation (end of audit.log.1 read first) and an offset checkpoint in
/data/audit-state.json; last AUDIT_BACKFILL (1h) read on first enable
- groups the records of an event by msg=audit(time:serial), closed on EOE,
after 2 s without a record, or once the log has moved 2 s past it
- fields audit_type, audit_serial, auid, user (enriched name, host
/etc/passwd or uid), exe, res, key, audit_raw; app = audit_type, host =
node= / AUDIT_HOST / /host/etc/hostname; summary "who: what [key] →
result"; warning when res=failed / success=no, info otherwise
- hexadecimal values (proctitle, cmd, exec arguments…) decoded
Internal logs (source_type=logstream, host=logstream, app=component):
- logEvent() for VictoriaLogs unreachable / back, lost batches, spooled
batches refused, syslog listener errors, Docker API and container
streams lost, failing sources, start and stop
- on by default from warning; switch and minimum level in Settings >
Sources (/data/selflog.json)
- bounded queue, never waits for the Store, same event at most once per
30 s with a count of the repeats: a failing Store cannot loop
UI: violet (audit) and turquoise (LogStream) left edge and tint, AUDIT /
LS badge before the app, raw audit record under the summary (folded in
compact density until the row is opened), Audit and LogStream in the
source filter, FR/EN translations. docker-compose: optional
/var/log/audit, /etc/passwd and /etc/hostname mounts. README (EN/FR):
host prerequisites (log_group, ACL for UID 10001).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
New source in Settings > Sources, off by default: reads the kernel ring
buffer of the host through /dev/kmsg, dates records from the boot time and
resumes after the last sequence number of the same boot (no duplicates on
restart). Missing device or denied access is shown in the UI, retried every
10 s. docker-compose.yml and .env.example document the device and the
kernel.dmesg_restrict / CAP_SYSLOG options.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- ALLOW_PURGE is now false by default; the UI shows a banner when there is
no authentication.
- Read-only role: AUTH_VIEWER_USER/AUTH_VIEWER_PASS in local mode, or
OIDC_ADMIN_GROUP in OIDC mode; changes get 403 and the admin settings
are greyed out.
- Content-Security-Policy (inline scripts allowed by hash) and other
security headers; cross-site changes are refused.
- Syslog TCP: at most SYSLOG_TCP_MAX_CONNS connections, closed after
SYSLOG_TCP_IDLE of silence; HTTP idle timeout.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Batches that fail go to /data/spool (SPOOL_MAX_MB, 1 GiB by default) and
are sent again oldest first; retries no longer block the store loop and
follow the shutdown context.
- Docker and host logs wait for room in a full queue instead of being
dropped; the Docker position only moves once a line is stored or spooled.
- Reverse DNS no longer holds up the syslog listeners, with an LRU cache
and a cap on concurrent lookups.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The presets move from app.js to presets.json, built into the binary and
served by /api/presets. PRESETS_FILE (default /data/presets.json) replaces
the list when present; it is read again each time Settings opens and the
built-in list is used if it is invalid. docs/presets.md (EN/FR) explains
each preset and the file format.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The local mode now shows a login page in the colors of the UI (light/dark theme, FR/EN)
with a signed session cookie and the log out button, like the OIDC mode. LOGIN_LOGO shows
a PNG mounted in the container on that page. SESSION_TTL applies to both modes
(OIDC_SESSION_TTL still works). HTTP Basic credentials are still accepted for scripts.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AUTH_MODE=local keeps the HTTP Basic authentication (unchanged default);
AUTH_MODE=oidc logs in through an OpenID Connect provider with the
authorization code flow and PKCE, standard library only: discovery,
ID token signature (RS/PS/ES) and claims checks, signed session cookie
whose key is kept in DATA_DIR. The UI gets a log out button and reloads
into the login when the session ends.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
New source, off by default and switched in Settings > Sources, that
collects the system logs of the machine hosting the stack:
- reads the systemd journal files directly (pure Go reader, no
journalctl in the image), from /var/log/journal and /run/log/journal
mounted read-only under /host;
- falls back to following the text files of /var/log (syslog,
messages, *.log) on hosts without journald;
- positions saved in /data/hostlogs-state.json, HOST_LOGS_BACKFILL
read when the source is turned on;
- source_type "host", selectable in the Source filter;
- compose mounts and group_add (HOST_LOGS_GID, adm by default), docs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- SyslogServer opens and closes the UDP/TCP listeners at runtime from the
configuration saved in /data/syslog.json; a busy port no longer stops
Logstream, the error is shown in Settings instead.
- Sources tab: syslog section with an on/off switch, UDP and TCP labels,
the live listening state and the published port (SYSLOG_PORT, passed as
SYSLOG_PUBLIC_PORT for display; the mapping stays in docker-compose).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- docker.go follows every running container through the Docker API
(events + logs with follow), resumes after a restart from the last
position saved in /data/docker-state.json, reads DOCKER_BACKFILL (1h)
of history for new containers, strips terminal color codes and guesses
the severity from the line (JSON, logfmt, [ERROR], ERROR ...).
- Logs carry source_type=docker, container, container_id, image,
compose_project, compose_service and stream; host is the Docker host.
- Settings > Sources: one switch per container (grouped by compose
project), enable/disable all, follow new containers automatically.
Choices are saved per compose service in /data/docker.json.
- Source filter (syslog / docker) in the filter bar and the live view.
- docker-compose: read-only docker-socket-proxy; Logstream and the proxy
are labelled logstream.exclude=true and never collected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- GET /api/export.csv streams every stored log matching the filters
(newest first, up to EXPORT_MAX rows, 100000 by default) straight from
VictoriaLogs, with dates in the time zone chosen in Settings.
- Export button with two variants: CSV (comma, UTF-8) and CSV for Excel
(semicolon + BOM, formula injection neutralized).
- Store.QueryStream streams query results without buffering them.
- Mobile: filter bar keeps two selects per row, count next to Export.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Reverse DNS (cached PTR lookups): IP hosts are stored with their name
in 'host' and the IP in 'host_ip'; older IP-only logs are resolved on
display and the host filter shows 'name (IP)'. RDNS / DNS_SERVER env.
- Settings > Danger zone: delete all logs (type PURGE to confirm) through
VictoriaLogs /delete/run_task; -delete.enable added to docker-compose.
ALLOW_PURGE env to disable it.
- Remove the custom YYYY-MM-DD - HH:MM:SS:mmm format; default is now the
usual French display DD/MM/YYYY HH:MM:SS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>