Contributing to DaemonEye#
Thank you for your interest in contributing to DaemonEye! This guide will help you get started with contributing to the project.
Table of Contents#
[TOC]
Getting Started#
Prerequisites#
Before contributing to DaemonEye, ensure you have:
- Rust 1.91+: Latest stable Rust toolchain
- Git: Version control system
- Docker: For containerized testing (optional)
- Just: Task runner (install with
cargo install just) - Editor: VS Code with Rust extension (recommended)
Fork and Clone#
-
Fork the repository on GitHub
-
Clone your fork locally:
git clone https://github.com/your-username/daemoneye.git cd daemoneye -
Add the upstream repository:
git remote add upstream https://github.com/EvilBit-Labs/daemoneye.git
Development Setup#
-
Install dependencies:
just setup -
Run tests to ensure everything works:
just test -
Build the project:
just build
Development Environment#
Project Structure#
DaemonEye/
├── procmond/ # Process monitoring daemon
├── daemoneye-agent/ # Agent orchestrator
├── daemoneye-cli/ # CLI interface
├── daemoneye-lib/ # Shared library
├── docs/ # Documentation
├── tests/ # Integration tests
├── examples/ # Example configurations
├── justfile # Task runner
├── Cargo.toml # Workspace configuration
└── README.md # Project README
Workspace Configuration#
DaemonEye uses a Cargo workspace with the following structure:
[workspace]
resolver = "2"
members = [
"procmond",
"daemoneye-agent",
"daemoneye-cli",
"daemoneye-lib",
]
[workspace.dependencies]
tokio = { version = "1.0", features = ["full"] }
clap = { version = "4.6.0", features = ["derive", "completion"] }
serde = { version = "1.0", features = ["derive"] }
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite"] }
sysinfo = "0.38.4"
tracing = "0.1"
thiserror = "1.0"
anyhow = "1.0"
Development Commands#
Use the just task runner for common development tasks:
# Setup development environment
just setup
# Build the project
just build
# Run tests
just test
# Run linting
just lint
# Format code
just fmt
# Run benchmarks
just bench
# Run procmond benchmarks
just bench-procmond
# Generate documentation
just docs
# Clean build artifacts
just clean
Commit Signing and GPG Setup#
Workspace tooling enforces signed commits (git.enableCommitSigning) and a rebase-first sync strategy. Configure GPG before making changes to avoid commit failures.
-
Install GnuPG
- macOS:
brew install gnupg pinentry-mac - Linux: install
gnupgand an appropriatepinentrypackage via your package manager - Windows: install Gpg4win and enable the GPG Agent during setup
- macOS:
-
Generate a signing key (4096-bit RSA recommended)
gpg --full-generate-keyChoose
RSA and RSA, key size4096, set an expiration (or none), and provide your name and email that matches theuser.emailconfigured for Git. -
Locate the long key ID
gpg --list-secret-keys --keyid-format=longCopy the 16-character key ID that appears after
sec rsa4096/. -
Export the public key for review systems
gpg --armor --export <KEY_ID> > ~/.gnupg/daemoneye.pubUpload this armored key to GitHub (Settings → SSH and GPG keys) and any internal key servers used by your organization.
-
Configure Git to sign by default
git config --global user.signingkey <KEY_ID> git config --global commit.gpgsign true git config --global tag.gpgsign true git config --global gpg.program $(command -v gpg)If the workspace settings conflict with per-repo preferences, create a project-local override via
.git/configafter following the required steps. -
Ensure the GPG agent is active
- macOS: add
pinentry-program /opt/homebrew/bin/pinentry-macto~/.gnupg/gpg-agent.conf, then rungpgconf --kill gpg-agent - Windows: restart the "GnuPG Agent" service from the Gpg4win config utility
- Linux: ensure
gpg-agentstarts from your desktop session (eval "$(gpg-agent --daemon)"in shell profile if necessary)
- macOS: add
-
Verify signing works
echo "test" | gpg --clearsign git commit --allow-empty -S -m "test: verify signing"If prompted for a passphrase, select "save in keychain/keyring" so future commits succeed from VS Code as well as the CLI.
Troubleshooting#
gpg: signing failed: No secret key— confirm the key ID ingit configmatchesgpg --list-secret-keysgpg: signing failed: Inappropriate ioctl for device— configure an appropriatepinentryprogram and restartgpg-agent- Emergency opt-out (e.g., broken key) — run
git config commit.gpgsign falselocally and notify the maintainers; automated pipelines still require signatures for merged commits.
These steps ensure developers understand the enforced workflow before VS Code rejects unsigned commits. Refer back to this section any time a workstation is rebuilt.
Code Standards#
Rust Standards#
DaemonEye follows strict Rust coding standards:
- Edition: Always use Rust 2024 Edition
- Linting: Zero warnings policy with
cargo clippy -- -D warnings - Safety:
unsafe_code = "forbid"enforced at workspace level - Formatting: Standard
rustfmtwith 119 character line length - Error Handling: Use
thiserrorfor structured errors,anyhowfor error context
Code Style#
// Use thiserror for library errors
#[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("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("No processes were collected")]
NoProcessesCollected,
}
// Use anyhow for application error context
use anyhow::{Context, Result};
// Helper function to validate collected process data
fn validate_process_data(processes: &[ProcessInfo]) -> Result<(), CollectionError> {
if processes.is_empty() {
return Err(CollectionError::NoProcessesCollected);
}
// Additional validation logic could go here
// For example: checking for required fields, data integrity, etc.
Ok(())
}
pub async fn collect_processes() -> Result<Vec<ProcessInfo>, CollectionError> {
let mut system = sysinfo::System::new_all();
system.refresh_all();
let processes = system
.processes()
.values()
.map(|p| ProcessInfo::from(p))
.collect::<Vec<_>>();
// Validate collected data before returning
validate_process_data(&processes)?;
Ok(processes)
}
// Document all public APIs
/// Collects information about all running processes.
///
/// # Returns
///
/// A vector of `ProcessInfo` structs containing details about each process.
///
/// # Errors
///
/// This function will return an error if:
/// - System information cannot be accessed
/// - Process enumeration fails
/// - No processes could be collected from the system
/// - Memory allocation fails
///
/// # Examples
///
/// ```rust
/// use daemoneye_lib::collector::ProcessCollector;
///
/// let collector = ProcessCollector::new();
/// let processes = collector.collect_processes().await?;
/// println!("Found {} processes", processes.len());
/// ```
pub async fn collect_processes() -> Result<Vec<ProcessInfo>, CollectionError> {
// Implementation
}
Naming Conventions#
- Functions:
snake_case - Variables:
snake_case - Types:
PascalCase - Constants:
SCREAMING_SNAKE_CASE - Modules:
snake_case - Files:
snake_case.rs
Documentation Standards#
All public APIs must be documented with rustdoc comments:
/// A process information structure containing details about a running process.
///
/// This structure provides comprehensive information about a process including
/// its PID, name, executable path, command line arguments, and resource usage.
///
/// # Examples
///
/// ```rust
/// use daemoneye_lib::ProcessInfo;
///
/// let process = ProcessInfo {
/// pid: 1234,
/// name: "example".to_string(),
/// executable_path: Some("/usr/bin/example".to_string()),
/// command_line: Some("example --arg value".to_string()),
/// start_time: Some(Utc::now()),
/// cpu_usage: Some(0.5),
/// memory_usage: Some(1024),
/// status: ProcessStatus::Running,
/// executable_hash: Some("abc123".to_string()),
/// collection_time: Utc::now(),
/// };
/// ```
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ProcessInfo {
/// The process ID (PID) of the process
pub pid: u32,
/// The name of the process
pub name: String,
/// The full path to the process executable
pub executable_path: Option<String>,
/// The command line arguments used to start the process
pub command_line: Option<String>,
/// The time when the process was started
pub start_time: Option<DateTime<Utc>>,
/// The current CPU usage percentage
pub cpu_usage: Option<f64>,
/// The current memory usage in bytes
pub memory_usage: Option<u64>,
/// The current status of the process
pub status: ProcessStatus,
/// The SHA-256 hash of the process executable
pub executable_hash: Option<String>,
/// The time when this information was collected
pub collection_time: DateTime<Utc>,
}
Testing Requirements#
Test Coverage#
All code must have comprehensive test coverage:
- Unit Tests: Test individual functions and methods
- Integration Tests: Test component interactions
- End-to-End Tests: Test complete workflows
- Property Tests: Test with random inputs
- Fuzz Tests: Test with malformed inputs
Test Structure#
#[cfg(test)]
mod tests {
use super::*;
use insta::assert_snapshot;
use tempfile::TempDir;
#[tokio::test]
async fn test_process_collection() {
let collector = ProcessCollector::new();
let processes = collector.collect_processes().await.unwrap();
assert!(!processes.is_empty());
assert!(processes.iter().any(|p| p.pid > 0));
}
#[test]
fn test_process_info_serialization() {
let process = ProcessInfo::default();
let serialized = serde_json::to_string(&process).unwrap();
let deserialized: ProcessInfo = serde_json::from_str(&serialized).unwrap();
assert_eq!(process, deserialized);
}
}
Running Tests#
# Run all tests
cargo test
# Run specific test
cargo test test_process_collection
# Run tests with output
cargo test -- --nocapture
# Run integration tests
cargo test --test integration
# Run benchmarks
cargo bench
# Run fuzz tests
cargo fuzz run process_info
Pull Request Process#
Before Submitting#
-
Sync with upstream:
git fetch upstream git checkout main git merge upstream/main -
Create a feature branch:
git checkout -b feature/your-feature-name -
Make your changes:
- Write code following the coding standards
- Add comprehensive tests
- Update documentation
- Run all tests and linting
-
Commit your changes:
git add . git commit -m "feat: add new feature description"
Commit Message Format#
Use conventional commits format:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types#
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changesrefactor: Code refactoringtest: Test changeschore: Build process or auxiliary tool changes
Examples#
feat(collector): add process filtering by name
Add ability to filter processes by name pattern using regex.
This improves performance when monitoring specific processes.
Closes #123
fix(database): resolve memory leak in query execution
Fix memory leak that occurred when executing large queries.
The issue was caused by not properly cleaning up prepared statements.
Fixes #456
Pull Request Guidelines#
- Title: Clear, descriptive title
- Description: Detailed description of changes
- Tests: Ensure all tests pass
- Documentation: Update relevant documentation
- Breaking Changes: Clearly mark any breaking changes
- Related Issues: Link to related issues
Pull Request Template#
## Description
Brief description of the changes made.
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring
## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] End-to-end tests pass
- [ ] Manual testing completed
- [ ] Performance tests pass
## Checklist
- [ ] Code follows the project's style guidelines
- [ ] Self-review of code completed
- [ ] Code is properly commented
- [ ] Documentation updated
- [ ] No new warnings introduced
- [ ] Breaking changes documented
## Related Issues
Closes #123
Fixes #456
Issue Reporting#
Bug Reports#
When reporting bugs, please include:
- Environment: OS, Rust version, DaemonEye version
- Steps to Reproduce: Clear, numbered steps
- Expected Behavior: What should happen
- Actual Behavior: What actually happens
- Logs: Relevant log output
- Screenshots: If applicable
Feature Requests#
When requesting features, please include:
- Use Case: Why is this feature needed?
- Proposed Solution: How should it work?
- Alternatives: Other solutions considered
- Additional Context: Any other relevant information
Issue Templates#
Use the provided issue templates:
- Bug Report:
.github/ISSUE_TEMPLATE/bug_report.md - Feature Request:
.github/ISSUE_TEMPLATE/feature_request.md - Question:
.github/ISSUE_TEMPLATE/question.md
Documentation#
Documentation Standards#
- Keep documentation up to date
- Use clear, concise language
- Include code examples
- Follow markdown best practices
- Use consistent formatting
Documentation Structure#
docs/
├── src/
│ ├── introduction.md
│ ├── getting-started.md
│ ├── architecture/
│ ├── technical/
│ ├── user-guides/
│ ├── api-reference/
│ ├── deployment/
│ ├── security.md
│ ├── testing.md
│ └── contributing.md
├── book.toml
└── README.md
Building Documentation#
# Install mdbook
cargo install mdbook
# Build documentation
mdbook build
# Serve documentation locally
mdbook serve
Community Guidelines#
Code of Conduct#
We are committed to providing a welcoming and inclusive environment for all contributors. Please:
- Be respectful and constructive
- Focus on what is best for the community
- Show empathy towards other community members
- Accept constructive criticism gracefully
- Help others learn and grow
Communication#
- GitHub Issues: For bug reports and feature requests
- GitHub Discussions: For questions and general discussion
- Pull Requests: For code contributions
- Discord: For real-time chat (invite link in README)
Recognition#
Contributors are recognized in:
- CONTRIBUTORS.md file
- Release notes
- Project documentation
- Community highlights
Development Workflow#
Branch Strategy#
main: Production-ready codedevelop: Integration branch for featuresfeature/*: Feature development branchesbugfix/*: Bug fix brancheshotfix/*: Critical bug fixes
Release Process#
- Version Bumping: Update version numbers
- Changelog: Update CHANGELOG.md
- Documentation: Update documentation
- Testing: Run full test suite
- Release: Create GitHub release
- Distribution: Publish to package managers
Continuous Integration#
All pull requests must pass:
- Unit tests
- Integration tests
- Linting checks
- Security scans
- Performance benchmarks
- Documentation builds
Getting Help#
If you need help contributing:
- Check Documentation: Review this guide and other docs
- Search Issues: Look for similar issues or discussions
- Ask Questions: Use GitHub Discussions or Discord
- Contact Maintainers: Reach out to project maintainers
License#
By contributing to DaemonEye, you agree that your contributions will be licensed under the same license as the project (Apache 2.0 for core features, commercial license for business/enterprise features).
Thank you for contributing to DaemonEye! Your contributions help make the project better for everyone.