Skip to content

Plex Music Rejector Design

v2.2 | Build 013.20260928.023457Z

Design for the Plex Music Rejector automation service.

One star music rejection migration and confirmation service

Document field Value
Document identifier NET-005-DRA
Version v2.2
Build 13
Status Implemented and verified on JUKEBOX as Plex Music Rejector v2.2 Build 13
Date September 27 2026
System Plex Music Rejector on JUKEBOX
Owner Todd McGuckin

Decision

Replace the original receiver with one small synchronous Plex Music Rejector service. One request handler validates each event, resolves and migrates the media pair under a process lock, requests a targeted Plex refresh, sends the isolated Pushover result, writes the result to append-only logs, and returns a response. This intentionally simple design uses no queue, SQL database, schema, state machine or worker coordination. Every external-interface decision is traceable to official Plex or Pushover documentation; locally observed and internally selected behavior is labeled separately.

Document control

Plex Music Rejector software and documentation share one release identity. Build 13 is version v2.2, dated September 27 2026. A change to either the controlled design or the software creates the next build and advances the shared version; documentation-only and software-only version numbers are prohibited. Build 13 retains the synchronous architecture and corrects certificate trust selection for the production Python 3.14.7 runtime.

The version rule is deterministic: Builds 1 through 10 are v1.0 through v1.9, Build 11 is v2.0, and every later group of ten builds advances the major version. For build B, the major version is 1 plus the whole-number result of (B - 1) divided by 10, and the minor version is the remainder. Every source package, design document, startup and shutdown entry, test record, deployment backup and rollback record must use the same version, build and release date.

Contents

1 Purpose and outcome

2 Authority and traceability

3 Replacement architecture

4 Webhook contract

5 Simple processing model

6 Media resolution and migration

7 Plex integration

8 Pushover and artwork

9 Logging audit and recovery

10 Security and configuration

11 Failure handling and restart recovery

12 Package deployment and rollback

13 Verification and acceptance

14 Costs and maintenance

15 Source register

Revision history

Version Build Date Summary
v1.0 1 2026-09-27 Establishes the approved replacement design and one-star migration outcome
v1.1 2 2026-09-27 Adds proportional secret handling and authorizes implementation
v1.2 3 2026-09-27 Redesigns the complete package and adds source traceability
v1.3 4 2026-09-27 Adopts one synchronous worker and removes unnecessary coordination machinery
v1.4 5 2026-09-27 Removes SQLite and background workers from the processing path
v1.5 6 2026-09-27 Adds controlled code quality, error handling, logging and Pushover behavior
v1.6 7 2026-09-27 Records production cutover, live acceptance and concise activity logging
v1.7 8 2026-09-27 Aligns software and documentation identity; adds typed ordered logs and lifecycle release data
v1.8 9 2026-09-27 Adds startup mode, Rejected terminology and historical lifecycle standardization
v1.9 10 2026-09-27 Adds listener IP address and port to startup entries
v2.0 11 2026-09-27 Makes lifecycle entries symmetrical and removes the repeated shutdown time
v2.1 12 2026-09-27 Corrects retry, deduplication, credential, rollback and log-volume safeguards
v2.2 13 2026-09-27 Corrects Python certificate trust selection and production-runtime TLS verification

1 Purpose and outcome

Plex Music Rejector gives a one star music rating a concrete library-management meaning. When the owner assigns that rating, the service moves the corresponding media out of the active Plex music roots and into a recoverable Rejected folder. This keeps disliked tracks and their matching music videos out of normal playback without permanently deleting the files.

The design treats an audio track and a music video with the same filename stem as one rejection unit. If both formats exist in the same source directory, both must move together. If either required move fails, the service must attempt to return every moved file to its original location and report the incomplete transaction clearly.

Version v2.2 Build 13 records the certificate trust correction added after a successful Bangles migration could not send its Pushover confirmation. Compatibility is preserved at the user and storage boundaries. The legacy launch agent remains unloaded, and its complete installation remains available as rollback material without reusing its exposed webhook secret.

Design objectives

  • Make a one star rating the only destructive-intent trigger, regardless of whether Plexamp presents it as one star, five stars, or thumbs down.

  • Move files to a recoverable Rejected folder rather than delete them.

  • Keep exact stem .m4a and .m4v companions together.

  • Send an accurate confirmation only after the migration commits.

  • Keep notification, scan, and logging failures from changing the result of a completed migration.

  • Create human-readable operations logs and machine-readable recovery records without exposing secrets.

  • Keep the complete request path synchronous and serialized so behavior is easy to follow and diagnose.

  • Make every vendor-dependent choice traceable to official documentation and every undocumented behavior visibly local.

  • Keep code well-factored and purposefully commented, with robust error isolation and human-readable secret-safe operational logs.

2 Authority and traceability

The design uses three authority classes. Vendor requirements are supported by official Plex or Pushover documentation. JUKEBOX observations fill gaps that the vendors do not publicly specify. Internal safety decisions govern this installation and are not attributed to either vendor.

Authority Examples How it controls the design
Official Plex Webhook eligibility, multipart payload, media.rate event, payload fields, attached JPEG Defines what the receiver must accept and which fields may be filtered
Official Pushover HTTPS POST endpoint, fields, priorities, TTL, attachment and response limits Defines notification formatting, validation and success criteria
JUKEBOX observation One star payload value, current account identity, local refresh endpoint behavior Must be commissioned and regression tested; never represented as vendor policy
Internal safety Exact stem pairing, immediate rollback, no overwrite, append-only audit and deduplication window Controls recoverability and predictable operations for this installation

Decision source matrix

Design decision Authority and source Implementation consequence
Receive media.rate as multipart JSON plus JPEG Official Plex Webhooks [P1] Parser preserves both named parts and rejects malformed or oversized input
Require owner and expected server Official Plex payload fields [P1] plus local commissioning Owner flag, configured account and verified server UUID must agree
Map one star to the rejection value JUKEBOX payload observation; Plex does not document the numeric mapping Keep the value configurable and replay a genuine event during acceptance
Send HTTPS multipart notification Official Pushover Message API [U1] Use the documented endpoint, TLS verification and multipart attachment field
Use priority zero or one and TTL 86400 Official Pushover priority and TTL definitions [U1] Success uses normal delivery; failure bypasses quiet hours; both expire after one day
Limit JPEG to 5,242,880 bytes Official Pushover attachment limit [U1] Reject an oversized image without rejecting the text notification
Retry temporary notification failures once Pushover permits retry after HTTP 5xx, connection failure or no reply, no sooner than five seconds [U1] Record each attempt, wait five seconds and make one retry; never retry 4xx or API rejection
Select a verified certificate trust source Python.org 3.14.7 on JUKEBOX has no configured default CA file; macOS provides /etc/ssl/cert.pem [L4] Use Python's default trust configuration when usable; otherwise load the macOS CA bundle explicitly and never disable certificate verification
Use targeted path refresh Locally verified Plex endpoint; not in public Plex support documentation Keep it behind an adapter, encode the path and never fall back to a whole-library scan automatically

3 Replacement architecture

Plex Music Rejector is one launchd service with one synchronous request path. A process lock permits only one rejection at a time. The handler authenticates and bounds the request, validates the event, plans and performs the migration, requests the targeted Plex refresh, attempts Pushover delivery, records the outcome and then returns. Plex and Pushover calls have short timeouts. No queue, database, state machine or background worker is present.

Component Responsibility Failure boundary
Receiver Authenticate request, bound size, parse payload and JPEG, serialize processing Rejects malformed or unauthorized input
Validator Check event, owner, server, rating, media type and configured roots Rejected events never reach the mover
Resolver Resolve Plex metadata and exact stem .m4a and .m4v companions Read only against Plex API and database fallback
Migrator Preflight, allocate collision-safe destinations, rename, verify and roll back Only component permitted to change media paths
Post actions Request targeted refresh, append audit and deliver Pushover Failures cannot reverse a successful migration

Package layout

  • app.py contains the synchronous service and HTTP boundary; parser.py owns bounded multipart parsing.

  • plex.py, migration.py and pushover.py isolate their external or filesystem responsibilities.

  • audit.py owns append-only recovery records and duplicate suppression; config.py and logging_setup.py own startup controls.

  • config.json and the mode-600 .env live in the stable application-support directory; the .env contains only Pushover credentials and is not part of a versioned release swap.

  • activity.log is the rotating human-readable log; audit-YYYY-MM.jsonl is the append-only operation record.

  • tests contain focused parser, migration, rollback, Pushover and replay checks.

Every module and non-obvious safety boundary requires a concise docstring or comment. Comments explain why a safeguard exists; they do not restate obvious syntax. Expected errors produce safe outcomes and clear log entries. Unexpected Pushover errors are contained, unexpected request errors return a generic response, and neither category exposes credentials or raw payloads.

4 Webhook contract

Property Contract
Listener 127.0.0.1 on the configured JUKEBOX port; no public Internet exposure
Webhook URL /webhook/v1/{secret}; exact path only; no query string
Authentication Constant-time comparison of a rotated opaque secret; failures return 404
Method and media type POST multipart/form-data as documented by Plex [P1]
Parts Exactly one payload JSON part; at most one JPEG part; no more than four total parts
Limits Eight MiB request, six MiB JPEG, one MiB JSON, maximum JSON depth 32 and maximum decoded string 65,536 characters
Success 200 OK after the synchronous processing attempt has a recorded result
Errors 400 malformed or duplicate parts, 413 oversized, 415 unsupported media type; authenticated processing failures are logged safely
Health GET /healthz reports service name, version, mode, verified Plex identity and log-volume availability without secrets

Plex officially states that media.rate includes a JSON payload and a JPEG thumbnail in multipart form [P1]. The receiver therefore treats the image as first-class event data. It never logs the request target, raw payload, secret, token or JPEG bytes.

5 Simple processing model

Stage Action Failure behavior
Receive Authenticate, bound and parse multipart payload and JPEG Return a safe HTTP error; no media access
Validate Confirm media.rate, owner, account, server, rating and media type Ignore unrelated events without adding activity-log noise
Plan Resolve files, exact-stem companion and collision-safe destinations Send PLEX - Track Removal Failed; no file changes
Move Rename one or two files on the same filesystem and verify Immediately restore completed renames in reverse order
Post actions Request targeted refresh, attempt Pushover and write final audit Log failure without reversing a successful move

Audit and duplicate handling

Before the first rename, append and fsync a started record containing the operation ID and complete source-to-destination plan. After processing, append and fsync one terminal record containing the outcome, rollback result, scan result and Pushover result. This is a recovery record, not a work queue or database.

A fingerprint combines server UUID, account identity, event type, rating key, rating value and normalized metadata. At startup, the service reads recent terminal audit records into a small in-memory cache. A repeated dry-run event inside the configurable 24-hour window is ignored. A repeated live event is ignored only when no source member of the recorded media pair exists at an approved path. If a file has been restored, a new one-star event is processed as a genuine rejection even when its Plex rating key and metadata match the earlier event. Failed and rolled-back attempts are never suppressed.

The JPEG exists only in memory for the duration of the request. It is never written to the audit log or retained after the Pushover attempt.

6 Media resolution and migration

Resolution order

1. Use regular .m4a or .m4v file paths embedded in the webhook when present.

2. Otherwise query the authenticated local Plex metadata endpoint using ratingKey.

3. If the API omits the path, perform the existing read-only database lookup. Never write to Plex's database.

4. Canonicalize the result, reject symbolic links, require an approved source root, and discover only the exact stem companion in the same directory.

Transaction rules

  • Write and fsync the complete plan before moving the first file.

  • Preserve the source-root-relative tree beneath /Volumes/Storage/Libraries/Rejected.

  • Never overwrite. Allocate one shared collision suffix for both members of a pair.

  • Require the source and destination to be on the same filesystem so each rename is atomic.

  • After every rename, record progress. After the pair, verify all destinations and absence of all sources before commit.

  • On a caught failure, restore completed moves in reverse order and verify the restored state. An incomplete rollback is logged with every affected path and produces a priority 1 failure notification.

7 Plex integration

Plex's documented server root response provides the machineIdentifier used to verify the configured server [P2]. The token is sent only to the local Plex server and never appears in URLs, logs or notifications. The official webhook payload supplies owner, Account, Server and Metadata fields [P1]; because the published account-ID description may not match every current installation, the receiver uses the owner flag plus the locally commissioned account identifier and verified server UUID rather than relying on one field alone.

Refresh strategy

After commit, the Plex adapter requests a path-scoped refresh for every affected parent directory and section, using URL encoding. This endpoint is a locally verified compatibility detail, not a published Plex support guarantee. It must be covered by a JUKEBOX integration test. Failure is logged for operator attention; Plex Music Rejector will not silently escalate to a whole-library scan. Existing Plex automatic partial scanning remains an independent fallback.

8 Pushover and artwork

Field Success Migration failure
Title PLEX - Track Removed PLEX - Track Removal Failed
Message {title} — {grandparentTitle} (album: {parentTitle}) Track identity, safe error category and rollback state
Priority 0 normal priority 1 high priority
TTL 86400 seconds 86400 seconds
Send condition Only after every move verifies After planning or migration failure

The client sends an HTTPS POST to https://api.pushover.net/1/messages.json with token, user, title, message, priority and ttl [U1]. An attachment uses multipart/form-data and the attachment field. HTTP 200 is not sufficient by itself; the response JSON must contain status 1, and the returned request identifier is recorded [U1].

Delivery isolation

  • Read PUSHOVER_TOKEN and PUSHOVER_USER only from the stable application-support .env beside config.json, with mode 600.

  • Limit titles to 250 characters, messages to 1024 UTF-8 characters and attachments to 5,242,880 bytes as documented by Pushover [U1].

  • Use verified TLS and an eight-second request timeout. HTTP 4xx or API status 0 is failed and is never retried. Pushover documents HTTP 5xx, connection failure and no reply as temporary conditions that may be retried no sooner than five seconds [U1]. The client waits five seconds and makes one retry; a second temporary failure is delivery_unknown.

  • Create the HTTPS context from Python's default certificate locations when they resolve to an existing CA file or certificate directory. On macOS, if the selected Python runtime has no usable default trust location, load /etc/ssl/cert.pem explicitly. Startup fails with a clear error if neither source exists. Certificate and hostname verification remain enabled in every case.

  • Record a dispatch-attempt audit entry before each network call and the documented Pushover request identifier after status 1. The one-retry limit follows Pushover's published temporary-failure guidance while preventing an unbounded retry loop.

  • A failed or uncertain Pushover result is logged and included in the terminal audit record. It never changes or reverses the media result.

  • Dry run uses Plex Music Rejector Dry Run and never PLEX - Track Removed.

Artwork fallback

1. Use the valid webhook JPEG preserved by the receiver.

2. Otherwise fetch the authenticated Plex metadata thumbnail before migration.

3. Otherwise send without an attachment so the dedicated Plex Music Rejector Pushover application icon is displayed.

4. If the application icon is unavailable, the text notification still sends. The generic Pushover logo is not fabricated as track artwork.

9 Logging audit and recovery

Human-readable logs and append-only recovery records live in /Volumes/PlexDB/Logs/Plex Music Rejector/. Before startup and before every live migration, the service verifies that /Volumes/PlexDB is the expected mounted volume and that the audit directory is writable. It then appends and fsyncs the complete recovery plan immediately before the first rename. If the volume is absent, replaced by an ordinary directory or not writable, the service refuses to move media.

Record Purpose Retention
activity.log Plain-language lifecycle, recognized rejection, migration result and actionable warning events Five rotating archives at five MiB each
audit-YYYY-MM.jsonl Append-only started and terminal operation records, including recovery paths No automatic deletion

Human-readable format

The rotating activity log is an operator summary, not an HTTP access log. It records only service start, clean shutdown, detected abnormal restart or crash, an authenticated rejection, successful migration details, a failed migration with reason and recommended action, and actionable post-migration results or warnings. A recognized one-star event is written as ‘Rejected’ followed by the track, artist, Plex item and operation identifier. Routine HTTP 200 responses, health checks, ignored playback and scrobble events, intermediate rating events and duplicate callbacks do not create activity entries.

Every startup entry uses this exact message structure: ‘Plex Music Rejector vVERSION Build BUILD Date YYYY-MM-DD started in MODE mode on IP:PORT.’ Every clean shutdown uses the symmetrical structure: ‘Plex Music Rejector vVERSION Build BUILD Date YYYY-MM-DD shut down from MODE mode on IP:PORT (reason: REASON).’ MODE is exactly ‘live’ or ‘dry-run’, and IP and PORT are the listener values from the loaded configuration. The activity logger supplies the bracketed event timestamp, so neither lifecycle message repeats the event date and time.

The rendered dashboard displays every activity.log entry and applies no hidden inclusion or exclusion policy. When obsolete entries from an earlier software version must be removed or standardized, maintenance first preserves a timestamped mode-600 copy, stops the writer to prevent races, rewrites activity.log, verifies the result, and restarts the service. A historical rewrite must preserve the event time, actual live or dry-run mode, configured listener IP address and port, and corresponding historical release identity; it must never relabel an old run as the current release. This maintenance never rewrites or deletes the append-only audit log.

A successful migration preserves five useful categories of evidence: one entry for each individual source-to-destination file move with size; the overall migration result naming the track, artist and file count; the targeted Plex refresh result; and the Pushover delivery result. Every file-move entry occupies its own line and begins with either ‘Moved audio file’ for .m4a or ‘Moved video file’ for .m4v, so paired media never runs together or becomes ambiguous. File-move entries are written first in execution order; only after all moves are complete does the service write the single migration-succeeded entry. These entries include the operation identifier so an operator can correlate them. A failure entry states the safe reason, rollback result, affected paths when manual recovery is required and a practical recommendation. The audit log retains exact paths, bytes, operation IDs, refresh results, artwork source and Pushover results. Control characters are escaped. Secrets, tokens, full webhook URLs, raw bodies and JPEG bytes are forbidden.

The canonical success order is: rejected track; moved audio file when present; moved video file when present; track migration succeeded; Plex targeted refresh result; Pushover delivery result. The canonical failure order is: rejected track; track migration failed with its reason, rollback state and recommendation; Pushover delivery result. A notification attempt never precedes the result it reports. Lifecycle entries remain chronological and outside the transaction sequence.

The dashboard uses exactly four meaningful categories: Information, Success, Warning and Error. There is no Default category. Any otherwise valid entry not matched by a more specific rule is Information, while malformed audit JSON is Error. This ensures every displayed entry has an operator-relevant classification.

Startup creates a small running marker beside the logs. A handled shutdown removes it and records the signal or reason. If the next startup finds the marker, it records an abnormal prior termination with the last known start information and recommends inspecting incomplete audit operations. Forced termination, power loss and catastrophic runtime failure cannot reliably record their own cause, so the later entry explicitly reports the cause as unknown when necessary.

The launch agent also sends standard output and standard error to ~/Library/Logs/Plex Music Rejector/launchd-out.log and launchd-error.log. These local launchd logs are the defined emergency channel for startup failures or PlexDB loss. They do not replace activity.log or the append-only audit record and never authorize a media move without the PlexDB recovery record.

10 Security and configuration

Item Handling
Webhook secret Low-sensitivity secondary control in mode-600 configuration; rotate once at cutover; never log path
Pushover credentials PUSHOVER_TOKEN and PUSHOVER_USER in stable mode-600 application-support .env beside config.json; never hardcode
Plex token Higher-sensitivity token in its existing mode-600 file; send only as a header to local Plex
Network Bind to JUKEBOX loopback because Plex Media Server runs on the same host; no router exposure
Configuration Strict schema, unknown-key rejection, absolute paths, explicit dry_run and startup permission checks

The existing secret is rotated once because historical request logs exposed the old URL. Historical logs may then remain. Routine scheduled rotation, Keychain storage and a dedicated vault are not required for this low-sensitivity control. The Pushover and Plex credentials remain private and are never copied into the design, source code or logs.

11 Failure handling and restart recovery

Condition Result Operator signal
Ignored event Receipt retained briefly; no media action No activity entry and no notification
Unsafe request Rejected before durable work or marked failed Safe warning; no notification for unauthenticated traffic
Planning failure No filesystem change PLEX - Track Removal Failed for an authenticated one-star event
Move failure and full rollback Sources restored Priority 1 failure with rollback result
Incomplete rollback Manual recovery required Priority 1 failure with exact manual locations
Refresh failure Migration stays successful Activity and audit failure; no false removal failure
Pushover failure Migration state unchanged Logged and recorded for inspection
PlexDB unavailable No media move begins Local launchd error identifies the missing, replaced or unwritable volume

Unexpected restart tradeoff

The service does not attempt automatic crash reconciliation. A running marker distinguishes a clean stop from an inferred abnormal termination. At startup the service also identifies any started operation lacking a terminal audit record and writes a prominent error with the recorded source and destination paths. It does not repeat a rename or send a success confirmation. An operator must inspect those paths and restore or complete the pair. This narrow manual-recovery window is the accepted cost of removing the database and recovery state machine.

12 Package deployment and rollback

Installation layout

Path Contents
~/Library/Application Support/Plex Music Rejector/current Versioned Python package and entry point
~/Library/Application Support/Plex Music Rejector/config.json Mode-600 non-Pushover configuration
~/Library/Application Support/Plex Music Rejector/.env Stable mode-600 Pushover credentials outside versioned releases
~/Library/LaunchAgents/com.mcguckin.plex-music-rejector.plist Single launchd service

Runtime compatibility

Build 13 targets the official CPython 3.14 feature series and the production launch agent's Python.org CPython 3.14.7 interpreter at /usr/local/bin/python3. The service uses only the Python standard library. The Python.org installation on JUKEBOX does not have its optional framework CA bundle configured, so Build 13 uses macOS's maintained /etc/ssl/cert.pem when Python reports no usable default trust location. The production interpreter, not a second Python installation, must perform the release TLS acceptance check. The health endpoint reports v2.2, Build 13 and the 2026-09-27 release date.

Controlled cutover

1. Preserve the complete existing Plex Rejector directory, launch agent, configuration and logs as a permission-restricted rollback package.

2. Install the replacement under its new official path and run automated tests without starting its listener.

3. Register the Pushover application as Plex Music Rejector, configure its dedicated icon and create the stable mode-600 application-support .env beside config.json.

4. Start the replacement on a temporary loopback port in dry-run mode and replay a stored multipart media.rate fixture with JPEG.

5. Verify the dry-run plan, JPEG preservation, redacted logs, Pushover dry-run message and duplicate suppression. No media may move.

6. Stop the legacy launch agent, move the replacement to the production port, rotate the webhook secret, update the Plex webhook URL and start the new launch agent as one controlled cutover.

7. Confirm health, Plex identity, current logs and the absence of the secret from new log entries.

8. Run one live acceptance rejection using user-approved test media. Retain rollback material until all acceptance checks pass.

Rollback stops the new service and restores the preserved legacy launch agent, but it must not restore the exposed legacy webhook URL. Generate a fresh secret for the legacy receiver, place it in its protected configuration, and update Plex to the new legacy URL as one controlled rollback step. Before rollback, inspect the audit log for any started operation without a terminal record and resolve its listed paths.

Operator dashboard publication

The rendered HTML dashboard is an operator-workstation artifact, not part of the JUKEBOX webhook listener. A separate macOS launch agent named com.mcguckin.plex-music-rejector-logs serves the McGuckin-Net Reports directory on 127.0.0.1:8765. It uses KeepAlive so the dashboard URL remains available after a shell or Codex session ends. The server binds only to loopback and cannot accept LAN or Internet connections. Regenerating the HTML updates the next browser response without restarting the server.

13 Verification and acceptance

Automated verification

  • Parser fixtures cover valid Plex multipart, missing payload, duplicate parts, malformed JSON, JPEG retention, wrong content type and all size bounds.

  • Audit tests cover started and terminal records, fsync, startup warning detection and restored-track-aware 24-hour deduplication.

  • Filesystem tests cover either seed format, paired moves, collision suffix symmetry, vanished sources, rollback success and rollback failure.

  • Plex adapter tests cover verified machineIdentifier, metadata fallback, read-only database fallback, URL-encoded targeted refresh and refresh failure isolation.

  • Pushover tests cover exact fields, UTF-8 punctuation, multipart JPEG, priorities, TTL, permanent failures, one delayed temporary-failure retry, delivery_unknown, malformed JSON and API status 0.

  • Certificate tests cover a usable Python default CA file, the macOS /etc/ssl/cert.pem fallback, absence of every trusted CA source and retention of hostname and certificate verification.

  • Logging tests prove lifecycle markers, meaningful event selection, actionable failure recommendations, control-character escaping, rotation and the absence of secrets, tokens, full webhook URLs, payloads and JPEG bytes.

  • Code-quality checks verify module boundaries, documentation of non-obvious safety decisions, syntax, and the isolation of unexpected Pushover exceptions.

Live acceptance

Area Pass condition
Ingress A valid Plex media.rate multipart request is processed once and receives HTTP 200 with a recorded result
Trigger Only the commissioned owner one-star value reaches migration
Pairing An exact stem .m4a and .m4v pair moves together in either rating direction
Recovery Caught failures restore completed moves; incomplete rollback records every manual-recovery path
Plex A targeted refresh removes the committed item from active selection without a whole-library scan
Success PLEX - Track Removed follows commit with priority 0 and TTL 86400; a documented temporary failure receives one retry after five seconds
Failure A migration failure produces PLEX - Track Removal Failed at priority 1 with rollback state
Isolation Refresh and Pushover failures never change the migration result
Log volume An absent, replaced or unwritable PlexDB volume prevents every live media move and is reported through the local launchd error log
Restart A stale running marker or started operation without a terminal record produces a prominent abnormal-restart or recovery error and is never repeated automatically
Security No credential, full webhook URL, raw body or JPEG appears in logs or source
TLS runtime The exact interpreter named by the launch agent completes a verified HTTPS request to Pushover; testing a different Python installation is not acceptable

14 Costs and maintenance

Item Expected cost Notes
Pushover iOS license $4.99 one time after the 30 day trial One purchase covers the account owner's iPhone and iPad devices on that platform
Pushover API messages $0 at expected use Individual accounts include up to 10,000 messages per month across applications
Plex Pass $0 incremental Existing lifetime license
JUKEBOX compute and storage $0 incremental Uses the existing host Rejected volume and PlexDB log volume
Operations Owner time Review failed migrations, local launchd errors and storage growth when alerted

Pricing was verified against the official Pushover pricing and API pages on September 27 2026. Recheck the price and included message limit before any later purchase or material expansion.

Maintenance tasks

  • Review Rejected storage capacity and monthly audit continuity.

  • Review high-priority failure notifications promptly and reconcile incomplete rollbacks before rating related media again.

  • Review activity log archives and the local launchd error log after PlexDB outages.

  • Test a dry run replay after receiver, Plex, Python, or macOS changes that affect webhooks, multipart parsing, filesystems, or TLS.

  • After every Python or launch-agent change, run the Pushover TLS acceptance check through the exact ProgramArguments interpreter before restarting production.

  • Rotate credentials when exposed or when account ownership changes.

15 Source register

Official Plex sources

Official Pushover sources

  • [U1] Pushover. Message API. Documents HTTPS POST, required and optional fields, priority behavior, TTL, multipart attachments, byte and text limits, response status, request identifier, permanent errors and temporary-failure retry timing. https://pushover.net/api Accessed September 27 2026.

  • [U2] Pushover Support. Example code and Pushover libraries. Demonstrates multipart file attachment and safe form-string handling. https://support.pushover.net/i44-example-code-and-pushover-libraries Accessed September 27 2026.

  • [U3] Pushover. Pricing. Current client licensing and trial information. https://pushover.net/pricing Accessed September 27 2026.

Local verification records

  • [L1] JUKEBOX commissioning record. Genuine owner media.rate payload used to verify the configured one-star numeric value and current Account identity. Reverify during live acceptance.

  • [L2] JUKEBOX Plex integration test. Authenticated root response supplies machineIdentifier; targeted section refresh with a URL-encoded path is verified locally. The targeted refresh is not claimed as an official public Plex contract.

  • [L3] Build 11 code, configuration and launch agent preserved at ~/Library/Application Support/Plex Music Rejector/Rollback/v2.0-build11-before-v2.1-build12-20260927-202139. Rollback requires a fresh webhook secret.

  • [L4] JUKEBOX incident record e36cd880-d76c-4e18-a36d-0b980f3053d8. The Bangles track Be With You and its companion video migrated successfully at 2026-09-27 10:23:43 PM EDT, but both Pushover attempts failed certificate validation. The launch agent used Python.org CPython 3.14.7 at /usr/local/bin/python3; that interpreter reported no default CA file or directory, while macOS /etc/ssl/cert.pem and the Homebrew Python trust bundle both validated the Pushover endpoint.

  • [L5] Build 13 production verification. Thirty automated tests passed under the launch agent's /usr/local/bin/python3 interpreter. The repaired context loaded 128 authorities from /etc/ssl/cert.pem with certificate verification required and hostname checking enabled. Pushover accepted the live test at 2026-09-27 10:34:14 PM EDT with request identifier 4c413e6a-23ee-4d79-bf94-d58c5364b452. No media was accessed or moved by the test.

Implementation and verification status

Builds 1 through 11 completed implementation, cutover, live rejection acceptance, concise activity logging, recoverable cleanup, typed ordered move entries, shared release identity, configured lifecycle messages and historical log standardization. Build 12 corrected retry, duplicate suppression, credential placement, rollback and log-volume safeguards. Build 13 responds to incident e36cd880-d76c-4e18-a36d-0b980f3053d8 by selecting a usable Python default certificate store or the macOS CA bundle without disabling TLS verification and rejecting startup when no trusted CA source exists. Thirty automated tests passed locally, in staging and from the installed package under the production Python.org CPython 3.14.7 interpreter. The exact interpreter completed verified HTTPS with 128 trusted authorities, certificate verification required and hostname checking enabled. The production service reports healthy, live mode and v2.2 Build 13. Pushover accepted the live certificate-repair test with request identifier 4c413e6a-23ee-4d79-bf94-d58c5364b452. No media was accessed or moved by that test. Build 12 rollback material is preserved at ~/Library/Application Support/Plex Music Rejector/Rollback/v2.1-build12-before-v2.2-build13-20260927-223500.

Document Control

Field Value
Control ID NET-AUT-001
Lifecycle PUB
Status Published
Version v2.2
Build 013.20260928.023457Z
Canonical Filename NET-AUT-001_PUB_plex-music-rejector_design_013-20260928-023457Z.md
Prior Identity NET-005