Commit Graph
14682 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 54a6f3b533 feat(svc-ai-litellm): warm the chat model during the deploy
A backend pulls its preload model at deploy time but only loads it into
memory on the first inference. That cold load outlives a reverse proxy's
read timeout, so the first consumer request comes back as a 504 while every
later one succeeds.

Send one max_tokens=1 completion from inside the litellm container right
after the per-consumer keys exist, where the container id, master key and
port are already resolved. Five retries cover a gateway that is not
listening yet.

This pairs with the ollama keep_alive setting: the warm-up pays the load
once, keep_alive stops it decaying after five idle minutes.

Note this hardens the deploy: a gateway that cannot serve its configured
model now fails the role instead of surfacing later as a consumer error.
That makes the openrouter/auto fallback with its empty API key loud rather
than silent.

The script ships with a mirrored unit test rather than a nocheck waiver; it
patches urlopen and asserts the URL, method, authorization header, model,
max_tokens and that the timeout exceeds a proxy read timeout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 12:02:17 +02:00
kevinveenbirkenbachandClaude Opus 5 dfe7a7fdd4 refactor(roles): sort role scripts into per-language folders
The merged branch moved every role-local script out of files/ into a folder
named for its language (files/python/, files/php/, files/shell/), but the
moves lost their staging and never landed. HEAD still carried the old paths
while the worktree had the new ones.

Move the 24 remaining scripts and update the six places that resolve them:
the mcp-adapter instance/isolation/probe tasks, the litellm key task, the
adapter README and the endpoint-path lint. Every changed line in those
files is a path.

Also drop roles/sys-svc-compose/files/swarm/stack_ready.sh, a stale
duplicate of the copy already tracked under files/shell/swarm/.

The added comments are the branch's own `nocheck: mirrored-unit-test`
directives, which state why a script whose measurement only exists inside
its container cannot have a mirrored unit test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 12:00:59 +02:00
kevinveenbirkenbachandClaude Opus 5 cd05368c9b feat(svc-ai-ollama): keep the preloaded model resident
Ollama unloads an idle model after five minutes by default. Preloading pulls
the model at deploy time but only the first inference loads it into memory,
so a consumer prompting a while after the deploy pays a cold load that eats
into its own provider timeout.

Declare keep_alive next to preload_async_time in the services SPOT, read it
in vars like its neighbours, and pass it to the container as
OLLAMA_KEEP_ALIVE. `-1` keeps the model loaded indefinitely; `ollama ps`
then reports UNTIL: Forever instead of Stopping...

This removes real latency but it is not what fixed the zammad timeout that
prompted it: that was a model which never emitted a stop token, addressed
separately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 11:56:16 +02:00
kevinveenbirkenbachandClaude Opus 5 8dbdd31b22 fix(svc-ai-ollama): preload a model that stops generating
The matrix variant preloaded smollm2:135m, which is too small to emit a
stop token reliably. Ollama's own log for the prompt "hi" shows it running
away rather than running slowly:

  n_gen = 22011, tg = 86.14 t/s
  slot context shift, n_keep = 4, n_left = 4091, n_discard = 2045
  aborting completion request due to client closing the connection

86 tokens per second is fast; 22000 tokens with repeated context shifts is
a model that never finishes. Every completion without a hard token cap ran
until the consumer's own timeout, which is why zammad reported
AI::Provider::ResponseError after exactly 300s while short warm-ups and
provider pings returned 200 immediately.

Preload qwen2.5:0.5b instead: a comparable footprint (397MB vs 270MB) that
is instruction-tuned and terminates.

web-app-openwebui asserted the served model with a hardcoded
id.startsWith("smollm2"), so it would have failed on any model change.
Render the deployed preload model into its Playwright env and compare
against that instead of a literal family name.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 11:54:15 +02:00
kevinveenbirkenbachandClaude Opus 5 b149ad0d22 fix(ai): pick the chat model from the deployed closure, not the whitelist
LITELLM_BACKEND_ROLES decides which backend branch LITELLM_CHAT_MODEL takes:
the first non-embedding ollama preload model, lmstudio/default, or the
openrouter/auto vendor fallback. It read lookup('deployment').running, which
plugins/lookup/deployment.py resolves as `whitelist or groups`.

On any targeted apps= deploy that whitelist holds only the roles named on
the command line, while backends like svc-ai-ollama arrive as dependencies
of the round. Both branches therefore missed and every consumer was
configured with openrouter/auto, a vendor model the in-cluster gateway does
not serve. The gateway answered each prompt with

  400 Invalid model name passed in model=openrouter/auto

while /v1/models listed smollm2:135m and lmstudio/default, so the surface
looked correctly wired while no consumer could complete a prompt.

Read .groups instead, the round's deployed closure, which is what the
`'<role>' in group_names` form used across meta/services.yml already means.
This reverses the selector introduced in 46a16127fc, whose rationale in
requirement 036 assumed the two selectors agree on a single-host round;
they do not once apps= is set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 11:52:14 +02:00
kevinveenbirkenbachandClaude Opus 5 0f6728a4c1 fix(tor): move the egress virtual range out of the docker address pool
TOR_EGRESS_VIRTUAL_NET_IPV4 was Tor's upstream default 10.192.0.0/10, which
spans 10.192.0.0 to 10.255.255.255 and therefore contains the whole docker
pool that 08_networks.yml allocates as 10.208.0.0/12.

The value drives an iptables rule in svc-net-tor/tasks/router.yml that
REDIRECTs TCP for that range into Tor's TransPort. With the ranges
overlapping, every connection from the controller to a container IP or to
the docker bridge gateway was captured and handed to Tor, which has no
mapping for a private address and resets it. The failure surfaced as
`errno 104 Connection reset by peer` against containers that had been up
for tens of minutes without a restart, and it reached anything addressing a
container through DOCKER_REACH_HOST, not just the LDAP schema load where it
was first seen.

Move the virtual range to 10.240.0.0/12, which sits above the docker pool.
Nothing else in group_vars or roles allocates the upper 10/8 space.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 11:50:05 +02:00
kevinveenbirkenbachandClaude Opus 5 1a81a6d864 fix(svc-db-mariadb): decouple the healthcheck from the root password
The container healthcheck logged in as root with MARIADB_ROOT_PASSWORD, so
it asserted two things at once: that the server is up, and that the root
credential matches. MariaDB only applies MARIADB_ROOT_PASSWORD while the
data directory is empty, so any later credential regeneration left the
container permanently unhealthy while every application user kept working.

That mixing produced a deadlock. sys-svc-compose waits for database health
before the play reaches svc-db-mariadb, but the repair for a drifted root
password lives in tasks/02_reset_root_password.yml, which runs after that
gate. The repair could therefore never run when it was needed: a failed
round showed the health wait retrying 65 times with the reset reached 0
times, while the round with this change reached the reset and root logins
worked again afterwards.

Use the credential-free check the image ships instead. It authenticates as
the mysql system user over the unix socket, so health answers only "is the
server up?". The credential question stays covered by the role itself:
tasks/00_core.yml probes the desired root login, includes the reset on
mismatch, and then waits until MariaDB accepts root credentials.

Verified against a container Docker reported as unhealthy: the new command
exits 0 there, and after the deploy the container came up healthy with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 11:48:19 +02:00
kevinveenbirkenbach 372c771ab1 Merge pull request #569 from infinito-nexus/update/docker-image-versions-svc-ai-ollama-20260827-fc73fbf
update(images): update Docker image versions (svc-ai-ollama)
2026-08-28 11:16:22 +02:00
kevinveenbirkenbach abc818df96 Merge pull request #572 from infinito-nexus/dependabot/npm_and_yarn/eslint-10.9.1
chore(deps-dev): bump eslint from 10.9.0 to 10.9.1
2026-08-28 11:00:27 +02:00
kevinveenbirkenbach 497536135d Merge pull request #570 from infinito-nexus/update/docker-image-versions-web-app-funkwhale-20260827-d5e092a
update(images): update Docker image versions (web-app-funkwhale)
2026-08-28 10:59:30 +02:00
kevinveenbirkenbach d061d98a39 Merge pull request #571 from infinito-nexus/update/docker-image-versions-web-app-penpot-20260827-62f7448
update(images): update Docker image versions (web-app-penpot)
2026-08-28 10:58:55 +02:00
dependabot[bot] 77b2623980 chore(deps-dev): bump eslint from 10.9.0 to 10.9.1
Bumps [eslint](https://github.com/eslint/eslint) from 10.9.0 to 10.9.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.9.0...v10.9.1)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.9.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-28 02:14:32 +00:00
infinito-nexus-updater[bot] 8c6436104f update(images): update Docker image versions (web-app-penpot) 2026-08-27 20:32:53 +00:00
infinito-nexus-updater[bot] 41322de703 update(images): update Docker image versions (web-app-funkwhale) 2026-08-27 20:32:48 +00:00
infinito-nexus-updater[bot] d1f722a431 update(images): update Docker image versions (svc-ai-ollama) 2026-08-27 20:32:41 +00:00
kevinveenbirkenbach 79a1957f67 Merge pull request #567 from infinito-nexus/dependabot/npm_and_yarn/stylelint-17.14.1
chore(deps-dev): bump stylelint from 16.26.1 to 17.14.1
2026-08-27 22:24:22 +02:00
kevinveenbirkenbach d472c178e9 Merge pull request #566 from infinito-nexus/update/docker-image-versions-web-app-semaphore-20260826-6290403
update(images): update Docker image versions (web-app-semaphore)
2026-08-27 22:23:01 +02:00
kevinveenbirkenbach c9ceb7c834 Merge pull request #565 from infinito-nexus/update/docker-image-versions-web-app-opentalk-20260826-79ed5ce
update(images): update Docker image versions (web-app-opentalk)
2026-08-27 22:22:44 +02:00
kevinveenbirkenbach 7ee2259bbc Merge pull request #562 from infinito-nexus/update/docker-image-versions-web-app-opentalk-20260826-da9308a
update(images): update Docker image versions (web-app-opentalk)
2026-08-27 22:22:20 +02:00
kevinveenbirkenbach 649f426515 Merge pull request #561 from infinito-nexus/update/docker-image-versions-web-app-nextcloud-20260826-f31a455
update(images): update Docker image versions (web-app-nextcloud)
2026-08-27 22:21:40 +02:00
kevinveenbirkenbach 575254200c Merge pull request #560 from infinito-nexus/update/docker-image-versions-web-app-gitlab-20260826-b29a436
update(images): update Docker image versions (web-app-gitlab)
2026-08-27 22:21:20 +02:00
kevinveenbirkenbach 54b678e6ba Merge pull request #558 from infinito-nexus/update/docker-image-versions-svc-ai-ollama-20260826-b5631c4
update(images): update Docker image versions (svc-ai-ollama)
2026-08-27 22:21:04 +02:00
dependabot[bot] f7bc5e9010 chore(deps-dev): bump stylelint from 16.26.1 to 17.14.1
Bumps [stylelint](https://github.com/stylelint/stylelint) from 16.26.1 to 17.14.1.
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint/compare/16.26.1...17.14.1)

---
updated-dependencies:
- dependency-name: stylelint
  dependency-version: 17.14.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-27 01:39:57 +00:00
infinito-nexus-updater[bot] d2266d7adc update(images): update Docker image versions (web-app-semaphore) 2026-08-26 18:38:34 +00:00
infinito-nexus-updater[bot] f024a89c55 update(images): update Docker image versions (web-app-opentalk) 2026-08-26 18:38:28 +00:00
kevinveenbirkenbach f21b831ae8 Merge pull request #563 from infinito-nexus/update/docker-image-versions-web-app-semaphore-20260826-9855a8c
update(images): update Docker image versions (web-app-semaphore)
2026-08-26 20:00:17 +02:00
kevinveenbirkenbach 8c2bde0f99 Merge pull request #559 from infinito-nexus/update/docker-image-versions-web-app-erpnext-20260826-079b453
update(images): update Docker image versions (web-app-erpnext)
2026-08-26 19:59:15 +02:00
kevinveenbirkenbach 8df5c31e6e Release version 13.0.0 v13.0.0 2026-08-26 09:39:19 +02:00
infinito-nexus-updater[bot] 9a7657334d update(images): update Docker image versions (web-app-semaphore) 2026-08-26 07:15:23 +00:00
infinito-nexus-updater[bot] 02e91d61bf update(images): update Docker image versions (web-app-opentalk) 2026-08-26 07:15:15 +00:00
infinito-nexus-updater[bot] dc183f443a update(images): update Docker image versions (web-app-nextcloud) 2026-08-26 07:15:09 +00:00
infinito-nexus-updater[bot] 6e6f7ab5b7 update(images): update Docker image versions (web-app-gitlab) 2026-08-26 07:15:03 +00:00
infinito-nexus-updater[bot] db17bba063 update(images): update Docker image versions (web-app-erpnext) 2026-08-26 07:14:59 +00:00
infinito-nexus-updater[bot] 261dbd8143 update(images): update Docker image versions (svc-ai-ollama) 2026-08-26 07:14:54 +00:00
kevinveenbirkenbach 8597e3f153 Merge pull request #380 from kevinveenbirkenbach/feature/svc-net-tor
feat(svc-net-tor): Tor onion-service provider (full onion node)
2026-08-26 09:13:16 +02:00
kevinveenbirkenbachandClaude Opus 5 ae71d8ea2c fix(test): give Composer an archive extractor
Without a zip extension or an unzip/7z binary Composer silently drops to
--prefer-source and mirror-clones every dependency, which exceeds its
300 s process timeout on phpunit.git. That took the Arch workspace job
from a 2m16s test-unit to a 16m08s failure. Debian never saw it because
apt pulls php-zip and unzip in as Composer dependencies; pacman does not.

Arch ships no zip extension and no php-zip package, so the extractor has
to be the unzip binary rather than the extension. Its mapping already
covers all five package managers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 02:40:06 +02:00
kevinveenbirkenbachandClaude Opus 5 95020d4fb4 fix(install): complete the ansible git fallback
requirements.git.yml carried 3 of the 7 collections the Galaxy file requests, and none of the transitive ones. When galaxy.ansible.com started returning 502, scripts/install/ansible.sh fell back to the git file, cloned its three collections, then resolved community.general's dependency on community.library_inventory_filtering_v1 against Galaxy again and died — five times over, taking make install and the whole CI workspace matrix with it.

All seven collections plus that dependency now resolve from git. Verified with an install run while galaxy.ansible.com was still 502: exit 0, seven collections installed, zero Galaxy contact.

A lint pins the parity so the fallback cannot rot back out of sync unnoticed; opt out per entry with the galaxy-git-parity nocheck marker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 19:02:45 +02:00
kevinveenbirkenbachandClaude Opus 5 fb54aa1451 fix(ci): force-cancel superseded runs that ignore the cancel
A cancel request does not stop a running step. The runner asks it to end, and a step that ignores the signal keeps going, bounded only by its own timeout-minutes -- 350 for a deploy. Meanwhile the concurrency group holds the newer run on pending until the occupant is terminal, not until it is cancel-requested. Measured on run 32859801382: the cancel was accepted at 15:27:45 and thirteen minutes later ten deploy jobs were still running and the newer run still had not started.

The sweep now waits FORCE_CANCEL_AFTER_SECONDS and force-cancels whatever is still not completed. Cleanup steps are skipped for those, which is the price: the artefacts of an already superseded run help nobody. Only entry-cancel-superseded.yml sets the variable -- a closed pull request and a deleted branch have nothing waiting on the group, so they keep cancelling and walking away.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 18:07:00 +02:00
kevinveenbirkenbach 9118a30ad0 Merge branch 'main' into feature/svc-net-tor 2026-08-25 17:27:25 +02:00
kevinveenbirkenbachandClaude Opus 5 e4353d9742 refactor(ci): derive the manual cancel override from the event
force_cancel_in_progress existed so a manual sweep could supersede a running orchestrator on any ref. It was a flag every caller had to remember to pass, and only one ever did.

A called workflow inherits github.event_name from its caller, so the orchestrator can see for itself that a human dispatched the run. The input is gone; the group reads the event instead. Automatic triggers keep following the ref alone -- everything but main -- and a manual dispatch supersedes everywhere, main included, because someone typed it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 16:29:11 +02:00
kevinveenbirkenbachandClaude Opus 5 e3d91fd8b2 docs(ci): catalog the cancel entry point
Adds entry-cancel-superseded.yml to the workflow catalog and widens the cancel emoji's legend, which named only PR close and branch delete.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 16:29:11 +02:00
kevinveenbirkenbachandClaude Opus 5 474aaf7bfe fix(ci): cancel superseded runs the group leaves behind
A long orchestrator sweep sometimes survives its concurrency group: the occupant sits on queued while its matrix waits for runners, cancel-in-progress does not reap it, and the newer run parks on pending until someone cancels the sweep by hand. PR #380 sat that way for nine hours behind a run from the previous commit.

entry-cancel-superseded.yml cancels those leftovers through the API. The group stays primary and untouched: it reaps within seconds and needs no runner, so the fallback normally arrives to find nothing left to do. It carries no concurrency group of its own, because the run holding a group cannot be the run that frees it.

Selection mirrors what the group would have done: cancel every matched run except the newest in the group instance, keyed the way the group is keyed. Not by head SHA, which goes stale while the job waits for a runner and would cancel the newest commit's CI while sparing the stale one. INCLUDE_PATHS keeps the sweep inside the group's own members, so a push to main cannot take the cron workflows with it.

deleted_branch_runs.sh becomes branch_runs.sh, since it now also serves live branches, and the shared parts move to lib.sh. A rejected cancel is logged and the remaining runs are still attempted; only a denied token fails the job.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 16:29:11 +02:00
kevinveenbirkenbachandClaude Opus 5 653dc632e4 refactor(ci): key cancellation on the ref, not a variable
CI_CANCEL_IN_PROGRESS gated whether a new run cancels the one before it, and it had to be remembered per repository: unset upstream meant cancel, set to false meant do not, and a fork inherited neither. Cancellation now follows the ref. Every branch but main supersedes its own runs; main never does, because a half-finished pipeline on the default branch is worse than a queued one.

call-orchestrator.yml compares github.ref rather than github.ref_name: a called workflow inherits the caller's ref, which is refs/pull/<n>/merge for a pull_request caller. The manual entry workflow keeps overriding the rule through force_cancel_in_progress.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 16:29:10 +02:00
kevinveenbirkenbach 4484842195 Merge branch 'main' into feature/svc-net-tor 2026-08-25 09:54:07 +02:00
kevinveenbirkenbach be6e5949b9 Merge pull request #556 from infinito-nexus/dependabot/npm_and_yarn/eslint-10.9.0
chore(deps-dev): bump eslint from 10.8.1 to 10.9.0
2026-08-25 09:54:06 +02:00
dependabot[bot] 5c7eeadd16 chore(deps-dev): bump eslint from 10.8.1 to 10.9.0
Bumps [eslint](https://github.com/eslint/eslint) from 10.8.1 to 10.9.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.8.1...v10.9.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 09:54:05 +02:00
kevinveenbirkenbach 477c30f7df Merge pull request #556 from infinito-nexus/dependabot/npm_and_yarn/eslint-10.9.0
chore(deps-dev): bump eslint from 10.8.1 to 10.9.0
2026-08-25 09:51:37 +02:00
dependabot[bot] f8f626a83d chore(deps-dev): bump eslint from 10.8.1 to 10.9.0
Bumps [eslint](https://github.com/eslint/eslint) from 10.8.1 to 10.9.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.8.1...v10.9.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-25 00:42:04 +00:00
kevinveenbirkenbachandClaude Opus 5 d751c8eb60 fix(web-app-bridgy-fed): pin upstream to a commit
The role tracked `main`, so every rebuild took whatever upstream had merged
since. On 2026-08-24 that was e7c6f80, "bump typing-validation from 1.2.12 to
2.2.1" - a release requiring Python >= 3.14, which turned three CI drills red
in one run. The image now names the commit it was tested against.

Upstream publishes no tags at all, so a semver pin is impossible and the
non-semver pin carries the marker the freshness lint asks for, rather than
opting out silently.

`git clone --branch` accepts a branch or a tag and refuses a commit, so the
clone becomes a shallow fetch of the single ref. GitHub serves an arbitrary
reachable commit that way, and the form takes a branch or tag unchanged if the
pin ever moves back to one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 00:25:00 +02:00
kevinveenbirkenbach f45df04bf1 test(docker): forbid ARG defaults in every Dockerfile
A defaulted build argument answers a question the caller failed to answer. The
build then succeeds against a base image, version or path nobody chose, and the
mistake surfaces as wrong behaviour at runtime instead of a failed build.

web-app-bridgy-fed shows the cost: its metadata declared python 3.14 while the
Dockerfile default silently built against 3.12. Both look plausible, so nothing
pointed at the drift. It and web-app-postmarks now take the base image from the
same services declaration the rest of the role reads.

The lint walks every tracked Dockerfile, template variants and paths outside
roles/ included, which the existing ARG-scope check does not. A second
assertion fails when the scan finds no Dockerfile at all, so the rule cannot
pass vacuously if the walk ever breaks.

Touching the two Dockerfiles brought their pre-existing narration under the
comment lint; what carried substance moved into the file header.
2026-08-25 00:25:00 +02:00