Skip to content

hello

The server’s half of the handshake: the protocol range it speaks, and who this connection is.

Direction: Chips → Harness
Sent by: Chips only; refused from a client
Requires a run: no
Schema: hello.json

Every event also carries the envelope.

Field Required Type Description
v yes 1 Protocol major version.
type yes "hello"
id yes string The event’s own id. Stable across a retry of the same event.
ts yes string When it happened, by Chips’ clock. Stamped on the way in; a value the sender writes is replaced.
workspace_id yes string A workspace identifier: ws_ followed by a ULID.
space_id no string The space, when the event belongs to one.
run_id no string The run, when the event belongs to one.
seq no number Assigned by the server on append, monotonic per workspace. Absent on the way in.
idempotency_key no string Required on every client-originated write. Sending the same key twice is a no-op.
actor yes object The participant an event is attributed to.
actor.kind yes "person" | "harness" | "agent" | "system" Whether a person, a harness, one of a harness’s agents or Chips itself acted.
actor.id yes string The participant id, or system for Chips.
actor.harness_id no string For an agent: the harness it speaks through, whose budget and runs are the agent’s (ADR 0087). Filled in by Chips.
protocol yes object The negotiated range. A client picks a version inside it or is refused.
protocol.min yes number Oldest major version the server accepts.
protocol.max yes number Newest major version the server speaks.
server yes object Who the client is talking to.
server.name yes string Usually chipsd.
server.version yes string The server’s own version.
harnesses yes object[] The harness the token belongs to, and only that one. One connection speaks for all of the harness’s agents (ADR 0087), which agents lists. A harness running inside another is a lane, not a duplicate.
harnesses[].harness_id yes string A harness identifier: hrn_ followed by a ULID.
harnesses[].display_name yes string The harness’s name in the workspace.
harnesses[].owner_id yes string The person accountable for this harness.
harnesses[].capabilities no string[] What the harness can do.
harnesses[].ephemeral no boolean True for a transient child minted for one lane, which inherits its lead’s settings.
harness no object The harness this connection is: harnesses[0], said once. Read this rather than the list.
harness.harness_id yes string A harness identifier: hrn_ followed by a ULID.
harness.display_name yes string The harness’s name in the workspace.
harness.owner_id yes string The person accountable for this harness.
harness.capabilities no string[] What the harness can do.
harness.ephemeral no boolean True for a transient child minted for one lane, which inherits its lead’s settings.
agents no object[] The harness’s agents, each with its rooms (ADR 0087). Everything the connection sends names one of them as actor; a harness’s own id is read as its one agent in the room. Sent only to a harness that declares agents.
agents[].agent_id yes string A agent identifier: agt_ followed by a ULID.
agents[].key yes string The harness’s own name for it.
agents[].name yes string What it is called, and so its @handle.
agents[].lead yes boolean Whether it is the harness’s Main agent.
agents[].state yes "active" | "archived" Archived when its thing was put away.
agents[].spaces yes string[] The rooms it is in. One at most, once one room per agent is the rule.
agents[].room no string | null The open room it is in (ADR 0087: one at a time), or null while it is free. What it says while free is held.
agents[].held no number How many of its words Chips is holding, said while it was in no room.
autonomy yes 0 | 1 | 2 | 3 The level in force for this connection.
policy no object The rules that fill in the details.
policy.autonomy yes 0 | 1 | 2 | 3 0 observe, 1 ask first, 2 ask when it matters, 3 autonomous and report back. Presets that expand to rules.
policy.rules yes object[] Evaluated in order; the workspace cap always wins.
policy.rules[].match yes string A glob over action kinds, for example payment.*.
policy.rules[].when no object Extra conditions on the action’s flags, and for a command, how its parts start.
policy.rules[].when.external no boolean
policy.rules[].when.irreversible no boolean
policy.rules[].when.money_usd_over no number
policy.rules[].when.risk no enum[] One of external, external_communication, irreversible, destructive, financial, credential, privacy, legal.
policy.rules[].when.starts_with no string[] For an action that carries a command (ADR 0239): the words a part of it starts with. A command goes ahead on such rules only when every part starts with one that allows it.
policy.rules[].decision yes "allow" | "ask" | "deny" What to do when the rule matches.
policy.rules[].approvers no string[] Who may answer, when the decision is ask.
policy.rules[].reason no string Shown to the harness, and recorded in the audit log.
policy.rules[].off no boolean Turned off by a person (ADR 0195): kept in the list to turn back on, and skipped by the policy engine until then.
policy.rules[].made_by no string Who made it by answering an ask with Always (ADR 0195). Filled in by Chips.
policy.rules[].made_at no string When a person’s answer made it (ADR 0195): Always, or This session. Filled in by Chips. A rule a person’s answer made never allows an action that matters.
budget no object The ceiling, and what has been spent.
budget.limit_usd yes number The ceiling for this harness.
budget.spent_usd yes number Spent so far, estimated.
spaces no string[] The spaces this credential is a member of as it connects. A client that kept a cache drops what is not here: a membership it lost while away is the one event a replay can never bring it, because it is no longer in the space that event belongs to.
workspace no { name: string; } The workspace as it is called now. A device keeps the name it was told when it paired and takes this one when they differ, so a workspace renamed since is called by its new name without pairing again. Absent when the workspace has no name.
workspace.name yes string
{
"v": 1,
"type": "hello",
"id": "evt_01K4ZQPX000000000000000073",
"ts": "2026-09-02T14:22:15.004Z",
"workspace_id": "ws_01K4ZQPX000000000000000001",
"space_id": "spc_01K4ZQPX000000000000000002",
"actor": {
"kind": "system",
"id": "system"
},
"protocol": {
"min": 1,
"max": 1
},
"server": {
"name": "chipsd",
"version": "0.0.0"
},
"harnesses": [
{
"harness_id": "hrn_01K4ZQPX000000000000000004",
"display_name": "Scout",
"owner_id": "usr_01K4ZQPX000000000000000003",
"capabilities": ["drive.read", "crm.read", "email.send"],
"ephemeral": false
}
],
"autonomy": 2,
"spaces": ["spc_01K4ZQPX000000000000000002"],
"policy": {
"autonomy": 2,
"rules": [
{
"match": "email.send",
"when": {
"external": true
},
"decision": "ask",
"approvers": ["owner"],
"reason": "External mail always asks."
},
{
"match": "payment.*",
"when": {
"money_usd_over": 500
},
"decision": "ask",
"approvers": ["role:finance"]
},
{
"match": "drive.read",
"decision": "allow"
}
]
},
"budget": {
"limit_usd": 25,
"spent_usd": 1.47
}
}