- INTEGRATION_TEST_PLAN.md: Comprehensive test plan with 10 scenarios - Authentication, config fetch, SNMP polling, metrics submission - Resilience testing (API outage, network interruption, token revocation) - Load testing and 24-hour stability test procedures - Setup instructions for SNMP simulator and real devices - USER_GUIDE.md: Complete deployment and operations guide - Deployment methods: Docker Compose, Podman, Kubernetes, Systemd - Configuration options and environment variables - Network requirements and firewall rules - Troubleshooting common issues - Upgrade and maintenance procedures - Best practices and security considerations - CLAUDE.md: Updated status to reflect all code complete
9.9 KiB
Towerops Agent - Development Notes
This file provides context for Claude Code when working on the Rust agent.
Project Overview
Lightweight Rust agent for remote SNMP polling. Deployed on customer networks to poll local SNMP devices and report metrics to Towerops API via HTTPS.
What's Complete ✅
Architecture & Design
- Complete module structure (12 source files)
- Configuration types matching Phoenix API responses
- Metric types (SensorReading, InterfaceStat) with proper serialization
- Event loop with 5 concurrent tasks (tokio::select!)
- SQLite buffering with 24-hour retention
- Error types and result handling throughout
Core Functionality
-
API Client (
api_client.rs)- fetch_config() - GET /api/v1/agent/config (Protocol Buffers)
- submit_metrics() - POST /api/v1/agent/metrics (Protocol Buffers)
- heartbeat() - POST /api/v1/agent/heartbeat (Protocol Buffers)
- Uses ureq with rustls-tls (30s timeout)
- Full Protocol Buffers integration for all endpoints
-
Storage (
buffer/storage.rs)- store_metric() - Save metrics to SQLite
- get_pending_metrics() - Retrieve unsent metrics
- mark_metrics_sent() - Track submission
- cleanup_old_metrics() - Remove old data
- Last poll time tracking per equipment
-
Scheduler (
poller/scheduler.rs)- Config refresh every 5 minutes
- Metrics flush every 30 seconds
- Heartbeat every 60 seconds
- Cleanup every hour
- Poll check every 5 seconds
-
Executor (
poller/executor.rs)- poll_sensors() - Poll configured sensors
- poll_interfaces() - Poll interface statistics
- Parallel polling with tokio::join!
- Applies sensor divisors
-
Main (
main.rs)- CLI with clap (--api-url, --token, etc.)
- Environment variable support
- Logging with tracing
- Graceful startup/shutdown
Protocol Buffers Integration
- Protobuf Definitions (
proto/agent.proto)- AgentConfig, Equipment, SnmpConfig, Sensor, Interface
- MetricBatch, Metric, SensorReading, InterfaceStat
- HeartbeatMetadata, HeartbeatResponse
- Code Generation (
build.rs)- Uses prost-build to compile protobuf definitions
- Generates Rust types at build time
- API Communication
- Config endpoint: Accepts
application/x-protobuf, decodes response - Metrics endpoint: Encodes batch to protobuf, sends with proper content-type
- Heartbeat endpoint: Encodes metadata to protobuf
- Conversion functions between protobuf and internal types
- Config endpoint: Accepts
Build & Deployment
- Cargo.toml with optimized release profile
- opt-level = "z"
- lto = true
- codegen-units = 1
- strip = true
- Multi-stage Dockerfile (Alpine, ~10-20 MB)
- docker-compose.example.yml
- README with user documentation
- .gitignore and .dockerignore
- GitLab CI/CD configured for Docker Hub
Build Status
✅ cargo build --release - SUCCESS
✅ cargo clippy - 0 warnings, 0 errors
📦 Target size optimized for minimal footprint
🚀 Protobuf integration complete
Testing Gaps
- Unit tests for SNMP client
- Unit tests for storage (SQLite operations)
- Unit tests for API client (mock server)
- Integration test with real SNMP device
Development Workflow
Quick Start
- Build the agent:
cargo build --release
- Run locally (needs Phoenix backend running):
cargo run -- \
--api-url http://localhost:4000 \
--token <get-from-ui> \
--database-path ./test.db
- Watch logs:
RUST_LOG=debug cargo run -- ...
Testing Changes
- Check compilation:
cargo check
- Run tests:
cargo test
- Format code:
cargo fmt
- Check for issues:
cargo clippy
Docker Testing
- Build image:
docker build -t towerops-agent:test .
- Run container:
docker run --rm \
-e TOWEROPS_API_URL=http://host.docker.internal:4000 \
-e TOWEROPS_AGENT_TOKEN=<token> \
-e RUST_LOG=info \
-v $(pwd)/data:/data \
towerops-agent:test
CI/CD Pipeline
Automated builds via GitLab CI (.gitlab-ci.yml):
- Push to branch → test + build with branch tag
- Push to main → test + build + tag as
latest - Create tag (e.g.,
v0.1.0) → test + build + release
Registry: registry.gitlab.com/towerops/towerops-agent
See: DEPLOYMENT.md for complete CI/CD documentation
Integration with Phoenix Backend
API Endpoints (from agent perspective)
GET /api/v1/agent/config
- Headers:
Authorization: Bearer <token> - Response: Equipment list with sensors and interfaces
- Called every 5 minutes
POST /api/v1/agent/metrics
- Headers:
Authorization: Bearer <token> - Body:
{"metrics": [...]} - Response:
{"status": "accepted", "received": N} - Called every 30 seconds with pending metrics
POST /api/v1/agent/heartbeat
- Headers:
Authorization: Bearer <token> - Body:
{"version": "0.1.0", "hostname": "...", "uptime_seconds": 3600} - Response:
{"status": "ok"} - Called every 60 seconds
Getting a Test Token
- Start Phoenix:
mix phx.server - Navigate to:
http://localhost:4000/orgs/:slug/agents - Click "Create New Agent"
- Copy the token (shown only once)
- Use in agent:
--token <copied-token>
Architecture Decisions
Why Tokio?
- Async event loop for efficient I/O
- Multiple concurrent timers (config, metrics, heartbeat)
- Non-blocking SNMP operations via spawn_blocking
Why SQLite?
- Embedded, no external dependencies
- Persist metrics during API outages
- Small footprint (~100 MB for 24h of metrics)
- No configuration needed
Why Rust?
- Small binary size (~10-20 MB with Alpine)
- Low memory usage (<256 MB typical)
- Cross-compile to multiple architectures
- Strong type safety for reliability
Why Async SNMP with spawn_blocking?
- SNMP crate uses synchronous I/O
- spawn_blocking moves sync operations to thread pool
- Keeps main event loop non-blocking
- Allows concurrent polling without blocking other tasks
Common Issues
"Failed to fetch config" Error
Check:
- Is Phoenix backend running?
- Is the token valid (not revoked)?
- Is the API URL correct?
- Is there network connectivity?
High Memory Usage
Check:
- Database size:
ls -lh /data/towerops-agent.db - Are metrics being submitted? (check logs)
- Is cleanup running? (should see log every hour)
Agent Not Showing as Online
Check:
- Is heartbeat working? (check Phoenix logs)
- Check
last_seen_atin database:SELECT last_seen_at FROM agent_tokens WHERE token_hash = ... - Time sync between agent and server
File Organization
towerops-agent/
├── src/
│ ├── main.rs # Entry point, CLI, initialization
│ ├── config.rs # Types matching API responses
│ ├── api_client.rs # HTTP client for Towerops API
│ ├── metrics/
│ │ └── mod.rs # Metric types (SensorReading, InterfaceStat)
│ ├── snmp/
│ │ ├── mod.rs # Module exports
│ │ ├── client.rs # ✅ SNMP client (GET and WALK)
│ │ └── types.rs # SNMP types and errors
│ ├── buffer/
│ │ ├── mod.rs # Module exports
│ │ └── storage.rs # SQLite buffering
│ └── poller/
│ ├── mod.rs # Module exports
│ ├── executor.rs # Poll execution logic
│ └── scheduler.rs # Main event loop
├── Cargo.toml # Dependencies and build config
├── Dockerfile # Multi-stage build
├── README.md # User documentation
└── CLAUDE.md # This file
Dependencies
Key Crates:
tokio- Async runtime with full featuresreqwest- HTTP client (rustls-tls, no default features)rusqlite- SQLite (bundled)serde+serde_json- Serializationsnmp- SNMP operations (v0.2) ⚠️ needs integrationtracing+tracing-subscriber- Loggingclap- CLI argument parsingchrono- Timestampsanyhow+thiserror- Error handlinghostname- Get system hostname
Next Actions
Immediate (for production readiness):
- Add more comprehensive unit tests
- Integration test with mock SNMP device
- Load test with 100 devices
- Stability test (7+ days continuous)
Long-term (nice to have):
- SNMPv3 support
- Agent-side threshold filtering
- Configurable sampling rates
- Agent health metrics endpoint
Resources
- Main Implementation Doc:
/Users/graham/dev/towerops/AGENT_IMPLEMENTATION.md - Next Steps Guide:
/Users/graham/dev/towerops/AGENT_NEXT_STEPS.md - SNMP Crate Docs: https://docs.rs/snmp/0.2.2/snmp/
- SNMP Crate Source: https://github.com/hroi/snmp-rs
Success Criteria
Agent is production-ready when:
- Compiles successfully
- Docker image builds
- API client works (config, metrics, heartbeat)
- SQLite buffering works
- Event loop runs without panics
- SNMP polling works
- Integration testing complete ← CURRENT FOCUS
- Metrics appear in Phoenix database
- Survives 24h API outage
- Uses <256 MB memory with 50 devices
- Runs for 7+ days without issues
Notes for Future Development
Adding New Metric Types
- Add variant to
Metricenum insrc/metrics/mod.rs - Update
metric_type()andtimestamp()methods - Update Phoenix API to accept new type
- Add serialization test
Adding New Configuration Fields
- Update structs in
src/config.rs - Update Phoenix API
build_equipment_config/1 - Consider backwards compatibility
Debugging SNMP Issues
- Set
RUST_LOG=debugto see all SNMP operations - Check IP reachability:
ping <device-ip> - Test SNMP manually:
snmpget -v2c -c public <device-ip> <oid> - Verify community string is correct
- Check firewall rules (UDP port 161)
Last Updated: January 14, 2026 Status: All code complete, integration testing needed Version: 0.1.0 (pre-release)