Agentic Covenants

Detect (DE) · Identity

Identity at the client side layer

deterministic · Outside the model's reasoning

If this concern is breached, how do we know?

What this cell does

PreToolUse hook emits structured identity events; auditd watches agent process startup; Vector/Fluent Bit ships to SIEM.

Artifacts (3)

auditd-agent.rulesview on GitHub
# ABOUTME: auditd rules for agent process startup. Drop in /etc/audit/rules.d/ and reload with augenrules --load.
# ABOUTME: Watches by absolute path; if the agent binary is moved or symlinked elsewhere the rule misses, so re-audit periodically.

# Watch for execve of any agent binary. The -k tag is what audit search uses.
-a always,exit -F arch=b64 -S execve -F path=/usr/local/bin/claude -k agent_exec
-a always,exit -F arch=b64 -S execve -F path=/usr/local/bin/agent-bwrap -k agent_exec
-a always,exit -F arch=b64 -S execve -F path=/usr/local/bin/mcp-launch -k agent_exec

# Watch for credential file reads (ACL should already deny these to the agent
# user, but a rule on the file detects attempts).
-w /etc/agents/ -p ra -k agent_credential_access

# Watch for hook config edits.
-w /etc/agents/hooks/ -p wa -k hook_config_edit
-w /etc/agents/mcp-allowlist.json -p wa -k allowlist_edit

# Watch for --no-verify in any process arg (the agent attempting to bypass
# pre-commit). The short form -n is also caught by the second rule.
-a always,exit -F arch=b64 -S execve -F a1=*--no-verify* -k git_no_verify
-a always,exit -F arch=b64 -S execve -F a1=*-n* -k git_short_no_verify
identity-log-hook.shview on GitHub
#!/usr/bin/env bash
# ABOUTME: PreToolUse hook that emits a structured JSON identity event to local syslog. Designed to chain ahead of pre_tool_use.sh.
# ABOUTME: Hashes the credential before logging; never logs the raw token.

set -euo pipefail

INPUT="$(cat)"
SESSION_ID="$(echo "$INPUT" | jq -r '.session_id // "unknown"')"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name // "unknown"')"
TOOL_INPUT="$(echo "$INPUT" | jq -r '.tool_input.command // ""')"

# Hash the credential. Truncate to 16 hex chars (64 bits) -- long enough that
# collisions across the agent population are negligible, short enough not to
# reveal the full hash in logs.
CRED_HASH="$(printf '%s' "${ANTHROPIC_API_KEY:-}" | sha256sum | cut -d' ' -f1 | head -c 16)"
EFFECTIVE_UID="$(id -u)"
HOSTNAME="$(hostname -f 2>/dev/null || hostname)"
TIMESTAMP="$(date -Iseconds)"

EVENT="$(jq -n \
  --arg session "$SESSION_ID" \
  --arg tool "$TOOL_NAME" \
  --arg input "$TOOL_INPUT" \
  --arg cred "$CRED_HASH" \
  --argjson uid "$EFFECTIVE_UID" \
  --arg host "$HOSTNAME" \
  --arg ts "$TIMESTAMP" \
  '{event: "identity", session: $session, tool: $tool, input: $input, cred_fingerprint: $cred, uid: $uid, host: $host, ts: $ts}')"

logger -t agent-sentinel -p user.info "$EVENT"

# Pass the original input through to the next hook in the chain. PreToolUse
# hooks are expected to echo the input for downstream consumers.
echo "$INPUT"
exit 0
vector.tomlview on GitHub
# ABOUTME: Vector config that ingests local syslog and ships parsed agent-sentinel events to Elasticsearch.
# ABOUTME: Substitute siem.example.com and SIEM_TOKEN. Vector 0.40+ required for the parse_json! built-in used here.

[sources.syslog_local]
type = "syslog"
mode = "tcp"
address = "127.0.0.1:6514"
max_length = 102400

[transforms.parse_agent]
type = "remap"
inputs = ["syslog_local"]
source = '''
  # Only process events tagged "agent-sentinel".
  if .appname != "agent-sentinel" {
    abort
  }
  parsed, err = parse_json(.message)
  if err != null {
    .parse_error = err
  } else {
    . = merge(., parsed)
  }
  .source = "agent-sentinel"
  .ingested_at = now()
'''

# Drop events that failed to parse so we don't pollute the index. The parse
# error is logged at WARN by Vector's internal stats.
[transforms.drop_unparsed]
type = "filter"
inputs = ["parse_agent"]
condition = '''
  exists(.event)
'''

[sinks.siem]
type = "elasticsearch"
inputs = ["drop_unparsed"]
endpoints = ["https://siem.example.com:9200"]
mode = "bulk"
bulk.index = "agent-sentinel-%Y.%m.%d"

[sinks.siem.auth]
strategy = "bearer"
token = "${SIEM_TOKEN}"

# Ship to a local file as a backstop in case the SIEM is unreachable.
# Vector buffers and replays automatically, but this gives us a last-resort
# audit trail on the operator host itself.
[sinks.local_archive]
type = "file"
inputs = ["drop_unparsed"]
path = "/var/log/agent-sentinel/%Y-%m-%d.jsonl"
encoding.codec = "json"
compression = "gzip"

Cell notes

Sentinels, Identity / Client-side

Control. PreToolUse hook emits structured identity events; auditd watches agent process startup; Vector or Fluent Bit ships to SIEM.

Strength. Deterministic when ships actually happen. Tamper-evident only if the SIEM endpoint is owned by the operator account, not the agent's.

Tooling

  • - auditd (Linux) or audit_pipe (macOS).
  • - Vector 0.40+ or Fluent Bit 3.0+.
  • - A SIEM (Splunk, Elastic, Loki, Datadog, Sumo Logic).

Files in this directory

  • - identity-log-hook.sh, PreToolUse hook that emits a structured JSON event with session ID, tool name, credential fingerprint (hash, never the token), effective UID, hostname, timestamp.
  • - auditd-agent.rules, auditd watch rules for agent process startup. Drop in /etc/audit/rules.d/.
  • - vector.toml, Vector config that parses syslog, enriches, and ships to Elasticsearch (substitute your SIEM endpoint).

Verification


# 1. Hook emits log on tool call
echo '{"session_id":"test","tool_name":"Read"}' | ./identity-log-hook.sh
journalctl -t agent-sentinel --since "1 minute ago"
# expected: structured JSON event with credential fingerprint, no raw token

# 2. Audit rule fires on agent start
sudo -u agent-runner /usr/local/bin/claude --version
ausearch -k agent_exec --start recent
# expected: execve event with UID and command line

# 3. Vector ships to SIEM
curl -sS https://siem.example.com:9200/agent-sentinel-*/_search?q=event:identity \
  -H "Authorization: Bearer $SIEM_TOKEN" | jq '.hits.total.value'
# expected: non-zero count

Common mistakes

  • - Logging the raw token instead of a hash. Anyone with SIEM access can replay the agent.
  • - Hook ships logs to local file only. Agent with filesystem access edits the log.
  • - Auditd rule on the binary path; if the agent moves the binary or runs from a different path, the rule misses. Use auditctl -l to confirm rules are loaded.
  • - Credential fingerprint hash too short and collides across agents. Use at least 16 hex chars.

Citation

NIST CSF 2.0 DE.CM-01, DE.CM-09. NIST SP 800-92. NIST NCCoE Concept Paper on Software and AI Agent Identity and Authorization (Feb 5, 2026).

Primary failure modes

Documented, not hypothetical. A control whose bypass is undocumented is worse than no control, because somebody trusted it.

  • logging the raw token instead of a hash
  • hook ships only to local file
  • auditd rule on binary path defeated by binary move

Crosswalk

NIST CSF 2 0DE.CM-01, DE.CM-09
OTHERNIST SP 800-92, NIST NCCoE Concept Paper on AI Agent Identity (Feb 5, 2026)