"We watch from the shadows — our eyes never close."
@GOTCHAS.md
DaemonEye is a silent observability and hunt layer for sensitive, restricted, and air-gapped environments. It turns endpoints into stealth honeypots: passive by default, surgically traceable on demand, and cryptographically auditable. Telemetry never leaves customer control unless explicitly exported by an operator.
This repository contains the open-source Community tier — the agent-side foundation that all higher tiers build upon:
- procmond — privileged process collector, audit ledger, binary hashing
- daemoneye-agent — detection orchestrator, alert delivery, event bus
- daemoneye-cli — operator query interface and rule management
- daemoneye-lib — shared library (config, models, storage, detection, crypto)
- collector-core — SDK for building new collectors in any language
DaemonEye also ships in higher commercial tiers (sold separately, not in this repo). The Community tier is designed to participate in that larger architecture — protobuf IPC contracts, capability negotiation, and store-and-forward patterns are built in from the start so this repo is the real foundation, not a stripped-down afterthought. See evilbitlabs.io for commercial details.
The cornerstone scenario DaemonEye is built around:
- Passive baseline — procmond collects lightweight process metadata, parent/child relationships, and connection tuples into the local store (redb)
- Heuristic trigger — a detection rule (e.g.,
Apache → bash spawn) fires in the agent - Silent trace — a TraceCommand targets the root PID and descendants for focused tracing without host-visible artifacts
- Focused capture — fork/exec, cmdline snapshots, file metadata, socket events — all cryptographically signed
- Cross-host stitching — if a traced process connects to a remote host running DaemonEye, the trace fans out via shared trace_id
- Analyst review — lineage graphs, replayable timeline, forensic SQL queries, signed export packages
- Manual action — analysts decide containment or continued surveillance; DaemonEye avoids automated enforcement
Every architectural decision in this repo — privilege separation, protobuf IPC, hash-chained audit ledger, SQL-based detection DSL, collector-core SDK — exists to serve this workflow.
| Principle | Description |
|---|---|
| Customer sovereignty | All telemetry owned by the customer; no automatic egress to external services |
| Silent observation | Default mode is passive; rules and traces generate no host-visible artifacts |
| Auditable forensics | All events Ed25519-signed, recorded in write-only audit ledger |
| Air-gap friendly | Fully functional offline; signed bundles for rule and update distribution |
| Privacy defaults | Command args masked by default; RBAC for trace initiation |
Source of Truth: Technical requirements for the Community tier live in spec/ and .kiro/specs/daemoneye-core-monitoring/. Higher-tier designs are maintained privately and are not part of this repo.
just fmt # Format all code
just fmt-check # Check formatting (CI-friendly)
just lint # Runs fmt-check + clippy + lint-just
just build # Build all binaries with features
just check # Quick check without build
just test # Run all tests
just run-procmond [args] # Run procmond
just run-daemoneye-cli [args] # Run daemoneye-cli
just run-daemoneye-agent [args] # Run daemoneye-agentcargo build --workspace && cargo test --workspace
cargo clippy --workspace -- -D warnings
NO_COLOR=1 TERM=dumb cargo test --workspace # Stable output
cargo bench # Criterion benchmarks
cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
# GitHub CI monitoring
gh run list --branch <branch> --limit 5 # Check CI status
gh run view <run-id> --log-failed # View failed job logs
gh run view <run-id> --json status,jobs # JSON status checkCommit style: .github/commit-instructions.md
- No Merging: Never merge without a passing CI check and code review approval on a PR. This must be performed by a human maintainer, not an AI assistant.
- No Direct Commits to main: Never commit to main without explicit permission. Work on a feature branch and open a PR for review so all changes go through the proper CI and review process. (Committing to a feature branch during agentic work is expected; merging to main is the gated step — see rule 01.)
- Security-First: All changes must maintain least privilege and undergo security review.
- Zero-Warnings Policy:
cargo clippy -- -D warningswith no exceptions. - Operator-Centric: Prioritize workflows efficient in contested/airgapped environments.
- Documentation: Mermaid for diagrams, relative links, maintain link hygiene.
- Testing Required: All code changes must include appropriate tests.
- Linter Restrictions: Never remove clippy restrictions or
denyattributes. - File Size Limit: Keep source files under 500-600 lines when possible.
- AI Disclosure: Always disclose AI usage in PR descriptions, following the AI Usage Policy AI Usage Policy. Be transparent, but brief — no need to list every prompt, just the tools used (e.g., "Used Claude Code (
Claude Opus 4.7 (1M Context)) for initial draft of detection engine refactor. All code reviewed and tested."). - Compounding Learnings: After a solved, verified problem, automatically invoke the
ce-compoundskill withmode:non-interactiveat the completion checkpoint only when the work produced durable project reasoning that is not readily recoverable from the final code, tests, types, comments, or existing documentation, and losing it would plausibly cause recurrence, material risk, or substantial rediscovery. Apply this counterfactual: if the learning document disappeared, would a future engineer reading the final implementation still be likely to repeat the mistake or redo substantial investigation? If not, do not invoke it. Completion, effort, and diff size alone are not enough. Capture at the checkpoint so a qualifying learning can ship in the PR that produced it, and only where the repository treats captured learnings as tracked, committed knowledge. - Chat Register: Write every report, summary, or handoff to the user through the
ce-noslopskill. This applies when you are the top-level agent writing to the user, not when you are a subagent reporting to its caller. Do not apply it to code, config, verbatim quotes, or text the user asked to post as written.
- Project Rules (AGENTS.md)
- Steering Documents (specs/, .kiro/steering/)
- Technical Specifications (.kiro/specs/)
- Embedded defaults
DaemonEye implements a three-component security architecture with strict privilege separation. This repo contains the host-side components. Commercial tiers extend this foundation with fleet management and centralized aggregation; those components live in separate private codebases and are not described here.
| Component | Privileges | Network | Database | Function |
|---|---|---|---|---|
| procmond | Elevated (drops after init) | None | Write-only (audit) | Privileged process collector |
| daemoneye-agent | Minimal | Outbound-only | Read/write (events) | Detection orchestrator |
| daemoneye-cli | Minimal | None | Read-only | Operator CLI |
| daemoneye-lib | N/A | N/A | N/A | Shared library |
| collector-core | N/A | N/A | N/A | Collector SDK |
flowchart LR
subgraph "Host (this repo)"
P[procmond] -->|IPC protobuf| A[daemoneye-agent]
A -->|reads/writes| DB1[(Event Store)]
P -->|writes| DB2[(Audit Ledger)]
C[daemoneye-cli] -->|reads| DB1 & DB2
end
A -->|outbound alerts| EXT[(Alert Sinks)]
A -.->|optional upstream| UP[External tiers]
The dashed line to "External tiers" indicates that daemoneye-agent's outbound IPC contract is designed to support upstream aggregation in commercial deployments. The upstream components are not part of this repo.
- Privilege Separation: Only procmond runs elevated when necessary
- IPC: Protobuf over Unix sockets/named pipes with CRC32 validation
- No Inbound Network: Outbound-only for alert delivery
- SQL Injection Prevention: AST validation with sqlparser, prepared statements only [Implemented: rule load-time validation, and
RuleExecutorruns rules over DataFusion — seedetection/execution/]
| Category | Technology |
|---|---|
| Language | Rust 2024 Edition (MSRV: 1.95+) |
| Async | Tokio |
| Database | redb (pure Rust embedded) |
| CLI | clap v4 with derive |
| Process Enum | sysinfo |
| Logging | tracing ecosystem |
| Config | YAML/TOML via figment |
| IPC | interprocess + protobuf |
| Event Bus | daemoneye-eventbus + tokio |
| Testing | cargo-nextest, insta, criterion |
| Category | Crate | Version |
|---|---|---|
| Runtime | tokio | 1.0+ |
| Serialization | serde | 1.0+ |
| CLI | clap | 4.0+ |
| Database | redb | 4.2+ |
| Process | sysinfo | 0.37+ |
| Logging | tracing | 0.1+ |
| Errors | thiserror | 2.0+ |
| Errors | anyhow | 1.0+ |
| Crypto | blake3 | 1.8+ |
| Crypto | rs-merkle | 1.5+ |
| Testing | insta | 1.0+ |
| Benchmarks | criterion | 0.7+ |
| OS | Version | Arch | Status |
|---|---|---|---|
| Linux | Ubuntu 20.04+ LTS, RHEL/CentOS 8+, Debian 11+ | x86_64, ARM64 | Primary |
| macOS | 14.0+ (Sonoma) | x86_64, ARM64 | Primary |
| Windows | 10+, Server 2019+ | x86_64, ARM64 | Primary |
| Linux | Alpine 3.16+, Amazon Linux 2+ | x86_64, ARM64 | Secondary |
| FreeBSD | 13.0+ | x86_64, ARM64 | Secondary |
- Edition: Rust 2024 (MSRV: 1.95+)
- Linting:
cargo clippy -- -D warnings(zero warnings) - Clippy/rustdoc lint traps: see GOTCHAS.md §5
- Safety:
unsafe_code = "forbid"at workspace level - Formatting:
rustfmtwith 119 char line length - Errors:
thiserrorfor structured errors,anyhowfor context - Async: Async-first with Tokio
- Logging: Structured with
tracing - Strictness:
warnings = "deny"at workspace;allowrequires justification
cargo clippy -- -D warnings(zero warnings)cargo fmt --all --check(formatting)cargo test --workspace(all tests pass)just lint-just(justfile syntax)- No new
unsafewithout approval - Benchmarks within acceptable ranges
Gotchas: see GOTCHAS.md (pre-commit hook behavior is in §1.3).
- Least privilege: Components run with minimal permissions
- Automatic privilege drop after initialization
- SQL injection prevention: AST validation at rule load time [Implemented]; SQL-based rule execution [Implemented:
RuleExecutorover DataFusion, seedetection/execution/]. Pipeline is two-phase: sqlparser lowers the custom dialect (spec §4.10) at rule-compile time into (a) protobuf collection tasks and (b) derived standard SQL; the runtime executor only sees the derived SQL, never the original dialect. - Credentials: Environment variables or OS keychain, never hardcoded
- No inbound network: Outbound-only for alerts
- Audit trail: BLAKE3 hash-chained audit ledger [Implemented]; Merkle tree inclusion proofs [In Progress —
generate_inclusion_proof()returns empty vec, seecrypto.rs]
- SLSA Level 3 provenance, Cosign signatures [Planned]
- Merkle tree with inclusion proofs [In Progress — chain hashing implemented; inclusion proof generation stubbed in
crypto.rs] - Sandboxed detection engine (read-only DB) [Planned]
- Query whitelist (SELECT only with approved functions) [Implemented: the load-time allowlist refuses any other function call, and the executor registers only those seven functions (
hex,instr,length,like,match,regexp,unhex), replacing DataFusion's defaults. Function-like syntax that parses to its own AST node (SUBSTR,TRIM,POSITION, ...) is refused at load by variant, so nothing loads that the executor cannot run]
Fleet-level transport security (mTLS between host agents and upstream aggregators) is handled in the commercial tiers, not in this repo.
[profile.release]
overflow-checks = true
lto = "thin"
codegen-units = 1Use checked_*, saturating_*, or explicit wrapping_* for security-sensitive calculations.
- Tokio primitives:
Semaphore,mpsc,oneshot,watch,Notify - Lock scope: Never await while holding locks
- Async locks:
tokio::sync::Mutex/RwLockfor async;std::syncfor sync-only - Bounded concurrency: Use
Semaphorewith defined capacity - Linting: Enable
clippy::await_holding_lock = "deny"
| Purpose | Approved |
|---|---|
| Hashing | BLAKE3, SHA2 (never SHA-1) |
| Hash roles | SHA-256 = executable identity (TI interop); ssdeep = binary change detection; BLAKE3 = audit ledger/forensic |
| Signatures | ed25519-dalek |
| AEAD | chacha20poly1305, aes-gcm |
| KDF | HKDF-SHA256, Argon2id |
| TLS | rustls (TLS 1.2+ min, 1.3 preferred) |
| Entropy | getrandom |
| Secrets | secrecy, zeroize crates |
- Treat all external inputs as untrusted
- Validate early, reject with actionable errors
- Use typed parsers over regex
- Length limits on all variable-length inputs (Unix socket-path limit: GOTCHAS.md §4.1)
- SQL: AST validation with
sqlparser[Implemented at rule load time; at execution the session registers only the allowlisted functions, seedetection/execution/session.rs]
Use newtypes for domain constraints (ports vs PIDs vs timestamps). Implement TryFrom with validation.
- Commit
Cargo.lock, useresolver = "3" - Pin security-critical deps, avoid wildcards
default-features = false, enable only needed- Git deps: pin to commit SHA
- Tools:
cargo audit,cargo deny,cargo vet
| Metric | Target |
|---|---|
| CPU Usage | < 5% sustained |
| Memory | < 100 MB resident |
| Process Enum | < 5s for 10,000+ processes |
| DB Writes | > 1,000 records/sec |
| Alert Latency | < 100ms per rule |
| Query Response | Sub-second for 100k+ events/min |
These are targets for a reference host, not hard bounds. Resident memory in particular scales with the deployment: a large, busy server may sit higher and a constrained embedded or SCADA endpoint lower. Size the tunables that govern it to the host rather than treating the number as a pass/fail gate.
- Bounded channels with backpressure
- Memory budget enforcement
- Cancellation tokens for graceful shutdown
- Circuit breakers for external dependencies
- Graceful degradation when constrained
DaemonEye/
├── Cargo.toml # Workspace root
├── procmond/ # Privileged collector
├── daemoneye-agent/ # Detection orchestrator
├── daemoneye-cli/ # Operator CLI
├── daemoneye-lib/ # Shared library
│ ├── proto/ # Protobuf definitions
│ └── src/
│ ├── alerting.rs # Alert delivery
│ ├── config.rs # Configuration
│ ├── crypto.rs # Cryptographic functions
│ ├── detection.rs # SQL detection engine
│ ├── models.rs # Data structures
│ └── storage.rs # Database (redb)
├── collector-core/ # Collector framework
├── tests/ # Integration tests
├── docs/solutions/ # Documented solutions to past problems (bugs, best practices, workflow patterns), by category with YAML frontmatter (module, tags, problem_type) — relevant when implementing or debugging in documented areas
├── CONCEPTS.md # Shared domain vocabulary (entities, named processes, status concepts) — relevant when orienting to the codebase or discussing domain concepts
└── .kiro/ # Steering docs & specs
#[async_trait]
pub trait ProcessCollectionService: Send + Sync {
async fn collect_processes(&self) -> Result<CollectionResult, CollectionError>;
}
#[async_trait]
pub trait AlertSink: Send + Sync {
async fn send(&self, alert: &Alert) -> Result<DeliveryResult, AlertingError>;
fn name(&self) -> &str;
async fn health_check(&self) -> Result<(), AlertingError>;
}| Tier | Scope | Tools |
|---|---|---|
| Unit | Individual components | tokio-test, trait mocking |
| Integration | Cross-component | insta, predicates, real redb |
| E2E (Optional) | Full system workflows | Test data seeding |
- Runner: cargo-nextest (parallel, failure isolation)
- Coverage: llvm-cov (target: >85%)
- Property: proptest for edge cases
- Fuzz: Security-critical components
- Snapshot: insta for CLI output
NO_COLOR=1 TERM=dumb cargo test --workspace # Stable output
RUST_BACKTRACE=1 cargo test -p daemoneye-lib --nocapture # Debug
cargo bench --baseline previous # Regression- Platforms: Linux, macOS, Windows
- Rust: stable, beta, MSRV (1.95+)
- Architectures: x86_64, ARM64
| Table | Access | Component |
|---|---|---|
| processes | R/W | agent |
| scans | R/W | agent |
| detection_rules | R/W | agent |
| alerts | R/W | agent |
| alert_deliveries | R/W | agent |
| audit_ledger | W (procmond), R (others) | procmond |
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessInfo {
pub pid: u32,
pub ppid: Option<u32>,
pub name: String,
pub executable_path: Option<String>,
pub command_line: Option<String>,
pub start_time: Option<DateTime<Utc>>,
pub cpu_usage: Option<f64>,
pub memory_usage: Option<u64>,
pub status: ProcessStatus,
pub executable_hash: Option<String>, // SHA-256
pub collection_time: DateTime<Utc>,
}
#[derive(Debug, Error)]
pub enum CollectionError {
#[error("Permission denied accessing process {pid}")]
PermissionDenied { pid: u32 },
#[error("Process {pid} no longer exists")]
ProcessNotFound { pid: u32 },
#[error("Database operation failed: {0}")]
DatabaseError(String),
#[error("IPC communication failed: {0}")]
IpcError(String),
}#[derive(Parser)]
#[command(name = "procmond", about = "Process monitoring daemon")]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[arg(short, long)]
pub config: Option<PathBuf>,
#[arg(short, long, default_value = "info")]
pub log_level: String,
}- Use
--jsonfor machine-readable output - Respect
NO_COLORandTERM=dumb - Provide actionable error messages
- Command-line flags
- Environment variables (component-namespaced:
DAEMONEYE_AGENT_*,DAEMONEYE_CLI_*,PROCMOND_*) - User config (
~/.config/daemoneye/config.toml) - System config (
/etc/daemoneye/config.toml) - Embedded defaults
- Trait-based plugin system (
AlertSinktrait) - Concurrent delivery to multiple sinks
- Retry with exponential backoff
- Circuit breaking for failing sinks
- Delivery audit trail
- GitHub Actions: Linux, macOS, Windows matrix
- Rust: stable, beta, MSRV (1.95+)
- Checks: fmt, clippy strict, tests, benchmarks
- Security: Dependency scanning, SLSA provenance
- Scanner gotchas: see GOTCHAS.md §3.4
- Primary tool: coderabbit.ai
- Security focus, performance assessment
- Single maintainer: UncleSp1d3r
- Format: Conventional Commits
- Types:
feat,fix,docs,style,refactor,perf,test,build,ci,chore - Breaking:
!in header orBREAKING CHANGE:in footer
Files: daemoneye-lib/src/detection.rs, daemoneye-lib/src/models.rs, daemoneye-agent/src/rules/, tests/integration/
- Define rule structure with SQL and metadata
- Implement AST validation (sqlparser)
- Unit tests with mock data
- Integration test via agent
- Validate alert delivery via CLI
-
cargo clippy -- -D warningspasses - Performance test if complex
- Rustdoc documentation
Files: daemoneye-cli/src/main.rs, daemoneye-lib/src/config.rs, tests/integration/
- Update clap derive structures
- Configuration handling in lib
- Help text and defaults
- Insta snapshot tests
- Test short/long forms
- Validate with
NO_COLOR=1 TERM=dumb - Update shell completions
- Rustdoc documentation
Files: daemoneye-lib/src/storage.rs, procmond/src/collector.rs, benches/
- Establish criterion baseline
- Implement optimization
- Batch processing if applicable
- Validate >1000 records/sec
- Test with 10k+ processes
- Memory profiling
- ACID guarantees maintained
- Document characteristics
DaemonEye is the Community tier of an open-core product. Content for commercial tiers lives in separate private codebases and Confluence — not here.
- No paid-tier feature enumerations (Business/Enterprise specifics, Security Center internals, fleet-management details, STIX/TAXII, federation)
- No internal-only URLs (private Confluence hyperlinks, Jira ticket IDs, internal Slack references, pricing specifics)
- No references to deleted files (sweep for
pricing.md,feature-tiers.md,business-tier.md,enterprise-tier.md,product_strategy.md,spec/product.md,spec/procmond/) - Boundary-acknowledgement footnotes preferred over erasure ("commercial tiers extend this foundation, sold separately, not in this repo")
- Never delete a repo doc without first verifying the Confluence copy fully matches — see docs/solutions/workflow-issues/open-core-hygiene-confluence-migration-2026-04-18.md for the eight-phase workflow
When generating code:
- Use Rust 2024 Edition
- Comprehensive error handling with thiserror
- Async/await with Tokio
- Tests with insta for snapshots
- Service layer pattern with traits
- Logging with tracing
- Workspace inheritance for deps
- Performance considerations
- Security best practices
- Rustdoc for public APIs
| Section | Source |
|---|---|
| Architecture | .kiro/steering/structure.md |
| Technology | .kiro/steering/tech.md |
| Product | .kiro/steering/product.md |
| Core Requirements | .kiro/specs/daemoneye-core-monitoring/requirements.md |
| Development | .kiro/steering/development.md |
| SQL-to-IPC Pipeline | spec/daemon_eye_spec_sql_to_ipc_detection_architecture.md |
| Detection Execution (binding) | ADR-0006: Apache DataFusion over redb TableProviders (spec §9/§11). Detection-engine requirements are R17–R24 in the core spec (merged 2026-06-09; the former upcoming-specs/sql-to-ipc-detection-engine/ is deleted) |
| Architecture Decision Records | docs/adr/ for ADRs recorded in this repo; ADR-0001 through ADR-0007 live in the Confluence ES space and are cited by number only |
| v1.0 Scope | Priority Tiers table in .kiro/specs/daemoneye-core-monitoring/requirements.md — R1–R10 are the v1.0 bar |
- Cursor IDE: .cursor/rules/
- Security: .cursor/rules/security-standards.mdc
- Rust: .cursor/rules/rust-standards.mdc
| Term | Definition |
|---|---|
| AST | Abstract Syntax Tree (SQL validation) |
| BLAKE3 | Cryptographic hash for audit trails |
| CEF | Common Event Format |
| eBPF | Extended Berkeley Packet Filter |
| ETW | Event Tracing for Windows |
| IPC | Inter-Process Communication |
| redb | Pure Rust embedded database |
| SLSA | Supply-chain Levels for Software Artifacts |
Remember: DaemonEye is security-focused. Prioritize security, performance, and reliability. When in doubt, choose the more secure and observable approach.
@.tessl/RULES.md follow the instructions