Skip to content

feat(audit): add audit logging with invoker identity tracking#21

Merged
qasim-nylas merged 1 commit intonylas:mainfrom
mqasimca:feat/audit-invoker-identity
Feb 5, 2026
Merged

feat(audit): add audit logging with invoker identity tracking#21
qasim-nylas merged 1 commit intonylas:mainfrom
mqasimca:feat/audit-invoker-identity

Conversation

@mqasimca
Copy link
Contributor

@mqasimca mqasimca commented Feb 5, 2026

Summary

Add a comprehensive audit logging system that tracks CLI command execution with automatic detection of who ran commands and from what source (terminal, AI agents, CI/CD, SSH, etc.).

Key Features

  • Invoker Identity Detection - Automatically detects:

    • claude-code - Anthropic's Claude Code (via CLAUDE_PROJECT_DIR, CLAUDE_CODE_*)
    • github-copilot - GitHub Copilot CLI (via COPILOT_MODEL, GH_COPILOT)
    • ssh - Remote SSH sessions (via SSH_CLIENT)
    • script - Non-interactive automation (TTY detection)
    • terminal - Interactive terminal (default)
    • Custom override via NYLAS_INVOKER_SOURCE env var
  • Nylas API Traceability - Logs request_id from API responses for full request tracing

  • Sensitive Data Protection - Automatically redacts:

    • API keys, tokens, passwords
    • Email body/subject content
    • Long base64 strings (likely tokens)
  • Rich Filtering - Query logs by user, source, command, status, date range, or request ID

  • Export - JSON/CSV export for compliance reporting and SIEM integration

Commands

# Setup
nylas audit init --enable              # Initialize and enable
nylas audit logs status                # Check status

# View logs
nylas audit logs show                  # Recent commands
nylas audit logs show --invoker alice  # Filter by user
nylas audit logs show --source claude-code  # Filter by AI agent
nylas audit logs show --request-id req_abc  # Find by Nylas request ID

# Statistics & Export
nylas audit logs summary --days 30     # Usage statistics
nylas audit export --output audit.csv  # Export for compliance

Example Output

TIMESTAMP            COMMAND       INVOKER      SOURCE        STATUS   DURATION
2026-02-05 10:30:00  email list    alice        claude-code   success  190ms
2026-02-05 10:28:00  auth status   alice        terminal      success  50ms
2026-02-05 10:25:00  email send    jenkins-svc  github-actions success  1.2s

Technical Details

Architecture

cmd/nylas/main.go          → Entry point, wires up LogAuditError
internal/cli/root.go       → Initializes audit hooks (PersistentPreRunE/PostRunE)
internal/cli/audit_hooks.go → Core hooks, invoker detection
internal/cli/audit/        → CLI commands (init, logs, config, export)
internal/adapters/audit/   → JSONL file storage with rotation
internal/domain/audit.go   → Domain types (AuditEntry, AuditConfig)
internal/ports/audit.go    → AuditStore interface, AuditRequestHook

Invoker Detection Logic

func getInvokerIdentity() (invoker, source string) {
    invoker = getUsername()  // os/user.Current() or SUDO_USER
    
    // 1. AI Agents (most specific)
    if os.Getenv("CLAUDE_PROJECT_DIR") != "" || hasClaudeCodeEnv() {
        return invoker, "claude-code"
    }
    if os.Getenv("COPILOT_MODEL") != "" || os.Getenv("GH_COPILOT") != "" {
        return invoker, "github-copilot"
    }
    if override := os.Getenv("NYLAS_INVOKER_SOURCE"); override != "" {
        return invoker, override
    }
    
    // 2. SSH
    if os.Getenv("SSH_CLIENT") != "" {
        return invoker, "ssh"
    }
    
    // 3. Non-interactive (script/automation)
    if !term.IsTerminal(int(os.Stdin.Fd())) {
        return invoker, "script"
    }
    
    // 4. Default: terminal
    return invoker, "terminal"
}

Storage Format

JSONL with daily rotation:

{"id":"abc123","timestamp":"2026-02-05T10:30:00Z","command":"email list","invoker":"alice","invoker_source":"claude-code","status":"success","duration":190000000,"request_id":"req_xyz"}

Files Changed

Category Files Lines
Core audit_hooks.go, domain/audit.go, ports/audit.go ~470
Adapter adapters/audit/store.go, summary.go ~530
CLI audit/*.go (8 command files) ~1,230
Tests audit_hooks_test.go, store_test.go ~1,270
Docs audit.md, COMMANDS.md, INDEX.md ~750
Integration main.go, root.go, client*.go ~70
Total 25 files ~4,320

Test Coverage

  • 60+ unit test cases covering:

    • Invoker detection (Claude Code, Copilot, SSH, terminal, script)
    • Sensitive argument redaction
    • Command path extraction
    • Audit context management
    • File store operations
  • Key functions at 100% coverage:

    • SetAuditRequestInfo, SetAuditGrantInfo
    • auditPreRun, auditPostRun, LogAuditError
    • getCommandPath, isExcludedCommand
    • sanitizeArgs, isLongBase64
    • hasClaudeCodeEnv, initAuditHooks

Security

  • ✅ No hardcoded secrets
  • ✅ Sensitive args automatically redacted
  • ✅ Passes make security and govulncheck
  • ✅ gosec G304 findings are false positives (paths are internally constructed)

Documentation

Comprehensive documentation added:

  • docs/commands/audit.md - 700+ line detailed guide
  • docs/COMMANDS.md - Quick reference section
  • docs/INDEX.md - Navigation updated

Test Plan

  • make ci-full passes
  • Unit tests pass (60+ cases)
  • Security scan clean
  • Manual testing of all commands
  • Test invoker detection in Claude Code environment
  • Test invoker detection in GitHub Actions

Add comprehensive audit logging system that tracks CLI command execution
with automatic detection of who ran commands and from what source.

Features:
- Automatic invoker detection (Claude Code, GitHub Copilot, SSH, scripts)
- Nylas API request ID tracking for full traceability
- Sensitive argument redaction (passwords, tokens, API keys)
- JSONL storage with configurable retention and rotation
- Rich filtering: by user, source, command, status, date, request ID
- Export to JSON/CSV for compliance reporting
- Summary statistics with invoker breakdown

Detection uses documented environment variables:
- Claude Code: CLAUDE_PROJECT_DIR, CLAUDE_CODE_* prefix
- GitHub Copilot: COPILOT_MODEL, GH_COPILOT
- Manual override: NYLAS_INVOKER_SOURCE

Includes 60+ unit tests covering all new code paths.
@qasim-nylas qasim-nylas self-requested a review February 5, 2026 16:32
@qasim-nylas qasim-nylas merged commit bbd77f5 into nylas:main Feb 5, 2026
2 checks passed
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.

2 participants