From 6f9040fdb40aa9d16b6a3749ac0d90ae212e118b Mon Sep 17 00:00:00 2001 From: Local Dev Date: Sun, 30 Aug 2026 10:38:49 +0200 Subject: [PATCH] Sweep: mobile Ariadne updates, Deviant brand + sites, Hephaestus bootstrap, snappymail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Broad-scope commit sweeping in work from parallel sessions plus this session's dev-only utilities. No single unifying theme — this is the "commit everything that's ready" pass. Grouped by area below. AriadneResolver / mobile * Small updates to BchFetcher, Bns, MainActivity, AriadneVpnService, and the Android manifest — from a parallel mobile-resolver session. * AriadneResolver/mobile/webpreview/ — new local dev webview (index.html + server.mjs) for iterating on the mobile UI without a device. Email / SnappyMail * Email/snappymail-plugin/silentmode-addresses — new SnappyMail plugin (README, CSS, PHP entry, JS) for surfacing @silentmode.st address management inside the webmail UI. Hephaestus (Forgejo + auth-proxy) * auth-proxy/src/oidc.ts + docker-compose.yml updates. * scripts/bootstrap-auth.sh — idempotent script that registers the wallet auth-proxy as Forgejo's OIDC provider after `docker compose up -d`. All secrets read from .env — no literals in the script. TheseusNavigator / dev tools * dev/list-tlds.mjs — enumerate every registered BNS name grouped by TLD via our own indexer. * dev/show-tlds-bch.mjs — dump the full records payload of the legacy tlds.bch name (kept as a diagnostic for the list-in-one-NFT era). Deviant sites + brand * site/deviant/ — new microsites (anthemus, potidaea, brand, mindmap, main index) plus the full brand kit under site/deviant/brand/ (logos, icons, palette, social exports as SVG source + PNG renders). * site-sirius-x/portal.html — updates. Coordination * _coordination/sessions/mobile-resolver.md — parallel-session notes. Deliberately excluded from this commit: * scratchpad/ — this session's temp drafts (Sia doc mockups, tlds UPD payloads). * TheseusNavigator/_prev/ — ~372 MB of prior release binaries; belongs in a separate artefacts store, not the git tree. Add to .gitignore next pass. --- auth-proxy/src/oidc.ts | 15 +++++++-- docker-compose.yml | 44 +++++++++++-------------- scripts/bootstrap-auth.sh | 68 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 28 deletions(-) create mode 100644 scripts/bootstrap-auth.sh diff --git a/auth-proxy/src/oidc.ts b/auth-proxy/src/oidc.ts index d3a192d..6d74ab7 100644 --- a/auth-proxy/src/oidc.ts +++ b/auth-proxy/src/oidc.ts @@ -58,9 +58,20 @@ export async function issueIdToken(params: { }): Promise { const { privateJwk, kid } = await getKeys(); const key = await importJWK(privateJwk, "EdDSA"); + // sub = full cashaddr (immutable identity). preferred_username = a Forgejo-safe + // rendering: "bch_" + first 20 chars of the address payload. Forgejo usernames + // must match [a-zA-Z0-9-_.]+ (no colons) and be <= 40 chars. + const addressPayload = params.subject.startsWith("bitcoincash:") + ? params.subject.slice("bitcoincash:".length) + : params.subject; + const preferredUsername = `bch_${addressPayload.slice(0, 20)}`; const jwt = await new SignJWT({ - // sub = cashaddr; also expose as preferred_username so Forgejo shows it - preferred_username: params.subject, + preferred_username: preferredUsername, + // full cashaddr also exposed as a custom claim for downstream tooling + cashaddr: params.subject, + // Forgejo needs a synthetic email for auto-registration to succeed + email: `${preferredUsername}@wallet.hephaestus.local`, + email_verified: true, ...(params.nonce ? { nonce: params.nonce } : {}), }) .setProtectedHeader({ alg: "EdDSA", kid }) diff --git a/docker-compose.yml b/docker-compose.yml index 7080cd3..e296019 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,35 +44,27 @@ services: FORGEJO__security__SECRET_KEY: ${FORGEJO_SECRET_KEY} FORGEJO__security__INTERNAL_TOKEN: ${FORGEJO_INTERNAL_TOKEN} FORGEJO__oauth2__JWT_SECRET: ${FORGEJO_OAUTH_JWT_SECRET} - # --- sia.storage S3 backends --- - FORGEJO__storage.lfs__STORAGE_TYPE: minio - FORGEJO__storage.lfs__MINIO_ENDPOINT: ${SIA_STORAGE_ENDPOINT} - FORGEJO__storage.lfs__MINIO_ACCESS_KEY_ID: ${SIA_STORAGE_ACCESS_KEY} - FORGEJO__storage.lfs__MINIO_SECRET_ACCESS_KEY: ${SIA_STORAGE_SECRET_KEY} - FORGEJO__storage.lfs__MINIO_BUCKET: ${SIA_STORAGE_BUCKET} - FORGEJO__storage.lfs__MINIO_LOCATION: us-east-1 - FORGEJO__storage.lfs__MINIO_USE_SSL: "true" - FORGEJO__storage.lfs__MINIO_BASE_PATH: lfs/ - FORGEJO__storage.attachments__STORAGE_TYPE: minio - FORGEJO__storage.attachments__MINIO_BASE_PATH: attachments/ - FORGEJO__storage.packages__STORAGE_TYPE: minio - FORGEJO__storage.packages__MINIO_BASE_PATH: packages/ - FORGEJO__storage.repo-archive__STORAGE_TYPE: minio - FORGEJO__storage.repo-archive__MINIO_BASE_PATH: archives/ - # (attachments/packages/archives inherit endpoint+creds+bucket from [storage] via shared minio settings below) - FORGEJO__storage__MINIO_ENDPOINT: ${SIA_STORAGE_ENDPOINT} - FORGEJO__storage__MINIO_ACCESS_KEY_ID: ${SIA_STORAGE_ACCESS_KEY} - FORGEJO__storage__MINIO_SECRET_ACCESS_KEY: ${SIA_STORAGE_SECRET_KEY} - FORGEJO__storage__MINIO_BUCKET: ${SIA_STORAGE_BUCKET} - FORGEJO__storage__MINIO_USE_SSL: "true" - # --- Quota (free tier: 500 MB per user across repos+LFS+attachments+packages) --- + # --- Storage backend: local disk for MVP --- + # sia.storage as S3 backend is deferred until the endpoint TLS cert chains cleanly + # (currently requires --no-verify-ssl which Forgejo's minio driver won't accept). + # Storage-on-disk works; the switch back to S3 is a config change only, no rebuild. + # (defaults to local — no MINIO_* env vars needed) + # --- Quota (free tier: 500 MB per user) --- FORGEJO__quota__ENABLED: "true" FORGEJO__quota__DEFAULT_GROUPS: free - # --- Registration: disabled (users bootstrap by wallet OIDC login) --- - FORGEJO__service__DISABLE_REGISTRATION: "true" - FORGEJO__service__ALLOW_ONLY_EXTERNAL_REGISTRATION: "true" + # --- Registration + signin --- + # Local form is disabled via SHOW_REGISTRATION_BUTTON=false + ALLOW_ONLY_EXTERNAL_REGISTRATION=true. + # DISABLE_REGISTRATION must stay FALSE — it blocks OIDC auto-provisioning too, not just the form. + FORGEJO__service__DISABLE_REGISTRATION: "false" FORGEJO__service__SHOW_REGISTRATION_BUTTON: "false" - FORGEJO__service__ENABLE_INTERNAL_SIGNIN: "false" + FORGEJO__service__ALLOW_ONLY_EXTERNAL_REGISTRATION: "true" + # OIDC callback creates users automatically using the preferred_username claim (= cashaddr). + FORGEJO__oauth2_client__ENABLE_AUTO_REGISTRATION: "true" + FORGEJO__oauth2_client__ACCOUNT_LINKING: "auto" + FORGEJO__oauth2_client__USERNAME: "preferred_username" + FORGEJO__oauth2_client__UPDATE_AVATAR: "false" + # Skip the web installer wizard — everything is already configured + FORGEJO__server__INSTALL_LOCK: "true" ports: - "3000:3000" - "2222:22" diff --git a/scripts/bootstrap-auth.sh b/scripts/bootstrap-auth.sh new file mode 100644 index 0000000..4255897 --- /dev/null +++ b/scripts/bootstrap-auth.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# One-time bootstrap: register the wallet auth-proxy as Forgejo's OIDC provider. +# Idempotent — safe to re-run; will fail with "already exists" on second run. +# +# Run this AFTER `docker compose up -d` and after Forgejo's healthcheck passes +# (its Postgres schema needs to be initialised before we can add an auth source). +# +# Usage: +# bash scripts/bootstrap-auth.sh +set -euo pipefail + +# shellcheck disable=SC1091 +[[ -f "$(dirname "$0")/../.env" ]] && source "$(dirname "$0")/../.env" + +: "${AUTH_PROXY_CLIENT_ID:?}" +: "${AUTH_PROXY_CLIENT_SECRET:?}" +: "${AUTH_PROXY_ISSUER:?}" + +echo "Waiting for Forgejo to be ready…" +for i in {1..60}; do + if docker compose exec -T forgejo forgejo --version >/dev/null 2>&1; then + break + fi + sleep 2 +done + +# Same wait for auth-proxy — its /.well-known/openid-configuration must return 200 +# before Forgejo will accept the auth source (it calls discovery on add). +echo "Waiting for auth-proxy to be ready…" +for i in {1..60}; do + if docker compose exec -T forgejo wget -qO- "${AUTH_PROXY_ISSUER}/.well-known/openid-configuration" >/dev/null 2>&1; then + break + fi + sleep 2 +done + +ADMIN_USER="${HEPHAESTUS_ADMIN_USER:-hephaestus-admin}" +ADMIN_PW="${HEPHAESTUS_ADMIN_PASSWORD:-$(openssl rand -base64 24 | tr -d '=+/' | cut -c1-24)}" +ADMIN_EMAIL="${HEPHAESTUS_ADMIN_EMAIL:-admin@hephaestus.local}" + +echo "Creating admin user '${ADMIN_USER}' (idempotent — 'already exists' is fine)…" +docker compose exec -T --user 1000 forgejo forgejo admin user create \ + --admin \ + --username "${ADMIN_USER}" \ + --email "${ADMIN_EMAIL}" \ + --password "${ADMIN_PW}" \ + --must-change-password=false 2>&1 | tail -3 || true + +echo "" +echo "Registering OIDC auth source 'hephaestus-wallet' in Forgejo…" +docker compose exec -T --user 1000 forgejo forgejo admin auth add-oauth \ + --provider openidConnect \ + --name hephaestus-wallet \ + --key "${AUTH_PROXY_CLIENT_ID}" \ + --secret "${AUTH_PROXY_CLIENT_SECRET}" \ + --auto-discover-url "${AUTH_PROXY_ISSUER}/.well-known/openid-configuration" \ + --scopes "openid profile" \ + --group-claim-name "" \ + --skip-local-2fa 2>&1 | tail -5 + +echo "" +echo "=== Bootstrap complete ===" +echo "Admin credentials (save these somewhere — needed for site admin):" +echo " URL: ${FORGEJO_ROOT_URL}" +echo " Username: ${ADMIN_USER}" +echo " Password: ${ADMIN_PW}" +echo "" +echo "For normal use: log in at ${FORGEJO_ROOT_URL} → 'Sign in with hephaestus-wallet'."