Skip to content

Audit Logging

Track every operation with tamper-evident, append-only audit logs.

Nefia records every command execution, file operation, and session event in an append-only audit log. Log entries are chained using SHA-256 hashes and signed with HMAC-SHA256, making any tampering detectable through verification.

What Gets Logged

Event TypeDescription
Session & Execution
session_openSession opened with a target host
session_closeSession closed
execCommand execution on a target host
exec_deniedOperation blocked by policy engine
factsHost facts gathered
File System Operations
fs_readFile read
fs_writeFile write
fs_patchFile patch (partial modification)
fs_listDirectory listing
fs_statFile/directory stat
fs_stream_readStreaming file read (download)
fs_stream_writeStreaming file write (upload)
fs_symlinkSymbolic link created
fs_readlinkSymbolic link target read
fs_archive_createArchive (tar/zip) created on target
fs_archive_extractArchive extracted on target
fs_diffFile diff between hosts or snapshots
Playbook
playbook_startPlaybook execution started
playbook_completePlaybook execution completed
Configuration & Host Management
config_changeConfiguration saved or updated
host_revokeOne host's VPN access revoked
host_revoke_allEmergency revocation of all hosts
VPN Operations
vpn.peer_reconcileVPN peer list reconciled
vpn.inviteVPN invite token generated
vpn.invite_revokeVPN invite token revoked
vpn.key_rotateVPN WireGuard key pair rotated
JIT Access
jit.requestJIT access request created
jit.approveJIT access request approved
jit.denyJIT access request denied
jit.revokeJIT access grant revoked
Queue
queue.retryQueued job retried
SSH CA
sshca.sign_userSSH user certificate signed
sshca.sign_hostSSH host certificate signed
sshca.trust_known_hostsSSH CA trust entries added to known_hosts
mTLS
mtls.issue_clientmTLS client certificate issued
mtls.revokemTLS client certificate revoked
Team Management
team.createTeam created
team.invite.createTeam invitation created
team.member.joinMember joined team
team.member.leaveMember left team
team.switchActive team switched
System Query (read-only)
sys_diskDisk usage queried
sys_envEnvironment variables listed
sys_networkNetwork interfaces listed
sys_group_listOS groups listed
sys_service_showService details queried
sys_service_logsService logs retrieved
sys_cron_listCron jobs listed
sys_mount_listMount points listed
sys_sysctl_listKernel parameters listed
sys_hostnameHostname queried
sys_timeSystem time queried
sys_update_checkAvailable updates checked
sys_logs_searchSystem logs searched
sys_package_listInstalled packages listed
net_pingICMP ping executed
net_tracerouteTraceroute executed
net_dnsDNS lookup performed
net_connectionsActive network connections listed
net_listenListening ports listed
firewall_listFirewall rules listed
firewall_statusFirewall status queried
container_listContainers listed
container_inspectContainer details inspected
System Mutation
power_rebootHost rebooted
power_shutdownHost shut down
sys_package_installPackage installed
sys_package_removePackage removed
sys_user_createOS user created
sys_user_modifyOS user modified
sys_user_deleteOS user deleted
sys_killProcess killed
sys_cron_createCron job created
sys_cron_deleteCron job deleted
sys_service_enableService enabled
sys_service_disableService disabled
sys_service_reloadService reloaded
sys_hostname_setHostname changed
sys_time_setSystem time or timezone changed
sys_update_applySystem updates applied
sys_sysctl_setKernel parameter changed
sys_mountFilesystem mounted
sys_umountFilesystem unmounted
firewall_addFirewall rule added
firewall_removeFirewall rule removed
container_logsContainer logs retrieved
container_startContainer started
container_stopContainer stopped
container_restartContainer restarted
Composite Operations
runOne-shot command execution (composite)
investigateComprehensive host investigation
file_deployFile pushed and verified
service_deployAtomic service deployment
config_applyAtomic configuration update
system_baselineSystem baseline drift detection
system_healthComprehensive system health check

Log Format

Audit logs are stored as JSONL (JSON Lines) files, date-partitioned with a new file per calendar day. The default directory follows the platform config path:

PlatformAudit log directory
macOS~/Library/Application Support/nefia/audit/
Linux~/.config/nefia/audit/
Windows%AppData%\nefia\audit\

For example, 2026-02-24.jsonl within that directory. Each entry contains these fields:

FieldTypeDescription
seqintegerMonotonically increasing sequence number
prev_hashstringhash value of the previous entry (chain link)
hashstringSHA-256 hash of the current entry
hash_versionintegerHash computation algorithm version (0-3)
hmacstringHMAC-SHA256 signature (for tamper detection)
tsstringISO 8601 timestamp (UTC)
host_idstringTarget host identifier
op_kindstringEvent type (exec, fs_read, etc.)
job_idstringUnique job identifier for correlation
session_idstringSession identifier (if within a session)
userstringAuthenticated operator identity
agent_idstringAgent identifier (if agent-initiated)
requestobjectOperation parameters (command, path, etc.)
resultobjectOutcome (exit code, bytes transferred, etc.)
team_idstringTeam identifier (if team-scoped)
actor_rolestringActor's RBAC role (if team-scoped)
artifactsarrayRelated artifact paths (if any)
sourcestringOperation source: "cli" (human operator), "mcp" (AI agent via MCP), or "tui" (TUI dashboard). Present when hash_version >= 4.

Example Entry

json
{
  "seq": 142,
  "prev_hash": "a1b2c3d4e5f6...previous entry hash...",
  "hash": "f7e8d9c0b1a2...current entry hash...",
  "hash_version": 3,
  "hmac": "e3b0c44298fc1c149afbf4c8996fb924...",
  "ts": "2026-02-24T09:15:32Z",
  "host_id": "web-01",
  "op_kind": "exec",
  "job_id": "j-a8f3bc91",
  "user": "operator@example.com",
  "team_id": "team-abc123",
  "actor_role": "admin",
  "request": { "command": "systemctl restart nginx", "timeout": "30s" },
  "result": { "exit_code": 0, "duration_ms": 1240 }
}

Hash Chain Integrity

Audit log tamper detection consists of two layers: a SHA-256 hash chain and HMAC-SHA256 signatures.

SHA-256 Hash Chain

Each entry's hash is computed from the entry's content and the previous entry's hash using SHA-256. Tampering with any entry invalidates the hashes of all subsequent entries:

plaintext
Entry 1: hash = SHA-256(seq=1 | prev_hash="" | fields...)
Entry 2: hash = SHA-256(seq=2 | prev_hash=entry_1.hash | fields...)
Entry 3: hash = SHA-256(seq=3 | prev_hash=entry_2.hash | fields...)

HMAC-SHA256 Signatures

A hash chain alone allows an attacker to conceal tampering by recomputing the entire chain. To prevent this, each entry's hash is signed with an HMAC-SHA256 signature (the hmac field). The HMAC key is stored outside the audit log directory, under the parent configuration directory (<config_dir>/secrets/audit_hmac.key), so an attacker without the key cannot produce valid HMAC values even if they recompute the chain.

Hash Version

The fields included in the hash computation vary by version:

VersionFields IncludedNotes
0Core fields only (seq, prev_hash, ts, op_kind, host_id, job_id, session_id, user)Legacy format
1V0 + request, resultBase default (auto-promoted to V2 when HMAC key is present)
2Same hash as V1 + HMAC signatureEffective default — HMAC key is auto-generated on first run
3V1 + team_id, actor_roleUsed with team features
4V3 + source fieldAuto-selected when an audit record includes a source value. Distinguishes AI agent operations from human operations.

Integrity Protection on Write Failure

If a file write (write) or sync (sync) fails, the seq and prev_hash are rolled back to preserve hash chain consistency. The failed entry is not incorporated into the chain, ensuring that subsequent entries link correctly.

CLI Commands

View Recent Entries

bash
nefia audit tail -n 200
nefia audit tail -n 200

SEQ TIME HOST OP JOB_ID USER 138 2026-02-24T08:55:12Z web-01 exec_denied j-c4a28f63 operator@example.com 140 2026-02-24T09:12:01Z web-01 fs_read j-3ef19ab2 operator@example.com 141 2026-02-24T09:14:58Z web-02 fs_write j-7cd2e4f0 operator@example.com 142 2026-02-24T09:15:32Z web-01 exec j-a8f3bc91 operator@example.com

Show a Specific Job

bash
nefia audit show --job j-a8f3bc91
nefia audit show --job j-a8f3bc91

Verify Hash Chain

Verifies the hash chain integrity of a single day's audit log file. Defaults to today's date when no argument is provided.

bash
nefia audit verify              # verify today's log
nefia audit verify 2026-02-24   # verify a specific date
nefia audit verify

OK: 142 records verified for 2026-02-24 (hash chain + HMAC).

Performance and Resource Management

File Handle Reuse

Audit log writes are optimized by reusing file handles on a per-day basis. Only two system calls — write + sync — are needed per record (no open/close required).

plaintext
Record write flow:
  1. ensureFile(date) — skipped if the existing handle's date matches (O(1))
  2. write(data)      — direct write without buffering
  3. sync()           — immediate flush to disk (prevents data loss)

Automatic File Rotation on Date Change

When the date changes (UTC-based), ensureFile() detects it, closes the previous day's file handle, and automatically opens a new file for the current day. No explicit rotation operation is required.

Resource Cleanup via Close()

When App.Close() is called, Audit.Close() is executed, performing the following:

  1. Closes the current file handle
  2. Waits for any running cleanup goroutines to complete

This ensures no file handle leaks occur on application shutdown.

Configuration

Configure audit logging in nefia.yaml:

yaml
audit:
  enabled: true
  required: false  # when true, operations fail if audit write fails
  dir: ~/.config/nefia/audit
  retention_days: 90
  syslog_addr: "localhost:514"
  syslog_proto: "udp"
KeyDefaultDescription
enabledtrueEnable or disable audit logging
requiredfalseWhen true, audit write failures are treated as fatal errors — operations are blocked if audit logging fails. Default behavior is warn-and-continue.
dirPlatform config dir + /auditDirectory for log files
retention_days90Automatically delete logs older than this (0 = never)
syslog_addr""Syslog server address (e.g., localhost:514). Empty disables forwarding.
syslog_proto"udp"Syslog protocol: udp or tcp

When syslog forwarding is enabled, events are sent in real time alongside the local JSONL write. The local log is always written regardless of syslog availability, ensuring the hash chain remains intact.

Verification Workflow

For compliance or incident investigation, verify integrity first, then filter and inspect:

bash
# Step 1: Verify the hash chain for today (or a specific date)
nefia audit verify
nefia audit verify 2026-02-24
 
# Step 2: View recent entries
nefia audit tail -n 100
 
# Step 3: Inspect specific jobs
nefia audit show --job j-a8f3bc91
Security Overview

Understand Nefia's defense-in-depth security architecture.

Policy Engine

Define the rules that generate exec_denied audit events.