diff --git a/2026/2026-10-21--castopod-php-stay-off.md b/2026/2026-10-21--castopod-php-stay-off.md deleted file mode 100644 index 0455ac3..0000000 --- a/2026/2026-10-21--castopod-php-stay-off.md +++ /dev/null @@ -1,13 +0,0 @@ -# Castopod stays off - -Rechecked 2026-10-21 after the stop note. Web unit container-koopa-castopod.service is disabled, inactive, dead. Container koopa-castopod is exited, finished 2026-10-21 22:48:06 +0200. Nothing listens on :9020. MariaDB and Redis user units stay loaded and active. No second stop. Caddy vhost unchanged. - -Log review found no sign of a successful remote code execution. Overlay file /tmp/fw-cp.php (1448 bytes, mode 600, sha256 7bb279bffa12dbb81787183108a2641b78a6657b069cbef7967950d80e13b5bc, mtime 2026-09-07 03:25:59 +0200) is a local database script for cp_users, outside the docroot, not a web shell. /tmp/os-dry.out is an apt dry-run. Five theme files differ by one branding line from 2026-07-13. Media volume has no scripts. Access logs run 2026-10-17 through the stop and show no 2xx on a non-index script. Critical lines on 2026-09-07 match other days. - -Limits: access logs start 2026-10-17, so HTTP from 2026-09-07 is absent. A memory-only or already rotated request is not ruled out. Whether the local script ran is not in the logs. MariaDB was not opened. - -Stays down until an image ships PHP newer than 8.4.18. Hub tag :1 from 2026-02-24 is not that image. Parent 537, slug castopod-restart-after-php-fix. - -Restore only then: - - systemctl --user enable --now container-koopa-castopod.service diff --git a/2026/2026-10-21--castopod-php-stop.md b/2026/2026-10-21--castopod-php-stop.md deleted file mode 100644 index e951d06..0000000 --- a/2026/2026-10-21--castopod-php-stop.md +++ /dev/null @@ -1,23 +0,0 @@ -# Castopod PHP stopped - -Checked 2026-10-21. Host has no php-fpm and no php package. The only PHP process was Castopod FrankenPHP in container koopa-castopod. - -| Item | Value | -|------|--------| -| image | docker.io/castopod/castopod:1 | -| repo digest | sha256:4e4f0440520f45257bfeac7be4347defd20048b4efef8f53d73ec9ed3a4f7966 | -| amd64 digest | sha256:757f3c2a96b31b81753c23bc092dfcf05039d9df4d13cc12c428bb752fce882b | -| image created | 2026-02-24 | -| hub tags 1 / latest / 1.15.5 | same digest, pushed 2026-02-24 | -| PHP | 8.4.18 (12 Feb 2026) | -| FrankenPHP | v2.10.2 | -| listen | 0.0.0.0:9020 -> 8080 | -| public | https://castopod.hacktivism.ch/ | - -Stable Hub tags have not moved since 2026-02-24. develop last push 2026-06-05 and is not a stable security update. php.net lists 8.4.26 (24 Sep 2026) and 8.4.27 (22 Oct 2026) after 8.4.18, with a banner to update for critical remote code execution. Pulling :1 repeats the same image. FrankenPHP ships its own PHP binary. - -Stopped and disabled user unit container-koopa-castopod.service. ExecStop is `podman stop -t 30 koopa-castopod`. Container status after stop: Exited (0). :9020 not listening. MariaDB and Redis units left running. Caddy vhost unchanged. - -Bring the web container back only when a newer image exists: - - systemctl --user enable --now container-koopa-castopod.service diff --git a/2026/2026-10-24--todo704-tor-cachyos-cookieauth.md b/2026/2026-10-24--todo704-tor-cachyos-cookieauth.md deleted file mode 100644 index 46665b5..0000000 --- a/2026/2026-10-24--todo704-tor-cachyos-cookieauth.md +++ /dev/null @@ -1,41 +0,0 @@ -# TODO=704 Tor CachyOS CTR + CookieAuth (koopa) - -Relay was down (stale PIDFile / start-timeout on forking unit), keys intact. Recreated **same identity** on CachyOS container; CookieAuth for ControlPort; host tor disabled. - -## Stand after APPLY (2026-10-24 ~21:47–21:51 Europe/Zurich) - -| Item | Value | -|------|--------| -| CTR | `koopa-tor-relay` Up, image `localhost/koopa-tor-relay:latest` (=`:cachyos`) | -| Tor | 0.4.9.14 (CachyOS/Arch extra) | -| Unit | `container-koopa-tor-relay.service` Type=simple, active | -| Host tor | `tor.service` disabled + failed/inactive | -| FP | `02C7EECCDF7814FBA2DB1CBAED000D06E8A32EE8` (keys mtime 2024-11-14) | -| ORPort | 8080 | -| ControlPort | 127.0.0.1:9051 CookieAuthentication=1, CookieAuthFile=/var/lib/tor/control_auth_cookie | -| Bootstrap | PROGRESS=100 done; status/circuit-established=1 | -| Unauth ControlPort | 514 Authentication required | -| Cookie AUTH | 250 OK | - -## Steps - -1. Dry-run `/tmp/tor.sh` on koopa → STATUS=dry-run-ok; NEXT=`sudo APPLY=1 /tmp/tor.sh` (host tor was still enabled). -2. Human: `systemctl disable --now tor.service`, then `sudo APPLY=1 /tmp/tor.sh`. -3. APPLY: wrote CachyOS Containerfile, pulled `docker.io/cachyos/cachyos:latest`, build (cache hit), `podman create --replace --network host --user 1000:1000 --userns keep-id`, torrc bak `torrc.bak-cookie-20261024-214720`, CookieAuth on. -4. First APPLY hit dbus fail under sudo (`$DBUS_SESSION_BUS_ADDRESS` / `$XDG_RUNTIME_DIR` unset). Unit rewrite + enable finished with XDG/DBUS set; script path re-exec as SUDO_USER for later runs. -5. Verify from magikoopa SSH: unit active, CTR Up, FP match, circuit-established=1, unauth 514. - -## Live paths - -- `~/koopa-tor-relay/{torrc,data,log,build/Containerfile}` -- `~/.config/systemd/user/container-koopa-tor-relay.service` -- Driver: `/tmp/tor.sh` (TARGET=koopa); upgrade later: `~/upgrade-containers.sh --apply --rebuild-local --only koopa-tor-relay` - -## Akte mirrors - -- `configs/tor/torrc` -- `configs/tor/build/Containerfile` -- `configs/systemd/user/container-koopa-tor-relay.service` -- `host/tor/README.md` updated (was stale host `tor.service`) - -Sibling: firecuda brew tor CookieAuth same day (FP `52BB94DDC1292F950CF728708AC48523E018A718`, MyFamily both ways). Parent TODO=704. diff --git a/AGENTS.md b/AGENTS.md index ba7a565..8ffc768 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,12 @@ # AGENTS.md +## §0 Secrets (HIGH — pointer) + +**Common SoT:** `$HOME/git/llm/llm-configs/operator-rules/secrets-policy.md` (`PATH[admin-secrets]`). +Secret **values** never enter chat/ctx as **input or output** (prompts, pasted tool results, commit subjects, admin-log prose). Handle via **scripts** only (disk `*.env`; report `STATUS=` / `KEY_PRESENT=` / paths) or else a **path-only local-model instruction**. Session stub: `$HOME/.grok/AGENTS.md` §0. Do not fork a second full copy here. + +--- + Session Must / Must not: `$HOME/.grok/AGENTS.md` only — do not duplicate. **Sync (must-sync trees / wenn Git allein nicht reicht):** siehe [SYNC.md](SYNC.md). diff --git a/configs/systemd/user/container-koopa-tor-relay.service b/configs/systemd/user/container-koopa-tor-relay.service deleted file mode 100644 index b65e1ce..0000000 --- a/configs/systemd/user/container-koopa-tor-relay.service +++ /dev/null @@ -1,21 +0,0 @@ -# container-koopa-tor-relay.service — regenerated by tor.sh 2026-10-24T21:47:24+02:00 -# Prefer simple over podman-generate-systemd forking+PIDFile (stale CID storm). - -[Unit] -Description=Podman koopa-tor-relay -Wants=network-online.target -After=network-online.target -RequiresMountsFor=%t/containers - -[Service] -Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always -RestartSec=5 -TimeoutStartSec=120 -TimeoutStopSec=70 -ExecStart=/usr/bin/podman start --attach koopa-tor-relay -ExecStop=/usr/bin/podman stop -t 10 koopa-tor-relay -Type=simple - -[Install] -WantedBy=default.target diff --git a/configs/tor/build/Containerfile b/configs/tor/build/Containerfile deleted file mode 100644 index c57a137..0000000 --- a/configs/tor/build/Containerfile +++ /dev/null @@ -1,24 +0,0 @@ -# koopa-tor-relay — Tor non-exit relay on CachyOS (Arch extra/tor) -# Same pattern as koopa-meet-web:cachyos. Rootless: tor user -> uid/gid 1000. -# Recreate: --user 1000:1000 --userns=keep-id (see README / tor.sh) -ARG CACHYOS_IMAGE=docker.io/cachyos/cachyos:latest -FROM ${CACHYOS_IMAGE} -ENV TZ=Europe/Zurich -RUN pacman -Syu --noconfirm --needed tor nyx ca-certificates \ - && pacman -Scc --noconfirm \ - && rm -rf /var/cache/pacman/pkg/* /tmp/* \ - && mkdir -p /var/lib/tor /var/log/tor \ - && if id tor >/dev/null 2>&1; then \ - groupmod -g 1000 tor 2>/dev/null || true; \ - usermod -u 1000 -g 1000 tor 2>/dev/null || true; \ - elif id debian-tor >/dev/null 2>&1; then \ - groupmod -g 1000 debian-tor; usermod -u 1000 -g 1000 debian-tor; \ - else \ - groupadd -g 1000 tor; useradd -u 1000 -g 1000 -d /var/lib/tor -s /usr/bin/nologin tor; \ - fi \ - && chown -R 1000:1000 /var/lib/tor /var/log/tor \ - && chmod 700 /var/lib/tor -EXPOSE 8080 9051 -USER 1000:1000 -ENTRYPOINT ["/usr/bin/tor"] -CMD ["-f", "/etc/tor/torrc", "--runasdaemon", "0"] diff --git a/configs/tor/torrc b/configs/tor/torrc deleted file mode 100644 index 9f1705f..0000000 --- a/configs/tor/torrc +++ /dev/null @@ -1,18 +0,0 @@ -# KoopaRelay — same policy as live /etc/tor/torrc + admin-log configs/tor/torrc -# Identity: keys in DataDirectory (migrate from host /var/lib/tor) -Log notice file /var/log/tor/notices.log -Nickname KoopaRelay -ContactInfo info+koopa@hacktivism.ch -# dual-stack: bind all addresses on 8080 (IPv4 + IPv6), same as host tor -ORPort 8080 -ControlPort 127.0.0.1:9051 -CookieAuthentication 1 -CookieAuthFile /var/lib/tor/control_auth_cookie -ExitRelay 0 -SocksPort 0 -MyFamily 52BB94DDC1292F950CF728708AC48523E018A718 -BandwidthRate 2000 MBytes -BandwidthBurst 2000 MBytes -RelayBandwidthRate 2000 MBytes -RelayBandwidthBurst 2000 MBytes -DataDirectory /var/lib/tor diff --git a/host/tor/README.md b/host/tor/README.md index fa68a9c..138d11d 100644 --- a/host/tor/README.md +++ b/host/tor/README.md @@ -1,30 +1,13 @@ # Tor on koopa -**Runtime:** rootless podman container **`koopa-tor-relay`** (CachyOS base, Arch `extra/tor`), managed by user unit **`container-koopa-tor-relay.service`** (`Type=simple`, `podman start --attach`). -**Host `tor.service`:** disabled / must stay off (port clash on ORPort 8080 + ControlPort 9051). -**Ports:** ORPort **8080** · ControlPort **127.0.0.1:9051** (CookieAuthentication + CookieAuthFile). -**Non-exit** relay (`ExitRelay 0`). Nickname **KoopaRelay**. -**Fingerprint (stable keys under `~/koopa-tor-relay/data/keys/`):** `02C7EECCDF7814FBA2DB1CBAED000D06E8A32EE8` -**Family:** firecuda FireCudaRelay `52BB94DDC1292F950CF728708AC48523E018A718` - -Live tree: `~/koopa-tor-relay/` (`torrc`, `data/`, `log/`, `build/Containerfile`). -Mirrors in this repo: `configs/tor/torrc`, `configs/tor/build/Containerfile`, `configs/systemd/user/container-koopa-tor-relay.service`. +**Runtime:** host **`tor.service`** (not a podman container). +**Ports:** ORPort **8080** · ControlPort **9051** (loopback). +**Non-exit** relay (`ExitRelay 0`). ```bash -# status -systemctl --user status container-koopa-tor-relay.service -podman ps --filter name=koopa-tor-relay -# ControlPort cookie auth (hex) -COOKIE=$(xxd -p -c 256 ~/koopa-tor-relay/data/control_auth_cookie) -printf 'AUTHENTICATE %s\r\nGETINFO fingerprint\r\nGETINFO status/circuit-established\r\nQUIT\r\n' "$COOKIE" | nc -w 3 127.0.0.1 9051 +systemctl status tor +ss -lntp | grep -E '8080|9051' ``` -Apply / recreate (after host tor off): - -```bash -sudo APPLY=1 /tmp/tor.sh -# later upgrades: -~/upgrade-containers.sh --apply --rebuild-local --only koopa-tor-relay -``` - -login-health: expect user unit active + CTR Up; host `tor.service` inactive. Parent TODO=704. +login-health: WARN if `tor.service` not active. +Legacy `koopa-tor-relay` / `configs/tor/` removed from live inventory (git history keeps them). diff --git a/scripts/caddy/README.md b/scripts/caddy/README.md index ae4921d..4d1fc8c 100644 --- a/scripts/caddy/README.md +++ b/scripts/caddy/README.md @@ -41,10 +41,6 @@ Host runtime: `/etc/caddy/secrets/dossiers-ngi-basicauth.txt` (root-only). SoT: `koopa-admin-secrets-enc` (git-crypt) → `koopa/home-hernani/caddy/`. Hosts: `dossiers.ngi` / `dossiers.2.ngi` / `dossiers.3.ngi`.hacktivism.ch -HTTP→HTTPS 308 for `dossiers.ngi` (add host to the shared ACME list, leave -HTTPS body alone): `scripts/dossiers-ngi/apply-dossiers-ngi-http-308.sh` -then `sudo ~/bin/caddy-apply`. - ## silverbullet.hacktivism.ch — merge + basic_auth After podman cutover (`scripts/notes/cutover-to-silverbullet.sh --apply`) and DNS: diff --git a/scripts/dossiers-ngi/README.md b/scripts/dossiers-ngi/README.md index 6b9b65c..a288176 100644 --- a/scripts/dossiers-ngi/README.md +++ b/scripts/dossiers-ngi/README.md @@ -63,7 +63,7 @@ Per branch in JSON / sticky bar: | `vs_parent_plus` / `vs_parent_minus` | `git diff --numstat` tip vs **parent** tip (sticky bar +grün/−rot; omitted when `parent` is empty or not in the tree, e.g. `main` and `feature/nl-net-injestion-injestor-update`) | | `loc` | text-file line count of the tip vs the empty tree (gray `N LOC` in the plus/minus columns; only when there is no parent) | -Rebuild runs **only when** the current origin tip is not already a successful freeze (`freeze_ok`; timer never forces). Cookie alias `editions/flask` copies from `DEFAULT_BRANCH` unless that alias is already `freeze_ok` for the same tip **and** `.edition-built-at` matches the source freeze. +Rebuild runs **only when** the current origin tip is not already a successful freeze (`freeze_ok`; timer never forces). Each run also: `git fetch --prune`, `worktree prune`, drop stale `editions//` (gone remotes), purge leftover `tdpd-*` tmp dirs. @@ -136,30 +136,5 @@ sudo ~/bin/caddy-apply Stanza SoT: `scripts/dossiers-ngi/caddy-dossiers-ngi.snippet`. -## HTTP → HTTPS 308 - -Shared Caddy HTTP list already `redir https://{host}{uri} permanent` (308). -`dossiers.ngi.hacktivism.ch` was missing there → HTTP `200` empty body / -Firefox `NS_ERROR_NET_EMPTY_RESPONSE`. HTTPS site body stays untouched. - -On koopa (hernani, then root apply): - -```bash -# SoT: scripts/dossiers-ngi/apply-dossiers-ngi-http-308.sh -cp /path/to/apply-dossiers-ngi-http-308.sh ~/bin/ -chmod +x ~/bin/apply-dossiers-ngi-http-308.sh -bash ~/bin/apply-dossiers-ngi-http-308.sh -sudo ~/bin/caddy-apply -``` - -Smoke after apply: - -```bash -curl -sSI http://dossiers.ngi.hacktivism.ch/ | grep -Ei 'HTTP|location' -# 308 Location: https://dossiers.ngi.hacktivism.ch/ -``` - -`.2` / `.3` stay off this list unless `SITE=` is set. - Caddy edition map must list each `editions/` explicitly. A map output of `{http.request.cookie.tdpd_edition}` did not expand on Caddy v2.11.4 (only `flask` worked → 404 for `main`/views). After new tips appear, refresh the snippet map (or regenerate from `ls editions`) before `apply-dossiers-ngi-edition-switch` + `caddy-apply`. diff --git a/scripts/dossiers-ngi/branch-status-hourly.py b/scripts/dossiers-ngi/branch-status-hourly.py index 8cf6433..7b32e78 100755 --- a/scripts/dossiers-ngi/branch-status-hourly.py +++ b/scripts/dossiers-ngi/branch-status-hourly.py @@ -534,26 +534,15 @@ def rebuild_edition(repo: Path, branch: str, tip: str, editions: Path) -> dict: shutil.rmtree(wt, ignore_errors=True) -def _edition_built_at(editions: Path, slug: str) -> str: - p = editions / slug / ".edition-built-at" - if not p.is_file(): - return "" - return p.read_text(encoding="utf-8").strip() - - def sync_flask_alias(editions: Path, tips: dict[str, str]) -> None: """Keep editions/flask in sync with DEFAULT_BRANCH freeze when present.""" src_branch = default_branch() if src_branch not in tips: return - src_slug = edition_slug(src_branch) - src = editions / src_slug + src = editions / edition_slug(src_branch) dst = editions / "flask" tip = tips[src_branch] - flask_ok, _ = freeze_ok_for(editions, "flask", tip) - src_built = _edition_built_at(editions, src_slug) - flask_built = _edition_built_at(editions, "flask") - if flask_ok and src_built and src_built == flask_built: + if tip_recorded(editions, "flask") == tip and (dst / "index.html").is_file(): return if not (src / "index.html").is_file(): ensure_placeholder(editions, "flask", tip) diff --git a/scripts/dossiers-ngi/caddy-dossiers-ngi.snippet b/scripts/dossiers-ngi/caddy-dossiers-ngi.snippet index 7e02a41..c746406 100644 --- a/scripts/dossiers-ngi/caddy-dossiers-ngi.snippet +++ b/scripts/dossiers-ngi/caddy-dossiers-ngi.snippet @@ -19,10 +19,7 @@ handle @use { header Set-Cookie "tdpd_edition={re.use.1}; Path=/; Max-Age=31536000; SameSite=Lax" header Cache-Control "private, no-store" - # Keep the caller's path (?next=). Never bounce to /. No next → bar page. - @next query_regexp next ^(/|/[^/].*)$ - redir @next {query.next} 302 - redir * /_cascade/ 302 + redir * / 302 } handle /_cascade/* { diff --git a/scripts/dossiers-ngi/smoke-branch-tree.sh b/scripts/dossiers-ngi/smoke-branch-tree.sh index e180854..7703693 100755 --- a/scripts/dossiers-ngi/smoke-branch-tree.sh +++ b/scripts/dossiers-ngi/smoke-branch-tree.sh @@ -100,24 +100,6 @@ PY printf '1\n' > "$EDITIONS/feat-a/.edition-freeze-ok" rm -f "$EDITIONS/feat-a/.edition-freeze-error" -# flask alias: matching tip + stale HTML must copy from DEFAULT_BRANCH -MAIN_TIP="$(tr -d '\n' < "$EDITIONS/main/.edition-tip")" -mkdir -p "$EDITIONS/flask" -printf '%s\n' "$MAIN_TIP" > "$EDITIONS/flask/.edition-tip" -printf '%s\n' 'stale-flask' > "$EDITIONS/flask/index.html" -printf '%s\n' '2026-09-30T17:35:26+02:00' > "$EDITIONS/flask/.edition-built-at" -FETCH=0 TDPD_ROOT="$CLONE" OUT="$OUT" EDITIONS_ROOT="$EDITIONS" \ - FREEZE_CMD="$FREEZE_CMD" REBUILD=1 DEFAULT_BRANCH=main \ - python3 "$SCRIPT" --rebuild --no-fetch -grep -q "main" "$EDITIONS/flask/index.html" -if grep -q "stale-flask" "$EDITIONS/flask/index.html"; then - echo "FAIL flask alias kept stale HTML" - exit 1 -fi -test "$(tr -d '\n' < "$EDITIONS/flask/.edition-freeze-ok")" = "1" -test "$(tr -d '\n' < "$EDITIONS/flask/.edition-built-at")" = "$(tr -d '\n' < "$EDITIONS/main/.edition-built-at")" -echo "OK flask alias copied after stale skip" - # delete smoke branch on origin; fetch --prune drops remote-tracking ref cd "$CLONE" git push origin --delete smoke/temp-sibling >/dev/null 2>&1 diff --git a/scripts/pepsi/add-mail-v6-static.sh b/scripts/pepsi/add-mail-v6-static.sh new file mode 100755 index 0000000..26b4014 --- /dev/null +++ b/scripts/pepsi/add-mail-v6-static.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# Add dedicated mail IPv6 on koopa eno1 (not Tor SLAAC). +# Addr: 2a02:168:53a8::25/64 (hernani-dns KOOPA_WAN_AAAA) +# Keeps ipv6.method=auto so SLAAC (Tor) stays. Adds one static address. +# Freigabe: human this session. Run on koopa as root / sudo. +# Live: ip -6 addr add; persist: nmcli connection modify +ipv6.addresses +set -euo pipefail + +IFACE="${IFACE:-eno1}" +ADDR="${MAIL_V6_ADDR:-2a02:168:53a8::25}" +PREFIX="${MAIL_V6_PREFIX:-64}" +CIDR="${ADDR}/${PREFIX}" +CON="${NM_CON:-}" + +if [[ ${EUID:-$(id -u)} -ne 0 ]]; then + echo "STATUS=need_root HINT='sudo $0'" + exit 2 +fi + +echo "STATUS=start HOST=$(hostname -s) IFACE=$IFACE CIDR=$CIDR" + +if ! ip link show "$IFACE" >/dev/null 2>&1; then + echo "STATUS=no_iface IFACE=$IFACE" + exit 3 +fi + +# Resolve NM connection for IFACE if not set +if [[ -z "$CON" ]]; then + CON=$(nmcli -g NAME,DEVICE con show --active 2>/dev/null \ + | awk -F: -v d="$IFACE" '$2==d {print $1; exit}') + if [[ -z "$CON" ]]; then + CON=$(nmcli -g NAME,DEVICE con show 2>/dev/null \ + | awk -F: -v d="$IFACE" '$2==d {print $1; exit}') + fi +fi +if [[ -z "$CON" ]]; then + CON="$IFACE" +fi +echo "NM_CON=$CON" + +# Idempotent live address +if ip -6 addr show dev "$IFACE" | grep -qE "inet6[[:space:]]+${ADDR}/"; then + echo "LIVE=present ADDR=$ADDR" + LIVE_ADDED=0 +else + ip -6 addr add "$CIDR" dev "$IFACE" + echo "LIVE=added ADDR=$CIDR" + LIVE_ADDED=1 +fi + +# Persist via NetworkManager (append; do not replace method=auto) +EXISTING=$(nmcli -g ipv6.addresses con show "$CON" 2>/dev/null || true) +if echo "$EXISTING" | grep -qF "$ADDR"; then + echo "NM=present ADDR=$ADDR" + NM_ADDED=0 +else + nmcli con mod "$CON" +ipv6.addresses "$CIDR" + echo "NM=added ADDR=$CIDR" + NM_ADDED=1 + # Apply without full down/up if possible + if nmcli con up "$CON" >/dev/null 2>&1; then + echo "NM=reapplied CON=$CON" + else + echo "NM=reapply_warn HINT='nmcli con up $CON' (addr already live)" + fi +fi + +# Verify +if ip -6 addr show dev "$IFACE" | grep -qE "inet6[[:space:]]+${ADDR}/"; then + echo "VERIFY=ok ADDR=$ADDR" + echo "STATUS=done LIVE_ADDED=$LIVE_ADDED NM_ADDED=$NM_ADDED CIDR=$CIDR" + # Show Tor SLAAC still present (informational) + if ip -6 addr show dev "$IFACE" | grep -q '2a02:168:53a8:0:d584:9e34:13cf:15d6'; then + echo "TOR_SLAAC=present" + else + echo "TOR_SLAAC=absent_or_changed HINT=ok_if_privacy_temp_rotated" + fi + exit 0 +fi + +echo "STATUS=verify_fail ADDR=$ADDR" +ip -6 addr show dev "$IFACE" || true +exit 1 diff --git a/scripts/pepsi/todo631-pepsi-kreis.sh b/scripts/pepsi/todo631-pepsi-kreis.sh new file mode 100755 index 0000000..cb5a2ba --- /dev/null +++ b/scripts/pepsi/todo631-pepsi-kreis.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +# TODO=631: g0v mon-* Kreis-Test (local circle via pepsi TRUST_LOOPBACK / :25). +# Inject via koopa container loopback :25. Empfang = Maildir MARK grep. +# Real OS users (ensure-mon-users.sh): mon-smtp maildir dkim bounce local. +# No secrets. Freigabe-free (local inject only). +set -euo pipefail + +KOOPA_HOST="${KOOPA_HOST:-koopa}" +CONTAINER="${PEPSI_CONTAINER:-koopa-pepsi}" +OUTDIR="${HOME}/git/llm/llm-work-input" +MARK="${MARK:-$(date +%Y%m%dT%H%M%S%z)-kreis}" +USERS=(mon-smtp mon-maildir mon-dkim mon-bounce mon-local) +DRAIN_SEC="${DRAIN_SEC:-6}" + +echo "STATUS=start MARK=${MARK} CONTAINER=${CONTAINER} N=${#USERS[@]}" + +# --- preflight: users exist --- +for u in "${USERS[@]}"; do + ssh -o BatchMode=yes "${KOOPA_HOST}" \ + "podman exec ${CONTAINER} getent passwd ${u}" >/dev/null \ + || { echo "STATUS=fail PREFLIGHT user=${u} missing"; exit 2; } +done +echo "STATUS=preflight USERS_OK=${#USERS[@]}" + +# --- send circle: each → next --- +SEND_PY=$(mktemp) +trap 'rm -f "${SEND_PY}"' EXIT +{ + echo 'import smtplib, time, sys' + echo 'from email.message import EmailMessage' + echo "MARK = ${MARK@Q}" + echo -n 'USERS = [' + for u in "${USERS[@]}"; do printf '%s,' "${u@Q}"; done + echo ']' + cat <<'PY' +ok = fail = 0 +n = len(USERS) +for i, u in enumerate(USERS): + to_u = USERS[(i + 1) % n] + frm = f"{u}@g0v.ch" + to = f"{to_u}@g0v.ch" + msg = EmailMessage() + msg["From"] = frm + msg["To"] = to + msg["Subject"] = f"[pepsi-kreis] TODO631 {i+1}/{n} {MARK}" + msg["Message-ID"] = f""${SEND_PY}" + +ssh -o BatchMode=yes "${KOOPA_HOST}" "podman exec -i ${CONTAINER} python3 -" <"${SEND_PY}" +echo "MARK=${MARK}" >"${OUTDIR}/todo631-kreis.stamp" + +sleep "${DRAIN_SEC}" + +# --- Maildir HIT per recipient (next-hop of each sender) --- +OUT="${OUTDIR}/todo631-kreis-test.txt" +{ + echo "STATUS=ok MARK=kreis-test TODO=631 TS=$(date -Iseconds)" + echo "MARK_PROBE=${MARK} USERS=$(IFS=,; echo "${USERS[*]}")" + echo "SEND=via container :25 loopback" + echo "EXPECT=each user MARK_HITS>=1 (circle next-hop)" + echo "NOTE_vacation=PONG may also land; SPF-fail path skips vacation" +} >"${OUT}" + +HIT_TOTAL=0 +MISS=0 +for u in "${USERS[@]}"; do + hits=$(ssh -o BatchMode=yes "${KOOPA_HOST}" \ + "podman exec ${CONTAINER} bash -c 'grep -lF ${MARK@Q} /home/${u}/Maildir/new/* /home/${u}/Maildir/cur/* 2>/dev/null | wc -l'" \ + | tr -d '[:space:]') + hits=${hits:-0} + echo "HIT_${u}=${hits}" | tee -a "${OUT}" + if [[ "${hits}" -ge 1 ]]; then + HIT_TOTAL=$((HIT_TOTAL + 1)) + else + MISS=$((MISS + 1)) + fi +done + +if [[ "${MISS}" -eq 0 ]]; then + echo "STATUS=PASS HIT_USERS=${HIT_TOTAL}/5 MARK=${MARK}" | tee -a "${OUT}" + exit 0 +fi +echo "STATUS=PARTIAL HIT_USERS=${HIT_TOTAL}/5 MISS=${MISS} MARK=${MARK}" | tee -a "${OUT}" +exit 1 diff --git a/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.css b/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.css index feefe10..d0a638e 100644 --- a/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.css +++ b/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.css @@ -118,24 +118,3 @@ a.cascade-tree-row.is-active:hover { color: #8a96a3; font-weight: 600; } -.cascade-hint { - width: 100%; - max-width: 45rem; - margin: 0.15rem auto 0; - box-sizing: border-box; - padding: 0.2rem 0.35rem; - color: #f0c674; - text-align: center; -} -.cascade-hint[hidden] { - display: none; -} -.cascade-missing-msg { - max-width: 45rem; - margin: 1.5rem auto; - padding: 0 0.75rem; - color: #c8d0d8; - font-family: ui-sans-serif, system-ui, sans-serif; - font-size: 1rem; - line-height: 1.4; -} diff --git a/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.html b/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.html index d81564d..ed51952 100644 --- a/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.html +++ b/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.html @@ -1,6 +1,5 @@ diff --git a/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.js b/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.js index 8db6e4a..8fe4765 100644 --- a/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.js +++ b/sites/dossiers.ngi.hacktivism.ch/_cascade/bar.js @@ -25,99 +25,6 @@ } } - function setEditionCookie(edition) { - document.cookie = - "tdpd_edition=" + - encodeURIComponent(edition) + - "; Path=/; Max-Age=31536000; SameSite=Lax"; - } - - function keepPath() { - return location.pathname + location.search; - } - - function useHref(edition) { - var path = keepPath(); - var href = "/_cascade/use/" + encodeURIComponent(edition); - if (!path || path.indexOf("/_cascade/use/") === 0) return href; - return href + "?next=" + encodeURIComponent(path); - } - - function hintEl() { - return document.getElementById("cascade-hint"); - } - - function showHint(msg) { - var el = hintEl(); - if (!el) { - var bar = document.getElementById("cascade-bar"); - if (!bar) return; - el = document.createElement("div"); - el.id = "cascade-hint"; - el.className = "cascade-hint"; - el.setAttribute("role", "status"); - bar.appendChild(el); - } - el.hidden = false; - el.textContent = msg; - pinBar(); - } - - function clearHint() { - var el = hintEl(); - if (!el) return; - el.hidden = true; - el.textContent = ""; - pinBar(); - } - - function responseKeepsBar(res, text) { - if (res && res.ok) return true; - var body = text || ""; - return ( - body.indexOf("cascade-bar") !== -1 || - body.indexOf("cascade-missing") !== -1 - ); - } - - function switchEdition(edition, branchLabel) { - var prev = cookieEdition(); - var here = keepPath() || "/"; - var label = branchLabel || edition; - if (!edition) return; - if (edition === prev) { - clearHint(); - return; - } - setEditionCookie(edition); - fetch(here, { cache: "no-store", credentials: "same-origin" }) - .then(function (res) { - return res.text().then(function (text) { - return { res: res, text: text }; - }); - }) - .then(function (got) { - if (responseKeepsBar(got.res, got.text)) { - location.replace(here + location.hash); - return; - } - setEditionCookie(prev); - showHint( - "This path is not in " + - label + - ". Choose another branch above." - ); - }) - .catch(function () { - setEditionCookie(prev); - showHint( - "This path is not in " + - label + - ". Choose another branch above." - ); - }); - } - function esc(s) { return String(s) .replace(/&/g, "&") @@ -369,8 +276,8 @@ var parent = row.parent || ""; var isDefault = branch === defaultBranch; html.push( - 'flask' + + 'flask' + ' (branch-status unavailable)'; markActive(mount, cookieEdition()); pinBar(); @@ -436,26 +341,6 @@ }); } - document.addEventListener("click", function (ev) { - var a = ev.target.closest - ? ev.target.closest("a.cascade-tree-row[data-edition]") - : null; - if (!a || ev.defaultPrevented) return; - if (ev.button !== 0 || ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) - return; - ev.preventDefault(); - switchEdition( - a.getAttribute("data-edition"), - a.getAttribute("data-branch") || a.getAttribute("data-edition") - ); - }); - - if (document.body && document.body.classList.contains("cascade-missing")) { - showHint( - "This path is not in the current branch. Choose another branch above." - ); - } - loadStatus(false); setInterval(function () { loadStatus(true); diff --git a/sites/dossiers.ngi.hacktivism.ch/_cascade/index.html b/sites/dossiers.ngi.hacktivism.ch/_cascade/index.html index db0bc96..036ead3 100644 --- a/sites/dossiers.ngi.hacktivism.ch/_cascade/index.html +++ b/sites/dossiers.ngi.hacktivism.ch/_cascade/index.html @@ -9,7 +9,6 @@

Stuck on an edition without a bar? Pick a build above, or reset to flask.

diff --git a/sites/dossiers.ngi.hacktivism.ch/_cascade/missing.html b/sites/dossiers.ngi.hacktivism.ch/_cascade/missing.html deleted file mode 100644 index 449d470..0000000 --- a/sites/dossiers.ngi.hacktivism.ch/_cascade/missing.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - -path not in this branch - - - - -

This address is not in the current branch. Choose another branch above.

- - - diff --git a/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.css b/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.css index feefe10..d0a638e 100644 --- a/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.css +++ b/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.css @@ -118,24 +118,3 @@ a.cascade-tree-row.is-active:hover { color: #8a96a3; font-weight: 600; } -.cascade-hint { - width: 100%; - max-width: 45rem; - margin: 0.15rem auto 0; - box-sizing: border-box; - padding: 0.2rem 0.35rem; - color: #f0c674; - text-align: center; -} -.cascade-hint[hidden] { - display: none; -} -.cascade-missing-msg { - max-width: 45rem; - margin: 1.5rem auto; - padding: 0 0.75rem; - color: #c8d0d8; - font-family: ui-sans-serif, system-ui, sans-serif; - font-size: 1rem; - line-height: 1.4; -} diff --git a/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.html b/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.html index d81564d..ed51952 100644 --- a/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.html +++ b/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.html @@ -1,6 +1,5 @@ diff --git a/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.js b/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.js index 8db6e4a..8fe4765 100644 --- a/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.js +++ b/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/bar.js @@ -25,99 +25,6 @@ } } - function setEditionCookie(edition) { - document.cookie = - "tdpd_edition=" + - encodeURIComponent(edition) + - "; Path=/; Max-Age=31536000; SameSite=Lax"; - } - - function keepPath() { - return location.pathname + location.search; - } - - function useHref(edition) { - var path = keepPath(); - var href = "/_cascade/use/" + encodeURIComponent(edition); - if (!path || path.indexOf("/_cascade/use/") === 0) return href; - return href + "?next=" + encodeURIComponent(path); - } - - function hintEl() { - return document.getElementById("cascade-hint"); - } - - function showHint(msg) { - var el = hintEl(); - if (!el) { - var bar = document.getElementById("cascade-bar"); - if (!bar) return; - el = document.createElement("div"); - el.id = "cascade-hint"; - el.className = "cascade-hint"; - el.setAttribute("role", "status"); - bar.appendChild(el); - } - el.hidden = false; - el.textContent = msg; - pinBar(); - } - - function clearHint() { - var el = hintEl(); - if (!el) return; - el.hidden = true; - el.textContent = ""; - pinBar(); - } - - function responseKeepsBar(res, text) { - if (res && res.ok) return true; - var body = text || ""; - return ( - body.indexOf("cascade-bar") !== -1 || - body.indexOf("cascade-missing") !== -1 - ); - } - - function switchEdition(edition, branchLabel) { - var prev = cookieEdition(); - var here = keepPath() || "/"; - var label = branchLabel || edition; - if (!edition) return; - if (edition === prev) { - clearHint(); - return; - } - setEditionCookie(edition); - fetch(here, { cache: "no-store", credentials: "same-origin" }) - .then(function (res) { - return res.text().then(function (text) { - return { res: res, text: text }; - }); - }) - .then(function (got) { - if (responseKeepsBar(got.res, got.text)) { - location.replace(here + location.hash); - return; - } - setEditionCookie(prev); - showHint( - "This path is not in " + - label + - ". Choose another branch above." - ); - }) - .catch(function () { - setEditionCookie(prev); - showHint( - "This path is not in " + - label + - ". Choose another branch above." - ); - }); - } - function esc(s) { return String(s) .replace(/&/g, "&") @@ -369,8 +276,8 @@ var parent = row.parent || ""; var isDefault = branch === defaultBranch; html.push( - 'flask' + + 'flask' + ' (branch-status unavailable)'; markActive(mount, cookieEdition()); pinBar(); @@ -436,26 +341,6 @@ }); } - document.addEventListener("click", function (ev) { - var a = ev.target.closest - ? ev.target.closest("a.cascade-tree-row[data-edition]") - : null; - if (!a || ev.defaultPrevented) return; - if (ev.button !== 0 || ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) - return; - ev.preventDefault(); - switchEdition( - a.getAttribute("data-edition"), - a.getAttribute("data-branch") || a.getAttribute("data-edition") - ); - }); - - if (document.body && document.body.classList.contains("cascade-missing")) { - showHint( - "This path is not in the current branch. Choose another branch above." - ); - } - loadStatus(false); setInterval(function () { loadStatus(true); diff --git a/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/index.html b/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/index.html index db0bc96..036ead3 100644 --- a/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/index.html +++ b/sites/dossiers.ngi.hacktivism.ch/cascade/_cascade/index.html @@ -9,7 +9,6 @@

Stuck on an edition without a bar? Pick a build above, or reset to flask.