varta-client 0.1.0

Varta agent API — emits VLP frames over a Unix Domain Socket.
Documentation
# varta-client

← [Workspace root](../../README.md)

Agent API — emit VLP frames over a Unix Domain Socket. One `connect` call
allocates a socket; every subsequent `beat` call is zero-allocation and
non-blocking.

## Quick start

```rust,no_run
use varta_client::{BeatOutcome, Status, Varta};

fn main() -> std::io::Result<()> {
    let mut agent = Varta::connect("/tmp/varta.sock")?;
    loop {
        match agent.beat(Status::Ok, 0) {
            BeatOutcome::Sent    => {}
            BeatOutcome::Dropped => { /* observer absent — safe to ignore */ }
            BeatOutcome::Failed(e) => eprintln!("beat error: {e}"),
        }
        std::thread::sleep(std::time::Duration::from_millis(500));
    }
}
```

## API summary

### `Varta`

| Method | Signature | Description |
|--------|-----------|-------------|
| `connect` | `(path: impl AsRef<Path>) -> io::Result<Varta>` | Open a non-blocking `UnixDatagram` to the observer. The only allocation point. |
| `beat` | `(&mut self, status: Status, payload: u64) -> BeatOutcome` | Emit one 32-byte VLP frame. Never blocks; never allocates. |

### `BeatOutcome`

| Variant | Meaning |
|---------|---------|
| `Sent` | Kernel accepted the datagram. |
| `Dropped` | Observer absent, queue full, or socket vanished — treat as no-op. |
| `Failed(io::Error)` | Unexpected I/O error; the inner error does not allocate. |

### `Status`

| Variant | Wire value | Meaning |
|---------|-----------|---------|
| `Ok` | `0` | Healthy and making progress. |
| `Degraded` | `1` | Making progress with elevated trouble. |
| `Critical` | `2` | About to die; also emitted by the panic hook. |
| `Stall` | `3` | Synthesised by `varta-watch` on silence; agents do not send this. |

## Payload encoding

The 64-bit `payload` field is application-defined. A common convention is to
pack two `u32` values:

```rust,no_run
// high 32 bits = queue depth, low 32 bits = last error code
let payload = (queue_depth as u64) << 32 | (last_error_code as u64);
```

The observer carries the payload opaquely; decoding belongs to the agent and
any downstream tool that reads the exported metrics file.

## `panic-handler` feature flag

Enable the optional panic hook to emit a `Status::Critical` frame before
normal unwinding:

```toml
# Cargo.toml
[dependencies.varta-client]
path = "../varta-client"
features = ["panic-handler"]
```

```rust,no_run
// Call once at process start, before any other setup.
varta_client::install_panic_handler("/tmp/varta.sock");
```

The hook chains the previously installed hook (preserving the default panic
message and any user hooks). The sole heap allocation is the `Box` created by
`std::panic::set_hook` at install time; the hook closure itself is stack-only.

## Constraints

- **Zero production dependencies.** `[dependencies]` is empty (plus the
  path dep on `varta-vlp`); no registry crate is pulled in.
- **Zero steady-state allocation.** After `Varta::connect`, `beat()` does not
  touch the heap. Verified by a guard-allocator test in `varta-tests`.
- **Non-blocking.** `beat()` calls `set_nonblocking(true)` on the socket;
  `WouldBlock` is treated as `Dropped` — the caller never stalls.

## See also

- Protocol crate: [`crates/varta-vlp/README.md`]../varta-vlp/README.md
- Examples: [`crates/varta-client/examples/`]examples/
- Architecture: [`docs/architecture/vlp-frame.md`]../../docs/architecture/vlp-frame.md