Files
2026-10-28 09:16:54 +01:00

21 KiB
Raw Permalink Blame History

Port coverage matrix — 0.0.1

Scope and source identity

The supplied source code, rather than the interface concept alone, defines functional coverage. The archive contains no .git metadata, so the supplied archive SHA-256 values identify the reviewed inputs:

Archive SHA-256
mikrotik_backup_system-master.zip 3be24e72b1816070a78ec7ee7336ae0a7f884db070dc019a8afdd9c229c05f47
mikrotik_backup_system_rust_migration.zip 65e4ee36cd60e81e4ff25a6d12d5fd7913b9360b61c96b7301b3de550c00a104

The source has 36 active API method/path combinations, all represented in the Axum router. Paths below use the default /api prefix; API_PREFIX remains configurable.

The test column identifies source tests and their intended coverage. A test's presence is not proof of a successful run. The revision-specific validation report records actual test, interface, build, and process checks. Controlled fixture servers exercise protocols without contacting production devices. No physical MikroTik or live Pushover account was supplied.

Contract test key

These test names are in tests/api_contract.rs.

Key Full test name
A auth_json_form_preferences_password_and_expiry
R registration_and_login_rate_limit
D devices_defaults_validation_secrets_and_disabled_operations
O ownership_applies_to_every_device_and_backup_operation
F files_filters_preview_diff_download_zip_and_bulk_deletion
S settings_secrets_validation_reveal_scheduler_and_notification_errors
L logs_retention_and_embedded_web_do_not_expose_storage

API operations

Method and path Rust file / handler Test and coverage
POST /api/auth/register src/api/auth.rs / register R: registration disabled/enabled, conflicts, validation
POST /api/auth/login src/api/auth.rs / login A: JSON and form; R: failed-login throttling
GET /api/auth/me src/api/auth.rs / me A: account, missing/invalid token, no password hash in JSON
PUT /api/auth/preferences src/api/auth.rs / preferences A: four languages, font, unsupported values
POST /api/auth/change-password src/api/auth.rs / change_password A: current password, new login, rejection of old password
GET /api/dashboard src/api/system.rs / dashboard O: owner-scoped counts; F: counts updated after deletion
GET /api/routers src/api/devices.rs / list O: user list isolation
GET /api/routers/ping-statuses src/api/devices.rs / ping D: disabled flag; ICMP tests in routeros.rs
POST /api/routers src/api/devices.rs / create D: RouterOS/SwOS, default ports, names, secrets
GET /api/routers/{id} src/api/devices.rs / get_one D: metadata and flags; O: another user's record
PUT /api/routers/{id} src/api/devices.rs / update D: validation, flags, secret preservation/clearing; O
DELETE /api/routers/{id} src/api/devices.rs / remove D: deletion and 404; O: ownership protection
GET /api/routers/{id}/test-connection src/api/devices.rs / test_connection O: authorization; local protocol fixtures in routeros.rs
GET /api/backups src/api/backups.rs / list F: type, device, day, search, sorting; O
GET /api/backups/router/{id} src/api/backups.rs / for_router O: device authorization
POST /api/backups/routers/export-all src/api/backups.rs / export_all D: disabled devices skipped; backup::run_all returns per-device results
POST /api/backups/routers/binary-all src/api/backups.rs / binary_all D: disabled devices skipped; backup::run_all returns per-device results
POST /api/backups/router/{id}/export src/api/backups.rs / export D: policies and unsupported SwOS export; O: ownership; SSH adapter results in validation report
POST /api/backups/router/{id}/binary src/api/backups.rs / binary D: policies; O: ownership; SwOS fixture and SSH adapter checks
POST /api/backups/router/{id}/upload/{backup_id} src/api/backups.rs / upload O: both resources owned; SSH/SFTP adapter check; upload does not restore
DELETE /api/backups/{id} src/api/backups.rs / remove F: file and metadata deletion; O: ownership
GET /api/backups/{id}/download src/api/backups.rs / download F: exact binary bytes and Content-Type; O
GET /api/backups/{id}/view src/api/backups.rs / view F: export text and rejection of binary content; O
POST /api/backups/{id}/email src/api/backups.rs / email O: ownership; smtp_delivery_with_attachment_against_local_fixture for SMTP
GET /api/backups/{left}/diff/{right} src/api/backups.rs / diff_json F: statistics/text and rejection of binary content; O: both resources
GET /api/backups/{left}/diff/{right}/html src/api/backups.rs / diff_html F: HTML; O; escapes_configuration_and_file_names
POST /api/backups/bulk src/api/backups.rs / bulk F: ZIP, no partial mutation for an invalid ID, deduplication; O
GET /api/settings src/api/system.rs / settings settings_secrets_round_trip_without_public_exposure; S: saved response
GET /api/settings/scheduler-status src/api/system.rs / scheduler_status S: timezone and five jobs; cron/DST tests in scheduler.rs
PUT /api/settings src/api/system.rs / update_settings S: cron, preserve/clear secrets, invalid input rejected before save
POST /api/settings/reveal-ssh-key src/api/system.rs / reveal_ssh_key S: incorrect and correct account password
POST /api/settings/test-email src/api/system.rs / test_email S: configuration errors; local SMTP provider fixture
POST /api/settings/test-pushover src/api/system.rs / test_pushover S and missing_pushover_settings_fail_before_network; no live account delivery
GET /api/logs src/api/system.rs / logs L: limit; original id/message/timestamp model
DELETE /api/logs/older-than/{days} src/api/system.rs / delete_logs L: validation and actual removal of old records
GET /api/health src/api/system.rs / health L: public health check, version, configurable prefix

Backup list parameters are search, backup_type=export|binary, router_id, created_on=YYYY-MM-DD, sort_by=created_at|file_name|backup_type|router_name, and order=asc|desc. Logs retain limit in the range 1–500. Bulk operations accept action=delete|download and backup_ids. Login accepts username/password; password change uses current_password/new_password; SSH key reveal uses password; preferences use preferred_language/preferred_font. A login response contains access_token, token_type=bearer, and user.

Models and source fields

Models are explicitly typed in src/models.rs. src/db.rs implements persistence, current-schema validation, and explicit import. src/schema.sql is the complete fresh-install schema. Revision 4 does not run incremental schema migrations at startup.

Model / group Preserved or added fields Notes
User id, username, password_hash, created_at, preferred_language, preferred_font Password hashes are never serialized. The user DTO has the same four public fields as the source.
Router identity id, owner_id, name, device_type, host, port, ssh_user, created_at Every device/backup access checks ownership.
Router credentials ssh_key, ssh_password Public responses contain null and presence flags; values remain stored in the database.
Router SwitchOS source use_global_switchos_credentials Nullable addition: true selects the global pair, false selects the local pair, null preserves legacy fallback. Changing mode preserves local secrets.
Router connection result last_connection_status, last_connection_tested_at, last_connection_error, last_connection_hostname, last_connection_model, last_connection_version, last_connection_uptime, last_connection_transport, last_connection_server, last_connection_auth_mode, last_connection_http_status, last_connection_backup_available Unknown values remain null; errors are not presented as success.
Router policies disable_all_backups, disable_export_backups, disable_binary_backups, disable_ping Enforced server-side, including scheduled and batch operations.
Router DTO effective_username, uses_global_ssh_key, has_effective_ssh_key, uses_global_switchos_credentials, has_effective_password, has_own_password, supports_export, supports_restore_upload Derived from the selected credential source and device type without disclosing secrets.
RouterTestConnection success, tested_at, model, uptime, hostname, version, error, transport, server, auth_mode, http_status, backup_available The same result is persisted in the device's last_connection fields.
RouterPingStatus router_id, reachable, latency_ms, disabled; optional error Batch response contains items. Diagnostic errors distinguish missing ICMP permissions from an ordinary timeout.
Backup id, router_id, file_path, file_name, backup_type, checksum, created_at Additional DTO fields: router_name, device_type, file_size. Import copies existing bytes without converting their format.
OperationLog id, message, timestamp No invented level/component/router_id fields.
Dashboard routers_count, export_count, binary_count, total_backups, storage, recent_logs Device/backup counts are owner-scoped; logs remain global as in the source.
StorageStats total, used, free, folder_used, usage_percent usage_percent measures the data directory against total disk capacity, matching the source.
Settings retention backup_retention_days, log_retention_days 0 means an age threshold of zero days, matching the original retention function; it does not disable cleanup.
Settings scheduling export_cron, binary_cron, retention_cron, enable_auto_export, connection_test_interval_minutes Five-field cron, configured timezone, Monday=0 as in APScheduler.
Settings device defaults global_ssh_key, default_switchos_username, default_switchos_password Revealing the global key requires password verification.
Settings notifications pushover_token, pushover_userkey, notify_failures_only, smtp_host, smtp_port, smtp_login, smtp_password, smtp_notifications_enabled, recipient_email SMTP, attachments, Pushover, provider tests, and failures-only mode.
Settings DTO id, has_global_ssh_key, has_default_switchos_credentials Additional has_smtp_password, has_pushover_token, has_pushover_userkey, and has_default_switchos_password flags support editing masked secrets.
Diff JSON left_backup_id, right_backup_id, left_file_name, right_file_name, diff_text, stats, lines Statistics: added/removed/modified/context. Lines: type, left_number/right_number, left_text/right_text. Standalone diff_html is generated only by the dedicated HTML endpoint to avoid duplicating large payloads in the browser response.
SchedulerStatus timezone, running, jobs Job fields: key, label, enabled, cron, description, description_params, valid, next_runs, error.

Client views and behavior

Source view / behavior Implementation
/login, /register, session restoration, auth/guest guards, Bearer token web/js/core.js, web/js/views.js; actual auth API
/ dashboard API counts, storage, recent backups/logs, and batch actions
/devices, alias /routers List, search, device state, create, and edit
/devices/:id, alias /routers/:id Connection test, metadata, and backups; no RSC exports or diff for SwitchOS; comparison only for RouterOS text exports
/files Filters, sorting, selection, ZIP, deletion, preview, email, and diff
/diff-configs Two exports, counts, line numbers, side-by-side/unified views, and search
/logs Original messages and timestamps, cleanup by age in days
/settings Retention, cron, global credentials, SMTP/Pushover, tests, and next runs
/settings?tab=automation, alias /schedule Flat schedule controls; no separate Schedule navigation item
/settings?tab=account, alias /change-password Password change in account settings; old route redirects to that tab
/docs, /openapi.json, alias /redoc Local Swagger UI, full contract, authentication, and real API requests
Language and font pl, en, es, no; default, adwaita_mono, hack; persisted user preferences
Theme, sidebar, responsive layout, toasts, confirmations web/css/app.css, web/js/core.js, web/js/views.js; no sample devices inserted into the database
CSS/JavaScript delivery Modular web/ sources bundled into one app.css and one app.js, embedded in the executable

Default font families preserve the source stacks: Inter/system UI for content and Roboto Mono/IBM Plex Mono/SFMono-Regular/Consolas for headings. Adwaita Mono and Hack preferences select their respective stacks. Specific fonts depend on what is installed on the client device.

Services, configuration, and data

Area Implementation Tests / evidence
Argon2id and Python password compatibility security.rs new_passwords_use_argon2id, verifies_python_passlib_fixture_without_reset, verifies_legacy_werkzeug_fixture, malformed_hashes_fail_closed
JWT HS256, username subject, expiration, and nbf security.rs jwt_has_original_username_subject, expired_and_wrong_algorithm_tokens_are_rejected, signed_malformed_or_missing_expiration_is_rejected, not_before_is_optional_but_must_be_valid_and_reached, verifies_independent_python_hmac_jwt_fixture
Environment, SQLite URL, timezone, initial secret config.rs generated_secret_survives_restart, accepts_legacy_sqlite_url_and_custom_prefix, rejects_unsafe_configuration
Fresh schema and existing-schema validation db.rs, src/schema.sql Fresh initialization and incompatible-schema rejection regressions in db.rs; generated_bootstrap_password_is_saved_and_valid
Explicit database/file import db.rs import_copies_files_and_preserves_source, failed_import_does_not_publish_partial_database, orphaned_legacy_backups_are_not_silently_skipped, import_rejects_corrupted_backup_before_publication, import_rejects_backup_symlink_outside_selected_root
Import exclusivity and atomic publication db.rs import_obeys_storage_lock_and_keeps_source_untouched, atomic_copy_publication_refuses_existing_destination
Import wrapper import-python.sh, tests/import_launcher.rs help_and_invalid_sources_do_not_build_or_import, resolves_source_and_preserves_arguments_and_destination_settings, forwards_import_failure_without_falling_back_to_serve
Secret redaction and preservation models.rs, db.rs settings_keep_secrets_and_clear_explicitly, invalid_patch_is_atomic, generic_serialization_does_not_leak_secrets, settings_secrets_round_trip_without_public_exposure
RouterOS SSH export/binary, SFTP/SCP, host keys, timeout routeros.rs, backup.rs Controlled SSH/SFTP server checks in the validation report; no physical MikroTik supplied
Native SSH library fixes vendor/libssh2-sys Patch reproduction and tests/run-security-regressions.sh; sources, hashes, and limits in dependency-security.md
SwOS through the existing device model routeros.rs switchos_digest_probe_and_binary_transfer_against_local_fixture, switchos_basic_fallback_and_html_rejection
ICMP ping routeros.rs native_icmp_loopback_and_disabled_flag, icmp_checksum_verifies_packet; non-root Linux requires configured net.ipv4.ping_group_range
Atomic files, checksums, directory boundaries, device locks backup.rs checksum_matches_sha256, file_names_cannot_escape, deletion_allows_missing_file_but_rejects_escape, symlink_cannot_escape_data_directory, device_lock_releases_on_drop
Python/Rust timestamp retention backup.rs supports_python_and_rust_timestamps, retention_uses_age_and_zero_is_not_disabled
Diff and escaped HTML diff.rs reports_all_line_types, escapes_configuration_and_file_names, final_newline_and_crlf_are_normalized, tracks_blank_lines_in_replacements, unified_output_matches_python_difflib_golden_fixtures against 32 Python difflib fixtures
Cron and DST scheduler.rs validates_cron_and_ranges, monday_zero_matches_apscheduler, timezone_and_dst_preview, named_weekdays_and_last_day, rare_calendar_schedule_is_not_lost
SMTP/Pushover/error redaction notifications.rs smtp_delivery_with_attachment_against_local_fixture, missing_pushover_settings_fail_before_network, error_redaction_covers_global_secret_fields

All source environment variables are mapped: APP_NAME, APP_ENV, SECRET_KEY, JWT_ALGORITHM (HS256 only), ACCESS_TOKEN_EXPIRE_MINUTES, DATABASE_URL, DATA_DIR, ALLOW_REGISTRATION, API_PREFIX, TIMEZONE, DEFAULT_ADMIN_USERNAME, DEFAULT_ADMIN_PASSWORD, SMTP_STARTTLS, SMTP_TIMEOUT_SECONDS, and CORS_ORIGINS. APP_PORT controls the default port. LISTEN_ADDR, DATABASE_PATH, SSH_TIMEOUT_SECONDS, SSH_HOST_KEY_POLICY, and MAX_PARALLEL_BACKUPS are explicit deployment extensions.

Intentional security and reliability changes

  • No shared admin/admin password. A fresh installation generates an initial password in a private file unless one is configured. Registration is disabled by default and can be explicitly enabled with ALLOW_REGISTRATION=true.
  • Public responses redact secrets. Blank/null secret input preserves the value; explicit clear_* removes it. Upload and diff check ownership of both resources.
  • SSH host keys are verified. Default accept-new permits the first connection, records the key, and rejects changes. Explicit strict requires a trusted known_hosts entry. A host key is distinct from a user's authentication private key.
  • Directory and symlink checks protect backups. ZIP responses stream a temporary file rather than buffering the complete archive in memory. Bulk downloads are limited to 2 GiB, and email attachments to 25 MiB; exceeding a limit is an error.
  • New backup filenames include a finer-grained timestamp and random suffix so concurrent operations cannot overwrite each other. Imported names and contents remain supported.
  • External service failures return controlled HTTP 502 responses with secret values redacted. A notification failure does not remove or invalidate a successful backup.
  • Naive SQLite timestamps are presented as UTC ISO 8601, preserving the source's actual time without relying on the browser timezone to interpret it.
  • Structured diff corrects old blank-line numbering in replacement blocks: an empty existing line is not treated as a missing line. Unified output is checked against 32 file-based fixtures generated by Python difflib.
  • Empty cron expressions disable their schedules. Invalid expressions, invalid ports, negative retention, and excessive request sizes are rejected. Jobs are prevented from overlapping themselves.
  • Revision 4 initializes only a fresh schema and validates an existing database before use. There are no startup migrations or backup-layout conversions. Python import is explicit, reads the source without modifying it, and requires a new destination. See data-import.md.
  • Runtime is one Rust process with SQLite, scheduling, and an embedded interface. Python, Node, Angular, and nginx are not required application processes.

Concept image and inactive source code

Item Decision
Git integration / commits / synchronization No source model, endpoint, or service exists; the concept screen is not represented as a working feature.
Arbitrary job CRUD The source has system jobs controlled through settings; that behavior is preserved.
/swos-beta/probe, /swos-beta/download The router was not included in backend/app/api/__init__.py; no new public beta API is activated.
Existing switchos type Preserved: ordinary connection tests and binary backups invoke SwOS code through the original RouterService despite the inactive separate beta router.
Concept's restore action The source only transfers a binary file to RouterOS. Upload is preserved without automatic restore or restart.
Invented network traffic or log levels Views use actual API data rather than demo measurements or fields absent from the model.

Evidence limits

No production database, physical routers, device host keys, or provider accounts were supplied. Local fixture checks do not establish compatibility with every firmware version. For a deployment transition, follow the explicit import procedure, verify a connection to one real device, and confirm that its backup can be downloaded. Starting the new server activates schedules from the imported settings, so stop the old processes first. Release tests do not automatically restore any device configuration.