Commit Graph
15 Commits
Author SHA1 Message Date
thethreemagi 43a4324885 plex: auto-update via watchtower (Mike's call 2026-08-02, supersedes the pin)
Back to :latest + a watchtower service checking daily at 05:00, cleanup
on, ntfy notification to arrsstack-alerts on every update. Rollback net
= Plex's own scheduled DB backups + the NAS media_pc backup. shell-mcp
is a local build, watchtower ignores it.
2026-08-02 03:58:43 +01:00
thethreemagi ce2b49ef73 plex: pin image to 1.43.3.10828-ls316 (the exact running version)
Same PMS build the database was born on. SQLite schema upgrades are
one-way; a surprise :latest bump no longer gets a vote. Bump the pin
deliberately when choosing to upgrade.
2026-08-02 03:47:42 +01:00
thethreemagi d6760a709d shell-mcp: don't block the event loop; honor longer timeouts
subprocess.run inside the async handler serialized every request behind
the slowest command (found 2026-08-01 when a du wedged the server mid-
migration). Run it in the default thread pool via run_in_executor so
concurrent calls actually run concurrently. Timeout default 30->60,
cap 300->600, schema text matched.
2026-08-02 03:45:46 +01:00
thethreemagi 973c4bc44b bootstrap: post first-boot result to ntfy arrsstack-alerts (best-effort)
The box announces itself — publish path verified from 10.0.1.20 with a
200 on 2026-08-01. Never blocks or fails the bootstrap.
2026-08-01 15:02:28 +01:00
thethreemagi 2438e715fc bootstrap: remaining-steps text matches the 2026-07-31 flow (copy-off D:, format before Plex) 2026-07-31 19:46:12 +01:00
thethreemagi bc1e3d78d3 migration: new sequence — D: is the backup, copy-off to media_pc, format before first start
No Windows-side robocopy. Everything on D: (PMS dir, Calibre, Nikola
iPad) is copied to the NAS media_pc share from Linux, Media/ staged via
the SSD, D: formatted ext4 BEFORE Plex first starts, thumbnails live on
/srv/data. Fixes the M3 restore path that pointed at the unmounted
Share. Per Mike 2026-07-31.
2026-07-31 19:35:52 +01:00
thethreemagi 579787c8db plex: Media/ cache lives on the 3TB ext4 disk from day one, not the SSD
Bind mount is now active, not a commented Phase-2 option — Mike's call
2026-07-31 (337 GB is too large for the SSD). Quick Sync confirmed as
the transcode device; NAS account is read-only on the library shares.
2026-07-31 19:34:52 +01:00
thethreemagi 6f0605a4fd 20-cifs: seven shares — six library mounts read-only, media_pc read-write
NAS account is read-only on the libraries at the Synology side; the ro
mount option is belt-and-braces. media_pc is this box's one write share
(PMS backup, Calibre, Nikola iPad). Per Mike 2026-07-31.
2026-07-31 19:34:26 +01:00
thethreemagi 6f6d563569 README: 6 mounts not 8, migration/ in the layout, no-claim rule 2026-07-31 17:56:59 +01:00
thethreemagi 7017e9f4d5 bootstrap: closing notes point at the migration, not a claim token
Step 3 was "claim the server". That is now the one thing that must
not happen. It now points at migration/README.md M3/M4.
2026-07-31 17:56:31 +01:00
thethreemagi 9a3e824b90 50-plex: drop claim flow, gate on migrated DB and preserved identity
The forward-only decision was reversed: the server identity is now
migrated, not minted. Claiming would destroy the exact thing the
migration exists to preserve.

- remove every PLEX_CLAIM path
- check the six real mount points by exact share name
- refuse to start against an un-remapped database
- refuse to start without ProcessedMachineIdentifier in Preferences.xml
- assert machineIdentifier after start
2026-07-31 17:56:10 +01:00
ClaudeandClaude Opus 5 351eab41ac Full Plex migration: preserve everything, no rebuild
Reverses the forward-only decision. That call predated measuring the library;
at 27 TB / 25,148 video items / 337 GB of generated preview cache, rebuilding
costs 1-3 weeks of saturated gigabit and permanently loses every manual match
fix across 23,493 episodes. Migration preserves watch history, resume points,
collections, playlists, manual matches, artwork choices, added-at dates, the
337 GB cache, and server identity - so shared users stay invited, clients do
not re-add, and there is no claim step at all.

migration/remap.sql
  Four path columns remapped: media_parts.file (80,126), section_locations
  .root_path (11 -> 9), media_streams.url (14,837) and metadata_items.guid (9).
  Two formats, not one: backslash/UNC for the first two, file:// with %20
  encoding for the last two - decoding those %20s would break every subtitle
  reference containing a space, which given share names like Radio Shows is
  most of them.

  A scan of all 80 text columns across 82 tables found SIX columns matching
  korval. Only four are paths. taggings.text is one row reading Dr. Korval,
  and metadata_items.summary is four Liaden Universe blurbs about Clan Korval.
  A bare REPLACE on the word would have corrupted a cast credit and four book
  summaries, so every statement is anchored to a path prefix. Proven against a
  synthetic database built from the real path shapes: 12/12, including both
  false positives surviving byte-identical.

  Also drops the Audio Books and Music Organized roots - configured as library
  roots but holding 0 files / 0 bytes. The consolidation had already happened;
  only the dead roots remained.

migration/migrate-db.sh
  Runs the remap through Plex own SQLite build borrowed from the container
  image, keeps a .pre-remap rollback, asserts the counts moved 1:1 and the
  false positives did not, then stats 200 random remapped paths against the
  real filesystem. That last check is the one that matters - SQL running
  without error proves nothing.

migration/gen-preferences.ps1
  Plex live settings store on Windows is the REGISTRY, not Preferences.xml,
  and the two disagree here. Folds ~50 values into one Linux file, dropping
  Windows-only keys including the per-GPU limit keyed by 10de:1b81, the GTX
  1070 PCI ID. Preserves MachineIdentifier and the online token, which is what
  keeps the server identity. The existing Preferences.xml is malformed anyway
  (duplicate allowedNetworks) and will not parse strictly.

scripts/20-cifs.sh
  Eight shares down to six. Mount points now mirror the share names verbatim -
  /mnt/nas/Home Movies, space and all - because that reduces the remap to one
  uniform prefix substitution instead of eleven special cases. New requirement
  this creates: \040 escaping in BOTH fstab fields, not just the share name.
  Verified, plus a round-trip check that a remapped DB path lands under the
  generated mount point.

plex/docker-compose.yml
  Six read-only NAS binds whose source path equals target path, so database,
  host and container agree with no translation. No PLEX_CLAIM. Phase 2 Media
  relocation present but commented.

scripts/60-media-relocate.sh
  Post-soak, optional: moves the 337 GB cache to the 3TB ext4 disk so future
  growth (~28 MB per content-hour) stops eating the SSD. Plex does not support
  this, so the script forces generation on one title afterwards to prove writes
  survive the EXDEV boundary, and rollback is deleting a compose override.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 21:41:26 -04:00
ClaudeandClaude Opus 5 7eadcdf9e8 Bootstrap tree for the media box Linux conversion
Autoinstall lays down a thin base (sshd, key, DHCP, Docker CE, /srv) and hands
off to this repo on first boot. Everything interesting stays in git so it is
reviewable and re-runnable, rather than frozen onto a USB nobody can diff.

Stages, all idempotent:
  00-preflight  asserts hardware/BIOS state, changes nothing. Catches a BIOS
                update having silently re-enabled Secure Boot, which would stop
                the NVIDIA DKMS module loading on a box with no keyboard.
  10-secrets    ADD-ONLY seeder for /srv/secrets/stacks.env. Never overwrites an
                existing key. Verified against a pre-populated file: existing
                values, unrelated keys, the operator tier and existing manifest
                lines all survive byte-for-byte; a second run is a no-op.
  20-cifs       the 8 shares Plex actually uses (Share is excluded, it is not a
                library root). \040 escaping, nofail + x-systemd.automount +
                _netdev. Managed-block rewrite verified not to duplicate or to
                drop the root fstab entry.
  30-nvidia     nvidia-driver-580 explicitly: 580 is the LAST branch supporting
                Pascal, and the -open modules need Turing+. Pins against newer
                branches. Not in late-commands because DKMS needs the installed
                kernel, not the installer's.
  40-shell-mcp  builds the native MCP locally for amd64; refuses to finish
                unless /sse returns 401 without a token.
  50-plex       run by hand: PLEX_CLAIM expires in 4 minutes. Refuses to start
                against missing mounts and disables autoEmptyTrash, which with
                read-write NAS credentials is the most dangerous default here.

shell-mcp was built on arm64 originally. It builds clean on amd64 (whole dep
tree resolves to prebuilt manylinux x86_64 wheels, no compiler needed), but
dependencies are now pinned - the original installed mcp/starlette/uvicorn
unpinned and starlette has since gone 1.x. Port moved to 8103 so NPM host 42
can simply be repointed, and the tool description now says media box rather
than arrsstack.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 18:12:44 -04:00
thethreemagi 965f5f5ba0 Add first-boot orchestrator
Runs the stages in order and fails soft. A stage failure must never wedge
boot: there is no keyboard attached to this box, so a machine that comes up
with sshd and a broken GPU driver is recoverable and one that hangs is not.
2026-07-27 23:00:22 +01:00
thethreemagi 490003baf6 Initial commit 2026-07-27 22:59:48 +01:00