Skip to content

mostrix first run with automatic settings.toml#41

Merged
grunch merged 6 commits into
mainfrom
fix-windows-launch
Mar 18, 2026
Merged

mostrix first run with automatic settings.toml#41
grunch merged 6 commits into
mainfrom
fix-windows-launch

Conversation

@arkanoider

@arkanoider arkanoider commented Mar 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #40: Mostrix no longer fails on first run when settings.toml is missing. On first launch, it auto-generates a working ~/.mostrix/settings.toml with sensible defaults.

What changed

  • On startup, Mostrix checks for ~/.mostrix/settings.toml.
  • If it does not exist, Mostrix:
    • generates a fresh Nostr keypair and stores it as nsec_privkey
    • sets defaults aligned with the issue:
      • relays = ["wss://relay.mostro.network"]
      • user_mode = "user"
      • pow = 0
      • currencies_filter = []
      • mostro_pubkey = "82fa8cb978b43c79b2156585bac2c022276a21d2aead6d9f7c575c005be88390"
    • writes the generated file to ~/.mostrix/settings.toml (with 0600 permissions on Unix)
    • prints the generated npub to stdout so the user can record it

Compatibility

  • If ~/.mostrix/settings.toml already exists, it is loaded normally.
  • The existing placeholder-values guard is kept: if a legacy default file exists but still contains placeholder values, Mostrix exits with a clear error.

Testing

  • cargo test
  • Manual verification suggestion: delete/rename ~/.mostrix/settings.toml, run mostrix, confirm the file is created and the app starts.

Summary by CodeRabbit

  • New Features

    • Automatic settings file generation on first run with embedded defaults and displayed public key
    • New portable/executable-adjacent and legacy locations supported for loading settings
  • Bug Fixes

    • Stronger config validation to reject incomplete or deprecated values
  • Chores

    • Updated ignore list to include IDE and build artifacts and cleaned formatting

@coderabbitai

coderabbitai Bot commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@arkanoider has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 57 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 488a1b74-c269-46af-b484-6ced9e0cd392

📥 Commits

Reviewing files that changed from the base of the PR and between e949199 and 2228088.

📒 Files selected for processing (1)
  • src/settings.rs

Walkthrough

Adds first-run settings auto-generation using an embedded defaults template (generates a Nostr keypair, writes settings.toml, validates configs) and refactors settings load paths; also reorganizes .gitignore into build and IDE sections.

Changes

Cohort / File(s) Summary
Git ignore updates
\.gitignore
Reorganized ignore list: removed .cursor/commands, added build and ide headers, added .cursorrules, .cursor/, .vscode/, and new log/target entries; formatting adjusted.
Settings initialization
src/settings.rs
Embedded default settings template via include_str!; added public load_settings_from_disk() and comprehensive init_or_load_settings_from_disk() flow that checks executable-adjacent and legacy home paths, auto-generates settings.toml on first run (creates Nostr keypair, fills defaults, writes file with secure permissions), validates config and prints generated npub.
Manifest / metadata
Cargo.toml
Minor manifest metadata reference updated (manifest-file-name noted).

Sequence Diagram

sequenceDiagram
    actor User
    participant App as Application Startup
    participant Config as Settings Module
    participant FS as File System
    participant Nostr as Nostr SDK

    User->>App: run binary
    App->>Config: load_settings_from_disk()
    Config->>FS: check executable-adjacent `settings.toml`
    alt found
        FS-->>Config: return path
        Config->>Config: deserialize & validate
        Config-->>App: settings loaded
    else not found
        Config->>FS: check legacy `~/.mostrix/settings.toml`
        alt legacy found
            FS-->>Config: return path
            Config->>Config: deserialize & validate
            Config-->>App: settings loaded
        else first run
            Config->>Nostr: generate keypair (nsec + npub)
            Nostr-->>Config: new keys
            Config->>Config: populate embedded defaults + generated values
            Config->>FS: create settings.toml (write, set mode 600)
            FS-->>Config: file created
            Config-->>App: settings generated (prints npub)
        end
    end
    App-->>User: startup complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Improve settings #20 — touches src/settings.rs and currencies/mostro_pubkey handling; likely overlaps with validation and defaults.
  • Create settings.toml #19 — earlier settings module/init implementation that this change builds upon.

Poem

🐇 I hopped in at first run, bright and keen,
I planted a key where none had been.
A config sprouted, relays in a row,
Npub displayed—a tiny glow.
Hop, hop—now launch and off we go!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main change: automatic generation of settings.toml on first run, which is the primary objective of this PR.
Linked Issues check ✅ Passed The code changes implement all key requirements from issue #40: detecting missing settings.toml, auto-generating with sensible defaults including a fresh keypair, persisting the config, and informing the user of the generated npub.
Out of Scope Changes check ✅ Passed The .gitignore changes add build and IDE section headers with related ignore patterns, which are minor scope adjustments to support development workflow without altering the core implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-windows-launch
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mostronatorcoder mostronatorcoder Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

✅ Checks Passed

  1. CI Status: Passing on latest commit (930b4c8)
  2. Implements #40: Auto-generates settings.toml on first run
  3. Security: Uses 0o600 permissions on Unix for the generated file
  4. UX: Prints generated npub so user knows their identity
  5. Backwards compatible: Existing configs still work, placeholder guard kept

👍 Good Implementation Details

  • Uses include_str! to embed default template at compile time
  • create_dir_all instead of create_dir (handles nested paths)
  • create_new(true) prevents overwriting existing files (race condition safe)
  • Cross-platform: Unix gets 0o600, others use fs::write
  • Generates fresh Nostr keypair with Keys::generate()
  • Clear error message if legacy file has placeholder values

⚠️ Minor Observations (non-blocking)

  1. Issue #40 asked for binary-relative lookup first:

    "1. look for the settings.toml in the same directory where the binary file"

    This PR only checks ~/.mostrix/. Not a blocker since ~/.mostrix/ is a reasonable canonical location, but worth noting the deviation from the original spec.

  2. No test for the new generation logic:
    The auto-generation path isn't unit tested. Consider adding a test that:

    • Creates a temp dir
    • Calls the init logic
    • Verifies file is created with correct defaults

    This can be a follow-up.

📋 Checklist

  • CI passes
  • Implements the issue requirements
  • Secure file permissions
  • Backwards compatible
  • User-friendly (prints npub)
  • Binary-relative lookup (spec deviation, non-blocking)
  • Unit test for generation path (can be follow-up)

Verdict

LGTM — Clean implementation that solves the first-run UX problem. The spec deviations are minor and the implementation is solid.

🦀

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/settings.rs`:
- Around line 106-111: The code only checks hidden_file
(~/.{package}/settings.toml) and never probes the executable directory for a
bundled settings.toml; update the path discovery to also build an executable_dir
candidate (resolve the current executable via std::env::current_exe(), get its
parent, join "settings.toml") and pass that path through the same placeholder
guard/validation used for hidden_file (the same load/placeholder-check logic
around hidden_dir/hidden_file and wherever the 129-151 block repeats) so a
portable install with settings.toml next to the binary is recognized before
falling back to "no config anywhere".
- Around line 129-205: There is a TOCTOU when writing hidden_file: replace the
non-atomic write path with an atomic create_new attempt and, on AlreadyExists,
fall back to loading the just-created file and reusing the same
placeholder-guarded validation you have earlier; specifically, when persisting
the generated settings (the block that currently writes toml_string to
hidden_file), use OpenOptions::new().create_new(true).write(true) (or create a
temp file and fs::rename for non-unix) so the write fails atomically if another
process won the race, and on Err(e) where e.kind() == AlreadyExists call
load_settings_from_path(&hidden_file) and perform the same placeholder checks
(mostro_pubkey, nsec_privkey) and return those settings instead of the in-memory
generated Settings/NKeys (Keys::generate(), nsec, npub).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 938606b7-f651-445f-9c30-bb7e921ca54c

📥 Commits

Reviewing files that changed from the base of the PR and between aff84ee and 930b4c8.

📒 Files selected for processing (2)
  • .gitignore
  • src/settings.rs

Comment thread src/settings.rs
Comment thread src/settings.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/settings.rs (1)

301-317: ⚠️ Potential issue | 🟡 Minor

save_settings may not preserve 0600 permissions on Unix.

The initial file creation uses .mode(0o600) for security, but save_settings uses fs::write(), which on some systems may reset permissions to the default umask (often 0644). This could expose the nsec_privkey to other users on multi-user systems.

Suggested fix to preserve permissions
+#[cfg(unix)]
+fn write_settings_file(path: &std::path::Path, content: &str) -> std::io::Result<()> {
+    use std::io::Write;
+    use std::os::unix::fs::OpenOptionsExt;
+    let mut file = fs::OpenOptions::new()
+        .write(true)
+        .create(true)
+        .truncate(true)
+        .mode(0o600)
+        .open(path)?;
+    file.write_all(content.as_bytes())
+}
+
+#[cfg(not(unix))]
+fn write_settings_file(path: &std::path::Path, content: &str) -> std::io::Result<()> {
+    fs::write(path, content)
+}
+
 pub fn save_settings(settings: &Settings) -> Result<(), anyhow::Error> {
     // ...
-    fs::write(&hidden_file, toml_string)
+    write_settings_file(&hidden_file, &toml_string)
         .map_err(|e| anyhow::anyhow!("Failed to write settings file: {}", e))?;
     Ok(())
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/settings.rs` around lines 301 - 317, save_settings currently uses
fs::write which can create the file with default umask (e.g., 0644) and expose
secrets; change it to open the hidden_file with std::fs::OpenOptions and on Unix
use std::os::unix::fs::OpenOptionsExt::mode(0o600) when creating/truncating,
write the toml bytes via the opened file (and flush), and if the file already
exists check its metadata().permissions().mode() and call fs::set_permissions to
enforce 0o600 when it differs; update references in save_settings (hidden_file,
toml_string) and add the necessary use of OpenOptions/Unix extension.
🧹 Nitpick comments (1)
src/settings.rs (1)

209-285: Consider consolidating the Unix/non-Unix file write logic.

The two #[cfg] blocks are nearly identical (~35 lines each), differing only in the .mode(0o600) call. This duplication increases maintenance burden.

Suggested refactor direction
use std::io::{ErrorKind, Write};

let write_result: std::io::Result<()> = (|| {
    #[cfg(unix)]
    let file = {
        use std::os::unix::fs::OpenOptionsExt;
        fs::OpenOptions::new()
            .write(true)
            .create_new(true)
            .mode(0o600)
            .open(&hidden_file)
    };
    
    #[cfg(not(unix))]
    let file = fs::OpenOptions::new()
        .write(true)
        .create_new(true)
        .open(&hidden_file);
    
    file?.write_all(toml_string.as_bytes())
})();

match write_result {
    Ok(()) => {}
    Err(e) if e.kind() == ErrorKind::AlreadyExists => {
        let settings = load_settings_from_path(&hidden_file)?;
        // placeholder guard...
        return Ok(settings);
    }
    Err(e) => {
        anyhow::bail!("Could not write generated settings.toml: {}", e);
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/settings.rs` around lines 209 - 285, The two cfg-specific blocks
duplicate almost identical logic writing toml_string to hidden_file; consolidate
by creating a single write attempt that uses a cfg-only OpenOptions variant for
the mode (e.g. assign a cfg-defined let file = ... using
OpenOptionsExt::mode(0o600) under #[cfg(unix)] and a plain OpenOptions under
#[cfg(not(unix)]; then perform file?.write_all(toml_string.as_bytes()) inside a
small Result-returning closure or block, capture the Result as write_result, and
reuse the existing match handling (Err(e) if e.kind() ==
ErrorKind::AlreadyExists -> load_settings_from_path(&hidden_file) and
placeholder checks on settings.mostro_pubkey/settings.nsec_privkey, other Err ->
anyhow::bail!). Ensure you still import ErrorKind and Write where needed and
preserve the exact error messages and placeholder guard logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/settings.rs`:
- Around line 250-285: The non-Unix branch uses file.write_all(...) but doesn't
import the std::io::Write trait into that #[cfg(not(unix))] block, causing a
compile error; add use std::io::Write; inside the same #[cfg(not(unix))] scope
(near where hidden_file is opened and file is matched) so file.write_all(...)
resolves, leaving the rest of the logic (load_settings_from_path, checking
settings.mostro_pubkey/nsec_privkey and returning settings) unchanged.

---

Outside diff comments:
In `@src/settings.rs`:
- Around line 301-317: save_settings currently uses fs::write which can create
the file with default umask (e.g., 0644) and expose secrets; change it to open
the hidden_file with std::fs::OpenOptions and on Unix use
std::os::unix::fs::OpenOptionsExt::mode(0o600) when creating/truncating, write
the toml bytes via the opened file (and flush), and if the file already exists
check its metadata().permissions().mode() and call fs::set_permissions to
enforce 0o600 when it differs; update references in save_settings (hidden_file,
toml_string) and add the necessary use of OpenOptions/Unix extension.

---

Nitpick comments:
In `@src/settings.rs`:
- Around line 209-285: The two cfg-specific blocks duplicate almost identical
logic writing toml_string to hidden_file; consolidate by creating a single write
attempt that uses a cfg-only OpenOptions variant for the mode (e.g. assign a
cfg-defined let file = ... using OpenOptionsExt::mode(0o600) under #[cfg(unix)]
and a plain OpenOptions under #[cfg(not(unix)]; then perform
file?.write_all(toml_string.as_bytes()) inside a small Result-returning closure
or block, capture the Result as write_result, and reuse the existing match
handling (Err(e) if e.kind() == ErrorKind::AlreadyExists ->
load_settings_from_path(&hidden_file) and placeholder checks on
settings.mostro_pubkey/settings.nsec_privkey, other Err -> anyhow::bail!).
Ensure you still import ErrorKind and Write where needed and preserve the exact
error messages and placeholder guard logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: db68d289-5e1f-497b-a851-12ba4f8d8822

📥 Commits

Reviewing files that changed from the base of the PR and between 930b4c8 and e949199.

📒 Files selected for processing (1)
  • src/settings.rs

Comment thread src/settings.rs

@AndreaDiazCorreia AndreaDiazCorreia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tACK

@grunch grunch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@grunch grunch merged commit 757f152 into main Mar 18, 2026
11 checks passed
@grunch grunch deleted the fix-windows-launch branch March 18, 2026 22:14
mostronatorcoder Bot added a commit that referenced this pull request Mar 19, 2026
Updates documentation to reflect the new first-run behavior implemented
in PR #41:

## Changes

### Settings section rewritten
- Documented zero-config first run experience
- Listed auto-generated defaults (keypair, relay, user_mode, etc.)
- Explained configuration priority (user dir > portable > auto-generate)
- Added note about 0600 file permissions on Unix

### Example settings.toml updated
- Removed placeholder values that suggested manual editing
- Shows actual defaults used by auto-generation
- Added note explaining values are auto-generated

### TODO list
- Marked auto-generate feature as complete with issue link

## Related
- Closes #40 (via PR #41)
- Documents behavior from PR #41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-generate settings.toml with sensible defaults on first run

3 participants