Tutorials
Run a private Unix daemon
Start a durable local host, attach multiple terminal clients, and reconnect to a session.
View Markdown source ↗Create a durable session, disconnect its client, and reattach to the same history.
Prerequisites and command context
Section titled “Prerequisites and command context”Complete installation and private example setup. All commands use dune exec from the repository root with the active opam environment. Terminal A owns the daemon and provider environment; clients run in separate terminals with the same absolute OCHAT_DEMO. Discovery and attachment are offline; submitting messages invokes a billable provider.
Read the current provider TLS and permission boundaries before model work or deployment. Build troubleshooting includes the Apple Silicon/OpenBLAS setup path.
1. Prepare
Section titled “1. Prepare”Complete example setup, retaining the
absolute OCHAT_DEMO directory. Provider credentials are needed only for model
work, and belong in the daemon environment, not just the TUI environment.
dune exec bin/ochat_agent_server.exe -- -config "$OCHAT_DEMO/unix.sexp" -validate-onlydune exec bin/ochat_agent_server.exe -- -config "$OCHAT_DEMO/unix.sexp" -print-configValidation must exit successfully. The printed normalized config contains paths and identity configuration; treat it as operator data when sharing diagnostics.
2. Start Terminal A
Section titled “2. Start Terminal A”export API_URL=api.openai.comdune exec bin/ochat_agent_server.exe -- -config "$OCHAT_DEMO/unix.sexp"Leave this foreground process running. The socket parent created by mktemp is
private. Do not run the HTTP config concurrently: both configs own the same store.
3. Create in Terminal B
Section titled “3. Create in Terminal B”Set OCHAT_DEMO in this terminal to the path from step 1, then:
dune exec bin/chat_tui.exe -- --no-config --connect "unix://$OCHAT_DEMO/agent.sock" \ --new-daemon-session --prompt hello --workspace project --detachedThe CLI resolves configured names hello and project to protocol catalog IDs.
Submitting a message now calls the provider. Quit with the normal TUI :q
workflow: only this client disconnects.
4. List and reattach
Section titled “4. List and reattach”dune exec bin/chat_tui.exe -- --no-config --connect "unix://$OCHAT_DEMO/agent.sock" --list-sessionsCopy the returned opaque session ID into OCHAT_SESSION, then:
dune exec bin/chat_tui.exe -- --no-config --connect "unix://$OCHAT_DEMO/agent.sock" \ --session "$OCHAT_SESSION"Open another terminal with the same directory/session variables and repeat with
--read-only. It receives updates but cannot send messages or approve tools.
Unix clients authenticate as the same effective user; read-only attachment is
not a separate low-scope credential. For transcript-only credentials use HTTP.
5. Owner-bound variation
Section titled “5. Owner-bound variation”Create another session with --owner-bound --disconnect-grace-ms 30000 instead
of --detached. It has an exclusive owner lease; loss starts a grace interval,
not an instantaneous stop at every network hiccup. Reconnect within permitted
lease/reclaim rules. Do not use --read-only with owner-bound mode.
6. Stop and shut down
Section titled “6. Stop and shut down”dune exec bin/chat_tui.exe -- --no-config --connect "unix://$OCHAT_DEMO/agent.sock" \ --stop-session "$OCHAT_SESSION"This stops session work; it does not delete the durable transcript. Quit attached clients. Use Ctrl+C in Terminal A for graceful daemon shutdown. Starting the same config again reopens the store. Recovery does not resume an interrupted tool’s process; see session recovery.
Archive the private directory if you want to retain this tutorial’s state, or remove only that exact directory after shutdown. No normal Ochat state was used.
Checkpoint, troubleshooting, and next step
Section titled “Checkpoint, troubleshooting, and next step”Success means a detached session remains listed after the first client quits, a new client attaches to the same opaque ID, and the store survives a normal daemon restart. If connection fails, check the foreground daemon, socket path, private-directory permissions, and matching shell variables. A missing session ID is not the configured prompt name. Use troubleshooting; do not run two daemons against the same store. Follow step 6 for shutdown and cleanup. Next, schedule background work or connect a stdio client.
View source · Background timer2 files
Complete source files. Open a filename to read it here.
timer.chatmdStart here
<developer>This host demonstrates a background timer; no model request is needed.</developer>
<script language="chatml" kind="moderator">
type state = int
type event = [ `Session_start | `Tick ]
let initial_state = 0
let on_event : context -> state -> event -> state task =
fun ctx st ev ->
match ev with
| `Session_start ->
Task.bind(Schedule.after_ms(10000, `Tick), fun timer_id ->
Task.pure(st + 1))
| `Tick ->
Task.bind(Runtime.end_session("scheduled tutorial stop"), fun ignored_end ->
Task.pure(st + 1))
</script>
LICENSE.txtnotice
MIT License
Copyright (c) 2025
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.View source · Protocol discovery2 files
Complete source files. Open a filename to read it here.
discover.ndjsonStart here
{"jsonrpc":"2.0","id":"initialize","method":"protocol.initialize","params":{"implementation":{"name":"tutorial","version":"1"},"protocol_min":{"major":1,"minor":0},"protocol_max":{"major":1,"minor":0},"features":[],"event_encodings":["json"],"max_inbound_event_bytes":16777216}}
{"jsonrpc":"2.0","id":"info","method":"server.info","params":{}}
{"jsonrpc":"2.0","id":"prompts","method":"prompt.list","params":{"limit":20}}
{"jsonrpc":"2.0","id":"workspaces","method":"workspace.list","params":{"limit":20}}
{"jsonrpc":"2.0","id":"sessions","method":"session.list","params":{"limit":20}}
LICENSE.txtnotice
MIT License
Copyright (c) 2025
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.