Skip to content

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:

  1. iMessage DB listener (or AppleScript bridge)
  2. Monitor ~/Library/Messages/chat.db for new messages
  3. Extract: chat_guid, participants, sender, text, timestamp

  4. Privacy filter (keyword matching + PII redaction)

  5. Check for planning keywords, direct mentions
  6. Redact phone numbers, addresses, emails
  7. Drop casual chat that doesn't need backend

  8. Local scheduler (SQLite for scheduled messages)

  9. Queue of (thread_id, message_text, send_at) tuples
  10. Check every 30 seconds for messages to send
  11. Execute even if backend is offline

  12. Sync protocol with backend (pull commands, send events)

  13. Poll /edge/sync every 60 seconds
  14. Send pending events (message_sent, message_filtered)
  15. Receive commands (schedule_message, cancel_scheduled)
  16. 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/register
  • POST /edge/sync
  • POST /edge/message
  • POST /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



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

  1. Unit tests - Privacy filter, scheduler logic
  2. Integration tests - Sync protocol, command execution
  3. End-to-end tests - Full message flow (user → edge → backend → edge → user)
  4. Failure tests - Backend down, network issues, invalid commands

Backend Integration

  1. Mock edge agent - For backend development
  2. Command queue tests - Verify command dispatch
  3. Event processing tests - Verify event handling
  4. 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