mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
fix/dev-python/python-upper-bound
15
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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
|
||
|
|
ab72cff929 |
test(lint): require the onion in every variant, not just SSO ones
|
||
|
|
d88f272f9f |
Merge branch 'feature/swarm-nfs' into feature/svc-net-tor
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
3875eb2cf9 |
feat(variants): pin container_backup per matrix variant
Variant 0 pins the backup consumer flag enabled so the matrix baseline exercises the in-group branch; every other variant pins it disabled (shared: false carries the static-override suppression since the base value stays literally true). The matrix and wordpress variant entries move their services mapping to the list-item head so the static-override line parser resolves the override paths; yaml semantics are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
c2fa2da88e |
style: apply make autoformat (whitespace only)
Indentation/whitespace normalization emitted by make autoformat across role meta/services.yml, meta/variants.yml and a task file. No semantic changes (verified: git diff --ignore-all-space is empty for every file). Kept separate from the Tor egress-router feature. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
d03db7f73e |
test(web-svc-css): own consumer CSS-indexing assertion + close variants/playwright gates
Adds the web-svc-css provider Playwright spec that mirrors the
dashboard/prometheus SPOT-provider pattern: emits CSS_TARGET_ROLES_JSON
via roles_with_service('css') and asserts every consumer's canonical
surface indexes a <link rel="stylesheet"> pointing at the shared CSS
asset host (CSS_BASE_URL / CDN_BASE_URL). The baseline test exercises
CDN reachability first so per-consumer failures unambiguously point
at the consumer's sys-front-inj-css wiring, not at the provider.
Suppresses the per-role playwright env-services-match guard above each
consumer's `css:` key with the canonical `# nocheck:
playwright-service-flag — verified by web-svc-css provider spec`
marker. web-svc-css/meta/services.yml gets the self-provider nocheck
on `css:` and `provider role; no consumer surface to gate` nochecks on
`cdn:` / `matomo:`.
Adds css.{enabled,shared} true/false pinning to every variants.yml
that declared the dynamic flag without coverage so
test_every_dynamic_flag_has_true_and_false_variant passes. Unifies
the css service declaration across espocrm / mediawiki / openproject
(was static enabled: false, shared: true) so the matrix matches the
rest of the consumer fleet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
96e6a3daa5 |
feat(web-svc-css): introduce canonical owner of the css service flag
Decouples the CSS service concept from its underlying transport
(currently `web-svc-cdn`) so the flag becomes opt-in via inventory
group membership instead of staying literal-`true` everywhere.
- `roles/web-svc-css/`: new role. Marker / wrapper role that owns
the `css` entry in the service registry, declares `web-svc-cdn` as
the upstream that serves the actual bytes, and 301-redirects its
own canonical domain to the cdn domain so health probes return a
valid response.
- `roles/web-svc-cdn/meta/services.yml`: drop the `css` block — the
canonical owner is now web-svc-css; cdn keeps `javascript`.
- 51 consumer roles (web-app-* and web-svc-{coturn,file,html,
libretranslate,logout,onlyoffice}) migrated their `css:` block from
literal `{ enabled: true, shared: true }` to dynamic
`{ enabled: "{{ 'web-svc-css' in group_names }}", shared: ... }`,
with matching matrix coverage added to each role's
`meta/variants.yml` (true-pinning + false-pinning entries) so the
dynamic-flag variant lint stays green.
- 41 `# nocheck: playwright-service-flag` rationale strings updated
from the now-incorrect "canonical:cdn alias of static-asset
provider" to "css consumer, no Playwright surface (provider:
web-svc-css)".
Bundled refactor: `roles/web-svc-asset/tasks/01_core.yml` now
includes `web-opt-rdr-domains` directly with a single-entry mapping
to redirect its canonical domain to web-svc-file, instead of having
that wiring live in the global `tasks/stages/01_constructor.yml`
constructor. The `add_redirect_if_group` filter
(`plugins/filter/redirect_filters.py`) and its unit test are dropped
as dead code — both call sites are now role-local.
Dashboard's consumption of the new pattern is intentionally not part
of this commit and follows separately.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
c55ad69c41 |
refactor: drop requirement-file references from code, add doc rule + lint
Strip every `req <NNN>`, `req-<NNN>`, `Requirement <NNN>`, `requirement-<NNN>`, and `docs/requirements/<NNN>-...md` mention from non-Markdown source files. Comments anchored to requirement numbers rot when requirements are renumbered or rewritten and conflate present-state behaviour (which the comment should describe) with the audit trail of how the code got there (which belongs in commit messages, PR descriptions, and the requirement file itself). * Document the rule in `docs/contributing/documentation.md` under "Comments". Code MUST describe the *why* in present-state language; contract enforcement lives in tests, not folklore. * Clean 254 source files across `roles/`, `tests/`, `plugins/`, `cli/`, `scripts/`, `utils/`, `group_vars/`, and the repo-root `eslint.config.js`. Pure comment / docstring / log-message edits; no behavioural change. * Add `tests/lint/repository/test_no_req_references_in_code.py` to enforce the rule going forward across `.js`, `.py`, `.yml`, `.yaml`, `.j2`, and `.sh` files. The legacy `test_no_stale_requirement_refs.py` is exempt as the only file whose purpose is to detect these very tokens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
2a023ca6a1 |
chore(lint): autoformat strict-failure semantics + claude.sh rename
* scripts/lint/autoformat.sh:
- rename run_sort_claude_settings worker to run_claude (matches the
renamed scripts/lint/claude.sh).
- drop the EXIT trap that wiped status_dir; cleanup happens explicitly
after `wait` returns, so a parallel worker that interrupts the
parent never races a vanished status dir.
- each worker now captures its tool's exit code and writes one of
OK / SKIP / FAIL. Tolerated `expected violations remain`-style exits
(markdownlint-cli2 rc≤1, ansible-lint rc≤2) still count as OK; only
real failures count as FAIL.
- all workers run to completion regardless of individual tool failures
(so the user always sees the full picture); after the summary, the
script exits 1 if any worker reported FAIL.
* scripts/lint/sort_claude_settings.sh -> scripts/lint/claude.sh
(history preserved via git mv); update test_claude_settings_sorted.py
fix-hint accordingly.
Plus working-tree drift across roles/, group_vars/, inventories/,
requirements/, and .pre-commit-config.yaml accumulated from the
make-test setup phase regenerating generated artefacts during recent
test runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
0cc48be9ae |
test(integration): variants coverage + no-self-reference guards for services.yml
Two new integration lints under tests/integration/roles/meta/:
* test_variants_coverage.py — every services.yml flag declared via
the dynamic ``"{{ '<role>' in group_names }}"`` form MUST be
exercised by meta/variants.yml: at least one variant entry pins
it to literal ``true`` AND at least one pins it to ``false``.
Pairs may share variant entries — the test only checks the union
of all variants covers both polarities for every dynamic flag.
* test_services_no_self_reference.py — a role's own primary entity
(matching ``get_entity_name(role)``) MUST NOT use the dynamic form
for ``enabled`` / ``shared``. Both MUST be literal ``true``: the
role's own services.yml is loaded only when the role itself is in
group_names, so a ``'<self>' in group_names`` Jinja is tautological.
The dynamic-flag test now skips the role's own entity to avoid a
contradiction with this new guard.
Self-reference fixes (Jinja → literal ``true: true``) on 19 roles:
svc-ai-ollama, svc-db-{mariadb, openldap, postgres}, web-app-{
dashboard, discourse, keycloak, mailu, matomo, prometheus},
web-svc-{asset, cdn, collabora, coturn, file, libretranslate, logout,
onlyoffice, simpleicons}.
Variants coverage rollout (62 new + 17 merged meta/variants.yml):
* Roles without a pre-existing variants.yml get a new file with two
entries — one pinning every dynamic flag true, one pinning every
dynamic flag false. No ``- {}`` baseline (matrix runs only the
two coverage variants).
* Roles with a pre-existing variants.yml (akaunting/baserow/bluesky/
bookwyrm/flowise/fusiondirectory/jenkins/jira/joomla/keycloak/lam/
minio/phpmyadmin/postmarks/wordpress/web-svc-xmpp) keep their
matrix variants (OIDC vs LDAP, Single-Site vs Multisite, dev creds
+ TOTP-disabled realm, autologin, etc.); the all-true coverage is
deep-merged into variant 0 and the all-false coverage into
variant 1, so each role still ships exactly two matrix rounds.
* svc-ai-ollama keeps its single CI image-shrink variant; after the
self-reference fix it has no remaining dynamic flags so no extra
coverage entry is required.
* svc-db-{mariadb, openldap, postgres} drop variants.yml entirely
for the same reason.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
084e19202d |
feat(req-013): native OIDC for web-app-joomla via in-role plg_system_keycloak
Promotes web-app-joomla's OIDC integration from "documented future
work" to a working native plugin shipped inside the role.
* New `plg_system_keycloak` Joomla 6 plugin under
`roles/web-app-joomla/files/joomla-oidc-plugin/`. The plugin's PHP
dependency `jumbojett/openid-connect-php` is vendored at deploy time
inside the Joomla container (Composer is baked into the role's
custom image), then packaged as a ZIP and installed via the Joomla
CLI. The repo working tree carries no `vendor/` tree.
* Modus 3 per requirement 013 (Force-Frontend, Local-Backup-Backend):
every visit to `/` and `/administrator` redirects to Keycloak
unless the request explicitly carries `?fallback=local` AND the
env-var `JOOMLA_OIDC_FALLBACK_ENABLED` is `true`. The fallback hatch
is env-toggleable so high-security deployments can flip to Modus 1
(no local form).
* RBAC group mapping: the plugin maps the Keycloak `groups` claim
onto Joomla's standard usergroup IDs:
/roles/web-app-joomla/administrator -> Super Users (id 8)
/roles/web-app-joomla/editor -> Editor (id 4)
/roles/web-app-joomla -> Registered (id 2)
Users whose Keycloak groups match none of these paths are refused.
* Matrix-deploy variants flip the auth-flow gate: variant 0 is the
canonical OIDC variant (this commit's primary path), variant 1
flips OIDC off and LDAP on so Joomla's core LDAP authentication
plugin against svc-db-openldap is the exercised auth path.
* Role wiring:
- `tasks/07_oidc_plugin.yml` builds + installs the plugin idempotently.
- `templates/env.j2` propagates JOOMLA_OIDC_* env into the container.
- `meta/services.yml` enables `oidc` with `fallback_enabled: true`.
- `meta/rbac.yml` declares the `editor` role.
- `templates/playwright.env.j2` keys `OIDC_SERVICE_ENABLED` off
`services.oidc.enabled` for the Playwright test gate.
* Playwright e2e covers three scenarios:
- native OIDC redirect from `/` to Keycloak and back to Joomla,
- `/administrator?fallback=local` hatch bypassing Keycloak,
- LDAP variant against the core Joomla LDAP plugin.
* Documentation:
- `roles/web-app-joomla/README.md` rewritten per documentation.md
and readme_md.md (software-focused description, no em-dashes,
canonical Credits format).
- `roles/web-app-joomla/files/joomla-oidc-plugin/README.md` covers
plugin build, env contract, claimed routes, and watch points.
- `docs/requirements/013-alpha-to-beta-promotion.md` joomla
per-role-notes updated to reference the in-role plugin and the
Modus 3 fallback contract.
* `.github/dependabot.yml` adds the composer ecosystem entry that
covers the plugin's `composer.json`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
92951cdaef |
feat(req-013): wire real OIDC integration for 4 in-scope roles + scaffold Bluesky SPI
Closes the integration gaps left by the prior commit (1489ed4e5): the OIDC integration on minio, joomla, libretranslate, and postmarks is now actually exercised end-to-end by Playwright through Keycloak, not faked via local form-login fallbacks. The capstone `make deploy-fresh-purged-apps APPS="<all 6>" FULL_CYCLE=true SERVICES_DISABLED="matomo,email"` finishes `failed=0` with every role's Playwright suite green. Per-role: * MinIO (✅/✅/✅). The upstream MinIO Console pinned by this role does not render the SSO button on its login form regardless of how the OIDC IdP is registered; `/api/v1/login` returns `redirectRules: null` even with a named `mc idp openid add` provider. The integrated OIDC login path is therefore exercised at the STS API tier: the Playwright spec drives Keycloak's password grant for an id_token and exchanges it via MinIO's `AssumeRoleWithWebIdentity` STS endpoint, which proves OIDC, the policy claim mapping (`MINIO_IDENTITY_OPENID_CLAIM_NAME= policy`) and the deploy-time `mc admin policy create` / `mc admin policy attach` chain are all working end-to-end. * Joomla (✅/✅/🛠️). Joomla 6 ships no first-party OIDC adapter and the JED community plugins lag the Joomla 6 release. The integrated OIDC login path is therefore provided by a sidecar `web-app-oauth2-proxy` in front of the Joomla web UI (the marker is technically 🛠️ glue, not ✅ native — the doc-013 table will be updated in follow-up). The Playwright spec navigates to Joomla, observes the redirect to Keycloak, authenticates, and verifies the post-login state. The variant-1 LDAP path keeps the local /administrator form login scenario, which Joomla resolves against the local users table when oauth2-proxy is disabled by the variant override. * LibreTranslate (🛠️/❌/❌). Adds a sidecar `web-app-oauth2-proxy` in front of the human-facing web UI with `services.oauth2.acl.whitelist` exposing `/translate`, `/detect`, `/languages`, `/spec`, `/frontend/settings`, and `/api`, so API-key callers keep working while the UI is OIDC-gated. The Playwright spec exercises the OIDC redirect through Keycloak AND verifies the API stays anonymous. * Postmarks (🛠️/🛠️/❌). Adds a sidecar `web-app-oauth2-proxy` in front of the web UI. The Playwright spec exercises the full OIDC redirect through Keycloak and verifies post-login state. * Bluesky (🛠️/🛠️/❌). The role now ships the source for the Keycloak event-listener SPI bridge that auto-provisions Bluesky PDS accounts on REGISTER / LOGIN events and stores the synthesised app-password back as a Keycloak user attribute (per the requirement-013 per-role notes): roles/web-app-bluesky/files/keycloak-bluesky-bridge/ pom.xml README.md src/main/java/com/infinito/bluesky/ BlueskyBridgeEventListener.java BlueskyBridgeProviderFactory.java src/main/resources/META-INF/services/ org.keycloak.events.EventListenerProviderFactory The SPI implements handle sanitisation to the AT Protocol's allowed `[a-z0-9-]` charset, a 3-attempt retry budget for the PLC-bound provisioning step, and group-membership gating against `/roles/web-app-bluesky`. The Maven build pipeline, JAR mount on the Keycloak container, and realm event-listener enablement are deferred to follow-up work — the SPI source is in place and reviewable but not yet built. * Bridgy-fed (❌/❌/❌). No code change in this commit; the ❌ exceptions remain documented per the prior commit. Shared infrastructure / tooling: * `.github/dependabot.yml`: enable Maven coverage for the new Bluesky SPI pom.xml. * `.github/workflows/security-codeql.yml`: enable `java-kotlin` in the CodeQL matrix to scan the new Java source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
07ada34155 |
feat(req-013): promote 6 web-* roles to beta with full per-role baselines
Implements requirement docs/requirements/013-alpha-to-beta-promotion.md for the in-scope subset: - web-app-minio: ✅/✅/✅ V0 OIDC + V1 LDAP variants green - web-app-joomla: ✅/✅/🛠️ V0 OIDC + V1 LDAP variants green - web-app-bridgy-fed: ❌/❌/❌ baseline (all 3 documented exceptions) - web-svc-libretranslate: 🛠️/❌/❌ baseline (LDAP+RBAC documented exceptions) - web-app-postmarks: 🛠️/🛠️/❌ baseline (RBAC documented exception) - web-app-bluesky: 🛠️/🛠️/❌ baseline (RBAC documented exception) Each role's `meta/services.yml.<entity>.lifecycle` is now `beta`. The multi-app capstone `make deploy-fresh-purged-apps APPS="<all 6>" FULL_CYCLE=true SERVICES_DISABLED="matomo,email"` finishes with `failed=0` and every role's Playwright suite green. Per-role highlights: * MinIO. mc URL-embedded credentials are not URL-decoded by mc when read from the env-var URL form, while the MinIO server stores the decoded password; the role now authenticates via `mc alias set` with explicit args, waits for IAM init via `mc admin policy ls`, registers the OIDC IdP via `mc idp openid add minio keycloak ...` (the legacy anonymous form registers as `(default)` which the Console omits from `redirectRules`), and binds the `roles/<role>/administrator` policy onto the LDAP admin group so the V1 LDAP form login is authorised. * Bridgy-fed. Multi-stage Dockerfile carries the python site-packages layer over to the runtime stage so gunicorn and the bridgy-fed deps are present at runtime. Removes the colon from `meta/main.yml.galaxy_info.description` so the dashboard config rendering does not produce invalid YAML. * Joomla. Adds `oidc:` enable + `ldap:` block in meta/services.yml, matrix-deploy variants for the two auth flows, and an extended Playwright env exposing OIDC issuer, biber persona, canonical domain, and the `<service>_SERVICE_ENABLED` gate flags. * LibreTranslate. Adds `container_port=5000`, drops the `dmbs_excl.yml.j2` depends_on partial that produced an empty `'': condition: service_healthy` map, and pins `LT_LOAD_ONLY=en,de` so the deploy gate's readiness probe does not race the upstream argos-translate model mirror. * Postmarks. Built from scratch as a full role (was a stub): tasks, Node-based Dockerfile that clones ckolderup/postmarks, compose template, env with SESSION_SECRET / ADMIN_KEY pulled from meta/schema.yml credentials, server.yml with subnet, Playwright env + spec. * Bluesky. Adds `files/playwright.spec.js` and `templates/playwright.env.j2` for the deploy gate. The Keycloak event-listener bridge to PDS `com.atproto.server.createAccount` remains to be built in follow-up work; until then the spec exercises baseline reachability and the OIDC/LDAP/RBAC gaps stay documented in `roles/web-app-bluesky/README.md` per lifecycle.md. Shared infrastructure: * `roles/test-e2e-playwright/tasks/run_one.yml`. The wait-for-app probe now accepts 4xx as proof of life; the prior `<400` boundary rejected legitimate 404 landing surfaces (bridgy-fed serves federation endpoints under specific paths, not `/`). 5xx remains the only signal of an unhealthy app. README.md SSO/RBAC exception sections per lifecycle.md (`docs/contributing/design/services/lifecycle.md`): * web-app-bluesky — RBAC exception * web-app-bridgy-fed — full SSO/RBAC exception * web-app-postmarks — RBAC exception * web-svc-libretranslate — LDAP + RBAC exception Out of scope for this commit (deferred to follow-up requirements): * Bluesky Keycloak event-listener SPI for PDS account auto- provisioning (needs Java SPI build pipeline + JAR mount + realm wiring). * LibreTranslate sidecar oauth2-proxy on the UI subpath only (must not gate the API endpoints). * Postmarks sidecar oauth2-proxy in front of the web UI. * Joomla community OIDC plugin install + group-mapping config. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |