Skip to content

design(local-capability): define external Adapter Protocol for participant-local capabilities #514

Description

@madawei2699

Status

FRESH-AGENT AUTHORING PROOF COMPLETE — PR #519 merged; next gate is local source-built Runtime registration against the real CUPS Adapter, then release and Room acceptance.

Related:

Decision to freeze

Free4Chat Runtime should not become a hardware integration host.

Canonical boundary:

Room
  ↓
Free4Chat Core / Runtime
  ↓
Local Capability Adapter Protocol
  ↓
Adapter
  ↓
vendor API / Home Assistant / IPP / BLE / serial / USB / LAN / SDK / whatever

The key rule is:

Runtime knows capabilities, not integrations.

More explicitly:

Runtime owns capability transport, authority, bounds and lifecycle. Adapter owns integration, vendor protocol, local configuration and credentials.

A Xiaomi token, Home Assistant token, printer password, BLE pairing key, MQTT credential, serial configuration or vendor account must not become a Free4Chat Runtime credential/configuration type merely because an Adapter needs it.

Why this is needed

#512 proves a bounded semantic capability can be projected from a participant-local Runtime into a Room:

Human deterministic invocation
        ┐
        ├→ daemon-owned semantic controller → local fixture
Agent   ┘

The current fixture-specific HTTP adapter was intentionally useful for the proof. It must not become the long-term extension model.

Without a strict external Adapter boundary, normal product evolution would pull integrations into Runtime:

Runtime
├─ Xiaomi / MiOT
├─ Home Assistant
├─ Epson / IPP / CUPS
├─ BLE
├─ MQTT
├─ serial / USB
├─ vendor SDKs
└─ vendor-specific credential/config lifecycle

That is explicitly rejected.

Product model

Runtime responsibilities

Runtime may own only the generic machinery necessary to safely project local semantic capabilities into a Room:

  • Adapter registration / deregistration;
  • bounded descriptor validation;
  • list / describe / observe / invoke dispatch;
  • Room authorization;
  • request correlation / timeout / cancellation;
  • process/local-IPC lifecycle if Runtime supervises an Adapter;
  • health / unavailable state;
  • Room / Runtime teardown cleanup;
  • protection against endpoint/config/credential leakage into Room/Harness surfaces.

Runtime does not understand the underlying device/service.

Adapter responsibilities

An Adapter owns everything integration-specific:

  • device/service discovery;
  • vendor protocol;
  • vendor OAuth/login;
  • vendor tokens / passwords / API keys;
  • local endpoint / LAN address;
  • BLE / serial / USB / IPP / MQTT / HTTP / SDK use;
  • vendor-specific retries and connection behavior;
  • conversion from messy local integration state into the bounded semantic capability contract.

Credentials stay in Adapter-owned/local-system storage, for example:

  • macOS Keychain;
  • Home Assistant's own credential/config store;
  • Adapter-local config;
  • environment variables;
  • an already-authenticated vendor CLI/session;
  • another local secret mechanism selected by that integration.

Free4Chat does not define a universal vendor credential schema or credential vault.

Agent role

Agents are first-class Adapter authors/operators.

A setup Agent may, subject to existing local Harness/operator approval policy:

inspect local environment
→ research an existing integration/protocol
→ search for an official/community Free4Chat-compatible Adapter
→ install/use that Adapter
OR
→ generate a small Adapter locally
→ configure vendor login locally
→ test describe/observe/invoke
→ ask Human to approve registration/publication

The Runtime must not require Free4Chat to know the vendor or device model.

An Agent-generated Adapter is allowed to solve an integration Free4Chat has never heard of.

Room messages themselves never grant the Agent new local filesystem/network/USB/secret authority.

Adapter sources are intentionally open

Free4Chat should define the protocol, not a closed integration catalogue.

A compatible Adapter may be:

  • generated on demand by an Agent;
  • maintained by the user;
  • published by the Free4Chat project as a reference/curated Adapter;
  • published by a device vendor;
  • found in an open-source repository;
  • provided by the community;
  • a thin wrapper over an existing local system such as Home Assistant or CUPS.

No central Adapter marketplace/registry is required for this issue.

The Agent may discover compatible Adapters using ordinary local/web research and then validate them against the protocol.

Protocol surface

Keep the first external contract extremely small.

Conceptually:

list?       // optional if one process exposes several capabilities
describe
observe
invoke
health

The semantic Room-facing model remains bounded:

capabilityId
title
version
observe availability
closed typed actions
bounded result

Do not put integration concepts into the protocol:

  • URL / HTTP path;
  • MQTT topic;
  • serial port;
  • BLE characteristic;
  • vendor token;
  • Home Assistant entity internals;
  • printer model;
  • device driver type.

Transport

Prefer the simplest local process/IPC transport that allows an Agent to implement an Adapter in any common language.

Candidate V1:

Runtime
↕ stdio JSON messages / JSON-RPC-like request-response
Adapter process

This is a candidate, not a reason to invent a large SDK.

Evaluate stdio against a Unix-socket/local-IPC alternative and choose the smallest contract with:

  • request correlation;
  • bounded frames;
  • timeout/cancellation;
  • no network listener required;
  • easy Python/Node/Go implementation;
  • explicit process lifecycle;
  • deterministic tests.

Avoid making generic loopback HTTP proxying the canonical Runtime abstraction.

Trust / approval split

Two separate approvals must remain distinguishable:

1. Local Adapter execution

Human/operator approves running/installing Adapter code

This is local-code authority governed by normal Harness/OS/operator policy.

2. Room capability publication/control

Human explicitly enables the Adapter's bounded capability for this Room

Running an Adapter does not automatically publish it to Rooms.

Publishing a capability does not authorize arbitrary Adapter code or local network access.

For Agent-generated temporary Adapters, prefer Room/session-bounded lifecycle where practical:

Agent generates Adapter
→ Human approves local execution
→ Runtime validates descriptor
→ Human approves Room projection
→ Room / registration ends
→ ephemeral Adapter can be stopped/removed

Do not require ephemeral lifecycle for every user-installed Adapter; long-lived local Adapter processes are also valid.

Secret boundary

Normal Runtime / Room / Harness-visible surfaces must never contain Adapter integration secrets.

Explicitly keep outside Free4Chat capability projection:

  • vendor credentials;
  • OAuth refresh/access tokens;
  • local endpoint URLs/IP addresses unless independently required for local debugging;
  • device pairing keys;
  • Adapter internal config;
  • arbitrary environment values;
  • vendor request/response auth material.

Runtime may know how to launch/connect to an Adapter process, but should not parse, own or persist the Adapter's vendor credentials.

Avoid putting secret values in argv.

Official support policy

Free4Chat Core should provide:

  1. the Adapter Protocol specification;
  2. a tiny validator/test harness for Adapter authors/Agents;
  3. Runtime registration/lifecycle support;
  4. at most a small number of reference Adapters/examples when useful.

Reference Adapters must remain outside the Runtime core dependency graph.

A future Home Assistant or CUPS example may be valuable because it demonstrates broad reuse, but it is not part of defining this protocol.

Phase 0 / V1 proof

Phase 0 result: PR #516 merged at 520e3f379f0373b1ca3890b16f1e824f7e1a6259. The retained baseline is supervised stdio NDJSON, protocolVersion 1, list / describe / observe / invoke, an external Python reference Adapter, and a conformance validator. Production Runtime migration remains the next step.

Do not start with Xiaomi-specific code.

First prove the boundary by extracting/replacing the current fixture-specific Runtime integration with one external Adapter process implementing the protocol.

Suggested proof:

existing localhost fixture
        ↑
Agent/user-written external Adapter
        ↑
generic Adapter Protocol
        ↑
Runtime
        ↑
Room #512 capability RPC

Acceptance should prove that the Runtime does not change when the Adapter implementation is replaced.

Then Lab #215 can use the same protocol for one real device/service.

Acceptance

Protocol

  • one small versioned external Adapter protocol is documented;
  • a Python/Node/Go Adapter can implement it without Free4Chat SDK dependencies;
  • descriptor / request / result frames are bounded and validated;
  • Adapter process failure becomes bounded unavailable;
  • unsupported actions fail explicitly;
  • protocol contains no vendor/device transport concepts.

Runtime boundary

  • Runtime no longer needs fixture/vendor-specific HTTP routes to project the proof capability;
  • adding/replacing an Adapter requires no Room/Core protocol change;
  • Runtime does not own vendor credentials/configuration;
  • Runtime does not become a generic HTTP/TCP/reverse proxy;
  • Adapter disappearance removes/unavailable capability cleanly.

Agent authoring

  • a fresh Agent can inspect the protocol/examples and implement a disposable Adapter;
  • Agent can test it locally with a small protocol validator;
  • Adapter can be registered without adding vendor-specific Runtime code;
  • local-code approval remains separate from Room capability authorization.

Reuse proof

After V1, Lab #215 should be able to choose any convenient real target — for example Home Assistant→Xiaomi light or IPP/CUPS→printer — by changing only the Adapter side.

Non-goals

  • Xiaomi / MiOT integration in Runtime;
  • Home Assistant client in Runtime;
  • Epson / CUPS / IPP integration in Runtime;
  • BLE/MQTT/serial/USB stacks in Runtime;
  • vendor credential manager;
  • universal secret vault;
  • device driver catalogue;
  • global Adapter registry;
  • marketplace;
  • package manager;
  • automatic installation of untrusted Adapter code;
  • arbitrary local network proxy;
  • Generated Task App unrestricted LAN access;
  • permanent device cloud;
  • broad plugin SDK beyond the minimum protocol.

Design test

For every proposed Runtime addition ask:

Is this required to securely project a bounded local semantic capability into a Room, or is this integration logic that belongs in the Adapter?

If the latter, keep it outside Runtime.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions