Skip to content

Protocol messages

This page covers consumer-to-Authority messages, which travel end-to-end encrypted through the bridge. The plaintext Authority-to-bridge handshake (challenge/auth/ready, including the proto version field) is documented in Bridge endpoints.

Messages handled by the Authority:

Diagram showing Varco protocol flow between consumer app, opaque bridge, and Home Assistant Authority
Message typeRequest fieldsResponseScope checked
access_requestconsumer_pk, manifest, nonce, signatureaccess_request_pending (access_request_id, pairing_code)Signature only; creates pending request.
claim_shareshare_id, secret, consumer_pkshare_claimed (grant_id, manifest)Claimable share exists, secret matches, and claims used is below max_claims; creates a grant for consumer_pk.
authenticateconsumer_pk, nonce, signatureauthenticated (grant_id, manifest)Active grant for consumer key. The signature covers varco-authenticate-v1\0 + nonce + \0 + the session channel binding, so it cannot be replayed in another encrypted session.
grant_infononegrant_info (grant_id, manifest)Authenticated session. Returns the stored manifest for generic clients that were not built with a manifest at development time.
get_statesentity_idsstatesread_entities.
subscribe_statesentity_idsstate_snapshot, then state_delta eventssubscriptions plus read_entities.
unsubscribe_statessubscription_idunsubscribedAuthenticated session.
history_queryentity_ids (max 10), optional start_time/end_time (range clamped to 30 days)history_result (history, truncated, range_clamped; max 5000 points per entity)history.
camera_snapshotentity_idcamera_snapshotcamera_snapshots.
call_servicedomain, service, service_data, target; optional top-level pin or pins{ type: "service_called", request_id, ok: true }actions.
webrtc_offerSDP offerwebrtc_answer or webrtc_unavailableActive grant.
webrtc_iceICE datawebrtc_ice_ack or webrtc_unavailableActive grant.

Errors use:

{
"type": "error",
"request_id": "optional request id",
"code": "permission_denied",
"message": "Entity not allowed"
}

The Authority enforces an in-memory per-consumer rate budget on all authenticated data-plane operations (default 240 weight units per 60 seconds). Each operation costs 1 unit, except history_query and camera_snapshot (5 units) and call_service (2 units). Requests over budget return an error with code: "rate_limited" and are audited as rate_limited without sensitive payloads.

Approved grants may include owner-managed restrictions. These are evaluated only by the Home Assistant Authority, after the normal grant scope check, in declaration order (fail-fast).

For PIN-protected operations, include pin (single PIN) or pins (object keyed by restriction id) at the top level of the request, not inside service_data:

{
"type": "call_service",
"domain": "lock",
"service": "unlock",
"target": { "entity_id": "lock.front_door" },
"pin": "1234"
}

The Authority denies the request if the matching restriction is expired, outside schedule, rate-limited, missing a required PIN, or has an invalid PIN. Restriction failures return permission_denied and are audited as restriction_denied.

All responses echo the request_id from the originating request.

subscribe_states returns two distinct message types:

  • state_snapshot: sent once on subscription with the full current states for all requested entities.
  • state_delta: sent on each Home Assistant state change; contains only the changed entities and the subscription_id.