Estate Sentry Development Roadmap¶
This document tracks all implementation tasks required to build a functional prototype of Estate Sentry with its full architecture including decentralization, the Switchboard, and Sentry Intelligence.
Status¶
Frames move end to end: a producer writes JPEG bytes to MinIO and publishes a
reference on frames.{camera_id}.raw; the perception service fetches, gates on
motion, detects objects, matches them to zones, and appends to the event log.
The dashboard shows live camera tiles and a detection feed.
Built
- Switchboard transport (NATS + MinIO) behind swappable interfaces
- L1 motion gate, L2 object detection (RT-DETRv2, Apache-2.0 — not YOLOv8, which is AGPL-3.0 and would conflict with this project's MIT licence)
- L3 zone intersection, with
zonesandintelligenceapps - HQ dashboard: Django templates + htmx, MJPEG and SSE, no build step
- Live overlay: detection brackets and zone perimeters drawn client-side over the camera tiles, each independently toggleable
- Development producer in
../mock-estate/on the real frame contract - Ingest rate limiting: 60/min per sensor on the readings endpoint
- Redis as the Django cache, so DRF's rate limits hold across worker processes
- JSON Schema validation and size ceilings on every JSONField (
core/) - Append-only security audit log, captured by allowlisted async-capable middleware
- Ed25519 signatures on sensor readings, with timestamp and nonce replay defence
- The API container: builds from the repository root, serves ASGI on three workers, and ships its own static files
Known gap
Seven of the ten sensor types — GLASS_BREAK, MOTION, SMOKE, CO,
WATER_LEAK, TEMPERATURE, CUSTOM — have no entry in the handler map, so
SensorReadingCreateSerializer.validate() falls through and stores whatever
arrives, unchecked and unnormalised, with no threat detection. A smoke detector
reporting smoke currently raises nothing. Pinned by tests in sensors/tests.py
so the behaviour is visible rather than surprising. JSON Schema validation
(Milestone 1) bounds the shape; the handler registry (Milestone 2) closes it.
Not built, deliberately
- L4 identity, L5 action/pose, L6 correlation, L7 threat scoring
- Neo4j and ChromaDB integration
- Remaining Milestone 1 item: certificate binding for trusted devices
Intelligence model split
The intended shape for the analysis layer, recorded here so it is not lost: an open-weights model (Gemma 4 or similar) does the bulk classification and summarisation locally, with a Claude reasoning agent above it for report generation and escalation control. Local inference keeps continuous footage analysis on the user's own hardware — which is the point of a self-hosted security system — while the reasoning agent is invoked only for the small number of events that clear the threat threshold, where its judgement is worth the call.
This is L7 / Sentry Intelligence territory and nothing depends on it yet.
Known measurements
- RT-DETRv2
r18vdon CPU: ~0.42s per frame, about 2.4 fps. - Rate limits and the cache backend. The real
SensorReadingRateThrottle, run 100 times against one sensor from two separate processes — which is what two server workers are. On LocMemCache both processes allowed 60, so a "60/min" limit passed 120; the Dockerfile runs three workers, so it was really 180. On a shared Redis the second process allowed 0 and the total was - DRF counts in the Django cache, so a per-process cache multiplies every limit — including the auth ones — by the worker count, silently.
- End-to-end detection lag, capture to browser: ~1.0s. Was ~4s until the pipeline's inbound queue was shortened to 2 — a deep queue on a slow consumer turns directly into staleness, and the pipeline was analysing frames that had already left the screen. The remaining second is inference plus one cycle of wait, and will not improve without a faster model. Two cameras at 5 fps oversubscribe the pipeline roughly fourfold; the bounded queues drop oldest-first and the producer is never throttled.
Milestone 1: Security Hardening (Critical)¶
Priority: CRITICAL - Must be completed before any production deployment
- [x] Hash PIN storage using Django password hashers (
make_password/check_password) - [x] Create data migration to hash existing plaintext PINs
- [x] Implement
TrustedDevicemodel for username-only authentication - [ ] Add device fingerprint and certificate binding for trusted devices — the token is the credential today; binding it to a client certificate would stop a copied token working from somewhere else. Overlaps Milestone 5, which builds an internal CA for node certificates. Client-certificate binding done now would need a CA of its own plus TLS termination that passes the certificate through — then be built again. A route with no CA at all is proof of possession: the device holds an Ed25519 key and signs its requests, the pattern sensor readings already use. Choose between them before building either.
- [x] Add rate limiting to authentication endpoints, plus a per-account lockout
- [x] Add rate limiting to sensor reading endpoints (60/minute per sensor)
- [x] Add
public_keytoSensorand verify reading signatures — rescued from issue #4, which predates the Django rewrite. Ed25519, so only public keys are stored and a leaked database cannot forge a reading. Enrolled out of band withmanage.py enroll_sensor_key;signed_atand a burned nonce stop a captured reading being replayed - [x] Create
core/validators.pywith JSON Schema validators - [x] Add JSON Schema validation to
connection_configandmetadatafields - [x] Implement
auditDjango app withAuditLogmodel - [x] Add audit middleware for automatic security event capture
- [x] Configure maximum payload sizes for JSONFields
Milestone 2: Handler Registry & Device Abstraction¶
Priority: HIGH - Foundation for extensibility
Scoped 2026-10-12. The registry and the missing handlers come first: seven of
ten sensor types have no handler, so their readings are stored unnormalised and
raise nothing — a smoke detector reporting smoke raises no alert. Two parts are
deferred out of this milestone. The protocol adapters (MQTT, REST, WebSocket)
are the transport question the sensor kit has to answer, and built ahead of it
they would likely be the wrong abstraction. CameraStreamHandler belongs with
the Switchboard: a camera's frames reach the system over the frame contract, not
as readings.
- [x] Create
sensors/registry.pywithHandlerRegistryclass - [x] Implement entry point-based plugin discovery
- [x] Add class-level metadata to
BaseSensorHandler(SENSOR_TYPES,PROTOCOLS,VERSION) - [x] Add
get_schema()method for reading JSON Schema — namedget_reading_schema(), as the plugin guide already published it - [x] Add
get_connection_schema()method for config validation - [ ] Implement
GlassBreakHandler - [ ] Implement
MotionHandler - [ ] Implement
EnvironmentalHandler(smoke, CO, water, temperature) - [ ] Implement
CameraStreamHandlerfor Switchboard integration — deferred to Milestone 3 - [x] Refactor
sensors/views.pyto useHandlerRegistryinstead of hardcoded map - [x] Refactor
sensors/serializers.pyto useHandlerRegistry - [ ] Create
devices/protocols/base.pywithBaseProtocolAdapter— deferred to the sensor kit - [ ] Implement
MQTTProtocolAdapter— deferred to the sensor kit - [ ] Implement
RESTProtocolAdapter— deferred to the sensor kit - [ ] Implement
WebSocketProtocolAdapter— deferred to the sensor kit
Milestone 3: Switchboard¶
Priority: HIGH - Central data routing for all sensor data
Reconciled 2026-10-12. This milestone first described a separate
estate-sentry-switchboard/ service. It was never built that way, and
deliberately: at the reboot the transport became a small library inside
estate-sentry-perception/perception/transport/, behind two protocols —
FrameStore and EventBus — with NATS and MinIO implementations and in-memory
doubles for tests. NATS and MinIO are the switchboard; the code is a thin
client layer that the pipeline and the API both import, so the frame contract
cannot drift between them. Read with the old checklist, everything below looked
unbuilt. The superseded items are kept at the end, struck through.
Built — frames, end to end¶
- [x] NATS with JetStream in Compose (
--jetstream;task switchboard:up) - [x] MinIO in Compose; buckets via
task minio:create-buckets, and the frame store creates its own on demand (ensure_bucket) - [x] Event bus —
NatsEventBus(perception/transport/nats_bus.py) - [x] Frame store —
MinioFrameStore(perception/transport/minio_store.py) - [x] Frame contract —
FrameRef,frame_key(),frame_subject(), onframes.{camera_id}.raw(perception/transport/protocols.py) - [x] In-memory doubles for both, so the pipeline's tests need no infrastructure
- [x] Consumers — the perception service, which posts zone events to the API, and the HQ dashboard's live MJPEG and SSE
Remaining¶
- [ ] Sensor readings over NATS (
sensors.{type}.{id}.readings). Today they go over REST to/api/sensors/{id}/readings/. Moving them would let JetStream absorb outages for devices on the far side of the internet. Conflicts with signing as built: a signed reading is refused oncesigned_atis more than five minutes old, so a device replaying a buffered backlog would be rejected. Design that before readings move to a buffered transport. - [ ] Sensor status subjects (
sensors.{type}.{id}.status) - [ ] Alert fan-out (
alerts.{severity}) - [ ] Switchboard status endpoints in the API — listed under Milestone 9
- [ ] Edge agent — now a "sensor kit": one lightweight daemon per device
hosting several sensors, which would replace
mock-estateas the producer. Not yet specified. First target is the Raspberry Pi 4B test device, whose USB camera emits MJPEG natively (up to 960x720, 15 fps), so capture needs no encode step. Milestone 2's protocol adapters belong to this design rather than ahead of it.
Superseded by the library design¶
Createestate-sentry-switchboard/directory structureCreatesrc/main.py,src/config.py,requirements.txtand aDockerfilefor a Switchboard service— built as the transport package abovemessaging/nats_client.py,storage/minio_client.py,storage/frame_store.py— NATS subjects do the routing; the contract lives inrouting/router.py,routing/topics.pyprotocols.pyAdd frame compression and encoding— moot for MJPEG cameras, which compress in hardware; revisit only for a camera limited to YUYV
Milestone 4: Database Enhancements¶
Priority: MEDIUM - Performance optimization
TimescaleDB¶
- [ ] Add TimescaleDB extension to Docker Compose
- [ ] Create migration to convert
sensor_readingsto hypertable - [ ] Configure chunk time interval (1 day)
- [ ] Add compression policy (compress after 7 days)
- [ ] Add retention policy (configurable, default 1 year)
Event Sourcing¶
- [ ] Create
event_logtable for node synchronization - [ ] Add
vector_clockfield for causality tracking - [ ] Implement event replay functionality
Additional Tables¶
- [ ] Create
media_filestable for MinIO references - [ ] Add indexes for time-range queries
- [ ] Add materialized views for statistics
Milestone 5: Node Architecture¶
Priority: MEDIUM - Decentralization foundation
Django App¶
- [ ] Create
nodesDjango app - [ ] Implement
Nodemodel (uuid, name, zone, role, status) - [ ] Implement
NodeHeartbeatmodel (node, timestamp, metrics) - [ ] Implement
NodeCertificatemodel (node, cert_pem, fingerprint, expires)
Discovery¶
- [ ] Implement mDNS discovery service using
zeroconf - [ ] Create manual node registration endpoint
- [ ] Implement node health check endpoint
gRPC Communication¶
- [ ] Create
nodes/grpc/node.protoprotocol definitions - [ ] Generate Python stubs from proto
- [ ] Implement
nodes/grpc/server.pygRPC server - [ ] Implement
nodes/grpc/client.pygRPC client
Synchronization¶
- [ ] Implement
nodes/sync.pyevent synchronization service - [ ] Add CRDT-based conflict resolution for concurrent updates
- [ ] Implement sync priority tiers (critical, important, background)
Security¶
- [ ] Implement internal CA for node certificates
- [ ] Add mTLS enforcement for inter-node communication
- [ ] Implement certificate rotation
Failover¶
- [ ] Implement leader election for zone primaries (Raft-based)
- [ ] Add automatic failover on primary node failure
- [ ] Configure data replication factor (default: 2)
Milestone 6: Neo4j Integration¶
Priority: MEDIUM - Threat intelligence graphs
- [ ] Create
sentry/graph/neo4j_client.pyclient wrapper - [ ] Implement Django signals to sync data to Neo4j
- [ ] Create
:Sensornodes with properties - [ ] Create
:Readingnodes with temporal relationships - [ ] Create
:Alertnodes with correlation relationships - [ ] Create
:Patternnodes for known threat patterns - [ ] Create
:Locationnodes for spatial analysis - [ ] Implement
GENERATED,FOLLOWED_BY,SIMILAR_TOrelationships - [ ] Create temporal neighbor linking queries
- [ ] Implement alert correlation queries (30-minute window)
- [ ] Add Graph Data Science algorithms for pattern detection
- [ ] Create community detection queries for sensor clusters
Milestone 7: Sentry Intelligence Service¶
Priority: MEDIUM - AI-powered threat analysis
Service Setup¶
- [ ] Create
estate-sentry-sentry/directory structure - [ ] Create
src/main.pyentry point - [ ] Add
requirements.txt(chromadb, mcp, anthropic, etc.) - [ ] Create
Dockerfilefor Sentry service
Vector Store¶
- [ ] Implement
intelligence/vector_store.pyabstraction interface - [ ] Implement
ChromaVectorStoreclass - [ ] Create event embedding schema
- [ ] Implement similarity search for historical patterns
Embeddings¶
- [ ] Implement
intelligence/embeddings.pyevent embedding pipeline - [ ] Create text representation of security events
- [ ] Implement batch embedding for historical data
MCP Server¶
- [ ] Implement
mcp/server.pyMCP server skeleton - [ ] Create
mcp/tools/analyze.pyalert analysis tool - [ ] Create
mcp/tools/correlate.pyevent correlation tool - [ ] Create
mcp/tools/search.pypattern search tool - [ ] Create
mcp/tools/report.pyreport generation tool - [ ] Expose resources via MCP protocol:
sentry://alerts/recentsentry://sensors/statussentry://patterns/known
Threat Analysis¶
- [ ] Implement
intelligence/scoring.pythreat scoring algorithm - [ ] Create severity escalation rules
- [ ] Implement alert correlation engine
- [ ] Add behavioral baseline detection
Case Reports¶
- [ ] Create
reports/generator.pyreport generator - [ ] Create markdown report template
- [ ] Create PDF report template (optional)
- [ ] Implement timeline generation from correlated events
- [ ] Add recommendation generation based on threat score
Integration¶
- [ ] Add Docker Compose configuration for Sentry
- [ ] Create API endpoints for Sentry analysis
- [ ] Integrate with Django alerts for automatic analysis
Milestone 8: Frontend Updates¶
Priority: LOW - Can work with existing frontend initially
- [ ] Add Switchboard WebSocket connection to HQ
- [ ] Implement real-time sensor streaming display
- [ ] Add node status dashboard component
- [ ] Create Sentry analysis panel
- [ ] Add case report viewer component
- [ ] Implement media/frame viewer for camera feeds
- [ ] Add node management interface
- [ ] Create device registration wizard
Milestone 9: API Enhancements¶
Priority: MEDIUM - Documentation and new endpoints
OpenAPI¶
- [ ] Add
drf-spectacularto requirements - [ ] Configure
SPECTACULAR_SETTINGSin settings.py - [ ] Add schema and docs endpoints to urls.py
- [ ] Generate new
openapi.yaml
New Endpoints¶
- [ ] Add
/api/switchboard/status/endpoint - [ ] Add
/api/switchboard/topics/endpoint - [ ] Add
/api/nodes/CRUD endpoints - [ ] Add
/api/nodes/{id}/health/endpoint - [ ] Add
/api/nodes/discover/mDNS discovery endpoint - [ ] Add
/api/sentry/analyze/analysis endpoint - [ ] Add
/api/sentry/correlate/correlation endpoint - [ ] Add
/api/sentry/reports/report generation endpoint
Milestone 10: Testing & Documentation¶
Priority: ONGOING
Testing¶
- [x] Write tests for PIN hashing and authentication security
- [ ] Write tests for handler registry and discovery
- [ ] Write tests for Switchboard message routing
- [ ] Write tests for node discovery and registration
- [ ] Write tests for Sentry threat scoring
- [x] Write integration tests for full sensor → alert pipeline
- [ ] Write integration tests for multi-node sync
- [ ] Add test fixtures for Neo4j graph data
Documentation¶
- [ ] Complete architecture documentation (see docs/ updates)
- [ ] Write plugin development guide with examples
- [ ] Create deployment runbook for production
- [ ] Document backup and restore procedures
- [ ] Create troubleshooting guide
- [ ] Add API changelog
Quick Reference: Priority Order¶
- CRITICAL: Milestone 1 (Security) - Do first, blocks everything
- HIGH: Milestone 2 (Handler Registry) - Foundation for devices
- HIGH: Milestone 3 (Switchboard) - Central to architecture
- MEDIUM: Milestone 4 (Database) - Performance optimization
- MEDIUM: Milestone 5 (Nodes) - Decentralization
- MEDIUM: Milestone 6 (Neo4j) - Intelligence backend
- MEDIUM: Milestone 7 (Sentry) - AI analysis
- MEDIUM: Milestone 9 (API) - Documentation
- LOW: Milestone 8 (Frontend) - Works without changes
- ONGOING: Milestone 10 (Testing) - Throughout development
Dependencies Between Milestones¶
Milestone 1 (Security)
↓
Milestone 2 (Handlers) ──────────────────┐
↓ ↓
Milestone 3 (Switchboard) ←──── Milestone 4 (Database)
↓ ↓
Milestone 5 (Nodes) ─────────────────────┤
↓ ↓
Milestone 6 (Neo4j) ←────────────────────┘
↓
Milestone 7 (Sentry)
↓
Milestone 8 (Frontend) & Milestone 9 (API)
↓
Milestone 10 (Testing) - runs throughout
Notes¶
- Each milestone should be completed in a separate feature branch
- Security (Milestone 1) must be merged before any production deployment
- Switchboard and Sentry are separate services with their own repositories
- Neo4j integration should use Django signals for automatic sync
- All new API endpoints must include OpenAPI documentation
- Tests should be written alongside implementation, not after