Skip to content

Design Decisions

coaxk edited this page Mar 12, 2026 · 1 revision

Design Decisions

Core Principles

Source of Truth

Green means green. MapArr will never report a false positive. If the dashboard shows a healthy stack, it IS healthy. Severity signals are honest — we'd rather miss an edge case than cry wolf.

Tone: Knowledgeable Friend

Lead with what it means for the user, one plain sentence before technical detail. Let the user move at their own pace. The CONFLICT_HANDRAILS constant in app.js enforces this.

No Framework

The frontend is vanilla JavaScript (~8.5K LOC, single file). No React, no Vue, no build step. This is deliberate:

  • Zero dependency risk — no npm supply chain concerns
  • Instant load — no bundler, no tree-shaking needed
  • Simple deployment — copy files, done
  • The UI is a diagnostic tool, not a web app. It doesn't need component lifecycle management.

The trade-off is that app.js is large and will be split into ES modules in Stage 3 (task F1).

No Database

All state is ephemeral. MapArr reads compose files, analyzes them, and reports results. Nothing is persisted server-side. Frontend uses localStorage for UI preferences (collapsed sections, dismissed warnings, wizard completion).

This means:

  • No migration headaches
  • No backup concerns
  • No data corruption risk
  • Fresh analysis every time (always current)

Core Scope = Arr Ecosystem Only

27 services are "core" (arr apps + download clients + media servers). Everything else is "other" — shown on the dashboard for awareness but NOT analyzed.

Why not analyze everything? Because reclassifying services into core roles pulls them into the analysis pipeline, which generates noise conflicts. An Nginx reverse proxy doesn't need PUID/PGID analysis. Overseerr doesn't need path conflict detection.

The 27-service list is documented in README.md "Supported Services".

Key Design Patterns

Conflict Category System

CONFLICT_CATEGORIES in analyzer.py is the single source of truth for all routing:

  • Category A (path) → volume YAML tab, Apply Fix generates corrected volumes
  • Category B (permission) → environment YAML tab, Apply Fix generates corrected env vars
  • Category C (infrastructure) → guidance tab, no fix generated
  • Category D (observation) → collapsed info section, no fix generated

Adding a new conflict type means adding it to this dict. The category determines everything downstream: tab selection, fix generation, health impact, UI rendering.

RPM Wizard Gating

The Remote Path Mapping wizard is gated by TWO conditions:

  1. Must be a Category A conflict (path issue)
  2. _calculate_rpm_mappings() must return possible=True

Condition 2 requires host path overlap — one service's host path must be a parent or child of another's. Sibling paths (e.g., /mnt/downloads and /mnt/media) cannot be bridged by RPM. This prevents the wizard from appearing when it can't actually help.

Pre-Flight Validation

Before any destructive action (Apply Fix, Restart), cross-check inputs against known state. Warn mid-action. Always offer a "proceed anyway" escape hatch. The Revert button is the ultimate safety net.

Pipeline Refresh After Fix

After Apply Fix modifies a compose file:

  1. mtime-based staleness detection triggers
  2. Inline rescan runs automatically
  3. All media services expand in dashboard (pipeline override mode)
  4. Redeploy banner persists until restart confirmed

Family-Aware Environment Patching

Different image families use different environment variable names for the same concept:

  • LinuxServer.io: PUID, PGID
  • Hotio: PUID, PGID (same)
  • jlesage: USER_ID, GROUP_ID
  • Plex: PLEX_UID, PLEX_GID

_get_service_env_var_names() resolves the correct var names per family before generating fixes.

Trade-Offs We Accept

Decision Trade-off Why
Single-file frontend Hard to navigate Will split in v1.6 (F1). Currently manageable.
No Docker SDK dependency Can't inspect running containers Keeps install simple. Docker socket optional.
YAML-only analysis Misses runtime state Compose files are the source of truth for path config. Runtime adds complexity without proportional value.
No multi-user support Single-user tool This is a local diagnostic tool, not a SaaS. Session locking deferred to v1.6 (B3).
No persistence Fresh scan every time Ensures results are always current. Cache planned for v1.6 (B4).