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.
| Action | What it does | Risk | Preview |
|---|---|---|---|
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.viewSearch and tail logslogs.audit.viewRead the audit traillogs.audit.verifyVerify the audit chainlogs.audit.exportExport the audit traillogs.adminLog 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 owncursorstable. A store outage leaves the cursors where they were (journal and files keep the data) and raiseslogs.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 withopenat2(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.