13 KiB
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 |
184 | make_login_data("random", ...) → "generate" |
src/agent_login.c |
425 | error string listing methods |
src/agent_login.c |
440 | strcmp(method, "random") → "generate" |
src/agent_login.c |
451 | error string listing methods |
src/agent_mcp.c |
137 | login tool description + enum |
src/agent_mcp.c |
138 | login tool JSON schema enum |
src/agent_mcp.c |
145 | switch_identity tool description |
src/agent_mcp.c |
146 | switch_identity tool JSON schema enum |
tests/test_agent_login.py |
— | any test using method: "random" |
README.md |
— | any documentation referencing "random" |
.roorules |
— | the login example uses "method":"random" |
The internal C function login_random() in
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()reads onlyargv[1]as an optional start URL (src/main.c:502). Nogetopt, no--help.gtk_init(&argc, &argv)runs first and would consume GTK's own flags; our parser must run beforegtk_initso we can strip our flags out ofargvbefore GTK sees them (otherwise GTK aborts on unknown options like--login-method).- Login methods are enumerated in
key_store_method_tand exercised byagent_login()which already accepts a cJSON params object. The CLI login path can reuseagent_login()directly — it callsapp_set_signer()and sets the same global state the GTK dialog would. - Settings live in
browser_settings_tand are loaded from~/.sovereign_browser/settings.conf. CLI flags should override settings, not replace them.
Design Principles
- Reuse
agent_login()for CLI login — it already does everything the GTK dialog does, returns structured results, and sets global state viaapp_set_signer(). The CLI parser just builds the cJSON params object from flags and calls it. - Parse before
gtk_init()so GTK doesn't choke on our flags. We strip recognized flags fromargc/argvand pass the reduced vector togtk_init(). - Flags override settings.conf but do not write to it. A flag is a one-shot override for this invocation.
- Login flags are mutually exclusive at the method level — specify
one
--login-*method; method-specific args are validated against it. --login-method generateis the zero-config path — generates a fresh key, logs in, and skips the dialog. Ideal for agents/CI.- Backward compatible —
./sovereign_browser https://example.comstill works as today (positional URL). - 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() 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) 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
# 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
- Rename "random" → "generate" in
src/agent_login.c,src/agent_mcp.c,tests/test_agent_login.py,README.md, and.roorules. Rename internallogin_random()→login_generate(). - Create
src/cli.hwith thecli_args_tstruct and API. - Implement
src/cli.c:getopt_longtable, validation,print_usage(),cli_login()wrapper. - Wire
cli_parse()intomain()beforegtk_init(); strip recognized flags fromargv. - Apply CLI overrides to a mutable settings snapshot used for this run (port, agent enabled, max tabs, new-tab URL, session restore).
- Implement
--url(repeatable) + positional URL collection; pass list to tab manager at startup. - Implement
--login-methodpath: build cJSON params, callagent_login(), checksuccess, skip GTK dialog on success, exit 1 on failure. - Implement
--version/--help(print and exit 0 before GTK init). - Implement
--no-save-identity(skipkey_store_save()on CLI login). - Update
Makefileto compilesrc/cli.c. - Update
browser.shto forward extra args to the binary. - Update
README.mdwith a CLI flags section. - Add a smoke test:
./sovereign_browser --login-method generate --no-agent --versionstyle checks (can be a shell test undertests/).
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:
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 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
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
- Should
--login-method generateauto-set--no-save-identityby default (since a generated key is usually ephemeral)? Proposal: no — keep explicit, but document that random keys will be saved unless--no-save-identityis passed. - Should we support reading
--nsec/--mnemonicfrom a file path (e.g.--nsec-file /run/secrets/nsec) to avoid leaking secrets inps/shell history? Proposal: defer to a follow-up; out of scope for this plan but worth noting. --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.