diff --git a/roles/web-app-socialhome/README.md b/roles/web-app-socialhome/README.md index 58d610c012..3e6db48b6f 100644 --- a/roles/web-app-socialhome/README.md +++ b/roles/web-app-socialhome/README.md @@ -1,35 +1,45 @@ -# SocialHome +# Social Home ## Description -Deploy **SocialHome**, a federated social network focused on content hubs and federation. This role provides a Docker-based scaffold and domain wiring so you can bring SocialHome into your Infinito.Nexus stack. +Deploy **[Social Home](https://github.com/social-home-io/socialhome)**, a privacy-first federated social platform built for a single household. One container, an embedded SQLite database, and peer-to-peer federation between households over Ed25519-signed WebRTC data channels — no ActivityPub, no message broker, no object store. + +This is **not** [jaywink/socialhome](https://github.com/jaywink/socialhome), the Django content-hub project of the same name. Different codebase, different protocol, different maintainers. ## Overview -This role sets up a SocialHome application using Docker Compose with basic domain and port wiring. It follows your standard role layout and prepares the service to run behind your existing reverse proxy. The current version is a scaffold intended to be expanded with database/cache services and app-specific settings. +The role runs the upstream `ghcr.io/social-home-io/socialhome` image behind the central reverse proxy on its own canonical domain. Everything the application keeps lives in one volume at `/data`: the SQLite database, uploaded media, and installed apps. The administrator account is provisioned from the role's generated credential, and the role finishes the upstream first-boot wizard over the API so the front page renders a login form instead of a setup screen. + +A rendered `socialhome.toml` is mounted alongside the environment file. It carries exactly one setting — `[standalone].external_url` — because that value has no `SH_*` environment equivalent upstream and federation pairing returns `422 NOT_CONFIGURED` without it. ## Cosmos -The diagram places SocialHome in the Infinito.Nexus cosmos: the components it deploys (capabilities), the central services it consumes (dependencies), and its outward reach (federation and bridged external networks). +The diagram places Social Home in the Infinito.Nexus cosmos: the components it deploys (capabilities), the central services it consumes (dependencies), and its outward reach (federation and bridged external networks). ```mermaid flowchart LR subgraph deps [Dependencies] + dep_svc_bkp_volume_2_local["svc-bkp-volume-2-local 💻"] dep_web_app_dashboard["web-app-dashboard 🐳🐝"] dep_web_app_keycloak["web-app-keycloak 🐳🐝"] dep_web_app_prometheus["web-app-prometheus 🐳🐝"] + dep_web_svc_coturn["web-svc-coturn 🐳🐝"] dep_web_svc_logout["web-svc-logout 🐳🐝"] end - subgraph role [web-app-socialhome] + subgraph role [web-app-socialhome 🐳🐝] svc_socialhome["socialhome"] + svc_coturn["coturn"] svc_prometheus["prometheus"] svc_logout["logout"] svc_dashboard["dashboard"] svc_sso["sso ❌"] + svc_container_backup["container_backup"] end + dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup dep_web_app_dashboard -. "0..1" .-> svc_dashboard dep_web_app_keycloak -- "1:1" --> svc_sso dep_web_app_prometheus -. "0..1" .-> svc_prometheus + dep_web_svc_coturn -. "0..1" .-> svc_coturn dep_web_svc_logout -. "0..1" .-> svc_logout ``` @@ -37,17 +47,24 @@ Solid `1:1` edges are fixed relationships; dashed `0..1` edges are conditional ( ## Features -- **Dockerized Scaffold:** Baseline Docker Compose integration and role structure to get you started quickly. -- **Domain & Port Wiring:** Integrates cleanly with your central domain/ports configuration. -- **Ready for Federation:** Intended to support ActivityPub-based federation once the application is fully wired. -- **Extensible Configuration:** Room for adding database, cache, worker processes, and environment tuning. +- **Single Container:** No database server, no cache, no worker, no object store. SQLite in one volume. +- **Headless Provisioning:** The administrator is seeded from the role's credential and the first-boot wizard is completed over the API, so no manual click-through is required. +- **Federation Ready:** The rendered `socialhome.toml` publishes the instance's external URL, which is what QR pairing hands to peer households. +- **Own STUN/TURN:** When `web-svc-coturn` is part of the deployment, WebRTC uses it with time-limited HMAC credentials instead of a third-party STUN server. +- **No Outbound Surprises:** The upstream app catalog fetch to github.com is disabled. Without `web-svc-coturn` the app still falls back to its built-in Google STUN server, since suppressing the variable entirely would leave WebRTC with none at all. - **Desktop Integration Hooks:** This README ensures inclusion in the Web App Desktop overview. +## Limitations + +- The role has never been deployed. `lifecycle` is `alpha`, so CI deploys it from the next run on; that run is its first execution. See [TODO.md](TODO.md). +- No SSO. The application authenticates against its own user table and has no OIDC client, so the reverse proxy is not SSO-gated — federation endpoints must stay publicly reachable. +- The application ships no logout control at `2026.6.16`, so the Playwright administrator persona is declared blocked. + ## Quick Setup ### Development -Clone, set up the workstation, and deploy SocialHome onto the local stack: +Clone, set up the workstation, and deploy Social Home onto the local stack: ```bash git clone https://github.com/infinito-nexus/core.git @@ -58,33 +75,34 @@ make compose-deploy mode=reinstall apps=web-app-socialhome full_cycle=false ### Production -Install SocialHome directly onto the target machine — clone the repository, install the OS prerequisites and the repository toolchain, then deploy against localhost over a local connection (no SSH, no container): +Run the published image to provision the inventory and deploy Social Home to a managed server (the mounted volume persists the inventory): ```bash -git clone https://github.com/infinito-nexus/core.git -cd core -bash scripts/install/package.sh -make install -source scripts/meta/env/load.sh - APP=web-app-socialhome +HOST= TLS_MODE=self_signed SSH_PUBLIC_KEY="" -INVENTORY=inventories/production -infinito administration inventory provision "$INVENTORY" \ - --inventory-file "$INVENTORY/devices.yml" \ - --host localhost \ - --include "$APP" \ - --vars "{\"TLS_MODE\": \"$TLS_MODE\", \"users\": {\"administrator\": {\"authorized_keys\": [\"$SSH_PUBLIC_KEY\"]}}}" -infinito administration deploy dedicated "$INVENTORY/devices.yml" \ - --password-file "$INVENTORY/.password" \ - --diff -vv + +docker run --rm -it \ + -v "$PWD/inventories:/etc/infinito.nexus/inventories" \ + -e APP="$APP" -e HOST="$HOST" -e TLS_MODE="$TLS_MODE" -e SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" \ + ghcr.io/infinito-nexus/core/debian bash -c ' + INVENTORY=/etc/infinito.nexus/inventories/production + infinito administration inventory provision "$INVENTORY" \ + --inventory-file "$INVENTORY/devices.yml" \ + --host "$HOST" \ + --include "$APP" \ + --vars "{\"TLS_MODE\": \"$TLS_MODE\", \"users\": {\"administrator\": {\"authorized_keys\": [\"$SSH_PUBLIC_KEY\"]}}}" && + infinito administration deploy dedicated "$INVENTORY/devices.yml" \ + --password-file "$INVENTORY/.password" \ + --diff -vv' ``` ## Further Resources -- [SocialHome Project](https://socialhome.network/) -- [ActivityPub (W3C)](https://www.w3.org/TR/activitypub/) +- [Social Home source](https://github.com/social-home-io/socialhome) +- [Published container images](https://github.com/social-home-io/socialhome/pkgs/container/socialhome) +- [TURN REST API credential scheme](https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00) ## Credits diff --git a/roles/web-app-socialhome/TODO.md b/roles/web-app-socialhome/TODO.md index e48de2c8cf..a41237423f 100644 --- a/roles/web-app-socialhome/TODO.md +++ b/roles/web-app-socialhome/TODO.md @@ -1,3 +1,51 @@ # Todo -- Implement +## Never deployed + +`lifecycle: alpha` puts this role into the CI deploy matrix before it has ever run against a real +host, deliberately: the first CI round is its first execution. Everything below the first live +`make compose-deploy apps=web-app-socialhome` is design-by-source-reading. + +Open items that only a live run can settle: + +- `index.html` ships an inline pre-paint theme bootstrap, and the rendered header is + `script-src-elem 'self' https://cdn.…` with no hash, so it is blocked and the first paint + flashes the wrong theme. Pinning `csp.hashes.script-src-elem` fixes it but has to be re-pinned + on every image bump; the flash is the cheaper trade until the theme matters. +- `meta/csp.yml` whitelists `blob:` on `script-src` for the audio worklet the DM composer builds + with `URL.createObjectURL`; worklet modules load under `script-src`, not `worker-src`. The + bundle registers no service worker and constructs no `Worker`. Whether the Preact bundle also + needs `unsafe-inline` for style attributes is unknown until a browser reports violations. +- The published image is multi-arch, but only `linux/amd64` has been exercised. +- `Dockerfile.sh` exposes 8124 for aiolibdatachannel signalling beside 8099. Nothing listens on + it at rest, so only 8099 is published; whether it binds lazily during a call is untested. + +## Blocked personas + +`PERSONA_ADMINISTRATOR_BLOCKED=true` +: Social Home 2026.6.16 imports `logout` from `client/src/store/auth.ts` in exactly one place — + `client/src/main.tsx` wires it as the 401 handler via `setUnauthorizedHandler(logout)`. No + component renders a logout control, so `personas/utils/logout.js` has nothing to click and the + admin journey cannot complete. The alternative — injecting a `[data-injected-logout]` button via + `templates/javascript.js.j2` — was rejected: the model + (`roles/web-app-prometheus/templates/javascript.js.j2`) points the button at + `OIDC.CLIENT.LOGOUT_URL`, which only exists for OIDC-gated roles, and this role deliberately runs + without SSO. Path back: upstream ships a logout control (or an in-app logout endpoint we can + target), then drop the flag and keep the `adminInteraction` already wired in the spec. + +`PERSONA_BIBER_BLOCKED=true` +: The app authenticates against its own `platform_users` table. There is no OIDC client and no + auto-provisioning, so the biber account never exists inside Social Home. Path back: upstream + grows an OIDC/trusted-header auth path. + +## Deferred wiring + +- `SH_MAX_STORAGE_BYTES` is pinned to 4 GiB in `vars/main.yml` so the app's media cap stays inside + `services.socialhome.min_storage` (5 GB). Raising one without the other lets the volume outgrow + its declared budget. +- `SH_APPS_CATALOG_URL` is emptied so the container never fetches the app catalog from github.com. + Set it to a self-hosted catalog when one exists. +- `SH_CORS_ALLOWED_ORIGINS` is unset — the SPA is same-origin. Needed only if a third-party client + should talk to the API. +- The GFS / global-server side of upstream (`Dockerfile.gfs`) is not deployed. Only the household + instance (`Dockerfile.sh`) is. diff --git a/roles/web-app-socialhome/files/playwright/playwright.spec.js b/roles/web-app-socialhome/files/playwright/playwright.spec.js index 0eee6722e8..dfb9f5d8e2 100644 --- a/roles/web-app-socialhome/files/playwright/playwright.spec.js +++ b/roles/web-app-socialhome/files/playwright/playwright.spec.js @@ -12,21 +12,21 @@ test.beforeEach(async ({ page }) => { await page.context().clearCookies(); }); -test("Socialhome front page is served under canonical domain with TLS", async ({ page }) => { +test("Social Home front page is served under canonical domain with TLS", async ({ page }) => { const response = await page.goto(`${appBaseUrl}/`); - expect(response, "Expected Socialhome response").toBeTruthy(); - expect(response.status(), "Expected Socialhome front page status < 400").toBeLessThan(400); + expect(response, "Expected Social Home response").toBeTruthy(); + expect(response.status(), "Expected Social Home front page status < 400").toBeLessThan(400); expect( response.url().includes(canonicalDomain), - `Expected canonical domain "${canonicalDomain}" to back the Socialhome URL` + `Expected canonical domain "${canonicalDomain}" to back the Social Home URL` ).toBe(true); const headers = response.headers(); - expect(headers["strict-transport-security"], "Socialhome must emit HSTS").toBeTruthy(); + expect(headers["strict-transport-security"], "Social Home must emit HSTS").toBeTruthy(); }); -test("Socialhome returns HTML content under canonical domain", async ({ request }) => { +test("Social Home returns HTML content under canonical domain", async ({ request }) => { const response = await request.get(`${appBaseUrl}/`); - expect(response.status(), "Expected Socialhome front page status < 400").toBeLessThan(400); + expect(response.status(), "Expected Social Home front page status < 400").toBeLessThan(400); const contentType = response.headers()["content-type"] || ""; expect( contentType.includes("text/html"), @@ -34,6 +34,14 @@ test("Socialhome returns HTML content under canonical domain", async ({ request ).toBe(true); }); +test("Social Home reports subsystem health on /healthz", async ({ request }) => { + const response = await request.get(`${appBaseUrl}/healthz`); + expect(response.status(), "Expected /healthz to report healthy").toBe(200); + const body = await response.json(); + expect(body.status, "Expected /healthz status ok").toBe("ok"); + expect(body.subsystems.db, "Expected the SQLite subsystem to answer SELECT 1").toBe("ok"); +}); + // Persona scenarios. // Bodies live in the shared helper roles/test-e2e-playwright/files/personas // so every role's persona flow stays consistent. @@ -49,15 +57,14 @@ test("biber: app → universal logout", async ({ page }) => { test("administrator: app → universal logout", async ({ page }) => { await runAdminFlow(page, { adminInteraction: async (interactivePage) => { - // web-app-socialhome admin-only interaction: open a management surface. const link = interactivePage - .getByRole("link", { name: /^(stream|profile|admin|federation)$/i }) + .getByRole("link", { name: /^(feed|spaces|calendar|federation|admin)$/i }) .first(); if (await link.isVisible({ timeout: 10_000 }).catch(() => false)) { await link.click().catch(() => {}); await interactivePage.waitForLoadState("domcontentloaded", { timeout: 30_000 }).catch(() => {}); await expect(interactivePage.locator("body")).toContainText( - /stream|profile|federation|content|socialhome/i, + /feed|spaces|calendar|federation|admin/i, { timeout: 30_000 }, ); } diff --git a/roles/web-app-socialhome/meta/csp.yml b/roles/web-app-socialhome/meta/csp.yml new file mode 100644 index 0000000000..5d7ccc87ce --- /dev/null +++ b/roles/web-app-socialhome/meta/csp.yml @@ -0,0 +1,6 @@ +--- +whitelist: + media-src: + - "blob:" + script-src: + - "blob:" diff --git a/roles/web-app-socialhome/meta/domains.yml b/roles/web-app-socialhome/meta/domains.yml new file mode 100644 index 0000000000..06addc8d8f --- /dev/null +++ b/roles/web-app-socialhome/meta/domains.yml @@ -0,0 +1,4 @@ +--- +canonical: + - socialhome.{{ DOMAIN_PRIMARY }} +aliases: [] diff --git a/roles/web-app-socialhome/meta/info.yml b/roles/web-app-socialhome/meta/info.yml index b3abdf9471..826516172c 100644 --- a/roles/web-app-socialhome/meta/info.yml +++ b/roles/web-app-socialhome/meta/info.yml @@ -1,2 +1,4 @@ --- -homepage: https://socialhome.network/ +logo: + class: fa-solid fa-house-signal +homepage: https://github.com/social-home-io/socialhome diff --git a/roles/web-app-socialhome/meta/main.yml b/roles/web-app-socialhome/meta/main.yml index 26919e0549..81bc070e23 100644 --- a/roles/web-app-socialhome/meta/main.yml +++ b/roles/web-app-socialhome/meta/main.yml @@ -2,7 +2,7 @@ # nocheck: info-media no official demo video galaxy_info: author: Kevin Veen-Birkenbach - description: Federated social network for content hubs and ActivityPub-based federation across the fediverse + description: Privacy-first social platform for households, federating peer-to-peer over WebRTC on an embedded SQLite store license: Infinito.Nexus Community License (Non-Commercial) company: | Kevin Veen-Birkenbach @@ -11,6 +11,8 @@ galaxy_info: - socialhome - social - federated + - webrtc + - sqlite issue_tracker_url: https://s.infinito.nexus/issues min_ansible_version: "2.9" platforms: diff --git a/roles/web-app-socialhome/meta/networks.yml b/roles/web-app-socialhome/meta/networks.yml new file mode 100644 index 0000000000..eab226ccb1 --- /dev/null +++ b/roles/web-app-socialhome/meta/networks.yml @@ -0,0 +1,3 @@ +--- +local: + subnet: 192.168.74.0/24 diff --git a/roles/web-app-socialhome/meta/schema.yml b/roles/web-app-socialhome/meta/schema.yml new file mode 100644 index 0000000000..3eb032d1b0 --- /dev/null +++ b/roles/web-app-socialhome/meta/schema.yml @@ -0,0 +1,6 @@ +--- +credentials: + administrator_password: + description: Password of the Social Home standalone administrator account + algorithm: alphanumeric + validation: ^[A-Za-z0-9]{32,}$ diff --git a/roles/web-app-socialhome/meta/services.yml b/roles/web-app-socialhome/meta/services.yml index 4b46216329..d26767c2d4 100644 --- a/roles/web-app-socialhome/meta/services.yml +++ b/roles/web-app-socialhome/meta/services.yml @@ -1,14 +1,38 @@ --- socialhome: + healthcheck: + flavor: http_status + path: healthz modes: host: enabled: false - lifecycle: pre-alpha + compose: + enabled: true + # Exception: the orchestrator chains swarm -> compose -> host under + # mode_fail_fast, so a red swarm job here skips 92 unrelated jobs. Flip to + # true once compose has been green once. + swarm: + enabled: false + # nocheck: default-placement-manager embedded sqlite in a node-local volume; no clustered storage + placement: manager + image: ghcr.io/social-home-io/socialhome + version: "2026.6.16" + ports: + local: + http: 8089 + internal: + http: 8099 + lifecycle: alpha min_storage: "5GB" cpus: "0.5" mem_reservation: "256m" mem_limit: "1g" pids_limit: 512 +# nocheck: playwright-service-flag — TURN/STUN server, no Playwright surface (provider: web-svc-coturn) +coturn: + bond: 1 + enabled: "{{ 'web-svc-coturn' in group_names }}" + shared: "{{ 'web-svc-coturn' in group_names }}" prometheus: bond: 1 enabled: "{{ 'web-app-prometheus' in group_names }}" @@ -27,3 +51,8 @@ dashboard: sso: enabled: false shared: false +# nocheck: playwright-service-flag, dynamic-flag infrastructure backup consumer +container_backup: # nocheck: bond-baseline backup host joins outside the variant topology + bond: 1 + enabled: "{{ 'svc-bkp-volume-2-local' in group_names }}" + shared: true diff --git a/roles/web-app-socialhome/meta/users.yml b/roles/web-app-socialhome/meta/users.yml new file mode 100644 index 0000000000..6c31213ebc --- /dev/null +++ b/roles/web-app-socialhome/meta/users.yml @@ -0,0 +1,2 @@ +--- +administrator: {} diff --git a/roles/web-app-socialhome/meta/variants.yml b/roles/web-app-socialhome/meta/variants.yml index 3b89194f44..1da93a33b0 100644 --- a/roles/web-app-socialhome/meta/variants.yml +++ b/roles/web-app-socialhome/meta/variants.yml @@ -5,6 +5,11 @@ # variant pinning every dynamic services.yml flag to true so the # matrix exercises the in-group_names branch of web-app-socialhome. - services: + container_backup: + enabled: true + coturn: + enabled: true + shared: true dashboard: enabled: true shared: true @@ -15,6 +20,12 @@ enabled: true shared: true - services: + container_backup: + enabled: false + shared: false # nocheck: variants-static-override backup shared stays literally true + coturn: + enabled: false + shared: false dashboard: enabled: false shared: false diff --git a/roles/web-app-socialhome/meta/volumes.yml b/roles/web-app-socialhome/meta/volumes.yml new file mode 100644 index 0000000000..afd61060bc --- /dev/null +++ b/roles/web-app-socialhome/meta/volumes.yml @@ -0,0 +1,15 @@ +--- +data: + type: volume + name: socialhome_data + mounts: + - service: socialhome + target: /data + +config: + type: config + source: "{{ [lookup('container', 'web-app-socialhome', 'directories.volumes'), 'socialhome.toml'] | path_join }}" + mode: "0444" + mounts: + - service: socialhome + target: /etc/socialhome/socialhome.toml diff --git a/roles/web-app-socialhome/tasks/01_setup.yml b/roles/web-app-socialhome/tasks/01_setup.yml new file mode 100644 index 0000000000..c6bb7681be --- /dev/null +++ b/roles/web-app-socialhome/tasks/01_setup.yml @@ -0,0 +1,28 @@ +--- +# Exception: upstream provisions the admin from SH_ADMIN_PASSWORD in +# platform/standalone/adapter.py on_startup but never calls mark_complete(), so the SPA keeps +# rendering the setup wizard. Re-posting the same credentials is a no-op for provision_admin() +# and flips the setup flag; 409 means a previous run already did it. + +- name: "⏳ Wait for Social Home at {{ SOCIALHOME_INTERNAL_URL }}/healthz" + uri_retry: + url: "{{ SOCIALHOME_INTERNAL_URL }}/healthz" + method: GET + status_code: 200 + register: socialhome_health + retries: 30 + delay: 5 + until: socialhome_health.status == 200 + +- name: "🔐 Mark the standalone first-boot setup complete" + uri_retry: + url: "{{ SOCIALHOME_INTERNAL_URL }}/api/setup/standalone" + method: POST + body_format: json + body: + username: "{{ SOCIALHOME_ADMIN_USERNAME }}" + password: "{{ SOCIALHOME_ADMIN_PASSWORD }}" + status_code: [201, 409] + register: socialhome_setup + changed_when: socialhome_setup.status == 201 + no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" diff --git a/roles/web-app-socialhome/tasks/main.yml b/roles/web-app-socialhome/tasks/main.yml index b7a3289860..50cbd72ce1 100644 --- a/roles/web-app-socialhome/tasks/main.yml +++ b/roles/web-app-socialhome/tasks/main.yml @@ -1,3 +1,21 @@ --- -# Pre-alpha scaffold: see TODO.md. The role does not deploy SocialHome yet. -# A full implementation will follow the standard webapp pattern via sys-stk-full. +- name: "🧩 load docker, proxy for '{{ application_id }}'" + include_role: + name: sys-stk-full + vars: + compose_handlers_flush: false + +- name: "📄 Render socialhome.toml for '{{ application_id }}'" + stack_host_template: + src: socialhome.toml.j2 + dest: "{{ SOCIALHOME_CONFIG_HOST_FILE }}" + mode: "0444" + notify: compose up + +- name: "🐳 Finalize compose deploy for '{{ application_id }}'" + include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/finalize.yml') }}" + +- name: "🔐 Complete the standalone first-boot setup for '{{ application_id }}'" + include_tasks: 01_setup.yml + run_once: true + when: IS_STACK_HOST | bool diff --git a/roles/web-app-socialhome/templates/compose.yml.j2 b/roles/web-app-socialhome/templates/compose.yml.j2 new file mode 100644 index 0000000000..b0b58f6e2d --- /dev/null +++ b/roles/web-app-socialhome/templates/compose.yml.j2 @@ -0,0 +1,15 @@ +{% include 'roles/sys-svc-compose/templates/base.yml.j2' %} + + {% set service_name = entity_name %} + {{ service_name }}: + {{ lookup('container_image', application_id, service_name) }} + {{ lookup('compose_only', 'container_name', entity_name) }} + {% include 'roles/sys-svc-container/templates/base.yml.j2' %} + {{ lookup('container_ports', [service_name, 'http', DOCKER_BIND_HOST]) | indent(4) }} + {{ lookup('container_depends_on', application_id) | indent(4) }} + {{ lookup('container_networks') | indent(4) }} + {{ lookup('container_healthcheck', service_name) | indent(4) }} + {{ lookup('container_volumes', application_id, service_name) | indent(4) }} + +{{ lookup('compose_volumes', application_id) }} +{{ lookup('compose_networks') }} diff --git a/roles/web-app-socialhome/templates/env.j2 b/roles/web-app-socialhome/templates/env.j2 new file mode 100644 index 0000000000..de8b83613a --- /dev/null +++ b/roles/web-app-socialhome/templates/env.j2 @@ -0,0 +1,27 @@ +{% include 'roles/sys-svc-container/templates/env.j2' %} + +SH_CONFIG={{ SOCIALHOME_CONFIG_FILE | dotenv_quote }} + +SH_LISTEN_PORT={{ SOCIALHOME_LISTEN_PORT }} + +SH_INSTANCE_NAME={{ SOCIALHOME_INSTANCE_NAME | dotenv_quote }} + +SH_SECURE_COOKIES=true + +SH_MAX_STORAGE_BYTES={{ SOCIALHOME_MAX_STORAGE_BYTES }} + +SH_APPS_CATALOG_URL= + +{# Exception: upstream reads these by env PRESENCE, so an empty value beats the + in-app default and would leave WebRTC with no STUN server at all. #} +{% if SOCIALHOME_COTURN_ENABLED | bool %} +SH_WEBRTC_STUN_URL={{ SOCIALHOME_STUN_URL | dotenv_quote }} + +SH_WEBRTC_TURN_URL={{ SOCIALHOME_TURN_URL | dotenv_quote }} + +SH_WEBRTC_TURN_SECRET={{ SOCIALHOME_COTURN_SECRET | dotenv_quote }} +{% endif %} + +SH_ADMIN_USERNAME={{ SOCIALHOME_ADMIN_USERNAME | dotenv_quote }} + +SH_ADMIN_PASSWORD={{ SOCIALHOME_ADMIN_PASSWORD | dotenv_quote }} diff --git a/roles/web-app-socialhome/templates/playwright.env.j2 b/roles/web-app-socialhome/templates/playwright.env.j2 index 24eb5eb2fe..4fe6eca614 100644 --- a/roles/web-app-socialhome/templates/playwright.env.j2 +++ b/roles/web-app-socialhome/templates/playwright.env.j2 @@ -4,4 +4,10 @@ CANONICAL_DOMAIN={{ lookup('domain', application_id) | dotenv_quote }} LOGOUT_SERVICE_ENABLED={{ lookup('config', application_id, 'services.logout.enabled') | string | lower }} +# nocheck: persona-block-rationale — Social Home 2026.6.16 wires `logout` only as the 401 handler and renders no logout control, so personas/utils/logout.js has nothing to click and the admin journey cannot close (see TODO.md "Blocked personas"). +PERSONA_ADMINISTRATOR_BLOCKED=true + +# nocheck: persona-block-rationale — the app authenticates against its own platform_users table with no OIDC client and no auto-provisioning, so the biber account never exists inside Social Home (see TODO.md "Blocked personas"). +PERSONA_BIBER_BLOCKED=true + SSO_SERVICE_ENABLED={{ lookup('config', application_id, 'services.sso.enabled') | string | lower }} diff --git a/roles/web-app-socialhome/templates/socialhome.toml.j2 b/roles/web-app-socialhome/templates/socialhome.toml.j2 new file mode 100644 index 0000000000..39bdda3b89 --- /dev/null +++ b/roles/web-app-socialhome/templates/socialhome.toml.j2 @@ -0,0 +1,5 @@ +# Exception: external_url has no SH_* env equivalent (socialhome/config.py `_split_toml` keeps +# [standalone] in platform_options), and without it POST /api/pairing/initiate answers 422 +# NOT_CONFIGURED and federation pairing is dead. That is the only reason this file exists. +[standalone] +external_url = "{{ SOCIALHOME_EXTERNAL_URL }}" diff --git a/roles/web-app-socialhome/vars/main.yml b/roles/web-app-socialhome/vars/main.yml index 4274f34373..6d5161108a 100644 --- a/roles/web-app-socialhome/vars/main.yml +++ b/roles/web-app-socialhome/vars/main.yml @@ -1,2 +1,23 @@ --- -application_id: web-app-socialhome +application_id: "web-app-socialhome" +entity_name: "{{ application_id | get_entity_name }}" + +SOCIALHOME_EXTERNAL_URL: "{{ lookup('tls', application_id, 'url.base') }}" +SOCIALHOME_INTERNAL_URL: "http://{{ DOCKER_REACH_HOST }}:{{ lookup('config', application_id, 'services.' ~ entity_name ~ '.ports.local.http') }}" +SOCIALHOME_LISTEN_PORT: "{{ lookup('config', application_id, 'services.' ~ entity_name ~ '.ports.internal.http') }}" +SOCIALHOME_INSTANCE_NAME: "{{ defaults_service_provider.platform.titel }}" + +SOCIALHOME_ADMIN_USERNAME: "{{ lookup('users', 'administrator').username }}" +SOCIALHOME_ADMIN_PASSWORD: "{{ lookup('config', application_id, 'credentials.administrator_password') }}" + +SOCIALHOME_CONFIG_FILE: "/etc/socialhome/socialhome.toml" +SOCIALHOME_CONFIG_HOST_FILE: "{{ [lookup('container', application_id, 'directories.volumes'), 'socialhome.toml'] | path_join }}" + +SOCIALHOME_MAX_STORAGE_BYTES: 4294967296 + +SOCIALHOME_COTURN_ENABLED: "{{ lookup('config', application_id, 'services.coturn.enabled') | bool }}" +SOCIALHOME_COTURN_HOST: "{{ lookup('domain', 'web-svc-coturn') if SOCIALHOME_COTURN_ENABLED | bool else '' }}" +SOCIALHOME_COTURN_PORT: "{{ lookup('config', 'web-svc-coturn', 'services.coturn.ports.public.stun_turn') if SOCIALHOME_COTURN_ENABLED | bool else '' }}" +SOCIALHOME_COTURN_SECRET: "{{ lookup('config', 'web-svc-coturn', 'credentials.auth_secret') if SOCIALHOME_COTURN_ENABLED | bool else '' }}" +SOCIALHOME_STUN_URL: "{{ 'stun:' ~ SOCIALHOME_COTURN_HOST ~ ':' ~ SOCIALHOME_COTURN_PORT if SOCIALHOME_COTURN_ENABLED | bool else '' }}" +SOCIALHOME_TURN_URL: "{{ 'turn:' ~ SOCIALHOME_COTURN_HOST ~ ':' ~ SOCIALHOME_COTURN_PORT if SOCIALHOME_COTURN_ENABLED | bool else '' }}"