oly 0.2.1

Run interactive CLIs and AI agents like managed services with persistent PTY sessions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# Specification: Open Relay (`oly`)

**Version:** 0.1.0  
**Language:** Rust  
**Status:** Implementation-aligned living spec

This document describes the implemented product surface for `oly` as reflected by the current CLI and runtime behavior. For deeper internal design details, see [ARCHITECTURE.md](./ARCHITECTURE.md).

---

## 1. Purpose

`oly` is a persistent session relay for long-running interactive CLI workloads.

A background daemon owns PTY-backed sessions so commands can outlive the terminal that launched them. Users and agents can:

- start commands under daemon ownership
- detach and reattach later
- inspect persisted logs
- send input without attaching
- receive notifications when a session likely needs attention
- access a local web UI / HTTP API
- proxy commands to connected secondary nodes

`oly` is aimed at AI agent supervision, interactive tooling, and other terminal workloads that benefit from durable control and auditability.

---

## 2. Product positioning

`oly` is not primarily a terminal multiplexer. It is a **session supervision layer**.

Compared with a traditional multiplexer:

- the daemon, not a single terminal, owns the workload
- logs persist independently of attach state
- input can be injected without opening an interactive terminal
- likely input-needed checkpoints can trigger notifications
- browser-based supervision and node federation are part of the product surface

---

## 3. Implemented scope

### 3.1 In scope

1. Local daemon process managed by the same `oly` binary.
2. PTY-backed session lifecycle: start, attach, detach, input, logs, stop.
3. Persisted session metadata and output logs.
4. Prompt / input-needed detection with notifications.
5. Local HTTP API and browser UI on loopback by default.
6. Password-protected web access by default, with explicit `--no-auth` opt-out.
7. Primary / secondary node federation via authenticated daemon-to-daemon connection.
8. Notification hooks for custom local automation.

---

## 4. High-level architecture

Single binary, three primary surfaces:

- **CLI**
  - parses commands from `src/cli.rs`
  - routes requests in `src/main.rs`
  - talks to the local daemon over IPC
- **Daemon**
  - owns live sessions
  - persists metadata and logs
  - performs notification and prompt-detection work
  - serves the local HTTP/UI surface unless disabled
- **HTTP/UI surface**
  - serves a browser UI and HTTP endpoints on `127.0.0.1:<port>`
  - exposes REST, SSE, and WebSocket session control primitives

Federation extends this model by letting a secondary daemon connect outbound to a primary daemon over WebSocket. CLI requests can then be wrapped with `--node <name>` and proxied transparently.

---

## 5. Session model

Each session has durable metadata plus live runtime state.

Notable fields include:

- `id`
- optional `title`
- `command` and `args`
- optional `cwd`
- `created_at`
- optional `pid`
- session `status`
- whether input is currently likely needed

Implemented status values exposed through `oly ls --status` are:

- `created`
- `running`
- `stopping`
- `stopped`
- `killed`
- `failed`
- `unknown`

---

## 6. CLI contract

### 6.1 Top-level commands

The implemented top-level commands are:

- `daemon`
- `start`
- `notify`
- `skill`
- `ls`
- `stop`
- `attach`
- `logs`
- `send`
- `api-key`
- `join`
- `node`

### 6.2 Daemon commands

### `oly daemon start [--detach] [--port <port>] [--no-auth] [--no-http]`

- Starts the daemon.
- Runs in the foreground by default.
- `--detach` starts it in the background.
- `--port` overrides the default HTTP port.
- `--no-auth` disables HTTP authentication and requires explicit risk acknowledgement.
- `--no-http` disables the HTTP API and browser UI entirely.

### `oly daemon stop [--grace <seconds>]`

- Requests daemon shutdown.
- Waits up to `--grace` seconds for sessions to exit cleanly before forcing termination.
- Default grace period is `15` seconds.

### 6.3 Session lifecycle commands

### `oly start [--title <title>] [--detach] [--disable-notifications] [--cwd <dir>] [--node <name>] <cmd> [args...]`

- Starts a PTY-backed session under daemon ownership.
- `--cwd` is resolved relative to the caller's current working directory when a relative path is provided.
- Without `--detach`, `oly` immediately tries to attach after creation.
- With `--detach`, `oly` prints the session ID and exits.
- `--disable-notifications` disables notification delivery for that session.
- `--node` runs the command on a connected secondary node.

### `oly ls [--search <text>] [--json] [--status <status>]... [--since <rfc3339>] [--until <rfc3339>] [--limit <n>] [--node <name>]`

- Lists sessions.
- `--search` filters by title or ID substring, case-insensitively.
- `--json` returns machine-readable output.
- `--status` is repeatable and accepts the implemented values from section 5.
- `--since` and `--until` accept RFC3339 timestamps.
- `--limit` defaults to `10`.
- `--node` targets a connected secondary node.

### `oly attach [id] [--node <name>]`

- Attaches to a running session.
- Replays recent buffered output first, then switches to live IO.
- If `id` is omitted, `oly` resolves the most recently created session.
- Detach escape is `Ctrl-]`, then `d`.

### `oly logs [id] [--tail <n>] [--keep-color] [--no-truncate] [--wait-for-prompt] [--timeout <duration>] [--node <name>]`

- Prints recent logs without attaching.
- If `id` is omitted, `oly` resolves the most recently created session.
- `--tail` defaults to terminal height minus one line when available, otherwise `40`.
- `--keep-color` preserves ANSI color codes.
- `--no-truncate` disables column truncation when rendering.
- `--wait-for-prompt` waits until the session likely needs input or exits, then prints logs.
- `--timeout` accepts plain milliseconds or `ms`, `s`, `m`, `h` suffixes.
- Default timeout is `30s`; `0` means wait forever.

### `oly send [id] [CHUNK]... [--node <name>]`

- Sends input to a session without attaching.
- If `id` is omitted, `oly` resolves the most recently created session.
- Positional chunks are processed left to right.
- Plain chunks send literal text.
- `key:<spec>` sends terminal key sequences.
- When no chunks are provided and stdin is piped, `oly` reads all stdin and sends it as input.

Supported key forms include:

- named keys: `enter`, `tab`, `esc`, `backspace`, `up`, `down`, `left`, `right`, `home`, `end`, `pageup`, `pagedown`, `delete`, `insert`
- modifier forms: `ctrl+<char>`, `alt+<char|key>`, `meta+<char|key>`, `shift+tab`
- raw bytes: `hex:<hex-bytes>`

### `oly stop [id] [--grace <seconds>] [--node <name>]`

- Stops a session.
- If `id` is omitted, `oly` resolves the most recently created session.
- Default grace period is `5` seconds before hard termination.

### 6.4 Notification commands

### `oly notify enable [id] [--node <name>]`

- Enables notifications for a running session.
- If `id` is omitted, `oly` resolves the most recently created session.

### `oly notify disable [id] [--node <name>]`

- Disables notifications for a running session.
- If `id` is omitted, `oly` resolves the most recently created session.

### 6.5 Skill command

### `oly skill`

- Prints the bundled `oly` skill markdown used for agent/tooling guidance.

### 6.6 Federation commands

### `oly api-key add <name>`

- Creates an API key on the primary daemon.
- Prints the plaintext key once.

### `oly api-key ls`

- Lists API key labels and creation timestamps on the primary daemon.

### `oly api-key remove <name>`

- Revokes a named API key.

### `oly join start --name <name> --key <key> <url>`

- Saves an outbound join configuration on the secondary.
- Requests the local daemon to connect to the primary.
- If the daemon is not running, the saved join config remains and will be used on the next daemon start.

### `oly join stop --name <name>`

- Removes the saved join config and requests disconnection.

### `oly join ls`

- Lists saved outbound join configs on the current daemon.

### `oly join ls --primary`

- Lists active joins from the daemon's primary-side view.

### `oly node ls`

- Lists currently connected secondary nodes on the primary daemon.

---

## 7. HTTP and browser surface

When HTTP is enabled, the daemon serves on loopback:

```text
http://127.0.0.1:15443
```

The port is configurable via `oly daemon start --port <port>` or config.

Implemented HTTP surface includes:

- auth status, login, and logout endpoints
- health endpoint
- session CRUD and log endpoints
- session event streaming via SSE
- interactive attach via WebSocket
- push subscription endpoints
- node listing and join-related endpoints
- static asset serving for the bundled web UI and optional local `wwwroot`

`--no-http` disables this entire surface.

---

## 8. Prompt detection and notifications

`oly` maintains prompt-like pattern matching plus idle detection to decide when a session likely needs input.

The default pattern set includes common confirmation, password, token, shell, and agent-style prompts such as:

- `(y/n)`
- `[y/n]`
- `[yes/no]`
- `password:`
- token / secret prompts
- `? ` prompt lines
- `continue?`
- `are you sure`
- `press enter`

Important implemented behaviors:

- session notifications can be enabled or disabled per session
- `oly logs --wait-for-prompt` blocks until the daemon reports likely input-needed state or the timeout expires
- notifications can be routed through a custom `notification_hook`

---

## 9. Persistence and configuration

Default state directory:

- Windows: `%LOCALAPPDATA%\oly`
- Linux: `$XDG_STATE_HOME/oly` or `~/.local/state/oly`
- macOS: `~/Library/Application Support/oly`

`OLY_STATE_DIR` overrides the state root.

Persisted state includes:

- daemon logs
- SQLite database
- session directories with logs and metadata
- `config.json`
- saved join configs
- optional `wwwroot` assets

Important implemented config fields include:

- `http_port`
- `log_level`
- `prompt_patterns`
- `web_push_subject`
- `web_push_vapid_public_key`
- `web_push_vapid_private_key`
- `max_running_sessions`
- `session_eviction_seconds`
- `notification_hook`

---

## 10. Security model

### 10.1 Local CLI / IPC

- CLI commands talk to a local daemon over local IPC.
- The daemon enforces local caller checks according to the host platform.

### 10.2 HTTP authentication

By default, the HTTP/UI surface is password protected.

Implemented behavior:

1. `oly daemon start` prompts for a password unless `--no-auth` is used.
2. Passwords are stored as Argon2 PHC hashes, not plaintext.
3. `GET /api/auth/status` is public and reports whether auth is required.
4. `POST /api/auth/login` issues an auth token on success.
5. `POST /api/auth/logout` revokes the token.
6. Static assets and selected public endpoints remain reachable for the login flow.

Lockout behavior:

- failed login attempts are tracked per client IP
- `3` failed attempts from the same client trigger a `15` minute lockout for that client
- successful login resets the failure counter for that client

### 10.3 Remote access pattern

`oly` is intended to stay local-first.

The recommended remote deployment pattern is:

```text
remote browser/client -> strong auth gateway -> secure tunnel -> local oly HTTP service
```

Examples include Cloudflare Access, Tailscale, or SSH tunneling.

---

## 11. Federation model

One daemon can act as the **primary** and accept outbound secondary connections.

The current model is:

1. Generate a key on the primary with `oly api-key add`.
2. Start a join from the secondary with `oly join start --name <name> --key <key> <url>`.
3. The secondary daemon maintains a connection to the primary.
4. Session-oriented commands can target the secondary with `--node <name>`.

This allows the CLI and browser surface to supervise sessions across multiple machines without changing the session command model.

---

## 12. Known boundaries

Current non-goals or intentionally externalized concerns:

- public internet exposure and TLS termination
- hosted auth / identity integration
- organization-wide approval policy engine
- daemon adoption of arbitrary pre-existing processes

---

## 13. References

- [`src/cli.rs`]./src/cli.rs
- [`src/main.rs`]./src/main.rs
- [`src/config.rs`]./src/config.rs
- [`ARCHITECTURE.md`]./ARCHITECTURE.md
- [`ARCHITECTURE_PTY.md`]./ARCHITECTURE_PTY.md