Logs

Search and tail your logs, and prove the audit trail was not altered.

In preview logs version 0.1.0 Part of Stats and logs

Actions

11 actions, callable from the panel, the command palette and the API as POST /api/v1/a/<id>. Internal actions used between modules are not listed.

ActionWhat it doesRiskPreview
logs.query Search logs (read) low No dry run
logs.tail.open Open a live tail low No dry run
logs.tail.close Close a live tail low No dry run
logs.audit.query Query the audit trail (read) low No dry run
logs.audit.verify Verify the audit chain (read) low No dry run
logs.audit.export Export the audit trail (read) medium No dry run
logs.tenant.list List log tenants (read) low No dry run
logs.retention.get Log retention and shipping health (read) low No dry run
logs.retention.set Set log retention high No dry run
logs.masking.set Set extra masking patterns high No dry run
logs.reconcile Re-apply the log store and shippers low No dry run

Permissions and limits

Permissions

  • logs.view Search and tail logs
  • logs.audit.view Read the audit trail
  • logs.audit.verify Verify the audit chain
  • logs.audit.export Export the audit trail
  • logs.admin Log retention, masking and shipping

Engineering notes

Generated from modules/logs/docs.md at build d90e9e2. These are the notes the engineers keep next to the code: precise, technical, and honest about what is not done yet.

Search, tail and audit. Logs live in VictoriaLogs on the control node with native multitenancy: every hosting account is one tenant (AccountID header, mapping table tenants), tenant 0 is the platform.

Shipping (AC-logs-16)

  • The plugin pulls from each node every 5 s with logs.ship.read (agent: journald after a cursor + log files), groups lines by tenant, masks secrets, POSTs NDJSON to /insert/jsonline, and only then stores the new cursors in its own cursors table. A store outage leaves the cursors where they were (journal and files keep the data) and raises logs.ingest.delayed; a crash in between re-ships at most one batch (at-least-once).
  • Attribution is done by the agent from things an account cannot forge: the journal entry's cgroup slice (rc-acct-<user>.slice), the root-owned directory /var/log/rc/<user>/*.log, or /home/<user>/logs/*.log. Account files are opened with openat2(RESOLVE_NO_SYMLINKS|NO_MAGICLINKS) and must be regular files, so an account cannot point a symlink at another file and read it into its own stream. Everything else (nginx, mail, selected system units) is platform (tenant 0, admin only).
  • Sources are named: journal, nginx.access, nginx.error, mail, account.<file>; JSON lines (nginx json) are parsed into fields, other lines stay raw.
  • Masking (AC-logs-10): defaults always on (Authorization/Cookie/password/token/api-key values, Bearer/Basic credentials); extra RE2 patterns via logs.masking.set.
  • VictoriaLogs is installed by logs.store.install (pinned release + SHA-256, DynamicUser, loopback :9428), self-healing like stats.

Isolation (AC-logs-11) - the security property

tenantsFor picks the tenants from the authenticated actor: user/sub-user = own account only, reseller = accounts.list as the caller, admin = platform + all. The tenant goes to VictoriaLogs as a header set by the plugin; nothing from the request (query text, parameters, headers) can change it, and asking for another account's account_id is not_found. Queries are time-bounded (<= 30 d, not future), capped (<= 1000 lines) and sources go through extra_filters. A query is evaluated per tenant and merged newest-first.

Live tail (AC-logs-04)

logs.tail.open starts a server-side tail of exactly one tenant, publishing logs.tail.lines events (owner = the account) once a second; ends after 10 min or logs.tail.close (only the opener can close); max 3 per person; the goroutine and the VictoriaLogs stream are cancelled together.

Audit (AC-logs-17/18/20/21/23)

The writer stays core. core.audit.query (scoped: admin all, reseller own tree, user own account, sub-user their account's) and core.audit.verify (recompute every hash, report the first broken link, ranges) live in internal/coremod/audit_verify.go. logs.audit.* are the UI-facing wrappers; export (JSON/CSV, formula-injection safe) carries a manifest (range, count, sha-256, chain verdict for admins). logs.audit.check runs every 10 min (incremental from a checkpoint, full chain daily) and emits logs.audit.tamper once per broken row. There is no action that edits or deletes an audit row. Verification relies on the writer's canonical JSON (sorted keys): rows written through core.Audit.Write with json.Marshal(map) data (all host paths) verify; a writer that hands pre-formatted, unsorted JSON would not. Deleting the last rows of the chain is only detectable with an external anchor (AC-logs-19, not built).

Not built

Destinations/SIEM export, saved searches, raw archive download, field suggestions, histogram, retention per source, per-account flood caps ("N lines dropped"), support bundle, legal hold/pruning, off-box anchors.