@benkovy/doh-host
v0.1.25
Published
Host-side connector that bridges OpenClaw and DOH backend
Readme
@benkovy/doh-host
Host-side connector runtime for DOH.
Role in the system
This process runs next to an OpenClaw host and creates an outbound, persistent WebSocket connection to the DOH backend.
It is responsible for:
- host authentication handshake (
host.hello->backend.challenge->host.proof->backend.ready) - receiving downlink relay commands (
relay.send,relay.history.request) - publishing uplink events (
relay.event) - publishing history responses (
relay.history.response) - reconnect behavior when network drops
Why this exists separately from the plugin
Keeping the connector as a standalone package/process allows:
- cleaner deployment and observability
- independent versioning and rollout
- option to reuse connector logic in non-plugin host integrations
The OpenClaw plugin package (@benkovy/doh) can wrap or invoke this connector behavior.
Environment variables
DOH_RECONNECT_INITIAL_MS(optional)DOH_RECONNECT_MAX_MS(optional)DOH_RECONNECT_FACTOR(optional)DOH_RECONNECT_JITTER(optional)DOH_RECONNECT_MAX_ATTEMPTS(optional,0= unlimited)
Runtime identity and adapter settings are persisted in ~/.doh-host/config.json.
Adapter-specific environment can be defined under adapter.env in that config.
For the pi adapter, config also supports:
adapter.tools(optional, defaults toread,write,edit,bash)adapter.skillsdir(default~/.agents/skills)include(optional skill directory names to include)exclude(optional skill directory names to exclude)enableModelLoad(optional, defaulttrue; exposesload_skilltool)enableSkillMetadataPrompt(optional, defaulttrue; includes available skill metadata in prompt)maxLoadsPerTurn(optional, default2; limitsload_skillcalls per turn)maxSkillBytes(optional, default65536; max bytes for loadedSKILL.mdand reference budget)
adapter.envprovider settingsPI_MODELasprovider/modelId(orPI_PROVIDER+PI_MODEL_ID)- provider API key env vars (for example
OPENAI_API_KEY,ANTHROPIC_API_KEY)
Skill commands are always enabled for the pi adapter:
/skill:<name> [args]loads the named skill immediately and appends optional args as user request context.
When a loaded skill includes references/*.md, doh-host includes those markdown references in the injected prompt block (subject to byte budget limits).
When using adapter.name = "pi", doh-host synchronizes a built-in doh skill into adapter.skills.dir on every startup (overwriting doh/SKILL.md and its references/*.md files).
Optional process env overrides are still used for reconnect tuning and state dir:
DOH_STATE_DIRDOH_RECONNECT_INITIAL_MSDOH_RECONNECT_MAX_MSDOH_RECONNECT_FACTORDOH_RECONNECT_JITTERDOH_RECONNECT_MAX_ATTEMPTS
Init workflow
CLI help:
doh-host --helpdoh-host supports self-serve bootstrap provisioning:
doh-host init --email [email protected]Or using token directly:
doh-host init --token <token-from-email>Machine-readable mode:
doh-host init --token <token-from-email> --json
doh-host status --jsonConfigure pi model and API key:
doh-host config set-model --model openai/gpt-4o-mini
doh-host config set-api-key-env --provider openai --value <OPENAI_API_KEY>After init completes, runtime config is written to:
~/.doh-host/config.json
init writes config only. Start runtime explicitly with doh-host run.
To run with existing env vars/config only:
doh-host runTo inspect runtime/config state:
doh-host statusTo probe live backend auth handshake and selected adapter health:
doh-host status --probeTo clear local host config state and restart onboarding:
doh-host reset --yesMachine-readable reset output:
doh-host reset --yes --jsonInvite commands
Use invites to grant client access:
doh-host invite create --email [email protected]
doh-host invite list --limit 50
doh-host invite revoke --invite-id <inviteId>Pi E2E smoke test
After bootstrap and config:
doh-host status --probe
doh-host runIn a second terminal:
doh-host invite create --email <client-email>Then log in from the client app with the invited email and send a test message.
No command defaults to run mode.
LLM/Agent setup
Recommended agent-driven onboarding sequence:
- Run
doh-host init --email <user-email> - Prompt the user to paste the bootstrap token from their email
- Run
doh-host init --token <token> - Run
doh-host run - Verify backend health reports at least one host connection
The runtime config file is written to ~/.doh-host/config.json.
Process adapter architecture
doh-host runtime now uses a process adapter interface so core relay logic is decoupled from any specific host process implementation.
Current default adapter is pi.
Reserved adapter name:
pi: in-process adapter implementation
All other adapter names are treated as external socket adapters and must connect to:
adapter.socketPathfrom config, or- default
~/.doh-host/adapter.sockwhenadapter.name !== "pi"and no socket path is provided.
Adapter responsibilities:
send(...): process user messages and return assistant outputgetHistory(...): return session historylistSessions(...): return session summaries- optional run lifecycle callbacks (
start/end/error)
Core doh-host responsibilities (adapter-independent):
- backend auth handshake/reconnect
- relay envelope translation and publish semantics
- runtime lock/single-process enforcement
- invite APIs and host-authenticated backend HTTP calls
- local UI bundle store and bundle response handling
External adapters communicate with doh-host over Unix socket JSONL request/response frames.
If no external adapter is connected, runtime continues and relay requests fail with adapter-unavailable events until an adapter connects.
DOH history is persisted in a local SQLite store at:
~/.doh-host/history.sqlite
The store is written directly during relay.send processing (pending -> sent/failed + assistant reply), so mobile history remains stable even if OpenClaw session maintenance prunes transcript artifacts.
On success/failure, the connector publishes corresponding relay.event entries for observability.
Development
pnpm --filter @benkovy/doh-host devSecurity notes
- API key secrets are host-machine secrets, never client-facing
- handshake uses nonce/challenge HMAC and timestamp checks in current scaffold
- always use TLS (
wss://) in non-local environments
Runtime diagnostics
Connector logs are emitted as JSON lines and distinguish:
- transport events (
connector.open,connector.closed,connector.error) - auth events (
auth.challenge.received,auth.ready,auth.error) - reconnect behavior (
connector.reconnect_scheduled,connector.reconnect_exhausted)
Release smoke checklist
Before and after publishing @benkovy/doh-host, run this quick checklist:
doh-host status --probereports backend and adapter probes asok.- Backend health (
/health) reportshostConnections > 0whiledoh-host runis active. - Send a message from client and confirm assistant reply arrives (
relay.send.accepted). - Restart mobile app and verify history reloads without duplicated rows.
Next implementation steps
- Add reconnect resume with server-issued session token
- Add message idempotency tracking and ack/nack handling
- Add optional health endpoint wrapper for supervisor integrations
- Add dedicated Gateway transport health probing for startup diagnostics
