# Architecture mirror.py maintains local copies of remote package repositories on Linux. Its normal service mode separates scheduling and state management from subprocess execution. It also offers foreground commands for one-off synchronization. ## Runtime modes ``` master.sock mirror tui/push/reload ───────────────▶ MasterServer │ mirror daemon │ schedules and tracks ├─ MasterServer ▼ └─ WorkerClientSupervisor ─────────▶ sync plug-in ▲ │ execute_command RPC │ job_finished ▼ └────────────────────── WorkerServer ──▶ subprocess mirror worker mirror standalone ──▶ sync plug-in ──▶ foreground subprocess mirror worker-execute ubuntu/jigdo ──▶ specialized foreground workflow ``` The master owns configuration, scheduling, package state, per-run logging, and the client-facing RPC API. The worker owns subprocess lifetimes. If the master restarts, a running subprocess remains under the worker. A supervised `WorkerClient` reconnects with exponential backoff, and the worker retries completion notifications for finished jobs. `mirror standalone` activates the same sync plug-ins without either socket server. Calls that would normally delegate to the worker instead use the foreground process runner, and completion is returned to the CLI without persisting daemon status files. The `worker-execute` commands are another foreground path. They expose the specialized Ubuntu and jigdo workflows used as subprocess entry points by those daemon sync methods. The apt-mirror2 and debmirror methods use private Python wrapper entry points instead, allowing repository discovery and native tool execution to happen under the worker's identity and environment. ## Master-worker flow 1. The daemon loads the main configuration and runtime state, registers enabled plug-ins, initializes logging, and starts `MasterServer`. 2. Starting the master also starts `WorkerClientSupervisor`, which maintains a persistent connection to `worker.sock`. 3. Once per second, the daemon skips disabled and running packages, then checks `lastsync`, `syncrate`, and the error retry interval to decide what is due. 4. `mirror.sync.start()` records the running log and `SYNC` status, then invokes the selected sync plug-in in a daemon thread. 5. The plug-in validates settings, builds its command, and calls `mirror.socket.worker.execute_command()`. The worker starts the process with the configured UID, GID, environment, and log destination. 6. When the process exits, the worker broadcasts `job_finished`. The master calls `mirror.sync.on_sync_done()`, runs the plug-in completion hook when present, finalizes the log, changes the status to `ACTIVE` or `ERROR`, and persists state. The daemon also reconciles package state with worker jobs. It repairs a package whose worker job is running but whose status is stale, and eventually marks a stale `SYNC` package as `ERROR` when the worker no longer has its job. An optional global maximum runtime watchdog stops overlong worker jobs. ## Client-facing master RPC The master socket serves the TUI and command-line control clients. Its handlers provide health and runtime information, package listing, manual start and stop, push-triggered synchronization, and configuration reload. The daemon writes the active master socket path to `/var/run/mirror/master.sock.path`; clients prefer that metadata when `--socket` is omitted. The TUI keeps one `MasterClient` connection, polls package state once per second, and performs manual start/stop RPCs outside the UI event loop. Its log reader accepts only regular non-symlink files under the configured package log root, supports plain and gzip files, and keeps bounded disk-backed windows while paging large logs. ## Socket protocol Master and worker IPC uses length-prefixed JSON frames over Unix domain sockets. Every connection completes a three-step handshake—server information, client information, then confirmation—before application messages are handled. The framing supports request/response RPCs and asynchronous notifications on the same persistent connection. The two sockets have distinct roles: - `master.sock` accepts control and status RPCs from CLI clients. - `worker.sock` accepts process-management RPCs from the master and sends `job_finished` notifications back over connected clients. ## Plug-in lifecycle The plug-in framework is active and has two loading phases: 1. Package import registers the built-in sync plug-ins so package validation knows every built-in method. 2. Configuration loading applies built-in enable/disable settings and discovers third-party entry points from `mirror.sync`, `mirror.event`, and `mirror.status`. External plug-ins declare a `(major, minor)` API version. Incompatible major versions and plug-ins that require a newer minor version are skipped. Sync plug-ins supply execution hooks, event plug-ins register event handlers, and status plug-ins can extend or transform status payloads or write additional status outputs. Per-plug-in JSON configuration is stored beside the main configuration and is read lazily. A plug-in may expose a `create_config` callback, invoked explicitly with `mirror plugin config create`; normal daemon startup never creates or rewrites plug-in configuration. ## Module map | Module | Responsibility | |--------|----------------| | `mirror/__main__.py` | Click entry point and top-level command registration. | | `mirror/command/` | Service, control, TUI, plug-in, standalone, and worker-workflow commands. | | `mirror/config/` | Main JSON loading, safe runtime reload, state persistence, and web status output. | | `mirror/structure/` | Configuration, package, settings, and status data structures. | | `mirror/socket/` | Framed Unix socket protocol plus master and worker clients and servers. | | `mirror/sync/` | Built-in sync plug-ins, scheduling state, and completion handling. | | `mirror/worker/` | Foreground and background subprocess lifecycle, log merging, and pruning. | | `mirror/plugin/` | Built-in registration, external entry-point loading, API compatibility, and status hooks. | | `mirror/event/` | Priority-based event publication and subscription. | | `mirror/logger/` | Daemon and package logging, rotation, compression, and ownership. | | `mirror/toolbox/` | Duration parsing, command lookup, and other shared utilities. | ## Persistence and paths | Path | Purpose | Written during daemon runtime? | |------|---------|--------------------------------| | `/etc/mirror/config.json` | Operator-supplied main configuration | No | | `/var/lib/mirror/stat.json` | Persistent package runtime state | Yes, by atomic replacement | | `/var/run/mirror/` | PID, socket, and active socket-path metadata | Yes | | `/var/log/mirror/` | Daemon and per-package logs | Yes | | `/var/www/mirror/status.json` | Web-facing package status | Yes, by atomic replacement | The main configuration is read-only after provisioning. `mirror setup` creates it only when it is absent. Runtime status, error counts, log paths, and timestamps belong in `stat.json`; web-facing state belongs in `status.json`. Plug-in status outputs are separate files owned by their plug-ins. ## Built-in sync methods | Method | Execution model | |--------|-----------------| | `rsync` | Validates rsync options, optionally checks FFTS metadata, then delegates rsync to the worker. | | `ftpsync` | Creates a temporary archvsync environment, preferring a git clone and falling back to the bundled archive, then delegates `ftpsync` to the worker. | | `lftp` | Builds a validated lftp mirror script and delegates it to the worker. | | `bandersnatch` | Runs the PyPI mirror command through the worker. | | `local` | Verifies that the authoritative local destination exists; no subprocess is needed. | | `ubuntu` | Delegates the `worker-execute ubuntu` two-stage rsync workflow. | | `jigdo` | Delegates the `worker-execute jigdo` template sync, image reconstruction, final ISO pull, and trace workflow. | | `debmirror` | Builds a native debmirror command, discovers omitted distributions, sections, and architectures inside the worker wrapper, and runs with isolated debmirror configuration. | | `apt-mirror2` | Resolves one or more APT repositories inside the worker wrapper, generates a temporary apt-mirror2 configuration, and runs the optional `apt-mirror==16` implementation. |