# MikroTik Backup System Version **0.0.1** is the initial Rust rewrite of the previous Python/TypeScript application. It is a single self-hosted service with an embedded web UI, SQLite storage, RouterOS/SwOS backup workflows, scheduling, retention, notifications, and local OpenAPI documentation. ## Quick start ### Docker Compose ```bash docker compose build app docker compose up -d ``` The default published address is `127.0.0.1:5581`. Persistent application data is stored in the `data` volume. The image uses `rust:1.99.1-alpine3.24` for the build stage and `alpine:3.24` for runtime. The service runs as UID/GID `10001` with a read-only root filesystem. ### Native build Requires Rust 1.99.1 or compatible toolchain and the native dependencies needed by the crates in `Cargo.lock`. ```bash cargo build --release --locked ./target/release/mikrotik-backup serve ``` Or use the launcher: ```bash ./start.sh ``` ## First account Registration is disabled by default. Set `DEFAULT_ADMIN_PASSWORD` before first startup, or let the application create the initial administrator password file under `DATA_DIR/secrets/initial-admin-password`. New installations and new accounts default to **English**. Imported accounts preserve their saved language preference. The UI also supports Polish, Spanish, and Norwegian. ## Main configuration | Variable | Default | Purpose | | --- | --- | --- | | `DATA_DIR` | `./storage` | Database, backups, secrets, SSH known hosts | | `DATABASE_PATH` | `/routeros_backup_next.db` | SQLite database path | | `LISTEN_ADDR` | `127.0.0.1:5581` | HTTP listener | | `API_PREFIX` | `/api` | API base path | | `TIMEZONE` | `Europe/Warsaw` | Scheduler timezone | | `SECRET_KEY` | generated/persisted when absent | JWT signing secret | | `ALLOW_REGISTRATION` | `false` | Allow account registration | | `DEFAULT_ADMIN_USERNAME` | `admin` | Initial administrator name | | `DEFAULT_ADMIN_PASSWORD` | empty | Initial administrator password | | `SSH_HOST_KEY_POLICY` | `accept-new` | `accept-new` or `strict` | | `SSH_TIMEOUT_SECONDS` | `30` | Device SSH timeout | | `MAX_PARALLEL_BACKUPS` | `4` | Concurrent backup limit | | `CORS_ORIGINS` | `[]` | Explicit allowed HTTP(S) origins | Run configuration validation without starting the server: ```bash ./target/release/mikrotik-backup check-config ``` ## Data layout ```text DATA_DIR/ ├── routeros_backup_next.db ├── backups/ │ ├── exports// │ └── binary// ├── secrets/ └── ssh/ ``` RouterOS text exports are stored under `backups/exports`; binary RouterOS backups and SwOS `.swb` files are stored under `backups/binary`. ## Legacy Python/TypeScript import The importer is explicit and never runs during normal startup. Stop the old application first and import into a **new** Rust destination. ```bash ./import-python.sh /srv/mikrotik-backup-old ``` Docker Compose example: ```bash docker compose run --rm \ -v /srv/mikrotik-backup-old:/legacy:ro \ app import-python --source /legacy ``` The importer copies accounts, devices, settings, logs, backup metadata, and every referenced backup file. It validates existing backup checksums when present and refuses to publish an incomplete destination. See [docs/migration.md](docs/migration.md). ## RouterOS and SwOS RouterOS uses SSH-based export/backup operations. SwOS uses its web interface and downloads `/backup.swb`, with Digest authentication first and Basic as fallback. Connection testing distinguishes general web reachability from backup-endpoint availability. SwOS details and troubleshooting are in [docs/switchos.md](docs/switchos.md). ## Configuration comparison `.rsc` files support: - side-by-side and unified diff; - changed-lines-only mode and search; - paged browser rendering to limit DOM size; - semantic RouterOS analysis that groups changes by configuration section and identifies added, removed, and modified commands/fields; - masking of sensitive values in semantic summaries. Binary backups are not diffed. ## Embedded web assets and caching `build.rs` combines the UI sources listed in `web/assets.list`. Each compiled CSS/JavaScript bundle receives a content hash in its filename, for example: ```text /assets/app..js /assets/app..css /assets/docs..js /assets/docs..css ``` The HTML shell is generated with those exact filenames. Hashed assets use: ```text Cache-Control: public, max-age=31536000, immutable ``` HTML and API responses remain non-cacheable by default. `/favicon.ico` intentionally returns `204 No Content`. ## API documentation Interactive local documentation: ```text /docs ``` OpenAPI JSON: ```text /openapi.json ``` Swagger UI is embedded locally and does not require a CDN. ## Validation Frontend unit tests: ```bash node web/tests/core.test.cjs ``` JavaScript syntax: ```bash node --check web/js/i18n.js node --check web/js/core.js node --check web/js/views.js node --check web/js/app.js ``` Rust checks when a toolchain is available: ```bash cargo fmt --check cargo test --locked cargo clippy --locked --all-targets -- -D warnings ``` The browser smoke tests under `web/tests/` require Playwright. ## Security and dependencies The service uses request size/time limits, JWT validation, password hashing, SSH host-key verification, secret redaction, restricted filesystem permissions, and a non-root container runtime. Dependency audit notes and the local `libssh2-sys` patch provenance are summarized in [docs/security.md](docs/security.md). Third-party license notices are in `docs/THIRD_PARTY_NOTICES.txt`.