From ba863f8a931d19a81ea3f24735efa0f8ea64d69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Mon, 7 Sep 2026 17:17:10 +0200 Subject: [PATCH] admin-log: add conflict-free clementine/vs-only paths onto main --- 2026/2026-08-17--decidim.md | 43 ++++++ 2026/2026-09-05--hernani-ollama-user+power.md | 16 ++ 2026/2026-09-07--git-meta-origin-forgejo.md | 55 +++++++ scripts/caddy/apply-dossiers-ngi-basicauth.sh | 137 ++++++++++++++++++ scripts/decidim/apply-branding.sh | 22 +++ scripts/decidim/install-systemd.sh | 47 ++++++ scripts/decidim/seed-example.sh | 39 +++++ scripts/decidim/up.sh | 21 +++ 8 files changed, 380 insertions(+) create mode 100644 2026/2026-08-17--decidim.md create mode 100644 2026/2026-09-05--hernani-ollama-user+power.md create mode 100644 2026/2026-09-07--git-meta-origin-forgejo.md create mode 100755 scripts/caddy/apply-dossiers-ngi-basicauth.sh create mode 100755 scripts/decidim/apply-branding.sh create mode 100755 scripts/decidim/install-systemd.sh create mode 100755 scripts/decidim/seed-example.sh create mode 100755 scripts/decidim/up.sh diff --git a/2026/2026-08-17--decidim.md b/2026/2026-08-17--decidim.md new file mode 100644 index 0000000..f2481c1 --- /dev/null +++ b/2026/2026-08-17--decidim.md @@ -0,0 +1,43 @@ +# Decidim — decidim.hacktivism.ch (2026-08-17) + +Site: **https://decidim.hacktivism.ch/** +Port **9027** → Caddy. Stack: `koopa-decidim` + `koopa-decidim-sidekiq` + `koopa-decidim-db` + `koopa-decidim-redis`. Image `decidim/decidim:0.30.8`. + +Live: `~/koopa-decidim/` (compose mirror `configs/decidim/`). Secrets only on host (`.env`, `users.env`). + +Instance name **hacktivism**. Theme: exchange-dark (gold `#e8a838`, bg `#1a1410`) via `theme/hacktivism.css`. Analog: `configs/lemmy/`. + +Admin users are on the host (`~/koopa-decidim/users.env`), not in this repo. + +Caddy: `sudo /home/hernani/koopa-caddy/apply.sh` +Boot: `scripts/decidim/install-systemd.sh` (linger already yes on koopa). + +## Cookie / privacy + +Stock Decidim still emits `#dc-dialog-wrapper` (Accept all / essential / Settings). This instance has no third-party trackers; privacy is default. + +- CSS: `#dc-dialog-wrapper, .cookies__container { display: none !important; }` +- JS: `/hacktivism-consent.js` sets `decidim-consent={"essential":true}` if missing +- Live 2026-08-17: HTML 200, CSS/JS 200, hide rules in public `/hacktivism.css` + +## Reboot (checked 2026-08-17) + +| Item | Value | +|------|--------| +| linger `hernani` | yes | +| `container-koopa-decidim-db` | enabled + active | +| `container-koopa-decidim` | enabled + active | +| `container-koopa-lemmy-db` / `container-koopa-lemmy` | enabled + active | +| listen | `:9027` `:9026` | + +login-health: probes + containers + linger + these units enabled. Loopback HTTP on :9027 is **301** (app wants HTTPS; TLS is Caddy). Probe allows 301. After reboot: login-health only unless asked. + +## Public example + isoc + +- Process **example** (published, not private): `https://decidim.hacktivism.ch/processes/example` + — title **Öffentliche digitale Allmende** (public-by-default reading). +- Published meeting **Offene Runde: Lesen ohne Konto** + one public proposal + background page. +- Homepage block `highlighted_processes` published. +- Participant `isoc` on the host (`users.env`); password not in this repo. +- Avatar: gold ISOC globe (same crop as Bonfire `@isoc`); host file `~/koopa-decidim/assets/img/isoc-gold.png`, not in this repo. +- Seed: `scripts/decidim/seed-example.sh` / `configs/decidim/seed_example.rb`. diff --git a/2026/2026-09-05--hernani-ollama-user+power.md b/2026/2026-09-05--hernani-ollama-user+power.md new file mode 100644 index 0000000..b6a6e22 --- /dev/null +++ b/2026/2026-09-05--hernani-ollama-user+power.md @@ -0,0 +1,16 @@ +# koopa: hernani user Ollama + power sampling (2026-09-05) + +Freigabe: operator — install Ollama under hernani (no root package); loopback only; laptop tunnels. + +## Ollama + +- Binary: `~hernani/.local/bin/ollama` (v0.33.3 from GitHub `ollama-linux-amd64.tar.zst`) +- Unit: `~/.config/systemd/user/ollama.service` · `OLLAMA_HOST=127.0.0.1:11434` · enabled +- Access: SSH as **hernani** from clementine/magikoopa → LocalForward (helper `koopa-ollama-tunnel` → laptop `:11438`) +- Models: small + exotic pulls (see pull log `/tmp/ollama-koopa-pull-20260905.log`) — prefer ≤3B + +## Power / energy + +- Deployed `host-power.py` to `~hernani/.local/bin/host-power` +- Laptop: `remote-power koopa` / `koopa-power` → JSON watts; combo derives Wh +- RAPL `energy_uj` root-only → quality **estimated** until Freigabe for `enable-host-power-access.sh --apply` diff --git a/2026/2026-09-07--git-meta-origin-forgejo.md b/2026/2026-09-07--git-meta-origin-forgejo.md new file mode 100644 index 0000000..d795832 --- /dev/null +++ b/2026/2026-09-07--git-meta-origin-forgejo.md @@ -0,0 +1,55 @@ +# `$HOME/git/` meta-repo origin on git.hacktivism.ch (2026-09-07) + +## Remote + +| Item | Value | +|------|--------| +| Host | **Forgejo rootless** `https://git.hacktivism.ch/` | +| Repo | **hernani/git** (meta-repo: non-secret globals only) | +| Visibility | **private** (`DEFAULT_PRIVATE` template is public — create with `private:true`) | +| Default branch | `main` | +| SSH clone (FQDN form) | `git@git.hacktivism.ch:hernani/git.git` | +| SSH URL (API form) | `ssh://git@git.hacktivism.ch:9200/hernani/git.git` | +| Local tree | `$HOME/git/` (`PATH[git-root]`) | + +Unauth HTTPS browse of a private repo → **404** (expected). + +## Local setup (done 2026-09-07 ~14:21) + +```bash +cd "$HOME/git" +git remote add origin git@git.hacktivism.ch:hernani/git.git +git push -u origin main +``` + +SSH Host `git.hacktivism.ch`: `HostName 127.0.0.1`, `Port 9200`, `User git`, `ProxyJump hernani@koopa`, `IdentityFile ~/.ssh/id_ed25519`. + +## Create via One-Shot-Token (on koopa) + +Do **not** use xAI / host-expert-ssh mutate for this. SSH as `hernani` → local HTTP **9024**. + +```bash +# unique token name (default gitea-admin may already exist) +podman exec koopa-forgejo gitea admin user generate-access-token \ + --username hernani \ + --token-name "oneshot-git-meta-$(date +%s)" \ + --scopes write:repository,write:admin \ + --raw +# → TOKEN (never log / never commit) + +curl -sS -X POST \ + -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"name":"git","private":true,"auto_init":false,"default_branch":"main"}' \ + http://127.0.0.1:9024/api/v1/admin/users/hernani/repos +# expect HTTP 201, full_name=hernani/git +``` + +Token delete via API often **403** with those scopes — purge leftover `oneshot-*` in Forgejo UI if needed. + +## Related + +- This admin-log’s own origin: `2026-07-10--admin-log-origin-forgejo.md` (public) +- Forgejo install: `2026-07-10--forgejo-rootless.md` +- Host skill quirk: private repos need explicit `private:true` +- Meta Must: no secrets / no child worktrees in this Forgejo repo diff --git a/scripts/caddy/apply-dossiers-ngi-basicauth.sh b/scripts/caddy/apply-dossiers-ngi-basicauth.sh new file mode 100755 index 0000000..4587f6a --- /dev/null +++ b/scripts/caddy/apply-dossiers-ngi-basicauth.sh @@ -0,0 +1,137 @@ +#!/usr/bin/env bash +# Apply HTTP basic_auth (Caddy equivalent of "htaccess in front") +# to dossiers.ngi / dossiers.2.ngi / dossiers.3.ngi.hacktivism.ch +# +# Caddy file_server does NOT honor Apache .htaccess — this edits the Caddyfile. +# +# On koopa (password for sudo): +# sudo -n true 2>/dev/null || sudo -v +# sudo bash /home/hernani/koopa-admin-log/scripts/caddy/apply-dossiers-ngi-basicauth.sh +# +# Idempotent. Password file is NOT in git: +# /home/hernani/koopa-secrets/dossiers-ngi-basicauth.txt +set -euo pipefail + +if [[ "$(id -u)" -ne 0 ]]; then + echo "ERROR: run as root: sudo bash $0" >&2 + exit 1 +fi + +CADDY=/etc/caddy/Caddyfile +USER_NAME="${DOSSIERS_AUTH_USER:-hernani}" +SECRETS_DIR=/home/hernani/koopa-secrets +PASS_FILE="${SECRETS_DIR}/dossiers-ngi-basicauth.txt" +SITES=( + dossiers.ngi.hacktivism.ch + dossiers.2.ngi.hacktivism.ch + dossiers.3.ngi.hacktivism.ch +) + +if [[ ! -f "$CADDY" ]]; then + echo "ERROR: missing $CADDY" >&2 + exit 1 +fi +command -v caddy >/dev/null || { echo "ERROR: caddy not in PATH" >&2; exit 1; } + +mkdir -p "$SECRETS_DIR" +chown hernani:hernani "$SECRETS_DIR" +chmod 700 "$SECRETS_DIR" + +if [[ -f "$PASS_FILE" ]]; then + # shellcheck disable=SC1090 + # file format: USER=... PASS=... HASH=... + # PASS may contain spaces; read with grep + USER_NAME="$(awk -F= '/^USER=/{print substr($0,6); exit}' "$PASS_FILE")" + PASS="$(awk -F= '/^PASS=/{print substr($0,6); exit}' "$PASS_FILE")" + HASH="$(awk -F= '/^HASH=/{print substr($0,6); exit}' "$PASS_FILE")" + if [[ -z "$USER_NAME" || -z "$HASH" ]]; then + echo "ERROR: $PASS_FILE incomplete — move it aside and re-run" >&2 + exit 1 + fi + echo "OK: reusing credentials in $PASS_FILE" +else + PASS="$(openssl rand -base64 18 | tr -d '/+=' | head -c 20)" + HASH="$(caddy hash-password --plaintext "$PASS")" + umask 077 + cat >"$PASS_FILE" < str: + marker = f"{site} {{" + i = src.find(marker) + if i < 0: + raise SystemExit(f"ERROR: site block not found: {site}") + # already has basic_auth in this block? + j = src.find("\n}", i) + if j < 0: + raise SystemExit(f"ERROR: unclosed block: {site}") + block = src[i:j] + if "basic_auth" in block or "basicauth" in block: + print(f"OK: {site} already has basic_auth") + return src + needle = ' header Alt-Svc "clear"\n' + k = block.find(needle) + auth = ( + needle + + " basic_auth {\n" + + f" {user} {hash_caddy}\n" + + " }\n" + ) + if k >= 0: + new_block = block.replace(needle, auth, 1) + else: + # insert after opening brace + nl = block.find("\n") + new_block = block[: nl + 1] + auth + block[nl + 1 :] + print(f"OK: injected basic_auth in {site}") + return src[:i] + new_block + src[j:] + +for s in sites: + text = inject_block(text, s) + +if text == orig: + print("no Caddyfile change") +else: + path.write_text(text) + print(f"wrote {path}") +PY + +echo "== validate ==" +caddy validate --config "$CADDY" + +echo "== reload ==" +if systemctl is-active --quiet caddy; then + systemctl reload caddy +else + echo "WARN: caddy unit not active — start it yourself" >&2 +fi + +echo +echo "Done. Browser: https://dossiers.ngi.hacktivism.ch/ (and .2. / .3.)" +echo "User/pass: $PASS_FILE" +echo "Reload later: sudo caddy validate --config $CADDY && sudo systemctl reload caddy" diff --git a/scripts/decidim/apply-branding.sh b/scripts/decidim/apply-branding.sh new file mode 100755 index 0000000..6327fe3 --- /dev/null +++ b/scripts/decidim/apply-branding.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Apply hacktivism colors + header CSS on the live organization. +set -euo pipefail +cd "${HOME}/koopa-decidim" +set -a +# shellcheck disable=SC1091 +source .env +# shellcheck disable=SC1091 +source users.env +set +a +podman exec \ + -e DECIDIM_HOST="${DECIDIM_HOST:-decidim.hacktivism.ch}" \ + -e DECIDIM_SYSTEM_EMAIL \ + -e DECIDIM_SYSTEM_PASSWORD \ + -e DECIDIM_ADMIN_EMAIL \ + -e DECIDIM_ADMIN_PASSWORD \ + -e DECIDIM_ADMIN_NAME \ + -e DECIDIM_APPLICATION_NAME \ + -e DECIDIM_DEFAULT_LOCALE \ + -e DECIDIM_AVAILABLE_LOCALES \ + koopa-decidim bundle exec rails runner /code/lib/koopa_bootstrap.rb +echo "branding applied. public: https://decidim.hacktivism.ch/" diff --git a/scripts/decidim/install-systemd.sh b/scripts/decidim/install-systemd.sh new file mode 100755 index 0000000..01934d3 --- /dev/null +++ b/scripts/decidim/install-systemd.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Install Decidim user systemd units on koopa (boot via linger). +set -euo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +LIVE="${HOME}/koopa-decidim" +# admin-log tree (scripts/decidim → ../..) or live ~/koopa-decidim/bin +if [ -f "${HERE}/../../configs/decidim/container-koopa-decidim.service" ]; then + UNIT_SRC="${HERE}/../../configs/decidim" +elif [ -f "${LIVE}/container-koopa-decidim.service" ]; then + UNIT_SRC="${LIVE}" +else + echo "ERROR: decidim unit files not found" >&2 + exit 1 +fi +UNIT_DIR="${HOME}/.config/systemd/user" + +mkdir -p "${UNIT_DIR}" +cp "${UNIT_SRC}/container-koopa-decidim-db.service" "${UNIT_DIR}/" +cp "${UNIT_SRC}/container-koopa-decidim.service" "${UNIT_DIR}/" + +systemctl --user daemon-reload +systemctl --user enable \ + container-koopa-decidim-db.service \ + container-koopa-decidim.service + +if ! podman ps --format '{{.Names}}' | grep -qx koopa-decidim; then + (cd "${HOME}/koopa-decidim" && set -a && source .env && set +a && podman-compose up -d) +fi + +systemctl --user start \ + container-koopa-decidim-db.service \ + container-koopa-decidim.service + +systemctl --user --no-pager --lines=0 status \ + container-koopa-decidim-db.service \ + container-koopa-decidim.service || true + +if command -v loginctl >/dev/null 2>&1; then + if [ "$(loginctl show-user "$(id -un)" -p Linger --value 2>/dev/null || echo no)" != "yes" ]; then + echo "NOTE: enable linger so units start after reboot without login:" + echo " sudo loginctl enable-linger $(id -un)" + else + echo "linger already enabled for $(id -un)" + fi +fi + +echo "OK. Public: https://decidim.hacktivism.ch/" diff --git a/scripts/decidim/seed-example.sh b/scripts/decidim/seed-example.sh new file mode 100755 index 0000000..51f1dba --- /dev/null +++ b/scripts/decidim/seed-example.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Publish public example process/meeting + optional isoc user from users.env. +set -euo pipefail +cd "${HOME}/koopa-decidim" +set -a +# shellcheck disable=SC1091 +source .env +# shellcheck disable=SC1091 +source users.env +set +a + +src="" +if [ -f "${HOME}/src/koopa/koopa-admin-log/configs/decidim/seed_example.rb" ]; then + src="${HOME}/src/koopa/koopa-admin-log/configs/decidim/seed_example.rb" +elif [ -f ./seed_example.rb ]; then + src="./seed_example.rb" +else + echo "ERROR: seed_example.rb not found" >&2 + exit 1 +fi +podman cp "${src}" koopa-decidim:/code/lib/koopa_seed_example.rb + +avatar_host="${DECIDIM_ISOC_AVATAR:-${HOME}/koopa-decidim/assets/img/isoc-gold.png}" +avatar_ctr="" +if [ -f "${avatar_host}" ]; then + avatar_ctr="/tmp/isoc-gold.png" + podman cp "${avatar_host}" "koopa-decidim:${avatar_ctr}" +fi + +podman exec \ + -e DECIDIM_HOST="${DECIDIM_HOST:-decidim.hacktivism.ch}" \ + -e DECIDIM_ADMIN_EMAIL \ + -e DECIDIM_ISOC_EMAIL \ + -e DECIDIM_ISOC_PASSWORD \ + -e DECIDIM_ISOC_NAME \ + -e DECIDIM_ISOC_NICKNAME \ + -e DECIDIM_ISOC_AVATAR="${avatar_ctr}" \ + koopa-decidim bundle exec rails runner /code/lib/koopa_seed_example.rb +echo "seed done. public: https://decidim.hacktivism.ch/processes/example" diff --git a/scripts/decidim/up.sh b/scripts/decidim/up.sh new file mode 100755 index 0000000..21a6a9d --- /dev/null +++ b/scripts/decidim/up.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail +DIR="${HOME}/koopa-decidim" +cd "${DIR}" +set -a +# shellcheck disable=SC1091 +source .env +set +a +podman-compose up -d +echo "== wait for :9027 ==" +ok=0 +for i in $(seq 1 80); do + code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 5 "http://127.0.0.1:9027/" || true) + echo "try $i: $code" + case "$code" in + 2*|3*) ok=1; break ;; + esac + sleep 5 +done +[[ "$ok" -eq 1 ]] || { echo "ERROR: no answer on 9027"; podman-compose ps; exit 1; } +echo "local ok. public: https://decidim.hacktivism.ch/"