Reading findings on the endpoint
An enrolled machine reports to the console, and the console is where its findings are read. This page is for the other case: operating an agent directly on the host — a self-managed or offline machine, a machine that was never enrolled, or a laptop running the agent locally to try it out.
Start here: lumen-agent status
Before reading a single finding, ask the endpoint what it is doing:
lumen-agent status # exit 0 daemon answered, 3 daemon down, 1 no report
lumen-agent status --json | jq '.daemon.policy, .hooks[].policy_id, .warnings'
One screen answers the questions a security review asks: what policy is in force, is anything actually running, which supervisor would restart the daemon, where the proxy forwards to, which Claude Code guards are registered and what policy each of them enforces, and how deep the spool is.
These lines are worth knowing about before you need them:
control DISABLEDis printed first, above the daemon block. A switched-off agent looks identical to a healthy one everywhere else.policy … source embeddedmeans the daemon is running the policy compiled into the binary, not your file, and a policy the console assigns is not applied either. That policy only logs, so the next line readsMONITOR (nothing is redacted or blocked)and nothing else looks wrong: the source is what gives it away. See Configuration.MONITOR (nothing is redacted or blocked)is a policy whose every rule logs, such as Local default, the one every endpoint starts on. The endpoint's enforcement mode makes no difference to it; see From monitor to enforce.MONITOR (console ceiling); the policy would: …means the endpoint holds a policy with promoted rules and the console holds it in Monitoring: the listed rules would redact or block, and all of them are logging until it is switched to Enforcing.MONITOR (agent default until the console's first heartbeat)is the same cap before the console has said anything since the agent started; it clears on the first heartbeat.uplinkis the findings upload, separate from the heartbeat. It always shows when an upload last succeeded, and readsFAILING(with a warning) when uploads are failing even though the heartbeat is fine, orPAUSEDwhen the workspace has reached its monthly spend limit.- A warning that the hook guard and the daemon are running different policies is the one to act on.
spool … queuedcomes from inside the daemon, never from opening the file. Adead-letter Nafter it counts records the agent stopped trying to upload because the control plane could not store them, even alone, while it stored the records around them, and failed each of them once more right after storing another record. They stay in the spool on this machine for up to 30 days, the rest of the queue keeps draining, and the console will never show them.statuslists a non-zero count as a warning. A control plane that is down, or that fails every upload, never moves a record there, whether it stays broken or recovers: the queue waits for it.- The
routed toolingblock lists what points at the capturing proxy. A target readingfound, not routedis a tool whose traffic is not being captured, and that is the usual reason a healthy agent has recorded nothing. Runautoconfig applyto route it.
The console owns each endpoint's
enforcement mode, and the
running daemon knows it. status and policy effective both apply it, so an
endpoint held at Monitoring whose policy has promoted rules prints
MONITOR (console ceiling) followed by what the policy would do, instead of
ENFORCING. A policy that only logs has nothing to hold back and prints the
same at either mode. With the daemon down,
policy effective says the ceiling is unknown. The hook rows in status still
show the guard's own policy.
Findings: one JSON object per interaction
Every inspection appends a line to /var/lib/lumen/findings.jsonl. No matched
value is ever stored, whatever the action: a redacted value is stored as its
placeholder ([REDACTED:credentials]), and a value that was only logged,
including everything in monitor mode, as [MASKED:<label>]. The record keeps
the current turn plus the earlier messages a rule matched, at most 64 KB; a cut
record carries "text_truncated": true and the original size in text_bytes.
tail -f /var/lib/lumen/findings.jsonl | jq -c \
'{ts, stage, action: .action.effective, classes: [.detections[] | select(.matched) | .class], text}'
{"ts":"2026-07-27T16:41:02Z","stage":"input","action":"log",
"classes":["sensitive_data"],
"text":"deploy fails with key [MASKED:credentials.aws_access_key], what should I check?"}
That is the record the built-in policy writes, since it only logs. With
pr_secrets promoted to redact, the same line reads "action":"redact" and
the text carries [REDACTED:credentials], which is also what reached the
provider.
In monitor mode nothing is redacted in flight, so a secret reaches the provider
as typed. The record masks it ([MASKED:credentials.aws_access_key]), but the
surrounding prompt text is still stored, and a file written by an agent
release that predates masking can still hold raw values until it rotates out.
The file is created 0600.
Treat it accordingly, and promote pr_secrets to redact as soon as its
false-positive rate looks acceptable.
Tuning a policy before you enforce
The findings file is the tuning corpus. In monitor mode every detector your
policy references runs and nothing acts, so these queries show what a stricter
policy would have done. A detector no rule references does not run, so it
leaves nothing here to query: add the rule at log first to size it.
# What is being detected, ranked
jq -r '.detections[] | select(.matched) | .class' findings.jsonl | sort | uniq -c | sort -rn
# Which rule would be doing the work
jq -r 'select(.action.effective!="log") | .action.by_rule' findings.jsonl | sort | uniq -c
# Everything that was blocked, and why
jq -r 'select(.action.effective=="block") | "\(.ts) \(.action.by_rule) \(.action.reason)"' findings.jsonl
Promoting a rule is a one-word change in policy.yaml (action: log →
action: redact), the running agent hot-reloads it within about two seconds, and
the same request changes answer with no restart. The full flow, and the second
gate that applies to enrolled machines, is in
From monitor to enforce.
Is it alive?
lumen-agent status # the whole picture, exit 3 if not
curl -s 127.0.0.1:7645/v1/health | jq # the liveness probe installers use
curl -s 127.0.0.1:7645/v1/policy | jq # which policy version is loaded
Service logs
| Platform | Command |
|---|---|
| Linux | journalctl -u lumen-agent -f |
| macOS | tail -f /var/log/lumen/agent.err.log |
| Windows | Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='lumen-agent'} -MaxEvents 25. The service writes its log to the Application event log under the source lumen-agent, which the installer registers. An endpoint last installed by v2.10.0 or earlier needs the installer re-run first. If the service crashed, the reason is in C:\ProgramData\Lumen\state\crash.log (Get-Content C:\ProgramData\Lumen\state\crash.log). Windows is a preview platform |
What a healthy first run looks like
health: ok | version: v2.7.0 | policy: pol_packaged_monitor
verdict: log | the key is AKIAIOSFODNN7EXAMPLE
The credential coming back untouched is the expected first result, not a failure: the shipped policy is monitor mode. The agent saw it, classified it, and recorded it. Promote the rule and the same request changes answer within seconds, with no restart. See From monitor to enforce.