Commit Graph
70 Commits
Author SHA1 Message Date
infinito-nexus-updater[bot] e6032b3e37 update(images): update Docker image versions (web-app-socialhome) 2026-11-02 13:46:56 +00:00
infinito-nexus-updater[bot] c7d964e803 update(images): update Docker image versions (web-app-socialhome) 2026-10-31 09:14:02 +00:00
infinito-nexus-updater[bot] cd6dc48537 update(images): update Docker image versions (web-app-socialhome) 2026-10-30 07:34:47 +00:00
infinito-nexus-updater[bot] 033401c584 update(images): update Docker image versions (web-app-socialhome) 2026-10-29 07:19:52 +00:00
infinito-nexus-updater[bot] f0382013f1 update(images): update Docker image versions (web-app-socialhome) 2026-10-28 01:55:24 +00:00
infinito-nexus-updater[bot] bed4581faa update(images): update Docker image versions (web-app-socialhome) 2026-10-24 11:02:40 +00:00
infinito-nexus-updater[bot] 9d80486cb4 update(images): update Docker image versions (web-app-socialhome) 2026-10-22 15:07:42 +00:00
infinito-nexus-updater[bot] 4353fdbb12 update(images): update Docker image versions (web-app-socialhome) 2026-10-20 10:16:26 +00:00
evangelostsak 0c9ad653f7 Merge remote-tracking branch 'upstream/main' into feature/svc-net-wireguard 2026-10-17 22:19:57 +02:00
infinito-nexus-updater[bot] ee8e040502 update(images): update Docker image versions (web-app-socialhome) 2026-10-17 10:07:07 +00:00
evangelostsak e62ed8c56f Merge remote-tracking branch 'upstream/main' into feature/svc-net-wireguard 2026-10-17 09:34:00 +02:00
infinito-nexus-updater[bot] 723218a00e update(images): update Docker image versions (web-app-socialhome) 2026-10-16 20:16:31 +00:00
evangelostsak 7e8b075418 feat(web-app-socialhome): let a deployment put its hosts into meshes 2026-10-16 03:40:20 +02:00
infinito-nexus-updater[bot] 829d555be6 update(images): update Docker image versions (web-app-socialhome) 2026-10-14 23:16:12 +00:00
infinito-nexus-updater[bot] abf9a32294 update(images): update Docker image versions (web-app-socialhome) 2026-10-14 19:55:22 +00:00
infinito-nexus-updater[bot] 2a37349a60 update(images): update Docker image versions (web-app-socialhome) 2026-10-12 07:34:13 +00:00
infinito-nexus-updater[bot] 274ba4ea5d update(images): update Docker image versions (web-app-socialhome) 2026-10-10 07:26:22 +00:00
infinito-nexus-updater[bot] 057347df3f update(images): update Docker image versions (web-app-socialhome) 2026-10-08 16:44:00 +00:00
infinito-nexus-updater[bot] e6d65a088b update(images): update Docker image versions (web-app-socialhome) 2026-10-07 14:47:54 +00:00
infinito-nexus-updater[bot] 9e39388502 update(images): update Docker image versions (web-app-socialhome) 2026-10-06 19:16:31 +00:00
infinito-nexus-updater[bot] c222a61b04 update(images): update Docker image versions (web-app-socialhome) 2026-10-05 00:23:49 +00:00
infinito-nexus-updater[bot] 3bbb2ed8e9 update(images): update Docker image versions (web-app-socialhome) 2026-10-02 20:09:40 +00:00
infinito-nexus-updater[bot] 5382303151 update(images): update Docker image versions (web-app-socialhome) 2026-10-01 14:41:01 +00:00
infinito-nexus-updater[bot] 82b50c212c update(images): update Docker image versions (web-app-socialhome) 2026-09-30 14:59:31 +00:00
infinito-nexus-updater[bot] 28286c0f3d update(images): update Docker image versions (web-app-socialhome) 2026-09-22 14:35:19 +00:00
kevinveenbirkenbach 2d61e6291b Merge branch 'main' into feature/ai-agent-employees 2026-09-19 16:20:54 +02:00
infinito-nexus-updater[bot] ade1b11861 update(images): update Docker image versions (web-app-socialhome) 2026-09-19 03:20:52 +00:00
kevinveenbirkenbachandClaude Opus 5 152c7b16ae fix(socialhome): declare the inline permission the app's own page needs
The served document runs an inline script at line 38. It is not ours: in the
failing variant the Lua body filter renders an empty snippet list, so nothing
this repository injects can account for it.

Until now the only token that admitted that script came from csp_filters.py,
which grants 'unsafe-inline' to script-src-attr and script-src-elem whenever
services.logout is enabled. socialhome derives that flag from 'web-svc-logout'
in group_names, so the app's own page worked or broke depending on whether an
unrelated service shared the round.

Measured in run 35152776835. Job 105253846799 reports `Blocked URI: inline`,
`Directive: script-src-elem`, `Source: /:38:0`. The two rendered headers differ
in exactly that token: variant 0 emits `script-src-elem 'self' 'unsafe-inline'
...`, variant 1 emits `script-src-elem 'self' ...`. csp-checker exits with the
violation count, so this single violation failed the hlth-csp unit and
sys-service-terminator took the job down after 25 attempts.

Rendering build_csp_header against HEAD and against this change confirms the
delta is bounded: with logout off the directive gains the token, with logout
on nothing moves. script-src-attr stays undeclared because no artifact reports
a violation there.

An inline hash was considered and dropped. No role in this repository declares
meta/csp.yml hashes, the hash would have to byte-match the served document,
and csp_filters.py discards every registered hash whenever 'unsafe-inline' is
present, which is the state this app is in as soon as logout is deployed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 22:04:01 +02:00
kevinveenbirkenbachandClaude Opus 5 ed7a012faf fix(socialhome): declare the font origins the app itself loads
socialhome's own page head links a Google Fonts stylesheet. The deployment CSP did not know the origin, so the browser blocked it, csp-checker reported the violation, the hlth-csp unit failed and sys-service-terminator took the job down with it.

Job 104991069038 carries the only violation in the whole bundle, 52 times: [CSP DOM] Blocked URI: https://fonts.googleapis.com/css2?family=Fraunces:... with Directive: style-src-elem. Fraunces appears nowhere in this repository, so the reference comes from upstream socialhome rather than from our theming.

font-src for fonts.gstatic.com rides along: the stylesheet pulls the font files from there, so declaring only the stylesheet origin would have surfaced the next violation instead of clearing the check. That half is derived from how @font-face resolves and from the four sibling roles that declare the same pair, not observed, because the stylesheet never loaded.

This keeps a clearnet origin reachable from an onion deployment, which the mirror exists to avoid. The sibling roles carry a standing TODO to move such assets to the mirror; socialhome joins that debt rather than adding a new kind of it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 12:23:02 +02:00
kevinveenbirkenbach 503c199ead Merge branch 'main' into feature/ai-agent-employees 2026-09-12 16:35:20 +02:00
infinito-nexus-updater[bot] 91a449ca7c update(images): update Docker image versions (web-app-socialhome) 2026-09-12 03:36:41 +00:00
kevinveenbirkenbachandClaude Opus 5 4ac07edaea refactor(meta): move the info-media exemption to the file it speaks about
The marker names the reason a role has no upstream homepage or demo video, and
it sat in meta/main.yml because a comment-only info.yml parses to an empty
document, which the schema lint rejected outright. So the exemption lived one
file away from the two fields it explains, and a reader looking for the video
found neither the field nor the reason it was missing.

The schema lint now accepts an empty document exactly when it carries the
marker, and that is what allows the move. The media lint reads the marker from
info.yml rather than main.yml, and its failure message names that same file, so
the fix it suggests lands where the absent field would have gone.

A hundred and two roles follow. Every occurrence was a standalone comment on
the second line of main.yml, directly under the document start, so the move is
a line delete and an insert at the same offset. Seventy-three roles already had
an info.yml and gain the line; twenty-nine get one whose whole content is the
document start and the marker. All hundred and two reasons carry over verbatim,
including the thirty-five bespoke ones.

Nothing downstream sees a change. Of the four readers of meta/info.yml, three
coerce a null parse through `or {}` behind an isinstance guard, and
utils/cache/applications.py skips a falsy parse before assignment, so
applications.<role>.info stays absent for the twenty-nine exactly as it was
while the file did not exist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 22:26:38 +02:00
kevinveenbirkenbachandClaude Opus 5 e16eb5728d test(lint): drop the minimal variant's exemption from the onion
72fd9c870 required the onion in every variant but exempted variant 1, on the
argument that the minimal round is the one place the stack runs without it. That
argument does not hold: the onion is part of the deployment under test, not a
feature bolted onto the full shape. A round that skips it is not a smaller
deployment, it is a different one.

So the exemption is gone and 95 variant-1 entries pin the onion on. Two
exclusions remain, both read from data rather than a comment: a role with no tor
block has no onion to pin, and a role whose meta/services.yml pins tor.enabled to
a literal false has already declared it has none.

That put the rule in conflict with test_coverage, which requires both polarities
of every dynamic flag: with tor on everywhere, the false side has no variant left
to live in. 94 roles take the exemption that contract already provides for
exactly this - "flags whose group-derived value is a hard invariant that variants
must never override", the same one 4e6ef2542 used for the seaweedfs frontend.
web-app-nextcloud needs none: its variant 8 pairs the clearnet-only PeerTube and
keeps the false polarity.

Measured, because the declaration is not what CI runs. assign_tor gives the onion
to every second capable entry with the run number as the parity switch, so more
capable variants do not mean proportionally more onion jobs: across five roles
and 21 entries the count goes 8 -> 10 on an even run and 9 on an odd one. The
clearnet path also survives - checkmk reports v1=false on run 0 and v1=true on
run 1, so both states are still covered across two consecutive runs. What the
94 markers give up is a declarative guarantee in variants.yml, not the coverage
itself.

The real cost is weight, not job count. web-app-nextcloud variant 1 goes from
3.09GB reservation / 7.27GB limit to 18.26GB / 43.01GB once svc-net-tor and its
closure join, so the cheap round is no longer cheap. Every variant stays under
the 32/64GB budget, and the same effect applies to the other 94 unmeasured.

Verified: tests/lint 336 passed / 307 subtests, tests/integration/roles/meta/
variants plus the role-meta lints 39 passed. The rule was checked in both
directions - it reported the variant-1 offenders before the flips and none after.

Not measured: what the heavier rounds do to wall-clock, against swarm jobs capped
at 355 minutes and one that has hit that ceiling before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 06:09:02 +02:00
kevinveenbirkenbachandClaude Opus 5 ab72cff929 test(lint): require the onion in every variant, not just SSO ones
7a70d27b7 made a variant that switches SSO on also switch the onion on, because
that pairing is where the platform breaks in ways clearnet never shows. The same
argument reaches further than SSO: a Secure cookie is dropped, an https identity
provider refuses to embed, and crypto.subtle is undefined for any feature on a
non-secure origin, not only for a login. A variant that never runs the onion is
not testing the deployment users get.

So the rule now covers every variant of a role that declares a tor block.
Variant 1 stays exempt - it is the minimal, partner-free round every role
declares and the one place the stack is exercised without the onion at all - and
a role whose meta/services.yml pins tor.enabled to a literal false is skipped
outright, because that declaration already says the role has no onion and needs
no comment repeating it.

That found 137 variants. 101 were variant 1. Of the rest, 28 are the LDAP-only
auth-isolation rounds, which gain real coverage from running LDAP over the onion
and are flipped on here. Four cannot: svc-net-tor is the provider itself, and
nextcloud's variant 8 pairs the clearnet-only PeerTube. Both carry a marker
naming the reason; opentalk and coturn need none, since their services.yml pins
the literal false the rule now reads.

Flipping them exposed a contract that contradicted itself. test_auth_isolation
and test_non_baseline_explicit_disables demanded `shared: false` beside
`enabled: false`, while variants-static-override flagged that same line as a
static override - which is why all 133 of them carried a nocheck marker. The
demand only makes sense where `shared` is dynamic: a literal base value cannot
leak through deep-merge, so restating it protects nothing. Both contracts now
ask for it only in that case, and the 133 lines are gone.

The result is a stricter rule with fewer lines: 189 insertions against 193
deletions.

Verified: tests/lint 336 passed / 307 subtests, tests/integration/roles 107
passed / 610 subtests. The rule is checked in both directions - it reported 137
offenders before the resolution and none after.

Not measured: 28 additional onion rounds cost matrix wall-clock, against swarm
jobs already capped at 355 minutes and one that has hit that ceiling before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 06:09:02 +02:00
kevinveenbirkenbachandClaude Opus 5 b2a0a317fa refactor(tor): loosen the onion bond to 0.5
Every variants.yml header states the rule the bond feeds: variant 0 carries each
tightly-coupled (bond>=1, same-host) service, while loosely-coupled (bond<1,
separate-host) partners are excluded from the closure and distributed across
later variants to keep every host under budget. tor was declared bond: 1 in all
101 consumers, so it had to sit in every variant-0 closure.

It does not belong there. The onion is a separate-host provider reached over the
node's hidden service, the same shape as the bridged partners that already carry
0.5, and f1dff50fb established both the reasoning and the precedent when it
dropped xwiki and bigbluebutton from 1 to 0.5: bridged partners with their own
role and their own spec, whose bond declaration was the outlier rather than
their placement.

This also unblocks the variant-level tor contract. Requiring a non-baseline
variant to pin the onion on while tor still counted as tightly coupled would
have pulled variant 0's closure requirements along with it.

Verified: tests/lint 336 passed / 307 subtests and tests/integration/roles 107
passed / 610 subtests, including test_resource_budget over every variant's
deduplicated footprint and the bond-aware test_order closure.

Not measured, and the honest cost: f1dff50fb's own caveat applies unchanged
here. Whether redistributing a service out of the baseline closure buys back
wall-clock is not established; role-runtimes has the per-role seconds and no run
has been made with this shape.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 06:09:01 +02:00
kevinveenbirkenbachandClaude Opus 5 e96c752549 Merge fix/playwright/env-template-gate into feature/svc-net-tor
75 commits: the socialhome role reimplemented against the project it actually
deploys, native-login specs for funkwhale and shopware, a window.open IdP
handoff and a settings-link fallback in the shared persona helpers, a swarm
converge budget that gives up on a terminal task, and a long tail of role
fixes.

Eleven conflicts. The shared persona helpers (keycloak.js, logout.js) take the
incoming structure — they gained real behaviour this branch never had — with
this branch's resolveTimeout convention re-applied over it and
confirmKeycloakLogoutIfPrompted re-attached at every return point; without it
the Keycloak SSO session outlives the logout. playwright.config.js keeps this
branch's version: both sides set actionTimeout, but ours routes through an env
var and 15s is too short behind an onion. socialhome's spec takes the incoming
content — 2dbd6eb10 found the role described the wrong upstream project
entirely — and keeps gotoOnion and expectHstsWhenTls, since demanding HSTS over
a plaintext onion is a false failure. Both READMEs were regenerated rather than
merged by hand.

Three collisions arrived with no conflict marker at all, because each side
changed a different place in a different file:

- group_vars/all/18_swarm.yml declared swarm_stack_converge_retries twice.
  Same value, so nothing breaks today; editing the documented one would have
  silently changed nothing.
- svc-net-tor and web-app-socialhome both claimed 192.168.74.0/24. Socialhome,
  the newer role, moves to 192.168.75.0/24.
- web-app-socialhome/meta/schema.yml arrived as a new file, so ef5ea7cb5's
  rename never saw it. Its credential would not have resolved and the role
  would have deployed with an unusable administrator account.

The incoming specs also needed this branch's conventions: raw timeouts wrapped,
four page.goto calls routed through gotoOnion, a request.get given an
onion-aware timeout, and 21 roles marked playwright-service-flag over the tor
block this branch added to them.

make quality green on the merge result.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 14:20:19 +02:00
kevinveenbirkenbachandClaude Opus 5 52ab686470 fix(web-app-socialhome): accept 405 from the webserver health probe
The probe sends HEAD, and socialhome answers 405 there while GET / and GET /healthz both return 200, so the role was healthy and the swarm deploy still failed on three nodes after 25 retries. web-app-prometheus declares the same code for the same reason.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 08:43:11 +02:00
kevinveenbirkenbachandClaude Opus 5 9329be8649 feat(web-app-socialhome): deploy in swarm as well
The role was the only web app that pinned to a manager and still disabled swarm; 28 sibling roles run that exact combination. The comment that gated it named a CI-chaining risk and a condition that compose has since met, so it went with the flag.

The node-local SQLite volume stays behind placement: manager. A reschedule across managers would meet an empty volume, which is a platform-level property the siblings share, not a reason to single this role out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:48:13 +02:00
kevinveenbirkenbachandClaude Opus 5 cb3010893d docs(web-app-socialhome): justify the min_storage headroom
The 5GB budget covers the 4 GiB media cap that vars/main.yml pins through SH_MAX_STORAGE_BYTES, plus the SQLite database, which the headroom lint cannot infer from the image size alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 06:48:21 +02:00
kevinveenbirkenbachandClaude Opus 5 2dbd6eb108 feat(socialhome): implement the role so it actually deploys
The role was a scaffold: tasks/main.yml was a two-line comment, there was no
compose.yml.j2, no domains.yml, no volumes.yml, no env.j2. It deployed nothing
while carrying a Playwright spec and two matrix variants.

It also described the wrong project. meta/main.yml and meta/info.yml pointed at
jaywink/socialhome -- a Django ActivityPub hub. The target here is
social-home-io/socialhome: created 2026-04-21, MPL-2.0, aiohttp + SQLite +
Preact, federating peer-to-peer over Ed25519/WebRTC rather than ActivityPub.
Different codebase, no fork relationship.

It turns out to be the simplest web-app in the repo. One published multi-arch
image (ghcr.io/social-home-io/socialhome:2026.6.16, pinned), no Postgres, no
Redis, no queue, no worker, no collectstatic -- SQLite in one volume, one port,
migrations applied in-process at startup.

Two things the source forced that reading the README would not have:

- [standalone].external_url is reachable only through the TOML file, never
  through SH_* env (config.py:238-261). Without it routes/pairing.py answers
  422 NOT_CONFIGURED and federation pairing is dead, so the role renders a
  config and mounts it as type: config.
- provision_admin() runs from SH_ADMIN_PASSWORD but never calls
  mark_complete(), so / keeps serving the setup wizard instead of the login
  form -- upstream's own docstring claims otherwise. 01_setup.yml closes it
  with an idempotent POST to /api/setup/standalone.

No SSO sidecar, deliberately, against the pgadmin model this otherwise copies:
oauth2-proxy would redirect POST /federation/inbox and /api/pairing/accept to
Keycloak and break the one thing the software exists for.

Verified against the real image rather than reasoned about. The http_status
probe's exact argv ran inside the container and returned 0. /healthz answers
with the byte-for-byte shape the spec asserts. The setup POST returns 201 then
409 and flips setup_required true -> false, which is the whole justification
for 01_setup.yml existing. [standalone].external_url lands in the inbox_url of
POST /api/pairing/initiate. The container runs as uid 10001, matching
meta/users.yml.

Both persona blocks are proven, not assumed: biber gets 401 from the token
endpoint, and the 97 static files contain zero occurrences of logout / sign out
/ abmelden, so utils/logout.js has nothing to click. Injecting a control the
way web-app-prometheus does was rejected -- that model points its button at
OIDC.CLIENT.LOGOUT_URL, which does not exist for a role with no OIDC.

swarm stays disabled for the first round. The orchestrator chains
swarm -> compose -> host under mode_fail_fast, and sequencing measures serial
at 198 jobs, so a red swarm job here would skip 92 unrelated jobs and leave 85
roles untested. Compose-only drops that to 10. Flip it once compose is green.

Adversarial review caught five defects before this landed, three of them
certain rather than likely:

- SOCIALHOME_INTERNAL_URL hardcoded 127.0.0.1, which sys-svc-container rewrites
  to the bridge gateway whenever DOCKER_IN_CONTAINER is set -- from inside the
  Ansible container that address is the Ansible container, so both setup tasks
  would have burned five minutes of retries and failed the play.
- SH_WEBRTC_STUN_URL sat outside the coturn guard while the TURN pair sat
  inside it. Upstream reads by env PRESENCE, so the empty value would have
  beaten the in-app default and left WebRTC with no STUN at all.
- meta/csp.yml whitelisted blob: on worker-src. The bundle registers no service
  worker and constructs no Worker; the only blob of JavaScript is an audio
  worklet, and worklet modules load under script-src. Voice recording would
  have died with the app's own "Audio processing unavailable".
- tasks/main.yml hand-rolled meta: flush_handlers instead of including
  sys-svc-compose's finalize.yml, which silently skipped utils/post_deploy.yml.
- 01_setup.yml was gated on IS_STACK_HOST but not run_once, so a three-manager
  swarm would fire three concurrent setup POSTs at one ingress port.

TODO.md carried a claim that steered the CSP design and was simply false --
that img-src is not a directive csp_filters.py emits. It appends
"img-src * data: blob:" unconditionally at line 383.

lifecycle: alpha puts the role in the CI deploy matrix before it has ever run
anywhere, deliberately: the first CI round is its first execution. README and
TODO say so rather than claiming a maturity it does not have.

Still unverified: no browser has enforced the CSP, the swarm path was only
rendered, TURN against a real coturn is untested, and arm64 has not been
exercised. TODO.md lists them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 17:57:24 +02:00
kevinveenbirkenbachandClaude Fable 5 d88f272f9f Merge branch 'feature/swarm-nfs' into feature/svc-net-tor
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 15:31:08 +02:00
kevinveenbirkenbachandClaude Fable 5 194671d90f feat(readme): uniform description schema and clearer role titles
Rewrite all 254 role descriptions to a uniform noun-phrase schema
(function-first, no leading verb, no trailing period, <=120 chars) and
enforce it via the extended description lint. Rename 12 role README H1
titles for clarity (e.g. Backup Container to Local -> Backup Docker
Volumes, CSS Service -> Corporate Design, NGINX Legal -> Imprint) and
regenerate their Cosmos and Quick Setup sections. Overview table
regenerated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 01:11:48 +02:00
kevinveenbirkenbachandClaude Fable 5 da598a2d67 feat(readme): roles overview table with homepage/video backfill
Add cli.build.readme.overview (make readme-index / make docs) rendering a
roles overview in the root README: Name, Lifecycle, Homepage, Video,
Description, Integrated with, Install. New info-media lint requires
homepage + video in every invokable role's meta/info.yml (opt out via
'# nocheck: info-media' in meta/main.yml); backfilled 147 roles with
researched, adversarially-verified homepages and official videos.

Retire the 'unsupported' lifecycle value: fold it into 'eol' as a valid
live end-of-life status (shipped but untested/unmaintained) across the
stage lists, symbol glossary, allow-list lint and docs. Retag confluence,
jira, minio, phpldapadmin to eol and magento to pre-alpha.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 19:10:01 +02:00
kevinveenbirkenbachandClaude Opus 4.8 672e93246b feat(deploy): add host deploy mode + matrix symbol view
Host lane for invokable non-stack roles (drivers, backup, wireguard, swapfile, ...). modes.host.enabled declared on 56 such roles; new lint test_role_host_mode enforces it. The complexity matrix gains a host column (not stack and modes.host.enabled), --deploy-mode host, and host bundles (one per variant); apps.sh accepts INFINITO_DEPLOY_MODE=host and filters host == true. New test-deploy-host.yml workflow (INFINITO_MAX_JOBS=10) deploys discovered host roles via the mode-agnostic ci/all.sh path, wired into ci-orchestrator (modes default 'swarm compose host').

Also: complexity -s/--symbol compact view (emoji-only headers, true/false as check/cross, lifecycle stage symbols, grouped legend with per-entry explanations, display-width aligned); stack column moved after host. scripts/meta/env/load.sh made zsh-compatible by delegating to bash and importing INFINITO_/PYTHON/VENV/PATH back.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 03:44:17 +02:00
kevinveenbirkenbachandClaude Opus 4.8 e191b91de0 style: apply make autoformat (whitespace + quote/line normalization)
make autoformat output across role meta files, a task file and 19_tor.yml (single->double quotes on mode values, Jinja line reflow). No semantic changes. Kept separate from the onion CSP / oauth2-proxy fixes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 13:25:44 +02:00
kevinveenbirkenbachandClaude Opus 4.8 1d9d355bc9 feat(tor): add onion on/off variant coverage to domain roles
Each role's meta/variants.yml now pins the tor onion gate both ways so the matrix exercises it: variant 0 pins services.tor.enabled: true, every non-baseline variant pins enabled: false + shared: false (# nocheck: variants-static-override, since tor.shared is a static literal). This gives real test_coverage / test_bond_baseline coverage without the removed per-key code exemptions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:53:17 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0a7164af48 feat(tor): per-role onion service block in meta/services.yml
Domain-serving roles reference the svc-net-tor provider via a tor: block. Consumers declare enabled (dynamic group_names gate) and shared: true (static — onion is always the shared node model, inline nocheck: dynamic-flag). The provider svc-net-tor additionally declares exclusive/primary defaults that consumers inherit via resolve_service_config unless overridden.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 23:53:16 +02:00
kevinveenbirkenbachandClaude Opus 4.8 c6a8924525 fix(roles/meta): canonicalise galaxy_info.author spelling
Correct 'Birchenbach' -> 'Birkenbach' and normalise the non-breaking
hyphen (U+2011) to an ASCII hyphen in galaxy_info.author across 34
roles, so the author SPOT matches the regenerated README Credits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 16:47:34 +02:00
kevinveenbirkenbachandClaude Opus 4.8 3fa781004d feat(roles): add bond integration-importance factor to shared services
`bond` is a 0..1 factor for how strongly a service is tied to the overall
integration: bond: 1 = always required, bond: 0 = omittable. Groundwork for
tooling that later scores service importance and decides what can be dropped.
Named `bond` (not `bound`) to avoid clashing with the resource bounds/limits.

Add test_service_bond.py (lint): every enabled + shared service must declare a
valid `bond` (number in 0..1). Seed `bond: 1` as a placeholder on all 638
enabled+shared service entries across 88 roles, to be refined later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 06:03:17 +02:00
kevinveenbirkenbachandClaude Opus 4.7 24d4ab8e4a refactor(sso)\!: collapse oauth2 + oidc into unified services.sso block (req 21)
Implements docs/requirements/021-sso-flavor-migration.md atomically:

* Schema: every role's meta/services.yml replaces the parallel
  `oauth2:` / `oidc:` top-level blocks with a single `sso:` block
  carrying `enabled`, `shared`, and a `flavor` discriminator
  (`oidc` | `oauth2` | `saml`; default `oidc`). Flavor-specific
  sub-keys live under `sso.<flavor>.*` (`sso.oauth2.{origin,acl,
  allowed_groups}`, `sso.oidc.plugin`).
* Provider role: roles/web-app-oauth2-proxy/ is deleted in its
  entirety; its 5 sidecar templates move under
  roles/web-app-keycloak/templates/sso_proxy/, and the per-consumer
  rendering task lives at roles/web-app-keycloak/tasks/sso_proxy.yml.
  web-app-keycloak now declares `provides: sso`.
* Runtime: new resolver utils/roles/applications/services/sso.py
  centralises the SSO predicates (`is_enabled`, `is_proxy_gated`,
  `is_oidc_native`) and the oauth2 sub-values
  (`oauth2_origin_{host,port}`, `oauth2_acl`, `oauth2_allowed_groups`).
  Three lookup plugins consume the resolver: `sso` (single-app
  predicate access), `sso_proxy_consumers` (enumerates oauth2-flavored
  consumers), `sso_oidc_plugin` (renamed from `oidc_flavor`, Nextcloud
  plugin selector). sys-stk-backend, sys-svc-compose, sys-svc-proxy
  and plugins/filter/compose_volumes.py all gate on
  `lookup('sso', ..., 'is_proxy_gated')` instead of manually combining
  `enabled` and `flavor`.
* Identifier renames repo-wide: `services.<entity>.ports.local.oauth2`
  → `ports.local.sso`; `PORT_BANDS.local.oauth2` → `PORT_BANDS.local.sso`;
  `OAUTH2_PROXY_*` env/Jinja vars → `SSO_PROXY_*`;
  `oauth2_proxy_cookie_secret` → `sso_proxy_cookie_secret`;
  `OAUTH2_SERVICE_ENABLED` + `OIDC_SERVICE_ENABLED` collapse into
  `SSO_SERVICE_ENABLED`; Playwright gating helpers
  (isServiceEnabled/skipUnlessServiceEnabled/requireService/safeIsEnabled/
  safeSkipUnlessEnabled) move from `oidc`/`oauth2` to `sso`.
* Dual-block resolution: per req's Background-Table, web-app-bookwyrm
  is `flavor: oauth2`; web-app-gitea and web-app-friendica keep
  `flavor: oauth2` with their existing `oauth2.acl.blacklist`
  (/user/login) and `oauth2.acl.whitelist` (federation + discovery
  endpoints) — preserving the operative oauth2-proxy gating.
* Tests: the two mutual-exclusion guards
  (tests/integration/iam/oauth2_oidc/test_mutual_exclusive.py and
  test_acl_mutual_exclusive.py) are deleted per Decision #10;
  test_oauth2_contract.py is renamed and rewritten to
  test_sso_contract.py, asserting that `flavor: oauth2` roles carry
  `oauth2.origin.{host,port}` plus `ports.local.sso`; test_sso.py
  (web-app integration) now requires the unified `sso:` block with an
  explicit flavor or a `# nocheck: sso` opt-out; new
  tests/lint/repository/no_legacy_sso_paths/ guards the source tree
  against re-introducing the legacy strings.
* Docs: req 21 itself has every AC checked off, and reqs 006, 008,
  009, 013, 015, 016, 019, 020 plus 10+ role READMEs and
  docs/contributing/actions/testing/suppression.md are updated to the
  unified SSO naming.

Empirically validated end-to-end against the validation app set
(web-app-keycloak, web-app-nextcloud, web-app-bookwyrm, web-app-gitea,
web-app-friendica, web-app-dashboard) in matrix variants 0 and 1; v2
also passes the 4 target roles' Playwright after a documented
pre-existing KC_HOSTNAME-drift workaround (separate concern, not
caused by this refactor).

BREAKING CHANGE: every consumer of `services.oauth2.*`,
`services.oidc.*`, `OAUTH2_PROXY_*`, `OAUTH2_SERVICE_ENABLED`,
`OIDC_SERVICE_ENABLED`, `oauth2_proxy_cookie_secret`,
`oauth2_proxy_application_id`, `ports.local.oauth2`, the
`web-app-oauth2-proxy` role, or the legacy gating helper service names
(`oidc`/`oauth2`) must migrate to the unified `sso` shape documented
in docs/requirements/021-sso-flavor-migration.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 12:59:59 +02:00