Halite writes an audit log entry for every significant action — logins, permission checks, key operations, job runs, settings changes, and more. The log is stored in the database and queryable through both the UI and the API.Documentation Index
Fetch the complete documentation index at: https://www.halite-app.com/llms.txt
Use this file to discover all available pages before exploring further.
What is recorded
Each entry in theaudit_log table captures the following fields (from audit/models.py):
| Field | Type | Description |
|---|---|---|
id | integer | Auto-incrementing primary key (BIGINT on Postgres, INTEGER on SQLite) |
at | timestamp (with timezone) | When the event occurred (UTC) |
user_id | UUID or null | The user who performed the action; null for unauthenticated requests (e.g. a failed login) |
action | string (≤128 chars) | The action name, e.g. auth.login, auth.change_password |
resource | string (≤255 chars) | The resource targeted, e.g. user:admin, key:web-01 |
args_json | JSON or null | Request arguments, with sensitive fields redacted |
salt_jid | string or null | The Salt job ID, when the action triggered a Salt job |
decision | string (≤16 chars) | allow or deny |
result_code | integer | The HTTP status code of the response |
duration_ms | integer | Response time in milliseconds (default 0 if not measured) |
Sensitive field redaction
args_json is automatically redacted before being written. Any key whose name matches a known sensitive identifier has its value replaced with [REDACTED]. The redacted keys include: password, passwd, pw, secret, token, api_key, apikey, authorization, new_password, and current_password.
When entries are written
Theaudit/writer.py record() function is called by route handlers at the point of the authorization decision or action outcome. Because it is called explicitly rather than via middleware, coverage is intentional — every route that calls record() is audited.
Examples of audited actions:
- Login (
auth.login) — written on both successful and failed login attempts. Failed attempts havedecision = "deny"anduser_id = null. - Change password (
auth.change_password) — written on both success and failure. - Any other action where a route handler explicitly calls
audit_record(...).
Auditing reads
By default, read-only requests (e.g. listing minions or jobs) are not written to the audit log. A reservedAUDIT_AUDIT_READS setting exists (default false) but is not currently wired into request handling.
Querying the log
The audit log is available atGET /api/audit. Access requires the view:audit:* permission, which only the admin role holds by default (see RBAC & Permissions).
Query parameters
| Parameter | Type | Description |
|---|---|---|
user_id | UUID | Filter to entries from a specific user |
action | string | Filter to a specific action name (exact match) |
decision | string | Filter by allow or deny |
since | datetime | Return entries at or after this timestamp |
until | datetime | Return entries before this timestamp |
limit | integer (1–500) | Number of entries to return (default 50) |
offset | integer (≥0) | Pagination offset (default 0) |
at descending). The response includes a total count (across all matching rows, not just the current page) alongside the entries array.
Example response shape
Related pages
- Audit feature page — the in-app audit log viewer
- RBAC & Permissions — who can view the audit log