{
  "uid": "cogitave.learn.install-the-massar-agent.a-session-and-local-proof",
  "kind": "moduleUnit",
  "href": "/modules/install-the-massar-agent/a-session-and-local-proof/",
  "title": "A session end to end, and proving it locally",
  "summary": "",
  "type": null,
  "products": [],
  "roles": [],
  "levels": [],
  "subjects": [],
  "headings": [
    "The wire protocol",
    "What happens at each stage",
    "Proving it locally",
    "What you'll see if it's working"
  ],
  "source": "The agent interprets exactly three control messages and one byte stream. This unit walks a session from open to close, then reproduces the whole relay on your own machine with no Diyar broker involved.\n\n## The wire protocol\n\nEvery hop - operator to broker, broker to agent - carries the same two message kinds, symmetric on both sides of the broker:\n\n- **WS Text** = control JSON: `{\"t\":\"open\"}`, `{\"t\":\"resize\",\"cols\":100,\"rows\":30}`, `{\"t\":\"close\"}`.\n- **WS Binary** = raw terminal bytes. Operator→agent is what you type (PTY stdin); agent→operator is what the shell prints (PTY stdout).\n\nThe broker itself only ever pipes frames between the two sides; it does not interpret them. The agent is what owns the PTY and reacts to control, which is what makes the broker replaceable - `massar-broker-dev` in this unit and the real Diyar broker both speak the exact same protocol to the agent.\n\n## What happens at each stage\n\n**Open.** `open_session` allocates a fresh PTY (`native_pty_system().openpty`), sized to whatever `cols`/`rows` came with the request or defaulting to 80x24, then spawns the platform's own default shell on it:\n\n```rust\nfn default_shell() -> String {\n    if cfg!(windows) {\n        std::env::var(\"COMSPEC\").unwrap_or_else(|_| \"powershell.exe\".into())\n    } else {\n        std::env::var(\"SHELL\").unwrap_or_else(|_| \"/bin/bash\".into())\n    }\n}\n```\n\nThe shell is started with `TERM=xterm-256color` and, when `HOME`/`USERPROFILE` is available, with that as its working directory. A dedicated OS thread blocks on reading the PTY's master side and forwards every chunk it reads to the broker as a `Binary` frame; a second thread drains a channel fed by incoming `Binary` frames and writes them to the PTY's stdin. Both are ordinary blocking reads and writes on their own threads, deliberately kept off the async runtime.\n\n**Resize.** A `resize` control message with `cols`/`rows` calls the PTY master's own resize directly - no restart of the shell, no new session.\n\n**Close.** An operator-initiated `close` kills the child process outright. Independently, when the shell itself exits - the user typed `exit`, or the process died - the PTY reader thread hits EOF, and the agent sends `{\"t\":\"exit\"}` back over the control channel so the operator side learns the session ended instead of just going silent.\n\nThere is nothing outside of this lifecycle: the agent holds no standing shell. README is explicit about it: \"a PTY exists only for the duration of an explicitly-opened, TTL-bounded session.\" It is torn down on an operator `close`, on the shell exiting on its own, or on the broker connection dropping - whichever comes first.\n\n## Proving it locally\n\nThe full path - operator client, broker, agent, a real PTY, a real shell - runs on one machine with no Diyar account, no network beyond loopback, using the `dev-tools` feature:\n\n```bash\ncargo build --features dev-tools\nmassar-broker-dev &                          # listens 127.0.0.1:8088\nMASSAR_BROKER_URL=ws://127.0.0.1:8088/agent MASSAR_DEVICE_ID=dev MASSAR_AGENT_TOKEN=dev massar-agent &\nMASSAR_DEVICE_ID=dev massar-op-test          # PASS = sentinel round-tripped through the PTY\n```\n\n- **`massar-broker-dev`** is an axum server exposing `GET /agent` (where `massar-agent` dials in and registers itself by `device_id`) and `GET /op` (where an operator attaches; the broker tells the registered agent to `open`, then pipes frames both ways verbatim). Its own doc comment is explicit about what it is not: \"NOT for production (no Vent RBAC, no audit, no session recording, no TLS) - those land when this moves into diyar.\"\n- **`massar-agent`** here is the identical binary you would install on a real device - it does not need the `dev-tools` feature to build or run; only the dev broker and the test client require it.\n- **`massar-op-test`** attaches to `/op` the same way a browser-based terminal would: it sends a `resize`, types `echo MASSAR_PROOF_OK\\r` followed by `exit\\r`, and requires the sentinel `MASSAR_PROOF_OK` to appear **twice** in the transcript - once as the command it typed, once as the shell's own echo of the result - before printing `PASS` and exiting `0`. Fewer than two hits is a `FAIL`, exit `1`.\n\n> [!TIP]\n> On Windows, ConPTY sends a cursor-position query (`ESC[6n`, a Device Status Report) the moment the shell starts, and stalls rendering until it gets an answer. A real terminal - the xterm.js web terminal on the operator side - answers this automatically. `massar-op-test` does the same thing (replying `ESC[1;1R`), which is why the automated relay test passes on Windows as well as Linux rather than only on one of them.\n\n## What you'll see if it's working\n\nBoth `massar-broker-dev` and `massar-agent` log at `info` level by default (no `RUST_LOG` needed). Across the three processes you should see, in order: the broker logging `agent registered` as `massar-agent` dials in, the agent logging `connected to console broker` and then `session opened` once `massar-op-test` attaches, the broker logging `operator attached`, and finally `massar-op-test` printing the relayed transcript followed by `PASS: sentinel round-tripped through the relayed PTY (2x)`.\n\n| Control message | Direction | Carries |\n| --- | --- | --- |\n| `open` | broker → agent | nothing (agent defaults to 80×24) |\n| `resize` | operator → agent | `cols`, `rows` |\n| `close` | operator → agent | nothing |\n| `exit` | agent → operator | nothing (shell exited on its own) |\n\nThis is the Day-0 proof that actually runs end to end in this repository: the local `massar-broker-dev` / `massar-op-test` harness, not a production Diyar deployment. It's the automated cross-platform relay test the project keeps green (ADR-0002), and it's what you can reproduce yourself before ever pointing a real agent at a real broker URL.\n",
  "partOf": "cogitave.learn.install-the-massar-agent",
  "durationInMinutes": 8,
  "quiz": null
}