Development¶
Development Workflow¶
- Make Changes: Edit frontend (HTML/CSS/JS) or backend (Rust) code
- Hot Reload: Frontend changes reload automatically in dev mode
- Rebuild Backend: Rust changes require rebuilding (
cargo tauri devhandles this) - Test: Use browser mode for faster iteration, Tauri mode for full testing
Running in Development Mode¶
cd tauri-app
cargo tauri dev
This will: - Build the Rust backend - Start the Tauri application - Enable hot reload for frontend changes - Show console output for debugging
Running Frontend in Browser (Development Mode)¶
You can run the frontend as a static site in your browser while connecting to the backend via HTTP:
# On macOS/Linux:
./run-browser-dev.sh
# On Windows:
run-browser-dev.bat
# Or manually:
# Terminal 1: Start HTTP server
cd backend
cargo run --bin http-server --release
# Terminal 2: Serve frontend
cd frontend
python3 -m http.server 8080
# Then open http://127.0.0.1:8080 in your browser
The HTTP server runs on http://127.0.0.1:1420 and the frontend is served on http://127.0.0.1:8080. The frontend automatically detects browser mode and uses HTTP instead of Tauri APIs.
Building for Production¶
cd tauri-app
cargo tauri build
The built application will be in backend/target/release/ (installers under backend/target/release/bundle/).
Build Targets¶
- Windows:
.exeinstaller - macOS:
.appbundle and.dmginstaller - Linux:
.deband.AppImagepackages - Android:
.apkfile
Releasing¶
The single source of truth for the app version is tauri-app/backend/tauri.conf.json → version. Both the CI build (.github/workflows/build.yml) and the landing page template (.github/workflows/mkdocs-deploy.yml) read from it.
Version format¶
Use plain semver: 1.0.4, 1.0.5, etc. Do not add pre-release suffixes like -beta or -rc1 — the Windows MSI bundler rejects non-numeric pre-release identifiers. Pre-release status is instead marked on the GitHub release itself via the prerelease: true flag in build.yml's release job. Flip it to false when shipping a stable version.
Release steps¶
- Bump
versionintauri-app/backend/tauri.conf.json. - Commit the bump on
master. - Create an annotated tag matching the version with a
vprefix:git tag -a v1.0.5 -m "Release v1.0.5" - Push the tag first, then the branch:
git push origin v1.0.5 # wait for the build workflow's release job to publish assets git push origin master - Once the GitHub Release assets are live, publish the public site by merging
masterintodocs(see below).
Why the ordering matters¶
Two workflows react to a release:
build.ymltriggers on the tag push. It builds Windows/Linux/macOS installers on three runners, then itsreleasejob attaches them to a GitHub Release named after the tag. End-to-end: ~15–30 minutes.mkdocs-deploy.ymltriggers on pushes to thedocsbranch. It readstauri.conf.json, substitutes{{VERSION_TAG}}/{{VERSION}}placeholders in the landing page, and deploys the landing page + docs to GitHub Pages (gh-pages). End-to-end: ~3–5 minutes.
Because the deploy is gated on the docs branch rather than master, bumping the
version and tagging on master does not change the public website. The site —
including the version number and download links — only updates when you merge into
docs. So after a release, wait until the GitHub Release assets exist, then merge
master → docs. This deliberately closes the window where the landing page could
point at download URLs for a release that is still compiling (which would 404).
Publishing docs (the docs branch)¶
The public site is published from the long-lived docs branch; master is the
authoring source of truth.
- To publish: merge
master → docs. The push todocstriggers the deploy. - Documentation-only changes may originate on
docs(the site updates immediately) and then be merged back tomaster. App and version changes originate onmasterand reach the site when you merge todocs. - Sync the two branches with ordinary merge commits only — never squash or rebase them. Squashing or rebasing rewrites SHAs and makes later merges in the opposite direction see phantom conflicts, causing the branches to drift.
- The version shown on the site is always whatever
tauri.conf.jsonholds on thedocsbranch, so keep the version bump offdocsuntil the release is live.
Manual trigger¶
Both workflows also support workflow_dispatch from the Actions tab, useful for
re-deploying or testing build changes without cutting a release.
Code Organization¶
Frontend¶
- Modular Architecture: Each major feature has its own service module
- State Management: Centralized state in
app-state.js - DOM Management: Centralized DOM utilities in
dom-manager.js - Service Layer: Backend communication abstracted in
tauri-service.js
Backend¶
- Command Handlers: Tauri commands are defined in
lib.rsand registered viatauri::generate_handler!.main.rsis a thin shim that callslib::run. - Business Logic: Feature-specific logic in separate modules (
email.rs,nostr.rs,crypto.rs, etc.) - Database Layer: SQLite operations in
database.rs - Key Storage: OS-keychain vault in
keychain.rs - Type Safety: Shared types in
types.rs
Debugging¶
Frontend Debugging¶
- Use browser DevTools when running in browser mode
- Console logs are available in Tauri dev mode
- Check
tauri-service.jsfor backend communication issues
Backend Debugging¶
- Rust compiler errors show in terminal
- Use
println!macros for debugging (visible in terminal) - Check Tauri console output for command execution
Database Debugging¶
- Database file location: OS-specific app data directory
- Use SQLite browser tools to inspect database
- Database schema defined in
database.rs
Testing¶
Manual Testing¶
- Test each page functionality
- Test email sending/receiving
- Test DM sending/receiving
- Test contact loading
- Test settings persistence
Integration Testing¶
- Test email + Nostr integration
- Test encryption/decryption
- Test database operations
- Test relay connectivity
Contributing¶
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
Code Style¶
JavaScript¶
- Use ES6+ features
- Follow module pattern (services, utilities)
- Use meaningful variable names
- Add comments for complex logic
Rust¶
- Follow Rust naming conventions
- Use
Resulttypes for error handling - Add documentation comments
- Keep functions focused and small