265 lines
13 KiB
Markdown
265 lines
13 KiB
Markdown
# CLI Flags Plan — sovereign_browser
|
|
|
|
## Rename: "random" → "generate"
|
|
|
|
As part of this work, rename the login method string `"random"` to
|
|
`"generate"` everywhere it appears. "random" implies an arbitrary method
|
|
choice; "generate" accurately describes what happens — a fresh local key
|
|
is generated and used for login.
|
|
|
|
Affected locations (from `grep`):
|
|
|
|
| File | Line | Change |
|
|
|---|---|---|
|
|
| [`src/agent_login.c`](src/agent_login.c:184) | 184 | `make_login_data("random", ...)` → `"generate"` |
|
|
| [`src/agent_login.c`](src/agent_login.c:425) | 425 | error string listing methods |
|
|
| [`src/agent_login.c`](src/agent_login.c:440) | 440 | `strcmp(method, "random")` → `"generate"` |
|
|
| [`src/agent_login.c`](src/agent_login.c:451) | 451 | error string listing methods |
|
|
| [`src/agent_mcp.c`](src/agent_mcp.c:137) | 137 | `login` tool description + enum |
|
|
| [`src/agent_mcp.c`](src/agent_mcp.c:138) | 138 | `login` tool JSON schema enum |
|
|
| [`src/agent_mcp.c`](src/agent_mcp.c:145) | 145 | `switch_identity` tool description |
|
|
| [`src/agent_mcp.c`](src/agent_mcp.c:146) | 146 | `switch_identity` tool JSON schema enum |
|
|
| [`tests/test_agent_login.py`](tests/test_agent_login.py) | — | any test using `method: "random"` |
|
|
| [`README.md`](README.md) | — | any documentation referencing "random" |
|
|
| `.roorules` | — | the `login` example uses `"method":"random"` |
|
|
|
|
The internal C function `login_random()` in
|
|
[`src/agent_login.c`](src/agent_login.c) can keep its name (internal) or
|
|
be renamed to `login_generate()` for consistency — recommend renaming
|
|
for clarity.
|
|
|
|
## Goal
|
|
|
|
Add command-line flags to `sovereign_browser` so that startup URLs, agent
|
|
server settings, session behavior, and — critically — Nostr login can be
|
|
controlled without the GTK login dialog. This enables headless/automated
|
|
use (agents, CI, scripting) while preserving the existing interactive
|
|
flow as the default.
|
|
|
|
## Current State
|
|
|
|
- [`main()`](src/main.c:493) reads only `argv[1]` as an optional start URL
|
|
([`src/main.c:502`](src/main.c:502)). No `getopt`, no `--help`.
|
|
- [`gtk_init(&argc, &argv)`](src/main.c:496) runs first and would consume
|
|
GTK's own flags; our parser must run **before** `gtk_init` so we can
|
|
strip our flags out of `argv` before GTK sees them (otherwise GTK aborts
|
|
on unknown options like `--login-method`).
|
|
- Login methods are enumerated in
|
|
[`key_store_method_t`](src/key_store.h:19) and exercised by
|
|
[`agent_login()`](src/agent_login.h:38) which already accepts a cJSON
|
|
params object. The CLI login path can reuse `agent_login()` directly —
|
|
it calls `app_set_signer()` and sets the same global state the GTK
|
|
dialog would.
|
|
- Settings live in [`browser_settings_t`](src/settings.h:25) and are
|
|
loaded from `~/.sovereign_browser/settings.conf`. CLI flags should
|
|
**override** settings, not replace them.
|
|
|
|
## Design Principles
|
|
|
|
1. **Reuse `agent_login()`** for CLI login — it already does everything
|
|
the GTK dialog does, returns structured results, and sets global state
|
|
via `app_set_signer()`. The CLI parser just builds the cJSON params
|
|
object from flags and calls it.
|
|
2. **Parse before `gtk_init()`** so GTK doesn't choke on our flags. We
|
|
strip recognized flags from `argc/argv` and pass the reduced vector to
|
|
`gtk_init()`.
|
|
3. **Flags override settings.conf** but do not write to it. A flag is a
|
|
one-shot override for this invocation.
|
|
4. **Login flags are mutually exclusive at the method level** — specify
|
|
one `--login-*` method; method-specific args are validated against it.
|
|
5. **`--login-method generate` is the zero-config path** — generates a
|
|
fresh key, logs in, and skips the dialog. Ideal for agents/CI.
|
|
6. **Backward compatible** — `./sovereign_browser https://example.com`
|
|
still works as today (positional URL).
|
|
7. **Use `getopt_long`** (POSIX, available on Linux, C99-compatible).
|
|
No external deps.
|
|
|
|
## Proposed Flag Set
|
|
|
|
### Browser / Startup
|
|
|
|
| Flag | Arg | Description |
|
|
|---|---|---|
|
|
| `--url <url>` (repeatable) | URL | Open one or more URLs in tabs at startup. Positional URLs (existing behavior) are still accepted and appended after any `--url` flags. |
|
|
| `--new-tab-url <url>` | URL | Override `settings.new_tab_url` for this run (used by Ctrl+T / new-tab button). |
|
|
| `--no-session-restore` | — | Skip [`session_restore()`](src/session.c) even if `settings.restore_session` is true. |
|
|
| `--session-restore` | — | Force session restore even if disabled in settings. |
|
|
| `--max-tabs <n>` | int | Override `settings.max_tabs` for this run. |
|
|
| `--version`, `-V` | — | Print `SB_VERSION` (from [`src/version.h`](src/version.h)) and exit 0. |
|
|
| `--help`, `-h` | — | Print usage and exit 0. |
|
|
|
|
### Agent Server
|
|
|
|
| Flag | Arg | Description |
|
|
|---|---|---|
|
|
| `--port <port>` | int | Override `settings.agent_server_port` (default 17777). |
|
|
| `--no-agent` | — | Disable the agent MCP server for this run (overrides `settings.agent_server_enabled`). |
|
|
| `--agent` | — | Force-enable the agent server even if disabled in settings. |
|
|
| `--agent-origin <origin>` (repeatable) | string | Append to `agent_allowed_origins` for this run. |
|
|
|
|
### Login (mutually exclusive method flags)
|
|
|
|
Exactly one of these may be specified. If none is specified, the GTK
|
|
login dialog runs as today.
|
|
|
|
| Flag | Arg | Description |
|
|
|---|---|---|
|
|
| `--login-method <m>` | `generate\|local\|seed\|readonly\|nip46\|nsigner` | Select login method. Required to use any other `--login-*` flag. `generate` needs no further flags. |
|
|
| `--nsec <nsec1...>` | string | (local) nsec bech32 private key. |
|
|
| `--privkey <hex>` | string | (local) 64-char hex private key. Alternative to `--nsec`. |
|
|
| `--mnemonic <words>` | string | (seed) BIP-39 mnemonic, quoted ("word1 word2 ..."). |
|
|
| `--account <n>` | int | (seed) BIP-44 account index, default 0. |
|
|
| `--npub <npub1...>` | string | (readonly) npub bech32 public key. |
|
|
| `--pubkey <hex>` | string | (readonly) 64-char hex pubkey. Alternative to `--npub`. |
|
|
| `--bunker <url>` | URL | (nip46) `bunker://...` remote signer URL. |
|
|
| `--nsigner-transport <t>` | `serial\|unix\|tcp\|qrexec` | (nsigner) transport type. |
|
|
| `--nsigner-device <path>` | string | (nsigner) device path / socket / host:port / qube. |
|
|
| `--nsigner-service <name>` | string | (nsigner) qrexec service name (qrexec transport only). |
|
|
| `--nsigner-index <n>` | int | (nsigner) NIP-06 nostr_index, default 0. |
|
|
| `--no-save-identity` | — | Do not persist the CLI-provided identity to `~/.sovereign_browser/identity.json` (default: save, matching GTK dialog behavior). |
|
|
| `--login-timeout <ms>` | int | Override `settings.agent_login_timeout_ms`. Only meaningful when the GTK dialog is shown (no `--login-method`). |
|
|
|
|
### Diagnostics
|
|
|
|
| Flag | Arg | Description |
|
|
|---|---|---|
|
|
| `--verbose`, `-v` | — | Increase log verbosity (g_print messages). Repeatable for more detail. |
|
|
| `--quiet`, `-q` | — | Suppress non-error log output. |
|
|
|
|
## Usage Examples
|
|
|
|
```bash
|
|
# Existing behavior — still works
|
|
./sovereign_browser https://example.com
|
|
|
|
# Agent / CI: generated key, no dialog, open a page
|
|
./sovereign_browser --login-method generate --url https://example.com
|
|
|
|
# Local key from env, skip dialog, custom agent port
|
|
./sovereign_browser --login-method local --nsec "$NSEC" --port 18888
|
|
|
|
# Read-only (npub), no session restore, two tabs
|
|
./sovereign_browser --login-method readonly --npub npub1... \
|
|
--no-session-restore --url https://a.com --url https://b.com
|
|
|
|
# Seed phrase, account 1
|
|
./sovereign_browser --login-method seed \
|
|
--mnemonic "abandon abandon abandon ... about" --account 1
|
|
|
|
# NIP-46 remote signer
|
|
./sovereign_browser --login-method nip46 --bunker "bunker://..."
|
|
|
|
# n_signer hardware via qrexec
|
|
./sovereign_browser --login-method nsigner --nsigner-transport qrexec \
|
|
--nsigner-device nostr_signer --nsigner-service qubes.NsignerRpc
|
|
|
|
# Disable agent server, just browse
|
|
./sovereign_browser --no-agent --url https://example.com
|
|
|
|
# Version / help
|
|
./sovereign_browser --version
|
|
./sovereign_browser --help
|
|
```
|
|
|
|
## Implementation Plan
|
|
|
|
### Files Touched
|
|
|
|
| File | Change |
|
|
|---|---|
|
|
| `src/cli.h` (new) | Declares `cli_args_t` struct and `cli_parse()`. |
|
|
| `src/cli.c` (new) | Implements `getopt_long` parsing, validation, usage text, and `cli_login()` wrapper that builds cJSON and calls `agent_login()`. |
|
|
| `src/main.c` | Call `cli_parse(&argc, &argv)` **before** `gtk_init()`. Apply overrides to a mutable copy of settings. If `--login-method` was given, call `cli_login()` and skip `do_login()`. Replace `start_url` logic with the `--url` / positional list. |
|
|
| `Makefile` | Add `src/cli.o` to `OBJS`. |
|
|
| `README.md` | Document the flags. |
|
|
| `browser.sh` | Optional: accept extra args after `start`/`restart` and forward them to the binary (e.g. `./browser.sh start --login-method generate`). |
|
|
|
|
### Todo List
|
|
|
|
1. Rename "random" → "generate" in [`src/agent_login.c`](src/agent_login.c), [`src/agent_mcp.c`](src/agent_mcp.c), [`tests/test_agent_login.py`](tests/test_agent_login.py), [`README.md`](README.md), and `.roorules`. Rename internal `login_random()` → `login_generate()`.
|
|
2. Create `src/cli.h` with the `cli_args_t` struct and API.
|
|
3. Implement `src/cli.c`: `getopt_long` table, validation, `print_usage()`, `cli_login()` wrapper.
|
|
4. Wire `cli_parse()` into [`main()`](src/main.c:493) before `gtk_init()`; strip recognized flags from `argv`.
|
|
5. Apply CLI overrides to a mutable settings snapshot used for this run (port, agent enabled, max tabs, new-tab URL, session restore).
|
|
6. Implement `--url` (repeatable) + positional URL collection; pass list to tab manager at startup.
|
|
7. Implement `--login-method` path: build cJSON params, call `agent_login()`, check `success`, skip GTK dialog on success, exit 1 on failure.
|
|
8. Implement `--version` / `--help` (print and exit 0 before GTK init).
|
|
9. Implement `--no-save-identity` (skip `key_store_save()` on CLI login).
|
|
10. Update `Makefile` to compile `src/cli.c`.
|
|
11. Update `browser.sh` to forward extra args to the binary.
|
|
12. Update `README.md` with a CLI flags section.
|
|
13. Add a smoke test: `./sovereign_browser --login-method generate --no-agent --version` style checks (can be a shell test under `tests/`).
|
|
|
|
### Key Implementation Details
|
|
|
|
**Parse order:** `cli_parse()` must run before `gtk_init()` because GTK
|
|
aborts on unknown `--options`. `getopt_long` with `optind` lets us
|
|
repack `argv` so GTK only sees positional URLs. Pattern:
|
|
|
|
```c
|
|
cli_args_t args;
|
|
if (cli_parse(&argc, &argv, &args) != 0) {
|
|
return EXIT_FAILURE; /* --help / --version / parse error */
|
|
}
|
|
gtk_init(&argc, &argv); /* sees only positional URLs now */
|
|
```
|
|
|
|
**Login reuse:** `cli_login()` builds the exact cJSON shape documented in
|
|
[`agent_login.h`](src/agent_login.h:27) and calls `agent_login()`. On
|
|
`success: true`, the global state is already set via `app_set_signer()`,
|
|
so we set `g_logged_in = TRUE` and skip `do_login()`. On failure, print
|
|
the error message to stderr and exit non-zero.
|
|
|
|
**Settings override:** Introduce a `settings_apply_cli_overrides(const
|
|
cli_args_t *)` that mutates the global singleton in memory (not on disk)
|
|
after `settings_load()`. The rest of the code reads via
|
|
`settings_get()` unchanged.
|
|
|
|
**Repeatable `--url`:** Collect into a `GPtrArray` in `cli_args_t`.
|
|
After session restore fails (or is skipped), open each URL in its own
|
|
tab via `tab_manager_new_tab(url)`. If no URLs given, fall back to
|
|
`settings.new_tab_url`.
|
|
|
|
**`--no-save-identity`:** The GTK dialog path calls `key_store_save()`
|
|
after successful login. The CLI path should do the same by default so
|
|
the identity persists, but `--no-save-identity` skips it — useful for
|
|
ephemeral/CI runs that should not write to `~/.sovereign_browser/`.
|
|
|
|
## Mermaid: Startup Decision Flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[main: cli_parse] --> B{help or version?}
|
|
B -- yes --> Z[print and exit 0]
|
|
B -- no --> C[gtk_init with stripped argv]
|
|
C --> D[settings_load + apply CLI overrides]
|
|
D --> E[agent_server_start unless --no-agent]
|
|
E --> F{login-method flag set?}
|
|
F -- yes --> G[cli_login: build cJSON, call agent_login]
|
|
G --> H{success?}
|
|
H -- no --> Y[print error, exit 1]
|
|
H -- yes --> I[optionally key_store_save unless --no-save-identity]
|
|
F -- no --> J[do_login: GTK dialog as today]
|
|
I --> K[session_restore unless --no-session-restore]
|
|
J --> K
|
|
K --> L{restored or URLs provided?}
|
|
L -- URLs --> M[open each --url / positional URL in a tab]
|
|
L -- none --> N[open settings.new_tab_url]
|
|
M --> O[gtk_main]
|
|
N --> O
|
|
```
|
|
|
|
## Open Questions
|
|
|
|
1. Should `--login-method generate` auto-set `--no-save-identity` by default
|
|
(since a generated key is usually ephemeral)? Proposal: **no** — keep
|
|
explicit, but document that random keys *will* be saved unless
|
|
`--no-save-identity` is passed.
|
|
2. Should we support reading `--nsec` / `--mnemonic` from a file path
|
|
(e.g. `--nsec-file /run/secrets/nsec`) to avoid leaking secrets in
|
|
`ps`/shell history? Proposal: defer to a follow-up; out of scope for
|
|
this plan but worth noting.
|
|
3. `--headless` (no GTK window, agent server only)? WebKitGTK requires a
|
|
display; true headless would need a virtual framebuffer or a non-WebKit
|
|
path. Out of scope for this plan.
|