Skip to content

feat: session management with inventory, export, and AI analysis#71

Merged
garagon merged 20 commits intomainfrom
feat/session-management
Mar 20, 2026
Merged

feat: session management with inventory, export, and AI analysis#71
garagon merged 20 commits intomainfrom
feat/session-management

Conversation

@garagon
Copy link
Copy Markdown
Contributor

@garagon garagon commented Mar 20, 2026

Summary

Major feature PR: session management, Aguara v0.9.1 upgrade, and NemoClaw-inspired egress policies.

Session Management (Phases 1, 2, 5)

  • /dashboard/sessions page with stats grid, search bar, threat filter (All/With threats/Clean)
  • QuerySessions() aggregation, idx_audit_session index
  • JSON/CSV export at /dashboard/api/sessions/export
  • AI session analysis: direct Claude/OpenAI call with enterprise prompt
  • Analysis persisted in DB with model name and timestamp (audit evidence)
  • 2-column trace layout: timeline left, AI analysis right
  • Server-side markdown rendering for analysis output
  • LLM hot-reload: configure via dashboard without restart

Egress Per-Tool Restrictions (NemoClaw-inspired)

  • tool_restrictions on EgressPolicy: map tool names to allowed domains
  • ToolDomainAllowed() checks tool-specific restrictions
  • Prevents exfiltration via tool switching (Bash reaching WebFetch-only domains)

Integration Presets (NemoClaw-inspired)

  • 16 built-in presets: Slack, GitHub, Telegram, Discord, Jira, Linear, Notion, Stripe, OpenAI, Anthropic, Supabase, Firebase, npm, PyPI, Docker, HuggingFace
  • integrations: ["slack", "github"] on agent egress config auto-loads domains

Aguara v0.9.1 Upgrade

  • Context-aware scanning with ScanContentWithTool()
  • NFKC normalization delegated to Aguara
  • ApplyToolScopedOverridesPostAguara() to avoid double-filtering
  • Integration test timeout fix, flaky auditcheck test fix

Files changed (26 files, +1220/-118)

Test plan

  • go test ./... all green
  • go build ./... clean
  • CI passes
  • Manual: /dashboard/sessions with search and threat filter
  • Manual: session trace with AI analysis (2-column, persisted)
  • Manual: JSON/CSV export
  • Manual: egress tool_restrictions in oktsec.yaml
  • Manual: integrations preset loading

garagon added 20 commits March 20, 2026 06:41
Aguara v0.9.0+ moves NFKC normalization, tool exemptions, and scan
profiles into the core engine. This delegates those responsibilities
from oktsec to Aguara and simplifies the security pipeline.

Changes:

Engine (internal/engine/scanner.go):
- Remove manual NFKC normalization (Aguara handles internally)
- Remove golang.org/x/text/unicode/norm dependency
- Add ScanContentWithTool() and ScanContentAsWithTool() methods
  that pass tool name to Aguara via WithToolName option, enabling
  built-in tool exemptions (TC-005 on Edit, MCPCFG_004 on WebFetch)

Verdict (internal/verdict/verdict.go):
- Refactor ApplyToolScopedOverrides into shared internal function
- Add ApplyToolScopedOverridesPostAguara() that skips built-in
  exemptions (already applied by Aguara) while preserving
  ContentTools filtering, NLP_ exemption, and user overrides

Pipeline (hooks + gateway):
- Hooks handler: ScanContent -> ScanContentWithTool(ctx, content, ev.ToolName)
- Gateway tool calls: ScanContent -> ScanContentWithTool(ctx, content, m.OriginalName)
- Gateway response scan: same migration
- All three switch to PostAguara variant for tool-scoped overrides

Gateway API (internal/gateway/gateway.go):
- NewGateway signature: variadic ...* audit.Store -> plain *audit.Store

Code quality (/simplify):
- Merge redundant agent collection loop in trace.go
- Replace manual slice reverse with slices.Reverse
- Replace manual byte reverse in avatar_test.go with slices.Reverse

Fix: auditcheck OpenClaw host scanning
- TestRunChecks_SecureConfig was flaky because it scanned real
  ~/.openclaw/openclaw.json from the host
- Add openClawScanFunc hook (same pattern as mcpScanFunc) for
  test isolation
Aguara v0.9.0+ uses Aho-Corasick for pattern matching, which has a
one-time automaton build on first scan. On CI runners this can push
the integration test suite past the 120s timeout. Increase to 300s.
Phase 1 - Session Inventory:
- Add idx_audit_session index on session_id for query performance
- Add QuerySessions() with aggregated stats (events, agents, blocks,
  quarantines, flags, duration, risk score) per session
- Add SessionSummary model and SessionTracer interface update
- New /dashboard/sessions page with table, filters (24h/7d/30d),
  and summary stats (total sessions, threats, avg duration)
- Sessions nav link in sidebar

Phase 2 - Session Export:
- GET /dashboard/api/sessions/export?format=json&range=24h
- GET /dashboard/api/sessions/export?format=csv&range=24h
- CSV with headers, JSON array of SessionSummary objects

Phase 5 - AI Session Analysis:
- POST /dashboard/api/sessions/{id}/analyze endpoint
- Uses existing LLM Analyzer (Claude/OpenAI/webhook) synchronously
- Specialized prompt for session-level analysis: intent, threats,
  anomalies, policy recommendations
- "Analyze with AI" button on session trace page with inline result
- Queue.Analyzer() getter for direct synchronous access

Code quality:
- Extract parseTS() as package-level function in audit store
  (was duplicated as closure in trace.go)
The LLM queue was only created at startup. If configured via the
dashboard UI, Test Connection and Analyze with AI would fail until
restart. Now handleSaveLLM and handleToggleLLM call reloadLLMQueue()
to create the queue in memory immediately after saving config.
Analyzer.Analyze() has its own security system prompt and expects
structured JSON output. Session analysis needs a simple text prompt
with a plain text response. Use direct Claude/OpenAI API calls
reading provider config from cfg.LLM.
- Save AI analysis to reasoning_log (audit_entry_id='session-analysis')
  so it survives page refresh and serves as audit evidence
- Load saved analysis on session trace page load
- 2-column layout: timeline left, AI analysis right (sticky panel)
- Falls back to single column when no analysis exists or on mobile
- Re-analyze button replaces the saved analysis
- Analysis stored with SHA-256 hash for integrity
- Add mdToHTML template function for server-side markdown rendering
  (bold, headers, lists, inline code) without external JS dependencies
- Session trace: step headers wrap instead of clipping (flex-wrap)
- Session trace: AI panel renders formatted HTML instead of raw markdown
- Sessions table: fixed column widths, text-overflow ellipsis,
  right-aligned numeric columns, better timestamp truncation
…ner analysis

- Remove "AI GENERATED" badge and "AI ANALYSIS" heading from trace panel
- LLM prompt instructs no title/heading, starts with Objective directly
- Sessions table: agents shown as pills instead of comma-separated text
- Timestamps in monospace with better truncation
- Add split template function for agent pill rendering
…rompt

Sessions table:
- Session name shown full (not truncated), with timestamp below
- Search bar filters by session ID, agent, or status
- Removed fixed table-layout for natural column sizing
- Agent pills with better spacing

Session trace:
- AI analysis moved above timeline as full-width block
- Removed 2-column layout (analysis is context, not a sidebar)

AI prompt rewritten for enterprise SOC:
- Risk Level (CRITICAL/HIGH/MEDIUM/LOW/CLEAN) as first line
- Focus on WHAT TO DO, not description
- Recommends suspension, restriction, escalation when warranted
- "No action needed" for false positives with explanation
- Actionable enough for a CISO to act without reading the timeline
Sessions table:
- Session name shown in full (word-break, not truncated)
- Timestamp + duration as subtitle below session name
- Removed redundant Duration column (now in session cell)
- 5 columns instead of 7 for cleaner proportions

Session trace:
- Restore 2-column layout (timeline left, AI analysis right)
- Analysis panel sticky on scroll

AI prompt rewritten for business audience:
- No em dashes, no jargon (red-team, SOC, IOC)
- Actions link to oktsec dashboard pages (suspend agent, review
  quarantine, add rule) so manager can act in one click
- Markdown links [text](/dashboard/...) rendered as clickable hrefs
- Only internal /dashboard/ links allowed for safety
…lacement

Prompt improvements:
- Timeline entries tagged [HUMAN] or [AGENT] so LLM correctly identifies
  who the threat actor is (human who injected, not agent who blocked)
- Existing detection rules passed to prompt so LLM does not suggest
  adding rules for patterns already covered
- Explicit instruction: suspend the sender of malicious content, not the
  agent that protected the system

UI:
- "Analyze with AI" button always visible next to CSV/SARIF/JSON exports
- Shows "Re-analyze" when analysis exists, "Analyze with AI" when not
Sessions table:
- 7 columns: Session, Date, Duration, Agents, Events, Threats, Risk
- Date formatted as "Mar 19 16:57" instead of raw ISO timestamp
- Session name truncated at 24 chars with tooltip for full name
- Duration as separate column for sortability

Session trace:
- Removed Re-analyze from footer (only in top actions bar)
- Top button shows "Analyze with AI" or "Re-analyze" based on state

AI prompt:
- No single hyphens as separators in sentences (use commas/periods)
Session trace:
- Stats box uses flex-wrap so it does not clip on narrow viewports
- Right column shows "Analyze with AI" CTA when no analysis exists
- Analysis footer shows model name and timestamp for audit evidence
- Model stored in reasoning_log.tool_use_id field

Data layer:
- QuerySessionAnalysis returns SessionAnalysisResult with Text, Model, Timestamp
- SaveSessionAnalysis accepts model parameter
Layout:
- Breakpoint adjusted to 1100px for 2-column layout

AI prompt:
- Explicit instruction: name after [HUMAN] is user account, name after
  [AGENT] is agent. Never suspend a name that only appears in [AGENT]
  entries. A name can appear in both roles.
- "Suspend user X" must reference a [HUMAN] entry sender
Layout:
- Grid columns 3fr/2fr (was 1fr/1fr) so timeline gets more space
  and stats box does not clip

AI prompt:
- Prepend explicit roster: "Human users: Gus (@guuuuus). AI agents:
  openclaw-main" before the timeline so the LLM cannot confuse roles
- Instruction: "Only suspend names listed as Human users above"
New order: Agents, Session, Threats, Events, Risk, Duration, Date.
Agents first for quick visual scan, threats prominent, metadata last.
Stats box: 4-column grid with borders (With Threats, Sessions,
Total Events, Avg Duration) matching the mockup layout.

Table columns reordered: Session, Agents, Threats, Events, Risk,
Duration, Date.

Added threat filter buttons (All / With threats / Clean) next to
the search bar. Filters combine with text search.
- Active sidebar item is now "sessions" instead of "events" when
  viewing a session trace
- Remove the empty "AI analysis not yet generated" placeholder box.
  The Analyze with AI button in the top actions bar is sufficient.
Stats box uses 4-column grid with vertical borders matching the
sessions page layout. Reordered: Threats, Tool Calls, Duration, Started.
Inspired by NVIDIA NemoClaw's policy model.

Egress per-tool:
- New ToolRestrictions field on EgressPolicy maps tool names to
  allowed domains. Empty list = no egress for that tool.
- ToolDomainAllowed() checks tool-specific restrictions before
  falling back to general DomainAllowed()
- Prevents exfiltration via tool switching (e.g. using Bash to
  reach a domain only WebFetch should access)

Integration presets:
- New Integrations field on EgressPolicy: ["slack", "github", etc.]
- 16 built-in presets with domain allowlists for common services:
  Slack, GitHub, Telegram, Discord, Jira, Linear, Notion, Stripe,
  OpenAI, Anthropic, Supabase, Firebase, npm, PyPI, Docker, HuggingFace
- ResolveIntegrationDomains() expands preset names to domain lists
- Preset domains merged with agent's explicit allowed_domains

Example config:
  agents:
    my-agent:
      egress:
        integrations: ["slack", "github"]
        tool_restrictions:
          WebFetch: ["arxiv.org", "api.github.com"]
          Bash: []  # no egress
@garagon garagon merged commit 72af9a2 into main Mar 20, 2026
1 check passed
@garagon garagon deleted the feat/session-management branch March 20, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant