Telemetry · Logs

Logs — audit-log entries, dual-written

Audit entries are dual-written: OTEL Logs export to <endpoint>/v1/logs AND a local JSONL file at collector.logs.path (default ~/.nio/audit.jsonl). The JSONL line is the entry verbatim; the OTEL LogRecord uses body = JSON.stringify(entry) plus a flat attribute set for indexing. For Guard evaluations, both carry the same evaluation payload; remote export uses the existing nio-audit logger and collector endpoint. Local writing continues without an endpoint, with remote logs disabled, or after export failure unless collector.logs.local=false.

Entry types

Discriminated by the event field. Five shapes — four named entry types plus the catch-all "hook event" record.

event: "guard" Guard evaluation

Each evaluation has its own ID and records every gate, including skips and failures. No session, trace, or span identity is required. See the canonical schema reference and receiver JSON Schema.

FieldTypeNotes
event"guard"discriminator
evaluation_idstringEvaluation UUID; independent of session, trace, and span identity
timestamp / duration_msstring / numberEvaluation start (ISO-8601) and elapsed milliseconds
platform / service_name / agent_namestringRequired host, service, and agent identity
session_idstring?Optional host session id
tool_name / tool_input_summarystringHost tool name and existing redacted input summary
decisionstringallow / deny / confirm / error
risk_score / risk_levelnumber | null / string | nullFinal risk; unknown values are null
risk_tagsstring[]Deduplicated rule IDs
phasesGuardPhase[]All seven gates in order: tool_gate, allowlist, runtime, static, behavioural, llm, external
findingsGuardFinding[]All findings with unique finding_id, producing phase_id, and rule metadata
terminationGuardTerminationkind, blocked_gate, trigger_finding_ids, and reason
explanationstringHuman-readable result
error{kind, message}?Actual engine error details
scores / top_findings / phase_stopped / max_finding_severityoptional legacy fieldsRetained summaries; top_findings remains limited to five
cwd / action_type / initiating_skill / event_typeoptional contextExisting local and hook context

Each gate records status (completed, skipped, failed), reason, duration, nullable score, finding IDs/count, and whether it triggered a block. Skips explain why and have null timing/score; failures retain errors and a null score. External results retain each endpoint outcome. Termination identifies the actual blocking gate or an aggregate block and references the triggering findings.

event: "session_scan" Skill scan (on-demand or session-start)
FieldTypeNotes
timestampstringISO-8601
platformstringhost
agent_namestring?User-configured agent_name; omitted when unset
session_idstring?host session id
skill_namestringscanned skill / dir
risk_levelstringaggregated severity
risk_tagsstring[]rule IDs hit
finding_countnumber?total findings
event: "lifecycle" Subagent / agent / session lifecycle
FieldTypeNotes
timestampstringISO-8601
platformstringhost
agent_namestring?User-configured agent_name; omitted when unset
session_idstring?host session id
lifecycle_typestringsubagent_spawning / subagent_ended / agent_end / session_start / session_end
detailsRecord<string, unknown>?platform-specific (e.g. OpenClaw: {subagent_id, run_id})
event: "config_error" Config load failure
FieldTypeNotes
timestampstringISO-8601
config_pathstringpath that failed to load
error_messagestringparser / IO error
event: <hook event> Collector hook record (one entry per dispatched hook event)

Discriminator is the canonical hook event name itself: UserPromptSubmit · PreToolUse · PostToolUse · TaskCreated · TaskCompleted · Stop · SubagentStop · SessionStart · SessionEnd.

FieldTypeNotes
timestampstringISO-8601
platformstringhost
agent_namestring?User-configured agent_name; omitted when unset
session_idstring?host session id
cwdstring | nullworking dir
transcript_pathstring?Claude Code-only — path to session transcript JSONL
tool_namestring?for PreToolUse / PostToolUse
tool_use_idstring?for PreToolUse / PostToolUse
tool_summarystring?for PreToolUse / PostToolUse
task_idstring?for TaskCreated / TaskCompleted
task_summarystring?for TaskCreated

OTEL LogRecord projection

The flat attribute set used for OTEL Logs indexing. Same key names as the matching trace span attributes wherever a concept overlaps (tool name, conversation id, guard decision, …) — same query keys work across logs and traces.

  • body = JSON-stringified entry (full content of the JSONL line)
  • severityNumber / severityText derived from risk_level: low→INFO, medium→WARN, high→ERROR, critical→FATAL; INFO when no risk_level
AttributeDescriptionCaptured atPlatforms
gen_ai.tool.nameHost tool name; same key as the tool-span attributePreToolUse · PostToolUse · guard decisionall
gen_ai.tool.call.idHost tool-call id; same key as the tool-span attributePreToolUse · PostToolUseall
gen_ai.conversation.idHost session id; same key as the turn-span attributeevery audit entry with a sessionall
session.idMirror of gen_ai.conversation.id for OTel base-spec consumersevery audit entry with a sessionall
nio.guard.decisionGuard verdict — allow / deny / confirm / errorguard decisionall
nio.guard.evaluation_idIndependent evaluation UUIDstructured guard evaluationall
nio.guard.termination / nio.guard.blocked_gateTermination kind; blocking gate when applicablestructured guard evaluationall
nio.guard.risk_levelGuard risk level — low / medium / high / criticalguard decisionall
nio.guard.risk_scoreGuard risk score, 0–1guard decisionall
nio.guard.risk_tagsComma-joined rule IDs that firedguard decisionall
nio.tool_summaryOne-line summary derived from tool inputPreToolUse · PostToolUseall
nio.task_idTask id from the dispatch eventTaskCreated · TaskCompletedClaude Code + OpenClaw
nio.task_summaryDerived from task inputTaskCreatedClaude Code + OpenClaw
nio.platformSource platform — claude-code / hermes / openclawevery audit entryall
gen_ai.agent.nameUser-configured agent_name; only emitted when setevery audit entry (when configured)all
nio.cwdWorking dir at hook fireevery audit entry with cwdall
nio.eventDiscriminator — hook event name vs guard / lifecycle / scan / config_errorevery audit entryall
nio.event_typepre / post for guard entriesguard decisionall
nio.action_typeexec_command / write_file / network_request / read_fileguard decisionall
nio.max_finding_severityHighest finding severity surfaced this runguard decisionall
nio.phase_stoppedWhich Phase 0–6 produced the decisionguard decisionall
nio.explanationHuman-readable reason for the verdictguard decisionall
nio.transcript_pathClaude Code-only — path to session transcript JSONLhook events with transcriptClaude Code only
nio.phases.{name}.scorePer-phase score (array index 0–6 for structured evaluations); null values omittedguard decisionall
nio.phases.{name}.finding_countPer-phase finding countguard decisionall
nio.phases.{name}.duration_msPer-phase wall-clock cost (ms)guard decisionall

Local JSONL path: collector.logs.path (default ~/.nio/audit.jsonl). Rotation kicks in at collector.logs.max_size_mb (default 100 MB) — the live file is renamed to <path>.1.