Five-part series · Apache 2.0

DoSync Concepts

The architectural ideas behind DoSync — why AI agents need a different kind of protocol, and what that protocol looks like at each layer.

What is semantic intent? And why it's not the same as a command.

There's a word that keeps appearing in conversations about AI and the physical world: intent. We say things like "the AI understood my intent" or "I want the system to infer my intent from context." But when you actually sit down to build something — a smart home, a hospital room, a hotel — you quickly discover that most protocols don't have a concept of intent at all.

They have commands. And that difference, small as it sounds, is architectural. It changes everything about how you build systems that are supposed to work with AI.

The command model

Every smart home protocol in existence today was designed around one assumption: a human decides what to do, an app translates that decision into a specific instruction, and a device executes it.

Human → App → Command → Device
lock.unlock()
light.set_brightness(80)
thermostat.set_temperature(21)

This is the command model. It works beautifully when the human is in the loop. The problem starts when you remove the human.

What an AI agent actually produces

When an AI system observes the world — a camera detecting a fall, a sensor registering unusual temperature, a model inferring that nobody is home — it doesn't produce a command. It produces an understanding of a situation.

"There is an emergency." "The person who lives here has arrived." "Something is wrong with the refrigerator." These are not commands. They're semantic descriptions of a state of the world. To translate this into device commands, someone has to write that translation — manually, in advance, for every possible situation. Miss one edge case and the system does nothing. Add a new device and you rewrite the rules. This is the command gap.

Semantic intent: a different contract

A semantic intent is a structured expression of a goal — what needs to be achieved — without specifying how to achieve it.

// Command (existing protocols)
{
  "device": "lock-frontdoor-01",
  "command": "unlock",
  "duration": 300
}

// Semantic Intent (DoSync)
{
  "intent": "ensure_safety",
  "urgency": "emergency",
  "context": {
    "trigger": "fall_detected",
    "location": "bedroom"
  }
}

The command says: unlock this specific lock for 5 minutes. The intent says: there is a safety emergency in the bedroom. The difference isn't just syntactic. It's about who holds the knowledge. In the command model, the knowledge of which devices to activate lives in the app or the rules engine — static, unable to adapt. In the intent model, that knowledge lives in the devices themselves, in their Capability Manifests.

How DoSync implements this

In DoSync, every device publishes a Capability Manifest when it joins the network:

{
  "device_id": "lock-frontdoor-01",
  "tags": ["door-lock", "entrance", "emergency"],
  "actuators": [
    { "type": "unlock", "description": "Unlock for emergency access" },
    { "type": "lock" }
  ],
  "emergency_capable": true
}

When an AI agent fires ensure_safety [emergency], the resolver reads every registered manifest and builds an action plan automatically — no rules written, no pre-configuration required. Add a new device tomorrow and it participates automatically in every relevant scenario.

The deeper shift

In the command model, the AI is a remote control. It breaks when a device changes, when a new device appears, or when a scenario wasn't anticipated. In the intent model, the AI is a coordinator. It expresses what needs to happen. The devices figure out their role. The system is resilient to change because the knowledge is distributed — each device owns its own context.

This is not a small difference. It's the difference between building a system that works today and building a system that can grow.

Read on dev.to ↗

Why hardcoded automations fail AI agents.

There's a rule every smart home developer has written at least once:

if motion_detected and time > 22:00:
    turn_on(hallway_light)

It works. It's simple. And it's the foundation of how every major home automation platform thinks about intelligence today. The problem isn't that the rule is wrong. The problem is what happens when you add an AI agent to the system.

The rule-writing problem

Rules are commands in disguise. "If X happens, do Y" is just a delayed command — the human still decided what Y should be, they just decided it in advance. That works perfectly when you can anticipate every scenario, your device set never changes, and the AI's job is simply to trigger pre-approved responses. None of those assumptions hold in a real AI-augmented environment.

Consider what happens when you add a new device. A smart lock arrives. You register it with your platform. Now you have to go back through every relevant rule and add the lock. Miss one and the system silently does nothing in exactly the scenario where you needed it most. This is the brittleness problem: hardcoded automations only respond to situations that were anticipated when the rule was written.

Why AI agents make this worse, not better

The intuitive solution is to write more rules, or smarter rules. But this misses the architectural problem. An AI agent doesn't produce rules. It produces understanding. When a vision model detects a fall, it knows there's an emergency — the translation from understanding to commands still has to be written somewhere, by someone, in advance. The more capable the AI, the more situations it can detect. And every new situation is a new set of rules that needs to be written.

What capability-based discovery changes

The alternative is to shift where the knowledge lives. Instead of centralizing the response logic in rules, distribute it to the devices themselves:

{
  "device_id": "lock-frontdoor-01",
  "tags": ["door-lock", "entrance", "emergency"],
  "actuators": [
    { "type": "unlock", "emergency_capable": true }
  ],
  "emergency_capable": true
}

When the AI fires ensure_safety [emergency], the resolver reads every manifest and asks: which devices declared themselves relevant to this situation? The response assembles at runtime from what's available. Add the smart lock tomorrow — it participates automatically. The device brought its own knowledge.

The difference that matters

Hardcoded automations are fragile because they encode knowledge in a central place that can't keep up with change. Capability-based discovery distributes that knowledge to the edges. For AI agents specifically, this is the difference between a system that only responds to situations you anticipated and a system that responds to situations the AI can detect — whether you anticipated them or not.

Read on dev.to ↗

Event vs intent. The architectural difference that matters.

There's a distinction that comes up constantly when designing systems that connect AI to physical environments, and it's one that most protocols blur or ignore entirely: the distinction between an event and an intent. They look similar on the surface — both are messages, both carry information about the world. But they describe fundamentally different things.

What an event is

An event is a description of something that happened.

{
  "device": "pir-sensor-01",
  "type": "motion_detected",
  "timestamp": 1748000000,
  "location": "entrance"
}

An event is past tense. It's factual. It carries no opinion about what should happen next. The PIR sensor doesn't know whether this motion means "the kids just got home" or "there's an intruder" or "the cat walked by again." It just observed something and reported it — fire and forget. Events are the raw material of a physical system.

What an intent is

An intent is a description of something that needs to happen.

{
  "intent": "children_arrived_home",
  "urgency": "info",
  "context": {
    "trigger": "motion_detected",
    "time": "18:45",
    "day": "monday"
  }
}

An intent is future tense. It carries meaning. It encodes a goal without specifying how to achieve it. The leap from event to intent requires reasoning. Something has to look at the motion event, consider the time, the day, the history, and decide: this event means the kids just arrived. That reasoning is exactly what AI agents are good at.

The agent handled the meaning. The protocol handled the coordination. These are two different jobs — and conflating them is where most smart home AI integrations break.

Why conflating the two breaks systems

Most smart home platforms today are built on a hidden assumption: that the translation from event to action is a simple, static mapping.

IF motion_detected AND time > 18:00 AND day IN [Mon..Fri]
THEN turn_on(light_1), turn_on(light_2), send_sms("kids home")

This works until it doesn't. Add a new light and the rule doesn't know about it. Change the schedule and you rewrite rules. The rule hardcodes both the reasoning and the execution. Separating events from intents breaks this coupling. The AI agent handles the reasoning layer — event to intent. The protocol handles the execution layer — intent to actions.

How DoSync implements this separation

In DoSync, the separation is structural. Events flow in from sensors and adapters — raw, factual, carrying no prescription. Intents flow out from AI agents — meaningful, goal-oriented, carrying no implementation. The hub sits in between: it receives intents, resolves them against every device's Capability Manifest, and builds an action plan at runtime. The resolver never sees the original event. The sensor never sees the resulting actions. The layers are cleanly decoupled — and that decoupling is what makes the system extensible by design.

Read on dev.to ↗

Rules aren't enough once an AI is in the loop.

Picture this: it's 03:00. A camera detects that an elderly person has fallen in the bedroom. The AI agent fires ensure_safety [emergency]. The action plan includes unlocking the front door so emergency services can get in. But someone wrote a rule three months ago: never unlock after midnight. The rule wins. The door stays locked. The paramedics arrive and can't get in.

This is not a bug in the AI. It's not a bug in the rule. It's a design problem — and it's the exact problem that appears when you build physical AI systems on top of automation logic that was never designed for an AI to act on.

What a rule is

A rule is a static, pre-written instruction that maps a condition to an action. Rules work well when the world is predictable, the device set is stable, and a human is always deciding what to do. They've been the foundation of home automation for fifteen years for exactly this reason. Rules fail not because they're a bad idea, but because they were never designed to compose with an autonomous agent that can fire arbitrary intents at arbitrary times.

The difference that matters

A rule answers: what should happen when X occurs? A policy answers: is this action permitted, given who's asking, what they want to do, and when? Consider a front door lock with rules:

# Rule 1: lock at midnight
if time == 00:00:
    lock.lock()

# Rule 2: unlock for delivery
if time > 14:00 and delivery_expected:
    lock.unlock()

These rules handle the scenarios they were written for. But what happens when an AI fires control_access [emergency] at 03:00? What happens when two intents fire simultaneously? Rules don't compose. Each was written in isolation, without knowledge of the others. A policy is different:

NeverAfterHoursPolicy(
    actuator="unlock",
    device_ids=["lock-frontdoor-01"],
    blocked_hours=range(0, 6),
    except_urgency="emergency",  # breakable in genuine emergencies
)

This policy applies to every intent that would unlock the front door, regardless of which AI fired it. It's declared once. It evaluates at runtime. And it has an explicit emergency exception — because in a physical system, the rule "never unlock after midnight" has to be breakable in genuine emergencies.

What a policy engine actually does

In DoSync, every intent passes through the PolicyEngine before execution. The engine evaluates the action plan against all configured policies and returns one of four verdicts: ALLOW, BLOCK, CONFIRM, or MODIFY. The AI never bypasses this layer. This happens between the resolver (which decides what to do) and the executor (which actually does it).

The audit implication

A policy verdict is explicit. Every intent execution in DoSync's audit log records what happened, what the resolver decided, and what the executor did — in a tamper-evident SHA-256 chained record. This matters for post-incident analysis, for regulatory compliance, and for the fundamental accountability question in any AI-augmented physical system: when something goes wrong, can you reconstruct exactly what the system decided and why?

{
  "type": "intent_executed",
  "intent": "save_energy",
  "urgency": "info",
  "actions": 9,
  "success": true,
  "hash": "a3f7...",
  "prev_hash": "9c12..."
}

Rules encode what to do. Policies encode what's permitted. In a physical system with an AI in the loop, you need both — but they operate at different layers and must never be conflated.

The deeper point

Rules encode what to do. Policies encode what's permitted. When an AI agent is in the loop, the judgment gap between what the rules say and what should actually happen has to be filled by the infrastructure. That's what a policy engine is for — not to constrain the AI arbitrarily, but to make the system's safety model explicit, configurable, and auditable.

Read on dev.to ↗

Why your smart home breaks the moment you add an AI agent.

You add a new smoke detector to your home network. It registers with the hub. Thirty seconds later, when someone fires an ensure_safety intent, the detector is part of the response — no automation written, no rule updated, no developer intervention. How is that possible? The answer is capability abstraction — the design decision that separates systems that work with AI from systems that merely tolerate it.

The Capability Manifest — a declaration, not an API

In DoSync, every device publishes a Capability Manifest when it joins the network. Here's a real one from the production deployment — a Philips WiZ bulb running on a Raspberry Pi 5:

{
  "device_id": "wiz-living1-01",
  "device_name": "Living Room — Bulb 1",
  "manufacturer": "Philips",
  "model": "WiZ RGBW Tunable",
  "category": "actuator",
  "tags": ["light", "climate", "smart-plug", "emergency", "wiz"],
  "actuators": [
    { "type": "turn_on",       "description": "Turn on" },
    { "type": "turn_off",      "description": "Turn off" },
    { "type": "set_brightness","description": "Set brightness 0-100%" },
    { "type": "set_color",     "description": "Set RGB color" }
  ],
  "emergency_capable": true,
  "adapter": "wiz",
  "adapter_config": { "ip": "192.168.1.x" }
}

This isn't an API specification. It's a declaration of identity and relevance. The resolver reads every registered manifest, scores each device for relevance — weighting tag overlap, location context, emergency bonus, and actuator match — and builds the action plan. The bulb is included because of its emergency tag and emergency_capable: true — not because anyone hardcoded it.

emergency_capable is a contract, not a flag

When a device declares emergency_capable: true, it's making a commitment to the protocol: I will respond to emergency intents without confirmation. I accept being included in the audit trail as a critical actor. The protocol enforces this contract. Emergency-capable devices bypass the normal policy evaluation flow and are always included as candidates in emergency intent resolution.

Rule-based:  developer writes "if emergency, unlock frontdoor-01"
             → breaks when frontdoor-01 is replaced
             → breaks when a second entrance is added

Capability:  device declares emergency_capable: true
             → protocol enforces the contract
             → survives device replacement, new entrances, new scenarios
             → audit trail is automatic

Why this is different from OpenAPI, MCP, and service registries

OpenAPI and MCP describe interfaces — the exact shape of inputs and outputs. They're precise but context-free. An OpenAPI schema for a door lock tells you the /unlock endpoint accepts a duration_seconds integer. It doesn't tell you that this lock is at the main entrance, that it's relevant in emergencies, or that it should be included in a safety response but not an energy-saving routine.

The Capability Manifest adds semantic context on top of interface description. The tags field isn't a type system — it's a declaration of relevance across scenarios. This distinction exists because AI agents reason at the semantic level, not the API level.

A well-designed manifest answers three questions: what can this device do, where does it fit in the physical space, and does it require special handling in safety scenarios?

The idea worth keeping

Five posts ago, we started with a simple observation: AI agents express goals, not commands. Every post since then has been an answer to the same question — what does a system have to look like, at each layer, to bridge that gap? Semantic intent replaced commands. Policies replaced rules. Events were separated from intents. And now capability abstraction replaces the developer who used to be the translator between device APIs and AI goals.

The translator was always the fragile part. Capability abstraction removes it. The device speaks for itself. The AI listens. The protocol enforces the contracts between them. That's not a smart home feature. That's the infrastructure for any physical environment where AI needs to act reliably.

Read on dev.to ↗

Ready to try it?

DoSync runs on a Raspberry Pi 5 with 41 devices.
It takes three commands to run locally.

No hardware required — the Docker demo starts the hub and registers 8 simulated devices automatically. The full certification suite runs in under 2 minutes.

Try the Docker demo ↗ Read the spec ↗

Reference

Intent classes — open vocabulary

DoSync defines the format of an intent class name, not its vocabulary. Five universal intents are built into every hub. Domain-specific intents (healthcare, industrial, residential) are registered via POST /v1/intent-classes — no code changes required.

Name format: ^[a-z][a-z0-9_]*$ — lowercase, digits, underscores. Urgency (emergency | alert | info) is the only protocol-controlled value.

Intent class Urgency Description Type
ensure_safetyemergencySafety emergency — protect people and property★ universal
alert_anomalyalertUnexpected condition detected — investigate★ universal
control_accessalertManage physical access to a space★ universal
report_statusinfoGenerate a status report of the environment★ universal
notifyinfoPush information to any target★ universal
prepare_operating_roomalertPrepare OR for a procedurehealthcare
line_emergency_stopemergencyEmergency production line shutdownindustrial
morning_routineinfoPrepare the space for the dayresidential
guest_checked_ininfoGuest arrived — configure room to preferenceshospitality