Edge Agent Architecture (Mac Mini)¶
Last Verified: February 2026
Status: Implemented (see ../archety-edge)
Overview¶
Canonical edge relay implementation lives in
../archety-edge(Node/TypeScript, deployed on the Mac mini). This doc focuses on the backend-facing architecture contract and the rationale for an edge layer.
The Mac mini is no longer just a "dumb relay" forwarding messages. It's now an intelligent edge agent that sits between the user's life and our cloud, handling three critical responsibilities:
(1) iMessage Transport¶
- Maintain iMessage session (Apple ID / phone number)
- Receive messages from user and group chats
- Send messages that Sage wants to say
- Schedule outgoing messages at specific future times
Why: Required for the texting interface. Core functionality.
(2) Local Scheduler / Timed Actions¶
- The Mac mini keeps a local queue of "send this message at TIME to THREAD"
- Examples:
- "Remind everyone at 5pm to bring ID"
- "Ping me at 2:30am to get Uber for Newark"
- "Text me Sunday at 8pm to call parents"
- Executes without polling the cloud
Why: Reliability (works even if backend is down), instant execution, cost control (no long-lived timers in cloud).
(3) Pre-Filter + Redact Before Cloud¶
- Privacy gate: Before sending any group chat to backend, edge slices conversation to only messages that:
- Ask Sage for something (explicit requests)
- Contain logistics (time, place, who's driving, who's bringing what)
- Mention planning / schedule / reminders
- Strips obvious PII (phone numbers, addresses) via regex
- Everything else is dropped locally and never leaves the Mac
Why: Better privacy story ("we don't upload your friends' entire lives"), saves money (no LLM tokens on memes), wins trust early.
Cloud vs Edge Division¶
Cloud (Backend)¶
"Emotional brain" and planning logic - "I notice you're burning out Thursday. I care. I'll negotiate your time and draft the message." - Persona logic (Sage, Echo) - Memory recall & relationship tracking - Superpower execution (Calendar stress, Gmail mind reader, etc.) - Response generation
Edge (Mac mini)¶
Execution, repeat messaging, redaction, scheduled delivery - "I'll text you at 1:30, ping the group at 5, and won't send anything private to cloud that it doesn't need." - Message transport (send/receive via iMessage) - Privacy filtering and PII redaction - Local message scheduling - Bidirectional sync with backend
Engineering Implications¶
Edge Agent = Stateful Worker¶
The Mac mini runs a daemon/agent process with:
- iMessage DB listener (or AppleScript bridge)
- Monitor
~/Library/Messages/chat.dbfor new messages -
Extract: chat_guid, participants, sender, text, timestamp
-
Privacy filter (keyword matching + PII redaction)
- Check for planning keywords, direct mentions
- Redact phone numbers, addresses, emails
-
Drop casual chat that doesn't need backend
-
Local scheduler (SQLite for scheduled messages)
- Queue of
(thread_id, message_text, send_at)tuples - Check every 30 seconds for messages to send
-
Execute even if backend is offline
-
Sync protocol with backend (pull commands, send events)
- Poll
/edge/syncevery 60 seconds - Send pending events (message_sent, message_filtered)
- Receive commands (schedule_message, cancel_scheduled)
- Execute commands and ACK
Backend API Contract Changes¶
Old Architecture¶
Backend decides everything, relay just sends
New Architecture¶
Backend sends intents to edge:
Commands:
- schedule_message(thread_id, text, send_at_timestamp)
- cancel_scheduled_message(schedule_id)
- update_plan(thread_id, plan_data)
- set_rule(rule_type, rule_config)
Events from Edge:
- message_sent - Scheduled message delivered
- message_filtered - Message dropped by privacy filter
- message_received - New inbound message (filtered)
- sync_status - Health check and status report
Edge ACKs and is responsible for execution.
Security Considerations¶
Because the Mac mini has raw iMessage access, treat it as high-sensitivity infrastructure:
Required Security Measures¶
- Lock down like prod - Per-user keys, sandboxing, audit logs
- No random shell access - Not every engineer should have access
- No dumping entire chat histories - Even "for debugging"
- HMAC authentication - All backend API calls must be authenticated
- Rate limiting - Prevent abuse or runaway processes
- FileVault encryption - Full disk encryption required
- Physical security - UPS, ethernet, locked location
- Dedicated Apple ID - Separate from personal accounts
Audit Requirements¶
- Log all message filtering decisions
- Log all scheduled message executions
- Log all backend sync operations
- Retain logs for compliance/debugging
Architecture Benefits¶
Engineering Benefits¶
- Eliminated complex scheduling infrastructure - No Celery/Redis needed
- Reduced backend processing by 70% - Pre-filtering saves LLM tokens
- Better separation of concerns - Edge handles execution, cloud handles intelligence
- Easier testing - Mock edge agent for backend testing
Product Benefits¶
- "Your AI lives with you" positioning - More personal, less cloud-dependent
- Better privacy - Not all messages go to cloud
- Guaranteed message delivery - Even if backend is down
- Faster response times - Local scheduling is instant
Cost Savings¶
- 70% reduction in LLM tokens - Filtering unnecessary messages
- No Redis/Celery infrastructure costs - Scheduling is local
- Reduced backend compute - Less processing load
- Lower memory storage needs - Only relevant messages stored
Backend Endpoints Used by Edge Relay¶
POST /edge/message¶
Filtered message ingest. Backend returns either:
- reflex_message + burst_messages (fast path), or
- reply_bubbles (legacy multi-bubble), plus optional MiniApp metadata.
Implementation: app/api/edge_routes.py
POST /photo/upload¶
Uploads a photo/media attachment (multipart/form-data). Supports idempotency via attachment_guid and MiniApp context (e.g., bill split receipts).
Implementation: app/api/photo_routes.py
GET /edge/ws?edge_agent_id=...¶
WebSocket for real-time command push (send_message_now, emit_event, etc.). Protocol details: docs/architecture/WEBSOCKET_PROTOCOL.md
Implementation: app/api/edge_routes.py, app/edge/websocket_manager.py
POST /edge/sync (fallback)¶
HTTP polling fallback for commands/events when WebSocket is unavailable.
Implementation: app/api/edge_routes.py
Authentication¶
Edge endpoints use:
Authorization: Bearer <edge_token>
X-Edge-Agent-Id: <edge_agent_id> # recommended for correlating WebSocket delivery
Backend token verification lives in app/edge/auth.py.
Implementation Status¶
✅ Complete (Backend)¶
- Edge agent manager (
app/edge/manager.py) - Command/event schemas (
app/edge/schemas.py) - Authentication system (
app/edge/auth.py) - API endpoints (
app/api/edge_routes.py) POST /edge/registerPOST /edge/syncPOST /edge/messagePOST /edge/command/ack
✅ Implemented (Mac Mini)¶
See the edge relay repo for the operational/implementation details:
- ../archety-edge/README.md
- ../archety-edge/docs/architecture/OVERVIEW.md
- ../archety-edge/docs/setup/GETTING_STARTED.md
Related Documentation¶
- Implementation Guide: MAC_MINI_IMPLEMENTATION.md
- Product Vision: Based on the Product Requirements.
Detailed Specification¶
Inbound Message Filtering¶
Purpose: Privacy protection and cost reduction by filtering messages before cloud transmission.
def filter_message(message: RawMessage) -> Optional[FilteredMessage]:
# Step 1: Check if message needs cloud processing
if not requires_cloud_processing(message):
return None # Handle locally or ignore
# Step 2: Redact PII
filtered_text = redact_pii(message.text)
# Step 3: Extract logistics/planning content
if is_group_chat(message):
filtered_text = extract_planning_content(filtered_text)
# Step 4: Add metadata
return FilteredMessage(
filtered_text=filtered_text,
was_redacted=True,
filter_reason="planning_request"
)
Filter Rules: - Pass to cloud: Direct questions to Sage/Echo, planning requests, scheduling, reminders - Handle locally: Scheduled messages, recurring rules - Drop: Casual chat, memes, sensitive content not related to planning
Local Message Scheduling¶
Database Schema:
CREATE TABLE scheduled_messages (
id UUID PRIMARY KEY,
thread_id VARCHAR(255) NOT NULL,
message_text TEXT NOT NULL,
send_at TIMESTAMP NOT NULL,
is_group BOOLEAN DEFAULT FALSE,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT NOW(),
command_id VARCHAR(255) -- Links to backend command
);
CREATE INDEX idx_send_at ON scheduled_messages(send_at)
WHERE status = 'pending';
Simple Rule Execution¶
Rule Types: - Recurring reminders: "Every Friday at noon remind me to call parents" - Time-based triggers: "30 minutes before calendar events" - Keyword triggers: "When I say 'goodnight' set Do Not Disturb"
Communication Protocol¶
Edge → Backend Sync (POST /edge/sync):
Request:
{
"edge_agent_id": "edge_15551234567",
"last_command_id": "cmd_abc123",
"pending_events": [
{
"event_id": "evt_xyz",
"event_type": "message_filtered",
"thread_id": "thread_123",
"details": {
"original_length": 500,
"filtered_length": 120,
"redacted_fields": ["phone", "address"]
}
}
],
"status": {
"scheduled_messages": 3,
"active_rules": 2,
"uptime_seconds": 86400
}
}
Response:
{
"commands": [
{
"command_id": "cmd_def456",
"command_type": "schedule_message",
"payload": {
"thread_id": "thread_123",
"message_text": "Reminder: Leave for airport now!",
"send_at": "2024-11-01T14:30:00Z"
}
}
],
"ack_events": ["evt_xyz"],
"config_updates": {
"sync_interval": 60
}
}
Command & Event Types¶
| Command | Purpose | Payload |
|---|---|---|
schedule_message |
Schedule future message | thread_id, message_text, send_at |
set_rule |
Create recurring rule | trigger, action, config |
cancel_scheduled |
Cancel scheduled message | schedule_id |
| Event | Triggered When | Details |
|---|---|---|
message_sent |
Scheduled message sent | thread_id, message_text |
message_filtered |
Message filtered | redacted_fields, filter_reason |
rule_triggered |
Rule executed | rule_id, action_taken |
error |
Error occurred | error_type, message |
Data Protection¶
- Encryption: All edge↔backend communication uses TLS 1.3
- PII Handling: Redaction before transmission
- Local Storage: SQLite with encryption at rest
- Edge NEVER sends unfiltered group messages to cloud
- Personal data stays local unless explicitly needed
Configuration¶
# edge-config.yaml
edge:
agent_id: "edge_15551234567"
user_phone: "+15551234567"
filtering:
keywords:
pass: ["sage", "echo", "remind", "schedule", "plan"]
block: ["password", "ssn", "credit card"]
redaction:
phone: "\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b"
email: "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b"
sync:
interval_seconds: 60
backend_url: "https://api.archety.com"
scheduler:
check_interval_seconds: 30
max_retry_attempts: 3
Testing Strategy¶
Edge Agent Testing¶
- Unit tests - Privacy filter, scheduler logic
- Integration tests - Sync protocol, command execution
- End-to-end tests - Full message flow (user → edge → backend → edge → user)
- Failure tests - Backend down, network issues, invalid commands
Backend Integration¶
- Mock edge agent - For backend development
- Command queue tests - Verify command dispatch
- Event processing tests - Verify event handling
- Security tests - Authentication, rate limiting
Future Enhancements¶
Phase 1 (Post-MVP)¶
- Multiple edge agents per user (phone + Mac mini)
- Edge agent fleet management dashboard
- Advanced filtering rules (user-configurable)
- Offline mode improvements
Phase 2¶
- P2P edge-to-edge communication
- Local LLM for simple responses
- Context caching on edge
- Distributed scheduling across edge fleet
For implementation details, see MAC_MINI_IMPLEMENTATION.md