# RustBox validation Release source checks: - one root Cargo package (`Cargo.toml` at repository root); - no `backend/` application directory; - no separate `frontend/` directory; - no `.ts` or `.tsx` source files; - no Node/Bun/Next/React build dependency; - UI files live under `src/web/` and are embedded into the Rust executable; - mail endpoints are configuration-owned; the login request contains only email/password/session preference; - authenticated mail routes reuse the resolved session connection profile; - CSS is built by Cargo `build.rs` from `src/web/css/manifest.txt`; - gradients appear only when the "Style gradients" switch is on; no Unicode pseudo-icons in the browser UI; - all UI action icons are served from the embedded SVG sprite; - `dev.sh` runs the root Cargo package; - native release build outputs `target/release/rustbox`; Full release gate on a host with Rust/Cargo and dependency access: ```bash ./scripts/build-release.sh ``` The release script runs formatting validation, locked tests, Clippy with warnings denied, and a locked release build before accepting the binary. ## Roundcube contacts and identities migration The migration is a standalone Python administration tool: `scripts/migrate_roundcube.py`. It does not start RustBox and is not exposed by the application UI/API. By default it reads RustBox database settings from `RUSTBOX_CONFIG` or `/etc/rustbox/rustbox.toml`, recognizes `database_backend = "sqlite" | "mysql" | "pgsql"`, and uses `database_url`. For SQLite without `database_url`, it follows RustBox behavior and uses `data_dir/rustbox.sqlite`. CLI values can override the target independently with `--config`, `--target-backend` and `--target-url`. The Roundcube source is MySQL and can be supplied either as `--roundcube-url` or as `--roundcube-host`, `--roundcube-port`, `--roundcube-database`, `--roundcube-db-user` and `--roundcube-db-password`. The default mode migrates every matching Roundcube user. RustBox accounts must already be provisioned so their isolated table prefixes exist in `rustbox_accounts`. Unmatched users are skipped and reported. A single account can be selected with `--roundcube-account`. Dependencies: ```bash python3 -m pip install pymysql # additionally only for a PostgreSQL RustBox target: python3 -m pip install 'psycopg[binary]' ``` Recommended dry run using the active RustBox config: ```bash ./scripts/migrate_roundcube.py \ --roundcube-url 'mysql://roundcube:secret@127.0.0.1/roundcube' \ --dry-run ``` Run the migration: ```bash ./scripts/migrate_roundcube.py \ --roundcube-url 'mysql://roundcube:secret@127.0.0.1/roundcube' ``` Example with explicit Roundcube connection fields and an overridden MySQL RustBox target: ```bash ./scripts/migrate_roundcube.py \ --config /etc/rustbox/rustbox.toml \ --target-backend mysql \ --target-url 'mysql://rustbox:secret@db/rustbox' \ --roundcube-host rc-db \ --roundcube-database roundcube \ --roundcube-db-user roundcube \ --roundcube-db-password secret ``` Use `--roundcube-prefix rc_` when Roundcube uses prefixed table names. The migration is idempotent for practical reruns: contacts are matched by email and identities by their stable identity fields before insert/update.