Commit Graph
5 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 a8469e0642 fix(jellyfin): authenticate once, so the token survives to the password change
configure-auth.sh pins DeviceId="infinito-deploy" in CLIENT_HDR, and realign_admin_password authenticated twice with it: once for the access token, once for the user id. Jellyfin replaces the session bound to a device id when a new one is created, so the first token was already revoked when POST /Users/{id}/Password presented it. Run 33828309745 job 100890535194 shows the result as fifteen times "curl: (22) The requested URL returned error: 401", with the deploy otherwise complete (ok=2830 failed=1). get_user_id arrived with 1bf28bb2bf and carried the second call in from the start.

One AuthenticateByName response now yields both fields, read through token_of and user_id_of so neither expression is spelled twice.

Redeeming the reset pin clears the stored password: in that job forty authentications with the deployed password were denied, and the next one, with the pin string as the password, succeeded. Every exit after the redemption therefore states that the administrator is currently without one, and the two silent returns around ForgotPassword now log why they gave up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 13:54:31 +02:00
kevinveenbirkenbachandClaude Opus 5 f07b01025d fix(jellyfin): read the reset pin where Jellyfin writes it
1bf28bb2bf added realign_admin_password for exactly the case CI produces -- an
existing server whose credential rotated between the two passes -- and then looked
for the pin file one directory too deep. The path had never run, so nothing caught
it.

Jellyfin#0 of run 33445831564 is that path's first execution. The sync deploy at
04:33 drove the wizard green; the async deploy at 06:03 met the same persisted
/config with a new password. /Startup/* is closed on a provisioned server, so the
wizard attempts produced exactly 40 x `curl: (22) ... error: 401`, and the realign
took over.

Its first step worked. The stderr block holds exactly 40 401s and no 41st, so
POST /Users/ForgotPassword returned 200; and the client IP 192.168.215.1 sits inside
the server's own `Used LAN subnets`, so `isInNetwork` held and no
"initiated from outside the local network" warning was logged -- two other [WRN]
lines in the same log prove warnings are captured. The file was written.

It was written to ProgramDataPath. DefaultPasswordResetProvider.cs:38-39 anchors
`passwordreset` at `ApplicationPaths.ProgramDataPath`, and the failing container's
own startup line reads `Main: Program data path: /config`. /config/data is the
DataPath, the SQLite directory the same log reports as a separate `Storage path`.
The glob never matched, `cat` fell into 2>/dev/null, pin came back empty, and the
function bailed with its own "wrote no pin file" -- an accurate message pointing at
the wrong culprit.

1bf28bb2bf's commit message names the right anchor
("DefaultPasswordResetProvider:100 ProgramDataPath/passwordreset<md5>.json"). Only
the shell line disagreed with it.

Line 47 was the only /config/data in the file; :102, :104, :110, :168 and
meta/volumes.yml:6 all use /config. The filename is MD5 of the uppercased username,
so exactly one file matches and head -n1 stays harmless.

Nothing is masked: both error branches stay, and no retry count or timeout is
touched. The steps after it are already correct against v10.11.11 --
DefaultPasswordResetProvider.cs:77 `ChangePassword(resetUser.Id, pin)` makes the pin
the password, which is what get_token and the follow-up password change assume.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 09:59:01 +02:00
kevinveenbirkenbachandClaude Opus 5 1bf28bb2bf fix(jellyfin): realign the administrator password the wizard can no longer set
seed_admin_and_get_token only ever drove the startup wizard, and that wizard works
exactly once: on a server that is not yet provisioned. After the first deploy the
/Startup/* endpoints are closed and the administrator carries whatever password the
inventory held at install time, so a rotated credential leaves the account
unreachable. The CI rotates every credential between the sync and the async pass,
which is why job 99275699298 spent its 40 attempts on

    curl: (22) The requested URL returned error: 401

and then failed with the script's own diagnosis: "the administrator cannot
authenticate after wizard retries; the deployed password does not match the running
server". The script named the cause correctly and had no way back.

realign_admin_password runs only after the wizard loop is exhausted and uses
Jellyfin's documented local recovery. Read from the server's own source rather than
assumed:

  UserController.ForgotPassword          writes the pin file only when the request
                                         is local (IsLocal || IsInLocalNetwork)
  DefaultPasswordResetProvider:100       ProgramDataPath/passwordreset<md5>.json,
                                         serialized without options, so key "Pin"
  DefaultPasswordResetProvider:77        ChangePassword(resetUser.Id, pin)

That last line is the one that matters: redeeming the pin sets the password TO the
pin, it does not clear it. The pin is therefore both the credential to authenticate
with afterwards and the CurrentPw of the change that follows. A first draft logged
in with an empty password and would have failed on every run while passing every
lint.

Two shell corrections came out of the same pass. get_token now takes the password as
an argument, because reading it from the environment invited
`JELLYFIN_ADMIN_PASSWORD="" get_token`, where an assignment prefixed to a function
call can outlive the call. And the three captured values now guard with
`x="$(cmd)" || x=""` instead of one of them carrying `|| true` and the others none:
under set -euo pipefail the unguarded assignments aborted the script before the
emptiness check below them could report anything.

Worst case is the status quo: nothing here runs unless the deploy was already lost.

Same shape as 82452789c9 did for the homeassistant owner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 15:07:06 +02:00
kevinveenbirkenbachandClaude Opus 5 7162b7bf4c Merge fork/feature/svc-net-tor into feature/ai-agent-employees
834 commits from the Tor onion-service branch against 299 of ours, from merge
base 7b646bdece: 127 conflicted files, 3506 files changed.

The two sides are largely orthogonal. Theirs adds svc-net-tor and a `tor` service
across the role tree, plus two sweeping refactors: shipped scripts sorted by
language (files/ruby/, files/python/, files/shell/, scripts/lint/*.sh) and the
unit suite moved under tests/unit/python/. Ours is requirement 036, the native AI
gateway, plus the MCP role work. Most conflicts were both sides appending to the
same service map, resolved as unions.

Resolutions that were not unions:

Credentials moved namespace. Theirs renamed meta/schema.yml to meta/secrets.yml
and re-addressed credentials as secrets.credentials.<name>. Git carried our
litellm_api_key blocks through the rename, but every consumer we added still read
'credentials.<name>', which no longer resolves. All 45 call sites are migrated.

plugins/filter/container_volumes.py is deleted by theirs and relocated to
plugins/lookup/container_volumes.py, where it carries the same tmpfs defect our
f85996b43 had just fixed: a hard-coded {"size": ...} that silently drops a per
mount `mode`. The fix is re-applied in the relocated file, as that commit's merge
note anticipated.

compose.yml keeps only INFINITO_PLAYWRIGHT_REPORTS_BASE_DIR. Theirs also forwards
INFINITO_PLAYWRIGHT_STAGE_BASE_DIR; that line is deliberately dropped, per the
analysis recorded in 45804e9ad -- STAGE has a drift-proof role-vars fallback,
while forwarding it promotes a setdefault-pinnable .env value above that SPOT.

tasks/stages/01_constructor.yml takes their include, ./tasks/utils/setup/group.yml,
because their refactor removed tasks/groups/*-roles.yml, leaving our include
target nonexistent. Our task name is kept, since svc-virt is in the loop.

Makefile swarm-zombie is a union: their validate plus teardown, then our
lab_subnet cleanup, which their side dropped while leaving lab_subnet.sh as the
only orphaned reference in the file.

01_bootstrap.sh keeps our deterministic --ip pinning, which distinguishes an
already-attached node from a real failure, over their `|| true`.

Their language-sorted layout orphaned references our side still held: zammad's
two scripts/apply/*.rb, jenkins' java-with-ca-trust.sh, and the notify topics
their handlers renamed to hyphenated form. All corrected.

roles/sys-svc-compose/files/shell/swarm/stack_ready.sh gains our latched-update
block: paused, rollback_paused, rollback_started and rollback_completed exit 2
rather than polling a state that cannot resolve without another deploy.

config_from_run is unioned to (title, logged, jobs); the production caller in
trigger/__main__.py now passes jobs, without which our suite suppression would
have been inert.

READMEs are regenerated rather than hand-merged: 15 LiteLLM rows and 91 Tor rows
now derive from the merged metadata, which is itself evidence the meta merge is
coherent.

Verified: 5161 unit tests pass. The single failure, test_query.py TestSortSpec,
also fails on fork/feature/svc-net-tor at b51d2f9919, measured in a baseline
worktree -- so this merge introduces no new unit-test failure. Two modules are
excluded from that run because `baudolo`, a dependency their branch introduces,
is not installed on this host.
Not verified: no deploy has run against the merged tree, and make quality has not
been run end to end here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 16:32:30 +02:00
kevinveenbirkenbachandClaude Opus 5 575a5a87ac refactor(layout): sort shipped scripts by language and split the unit suite
Every script a role ships now lives under `files/<language>/`, and the unit
suite is split the same way: `tests/unit/python/` beside new sibling trees for
the other languages. `tests/lint/filesystem/test_role_files_layout.py` keeps it
that way; directories whose shape is dictated from outside carry a `.nocheck`
naming the constraint (Docker build contexts, WordPress mu-plugins, the Joomla
extension, Odoo addons, Playwright suites, the `files/test/test.sh` harness
that `test-e2e-cli` resolves generically).

This cannot be applied in halves. Moving a file and repointing what names it
are the same change: 14 references resolve only at deploy time, so the tree is
green either before or after, never between.

Those 14 were found by static resolution over every reference class, not by the
test suite -- a full `make quality-high` was green while all of them were
broken:

  * 7 `src:` values built from a role variable holding a bare filename
    (svc-bkp-remote-2-local, decidim, espocrm, friendica, joomla, pixelfed x2).
    The variable doubles as the destination name inside the container, so the
    language segment belongs at the call site, not in the variable.
  * `sys-service/tasks/04_base.yml` resolved every consumer's systemd script
    through `first_found(..., errors='strict')` over `files/script.{sh,py}`.
    18 roles ship exactly that and have no template fallback: the play would
    have died on each.
  * `sys-front-inj-javascript` fell back to `<role>/files/javascript.js`; the
    interface now reads `files/javascript/javascript.js`.
  * `sys-front-inj-logout/templates/head_sub.j2` built the cache-busting mtime
    path through `path_absolute(... 'files', ...)`.
  * 4 `ansible.builtin.script:` invocations naming a bare command
    (pull.py, prune.sh, set_postgres_superuser_password.sh, ganesha-serve.sh).
  * `dev-python/files/shell/install.sh` pointed at a sibling role's script.

Depth-dependent paths inside the moved trees were bumped accordingly: 59
`parents[N]` computations, 12 relative climbs, and the per-role recover CLI,
whose path `cli/administration/recover/recoverers.py` still assembled from the
pre-move location.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 01:31:09 +02:00