144 lines
7.4 KiB
Markdown
144 lines
7.4 KiB
Markdown
# Plan: Interactive multi-transport selection at startup
|
|
|
|
Status: design / ready for review.
|
|
|
|
Related:
|
|
- [`README.md`](../README.md) §7 Transport, §8.3 Qubes OS qube, §9 Usage
|
|
- [`src/main.c`](../src/main.c) — argument parsing, main loop, TUI
|
|
- [`src/server.c`](../src/server.c) — `server_ctx_t`, `server_start`, `server_handle_one`
|
|
- [`plans/qrexec_persistent_bridge.md`](qrexec_persistent_bridge.md) — bridge design
|
|
|
|
---
|
|
|
|
## 1. Motivation
|
|
|
|
The current CLI has many transport flags (`--listen`, `--socket-name`, `--bridge-source-trusted`, `--auth`, `--allow-all`) that the user must know in advance. For interactive use, this is unfriendly — the user just wants to answer "how should other programs reach this signer?"
|
|
|
|
Additionally, the user may want **multiple transports active simultaneously** (e.g. unix socket for local clients + TCP for FIPS mesh clients + qrexec bridge for other qubes). Today only one `--listen` mode is supported at a time.
|
|
|
|
---
|
|
|
|
## 2. Design
|
|
|
|
### 2.1 Interactive transport menu at startup
|
|
|
|
After mnemonic entry and before the running phase, if no `--listen` flag was given (i.e. interactive mode), present a multi-select menu:
|
|
|
|
```text
|
|
Transport — how should other programs reach this signer?
|
|
Select one or more (space to toggle, enter to confirm):
|
|
|
|
[x] 1. Local Unix socket (same machine/qube)
|
|
[ ] 2. Qubes qrexec bridge (other qubes via qrexec, no network)
|
|
[ ] 3. TCP listener (FIPS mesh or local network)
|
|
[ ] 4. Qrexec one-shot (legacy, one request per invocation)
|
|
|
|
[a] select all
|
|
|
|
(at least one must be selected)
|
|
```
|
|
|
|
Each selected transport gets configured with sensible defaults:
|
|
|
|
| Choice | What it starts | Defaults |
|
|
|---|---|---|
|
|
| 1. Unix socket | `--listen unix` | socket name `nsigner` (or random if collision) |
|
|
| 2. Qrexec bridge | `--listen unix --bridge-source-trusted` | socket name `nsigner`, accepts qrexec preamble |
|
|
| 3. TCP | `--listen tcp:[::]:11111` | bind all interfaces, port 11111 |
|
|
| 4. Qrexec one-shot | `--listen qrexec` | one request via stdin, then exit |
|
|
|
|
**Choices 1 and 2 can coexist** — they're both unix listeners, just with different identity handling. Choice 2 implies choice 1's socket. If both are selected, the bridge-source-trusted flag is set on the single unix listener (it handles both local and bridge connections).
|
|
|
|
**Choice 3 (TCP) can coexist with 1+2** — it's a separate listener on a different fd.
|
|
|
|
**Choice 4 (qrexec one-shot) is mutually exclusive** with all others — it uses stdin/stdout and exits after one request. If selected alone, it runs the one-shot path. If selected with others, it's ignored with a warning (or: the user is told it can't combine).
|
|
|
|
### 2.2 Multi-listener architecture
|
|
|
|
Currently `main()` creates one `server_ctx_t` and polls `server.listen_fd` + `STDIN_FILENO`. To support multiple listeners:
|
|
|
|
- Create an array of `server_ctx_t` (up to 3: unix, tcp, and optionally a second unix for bridge — though 1+2 collapse into one).
|
|
- Each `server_ctx_t` gets its own `server_start()`.
|
|
- The poll loop expands to poll all listener fds + STDIN_FILENO.
|
|
- When a listener fd has activity, call `server_handle_one()` on that context.
|
|
- The TUI status display shows all active transports.
|
|
|
|
```text
|
|
Connections
|
|
listen: unix @nsigner (bridge-source-trusted)
|
|
listen: tcp [::]:11111
|
|
client: nsigner --socket-name nsigner client '<json>'
|
|
```
|
|
|
|
### 2.3 Coexistence with CLI flags
|
|
|
|
- If `--listen` is given on the command line, **skip the interactive menu** (automation/scripting path unchanged).
|
|
- If no `--listen` is given and stdin is a TTY, **show the interactive menu**.
|
|
- If no `--listen` is given and stdin is NOT a TTY, **default to unix socket** (current behavior, for `--mnemonic-stdin` / `--mnemonic-fd` supervised launches).
|
|
|
|
This preserves backward compatibility: all existing flags and scripts work unchanged. The menu is purely an interactive convenience.
|
|
|
|
### 2.4 TUI implementation
|
|
|
|
The menu uses the existing TUI rendering helpers (`tui_render_content_screen`, `tui_print`). It appears after mnemonic acceptance and before the status display. Navigation:
|
|
|
|
- `1`-`4` or space: toggle a choice
|
|
- `a`: select all (except 4, which is mutually exclusive)
|
|
- `a`: select all (except 4, which is mutually exclusive)
|
|
- `enter`: confirm and proceed to running phase (at least one must be selected)
|
|
|
|
After confirmation, the selected listeners are started and the status display renders.
|
|
|
|
### 2.5 Security considerations
|
|
|
|
- The menu is purely a UX layer — it doesn't change the security model. Each transport still goes through the same policy/approval pipeline.
|
|
- `--bridge-source-trusted` is only set if the user explicitly selects the qrexec bridge option. It's never silently enabled.
|
|
- TCP listener still requires auth envelopes (existing behavior).
|
|
- The menu doesn't offer `--allow-all`; that remains a CLI flag for users who want it.
|
|
|
|
---
|
|
|
|
## 3. Implementation checklist
|
|
|
|
### 3.1 Multi-listener support in `main.c`
|
|
- [ ] Replace single `server_ctx_t server` with an array (e.g. `server_ctx_t servers[3]`).
|
|
- [ ] Add a `server_count` variable.
|
|
- [ ] Expand the poll loop to poll all `servers[i].listen_fd` + STDIN_FILENO.
|
|
- [ ] On activity, call `server_handle_one(&servers[i], ...)` for the matching fd.
|
|
- [ ] `server_stop()` all on shutdown.
|
|
|
|
### 3.2 Interactive menu
|
|
- [ ] Add `prompt_transport_selection()` function in `main.c` (after mnemonic load, before server start).
|
|
- [ ] Returns a bitmask of selected transports.
|
|
- [ ] Only called when `listen_mode` was not set by CLI and stdin is a TTY.
|
|
- [ ] Uses `tui_render_content_screen` + `read_line_stdin` for input.
|
|
|
|
### 3.3 Transport setup from menu selection
|
|
- [ ] Unix: `server_init` + `server_start` with `NSIGNER_LISTEN_UNIX`, socket name `nsigner`.
|
|
- [ ] Qrexec bridge: same as unix + `server_set_bridge_source_trusted(&server, 1)`.
|
|
- [ ] TCP: `server_init` + `server_start` with `NSIGNER_LISTEN_TCP`, target `tcp:[::]:11111`.
|
|
- [ ] Qrexec one-shot: existing `NSIGNER_LISTEN_QREXEC` path (stdin, one request, exit).
|
|
|
|
### 3.4 TUI status display
|
|
- [ ] Update `render_status()` to show multiple active listeners.
|
|
- [ ] Show `(bridge-source-trusted)` annotation on unix listeners that have it.
|
|
|
|
### 3.5 Testing
|
|
- [ ] Interactive: start nsigner with no `--listen`, select unix+TCP, verify both listeners work.
|
|
- [ ] Interactive: select qrexec bridge, verify bridge connections get `qubes:<vm>` identity.
|
|
- [ ] CLI: `--listen tcp:[::]:11111` still works (skips menu).
|
|
- [ ] CLI: `--listen unix --bridge-source-trusted` still works (skips menu).
|
|
- [ ] Non-TTY: `--mnemonic-stdin` without `--listen` defaults to unix (no menu).
|
|
|
|
---
|
|
|
|
## 4. Open questions
|
|
|
|
1. **Should the menu remember the last selection?** n_signer has no config files (by design). We could store the preference in an env var or a `/tmp` file, but that violates the zero-filesystem-footprint principle. **Decision: no persistence — the menu appears fresh each time.**
|
|
|
|
2. **Should there be a hotkey to add/remove transports at runtime?** E.g. press `t` in the TUI to bring up the transport menu again. This is a nice-to-have but adds complexity. **Decision: defer — the menu is startup-only for now.**
|
|
|
|
3. **TCP port selection in the menu?** The menu could ask for a port number if TCP is selected. **Decision: default to 11111, let the user override with `--listen tcp:...` if they need a different port. Keep the menu simple.**
|
|
|
|
4. **Socket name selection?** Same — default to `nsigner`, override with `--socket-name` if needed. **Decision: default, no prompt.**
|