# Debugging
The debug/REPL modules split into four layers: a client transport, an
attachable host for panels, a manager for isolated REPL
runtimes, and the host module loaded inside each REPL. The JSON-RPC
protocol itself is documented in
Debug server; this page covers the Deft modules.
import "deft/stdext/debug-client" as dbg import "deft/stdext/debug-host" import "deft/stdext/repl"
## Debug Client
`debug-client` speaks newline-delimited JSON-RPC 2.0 to a
`[debug/serve …]` endpoint over TCP. All request functions are `@async`
— call them with `await`.
| Group | Functions |
|---|---|
| Session | `connect`, `connect-with-retry` (15 × 150 ms), `_close` |
| Eval | `_eval`, `eval-file`, `list-defs`, `list-vars`, `inspect`, `complete`, `get-source` |
| Breakpoints | `bp-set`, `bp-clear`, `bp-list` |
| Stepping | `stack-trace`, `stack-frame`, `step` (`:into`/`:over`/`:out`), `dbg-continue`, `dbg-pause`, `dbg-status` |
| Notifications | `set-notify-handler!` for `debugger/stopped`, `debugger/continued`, `server/hello` |
`_eval` and `_close` keep their leading underscore for historical
reasons; they are the public entry points.
set sess [await [dbg/connect-with-retry 9001]] set r [await [dbg/_eval $sess "(+ 1 2)"]] echo $r~value [await [dbg/_close $sess]]
A session is a map carrying the connection, a response queue, a
notification handler, and a pending-response table keyed by request id,
so concurrent requests each receive only their own response.
## Debug Host (panels)
`debug-host` is the attachable-target half. Import it and subscribe:
import "deft/stdext/debug-host" [pub/subscribe "debug-attach-request" "debug-host/on-attach-request"] [pub/subscribe "debug-detach" "debug-host/on-detach"]
| Def | Description |
|---|---|
| `on-attach-request` | Start this panel's debug server when the request targets it |
| `on-detach` | Stop the server when the target matches |
| `start-and-announce` | Start and broadcast `debug-attach-ready` |
| `unwrap-data` / `panel-label` | Helpers for cross-runtime payloads / labels |
Topics: `debug-attach-request` (`%{:target-panel :from}`) →
`debug-attach-ready` (`%{:panel-id :port :app-name}`) on a deterministic
port (`9000 + panel-id`). Deftlet wires this automatically for
every panel that imports it.
## REPL Runtimes
`repl` spawns an isolated child runtime (own thread, heap, event
loop) hosting a debug server, so prompt code never runs in a panel's VM.
`repl-host` is imported by the generated launcher inside that child.
| Def (manager) | Description |
|---|---|
| `create {| name |}` | Spawn a REPL runtime named `name` |
| `attach-by-name {| name |}` | Poll for the port and connect (12 s budget) |
| `port-for {| name |}` | Debug-registry port lookup |
| `destroy {| session name |}` | Close the session, publish `repl-stopped`, destroy the runtime |
| `list {||}` | Child runtime names |
| Def (host) | Description |
|---|---|
| `start {| name |}` | Bind the first free port in `[9100, 9200)` and announce |
| `stop {||}` | Stop the server (idempotent) |
Topics: `repl-ready` (`%{:name :port}`), `repl-failed`, `repl-stopped`.
Clients discover servers through the process-global debug registry, so
no port scanning is needed.
## See Also
- Debug server — protocol, methods, limits
- Deftlet — automatic panel attach
- App Platform — `pub/*` and child runtimes