relmio 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ # Getting started
2
+
3
+ Relmio gives you two intentionally separate paths:
4
+
5
+ | Need | Use | Credential |
6
+ | --- | --- | --- |
7
+ | An OpenAI-compatible local endpoint | Local OpenAI API gateway (`/v1`) | Your OpenAI Platform API key |
8
+ | A trusted native Codex integration | Codex App Server (JSON-RPC over WebSocket) | ChatGPT sign-in and a local capability |
9
+ | A small local chat backend | Codex Chat Adapter (`POST /chat`) | ChatGPT sign-in and a local bearer credential |
10
+ | An n8n bridge on a VPS | The separate n8n sidecar | Your locally created ChatGPT sign-in file |
11
+
12
+ ChatGPT sign-in is never converted into an OpenAI Platform API key. The Codex
13
+ options are experimental and are not generic `/v1` services.
14
+
15
+ ## Install
16
+
17
+ On macOS, Linux, WSL, or Git Bash, start the local wizard with:
18
+
19
+ ```bash
20
+ npx --yes --ignore-scripts relmio@latest
21
+ ```
22
+
23
+ The wizard prints a private loopback URL, verifies Docker before it changes
24
+ anything, and asks for final confirmation before remote VPS writes. It binds
25
+ local endpoints to `127.0.0.1`, never to a LAN interface.
26
+
27
+ For installation options and prerequisites, see the [package
28
+ README](https://www.npmjs.com/package/relmio). For a VPS/n8n walkthrough, see
29
+ [VPS and n8n](./vps-and-n8n.md).
30
+
31
+ ## Choose the next guide
32
+
33
+ - [Local endpoints](./local-endpoints.md) for the local gateway, Codex App
34
+ Server, or Chat Adapter.
35
+ - [Troubleshooting](./troubleshooting.md) when Docker, authentication, or a
36
+ local image build stops the flow.
37
+ - [Security](./security.md) for credential and trust boundaries.
38
+ - [Reference](./reference.md) for test commands and protocol notes.
@@ -2,7 +2,8 @@
2
2
 
3
3
  ## Status
4
4
 
5
- Approved for implementation on `codex/local-openai-endpoint` on 2026-08-13.
5
+ Originally approved on 2026-08-13 and extended for the additive
6
+ `codex/local-codex-chat-adapter` target on 2026-08-15.
6
7
 
7
8
  This spec is product and engineering guidance based on the current official
8
9
  OpenAI documentation. It is not a legal opinion. Relmio must not claim that
@@ -13,16 +14,19 @@ OpenAI has endorsed, certified, or pre-approved the project.
13
14
  Add a local Docker installation path to the Relmio browser wizard without
14
15
  weakening the existing VPS/n8n safety boundary.
15
16
 
16
- Relmio offers two intentionally different local providers:
17
+ Relmio offers three intentionally different local client contracts:
17
18
 
18
19
  1. `openai-api` is an OpenAI-compatible HTTP gateway backed by the user's
19
20
  OpenAI Platform API key.
20
21
  2. `codex-chatgpt` is the official Codex App Server protocol backed by the
21
22
  user's ChatGPT/Codex sign-in.
23
+ 3. `codex-chat` is a small Relmio-specific HTTP chat adapter backed by the
24
+ same official App Server lifecycle and ChatGPT/Codex sign-in.
22
25
 
23
26
  Relmio must never exchange, translate, or present a ChatGPT/Codex credential as
24
- a general OpenAI API bearer credential. The Codex provider must not expose an
25
- OpenAI-shaped `/v1` compatibility surface.
27
+ a general OpenAI API bearer credential. Neither Codex target may expose an
28
+ OpenAI-shaped `/v1` compatibility surface. The adapter owns only its narrow
29
+ `POST /chat` contract.
26
30
 
27
31
  ## Official-source boundary
28
32
 
@@ -53,7 +57,7 @@ The existing VPS/n8n wizard remains a separate legacy setup path. The wizard
53
57
  landing experience adds a prominent **Local endpoints** option which opens a
54
58
  dedicated local installer.
55
59
 
56
- The local installer starts with two provider cards:
60
+ The local installer starts with three provider cards:
57
61
 
58
62
  ### OpenAI API
59
63
 
@@ -81,7 +85,21 @@ The local installer starts with two provider cards:
81
85
  - An explicit statement that this is Codex JSON-RPC, not OpenAI `/v1`
82
86
  - An explicit experimental/non-production notice
83
87
 
84
- Both flows show a review screen and require a final confirmation before any
88
+ ### Codex Chat Adapter
89
+
90
+ - Label: **Codex Chat Adapter — development backends**
91
+ - Default HTTP port: `14501`
92
+ - Uses the same pinned official Codex CLI and official device-code sign-in.
93
+ - Result:
94
+ - Endpoint: `http://127.0.0.1:<port>`
95
+ - A newly generated Relmio bearer token, displayed once
96
+ - A device-code sign-in action
97
+ - An explicit server-side-only statement
98
+ - An explicit statement that `POST /chat` is Relmio-specific, not OpenAI
99
+ `/v1`
100
+ - An explicit experimental/non-production notice
101
+
102
+ All three flows show a review screen and require a final confirmation before any
85
103
  filesystem or Docker write.
86
104
 
87
105
  This release supports macOS, Linux, and Linux under WSL2. Native Windows is
@@ -94,6 +112,33 @@ say so, and the installer must reject native Windows before any write.
94
112
  Every wizard API route continues to require the existing `X-Setup-Token` and
95
113
  same-origin protections.
96
114
 
115
+ ### Chat Adapter tester APIs
116
+
117
+ The tester is available only after a live `codex-chat` installation reaches the
118
+ Ready screen. Sanitized preview mode rejects all three routes. Each route uses
119
+ the same `POST` exact-Origin and `X-Setup-Token` protections as the rest of
120
+ the local wizard.
121
+
122
+ - `POST /api/local/chat-test/key` returns only `keyId`, an RSA public JWK,
123
+ `RSA-OAEP-256`, and an expiry. Its private key remains only in the local
124
+ server's bounded, in-memory tester-session map.
125
+ - `POST /api/local/chat-test/message` accepts a literal loopback adapter base
126
+ URL, `keyId`, RSA-OAEP SHA-256 ciphertext, a bounded input, and an optional
127
+ bounded conversation ID. It returns only validated `conversationId` and
128
+ `output`.
129
+ - `POST /api/local/chat-test/reset` invalidates the specified in-memory key.
130
+
131
+ The browser sends no adapter request and stores no tester data in browser
132
+ storage. It clears the plaintext credential input before it awaits key issuance
133
+ or encryption, then retains ciphertext and key ID only in page memory. The
134
+ server does not retain prompts or transcript beyond a single request.
135
+
136
+ The local proxy accepts only `http://127.0.0.1:<1-65535>` with an optional
137
+ trailing slash. It rejects DNS names, IPv6, credentials, query strings,
138
+ fragments, paths, redirects, malformed JSON, oversized request/response data,
139
+ expiry, and concurrent use. It appends `/chat`, uses a bounded timeout, sends
140
+ no `Origin` header, and returns generic redacted failures.
141
+
97
142
  ### `GET /api/local/docker/status`
98
143
 
99
144
  Returns local Docker and Compose availability. It never returns filesystem
@@ -143,7 +188,9 @@ is released in a `finally` path after both success and failure.
143
188
  ### `POST /api/local/codex/login`
144
189
 
145
190
  Starts an official Codex App Server device-code login through a one-shot stdio
146
- App Server process attached to the same persistent Codex home volume.
191
+ App Server process attached to the selected Codex target's persistent home
192
+ volume. The request identifies either `codex-chatgpt` or `codex-chat`;
193
+ every other target is rejected before process construction.
147
194
 
148
195
  Every login attempt resolves the managed Codex directory and attests its
149
196
  schema-2 marker and matching Docker resources, even in a fresh wizard process.
@@ -249,12 +296,77 @@ The host mapping is exactly `127.0.0.1:<selected-port>:4500`.
249
296
  The capability is therefore password-equivalent and limited to a trusted,
250
297
  same-owner native client.
251
298
 
299
+ ## Codex Chat Adapter contract
300
+
301
+ The adapter container starts a dependency-free Node HTTP service and launches
302
+ the pinned official `codex app-server --strict-config --stdio` process only
303
+ for a bounded chat operation. It uses the target's private Codex home and
304
+ workspace volumes and never exposes the App Server transport on the host.
305
+
306
+ ### Listener and authentication
307
+
308
+ - Container listener: `0.0.0.0:14501`
309
+ - Host publication: `127.0.0.1:<selected-port>:14501`
310
+ - `GET /health` is the only unauthenticated, non-forwarding route.
311
+ - Every route except `GET /health` requires exactly one
312
+ `Authorization: Bearer <Relmio capability>` header.
313
+ - Only the SHA-256 verifier is persisted and comparison is constant-time.
314
+ - Requests with an `Origin` header are rejected and no CORS permission is
315
+ emitted.
316
+
317
+ ### Request and response
318
+
319
+ The accepted JSON object contains only:
320
+
321
+ ```json
322
+ {
323
+ "input": "Required nonempty conversational input",
324
+ "conversationId": "Optional App Server thread ID"
325
+ }
326
+ ```
327
+
328
+ The successful response contains only:
329
+
330
+ ```json
331
+ {
332
+ "conversationId": "The started or resumed thread ID",
333
+ "output": "The authoritative final agent message"
334
+ }
335
+ ```
336
+
337
+ The adapter performs `initialize`, `initialized`, `thread/start` or
338
+ `thread/resume`, and `turn/start`. The turn is constrained to the empty
339
+ private workspace with a read-only sandbox. Its filesystem policy denies root
340
+ by default, permits only Codex's minimal runtime paths and `/workspace`, and
341
+ explicitly denies `/home/node/.codex`; turn network access is disabled. The
342
+ instruction not to inspect or modify files, run commands, call tools, or access
343
+ external resources remains defense in depth, not the credential boundary.
344
+ `item/completed` agent-message text is authoritative; bounded deltas are kept
345
+ separate by `itemId`, with only the most recent item's text used as a
346
+ compatibility fallback. Success requires `turn/completed` with
347
+ `status: completed`.
348
+
349
+ ### Resource and failure bounds
350
+
351
+ - Header, body, input, conversation ID, stdout, stderr, line, and output sizes
352
+ are bounded.
353
+ - Concurrent chats, turn duration, child-process termination grace, and the
354
+ final wait for an unreaped process are bounded. The concurrency slot remains
355
+ occupied until the helper closes or that final bound expires.
356
+ - Client disconnect, timeout, malformed protocol, overflow, and failed turns
357
+ terminate the helper.
358
+ - HTTP errors are generic and never include App Server output, process errors,
359
+ ChatGPT credentials, or stderr.
360
+ - Installation and credential rotation verify readiness/authentication without
361
+ starting a model turn or requiring sign-in before the device-code step.
362
+
252
363
  ## Local filesystem and process boundary
253
364
 
254
365
  Managed roots:
255
366
 
256
367
  - `~/.relmio/local/openai-api`
257
368
  - `~/.relmio/local/codex-chatgpt`
369
+ - `~/.relmio/local/codex-chat`
258
370
 
259
371
  `RELMIO_HOME` may replace `~/.relmio` for testing or advanced use, but it must
260
372
  be an absolute path whose final component is `.relmio`.
@@ -282,7 +394,7 @@ Controls:
282
394
 
283
395
  ## Container hardening
284
396
 
285
- Both long-running endpoint services:
397
+ All three long-running endpoint services:
286
398
 
287
399
  - run as a non-root user;
288
400
  - set `no-new-privileges`;
@@ -326,26 +438,29 @@ gateway; it is removed immediately after seeding.
326
438
  | LAN/public exposure | literal `127.0.0.1` Compose binding plus template and runtime inspection tests |
327
439
  | Local cross-site request | bearer capability, exact Origin allowlist, strict preflight, Host validation |
328
440
  | Upstream key disclosure | separate local/upstream credentials, stdin-seeded private named volume, redacted errors, no body logging |
329
- | ChatGPT token repurposing | official App Server only; no `/v1` adapter for Codex |
441
+ | ChatGPT token repurposing | official App Server lifecycle only; the adapter is Relmio-specific and exposes no `/v1` route |
330
442
  | Command injection | validated scalar values, spawn argument arrays, no shell |
331
443
  | Managed-path takeover | refuse unmanaged roots and every symlinked component |
332
444
  | Streaming resource exhaustion | header/body/concurrency/time bounds and backpressure |
333
445
  | Docker privilege compromise | document Docker control as a privileged local boundary; mount no Docker socket into services |
334
446
  | Secret recovery from UI | show capabilities once; never use browser storage; rotate on reinstall |
335
- | Codex capability compromise | explicit credential-equivalent warning; trusted same-owner native clients only; private container volumes and no host mounts |
447
+ | Codex capability compromise | explicit trust warning; target-specific least-privilege interfaces, private container volumes, no host mounts, and server-side-only adapter use |
448
+ | Model-induced credential read | root-deny model filesystem profile, minimal runtime read allowlist, explicit `/home/node/.codex` deny, empty private workspace, and no turn network access |
336
449
 
337
450
  ## Acceptance criteria
338
451
 
339
- - The browser wizard visibly offers both local providers and the legacy VPS
452
+ - The browser wizard visibly offers all three local contracts and the legacy VPS
340
453
  path remains separate.
341
- - Platform keys are accepted only by `openai-api`; ChatGPT auth is accepted only
342
- by official App Server.
454
+ - Platform keys are accepted only by `openai-api`; both Codex targets use only
455
+ official App Server-backed ChatGPT authentication.
343
456
  - Generated Compose files publish only literal loopback bindings.
344
457
  - Every non-health gateway operation that can reach OpenAI and every App Server
345
458
  WebSocket handshake is capability-authenticated; exact-origin CORS preflight
346
459
  is a non-forwarding metadata exception.
347
460
  - Gateway unit/integration tests cover auth, origins, Host validation,
348
461
  streaming, cancellation, upstream errors, and secret redaction.
462
+ - Adapter tests cover auth, Origin rejection, request/protocol validation,
463
+ process cleanup, bounds, concurrency, final-output selection, and redaction.
349
464
  - Local installer tests prove confirmation, unmanaged-root refusal, symlink
350
465
  refusal, port collision behavior, exact Docker arguments, file modes, and
351
466
  absence of n8n commands.
@@ -1,33 +1,46 @@
1
1
  # Local Docker endpoints
2
2
 
3
3
  Relmio can install a provider endpoint in Docker on the same computer as your
4
- app. The local installer is deliberately split into two protocols with two
5
- different authentication methods:
4
+ app. The local installer keeps three explicit client contracts across two
5
+ different provider authentication methods:
6
6
 
7
7
  | Wizard option | Local interface | Upstream sign-in | Intended client |
8
8
  |---|---|---|---|
9
9
  | **OpenAI API: compatible clients** | OpenAI-compatible HTTP under `/v1` | Server-side OpenAI Platform API key only | A private local app, SDK, or same-owner development web app |
10
10
  | **Codex with ChatGPT: agent clients** | Official Codex App Server JSON-RPC over WebSocket | ChatGPT sign-in through Codex | A trusted native Codex/App Server client owned by the same person |
11
+ | **Codex Chat Adapter: development backends** | Relmio-specific HTTP `POST /chat` | ChatGPT sign-in through Codex | A trusted local backend or development server owned by the same person |
11
12
 
12
13
  Relmio does not exchange or translate a ChatGPT OAuth/session credential into
13
- an OpenAI-compatible `/v1` bearer credential. The Codex option keeps Codex's
14
- thread, turn, approval, and streamed-event semantics instead of pretending to
15
- be the OpenAI API.
14
+ an OpenAI-compatible `/v1` bearer credential. The native Codex option keeps
15
+ Codex's thread, turn, approval, and streamed-event semantics. The adapter
16
+ translates only a small Relmio-owned `/chat` contract into that official
17
+ protocol; it does not imitate the OpenAI API.
16
18
 
17
19
  This is a documentation-backed engineering boundary, not legal advice or a
18
20
  guarantee that a particular account or use case is permitted. Review the
19
21
  agreements and policies that apply to your account.
20
22
 
23
+ ## ChatGPT/Codex sign-in lifetime
24
+
25
+ ChatGPT/Codex sign-in tokens expire, but the official Codex client refreshes
26
+ them automatically during active use before they expire, so active sessions
27
+ usually continue without another browser login. The official [OpenAI
28
+ authentication documentation](https://learn.chatgpt.com/docs/auth) does not
29
+ publish a fixed 10-day lifetime; do not plan around one. This provider
30
+ credential is separate from Relmio's local capability, which remains valid
31
+ until you rotate it.
32
+
21
33
  ## Requirements
22
34
 
23
35
  - macOS, Linux, or Linux under WSL2. Native Windows is not supported because
24
36
  this release depends on POSIX owner-only directory and file permissions for
25
37
  local credentials.
26
38
  - Docker Engine or Docker Desktop with Docker Compose v2 on the local computer
27
- - A free loopback port (`12435` by default for OpenAI API or `14500` for Codex)
39
+ - A free loopback port (`12435` for OpenAI API, `14500` for native Codex,
40
+ or `14501` for the Codex Chat Adapter by default)
28
41
  - One of these provider credentials:
29
42
  - an OpenAI Platform API key for the OpenAI-compatible endpoint; or
30
- - a ChatGPT account eligible for Codex for the App Server endpoint
43
+ - a ChatGPT account eligible for Codex for either Codex target
31
44
  - A trusted local app that can keep the Relmio capability secret
32
45
 
33
46
  The local path does not need a VPS or SSH access and does not modify the
@@ -37,7 +50,8 @@ project on the local computer.
37
50
  ## Install with the browser wizard
38
51
 
39
52
  1. Start Relmio on the computer that will run the endpoint. Use one of the
40
- commands in the [README](../README.md#quick-install), or run:
53
+ commands on the [hosted install page](https://relmio.vercel.app/install),
54
+ or run:
41
55
 
42
56
  ```bash
43
57
  npx --yes --ignore-scripts relmio@latest
@@ -45,8 +59,8 @@ project on the local computer.
45
59
 
46
60
  2. Open the one-time local wizard URL printed in the terminal and choose
47
61
  **Local endpoints**.
48
- 3. Choose **OpenAI API: compatible clients** or **Codex with ChatGPT: agent
49
- clients**.
62
+ 3. Choose **OpenAI API: compatible clients**, **Codex with ChatGPT: agent
63
+ clients**, or **Codex Chat Adapter: development backends**.
50
64
  4. Keep the default port or select another unused local port. For the OpenAI
51
65
  API option, add any browser origins that must be allowed and enter your
52
66
  Platform API key.
@@ -64,6 +78,7 @@ inside its managed path. Its local files live under:
64
78
  ```text
65
79
  ~/.relmio/local/openai-api
66
80
  ~/.relmio/local/codex-chatgpt
81
+ ~/.relmio/local/codex-chat
67
82
  ```
68
83
 
69
84
  Advanced or test environments can set `RELMIO_HOME` before starting the
@@ -81,8 +96,9 @@ To replace only the credential used by your local client, select **Rotate client
81
96
  credential** on the installed endpoint's Ready screen. Relmio first stages and
82
97
  shows the new one-time capability while the previous capability remains active.
83
98
  It then updates and validates the managed Compose configuration, recreates only
84
- the attested service, and verifies the new bearer against `/v1/models` or the
85
- authenticated Codex WebSocket handshake before reporting success.
99
+ the attested service, and verifies the new bearer against `/v1/models`, the
100
+ authenticated Codex WebSocket handshake, or the adapter's authenticated probe
101
+ before reporting success.
86
102
 
87
103
  This client-only rotation preserves the upstream Platform API key in its private
88
104
  named volume and preserves the Codex home and workspace volumes. If activation
@@ -97,8 +113,8 @@ managed update. Relmio verifies the marker and Docker resource ownership and
97
113
  reuses the installation's unique Compose identity. For `openai-api`, provide the
98
114
  current or replacement Platform API key during that full update; Relmio reseeds
99
115
  its private named volume independently from the local client capability. A full
100
- `codex-chatgpt` update retains the private Codex home and workspace volumes unless
101
- you explicitly delete them.
116
+ `codex-chatgpt` or `codex-chat` update retains the private Codex home and
117
+ workspace volumes unless you explicitly delete them.
102
118
 
103
119
  Do not hand-edit the marker, Compose file, credential volume, or verifier. If
104
120
  the marker or resource labels do not attest as one Relmio installation, the
@@ -124,8 +140,9 @@ For a quick private test:
124
140
 
125
141
  ```bash
126
142
  export RELMIO_LOCAL_KEY="<capability shown once by the wizard>"
127
- curl http://127.0.0.1:12435/v1/models \
128
- -H "Authorization: Bearer $RELMIO_LOCAL_KEY"
143
+ printf 'Authorization: Bearer %s\n' "$RELMIO_LOCAL_KEY" |
144
+ curl http://127.0.0.1:12435/v1/models --header @-
145
+ unset RELMIO_LOCAL_KEY
129
146
  ```
130
147
 
131
148
  The upstream key is passed only over stdin to a transient, network-disabled
@@ -174,12 +191,15 @@ official verification URL, enter the device code, and complete authentication.
174
191
  Relmio starts the login through the official Codex App Server account method;
175
192
  it never returns the resulting ChatGPT access or refresh tokens.
176
193
 
177
- A compatible Codex CLI can connect like this:
194
+ A compatible Codex CLI can connect like this. Read the capability without
195
+ putting it in the command line:
178
196
 
179
197
  ```bash
180
- export CODEX_REMOTE_TOKEN="<capability shown once by the wizard>"
198
+ read -r -s CODEX_REMOTE_TOKEN
199
+ printf '\n'
181
200
  codex --remote ws://127.0.0.1:14500 \
182
201
  --remote-auth-token-env CODEX_REMOTE_TOKEN
202
+ unset CODEX_REMOTE_TOKEN
183
203
  ```
184
204
 
185
205
  This is not an OpenAI `/v1` endpoint. A client must implement the official
@@ -205,9 +225,87 @@ workspace and credential volume. The service receives no host directory,
205
225
  Docker socket, SSH key, browser profile, or host home-directory mount. This
206
226
  reduces host exposure; it does not make an untrusted App Server client safe.
207
227
 
228
+ ## Codex Chat Adapter: development backends
229
+
230
+ The adapter result screen provides:
231
+
232
+ ```text
233
+ Endpoint: http://127.0.0.1:14501
234
+ Authorization: Bearer <the Relmio capability shown once by the wizard>
235
+ Protocol: Relmio Codex Chat HTTP
236
+ ```
237
+
238
+ After completing the same official Codex device-code sign-in, a local backend
239
+ can start a conversation with. Read the bearer rather than placing it in a
240
+ shell command:
241
+
242
+ ```bash
243
+ read -r -s RELMIO_CODEX_CHAT_KEY
244
+ printf '\n'
245
+ printf 'Authorization: Bearer %s\n' "$RELMIO_CODEX_CHAT_KEY" |
246
+ curl --fail-with-body --silent --show-error \
247
+ --request POST http://127.0.0.1:14501/chat \
248
+ --header @- \
249
+ --header "Content-Type: application/json" \
250
+ --data '{"input":"Reply with a short hello."}'
251
+ unset RELMIO_CODEX_CHAT_KEY
252
+ ```
253
+
254
+ The response contains only the App Server thread ID and final conversational
255
+ text:
256
+
257
+ ```json
258
+ {
259
+ "conversationId": "thread-id-from-the-first-response",
260
+ "output": "Hello!"
261
+ }
262
+ ```
263
+
264
+ Send that `conversationId` with the next `input` to continue the same
265
+ conversation. The adapter initializes the official App Server, starts or
266
+ resumes the thread, runs a read-only conversational turn, and returns the
267
+ authoritative final agent message. The model sandbox has no network access and
268
+ uses a root-deny filesystem policy that reads only Codex's minimal runtime
269
+ paths and the empty private workspace. It explicitly denies
270
+ `/home/node/.codex`, the private volume containing the ChatGPT session.
271
+
272
+ This route is deliberately not `/v1/chat/completions` or `/v1/responses`.
273
+ OpenAI SDKs and tools that require those schemas still need the Platform-backed
274
+ OpenAI API target. The adapter rejects every request carrying an `Origin`
275
+ header and sends no CORS permission, so browser JavaScript must not call it
276
+ directly. Keep the bearer in a trusted local backend or development server and
277
+ let the browser call that server's own session-aware route.
278
+
279
+ The adapter is experimental because it depends on the experimental App Server
280
+ interface. It is loopback-only, single-owner development tooling, not a hosted,
281
+ LAN, multi-user, or production service. It enforces bounded request bodies,
282
+ output, concurrency, process lifetime, and sanitized failures, but those
283
+ controls do not create a general-purpose API entitlement.
284
+
285
+ ### In-wizard Chat Adapter tester
286
+
287
+ The Ready screen for an installed Chat Adapter includes a narrow local tester.
288
+ It is intended for a literal `http://127.0.0.1:PORT` adapter address only. The
289
+ browser never calls the adapter: it calls the local wizard's existing
290
+ same-origin, `X-Setup-Token` protected APIs, and the wizard makes the
291
+ server-side `POST /chat` request without an `Origin` header.
292
+
293
+ When the user secures the displayed client credential, the browser clears the
294
+ input and encrypts it with the tester's short-lived RSA-OAEP SHA-256 public
295
+ key. The private key exists only in local server memory, expires after a few
296
+ minutes, has a bounded session count, and can be invalidated with **Forget
297
+ tester**. The browser retains only ciphertext and key ID for the test session;
298
+ it keeps prompts and transcript only in current-page memory and DOM.
299
+
300
+ This reduces accidental credential transit and storage exposure. It is not
301
+ encryption at rest or end-to-end encryption, and it cannot protect against a
302
+ compromised browser, extension, or local machine. The tester rejects redirects,
303
+ non-loopback URLs, malformed or oversized data, concurrent key use, and
304
+ adapter failures with redacted messages.
305
+
208
306
  ## Network and container boundary
209
307
 
210
- Both local Compose projects publish exactly one host mapping:
308
+ Every local Compose project publishes exactly one host mapping:
211
309
 
212
310
  ```text
213
311
  127.0.0.1:<selected-port>:<container-port>
@@ -216,12 +314,13 @@ Both local Compose projects publish exactly one host mapping:
216
314
  They are not available through the computer's LAN address. Each long-running
217
315
  service runs as a non-root user, drops Linux capabilities, sets
218
316
  `no-new-privileges`, uses a read-only root filesystem, and has bounded
219
- temporary storage and resource limits. Neither service mounts the Docker
317
+ temporary storage and resource limits. None of the services mounts the Docker
220
318
  socket or a general host directory. The one-shot OpenAI seed helper has no
221
319
  network, port, or logs and retains only `CHOWN` while running as root long
222
320
  enough to atomically set ownership on the volume entry.
223
321
  The OpenAI gateway receives only its private API-key named volume, mounted
224
- read-only; Codex receives no host path and uses separate private named volumes.
322
+ read-only; both Codex targets receive no host path and use target-specific
323
+ private named volumes.
225
324
 
226
325
  The loopback binding and capability are complementary controls. Other
227
326
  processes running as the same local user may still be able to reach a loopback
@@ -244,7 +343,8 @@ literal values only after confirming all of these conditions:
244
343
  In every example, manually replace each angle-bracket placeholder with the
245
344
  already-validated literal. Do not use `eval`, source the JSON, or construct a
246
345
  Docker command from unvalidated marker text. The only valid service name is
247
- `gateway` for `openai-api` or `codex` for `codex-chatgpt`.
346
+ `gateway` for `openai-api`, `codex` for `codex-chatgpt`, or `codex-chat`
347
+ for `codex-chat`.
248
348
 
249
349
  ### Recover from a failed install
250
350
 
@@ -285,7 +385,7 @@ service:
285
385
  docker --host <dockerHost> compose \
286
386
  --project-name <projectName> \
287
387
  --file <absolute-managed-compose> \
288
- rm --stop --force <gateway-or-codex>
388
+ rm --stop --force <gateway-or-codex-or-codex-chat>
289
389
  ```
290
390
 
291
391
  This recovery command does not target other services, remove the project
@@ -344,7 +444,8 @@ docker --host <dockerHost> compose \
344
444
  `--volumes` irreversibly deletes the managed Codex home and workspace volumes,
345
445
  including the container's ChatGPT credentials. After Docker confirms the
346
446
  matching resources are gone, you may remove only the exact
347
- `~/.relmio/local/codex-chatgpt` managed directory through your file manager.
447
+ `~/.relmio/local/codex-chatgpt` or `~/.relmio/local/codex-chat` managed
448
+ directory for the target you verified through your file manager.
348
449
 
349
450
  ## Troubleshooting
350
451
 
@@ -358,8 +459,9 @@ matching resources are gone, you may remove only the exact
358
459
  - **Browser request rejected:** enter the exact page origin, including scheme
359
460
  and port, then update the managed endpoint. Wildcards are intentionally not
360
461
  supported.
361
- - **Browser cannot connect to Codex:** this is expected. App Server's raw
362
- WebSocket is for trusted native clients, not browser-origin connections.
462
+ - **Browser cannot connect to Codex:** this is expected. Both Codex targets
463
+ reject browser-origin requests. Use the raw App Server from a trusted native
464
+ client or keep the Chat Adapter bearer in a local backend.
363
465
  - **Codex reports signed out:** repeat the device-code sign-in in the local
364
466
  wizard. Never copy a Codex credential file between users.
365
467
  - **Native Windows:** this local Docker feature is unsupported. Run Relmio in a
@@ -373,6 +475,7 @@ credentials and Codex/ChatGPT authentication:
373
475
 
374
476
  - [OpenAI API authentication](https://developers.openai.com/api/reference/overview#authentication)
375
477
  - [Codex authentication](https://learn.chatgpt.com/docs/auth)
478
+ - [Codex permission profiles](https://learn.chatgpt.com/docs/permissions)
376
479
  - [Codex App Server protocol and WebSocket limitations](https://learn.chatgpt.com/docs/app-server)
377
480
  - [Codex for Open Source program terms](https://learn.chatgpt.com/docs/codex-for-oss-terms)
378
481
 
@@ -0,0 +1,77 @@
1
+ # Reference
2
+
3
+ ## Chat Adapter test commands
4
+
5
+ The experimental Chat Adapter is a loopback-only, Relmio-specific `POST /chat`
6
+ service for trusted local backends or development servers. It is not OpenAI
7
+ `/v1` and it rejects browser `Origin` headers.
8
+
9
+ Set the endpoint, then read the one-time credential without typing the literal
10
+ credential into shell history:
11
+
12
+ ```bash
13
+ export RELMIO_CHAT_BASE_URL="http://127.0.0.1:14501"
14
+ read -r -s RELMIO_CHAT_CLIENT_CREDENTIAL
15
+ printf '\n'
16
+ ```
17
+
18
+ Start a conversation:
19
+
20
+ ```bash
21
+ printf 'Authorization: Bearer %s\n' "$RELMIO_CHAT_CLIENT_CREDENTIAL" |
22
+ curl --fail-with-body --silent --show-error \
23
+ --request POST "$RELMIO_CHAT_BASE_URL/chat" \
24
+ --header @- \
25
+ --header "Content-Type: application/json" \
26
+ --data '{"input":"Reply with exactly: adapter works"}'
27
+ ```
28
+
29
+ Copy the returned `conversationId`, then send a continuation:
30
+
31
+ ```bash
32
+ export RELMIO_CONVERSATION_ID="CONVERSATION_ID_FROM_THE_PREVIOUS_RESPONSE"
33
+ printf 'Authorization: Bearer %s\n' "$RELMIO_CHAT_CLIENT_CREDENTIAL" |
34
+ curl --fail-with-body --silent --show-error \
35
+ --request POST "$RELMIO_CHAT_BASE_URL/chat" \
36
+ --header @- \
37
+ --header "Content-Type: application/json" \
38
+ --data "{\"input\":\"Continue with one short sentence.\",\"conversationId\":\"$RELMIO_CONVERSATION_ID\"}"
39
+ ```
40
+
41
+ Unset the shell credential when finished:
42
+
43
+ ```bash
44
+ unset RELMIO_CHAT_CLIENT_CREDENTIAL
45
+ ```
46
+
47
+ ## Raw Codex App Server command
48
+
49
+ The raw Codex App Server transport is JSON-RPC over WebSocket, experimental,
50
+ and high-trust. It is for trusted native clients only; it is not an
51
+ OpenAI-compatible `/v1` endpoint. Read the one-time local capability into a
52
+ named environment variable rather than placing it in the command:
53
+
54
+ ```bash
55
+ read -r -s RELMIO_CODEX_CLIENT_CREDENTIAL
56
+ printf '\n'
57
+ codex --remote ws://127.0.0.1:14500 \
58
+ --remote-auth-token-env RELMIO_CODEX_CLIENT_CREDENTIAL
59
+ unset RELMIO_CODEX_CLIENT_CREDENTIAL
60
+ ```
61
+
62
+ Keep that capability private. A trusted Codex client can control the isolated
63
+ container and may be able to recover its ChatGPT session credential.
64
+
65
+ ## Local wizard tester API
66
+
67
+ The browser never contacts the adapter directly. While the local wizard is
68
+ running, its same-origin, `X-Setup-Token` protected APIs are:
69
+
70
+ | Method | Route | Purpose |
71
+ | --- | --- | --- |
72
+ | `POST` | `/api/local/chat-test/key` | Issue one ephemeral RSA-OAEP public key |
73
+ | `POST` | `/api/local/chat-test/message` | Send encrypted credential and one bounded chat turn |
74
+ | `POST` | `/api/local/chat-test/reset` | Invalidate the tester key and clear the browser transcript |
75
+
76
+ The local proxy accepts only a literal `http://127.0.0.1:PORT` adapter base
77
+ URL and appends `/chat` itself.