ops: paivana and caddy config refresh
This commit is contained in:
parent
d8dbc6242b
commit
56f814a6de
74 changed files with 4517 additions and 79 deletions
|
|
@ -14,7 +14,8 @@ Directories are named to match **live podman container names** where possible.
|
|||
| `caddy/` `firewalld/` `systemd/` | host services | |
|
||||
| `tor/` | **`koopa-tor-relay`** (podman host net) | `localhost/koopa-tor-relay:latest` (**non-root** uid 1000) |
|
||||
| `nym/` | **`koopa-nym`** (nym.com nym-node) | `localhost/koopa-nym:latest` (**non-root** uid 1000) |
|
||||
| `paivana/` | **`koopa-paivana`** (+ upstream) | `localhost/koopa-paivana:latest` (**non-root**); upstream unprivileged nginx |
|
||||
| `paivana/` | **`koopa-paivana`** + paywalls + upstream | `localhost/koopa-paivana:latest` (**non-root**); public **9025/9028–9030** |
|
||||
| `notes/` | **`koopa-silverbullet`** (SilverBullet) | `localhost/koopa-silverbullet:cachyos` (backend **127.0.0.1:19128**; paywall **9028**) |
|
||||
| `forgejo/` | **`koopa-forgejo`** | rootless image + `user: 1000` + `userns keep-id` |
|
||||
| `prime/` | jellyfin / qbittorrent | linuxserver **PUID/PGID=1000** |
|
||||
|
||||
|
|
|
|||
36
configs/bbb/Containerfile
Normal file
36
configs/bbb/Containerfile
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# koopa-bbb-front — BigBlueButton front (Ubuntu)
|
||||
# Exception to CachyOS policy: BBB packages and install scripts target Ubuntu.
|
||||
# Build: bash ~/koopa-admin-log/scripts/bbb/build.sh → localhost/koopa-bbb-front:ubuntu
|
||||
#
|
||||
# Placeholder reverse-proxy target (nginx) until Greenlight and/or BBB core
|
||||
# (bbb-install on Ubuntu) is chosen. Not production BBB media.
|
||||
|
||||
ARG UBUNTU_IMAGE=docker.io/library/ubuntu:24.04
|
||||
FROM ${UBUNTU_IMAGE}
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
TZ=Europe/Zurich
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates curl nginx tini \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN printf '%s\n' \
|
||||
'server {' \
|
||||
' listen 80 default_server;' \
|
||||
' root /var/www/html;' \
|
||||
' index index.html;' \
|
||||
' location = /health { access_log off; default_type text/plain; return 200 "ok\n"; }' \
|
||||
' location / { try_files $uri $uri/ =404; }' \
|
||||
'}' > /etc/nginx/sites-available/default \
|
||||
&& printf '%s\n' \
|
||||
'<!doctype html><meta charset=utf-8><title>bbb.hacktivism.ch</title>' \
|
||||
'<p>BBB front stub (Ubuntu 24.04). Full BigBlueButton / Greenlight is a separate Ubuntu install step.</p>' \
|
||||
> /var/www/html/index.html
|
||||
|
||||
COPY entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod 755 /docker-entrypoint.sh
|
||||
|
||||
EXPOSE 80
|
||||
ENTRYPOINT ["/usr/bin/tini", "--", "/docker-entrypoint.sh"]
|
||||
38
configs/bbb/README.md
Normal file
38
configs/bbb/README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# BBB (BigBlueButton) — Ubuntu container (koopa)
|
||||
|
||||
CachyOS exception: BBB packages and `bbb-install` target **Ubuntu**. Front stub image is Ubuntu 24.04.
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Stack | BigBlueButton (front stub → core later) |
|
||||
| Image | `localhost/koopa-bbb-front:ubuntu` (`FROM docker.io/library/ubuntu:24.04`) |
|
||||
| Live dir | `/home/hernani/koopa-bbb/` |
|
||||
| Container | `koopa-bbb-front` |
|
||||
| Host port | **9032** |
|
||||
| Public site | `bbb.hacktivism.ch` |
|
||||
|
||||
## Phases
|
||||
|
||||
1. **Front stub (this tree)** — nginx placeholder on `:9032` so Caddy + DNS can land.
|
||||
2. **Greenlight (optional)** — Rails UI talking to a real BBB API; still Ubuntu-based images.
|
||||
3. **BBB core** — FreeSWITCH / mediasoup / akka-apps. Prefer official Ubuntu install (`bbb-install.sh`) on a dedicated Ubuntu host/VM, or a known multi-container BBB compose. Not CachyOS.
|
||||
|
||||
Media UDP/TCP and TURN are documented when core is chosen — not in this stub.
|
||||
|
||||
## Build + up (koopa)
|
||||
|
||||
```bash
|
||||
bash /path/to/koopa-admin-log/scripts/bbb/build.sh
|
||||
mkdir -p ~/koopa-bbb && cp -a /path/to/koopa-admin-log/configs/bbb/{compose.yml,Containerfile,entrypoint.sh} ~/koopa-bbb/
|
||||
cd ~/koopa-bbb && HOST_PORT=9032 podman-compose up -d
|
||||
```
|
||||
|
||||
## Caddy
|
||||
|
||||
```bash
|
||||
sudo bash /path/to/koopa-admin-log/scripts/caddy/caddy-apply.sh \
|
||||
--snippet /path/to/koopa-admin-log/configs/caddy/bbb.hacktivism.ch.caddy \
|
||||
--site bbb.hacktivism.ch --port 9032 --label bbb
|
||||
```
|
||||
|
||||
DNS + VeciGate for the hostname after apply Freigabe.
|
||||
31
configs/bbb/compose.yml
Normal file
31
configs/bbb/compose.yml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# koopa-bbb — BigBlueButton front on Ubuntu (exception to CachyOS)
|
||||
# Live: /home/hernani/koopa-bbb/
|
||||
# Build: bash ~/koopa-admin-log/scripts/bbb/build.sh → localhost/koopa-bbb-front:ubuntu
|
||||
#
|
||||
# HOST_PORT: 9032 → Caddy bbb.hacktivism.ch
|
||||
# Core BBB / Greenlight: separate follow-up (Ubuntu), not this stub.
|
||||
services:
|
||||
bbb-front:
|
||||
image: localhost/koopa-bbb-front:ubuntu
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Containerfile
|
||||
container_name: koopa-bbb-front
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
ports:
|
||||
- "${HOST_PORT:-9032}:80"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fsS", "http://127.0.0.1/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
labels:
|
||||
org.hacktivism.service: bbb
|
||||
org.hacktivism.host_port: "${HOST_PORT:-9032}"
|
||||
org.hacktivism.site: bbb.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.port_status: allocated-9032
|
||||
org.hacktivism.image_base: ubuntu
|
||||
3
configs/bbb/entrypoint.sh
Executable file
3
configs/bbb/entrypoint.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
exec nginx -g 'daemon off;'
|
||||
27
configs/bbb/vm/defaults.env
Normal file
27
configs/bbb/vm/defaults.env
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# koopa BBB Ubuntu VM — lean defaults (host ~15 Gi / 4 cores)
|
||||
# Override before sourcing: RAM_MIB=4096 VCPUS=2 …
|
||||
|
||||
VM_NAME=koopa-bbb
|
||||
# Official prod=16G; local-dev floor=8G. Koopa lean default=6G + guest swap.
|
||||
RAM_MIB=6144
|
||||
VCPUS=2
|
||||
DISK_GB=50
|
||||
# Ubuntu 22.04 LTS (bbb-install 3.0). 24.04 only if docs for your branch say so.
|
||||
OS_VARIANT=ubuntu22.04
|
||||
CLOUD_IMAGE_URL=https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img
|
||||
HOSTNAME=bbb.hacktivism.ch
|
||||
# Bridged LAN (preferred for media DNAT). Set BRIDGE= after host-prep creates br0.
|
||||
BRIDGE=
|
||||
# Fallback: libvirt default NAT (media harder; ok for first boot/ssh)
|
||||
NETWORK_SOURCE=default
|
||||
# Guest swap (MiB) — important when RAM_MIB < 8192
|
||||
SWAP_MIB=4096
|
||||
# SSH pubkey injected via cloud-init (path on koopa host)
|
||||
SSH_PUBKEY_FILE=/home/hernani/.ssh/id_ed25519.pub
|
||||
# Disk pool
|
||||
POOL_DIR=/var/lib/libvirt/images
|
||||
# bbb-install branch + Greenlight
|
||||
BBB_INSTALL_BRANCH=v3.0.x-release
|
||||
BBB_INSTALL_EXTRA=-g
|
||||
# After VM has IP: Caddy may stay on :9032 stub until cutover, or point at VM:80
|
||||
CADDY_BACKEND_HINT=VM_HTTP_80_OR_DNAT
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Internal only (not in the browser URL):
|
||||
# 9010 merchant API | 9011 exchange API | 9012 bank API
|
||||
# 9013 bank landing | 9014 exchange landing | 9015 merchant landing
|
||||
# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9200 forgejo-ssh
|
||||
# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9029 galene | 9030 cryptpad | 9031 meet | 9032 bbb | 9028 silverbullet | 9200 forgejo-ssh
|
||||
# 9090 tops ng1 | 9091 tops ng2 | 9092 tops ng3
|
||||
|
||||
{
|
||||
|
|
@ -332,7 +332,101 @@ decidim.hacktivism.ch {
|
|||
}
|
||||
}
|
||||
|
||||
http://taler.hacktivism.ch, http://exchange.hacktivism.ch, http://bank.hacktivism.ch, http://castopod.hacktivism.ch, http://bonfire.hacktivism.ch, http://prime.hacktivism.ch, http://bt.hacktivism.ch, http://git.hacktivism.ch, http://paivana.hacktivism.ch, http://tops.ng1.hacktivism.ch, http://tops.ng2.hacktivism.ch, http://tops.ng3.hacktivism.ch, http://lemmy.hacktivism.ch, http://decidim.hacktivism.ch {
|
||||
# 9028 silverbullet / SilverBullet — allocated 2026-09-08
|
||||
# Merge into /etc/caddy/Caddyfile (sudo).
|
||||
# Also add http://silverbullet.hacktivism.ch to the shared ACME/redirect list.
|
||||
silverbullet.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9028 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9029 galene — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site galene.hacktivism.ch --port 9029 --label galene
|
||||
galene.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9029 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9030 cryptpad — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site cryptpad.hacktivism.ch --port 9030 --label cryptpad
|
||||
# CryptPad also needs sandbox subdomain (cryptpad-ui / sandbox) — wire after first boot.
|
||||
cryptpad.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9030 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9031 meet (Jitsi) — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site meet.hacktivism.ch --port 9031 --label meet
|
||||
# WebRTC media (UDP/JVB) is separate from this HTTPS vhost — VeciGate/firewall later.
|
||||
meet.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9031 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9032 bbb (BigBlueButton) — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site bbb.hacktivism.ch --port 9032 --label bbb
|
||||
# Stack image base: Ubuntu (not CachyOS). Media/UDP ports TBD with install.
|
||||
bbb.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9032 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http://taler.hacktivism.ch, http://exchange.hacktivism.ch, http://bank.hacktivism.ch, http://castopod.hacktivism.ch, http://bonfire.hacktivism.ch, http://prime.hacktivism.ch, http://bt.hacktivism.ch, http://git.hacktivism.ch, http://paivana.hacktivism.ch, http://tops.ng1.hacktivism.ch, http://tops.ng2.hacktivism.ch, http://tops.ng3.hacktivism.ch, http://lemmy.hacktivism.ch, http://decidim.hacktivism.ch, http://galene.hacktivism.ch, http://cryptpad.hacktivism.ch, http://meet.hacktivism.ch, http://bbb.hacktivism.ch {
|
||||
handle /.well-known/acme-challenge/* {
|
||||
root * /var/www/acme
|
||||
file_server
|
||||
|
|
|
|||
343
configs/caddy/Caddyfile.bak-caddy-apply-20260908-090954
Normal file
343
configs/caddy/Caddyfile.bak-caddy-apply-20260908-090954
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
# Internal only (not in the browser URL):
|
||||
# 9010 merchant API | 9011 exchange API | 9012 bank API
|
||||
# 9013 bank landing | 9014 exchange landing | 9015 merchant landing
|
||||
# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9200 forgejo-ssh
|
||||
# 9090 tops ng1 | 9091 tops ng2 | 9092 tops ng3
|
||||
|
||||
{
|
||||
email info+koopa@hacktivism.ch
|
||||
http_port 9000
|
||||
https_port 9001
|
||||
auto_https disable_redirects
|
||||
# Caddy listens on 9001 behind VeciGate/https-proxy :443.
|
||||
# Default HTTP/3 would send Alt-Svc: h3=":9001" — break public HTTPS on :443.
|
||||
servers {
|
||||
protocols h1 h2
|
||||
}
|
||||
}
|
||||
|
||||
(proxy_public) {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
header_down Location "^https?://[^/]+:90[0-9]{2}(.*)$" "https://{host}$1"
|
||||
}
|
||||
|
||||
taler.hacktivism.ch {
|
||||
tls /etc/caddy/certs/taler.hacktivism.ch/fullchain.pem /etc/caddy/certs/taler.hacktivism.ch/privkey.pem
|
||||
header Alt-Svc "clear"
|
||||
|
||||
# Public landing first
|
||||
redir / /intro/ 302
|
||||
|
||||
handle /intro* {
|
||||
reverse_proxy 127.0.0.1:9015 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
# surface + aptdeploy HTML (taler.hacktivism.ch only)
|
||||
handle /taler-monitoring-surface_err {
|
||||
redir /taler-monitoring-surface_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-surface_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-surface {
|
||||
redir /taler-monitoring-surface/ 302
|
||||
}
|
||||
handle /taler-monitoring-surface* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy_err {
|
||||
redir /taler-monitoring-aptdeploy_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy {
|
||||
redir /taler-monitoring-aptdeploy/ 302
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
handle /taler-monitoring-mattermost_err {
|
||||
redir /taler-monitoring-mattermost_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-mattermost_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-mattermost {
|
||||
redir /taler-monitoring-mattermost/ 302
|
||||
}
|
||||
handle /taler-monitoring-mattermost* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
handle /taler-monitoring-mail_err {
|
||||
redir /taler-monitoring-mail_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-mail_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-mail {
|
||||
redir /taler-monitoring-mail/ 302
|
||||
}
|
||||
handle /taler-monitoring-mail* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# SPA: /webui → /webui/
|
||||
redir /webui /webui/ 302
|
||||
|
||||
# Merchant API + WebUI (nginx :9010 → unix socket)
|
||||
reverse_proxy https://127.0.0.1:9010 {
|
||||
transport http {
|
||||
tls_insecure_skip_verify
|
||||
}
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
exchange.hacktivism.ch {
|
||||
tls /etc/caddy/certs/exchange.hacktivism.ch/fullchain.pem /etc/caddy/certs/exchange.hacktivism.ch/privkey.pem
|
||||
header Alt-Svc "clear"
|
||||
|
||||
# Public landing first
|
||||
redir / /intro/ 302
|
||||
|
||||
handle /intro* {
|
||||
reverse_proxy 127.0.0.1:9014 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
reverse_proxy 127.0.0.1:9011 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
bank.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
|
||||
# Public landing first
|
||||
redir / /intro/ 302
|
||||
|
||||
handle /intro* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
# Static terms/privacy on landing nginx :9013
|
||||
handle /terms* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
handle /privacy* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
reverse_proxy 127.0.0.1:9012 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
castopod.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9020 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
bonfire.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9021 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
flush_interval -1
|
||||
}
|
||||
}
|
||||
|
||||
prime.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9022 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
flush_interval -1
|
||||
}
|
||||
}
|
||||
|
||||
bt.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9023 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
# 9024 forgejo HTTP (SSH :9200 host-direct, not via Caddy)
|
||||
git.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9024 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Taler Operations design previews (static nginx)
|
||||
tops.ng1.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9090 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
tops.ng2.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9091 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
tops.ng3.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9092 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# 9025 paivana-httpd (GOA paywall)
|
||||
paivana.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9025 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
}
|
||||
}
|
||||
|
||||
# 9026 lemmy (+ ui/pictrs via internal nginx)
|
||||
lemmy.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9026 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9027 decidim (rails)
|
||||
decidim.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9027 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http://taler.hacktivism.ch, http://exchange.hacktivism.ch, http://bank.hacktivism.ch, http://castopod.hacktivism.ch, http://bonfire.hacktivism.ch, http://prime.hacktivism.ch, http://bt.hacktivism.ch, http://git.hacktivism.ch, http://paivana.hacktivism.ch, http://tops.ng1.hacktivism.ch, http://tops.ng2.hacktivism.ch, http://tops.ng3.hacktivism.ch, http://lemmy.hacktivism.ch, http://decidim.hacktivism.ch {
|
||||
handle /.well-known/acme-challenge/* {
|
||||
root * /var/www/acme
|
||||
file_server
|
||||
}
|
||||
handle {
|
||||
redir https://{host}{uri} permanent
|
||||
}
|
||||
}
|
||||
399
configs/caddy/Caddyfile.bak-caddy-apply-20260908-090955
Normal file
399
configs/caddy/Caddyfile.bak-caddy-apply-20260908-090955
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
# Internal only (not in the browser URL):
|
||||
# 9010 merchant API | 9011 exchange API | 9012 bank API
|
||||
# 9013 bank landing | 9014 exchange landing | 9015 merchant landing
|
||||
# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9029 galene | 9030 cryptpad | 9031 meet | 9200 forgejo-ssh
|
||||
# 9090 tops ng1 | 9091 tops ng2 | 9092 tops ng3
|
||||
|
||||
{
|
||||
email info+koopa@hacktivism.ch
|
||||
http_port 9000
|
||||
https_port 9001
|
||||
auto_https disable_redirects
|
||||
# Caddy listens on 9001 behind VeciGate/https-proxy :443.
|
||||
# Default HTTP/3 would send Alt-Svc: h3=":9001" — break public HTTPS on :443.
|
||||
servers {
|
||||
protocols h1 h2
|
||||
}
|
||||
}
|
||||
|
||||
(proxy_public) {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
header_down Location "^https?://[^/]+:90[0-9]{2}(.*)$" "https://{host}$1"
|
||||
}
|
||||
|
||||
taler.hacktivism.ch {
|
||||
tls /etc/caddy/certs/taler.hacktivism.ch/fullchain.pem /etc/caddy/certs/taler.hacktivism.ch/privkey.pem
|
||||
header Alt-Svc "clear"
|
||||
|
||||
# Public landing first
|
||||
redir / /intro/ 302
|
||||
|
||||
handle /intro* {
|
||||
reverse_proxy 127.0.0.1:9015 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
# surface + aptdeploy HTML (taler.hacktivism.ch only)
|
||||
handle /taler-monitoring-surface_err {
|
||||
redir /taler-monitoring-surface_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-surface_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-surface {
|
||||
redir /taler-monitoring-surface/ 302
|
||||
}
|
||||
handle /taler-monitoring-surface* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy_err {
|
||||
redir /taler-monitoring-aptdeploy_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy {
|
||||
redir /taler-monitoring-aptdeploy/ 302
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
handle /taler-monitoring-mattermost_err {
|
||||
redir /taler-monitoring-mattermost_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-mattermost_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-mattermost {
|
||||
redir /taler-monitoring-mattermost/ 302
|
||||
}
|
||||
handle /taler-monitoring-mattermost* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
handle /taler-monitoring-mail_err {
|
||||
redir /taler-monitoring-mail_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-mail_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-mail {
|
||||
redir /taler-monitoring-mail/ 302
|
||||
}
|
||||
handle /taler-monitoring-mail* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# SPA: /webui → /webui/
|
||||
redir /webui /webui/ 302
|
||||
|
||||
# Merchant API + WebUI (nginx :9010 → unix socket)
|
||||
reverse_proxy https://127.0.0.1:9010 {
|
||||
transport http {
|
||||
tls_insecure_skip_verify
|
||||
}
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
exchange.hacktivism.ch {
|
||||
tls /etc/caddy/certs/exchange.hacktivism.ch/fullchain.pem /etc/caddy/certs/exchange.hacktivism.ch/privkey.pem
|
||||
header Alt-Svc "clear"
|
||||
|
||||
# Public landing first
|
||||
redir / /intro/ 302
|
||||
|
||||
handle /intro* {
|
||||
reverse_proxy 127.0.0.1:9014 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
reverse_proxy 127.0.0.1:9011 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
bank.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
|
||||
# Public landing first
|
||||
redir / /intro/ 302
|
||||
|
||||
handle /intro* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
# Static terms/privacy on landing nginx :9013
|
||||
handle /terms* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
handle /privacy* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
reverse_proxy 127.0.0.1:9012 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
castopod.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9020 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
bonfire.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9021 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
flush_interval -1
|
||||
}
|
||||
}
|
||||
|
||||
prime.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9022 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
flush_interval -1
|
||||
}
|
||||
}
|
||||
|
||||
bt.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9023 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
# 9024 forgejo HTTP (SSH :9200 host-direct, not via Caddy)
|
||||
git.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9024 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Taler Operations design previews (static nginx)
|
||||
tops.ng1.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9090 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
tops.ng2.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9091 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
tops.ng3.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9092 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# 9025 paivana-httpd (GOA paywall)
|
||||
paivana.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9025 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
}
|
||||
}
|
||||
|
||||
# 9026 lemmy (+ ui/pictrs via internal nginx)
|
||||
lemmy.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9026 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9027 decidim (rails)
|
||||
decidim.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9027 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9029 galene — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site galene.hacktivism.ch --port 9029 --label galene
|
||||
galene.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9029 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9030 cryptpad — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site cryptpad.hacktivism.ch --port 9030 --label cryptpad
|
||||
# CryptPad also needs sandbox subdomain (cryptpad-ui / sandbox) — wire after first boot.
|
||||
cryptpad.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9030 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9031 meet (Jitsi) — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site meet.hacktivism.ch --port 9031 --label meet
|
||||
# WebRTC media (UDP/JVB) is separate from this HTTPS vhost — VeciGate/firewall later.
|
||||
meet.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9031 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http://taler.hacktivism.ch, http://exchange.hacktivism.ch, http://bank.hacktivism.ch, http://castopod.hacktivism.ch, http://bonfire.hacktivism.ch, http://prime.hacktivism.ch, http://bt.hacktivism.ch, http://git.hacktivism.ch, http://paivana.hacktivism.ch, http://tops.ng1.hacktivism.ch, http://tops.ng2.hacktivism.ch, http://tops.ng3.hacktivism.ch, http://lemmy.hacktivism.ch, http://decidim.hacktivism.ch, http://galene.hacktivism.ch, http://cryptpad.hacktivism.ch, http://meet.hacktivism.ch {
|
||||
handle /.well-known/acme-challenge/* {
|
||||
root * /var/www/acme
|
||||
file_server
|
||||
}
|
||||
handle {
|
||||
redir https://{host}{uri} permanent
|
||||
}
|
||||
}
|
||||
418
configs/caddy/Caddyfile.bak-caddy-apply-20260908-143821
Normal file
418
configs/caddy/Caddyfile.bak-caddy-apply-20260908-143821
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
# Internal only (not in the browser URL):
|
||||
# 9010 merchant API | 9011 exchange API | 9012 bank API
|
||||
# 9013 bank landing | 9014 exchange landing | 9015 merchant landing
|
||||
# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9029 galene | 9030 cryptpad | 9031 meet | 9032 bbb | 9200 forgejo-ssh
|
||||
# 9090 tops ng1 | 9091 tops ng2 | 9092 tops ng3
|
||||
|
||||
{
|
||||
email info+koopa@hacktivism.ch
|
||||
http_port 9000
|
||||
https_port 9001
|
||||
auto_https disable_redirects
|
||||
# Caddy listens on 9001 behind VeciGate/https-proxy :443.
|
||||
# Default HTTP/3 would send Alt-Svc: h3=":9001" — break public HTTPS on :443.
|
||||
servers {
|
||||
protocols h1 h2
|
||||
}
|
||||
}
|
||||
|
||||
(proxy_public) {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
header_down Location "^https?://[^/]+:90[0-9]{2}(.*)$" "https://{host}$1"
|
||||
}
|
||||
|
||||
taler.hacktivism.ch {
|
||||
tls /etc/caddy/certs/taler.hacktivism.ch/fullchain.pem /etc/caddy/certs/taler.hacktivism.ch/privkey.pem
|
||||
header Alt-Svc "clear"
|
||||
|
||||
# Public landing first
|
||||
redir / /intro/ 302
|
||||
|
||||
handle /intro* {
|
||||
reverse_proxy 127.0.0.1:9015 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
# surface + aptdeploy HTML (taler.hacktivism.ch only)
|
||||
handle /taler-monitoring-surface_err {
|
||||
redir /taler-monitoring-surface_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-surface_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-surface {
|
||||
redir /taler-monitoring-surface/ 302
|
||||
}
|
||||
handle /taler-monitoring-surface* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy_err {
|
||||
redir /taler-monitoring-aptdeploy_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy {
|
||||
redir /taler-monitoring-aptdeploy/ 302
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
handle /taler-monitoring-mattermost_err {
|
||||
redir /taler-monitoring-mattermost_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-mattermost_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-mattermost {
|
||||
redir /taler-monitoring-mattermost/ 302
|
||||
}
|
||||
handle /taler-monitoring-mattermost* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
handle /taler-monitoring-mail_err {
|
||||
redir /taler-monitoring-mail_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-mail_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-mail {
|
||||
redir /taler-monitoring-mail/ 302
|
||||
}
|
||||
handle /taler-monitoring-mail* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# SPA: /webui → /webui/
|
||||
redir /webui /webui/ 302
|
||||
|
||||
# Merchant API + WebUI (nginx :9010 → unix socket)
|
||||
reverse_proxy https://127.0.0.1:9010 {
|
||||
transport http {
|
||||
tls_insecure_skip_verify
|
||||
}
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
exchange.hacktivism.ch {
|
||||
tls /etc/caddy/certs/exchange.hacktivism.ch/fullchain.pem /etc/caddy/certs/exchange.hacktivism.ch/privkey.pem
|
||||
header Alt-Svc "clear"
|
||||
|
||||
# Public landing first
|
||||
redir / /intro/ 302
|
||||
|
||||
handle /intro* {
|
||||
reverse_proxy 127.0.0.1:9014 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
reverse_proxy 127.0.0.1:9011 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
bank.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
|
||||
# Public landing first
|
||||
redir / /intro/ 302
|
||||
|
||||
handle /intro* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
# Static terms/privacy on landing nginx :9013
|
||||
handle /terms* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
handle /privacy* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
reverse_proxy 127.0.0.1:9012 {
|
||||
import proxy_public
|
||||
}
|
||||
}
|
||||
|
||||
castopod.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9020 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
bonfire.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9021 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
flush_interval -1
|
||||
}
|
||||
}
|
||||
|
||||
prime.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9022 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
flush_interval -1
|
||||
}
|
||||
}
|
||||
|
||||
bt.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9023 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
}
|
||||
}
|
||||
|
||||
# 9024 forgejo HTTP (SSH :9200 host-direct, not via Caddy)
|
||||
git.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9024 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Taler Operations design previews (static nginx)
|
||||
tops.ng1.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9090 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
tops.ng2.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9091 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
tops.ng3.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9092 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# 9025 paivana-httpd (GOA paywall)
|
||||
paivana.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9025 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
}
|
||||
}
|
||||
|
||||
# 9026 lemmy (+ ui/pictrs via internal nginx)
|
||||
lemmy.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9026 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9027 decidim (rails)
|
||||
decidim.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9027 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9029 galene — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site galene.hacktivism.ch --port 9029 --label galene
|
||||
galene.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9029 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9030 cryptpad — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site cryptpad.hacktivism.ch --port 9030 --label cryptpad
|
||||
# CryptPad also needs sandbox subdomain (cryptpad-ui / sandbox) — wire after first boot.
|
||||
cryptpad.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9030 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9031 meet (Jitsi) — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site meet.hacktivism.ch --port 9031 --label meet
|
||||
# WebRTC media (UDP/JVB) is separate from this HTTPS vhost — VeciGate/firewall later.
|
||||
meet.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9031 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9032 bbb (BigBlueButton) — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site bbb.hacktivism.ch --port 9032 --label bbb
|
||||
# Stack image base: Ubuntu (not CachyOS). Media/UDP ports TBD with install.
|
||||
bbb.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9032 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http://taler.hacktivism.ch, http://exchange.hacktivism.ch, http://bank.hacktivism.ch, http://castopod.hacktivism.ch, http://bonfire.hacktivism.ch, http://prime.hacktivism.ch, http://bt.hacktivism.ch, http://git.hacktivism.ch, http://paivana.hacktivism.ch, http://tops.ng1.hacktivism.ch, http://tops.ng2.hacktivism.ch, http://tops.ng3.hacktivism.ch, http://lemmy.hacktivism.ch, http://decidim.hacktivism.ch, http://galene.hacktivism.ch, http://cryptpad.hacktivism.ch, http://meet.hacktivism.ch, http://bbb.hacktivism.ch {
|
||||
handle /.well-known/acme-challenge/* {
|
||||
root * /var/www/acme
|
||||
file_server
|
||||
}
|
||||
handle {
|
||||
redir https://{host}{uri} permanent
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|------|------|
|
||||
| **`Caddyfile`** | Multi-vhost config mirror (`/etc/caddy/Caddyfile` on host) |
|
||||
| `git.hacktivism.ch.caddy` | Site-block snippet for Forgejo |
|
||||
| `silverbullet.hacktivism.ch.caddy` | Site-block snippet for SilverBullet |
|
||||
| `Caddyfile.taler-host` | **Legacy** Taler-only sketch — do not deploy |
|
||||
|
||||
Also: `host/caddy/Caddyfile` (same as `Caddyfile` here).
|
||||
|
|
|
|||
18
configs/caddy/bbb.hacktivism.ch.caddy
Normal file
18
configs/caddy/bbb.hacktivism.ch.caddy
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# 9032 bbb (BigBlueButton) — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site bbb.hacktivism.ch --port 9032 --label bbb
|
||||
# Stack image base: Ubuntu (not CachyOS). Media/UDP ports TBD with install.
|
||||
bbb.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9032 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
18
configs/caddy/cryptpad.hacktivism.ch.caddy
Normal file
18
configs/caddy/cryptpad.hacktivism.ch.caddy
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# 9030 cryptpad — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site cryptpad.hacktivism.ch --port 9030 --label cryptpad
|
||||
# Backend is koopa-cryptpad-edge (HTTP+WS). Sandbox subdomain (cryptpad-ui) — wire after first boot.
|
||||
cryptpad.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9030 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
17
configs/caddy/galene.hacktivism.ch.caddy
Normal file
17
configs/caddy/galene.hacktivism.ch.caddy
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# 9029 galene — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site galene.hacktivism.ch --port 9029 --label galene
|
||||
galene.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9029 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
18
configs/caddy/meet.hacktivism.ch.caddy
Normal file
18
configs/caddy/meet.hacktivism.ch.caddy
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# 9031 meet (Jitsi) — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site meet.hacktivism.ch --port 9031 --label meet
|
||||
# WebRTC media (UDP/JVB) is separate from this HTTPS vhost — VeciGate/firewall later.
|
||||
meet.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9031 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
18
configs/caddy/silverbullet.hacktivism.ch.caddy
Normal file
18
configs/caddy/silverbullet.hacktivism.ch.caddy
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# 9028 silverbullet / SilverBullet — allocated 2026-09-08
|
||||
# Merge into /etc/caddy/Caddyfile (sudo).
|
||||
# Also add http://silverbullet.hacktivism.ch to the shared ACME/redirect list.
|
||||
silverbullet.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9028 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
47
configs/cryptpad/Containerfile
Normal file
47
configs/cryptpad/Containerfile
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# koopa-cryptpad — CryptPad on CachyOS base + Node from pacman
|
||||
# Build on koopa (x86_64):
|
||||
# bash ~/koopa-admin-log/scripts/cryptpad/build.sh
|
||||
# → localhost/koopa-cryptpad:cachyos
|
||||
#
|
||||
# App tree from upstream release tarball (not the official Debian-ish image).
|
||||
|
||||
ARG CACHYOS_IMAGE=docker.io/cachyos/cachyos:latest
|
||||
FROM ${CACHYOS_IMAGE}
|
||||
|
||||
ARG CRYPTPAD_VERSION=2025.9.0
|
||||
ARG CRYPTPAD_URL=https://github.com/cryptpad/cryptpad/archive/refs/tags/${CRYPTPAD_VERSION}.tar.gz
|
||||
|
||||
ENV TZ=Europe/Zurich \
|
||||
PATH=/usr/local/bin:$PATH \
|
||||
CRYPTPAD_HOME=/cryptpad
|
||||
|
||||
RUN pacman -Syu --noconfirm --needed \
|
||||
bash curl ca-certificates catatonit tar git \
|
||||
nodejs npm \
|
||||
&& pacman -Scc --noconfirm \
|
||||
&& rm -rf /var/cache/pacman/pkg/* /tmp/*
|
||||
|
||||
# npm may block git deps (EALLOWGIT); rewrite git+ssh → https and allow git installs.
|
||||
RUN curl -fsSL "$CRYPTPAD_URL" -o /tmp/cryptpad.tgz \
|
||||
&& mkdir -p /cryptpad \
|
||||
&& tar -xzf /tmp/cryptpad.tgz -C /cryptpad --strip-components=1 \
|
||||
&& rm -f /tmp/cryptpad.tgz \
|
||||
&& cd /cryptpad \
|
||||
&& git config --global url."https://github.com/".insteadOf "ssh://git@github.com/" \
|
||||
&& git config --global url."https://github.com/".insteadOf "git@github.com:" \
|
||||
&& git config --global url."https://".insteadOf "git://" \
|
||||
&& printf 'allow-git=all\nallow-remote=all\n' > /root/.npmrc \
|
||||
&& (npm ci --omit=dev --allow-git=all --allow-remote=all \
|
||||
|| npm install --omit=dev --allow-git=all --allow-remote=all) \
|
||||
&& (npm run install:components || true) \
|
||||
&& rm -rf /root/.npm /tmp/* /var/cache/pacman/pkg/*
|
||||
|
||||
COPY entrypoint.sh /docker-entrypoint.sh
|
||||
RUN mkdir -p /cryptpad/config /cryptpad/data /cryptpad/datastore /cryptpad/block /cryptpad/blob \
|
||||
/cryptpad/blobstage /cryptpad/data/logs
|
||||
COPY config.example.js /cryptpad/config/config.js
|
||||
RUN chmod 755 /docker-entrypoint.sh
|
||||
|
||||
EXPOSE 3000
|
||||
WORKDIR /cryptpad
|
||||
ENTRYPOINT ["/usr/bin/catatonit", "--", "/docker-entrypoint.sh"]
|
||||
32
configs/cryptpad/README.md
Normal file
32
configs/cryptpad/README.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# CryptPad — on CachyOS (koopa)
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Stack | [CryptPad](https://cryptpad.org/) |
|
||||
| Image | `localhost/koopa-cryptpad:cachyos` |
|
||||
| Live dir | `/home/hernani/koopa-cryptpad/` |
|
||||
| Host port | **9030** = Paivana paywall; nginx edge backend **127.0.0.1:19130** → app |
|
||||
| App ports (internal) | **3000** HTTP, **3003** websocket |
|
||||
| Public site | `cryptpad.hacktivism.ch` |
|
||||
|
||||
`websocketPort` must differ from `httpPort` (upstream default 3003). Compose runs `koopa-cryptpad-edge` (nginx) so Caddy still only targets **9030**.
|
||||
Upstream wants a separate **safe/sandbox** origin for some builds — add a second vhost after first boot if the admin UI requires it.
|
||||
|
||||
## Build + up
|
||||
|
||||
```bash
|
||||
bash /path/to/koopa-admin-log/scripts/cryptpad/build.sh
|
||||
mkdir -p ~/koopa-cryptpad
|
||||
cp -a /path/to/koopa-admin-log/configs/cryptpad/{compose.yml,config.example.js} ~/koopa-cryptpad/
|
||||
cp ~/koopa-cryptpad/config.example.js ~/koopa-cryptpad/config.js
|
||||
# edit httpUnsafeOrigin / httpSafeOrigin in config.js
|
||||
cd ~/koopa-cryptpad && podman-compose up -d
|
||||
```
|
||||
|
||||
## Caddy
|
||||
|
||||
```bash
|
||||
sudo bash /path/to/koopa-admin-log/scripts/caddy/caddy-apply.sh \
|
||||
--snippet …/configs/caddy/cryptpad.hacktivism.ch.caddy \
|
||||
--site cryptpad.hacktivism.ch --port 9030 --label cryptpad
|
||||
```
|
||||
53
configs/cryptpad/compose.yml
Normal file
53
configs/cryptpad/compose.yml
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# koopa-cryptpad — CryptPad on CachyOS + nginx edge (HTTP+WS on :9030)
|
||||
# Live: /home/hernani/koopa-cryptpad/
|
||||
# Build: bash ~/koopa-admin-log/scripts/cryptpad/build.sh → localhost/koopa-cryptpad:cachyos
|
||||
#
|
||||
# Public :9030 = koopa-paivana-cryptpad (shared paivana image).
|
||||
# Edge backend loopback only — Paivana DEST → 127.0.0.1:19130
|
||||
# App listens 3000 (http) + 3003 (websocket) inside the net.
|
||||
services:
|
||||
cryptpad:
|
||||
image: localhost/koopa-cryptpad:cachyos
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Containerfile
|
||||
args:
|
||||
CRYPTPAD_VERSION: "${CRYPTPAD_VERSION:-2025.9.0}"
|
||||
container_name: koopa-cryptpad
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
volumes:
|
||||
- ./data:/cryptpad/data
|
||||
- ./datastore:/cryptpad/datastore
|
||||
- ./block:/cryptpad/block
|
||||
- ./blob:/cryptpad/blob
|
||||
- ./config.js:/cryptpad/config/config.js:ro
|
||||
expose:
|
||||
- "3000"
|
||||
- "3003"
|
||||
labels:
|
||||
org.hacktivism.service: cryptpad
|
||||
org.hacktivism.host_port: "9030"
|
||||
org.hacktivism.backend_port: "${BACKEND_PORT:-19130}"
|
||||
org.hacktivism.site: cryptpad.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.port_status: backend-19130-paywall-9030
|
||||
org.hacktivism.image_base: cachyos
|
||||
|
||||
edge:
|
||||
image: docker.io/library/nginx:alpine
|
||||
container_name: koopa-cryptpad-edge
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- cryptpad
|
||||
volumes:
|
||||
- ./edge.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
ports:
|
||||
- "127.0.0.1:${BACKEND_PORT:-19130}:80"
|
||||
labels:
|
||||
org.hacktivism.service: cryptpad-edge
|
||||
org.hacktivism.host_port: "9030"
|
||||
org.hacktivism.backend_port: "${BACKEND_PORT:-19130}"
|
||||
org.hacktivism.site: cryptpad.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
13
configs/cryptpad/config.example.js
Normal file
13
configs/cryptpad/config.example.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/* CryptPad config — http on 3000, websockets on 3003 (must differ).
|
||||
* Live: mount ./config.js over /cryptpad/config/config.js
|
||||
* Edge nginx in compose merges both onto host :9030 (Caddy unchanged).
|
||||
*/
|
||||
module.exports = {
|
||||
httpUnsafeOrigin: 'https://cryptpad.hacktivism.ch',
|
||||
httpSafeOrigin: 'https://cryptpad.hacktivism.ch',
|
||||
httpAddress: '0.0.0.0',
|
||||
httpPort: 3000,
|
||||
websocketPort: 3003,
|
||||
logToStdout: true,
|
||||
maxWorkers: 2,
|
||||
};
|
||||
37
configs/cryptpad/edge.conf
Normal file
37
configs/cryptpad/edge.conf
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Front for koopa-cryptpad: HTTP :3000 + /cryptpad_websocket → :3003
|
||||
upstream cryptpad_http {
|
||||
server cryptpad:3000;
|
||||
}
|
||||
upstream cryptpad_ws {
|
||||
server cryptpad:3003;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
client_max_body_size 150m;
|
||||
|
||||
location /cryptpad_websocket {
|
||||
proxy_pass http://cryptpad_ws;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://cryptpad_http;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
}
|
||||
}
|
||||
5
configs/cryptpad/entrypoint.sh
Executable file
5
configs/cryptpad/entrypoint.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
cd /cryptpad
|
||||
# Expect config.js already present (compose mounts or image default).
|
||||
exec node server.js "$@"
|
||||
41
configs/galene/Containerfile
Normal file
41
configs/galene/Containerfile
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# koopa-galene — Galene SFU on CachyOS base
|
||||
# Build on koopa (x86_64):
|
||||
# bash ~/koopa-admin-log/scripts/galene/build.sh
|
||||
# → localhost/koopa-galene:cachyos
|
||||
#
|
||||
# Upstream no longer ships release tarballs; build from git tag/branch.
|
||||
|
||||
ARG CACHYOS_IMAGE=docker.io/cachyos/cachyos:latest
|
||||
FROM ${CACHYOS_IMAGE}
|
||||
|
||||
ARG GALENE_REF=galene-0.96.3
|
||||
ARG GALENE_REPO=https://github.com/jech/galene.git
|
||||
|
||||
ENV TZ=Europe/Zurich \
|
||||
PATH=/usr/local/bin:$PATH \
|
||||
GALENE_DATA=/data \
|
||||
GOPATH=/tmp/go \
|
||||
GOTOOLCHAIN=auto
|
||||
|
||||
RUN pacman -Syu --noconfirm --needed \
|
||||
bash curl ca-certificates catatonit tar git go \
|
||||
&& pacman -Scc --noconfirm \
|
||||
&& rm -rf /var/cache/pacman/pkg/* /tmp/*
|
||||
|
||||
RUN set -eux \
|
||||
&& git clone --depth 1 --branch "$GALENE_REF" "$GALENE_REPO" /src/galene \
|
||||
&& cd /src/galene \
|
||||
&& CGO_ENABLED=0 go build -ldflags='-s -w' -o /usr/local/bin/galene . \
|
||||
&& test -x /usr/local/bin/galene \
|
||||
&& mkdir -p /opt/galene \
|
||||
&& cp -a /src/galene/static /opt/galene/static \
|
||||
&& rm -rf /src/galene /tmp/go /root/go /var/cache/pacman/pkg/*
|
||||
|
||||
COPY entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod 755 /docker-entrypoint.sh \
|
||||
&& mkdir -p /data/groups /data/recordings /data/data \
|
||||
&& ln -sfn /opt/galene/static /data/static
|
||||
|
||||
EXPOSE 8443
|
||||
WORKDIR /data
|
||||
ENTRYPOINT ["/usr/bin/catatonit", "--", "/docker-entrypoint.sh"]
|
||||
36
configs/galene/README.md
Normal file
36
configs/galene/README.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Galene — SFU on CachyOS (koopa)
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Stack | [Galene](https://galene.org/) |
|
||||
| Image | `localhost/koopa-galene:cachyos` (`FROM docker.io/cachyos/cachyos`) |
|
||||
| Build | Go from git tag `GALENE_REF` (default `galene-0.96.3`; no release tarballs) |
|
||||
| Live dir | `/home/hernani/koopa-galene/` |
|
||||
| Container | `koopa-galene` |
|
||||
| Host port | **9029** = Paivana paywall; Galene HTTP backend **127.0.0.1:19129** (`-insecure`) |
|
||||
| Public site | `galene.hacktivism.ch` |
|
||||
|
||||
## Build + up (koopa)
|
||||
|
||||
```bash
|
||||
bash /path/to/koopa-admin-log/scripts/galene/build.sh
|
||||
mkdir -p ~/koopa-galene && cp -a /path/to/koopa-admin-log/configs/galene/compose.yml ~/koopa-galene/
|
||||
cd ~/koopa-galene && podman-compose up -d
|
||||
```
|
||||
|
||||
## Caddy
|
||||
|
||||
```bash
|
||||
sudo bash /path/to/koopa-admin-log/scripts/caddy/caddy-apply.sh \
|
||||
--snippet /path/to/koopa-admin-log/configs/caddy/galene.hacktivism.ch.caddy \
|
||||
--site galene.hacktivism.ch --port 9029 --label galene
|
||||
```
|
||||
|
||||
DNS + VeciGate for the hostname; WebRTC UDP ports documented when first room is live.
|
||||
|
||||
## Notes
|
||||
|
||||
- Volume `./data` must not hide UI assets; entrypoint passes `-static /opt/galene/static`.
|
||||
- Example group: copy `groups/hacktivism.json.example` → `~/koopa-galene/data/groups/hacktivism.json` (change op password).
|
||||
- WebRTC UDP: compose publishes **10000–10099/udp**; set the same range in VeciGate DNAT → koopa. Entrypoint uses `-udp-range` (`GALENE_UDP_RANGE`).
|
||||
- Built-in TURN (`-turn auto`) logs `no public addresses` until the host has a reachable public/mapped address; fix UDP/NAT first, then re-check logs.
|
||||
30
configs/galene/compose.yml
Normal file
30
configs/galene/compose.yml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# koopa-galene — Galene on CachyOS
|
||||
# Live: /home/hernani/koopa-galene/
|
||||
# Build: bash ~/koopa-admin-log/scripts/galene/build.sh → localhost/koopa-galene:cachyos
|
||||
#
|
||||
# Public :9029 = koopa-paivana-galene (shared paivana image).
|
||||
# Backend loopback only — Paivana DEST → 127.0.0.1:19129 (HTTP -insecure).
|
||||
# WebRTC UDP: still published (see README / ports.md)
|
||||
services:
|
||||
galene:
|
||||
image: localhost/koopa-galene:cachyos
|
||||
container_name: koopa-galene
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
- GALENE_UDP_RANGE=${GALENE_UDP_RANGE:-10000-10099}
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./entrypoint.sh:/docker-entrypoint.sh:ro
|
||||
ports:
|
||||
- "127.0.0.1:${BACKEND_PORT:-19129}:8443"
|
||||
- "${GALENE_UDP_LO:-10000}-${GALENE_UDP_HI:-10099}:${GALENE_UDP_LO:-10000}-${GALENE_UDP_HI:-10099}/udp"
|
||||
labels:
|
||||
org.hacktivism.service: galene
|
||||
org.hacktivism.host_port: "9029"
|
||||
org.hacktivism.backend_port: "${BACKEND_PORT:-19129}"
|
||||
org.hacktivism.site: galene.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.port_status: backend-19129-paywall-9029
|
||||
org.hacktivism.image_base: cachyos
|
||||
|
||||
21
configs/galene/entrypoint.sh
Executable file
21
configs/galene/entrypoint.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
# Galene listens HTTPS by default; behind Caddy we serve plain HTTP on :8443
|
||||
# with -insecure (TLS terminated at Caddy).
|
||||
# Static assets stay in the image (/opt/galene/static); the ./data volume must
|
||||
# not shadow them — pass -static explicitly.
|
||||
DATA="${GALENE_DATA:-/data}"
|
||||
STATIC="${GALENE_STATIC:-/opt/galene/static}"
|
||||
# Host/UDP map: VeciGate WAN → koopa 10000-10099/udp (see configs/ports.md).
|
||||
UDP_RANGE="${GALENE_UDP_RANGE:-10000-10099}"
|
||||
mkdir -p "$DATA/groups" "$DATA/recordings" "$DATA/data"
|
||||
cd "$DATA"
|
||||
exec /usr/local/bin/galene \
|
||||
-http "0.0.0.0:8443" \
|
||||
-insecure \
|
||||
-static "$STATIC" \
|
||||
-udp-range "$UDP_RANGE" \
|
||||
-data "$DATA/data" \
|
||||
-groups "$DATA/groups" \
|
||||
-recordings "$DATA/recordings" \
|
||||
"$@"
|
||||
7
configs/galene/groups/hacktivism.json.example
Normal file
7
configs/galene/groups/hacktivism.json.example
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"displayName": "hacktivism",
|
||||
"public": true,
|
||||
"op": [{"username": "op", "password": "changeme"}],
|
||||
"presenter": [{}],
|
||||
"other": []
|
||||
}
|
||||
55
configs/meet/Containerfile
Normal file
55
configs/meet/Containerfile
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# koopa-meet-web — Jitsi Meet web assets on CachyOS
|
||||
# Build: bash ~/koopa-admin-log/scripts/meet/build.sh → localhost/koopa-meet-web:cachyos
|
||||
#
|
||||
# Full Meet needs prosody + jicofo + jvb (see compose.yml). This image serves
|
||||
# the static web UI via nginx from Arch/CachyOS packages + upstream meet tarball.
|
||||
# Signaling/media containers are separate services in the same compose.
|
||||
|
||||
ARG CACHYOS_IMAGE=docker.io/cachyos/cachyos:latest
|
||||
FROM ${CACHYOS_IMAGE}
|
||||
|
||||
ARG JITSI_MEET_VERSION=stable
|
||||
ARG JITSI_MEET_URL=https://download.jitsi.org/jitsi-meet/src/jitsi-meet-1.0.0.tar.bz2
|
||||
|
||||
ENV TZ=Europe/Zurich
|
||||
|
||||
RUN pacman -Syu --noconfirm --needed \
|
||||
bash curl ca-certificates catatonit tar nginx \
|
||||
&& pacman -Scc --noconfirm \
|
||||
&& rm -rf /var/cache/pacman/pkg/* /tmp/*
|
||||
|
||||
# CachyOS/Arch nginx.conf embeds a default server and does NOT include http.d/.
|
||||
# Replace it with a minimal conf that serves the branded landing.
|
||||
RUN mkdir -p /usr/share/jitsi-meet /etc/nginx/http.d \
|
||||
&& printf '%s\n' \
|
||||
'worker_processes 1;' \
|
||||
'events { worker_connections 1024; }' \
|
||||
'http {' \
|
||||
' include mime.types;' \
|
||||
' default_type application/octet-stream;' \
|
||||
' sendfile on;' \
|
||||
' keepalive_timeout 65;' \
|
||||
' server {' \
|
||||
' listen 80 default_server;' \
|
||||
' server_name _;' \
|
||||
' root /usr/share/jitsi-meet;' \
|
||||
' index index.html;' \
|
||||
' location / { try_files $uri $uri/ /index.html; }' \
|
||||
' }' \
|
||||
'}' > /etc/nginx/nginx.conf \
|
||||
&& printf '%s\n' \
|
||||
'<!doctype html><html lang=en><meta charset=utf-8>' \
|
||||
'<meta name=viewport content="width=device-width,initial-scale=1">' \
|
||||
'<title>meet.hacktivism.ch</title>' \
|
||||
'<style>body{font-family:system-ui,sans-serif;max-width:40rem;margin:3rem auto;padding:0 1rem;line-height:1.5}' \
|
||||
'code{background:#f2f2f2;padding:.1rem .35rem;border-radius:4px}</style>' \
|
||||
'<h1>meet.hacktivism.ch</h1>' \
|
||||
'<p>Jitsi Meet <strong>web</strong> (CachyOS). Signaling/media (prosody / jicofo / jvb) is Phase 2 — see compose + README.</p>' \
|
||||
'<p>HTTP OK on host port <code>9031</code>. Full rooms need UDP/JVB after Phase 2.</p>' \
|
||||
> /usr/share/jitsi-meet/index.html
|
||||
|
||||
COPY entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod 755 /docker-entrypoint.sh
|
||||
|
||||
EXPOSE 80
|
||||
ENTRYPOINT ["/usr/bin/catatonit", "--", "/docker-entrypoint.sh"]
|
||||
23
configs/meet/README.md
Normal file
23
configs/meet/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Meet (Jitsi) — CachyOS web stub (koopa)
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Public site | `meet.hacktivism.ch` |
|
||||
| Public port | **9031** (`koopa-paivana-meet`) |
|
||||
| Backend | `127.0.0.1:19131` (`koopa-meet-web`) |
|
||||
| Image (web) | `localhost/koopa-meet-web:cachyos` |
|
||||
| Live dir | `/home/hernani/koopa-meet/` |
|
||||
|
||||
Phase 1 = Caddy vhost + branded nginx landing on CachyOS (`koopa-meet-web`) behind Paivana (GOA:42).
|
||||
Phase 2 = prosody + jicofo + jvb — scaffold in `compose.phase2.yml` (`profiles: [phase2]`); images under `configs/meet/phase2/` still TODO. Not `docker-jitsi-meet` Debian by default.
|
||||
UDP/JVB and TURN need VeciGate once rooms work (range TBD in `ports.md`).
|
||||
|
||||
## Caddy
|
||||
|
||||
Caddy stays on `127.0.0.1:9031` (paywall). App bind is loopback `19131` only.
|
||||
|
||||
```bash
|
||||
sudo bash /path/to/koopa-admin-log/scripts/caddy/caddy-apply.sh \
|
||||
--snippet …/configs/caddy/meet.hacktivism.ch.caddy \
|
||||
--site meet.hacktivism.ch --port 9031 --label meet
|
||||
```
|
||||
49
configs/meet/compose.phase2.yml
Normal file
49
configs/meet/compose.phase2.yml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Phase-2 stubs — NOT started by default.
|
||||
# Usage (after images exist):
|
||||
# cd ~/koopa-meet && podman-compose -f compose.yml -f compose.phase2.yml up -d
|
||||
#
|
||||
# Do not pull official docker-jitsi-meet Debian images as the default base.
|
||||
# Build CachyOS (or pinned binary) images under configs/meet/phase2/ when ready.
|
||||
|
||||
services:
|
||||
prosody:
|
||||
profiles: ["phase2"]
|
||||
image: localhost/koopa-meet-prosody:cachyos
|
||||
container_name: koopa-meet-prosody
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
# ports / volumes: fill when Containerfile exists
|
||||
labels:
|
||||
org.hacktivism.service: meet-prosody
|
||||
org.hacktivism.site: meet.hacktivism.ch
|
||||
org.hacktivism.phase: "2"
|
||||
|
||||
jicofo:
|
||||
profiles: ["phase2"]
|
||||
image: localhost/koopa-meet-jicofo:cachyos
|
||||
container_name: koopa-meet-jicofo
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
depends_on:
|
||||
- prosody
|
||||
labels:
|
||||
org.hacktivism.service: meet-jicofo
|
||||
org.hacktivism.site: meet.hacktivism.ch
|
||||
org.hacktivism.phase: "2"
|
||||
|
||||
jvb:
|
||||
profiles: ["phase2"]
|
||||
image: localhost/koopa-meet-jvb:cachyos
|
||||
container_name: koopa-meet-jvb
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
# UDP host publish TBD in ports.md when allocated
|
||||
depends_on:
|
||||
- prosody
|
||||
labels:
|
||||
org.hacktivism.service: meet-jvb
|
||||
org.hacktivism.site: meet.hacktivism.ch
|
||||
org.hacktivism.phase: "2"
|
||||
26
configs/meet/compose.yml
Normal file
26
configs/meet/compose.yml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# koopa-meet — Jitsi Meet (CachyOS web stub + follow-up signaling/media)
|
||||
# Live: /home/hernani/koopa-meet/
|
||||
# Public :9031 = koopa-paivana-meet (shared paivana image).
|
||||
# Backend loopback only — Paivana DEST → 127.0.0.1:19131
|
||||
#
|
||||
# Next: add prosody / jicofo / jvb services (CachyOS images or pinned binaries).
|
||||
# Official docker-jitsi-meet is Debian-based — do not pull those as default here.
|
||||
services:
|
||||
meet-web:
|
||||
image: localhost/koopa-meet-web:cachyos
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Containerfile
|
||||
container_name: koopa-meet-web
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
ports:
|
||||
- "127.0.0.1:${BACKEND_PORT:-19131}:80"
|
||||
labels:
|
||||
org.hacktivism.service: meet
|
||||
org.hacktivism.backend_port: "${BACKEND_PORT:-19131}"
|
||||
org.hacktivism.site: meet.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.port_status: backend-19131-paywall-9031
|
||||
org.hacktivism.image_base: cachyos
|
||||
3
configs/meet/entrypoint.sh
Executable file
3
configs/meet/entrypoint.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
exec nginx -g 'daemon off;'
|
||||
5
configs/notes/.env.example
Normal file
5
configs/notes/.env.example
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Live on koopa: ~/koopa-silverbullet/.env (not git)
|
||||
# Allocate HOST_PORT only after live `ss` + Freigabe + ports.md update.
|
||||
HOST_PORT=9028
|
||||
# Optional SilverBullet app auth (prefer Caddy basicauth):
|
||||
# SB_USER=hernani:changeme
|
||||
44
configs/notes/Containerfile
Normal file
44
configs/notes/Containerfile
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# koopa-silverbullet — SilverBullet on CachyOS base + server binary
|
||||
# Build on koopa (x86_64):
|
||||
# bash ~/koopa-admin-log/scripts/notes/build.sh
|
||||
# → localhost/koopa-silverbullet:cachyos
|
||||
#
|
||||
# Base: docker.io/cachyos/cachyos (Arch-derived). App binary from upstream
|
||||
# silverbullet-server-linux-x86_64 release (not the Alpine ghcr image).
|
||||
|
||||
ARG CACHYOS_IMAGE=docker.io/cachyos/cachyos:latest
|
||||
FROM ${CACHYOS_IMAGE}
|
||||
|
||||
ARG SB_VERSION=2.10.0
|
||||
ARG SB_ZIP_URL=https://github.com/silverbulletmd/silverbullet/releases/download/${SB_VERSION}/silverbullet-server-linux-x86_64.zip
|
||||
|
||||
ENV SB_HOSTNAME=0.0.0.0 \
|
||||
SB_FOLDER=/space \
|
||||
SB_PORT=3000 \
|
||||
SB_NAME=SilverBullet \
|
||||
TZ=Europe/Zurich \
|
||||
PATH=/usr/local/bin:$PATH
|
||||
|
||||
# catatonit = Arch/CachyOS container init (tini not in repos)
|
||||
RUN pacman -Syu --noconfirm --needed \
|
||||
bash curl git openssh catatonit unzip \
|
||||
&& pacman -Scc --noconfirm \
|
||||
&& rm -rf /var/cache/pacman/pkg/* /tmp/*
|
||||
|
||||
# Fetch + install server binary (build-time; no Deno in image)
|
||||
RUN curl -fsSL "$SB_ZIP_URL" -o /tmp/sb.zip \
|
||||
&& unzip -o /tmp/sb.zip -d /tmp \
|
||||
&& install -m 755 /tmp/silverbullet /silverbullet \
|
||||
&& /silverbullet --version || true \
|
||||
&& rm -rf /tmp/sb.zip /tmp/silverbullet
|
||||
|
||||
COPY entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod 755 /docker-entrypoint.sh \
|
||||
&& mkdir -p /space
|
||||
|
||||
EXPOSE 3000
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
||||
CMD curl -fsS "http://127.0.0.1:${SB_PORT}/.instance" || exit 1
|
||||
|
||||
WORKDIR /space
|
||||
ENTRYPOINT ["/usr/bin/catatonit", "--", "/docker-entrypoint.sh"]
|
||||
65
configs/notes/README.md
Normal file
65
configs/notes/README.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# SilverBullet — SilverBullet on CachyOS (koopa)
|
||||
|
||||
Cross-host markdown notes (magikoopa + clementine). Stack name **SilverBullet** (parked web UI). Product **FreeNotes** = separate native enc notes (not this stack).
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Stack | [SilverBullet](https://silverbullet.md/) server binary |
|
||||
| Image | `localhost/koopa-silverbullet:cachyos` (`FROM docker.io/cachyos/cachyos`) |
|
||||
| Live dir | `/home/hernani/koopa-silverbullet/` |
|
||||
| Container | `koopa-silverbullet` |
|
||||
| Host port | **9028** = Paivana paywall; app backend **127.0.0.1:19128** |
|
||||
| Public site | `silverbullet.hacktivism.ch` |
|
||||
| Auth | Paivana (GOA:42); no Caddy basic_auth |
|
||||
| Forgejo space backup | private `hernani/freenotes` (historical name; SB space only) |
|
||||
|
||||
## Cutover freenotes → silverbullet (koopa)
|
||||
|
||||
Live still may be `~/koopa-freenotes` / CT `koopa-freenotes`. Scripts default dry-run; `--apply` needs Freigabe.
|
||||
|
||||
```bash
|
||||
bash /path/to/koopa-admin-log/scripts/notes/status.sh
|
||||
bash /path/to/koopa-admin-log/scripts/notes/cutover-to-silverbullet.sh
|
||||
# Freigabe:
|
||||
bash /path/to/koopa-admin-log/scripts/notes/cutover-to-silverbullet.sh --apply
|
||||
```
|
||||
|
||||
See `scripts/notes/README.md` for seed + Caddy order. DNS: `silverbullet.hacktivism.ch`.
|
||||
|
||||
## Build + recreate (koopa)
|
||||
|
||||
```bash
|
||||
# clone koopa-admin-log from Forgejo when needed (no permanent ~/ clone)
|
||||
bash /path/to/koopa-admin-log/scripts/notes/build.sh
|
||||
cd ~/koopa-silverbullet
|
||||
podman-compose down
|
||||
podman-compose up -d
|
||||
bash /path/to/koopa-admin-log/scripts/notes/up.sh
|
||||
```
|
||||
|
||||
## Caddy apply (sudo on koopa)
|
||||
|
||||
```bash
|
||||
sudo bash /path/to/koopa-admin-log/scripts/caddy/merge-notes-site.sh
|
||||
# seed from secrets-enc SoT so apply does not rotate PASS
|
||||
sudo NOTES_AUTH_SEED=/tmp/silverbullet-basicauth-seed.txt \
|
||||
bash /path/to/koopa-admin-log/scripts/caddy/apply-notes-basicauth.sh
|
||||
```
|
||||
|
||||
Runtime: `/etc/caddy/secrets/silverbullet-basicauth.txt` (root-only). Never recreate `~/koopa-secrets`.
|
||||
|
||||
## Space SoT (Forgejo)
|
||||
|
||||
Private `hernani/freenotes` — live tree `~/koopa-silverbullet/space/` (git). On-koopa push uses oneshot HTTP token (no hernani SSH key in `~/.ssh`); laptop push via `git@git.hacktivism.ch:hernani/freenotes.git` (ProxyJump).
|
||||
|
||||
## Client apps (hosts)
|
||||
|
||||
Notes-like **system apps** (standalone Brave/Chrome `--app=` window + own profile): `configs/notes/client-apps/`.
|
||||
|
||||
- magikoopa: `~/Applications/SilverBullet.app` — `bash client-apps/install-magikoopa.sh`
|
||||
- clementine (on-box): `silverbullet.desktop` — `bash client-apps/install-clementine.sh`
|
||||
- optional: browser **Install app** (PWA) for Dock identity
|
||||
|
||||
## Plugs (SB)
|
||||
|
||||
`space/_plug/`: `treeview.plug.js`, `silversearch.plug.js` — see `PLUGS.md`.
|
||||
23
configs/notes/client-apps/README.md
Normal file
23
configs/notes/client-apps/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# SilverBullet — system Notes apps (hosts)
|
||||
|
||||
Goal: **like Apple Notes** — a real system app window on each laptop, not a browser bookmark and not SilverBullet plugs.
|
||||
|
||||
| Host | App | Behavior |
|
||||
|------|-----|----------|
|
||||
| magikoopa | `SilverBullet.app` → `~/Applications/` | Brave/Chrome **`--app=`** + own profile (`SilverBullet-BraveProfile`) — no tab bar |
|
||||
| clementine | `silverbullet.desktop` | same `--app=` via Brave/Chromium + `~/.local/share/SilverBullet-browser` |
|
||||
| both (optional) | Browser **Install app** / PWA | even closer to native Dock/menu identity |
|
||||
|
||||
**Not this:** `space/_plug/` (TreeView/SilverSearch) — those are in-editor extensions.
|
||||
|
||||
Install:
|
||||
|
||||
```bash
|
||||
# magikoopa
|
||||
bash configs/notes/client-apps/install-magikoopa.sh
|
||||
|
||||
# clementine (on-box)
|
||||
bash configs/notes/client-apps/install-clementine.sh
|
||||
```
|
||||
|
||||
First launch: log in once (Caddy basicauth); credentials stay in the app profile.
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>SilverBullet</string>
|
||||
<key>CFBundleDisplayName</key><string>SilverBullet</string>
|
||||
<key>CFBundleIdentifier</key><string>ch.hacktivism.silverbullet</string>
|
||||
<key>CFBundleVersion</key><string>1</string>
|
||||
<key>CFBundleShortVersionString</key><string>1.0</string>
|
||||
<key>CFBundleExecutable</key><string>SilverBullet</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key><string>12.0</string>
|
||||
<key>CFBundleIconFile</key><string>AppIcon</string>
|
||||
</dict>
|
||||
</plist>
|
||||
31
configs/notes/client-apps/SilverBullet.app/Contents/MacOS/SilverBullet
Executable file
31
configs/notes/client-apps/SilverBullet.app/Contents/MacOS/SilverBullet
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
# SilverBullet — Notes-like system app (standalone window, not a browser tab)
|
||||
URL="https://silverbullet.hacktivism.ch/"
|
||||
USER_DATA="${HOME}/Library/Application Support/SilverBullet-BraveProfile"
|
||||
mkdir -p "$USER_DATA"
|
||||
|
||||
BRAVE="/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
|
||||
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
|
||||
# Dedicated Chromium profile so this feels like its own app (Dock, cookies, no tab chrome)
|
||||
if [[ -x "$BRAVE" ]]; then
|
||||
exec "$BRAVE" \
|
||||
--user-data-dir="$USER_DATA" \
|
||||
--class=SilverBullet \
|
||||
--app="$URL" \
|
||||
--name=SilverBullet
|
||||
elif [[ -x "$CHROME" ]]; then
|
||||
exec "$CHROME" \
|
||||
--user-data-dir="$USER_DATA" \
|
||||
--class=SilverBullet \
|
||||
--app="$URL" \
|
||||
--name=SilverBullet
|
||||
else
|
||||
# last resort: Safari has no --app; open dedicated window via osascript
|
||||
exec osascript <<'OSA'
|
||||
tell application "Safari"
|
||||
activate
|
||||
make new document with properties {URL:"https://silverbullet.hacktivism.ch/"}
|
||||
end tell
|
||||
OSA
|
||||
fi
|
||||
13
configs/notes/client-apps/install-clementine.sh
Executable file
13
configs/notes/client-apps/install-clementine.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
# Install SilverBullet XFCE launcher on clementine (on-box). No Freigabe (user home).
|
||||
set -euo pipefail
|
||||
SRC="$(cd "$(dirname "$0")" && pwd)/silverbullet.desktop"
|
||||
APPDIR="${HOME}/.local/share/applications"
|
||||
DESK="${HOME}/Desktop"
|
||||
mkdir -p "$APPDIR" "$DESK"
|
||||
install -m 0644 "$SRC" "$APPDIR/silverbullet.desktop"
|
||||
install -m 0644 "$SRC" "$DESK/silverbullet.desktop"
|
||||
# refresh menu cache if available
|
||||
command -v update-desktop-database >/dev/null && update-desktop-database "$APPDIR" || true
|
||||
echo "STATUS=ok DESKTOP=$APPDIR/silverbullet.desktop SHORTCUT=$DESK/silverbullet.desktop"
|
||||
echo "TIP=open https://silverbullet.hacktivism.ch then browser Install app for PWA"
|
||||
34
configs/notes/client-apps/install-magikoopa.sh
Executable file
34
configs/notes/client-apps/install-magikoopa.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env bash
|
||||
# Install SilverBullet.app into ~/Applications (magikoopa). No Freigabe (user home).
|
||||
set -euo pipefail
|
||||
SRC="$(cd "$(dirname "$0")" && pwd)/SilverBullet.app"
|
||||
DEST="${HOME}/Applications/SilverBullet.app"
|
||||
URL="${HOME}/Desktop/SilverBullet.url"
|
||||
mkdir -p "${HOME}/Applications" "${HOME}/Desktop"
|
||||
if [[ -d "$SRC" ]]; then
|
||||
rm -rf "$DEST"
|
||||
cp -R "$SRC" "$DEST"
|
||||
else
|
||||
# bootstrap minimal app if scaffold app tree missing
|
||||
mkdir -p "$DEST/Contents/MacOS" "$DEST/Contents/Resources"
|
||||
cat >"$DEST/Contents/Info.plist" <<'PLIST'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>SilverBullet</string>
|
||||
<key>CFBundleDisplayName</key><string>SilverBullet</string>
|
||||
<key>CFBundleIdentifier</key><string>ch.hacktivism.silverbullet</string>
|
||||
<key>CFBundleVersion</key><string>1</string>
|
||||
<key>CFBundleShortVersionString</key><string>1.0</string>
|
||||
<key>CFBundleExecutable</key><string>SilverBullet</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key><string>12.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
printf '%s\n' '#!/bin/bash' 'open "https://silverbullet.hacktivism.ch/"' >"$DEST/Contents/MacOS/SilverBullet"
|
||||
chmod +x "$DEST/Contents/MacOS/SilverBullet"
|
||||
fi
|
||||
printf '%s\n' '[InternetShortcut]' 'URL=https://silverbullet.hacktivism.ch/' >"$URL"
|
||||
echo "STATUS=ok APP=$DEST URL=$URL"
|
||||
12
configs/notes/client-apps/silverbullet.desktop
Normal file
12
configs/notes/client-apps/silverbullet.desktop
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=SilverBullet
|
||||
Comment=Notes-like SilverBullet on koopa — standalone window
|
||||
TryExec=brave-browser
|
||||
Exec=sh -c 'B=""; for c in brave-browser brave chromium chromium-browser google-chrome; do command -v "$c" >/dev/null && B="$c" && break; done; if [ -n "$B" ]; then exec "$B" --user-data-dir="$HOME/.local/share/SilverBullet-browser" --class=SilverBullet --app=https://silverbullet.hacktivism.ch/; else exec xdg-open https://silverbullet.hacktivism.ch/; fi'
|
||||
Terminal=false
|
||||
Categories=Office;Network;
|
||||
Keywords=notes;todo;silverbullet;
|
||||
StartupWMClass=SilverBullet
|
||||
StartupNotify=true
|
||||
38
configs/notes/compose.yml
Normal file
38
configs/notes/compose.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# koopa-silverbullet — SilverBullet on CachyOS base (parked; FreeNotes = native enc)
|
||||
# Live: /home/hernani/koopa-silverbullet/
|
||||
# Build: bash ~/koopa-admin-log/scripts/notes/build.sh → localhost/koopa-silverbullet:cachyos
|
||||
#
|
||||
# Public :9028 = koopa-paivana-silverbullet (shared paivana image).
|
||||
# Backend loopback only — Paivana DEST → 127.0.0.1:19128
|
||||
services:
|
||||
notes:
|
||||
image: localhost/koopa-silverbullet:cachyos
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Containerfile
|
||||
args:
|
||||
SB_VERSION: "${SB_VERSION:-2.10.0}"
|
||||
container_name: koopa-silverbullet
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=Europe/Zurich
|
||||
- SB_INDEX_PAGE=index
|
||||
- SB_NAME=SilverBullet
|
||||
- SB_SHELL_BACKEND=off
|
||||
# Optional app-layer auth (prefer Caddy basicauth; leave empty in .env to skip):
|
||||
# - SB_USER=${SB_USER}
|
||||
volumes:
|
||||
- ./space:/space
|
||||
ports:
|
||||
- "127.0.0.1:${BACKEND_PORT:-19128}:3000"
|
||||
labels:
|
||||
org.hacktivism.service: silverbullet
|
||||
org.hacktivism.host_port: "9028"
|
||||
org.hacktivism.backend_port: "${BACKEND_PORT:-19128}"
|
||||
org.hacktivism.site: silverbullet.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.port_status: backend-19128-paywall-9028
|
||||
org.hacktivism.image_base: cachyos
|
||||
|
||||
28
configs/notes/container-koopa-silverbullet.service
Normal file
28
configs/notes/container-koopa-silverbullet.service
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# container-koopa-silverbullet.service
|
||||
# Install on koopa as hernani (after compose create):
|
||||
# mkdir -p ~/.config/systemd/user
|
||||
# cp configs/notes/container-koopa-silverbullet.service ~/.config/systemd/user/
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable --now container-koopa-silverbullet.service
|
||||
# Requires: loginctl enable-linger hernani (root, once)
|
||||
#
|
||||
# Container must already exist (podman-compose up -d once).
|
||||
|
||||
[Unit]
|
||||
Description=Podman container-koopa-silverbullet.service (SilverBullet)
|
||||
Documentation=https://silverbullet.md/
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
RequiresMountsFor=/run/user/1000/containers
|
||||
|
||||
[Service]
|
||||
Environment=PODMAN_SYSTEMD_UNIT=%n
|
||||
Restart=always
|
||||
TimeoutStopSec=90
|
||||
ExecStart=/usr/bin/podman start koopa-silverbullet
|
||||
ExecStop=/usr/bin/podman stop -t 20 koopa-silverbullet
|
||||
ExecStopPost=/usr/bin/podman stop -t 20 koopa-silverbullet
|
||||
Type=forking
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
36
configs/notes/entrypoint.sh
Executable file
36
configs/notes/entrypoint.sh
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/bash
|
||||
# SilverBullet entrypoint (CachyOS/Arch user tools).
|
||||
# Adapted from upstream docker-entrypoint.sh (busybox → shadow useradd).
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -f /space/CONTAINER_BOOT.md ]]; then
|
||||
echo "Executing CONTAINER_BOOT.md script"
|
||||
bash /space/CONTAINER_BOOT.md &
|
||||
fi
|
||||
|
||||
SB_FOLDER="${SB_FOLDER:-/space}"
|
||||
|
||||
if [[ -z "${PUID:-}" && "$(id -u)" == "0" ]]; then
|
||||
PUID=$(stat -c "%u" "$SB_FOLDER")
|
||||
echo "Will run SilverBullet with UID $PUID (from owner of $SB_FOLDER)"
|
||||
fi
|
||||
if [[ -z "${PGID:-}" ]]; then
|
||||
PGID=$(stat -c "%g" "$SB_FOLDER")
|
||||
fi
|
||||
|
||||
if [[ "${PUID:-0}" == "0" || "$(id -u)" != "0" ]]; then
|
||||
exec /silverbullet "$@"
|
||||
fi
|
||||
|
||||
echo "Creating silverbullet user UID=$PUID GID=$PGID"
|
||||
getent group silverbullet >/dev/null 2>&1 || groupadd -g "$PGID" silverbullet || groupadd -o -g "$PGID" silverbullet
|
||||
getent passwd silverbullet >/dev/null 2>&1 || useradd -M -N -g silverbullet -u "$PUID" -d "$SB_FOLDER" -s /bin/bash silverbullet \
|
||||
|| useradd -o -M -N -g silverbullet -u "$PUID" -d "$SB_FOLDER" -s /bin/bash silverbullet
|
||||
|
||||
echo "Running SilverBullet as PUID=$PUID PGID=$PGID"
|
||||
# quote-safe: pass args via "$@" under su -c is awkward; use runuser when present
|
||||
if command -v runuser >/dev/null 2>&1; then
|
||||
exec runuser -u silverbullet -- /silverbullet "$@"
|
||||
fi
|
||||
args=$(printf '%q ' "$@")
|
||||
exec su silverbullet -s /bin/bash -c "/silverbullet $args"
|
||||
12
configs/notes/space/PLUGS.md
Normal file
12
configs/notes/space/PLUGS.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Plugs (apps)
|
||||
|
||||
Third-party SilverBullet plugs in (loaded automatically).
|
||||
|
||||
| Plug | Role |
|
||||
|------|------|
|
||||
| treeview.plug.js | Folder sidebar (Apple Notes-like) |
|
||||
| silversearch.plug.js | Full-text search |
|
||||
|
||||
Install/update: drop into , reload SilverBullet.
|
||||
|
||||
Builtin SB plugs stay in the image; only extras live here.
|
||||
31
configs/notes/space/TODO.md
Normal file
31
configs/notes/space/TODO.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# TODO
|
||||
|
||||
Canonical ADHD todo (SilverBullet space SoT; FreeNotes = separate native enc).
|
||||
|
||||
## Now (max 3) — active quest board
|
||||
- [x] magikoopa SilverBullet.app (`~/Applications`) + Desktop SilverBullet.url
|
||||
- [ ] Install silverbullet.desktop on clementine (on-box: client-apps/install-clementine.sh)
|
||||
- [ ] One-shot migrate: Apple Notes export + Dokumente/TODO.md → here (or FreeNotes later)
|
||||
- [ ] Human sudo: confirm `sudo ls -la /etc/caddy/secrets/silverbullet-basicauth.txt`
|
||||
- [ ] Live cutover Freigabe: dir/container → koopa-silverbullet; Caddy → silverbullet.hacktivism.ch; DNS
|
||||
|
||||
## Next
|
||||
- [x] Private Forgejo backup remote for space/ (hernani/freenotes — historical name)
|
||||
- [x] Seed notes space on koopa
|
||||
- [x] Caddy merge + basicauth scripts (live cutover pending Freigabe)
|
||||
- [ ] DNS / public hostname silverbullet.hacktivism.ch (human)
|
||||
- [x] Update productivity-adhd-notes 02 / 02b → single SoT pointing here
|
||||
- [x] Wire magikoopa SSH push to hernani/freenotes (ProxyJump) — clone ~/git/freenotes
|
||||
|
||||
## Someday / amnesty
|
||||
- [ ] PWA install both laptops (browser Install app)
|
||||
- [ ] Second-layer SB_USER if basicauth alone is not enough
|
||||
- [ ] TreeView / SilverSearch UX polish after first week
|
||||
|
||||
## Stats (gamification — agent updates)
|
||||
- Session streak: 1
|
||||
- Quests finished today: 6
|
||||
- XP today: 60
|
||||
- Last win: rename stack freenotes → silverbullet
|
||||
- Boss: silverbullet cutover
|
||||
- Boss HP: 70%
|
||||
21
configs/notes/space/_plug/silversearch.plug.js
Normal file
21
configs/notes/space/_plug/silversearch.plug.js
Normal file
File diff suppressed because one or more lines are too long
39
configs/notes/space/_plug/treeview.plug.js
Normal file
39
configs/notes/space/_plug/treeview.plug.js
Normal file
File diff suppressed because one or more lines are too long
13
configs/notes/space/inbox.md
Normal file
13
configs/notes/space/inbox.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Inbox
|
||||
|
||||
Apple **Quick Note** analogue. Dump here first; triage into [[TODO]] (Now / Next / Waiting / Someday) or a dedicated page.
|
||||
|
||||
Rules:
|
||||
|
||||
- Empty capture OK — fill the next `- [ ]` line and save.
|
||||
- Do not create a new folder just to capture.
|
||||
- After triage, clear or check off lines so Inbox stays short.
|
||||
|
||||
## Capture
|
||||
|
||||
- [ ]
|
||||
46
configs/notes/space/index.md
Normal file
46
configs/notes/space/index.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
# SilverBullet index page (SB_INDEX_PAGE=index)
|
||||
# Apple-Notes-like home: pins + capture path + search
|
||||
---
|
||||
|
||||
# Notes
|
||||
|
||||
Cross-host notes (magikoopa + clementine) via koopa SilverBullet.
|
||||
Mental model = Apple Notes: **folders/pages · list · editor**, pin primary TODO, Quick Note → triage.
|
||||
|
||||
| Page | Apple analogue | Role |
|
||||
|------|----------------|------|
|
||||
| [[TODO]] | Pinned primary note | ADHD SoT: Now≤3 / Next / Waiting / Someday / Stats |
|
||||
| [[inbox]] | Quick Note | Capture before triage |
|
||||
|
||||
## Pins
|
||||
|
||||
- [[TODO]]
|
||||
- [[inbox]]
|
||||
|
||||
## How to use (Apple habits → here)
|
||||
|
||||
1. **Capture** into [[inbox]] (or SB quick-note if enabled) — do not invent folders first.
|
||||
2. **Triage** into [[TODO]] sections (Now max 3). Same rules as former Apple Notes guide.
|
||||
3. **Find** with Page Picker / search (**Cmd/Ctrl-K**).
|
||||
4. **New page** when a topic is not a todo (meetings, dumps) — link from TODO by title, do not dump transcripts into TODO.
|
||||
5. Optional later: TreeView plug (folder sidebar), Space Lua query page = “smart folder” for `#tag` open tasks.
|
||||
|
||||
## Shortcuts (target)
|
||||
|
||||
| Action | Typical SB |
|
||||
|--------|------------|
|
||||
| Open page / filter | Cmd/Ctrl-K (Page Picker) |
|
||||
| Quick note | Ctrl-q (if client supports) → prefer landing in [[inbox]] |
|
||||
| Search body | built-in search / SilverSearch plug |
|
||||
|
||||
## Hygiene (from Apple Notes ADHD pack)
|
||||
|
||||
- One primary [[TODO]] — no competing systems.
|
||||
- Capture first; tags (`#taler` …) optional and light.
|
||||
- On “I’m back”: re-read **Now** only.
|
||||
- No passwords/tokens in notes.
|
||||
|
||||
## Apps
|
||||
|
||||
- [[PLUGS]] — TreeView + SilverSearch
|
||||
|
|
@ -2,27 +2,32 @@
|
|||
|
||||
GNU Taler **paivana-httpd** reverse-proxy paywall (DD 95 / DD 76 style), GOA payments.
|
||||
|
||||
One process = one `BASE_URL` + one `DESTINATION_BASE_URL` + one `MERCHANT_BACKEND_URL`.
|
||||
Shared **image / secrets / entrypoint / conf template**; per-service env sets
|
||||
`PAIVANA_BASE_URL` + `PAIVANA_DESTINATION_BASE_URL`. Portal on **9025**; app
|
||||
paywalls on **9028–9031** (Caddy unchanged). App backends listen loopback only
|
||||
(`19128` SilverBullet, `19129` Galene HTTP, `19130` CryptPad edge, `19131` Meet).
|
||||
|
||||
| Item | Value |
|
||||
|------|--------|
|
||||
| Live | `/home/hernani/koopa-paivana/` |
|
||||
| Containers | `koopa-paivana`, `koopa-paivana-upstream` |
|
||||
| Containers | `koopa-paivana`, `koopa-paivana-{silverbullet,galene,cryptpad,meet}`, `koopa-paivana-upstream` |
|
||||
| Image | `localhost/koopa-paivana:latest` (built from `Containerfile`); process **uid 1000** `paivana-httpd` |
|
||||
| Upstream | `nginxinc/nginx-unprivileged` on **:8080** (not root nginx:80) |
|
||||
| Host port | **9025** → Caddy `paivana.hacktivism.ch` |
|
||||
| Currency | **GOA** |
|
||||
| Upstream | portal: `nginxinc/nginx-unprivileged` on **:8080**; apps via host `172.17.0.1:19128–19131` |
|
||||
| Host ports | **9025** portal; **9028/9029/9030/9031** app paywalls → Caddy |
|
||||
| Currency | **GOA** (goa-shop); other units need other merchant backends / later instances |
|
||||
| Merchant | `https://taler.hacktivism.ch/instances/goa-shop/` |
|
||||
| Template | `paivana` (`template_type: paivana`, amount **`GOA:4200`**, `website_regex: .*`) |
|
||||
| Access | **`-g`** site-wide: pay once → example upstream free to browse |
|
||||
| Upstream | example site in `upstream/` (compose service) |
|
||||
| Template | `paivana` (`template_type: paivana`, amount **`GOA:42`**, `website_regex: .*`) |
|
||||
| Access | **`-g`** site-wide: pay once → that hostname free to browse |
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | Role |
|
||||
|------|------|
|
||||
| `Containerfile` | Multi-stage: build paivana-httpd from `git.taler.net/paivana` + Taler libs |
|
||||
| `compose.yml` | `koopa-paivana` + nginx upstream |
|
||||
| `conf/paivana.conf.template` | Config; secrets substituted at start |
|
||||
| `entrypoint.sh` | Inject secrets → run `paivana-httpd -g -f` |
|
||||
| `compose.yml` | portal + 4 app paywalls + nginx upstream (YAML anchor `x-paivana-common`) |
|
||||
| `conf/paivana.conf.template` | Shared conf; `@BASE_URL@` / `@DESTINATION_BASE_URL@` / secrets at start |
|
||||
| `entrypoint.sh` | Env + secrets → run `paivana-httpd -g -f` |
|
||||
| `secrets/` | **not in git** — live only |
|
||||
| `upstream/` | Protected demo content |
|
||||
| `container-koopa-paivana.service` | systemd --user oneshot compose up |
|
||||
|
|
@ -90,9 +95,10 @@ extra_hosts:
|
|||
| Check | Expect |
|
||||
|-------|--------|
|
||||
| `curl -si http://127.0.0.1:9025/` | **302** → `/.well-known/paivana/templates/paivana#…` |
|
||||
| `https://paivana.hacktivism.ch/` | **402** paywall HTML + `paivana: taler://pay-template/…` |
|
||||
| `curl -si http://127.0.0.1:902{8,9,0}/` | **302** same template path (per-host BASE_URL) |
|
||||
| `https://{paivana,silverbullet,galene,cryptpad,meet}.hacktivism.ch/` | unpaid → **302** `…/templates/paivana#…` (or paywall HTML) |
|
||||
| monitoring `urls` | `www.paivana` OK |
|
||||
| e2e (GOA, lokal) | `e2e.paivana` Template-Pay **GOA:4200** (wenn `E2E_PAIVANA=1`) |
|
||||
| e2e (GOA, lokal) | `e2e.paivana` Template-Pay **GOA:42** (wenn `E2E_PAIVANA=1`) |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -137,22 +143,27 @@ Flags `-g` (site-wide payment) and `-f` (X-Forwarded-For).
|
|||
|
||||
## Merchant template
|
||||
|
||||
Create (or replace via PATCH / DELETE+POST — see live merchant API):
|
||||
|
||||
```bash
|
||||
curl -X POST 'https://taler.hacktivism.ch/instances/goa-shop/private/templates' \
|
||||
-H "Authorization: Bearer secret-token:…" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"template_id": "paivana",
|
||||
"template_description": "Paivana paywall (GOA)",
|
||||
"template_description": "Paivana paywall portal (GOA)",
|
||||
"template_contract": {
|
||||
"template_type": "paivana",
|
||||
"summary": "Access to paivana.hacktivism.ch",
|
||||
"summary": "Access to paivana.hacktivism.ch portal",
|
||||
"website_regex": ".*",
|
||||
"choices": [{ "amount": "GOA:4200", "description": "Unlock example site (then free to browse)" }]
|
||||
"choices": [{ "amount": "GOA:42", "description": "Unlock portal (cryptpad / galene / silverbullet links)" }]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
goa-shop is GOA-only. TESTPAYSAN / FLOODS / talersticker CHF / stage-CHF need
|
||||
other `MERCHANT_BACKEND_URL` values (separate Paivana process or later phase).
|
||||
|
||||
## Docs
|
||||
|
||||
- https://docs.taler.net/taler-paivana-manual.html
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
# koopa-paivana — host :9025 → Caddy paivana.hacktivism.ch
|
||||
# koopa-paivana — shared image/secrets/entrypoint for portal + app paywalls
|
||||
# Live: /home/hernani/koopa-paivana/
|
||||
services:
|
||||
paivana:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Containerfile
|
||||
#
|
||||
# Public Caddy targets (unchanged):
|
||||
# 9025 → portal (paivana.hacktivism.ch)
|
||||
# 9028 → paywall silverbullet
|
||||
# 9029 → paywall galene
|
||||
# 9030 → paywall cryptpad
|
||||
# 9031 → paywall meet (Jitsi web)
|
||||
# App backends (loopback only; see app compose):
|
||||
# 127.0.0.1:19128 / 19129 / 19130 / 19131
|
||||
|
||||
x-paivana-common: &paivana-common
|
||||
image: localhost/koopa-paivana:latest
|
||||
container_name: koopa-paivana
|
||||
restart: unless-stopped
|
||||
# Image already USER paivana-httpd; pin numeric + keep-id for host secrets/volumes.
|
||||
user: "1000:1000"
|
||||
userns_mode: keep-id
|
||||
ports:
|
||||
- "9025:9967"
|
||||
extra_hosts:
|
||||
# pasta publishes merchant on host :9010. Prefer fixed IPv4 — host-gateway
|
||||
# alone still loses to public AAAA and times out from the container.
|
||||
- "taler.hacktivism.ch:172.17.0.1"
|
||||
- "bank.hacktivism.ch:172.17.0.1"
|
||||
- "exchange.hacktivism.ch:172.17.0.1"
|
||||
|
|
@ -24,6 +24,22 @@ services:
|
|||
- ./entrypoint.sh:/usr/local/bin/paivana-entrypoint:ro
|
||||
- ./secrets/merchant-access-token:/run/secrets/merchant-access-token:ro
|
||||
- ./secrets/paivana-secret:/run/secrets/paivana-secret:ro
|
||||
labels:
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.currency: GOA
|
||||
|
||||
services:
|
||||
paivana:
|
||||
<<: *paivana-common
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Containerfile
|
||||
container_name: koopa-paivana
|
||||
ports:
|
||||
- "9025:9967"
|
||||
environment:
|
||||
PAIVANA_BASE_URL: https://paivana.hacktivism.ch/
|
||||
PAIVANA_DESTINATION_BASE_URL: http://upstream:8080/
|
||||
depends_on:
|
||||
- upstream
|
||||
labels:
|
||||
|
|
@ -33,8 +49,68 @@ services:
|
|||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.currency: GOA
|
||||
|
||||
# App paywalls: same image/secrets; BASE/DEST via env. Caddy still hits 9028–9031.
|
||||
paivana-silverbullet:
|
||||
<<: *paivana-common
|
||||
container_name: koopa-paivana-silverbullet
|
||||
ports:
|
||||
- "9028:9967"
|
||||
environment:
|
||||
PAIVANA_BASE_URL: https://silverbullet.hacktivism.ch/
|
||||
PAIVANA_DESTINATION_BASE_URL: http://172.17.0.1:19128/
|
||||
labels:
|
||||
org.hacktivism.service: paivana-silverbullet
|
||||
org.hacktivism.host_port: "9028"
|
||||
org.hacktivism.site: silverbullet.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.currency: GOA
|
||||
|
||||
paivana-galene:
|
||||
<<: *paivana-common
|
||||
container_name: koopa-paivana-galene
|
||||
ports:
|
||||
- "9029:9967"
|
||||
environment:
|
||||
PAIVANA_BASE_URL: https://galene.hacktivism.ch/
|
||||
PAIVANA_DESTINATION_BASE_URL: http://172.17.0.1:19129/
|
||||
labels:
|
||||
org.hacktivism.service: paivana-galene
|
||||
org.hacktivism.host_port: "9029"
|
||||
org.hacktivism.site: galene.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.currency: GOA
|
||||
|
||||
paivana-cryptpad:
|
||||
<<: *paivana-common
|
||||
container_name: koopa-paivana-cryptpad
|
||||
ports:
|
||||
- "9030:9967"
|
||||
environment:
|
||||
PAIVANA_BASE_URL: https://cryptpad.hacktivism.ch/
|
||||
PAIVANA_DESTINATION_BASE_URL: http://172.17.0.1:19130/
|
||||
labels:
|
||||
org.hacktivism.service: paivana-cryptpad
|
||||
org.hacktivism.host_port: "9030"
|
||||
org.hacktivism.site: cryptpad.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.currency: GOA
|
||||
|
||||
paivana-meet:
|
||||
<<: *paivana-common
|
||||
container_name: koopa-paivana-meet
|
||||
ports:
|
||||
- "9031:9967"
|
||||
environment:
|
||||
PAIVANA_BASE_URL: https://meet.hacktivism.ch/
|
||||
PAIVANA_DESTINATION_BASE_URL: http://172.17.0.1:19131/
|
||||
labels:
|
||||
org.hacktivism.service: paivana-meet
|
||||
org.hacktivism.host_port: "9031"
|
||||
org.hacktivism.site: meet.hacktivism.ch
|
||||
org.hacktivism.managed_by: koopa-admin
|
||||
org.hacktivism.currency: GOA
|
||||
|
||||
upstream:
|
||||
# Non-root nginx (listens on 8080). See DESTINATION_BASE_URL in conf template.
|
||||
image: docker.io/nginxinc/nginx-unprivileged:1.27-alpine
|
||||
container_name: koopa-paivana-upstream
|
||||
restart: unless-stopped
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
# paivana.hacktivism.ch — paywall reverse proxy
|
||||
# Secrets injected by entrypoint from /run/secrets/
|
||||
# Shared paivana-httpd conf — BASE/DEST injected by entrypoint (env).
|
||||
# Secrets injected from /run/secrets/
|
||||
|
||||
[paivana]
|
||||
SERVE = tcp
|
||||
PORT = 9967
|
||||
BIND_TO = 0.0.0.0
|
||||
|
||||
BASE_URL = https://paivana.hacktivism.ch/
|
||||
BASE_URL = @BASE_URL@
|
||||
|
||||
DESTINATION_BASE_URL = http://upstream:8080/
|
||||
DESTINATION_BASE_URL = @DESTINATION_BASE_URL@
|
||||
|
||||
MERCHANT_BACKEND_URL = https://taler.hacktivism.ch/instances/goa-shop/
|
||||
MERCHANT_ACCESS_TOKEN = @MERCHANT_ACCESS_TOKEN@
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ SECRET_FILE="${PAIVANA_SECRET_FILE:-/run/secrets/paivana-secret}"
|
|||
TEMPLATE="${PAIVANA_CONF_TEMPLATE:-/etc/paivana/paivana.conf.template}"
|
||||
OUT="${PAIVANA_CONF:-/tmp/paivana.conf}"
|
||||
|
||||
# Per-instance (portal + app paywalls share this entrypoint/image).
|
||||
BASE_URL="${PAIVANA_BASE_URL:-https://paivana.hacktivism.ch/}"
|
||||
DEST_URL="${PAIVANA_DESTINATION_BASE_URL:-http://upstream:8080/}"
|
||||
|
||||
if [ ! -r "$TOKEN_FILE" ]; then
|
||||
echo "missing merchant token: $TOKEN_FILE" >&2
|
||||
exit 1
|
||||
|
|
@ -17,10 +21,12 @@ fi
|
|||
TOKEN=$(tr -d '\n\r' < "$TOKEN_FILE")
|
||||
SECRET=$(tr -d '\n\r' < "$SECRET_FILE")
|
||||
|
||||
awk -v t="$TOKEN" -v s="$SECRET" '
|
||||
awk -v t="$TOKEN" -v s="$SECRET" -v b="$BASE_URL" -v d="$DEST_URL" '
|
||||
{
|
||||
gsub(/@MERCHANT_ACCESS_TOKEN@/, t)
|
||||
gsub(/@PAIVANA_SECRET@/, s)
|
||||
gsub(/@BASE_URL@/, b)
|
||||
gsub(/@DESTINATION_BASE_URL@/, d)
|
||||
print
|
||||
}
|
||||
' "$TEMPLATE" > "$OUT"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>Example site — free after GOA:4200</title>
|
||||
<title>hacktivism tools — unlocked</title>
|
||||
<style>
|
||||
:root { color-scheme: light dark; }
|
||||
body {
|
||||
|
|
@ -26,29 +26,41 @@
|
|||
.price { font-variant-numeric: tabular-nums; }
|
||||
code { background: color-mix(in srgb, CanvasText 12%, transparent); padding: 0.1em 0.35em; border-radius: 3px; }
|
||||
.card {
|
||||
margin-top: 1.5rem;
|
||||
margin-top: 1.25rem;
|
||||
padding: 1rem 1.1rem;
|
||||
border: 1px solid color-mix(in srgb, CanvasText 18%, transparent);
|
||||
border-radius: 10px;
|
||||
}
|
||||
ul { padding-left: 1.2rem; }
|
||||
a { color: inherit; }
|
||||
.sites a {
|
||||
display: block;
|
||||
margin: 0.45rem 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<span class="badge">unlocked</span>
|
||||
<h1>Example site</h1>
|
||||
<h1>hacktivism tools</h1>
|
||||
<p>
|
||||
You paid <strong class="price">GOA:4200</strong> once via GNU Taler / Paivana.
|
||||
This whole example site is now free for you to browse (site-wide access cookie).
|
||||
You paid once via GNU Taler / Paivana
|
||||
(<strong class="price">GOA:42</strong> on this merchant).
|
||||
Site-wide access cookie is set for <code>paivana.hacktivism.ch</code>.
|
||||
</p>
|
||||
<div class="card sites">
|
||||
<p><strong>Sites</strong></p>
|
||||
<a href="https://cryptpad.hacktivism.ch/">cryptpad.hacktivism.ch</a>
|
||||
<a href="https://galene.hacktivism.ch/">galene.hacktivism.ch</a>
|
||||
<a href="https://silverbullet.hacktivism.ch/">silverbullet.hacktivism.ch</a>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p><strong>What this demonstrates</strong></p>
|
||||
<p><strong>Notes</strong></p>
|
||||
<ul>
|
||||
<li>Paywall: <code>paivana-httpd</code> in front of a normal upstream HTTP site</li>
|
||||
<li>Currency: exploration money <code>GOA</code> on exchange.hacktivism.ch</li>
|
||||
<li>After payment: no further charge for pages behind this proxy</li>
|
||||
<li>One Paivana process = one <code>DESTINATION_BASE_URL</code> → this portal is the gated upstream.</li>
|
||||
<li>Merchant: <code>goa-shop</code> · template <code>paivana</code>.</li>
|
||||
<li>Host: <code>https://paivana.hacktivism.ch/</code></li>
|
||||
</ul>
|
||||
<p style="margin-bottom:0">Host: <code>https://paivana.hacktivism.ch/</code></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -16,16 +16,27 @@
|
|||
| 9022 | podman `koopa-prime` (Jellyfin) → Caddy `prime.hacktivism.ch` |
|
||||
| 9023 | podman qBittorrent → Caddy `bt.hacktivism.ch` |
|
||||
| **9024** | podman **`koopa-forgejo`** (HTTP) → Caddy **`git.hacktivism.ch`** |
|
||||
| **9025** | podman **`koopa-paivana`** (paivana-httpd) → Caddy **`paivana.hacktivism.ch`** |
|
||||
| **9025** | podman **`koopa-paivana`** (portal paywall) → Caddy **`paivana.hacktivism.ch`** |
|
||||
| **9026** | podman **`koopa-lemmy-proxy`** → Caddy **`lemmy.hacktivism.ch`** |
|
||||
| **9027** | podman **`koopa-decidim`** → Caddy **`decidim.hacktivism.ch`** |
|
||||
| **9028** | podman **`koopa-paivana-silverbullet`** → Caddy **`silverbullet.hacktivism.ch`** (app backend `127.0.0.1:19128`) |
|
||||
| **9029** | podman **`koopa-paivana-galene`** → Caddy **`galene.hacktivism.ch`** (app backend `127.0.0.1:19129`; UDP 10000–10099 still Galene) |
|
||||
| **9030** | podman **`koopa-paivana-cryptpad`** → Caddy **`cryptpad.hacktivism.ch`** (edge backend `127.0.0.1:19130`) |
|
||||
| **9031** | podman **`koopa-paivana-meet`** → Caddy **`meet.hacktivism.ch`** (app backend `127.0.0.1:19131`) |
|
||||
| **9032** | podman **`koopa-bbb-front`** (Ubuntu) → Caddy **`bbb.hacktivism.ch`** |
|
||||
| **9200** | podman **Forgejo git-SSH** (host-direct; not Caddy) |
|
||||
| 9090–9092 | podman `koopa-tops-ng1` … `ng3` → Caddy `tops.ng{1,2,3}.hacktivism.ch` |
|
||||
| 8080 | Tor ORPort |
|
||||
|
||||
VeciGate: WAN **80→9000**, WAN **443→9001**.
|
||||
Public apps: Caddy vhosts on **9001** → 127.0.0.1:{9010–9015, 9020–9027, 9090–9092}.
|
||||
Public apps: Caddy vhosts on **9001** → 127.0.0.1:{9010–9015, 9020–9032, 9090–9092}.
|
||||
Git SSH needs separate NAT/firewall **9200/tcp** if exposed to WAN.
|
||||
Meet/Galene/BBB media UDP is separate from these HTTP ports.
|
||||
|
||||
| UDP range | Role |
|
||||
|-----------|------|
|
||||
| **10000–10099/udp** | **Galene** WebRTC (host publish + planned VeciGate DNAT → koopa) |
|
||||
| (TBD) | Meet JVB / BBB media — allocate when Phase‑2 / full BBB leaves park |
|
||||
|
||||
| **1789** | podman **`koopa-nym`** mixnet (nym.com nym-node) |
|
||||
| **1790** | podman **`koopa-nym`** verloc |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Internal only (not in the browser URL):
|
||||
# 9010 merchant API | 9011 exchange API | 9012 bank API
|
||||
# 9013 bank landing | 9014 exchange landing | 9015 merchant landing
|
||||
# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9200 forgejo-ssh
|
||||
# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9029 galene | 9030 cryptpad | 9031 meet | 9032 bbb | 9200 forgejo-ssh
|
||||
# 9090 tops ng1 | 9091 tops ng2 | 9092 tops ng3
|
||||
|
||||
{
|
||||
|
|
@ -37,22 +37,85 @@ taler.hacktivism.ch {
|
|||
}
|
||||
}
|
||||
|
||||
# surface + aptdeploy HTML (taler.hacktivism.ch only)
|
||||
handle /taler-monitoring-surface_err {
|
||||
redir /taler-monitoring-surface_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-surface_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-surface {
|
||||
redir /taler-monitoring-surface/ 302
|
||||
}
|
||||
handle /taler-monitoring-surface* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy_err {
|
||||
redir /taler-monitoring-aptdeploy_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy {
|
||||
redir /taler-monitoring-aptdeploy/ 302
|
||||
}
|
||||
handle /taler-monitoring-aptdeploy* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
handle /taler-monitoring-mattermost_err {
|
||||
redir /taler-monitoring-mattermost_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-mattermost_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-mattermost {
|
||||
redir /taler-monitoring-mattermost/ 302
|
||||
}
|
||||
handle /taler-monitoring-mattermost* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
handle /taler-monitoring-mail_err {
|
||||
redir /taler-monitoring-mail_err/ 302
|
||||
}
|
||||
handle /taler-monitoring-mail_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /taler-monitoring-mail {
|
||||
redir /taler-monitoring-mail/ 302
|
||||
}
|
||||
handle /taler-monitoring-mail* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
# taler-monitoring console HTML (host static — not in app containers)
|
||||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err/ {
|
||||
root * /var/www/monitoring-sites/{host}/monitoring_err
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring/ {
|
||||
root * /var/www/monitoring-sites/{host}/monitoring
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# SPA: /webui → /webui/
|
||||
redir /webui /webui/ 302
|
||||
|
||||
|
|
@ -82,18 +145,19 @@ exchange.hacktivism.ch {
|
|||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err/ {
|
||||
root * /var/www/monitoring-sites/{host}/monitoring_err
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring/ {
|
||||
root * /var/www/monitoring-sites/{host}/monitoring
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
reverse_proxy 127.0.0.1:9011 {
|
||||
import proxy_public
|
||||
}
|
||||
|
|
@ -115,18 +179,19 @@ bank.hacktivism.ch {
|
|||
handle /monitoring_err {
|
||||
redir /monitoring_err/ 302
|
||||
}
|
||||
handle /monitoring_err/ {
|
||||
root * /var/www/monitoring-sites/{host}/monitoring_err
|
||||
handle /monitoring_err* {
|
||||
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
handle /monitoring {
|
||||
redir /monitoring/ 302
|
||||
}
|
||||
handle /monitoring/ {
|
||||
root * /var/www/monitoring-sites/{host}/monitoring
|
||||
handle /monitoring* {
|
||||
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||
file_server
|
||||
}
|
||||
|
||||
|
||||
# Static terms/privacy on landing nginx :9013
|
||||
handle /terms* {
|
||||
reverse_proxy 127.0.0.1:9013 {
|
||||
|
|
@ -235,7 +300,114 @@ paivana.hacktivism.ch {
|
|||
}
|
||||
}
|
||||
|
||||
http://taler.hacktivism.ch, http://exchange.hacktivism.ch, http://bank.hacktivism.ch, http://castopod.hacktivism.ch, http://bonfire.hacktivism.ch, http://prime.hacktivism.ch, http://bt.hacktivism.ch, http://git.hacktivism.ch, http://paivana.hacktivism.ch, http://tops.ng1.hacktivism.ch, http://tops.ng2.hacktivism.ch, http://tops.ng3.hacktivism.ch {
|
||||
# 9026 lemmy (+ ui/pictrs via internal nginx)
|
||||
lemmy.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9026 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9027 decidim (rails)
|
||||
decidim.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9027 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9029 galene — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site galene.hacktivism.ch --port 9029 --label galene
|
||||
galene.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9029 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9030 cryptpad — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site cryptpad.hacktivism.ch --port 9030 --label cryptpad
|
||||
# CryptPad also needs sandbox subdomain (cryptpad-ui / sandbox) — wire after first boot.
|
||||
cryptpad.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9030 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9031 meet (Jitsi) — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site meet.hacktivism.ch --port 9031 --label meet
|
||||
# WebRTC media (UDP/JVB) is separate from this HTTPS vhost — VeciGate/firewall later.
|
||||
meet.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9031 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 9032 bbb (BigBlueButton) — allocated 2026-09-08
|
||||
# Merge: scripts/caddy/caddy-apply.sh --site bbb.hacktivism.ch --port 9032 --label bbb
|
||||
# Stack image base: Ubuntu (not CachyOS). Media/UDP ports TBD with install.
|
||||
bbb.hacktivism.ch {
|
||||
header Alt-Svc "clear"
|
||||
reverse_proxy 127.0.0.1:9032 {
|
||||
header_up Host {host}
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Host {host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Forwarded-Port 443
|
||||
flush_interval -1
|
||||
transport http {
|
||||
read_timeout 3600s
|
||||
write_timeout 3600s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http://taler.hacktivism.ch, http://exchange.hacktivism.ch, http://bank.hacktivism.ch, http://castopod.hacktivism.ch, http://bonfire.hacktivism.ch, http://prime.hacktivism.ch, http://bt.hacktivism.ch, http://git.hacktivism.ch, http://paivana.hacktivism.ch, http://tops.ng1.hacktivism.ch, http://tops.ng2.hacktivism.ch, http://tops.ng3.hacktivism.ch, http://lemmy.hacktivism.ch, http://decidim.hacktivism.ch, http://galene.hacktivism.ch, http://cryptpad.hacktivism.ch, http://meet.hacktivism.ch, http://bbb.hacktivism.ch {
|
||||
handle /.well-known/acme-challenge/* {
|
||||
root * /var/www/acme
|
||||
file_server
|
||||
|
|
|
|||
11
scripts/bbb/build.sh
Executable file
11
scripts/bbb/build.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build BBB front stub on Ubuntu (run on koopa).
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CTX="${ROOT}/configs/bbb"
|
||||
TAG="${IMAGE_TAG:-localhost/koopa-bbb-front:ubuntu}"
|
||||
cd "$CTX"
|
||||
[[ -f Containerfile ]] || { echo "ERROR: missing $CTX/Containerfile" >&2; exit 1; }
|
||||
echo "build $TAG ctx=$CTX"
|
||||
podman build -t "$TAG" -f Containerfile .
|
||||
echo "OK: $TAG"
|
||||
|
|
@ -15,12 +15,52 @@ sudo systemctl reload caddy
|
|||
Caddy **ignores** Apache `.htaccess`. Use:
|
||||
|
||||
```bash
|
||||
sudo bash /home/hernani/koopa-admin-log/scripts/caddy/apply-dossiers-ngi-basicauth.sh
|
||||
# clone koopa-admin-log from Forgejo when needed (no permanent ~/ clone)
|
||||
sudo bash /path/to/koopa-admin-log/scripts/caddy/apply-dossiers-ngi-basicauth.sh
|
||||
```
|
||||
|
||||
Password file (not git): `/home/hernani/koopa-secrets/dossiers-ngi-basicauth.txt`
|
||||
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
|
||||
|
||||
## New HTTPS vhost
|
||||
## silverbullet.hacktivism.ch — merge + basic_auth
|
||||
|
||||
Add a site block to the Caddyfile (live + mirror), then validate and reload. Include the host in the shared HTTP site list (ACME + redirect) at the bottom of the file.
|
||||
After podman cutover (`scripts/notes/cutover-to-silverbullet.sh --apply`) and DNS:
|
||||
|
||||
```bash
|
||||
# laptop: SoT → /tmp seed (no secret echo)
|
||||
bash /path/to/koopa-admin-log/scripts/notes/prepare-basicauth-seed.sh
|
||||
scp /tmp/silverbullet-basicauth-seed.txt hernani@koopa:/tmp/
|
||||
|
||||
# koopa root:
|
||||
sudo bash /path/to/koopa-admin-log/scripts/caddy/merge-notes-site.sh
|
||||
sudo NOTES_AUTH_SEED=/tmp/silverbullet-basicauth-seed.txt \
|
||||
bash /path/to/koopa-admin-log/scripts/caddy/apply-notes-basicauth.sh
|
||||
```
|
||||
|
||||
`merge-notes-site.sh` renames leftover `freenotes`/`notes` site blocks → `silverbullet.hacktivism.ch`.
|
||||
Host runtime: `/etc/caddy/secrets/silverbullet-basicauth.txt` (root-only).
|
||||
SoT: `koopa-admin-secrets-enc` (git-crypt) → `koopa/home-hernani/caddy/silverbullet-basicauth.txt`.
|
||||
|
||||
## New HTTPS vhost — `caddy-apply.sh`
|
||||
|
||||
Generic merge (site snippet + ACME `http://` list + ports comment):
|
||||
|
||||
```bash
|
||||
# laptop mirror (no reload):
|
||||
bash scripts/caddy/caddy-apply.sh \
|
||||
--config configs/caddy/Caddyfile \
|
||||
--snippet configs/caddy/galene.hacktivism.ch.caddy \
|
||||
--site galene.hacktivism.ch --port 9029 --label galene \
|
||||
--no-reload
|
||||
|
||||
# koopa live (root):
|
||||
sudo bash …/scripts/caddy/caddy-apply.sh \
|
||||
--snippet …/configs/caddy/SITE.caddy \
|
||||
--site SITE.hacktivism.ch --port NNNN --label NAME
|
||||
```
|
||||
|
||||
Allocated collab vhosts (2026-09-08): `galene` **9029**, `cryptpad` **9030**, `meet` **9031**, `bbb` **9032**.
|
||||
Snippets under `configs/caddy/*.hacktivism.ch.caddy`. Image bases: CachyOS except **bbb=Ubuntu**.
|
||||
|
||||
Still valid: hand-edit live + mirror, then validate/reload.
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
#
|
||||
# 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
|
||||
# On koopa (clone admin-log from Forgejo when needed; no home git dump):
|
||||
# sudo bash /path/to/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
|
||||
# Host runtime file (root-only, not under ~):
|
||||
# /etc/caddy/secrets/dossiers-ngi-basicauth.txt
|
||||
# SoT (git-crypt): koopa-admin-secrets-enc → koopa/home-hernani/caddy/
|
||||
set -euo pipefail
|
||||
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
|
|
@ -19,8 +19,9 @@ fi
|
|||
|
||||
CADDY=/etc/caddy/Caddyfile
|
||||
USER_NAME="${DOSSIERS_AUTH_USER:-hernani}"
|
||||
SECRETS_DIR=/home/hernani/koopa-secrets
|
||||
SECRETS_DIR=/etc/caddy/secrets
|
||||
PASS_FILE="${SECRETS_DIR}/dossiers-ngi-basicauth.txt"
|
||||
OLD_PASS_FILE=/home/hernani/koopa-secrets/dossiers-ngi-basicauth.txt
|
||||
SITES=(
|
||||
dossiers.ngi.hacktivism.ch
|
||||
dossiers.2.ngi.hacktivism.ch
|
||||
|
|
@ -34,9 +35,14 @@ 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" && -f "$OLD_PASS_FILE" ]]; then
|
||||
cp -a "$OLD_PASS_FILE" "$PASS_FILE"
|
||||
chmod 600 "$PASS_FILE"
|
||||
echo "OK: migrated $OLD_PASS_FILE → $PASS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$PASS_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
# file format: USER=... PASS=... HASH=...
|
||||
|
|
|
|||
179
scripts/caddy/apply-notes-basicauth.sh
Executable file
179
scripts/caddy/apply-notes-basicauth.sh
Executable file
|
|
@ -0,0 +1,179 @@
|
|||
#!/usr/bin/env bash
|
||||
# Apply HTTP basic_auth to silverbullet.hacktivism.ch (dossiers pattern).
|
||||
#
|
||||
# On koopa (clone admin-log from Forgejo when needed; no home git dump):
|
||||
# sudo bash /path/to/koopa-admin-log/scripts/caddy/apply-notes-basicauth.sh
|
||||
#
|
||||
# Host runtime file (root-only, not under ~):
|
||||
# /etc/caddy/secrets/silverbullet-basicauth.txt
|
||||
# SoT (git-crypt): koopa-admin-secrets-enc → koopa/home-hernani/caddy/
|
||||
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="${NOTES_AUTH_USER:-hernani}"
|
||||
SECRETS_DIR=/etc/caddy/secrets
|
||||
PASS_FILE="${SECRETS_DIR}/silverbullet-basicauth.txt"
|
||||
# legacy home dumps (migrate once, then delete)
|
||||
OLD_PASS_FILE=/home/hernani/koopa-secrets/silverbullet-basicauth.txt
|
||||
OLD_NOTES_FILE=/home/hernani/koopa-secrets/notes-basicauth.txt
|
||||
OLD_FREENOTES_FILE=/home/hernani/koopa-secrets/freenotes-basicauth.txt
|
||||
SITE=silverbullet.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"
|
||||
chmod 700 "$SECRETS_DIR"
|
||||
|
||||
# Prefer explicit seed (SoT from secrets-enc), then legacy home dumps once.
|
||||
SEED_FILE="${NOTES_AUTH_SEED:-}"
|
||||
if [[ ! -f "$PASS_FILE" && -n "$SEED_FILE" && -f "$SEED_FILE" ]]; then
|
||||
cp -a "$SEED_FILE" "$PASS_FILE"
|
||||
chmod 600 "$PASS_FILE"
|
||||
echo "OK: seeded $SEED_FILE → $PASS_FILE"
|
||||
fi
|
||||
if [[ ! -f "$PASS_FILE" && -f "$OLD_PASS_FILE" ]]; then
|
||||
cp -a "$OLD_PASS_FILE" "$PASS_FILE"
|
||||
chmod 600 "$PASS_FILE"
|
||||
echo "OK: migrated $OLD_PASS_FILE → $PASS_FILE"
|
||||
fi
|
||||
if [[ ! -f "$PASS_FILE" && -f "$OLD_NOTES_FILE" ]]; then
|
||||
cp -a "$OLD_NOTES_FILE" "$PASS_FILE"
|
||||
chmod 600 "$PASS_FILE"
|
||||
echo "OK: migrated $OLD_NOTES_FILE → $PASS_FILE"
|
||||
fi
|
||||
if [[ ! -f "$PASS_FILE" && -f "$OLD_FREENOTES_FILE" ]]; then
|
||||
cp -a "$OLD_FREENOTES_FILE" "$PASS_FILE"
|
||||
chmod 600 "$PASS_FILE"
|
||||
echo "OK: migrated $OLD_FREENOTES_FILE → $PASS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$PASS_FILE" ]]; then
|
||||
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" <<EOF
|
||||
USER=${USER_NAME}
|
||||
PASS=${PASS}
|
||||
HASH=${HASH}
|
||||
EOF
|
||||
chmod 600 "$PASS_FILE"
|
||||
echo "OK: wrote new password to $PASS_FILE — mirror into koopa-admin-secrets-enc (git-crypt)"
|
||||
fi
|
||||
|
||||
# Write bcrypt hash with single "$". Doubling to "$$" left literal "$$" in
|
||||
# `caddy adapt` JSON here and made every password 401.
|
||||
HASH_CADDY="$HASH"
|
||||
|
||||
ts=$(date +%Y%m%d-%H%M%S)
|
||||
cp -a "$CADDY" "${CADDY}.bak-silverbullet-auth-${ts}"
|
||||
echo "backup ${CADDY}.bak-silverbullet-auth-${ts}"
|
||||
|
||||
python3 - "$CADDY" "$USER_NAME" "$HASH_CADDY" "$SITE" <<'PY'
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
path = Path(sys.argv[1])
|
||||
user = sys.argv[2]
|
||||
hash_caddy = sys.argv[3]
|
||||
site = sys.argv[4]
|
||||
text = path.read_text()
|
||||
orig = text
|
||||
|
||||
|
||||
def site_span(src: str, site_name: str) -> tuple[int, int]:
|
||||
marker = f"{site_name} {{"
|
||||
i = src.find(marker)
|
||||
if i < 0:
|
||||
raise SystemExit(
|
||||
f"ERROR: site block not found: {site_name} — merge silverbullet.hacktivism.ch.caddy first"
|
||||
)
|
||||
depth = 0
|
||||
for k in range(i + len(marker) - 1, len(src)):
|
||||
ch = src[k]
|
||||
if ch == "{":
|
||||
depth += 1
|
||||
elif ch == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
return i, k + 1
|
||||
raise SystemExit(f"ERROR: unclosed block: {site_name}")
|
||||
|
||||
|
||||
def has_basic_auth_directive(block: str) -> bool:
|
||||
# Ignore comments; only real directives count.
|
||||
stripped = "\n".join(
|
||||
ln for ln in block.splitlines() if not ln.lstrip().startswith("#")
|
||||
)
|
||||
return bool(re.search(r"(?m)^\s*basic_?auth\b", stripped))
|
||||
|
||||
|
||||
i, j = site_span(text, site)
|
||||
block = text[i:j]
|
||||
auth_lines = (
|
||||
"\tbasic_auth {\n"
|
||||
f"\t\t{user} {hash_caddy}\n"
|
||||
"\t}\n"
|
||||
)
|
||||
# Replace existing basic_auth block (also fixes legacy "$$" hashes that never matched).
|
||||
block_new, n = re.subn(
|
||||
r"(?m)^[ \t]*basic_auth\s*\{.*?\n[ \t]*\}\n?",
|
||||
auth_lines,
|
||||
block,
|
||||
count=1,
|
||||
flags=re.S,
|
||||
)
|
||||
if n:
|
||||
text = text[:i] + block_new + text[j:]
|
||||
print(f"OK: replaced basic_auth in {site}")
|
||||
elif has_basic_auth_directive(block):
|
||||
print(f"OK: {site} already has basic_auth")
|
||||
else:
|
||||
m = re.search(r'(?m)^[ \t]*header Alt-Svc "clear"[ \t]*\n', block)
|
||||
if m:
|
||||
insert_at = m.end()
|
||||
new_block = block[:insert_at] + auth_lines + block[insert_at:]
|
||||
else:
|
||||
nl = block.find("\n")
|
||||
new_block = block[: nl + 1] + auth_lines + block[nl + 1 :]
|
||||
text = text[:i] + new_block + text[j:]
|
||||
print(f"OK: injected basic_auth in {site}")
|
||||
|
||||
if text != orig:
|
||||
path.write_text(text)
|
||||
print(f"wrote {path}")
|
||||
else:
|
||||
print("no Caddyfile change")
|
||||
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://${SITE}/"
|
||||
echo "User/pass: $PASS_FILE"
|
||||
217
scripts/caddy/caddy-apply.sh
Executable file
217
scripts/caddy/caddy-apply.sh
Executable file
|
|
@ -0,0 +1,217 @@
|
|||
#!/usr/bin/env bash
|
||||
# Generic: merge a site snippet into a Caddyfile + ACME http:// list + ports comment.
|
||||
#
|
||||
# Laptop (mirror only, no reload):
|
||||
# bash scripts/caddy/caddy-apply.sh \
|
||||
# --config configs/caddy/Caddyfile \
|
||||
# --snippet configs/caddy/galene.hacktivism.ch.caddy \
|
||||
# --site galene.hacktivism.ch --port 9029 --label galene \
|
||||
# --no-reload
|
||||
#
|
||||
# Koopa live (root, Freigabe):
|
||||
# sudo bash …/caddy-apply.sh \
|
||||
# --snippet /home/hernani/koopa-admin-log/configs/caddy/galene.hacktivism.ch.caddy \
|
||||
# --site galene.hacktivism.ch --port 9029 --label galene
|
||||
set -euo pipefail
|
||||
|
||||
CADDY=/etc/caddy/Caddyfile
|
||||
SNIPPET=""
|
||||
SITE=""
|
||||
PORT=""
|
||||
LABEL=""
|
||||
INSERT_AFTER="decidim.hacktivism.ch"
|
||||
ACME_ANCHOR="http://decidim.hacktivism.ch"
|
||||
NO_RELOAD=0
|
||||
DRY=0
|
||||
OLD_SITES=()
|
||||
|
||||
usage() {
|
||||
sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'
|
||||
echo "Options: --config PATH --snippet PATH --site HOST --port N --label NAME"
|
||||
echo " --insert-after HOST --acme-anchor http://HOST --old-site HOST"
|
||||
echo " --no-reload --dry-run"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--config) CADDY=$2; shift 2 ;;
|
||||
--snippet) SNIPPET=$2; shift 2 ;;
|
||||
--site) SITE=$2; shift 2 ;;
|
||||
--port) PORT=$2; shift 2 ;;
|
||||
--label) LABEL=$2; shift 2 ;;
|
||||
--insert-after) INSERT_AFTER=$2; shift 2 ;;
|
||||
--acme-anchor) ACME_ANCHOR=$2; shift 2 ;;
|
||||
--old-site) OLD_SITES+=("$2"); shift 2 ;;
|
||||
--no-reload) NO_RELOAD=1; shift ;;
|
||||
--dry-run) DRY=1; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "ERROR: unknown arg: $1" >&2; usage >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n "$SNIPPET" && -n "$SITE" ]] || { echo "ERROR: --snippet and --site required" >&2; exit 2; }
|
||||
[[ -f "$CADDY" ]] || { echo "ERROR: missing $CADDY" >&2; exit 1; }
|
||||
[[ -f "$SNIPPET" ]] || { echo "ERROR: missing $SNIPPET" >&2; exit 1; }
|
||||
LABEL="${LABEL:-$SITE}"
|
||||
|
||||
if [[ "$CADDY" == /etc/caddy/Caddyfile && "$(id -u)" -ne 0 && "$DRY" -eq 0 ]]; then
|
||||
echo "ERROR: live Caddyfile needs root (or --dry-run / --config mirror)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ts=$(date +%Y%m%d-%H%M%S)
|
||||
if [[ "$DRY" -eq 0 ]]; then
|
||||
cp -a "$CADDY" "${CADDY}.bak-caddy-apply-${ts}"
|
||||
echo "backup ${CADDY}.bak-caddy-apply-${ts}"
|
||||
fi
|
||||
|
||||
export CADDY SNIPPET SITE PORT LABEL INSERT_AFTER ACME_ANCHOR DRY
|
||||
export OLD_SITES_CSV
|
||||
OLD_SITES_CSV=$(IFS=,; echo "${OLD_SITES[*]-}")
|
||||
|
||||
python3 - <<'PY'
|
||||
import os, re, sys
|
||||
from pathlib import Path
|
||||
|
||||
caddy = Path(os.environ["CADDY"])
|
||||
snip = Path(os.environ["SNIPPET"]).read_text().rstrip() + "\n\n"
|
||||
site = os.environ["SITE"]
|
||||
port = os.environ.get("PORT") or ""
|
||||
label = os.environ.get("LABEL") or site
|
||||
insert_after = os.environ["INSERT_AFTER"]
|
||||
acme_anchor = os.environ["ACME_ANCHOR"]
|
||||
dry = os.environ.get("DRY") == "1"
|
||||
old_sites = [s for s in os.environ.get("OLD_SITES_CSV", "").split(",") if s]
|
||||
|
||||
text = caddy.read_text()
|
||||
orig = text
|
||||
|
||||
def drop_site_block(text: str, old_site: str) -> str:
|
||||
marker_old = f"{old_site} {{"
|
||||
i = text.find(marker_old)
|
||||
if i < 0:
|
||||
return text
|
||||
j = text.find("\n}", i)
|
||||
if j < 0:
|
||||
return text
|
||||
end = j + 2
|
||||
while end < len(text) and text[end] == "\n":
|
||||
end += 1
|
||||
text = text[:i] + text[end:]
|
||||
text = text.replace(f", http://{old_site}", "").replace(f"http://{old_site}, ", "")
|
||||
text = text.replace(f"http://{old_site}", "")
|
||||
print(f"OK: removed duplicate old block {old_site}")
|
||||
return text
|
||||
|
||||
for old_site in old_sites:
|
||||
if old_site in text and site not in text:
|
||||
text = text.replace(old_site, site)
|
||||
print(f"OK: renamed {old_site} → {site}")
|
||||
elif old_site in text and site in text:
|
||||
text = drop_site_block(text, old_site)
|
||||
|
||||
marker = f"{site} {{"
|
||||
if marker in text:
|
||||
print(f"OK: site block already present: {site}")
|
||||
else:
|
||||
insert_at = None
|
||||
after = text.find(f"{insert_after} {{")
|
||||
if after >= 0:
|
||||
j = text.find("\n}", after)
|
||||
if j >= 0:
|
||||
insert_at = j + 2
|
||||
while insert_at < len(text) and text[insert_at] == "\n":
|
||||
insert_at += 1
|
||||
if insert_at is None:
|
||||
acme = text.find("http://taler.hacktivism.ch")
|
||||
insert_at = acme if acme >= 0 else len(text)
|
||||
text = text[:insert_at] + snip + text[insert_at:]
|
||||
print(f"OK: inserted site block {site}")
|
||||
|
||||
http_tok = f"http://{site}"
|
||||
if http_tok in text:
|
||||
print(f"OK: ACME http list already has {http_tok}")
|
||||
else:
|
||||
# Prefer exact "anchor {" form; else append before " {" of the long http list
|
||||
old = f"{acme_anchor} {{"
|
||||
new = f"{acme_anchor}, {http_tok} {{"
|
||||
if old in text:
|
||||
text = text.replace(old, new, 1)
|
||||
print(f"OK: added {http_tok} to ACME http list (anchor)")
|
||||
else:
|
||||
# Find the shared ACME line (starts with http://taler…)
|
||||
m = re.search(r"(http://taler\.hacktivism\.ch[^\n]*?)(\s*\{)", text)
|
||||
if not m:
|
||||
raise SystemExit("ERROR: ACME http list not found")
|
||||
line = m.group(1)
|
||||
if http_tok in line:
|
||||
print(f"OK: ACME http list already has {http_tok}")
|
||||
else:
|
||||
text = text[: m.start(1)] + line.rstrip() + f", {http_tok}" + text[m.end(1) :]
|
||||
print(f"OK: added {http_tok} to ACME http list (append)")
|
||||
|
||||
# Ports comment header (first matching line starting with "# 9020 castopod")
|
||||
if port:
|
||||
token = f"{port} {label}"
|
||||
def upd_hdr(line: str) -> str:
|
||||
if token in line or f"| {port} " in line or f"| {port}|" in line:
|
||||
return line
|
||||
# insert before " | 9200 forgejo-ssh" if present, else before end
|
||||
if "9200 forgejo-ssh" in line:
|
||||
return line.replace(" | 9200 forgejo-ssh", f" | {token} | 9200 forgejo-ssh", 1)
|
||||
if line.rstrip().endswith("forgejo-ssh"):
|
||||
return line.rstrip() + f" | {token}\n"
|
||||
return line.rstrip() + f" | {token}\n"
|
||||
|
||||
lines = text.splitlines(keepends=True)
|
||||
changed = False
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("# 9020 castopod"):
|
||||
new_line = upd_hdr(line)
|
||||
if new_line != line:
|
||||
lines[i] = new_line if new_line.endswith("\n") else new_line + "\n"
|
||||
print(f"OK: updated ports comment (+{token})")
|
||||
changed = True
|
||||
else:
|
||||
print(f"OK: ports comment already has {token}")
|
||||
break
|
||||
if changed:
|
||||
text = "".join(lines)
|
||||
|
||||
if text != orig:
|
||||
if dry:
|
||||
print(f"DRY: would write {caddy} ({len(text) - len(orig):+d} bytes)")
|
||||
else:
|
||||
caddy.write_text(text)
|
||||
print(f"wrote {caddy}")
|
||||
else:
|
||||
print("no Caddyfile change")
|
||||
PY
|
||||
|
||||
if [[ "$DRY" -eq 1 ]]; then
|
||||
echo "dry-run done (no validate/reload)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if command -v caddy >/dev/null 2>&1; then
|
||||
echo "== validate =="
|
||||
caddy validate --config "$CADDY"
|
||||
else
|
||||
echo "WARN: caddy binary missing — skip validate" >&2
|
||||
fi
|
||||
|
||||
if [[ "$NO_RELOAD" -eq 1 ]]; then
|
||||
echo "skip reload (--no-reload)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$CADDY" == /etc/caddy/Caddyfile ]] && command -v systemctl >/dev/null 2>&1; then
|
||||
echo "== reload =="
|
||||
if systemctl is-active --quiet caddy; then
|
||||
systemctl reload caddy
|
||||
else
|
||||
echo "WARN: caddy unit not active — start it yourself" >&2
|
||||
fi
|
||||
else
|
||||
echo "skip reload (not live path or no systemctl)"
|
||||
fi
|
||||
133
scripts/caddy/merge-notes-site.sh
Executable file
133
scripts/caddy/merge-notes-site.sh
Executable file
|
|
@ -0,0 +1,133 @@
|
|||
#!/usr/bin/env bash
|
||||
# Merge silverbullet.hacktivism.ch into live /etc/caddy/Caddyfile + ACME http list.
|
||||
# Run on koopa as root AFTER podman is up on :9028:
|
||||
# sudo bash /home/hernani/koopa-admin-log/scripts/caddy/merge-notes-site.sh
|
||||
# Then:
|
||||
# sudo bash /home/hernani/koopa-admin-log/scripts/caddy/apply-notes-basicauth.sh
|
||||
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
|
||||
SNIPPET=/home/hernani/koopa-admin-log/configs/caddy/silverbullet.hacktivism.ch.caddy
|
||||
SITE=silverbullet.hacktivism.ch
|
||||
# Prior hostnames to rename into SITE (order: newest leftovers first)
|
||||
OLD_SITES="freenotes.hacktivism.ch notes.hacktivism.ch"
|
||||
|
||||
[[ -f "$CADDY" ]] || { echo "ERROR: missing $CADDY" >&2; exit 1; }
|
||||
[[ -f "$SNIPPET" ]] || { echo "ERROR: missing $SNIPPET" >&2; exit 1; }
|
||||
command -v caddy >/dev/null || { echo "ERROR: caddy not in PATH" >&2; exit 1; }
|
||||
|
||||
ts=$(date +%Y%m%d-%H%M%S)
|
||||
cp -a "$CADDY" "${CADDY}.bak-silverbullet-merge-${ts}"
|
||||
echo "backup ${CADDY}.bak-silverbullet-merge-${ts}"
|
||||
|
||||
python3 - "$CADDY" "$SNIPPET" "$SITE" $OLD_SITES <<'PY'
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
caddy = Path(sys.argv[1])
|
||||
snippet_path = Path(sys.argv[2])
|
||||
site = sys.argv[3]
|
||||
old_sites = sys.argv[4:]
|
||||
text = caddy.read_text()
|
||||
orig = text
|
||||
snip = snippet_path.read_text().rstrip() + "\n\n"
|
||||
|
||||
def drop_site_block(text: str, old_site: str) -> str:
|
||||
marker_old = f"{old_site} {{"
|
||||
i = text.find(marker_old)
|
||||
if i < 0:
|
||||
return text
|
||||
j = text.find("\n}", i)
|
||||
if j < 0:
|
||||
return text
|
||||
end = j + 2
|
||||
while end < len(text) and text[end] == "\n":
|
||||
end += 1
|
||||
text = text[:i] + text[end:]
|
||||
text = text.replace(f", http://{old_site}", "").replace(f"http://{old_site}, ", "")
|
||||
text = text.replace(f"http://{old_site}", "")
|
||||
print(f"OK: removed duplicate old block {old_site}")
|
||||
return text
|
||||
|
||||
# Rename leftover freenotes/notes.hacktivism.ch → silverbullet.hacktivism.ch
|
||||
for old_site in old_sites:
|
||||
if old_site in text and site not in text:
|
||||
text = text.replace(old_site, site)
|
||||
text = text.replace("9028 freenotes", "9028 silverbullet")
|
||||
text = text.replace("9028 notes", "9028 silverbullet")
|
||||
print(f"OK: renamed {old_site} → {site}")
|
||||
elif old_site in text and site in text:
|
||||
text = drop_site_block(text, old_site)
|
||||
|
||||
marker = f"{site} {{"
|
||||
if marker in text:
|
||||
print(f"OK: site block already present: {site}")
|
||||
else:
|
||||
# Insert after decidim block if present, else before ACME http list
|
||||
insert_at = None
|
||||
dec = text.find("decidim.hacktivism.ch {")
|
||||
if dec >= 0:
|
||||
j = text.find("\n}", dec)
|
||||
if j >= 0:
|
||||
insert_at = j + 2 # after closing brace newline
|
||||
while insert_at < len(text) and text[insert_at] == "\n":
|
||||
insert_at += 1
|
||||
if insert_at is None:
|
||||
acme = text.find("http://taler.hacktivism.ch")
|
||||
insert_at = acme if acme >= 0 else len(text)
|
||||
text = text[:insert_at] + snip + text[insert_at:]
|
||||
print(f"OK: inserted site block {site}")
|
||||
|
||||
http_tok = f"http://{site}"
|
||||
if http_tok in text:
|
||||
print(f"OK: ACME http list already has {http_tok}")
|
||||
else:
|
||||
old = "http://decidim.hacktivism.ch {"
|
||||
new = f"http://decidim.hacktivism.ch, {http_tok} {{"
|
||||
if old not in text:
|
||||
# maybe already on a long list without trailing brace-only form
|
||||
raise SystemExit("ERROR: ACME http list marker not found (decidim)")
|
||||
text = text.replace(old, new, 1)
|
||||
print(f"OK: added {http_tok} to ACME http list")
|
||||
|
||||
# Comment header line near top ports comment
|
||||
for old_hdr, new_hdr in (
|
||||
(
|
||||
"# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9200 forgejo-ssh",
|
||||
"# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9028 silverbullet | 9200 forgejo-ssh",
|
||||
),
|
||||
(
|
||||
"# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9028 notes | 9200 forgejo-ssh",
|
||||
"# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9028 silverbullet | 9200 forgejo-ssh",
|
||||
),
|
||||
):
|
||||
if old_hdr in text:
|
||||
text = text.replace(old_hdr, new_hdr, 1)
|
||||
print("OK: updated ports comment header")
|
||||
break
|
||||
|
||||
if text != orig:
|
||||
caddy.write_text(text)
|
||||
print(f"wrote {caddy}")
|
||||
else:
|
||||
print("no Caddyfile change")
|
||||
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 "Next: sudo bash /home/hernani/koopa-admin-log/scripts/caddy/apply-notes-basicauth.sh"
|
||||
echo "DNS/VeciGate for ${SITE} still separate."
|
||||
17
scripts/cryptpad/build.sh
Executable file
17
scripts/cryptpad/build.sh
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build CryptPad image on CachyOS base (run on koopa).
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CTX="${ROOT}/configs/cryptpad"
|
||||
TAG="${IMAGE_TAG:-localhost/koopa-cryptpad:cachyos}"
|
||||
CRYPTPAD_VERSION="${CRYPTPAD_VERSION:-2025.9.0}"
|
||||
cd "$CTX"
|
||||
[[ -f Containerfile ]] || { echo "ERROR: missing $CTX/Containerfile" >&2; exit 1; }
|
||||
echo "build $TAG (CRYPTPAD_VERSION=$CRYPTPAD_VERSION) ctx=$CTX"
|
||||
podman build \
|
||||
--build-arg "CRYPTPAD_VERSION=${CRYPTPAD_VERSION}" \
|
||||
-t "$TAG" \
|
||||
-f Containerfile \
|
||||
.
|
||||
echo "OK: $TAG"
|
||||
podman image inspect "$TAG" --format '{{.Id}} {{.Size}} {{.Architecture}}' 2>/dev/null || true
|
||||
17
scripts/galene/build.sh
Executable file
17
scripts/galene/build.sh
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build Galene image on CachyOS base (run on koopa).
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CTX="${ROOT}/configs/galene"
|
||||
TAG="${IMAGE_TAG:-localhost/koopa-galene:cachyos}"
|
||||
GALENE_VERSION="${GALENE_VERSION:-0.9.1}"
|
||||
cd "$CTX"
|
||||
[[ -f Containerfile ]] || { echo "ERROR: missing $CTX/Containerfile" >&2; exit 1; }
|
||||
echo "build $TAG (GALENE_VERSION=$GALENE_VERSION) ctx=$CTX"
|
||||
podman build \
|
||||
--build-arg "GALENE_VERSION=${GALENE_VERSION}" \
|
||||
-t "$TAG" \
|
||||
-f Containerfile \
|
||||
.
|
||||
echo "OK: $TAG"
|
||||
podman image inspect "$TAG" --format '{{.Id}} {{.Size}} {{.Architecture}}' 2>/dev/null || true
|
||||
11
scripts/meet/build.sh
Executable file
11
scripts/meet/build.sh
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build Meet web stub on CachyOS (run on koopa).
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CTX="${ROOT}/configs/meet"
|
||||
TAG="${IMAGE_TAG:-localhost/koopa-meet-web:cachyos}"
|
||||
cd "$CTX"
|
||||
[[ -f Containerfile ]] || { echo "ERROR: missing $CTX/Containerfile" >&2; exit 1; }
|
||||
echo "build $TAG ctx=$CTX"
|
||||
podman build -t "$TAG" -f Containerfile .
|
||||
echo "OK: $TAG"
|
||||
40
scripts/notes/README.md
Normal file
40
scripts/notes/README.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# SilverBullet — scripts (koopa)
|
||||
|
||||
Product **FreeNotes** is separate (native enc). These scripts operate the parked **SilverBullet** web stack on port **9028**.
|
||||
|
||||
| Script | Role |
|
||||
|--------|------|
|
||||
| `status.sh` | Read-only dirs / containers / `:9028` |
|
||||
| `build.sh` | `podman build` → `localhost/koopa-silverbullet:cachyos` |
|
||||
| `up.sh` | `podman-compose up` in `~/koopa-silverbullet` + health |
|
||||
| `cutover-to-silverbullet.sh` | Live freenotes → silverbullet (default **dry-run**; `--apply` mutates) |
|
||||
| `prepare-basicauth-seed.sh` | SoT → `/tmp/silverbullet-basicauth-seed.txt` (no sudo) |
|
||||
|
||||
Caddy (sudo, separate): `scripts/caddy/merge-notes-site.sh`, `apply-notes-basicauth.sh`.
|
||||
|
||||
## Cutover order (needs Freigabe + DNS)
|
||||
|
||||
On koopa as `hernani` (admin-log clone from Forgejo when needed):
|
||||
|
||||
```bash
|
||||
bash …/scripts/notes/status.sh
|
||||
bash …/scripts/notes/cutover-to-silverbullet.sh # dry-run
|
||||
bash …/scripts/notes/cutover-to-silverbullet.sh --apply # Freigabe
|
||||
```
|
||||
|
||||
Then Caddy (root) + seed from laptop SoT:
|
||||
|
||||
```bash
|
||||
# laptop (unlocked secrets-enc):
|
||||
bash …/scripts/notes/prepare-basicauth-seed.sh
|
||||
scp /tmp/silverbullet-basicauth-seed.txt hernani@koopa:/tmp/
|
||||
|
||||
# koopa root:
|
||||
sudo bash …/scripts/caddy/merge-notes-site.sh
|
||||
sudo NOTES_AUTH_SEED=/tmp/silverbullet-basicauth-seed.txt \
|
||||
bash …/scripts/caddy/apply-notes-basicauth.sh
|
||||
```
|
||||
|
||||
DNS: `silverbullet.hacktivism.ch` → koopa (human). Public: `https://silverbullet.hacktivism.ch/`.
|
||||
|
||||
Keeps on cutover: `space/`, `.env`. Refreshes compose/Containerfile/entrypoint/unit from `configs/notes/`.
|
||||
23
scripts/notes/build.sh
Executable file
23
scripts/notes/build.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build SilverBullet image on CachyOS base (run on koopa).
|
||||
# bash ~/koopa-admin-log/scripts/notes/build.sh
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CTX="${ROOT}/configs/notes"
|
||||
TAG="${IMAGE_TAG:-localhost/koopa-silverbullet:cachyos}"
|
||||
SB_VERSION="${SB_VERSION:-2.10.0}"
|
||||
|
||||
cd "$CTX"
|
||||
[[ -f Containerfile ]] || { echo "ERROR: missing $CTX/Containerfile" >&2; exit 1; }
|
||||
[[ -f entrypoint.sh ]] || { echo "ERROR: missing $CTX/entrypoint.sh" >&2; exit 1; }
|
||||
|
||||
echo "build $TAG (SB_VERSION=$SB_VERSION) ctx=$CTX"
|
||||
podman build \
|
||||
--build-arg "SB_VERSION=${SB_VERSION}" \
|
||||
-t "$TAG" \
|
||||
-f Containerfile \
|
||||
.
|
||||
|
||||
echo "OK: $TAG"
|
||||
podman image inspect "$TAG" --format '{{.Id}} {{.Size}} {{.Architecture}}' 2>/dev/null || true
|
||||
162
scripts/notes/cutover-to-silverbullet.sh
Executable file
162
scripts/notes/cutover-to-silverbullet.sh
Executable file
|
|
@ -0,0 +1,162 @@
|
|||
#!/usr/bin/env bash
|
||||
# Cut over live stack freenotes → silverbullet on koopa (hernani).
|
||||
# Default: dry-run. Mutate only with --apply.
|
||||
#
|
||||
# bash /path/to/koopa-admin-log/scripts/notes/cutover-to-silverbullet.sh
|
||||
# bash /path/to/koopa-admin-log/scripts/notes/cutover-to-silverbullet.sh --apply
|
||||
#
|
||||
# Keeps: space/, .env. Refreshes: compose.yml, Containerfile, entrypoint.sh, systemd unit.
|
||||
# Image: retag localhost/koopa-freenotes:cachyos → localhost/koopa-silverbullet:cachyos (no rebuild).
|
||||
# Does NOT touch Caddy (sudo) — print next steps only.
|
||||
set -euo pipefail
|
||||
|
||||
APPLY=0
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--apply) APPLY=1 ;;
|
||||
-h|--help)
|
||||
sed -n '2,12p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown arg: $a (use --apply)" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
OLD_DIR="${HOME}/koopa-freenotes"
|
||||
NEW_DIR="${HOME}/koopa-silverbullet"
|
||||
OLD_CT=koopa-freenotes
|
||||
NEW_CT=koopa-silverbullet
|
||||
OLD_IMG=localhost/koopa-freenotes:cachyos
|
||||
NEW_IMG=localhost/koopa-silverbullet:cachyos
|
||||
PORT=9028
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CFG="${ROOT}/configs/notes"
|
||||
|
||||
[[ -d "$CFG" ]] || { echo "ERROR: missing $CFG" >&2; exit 1; }
|
||||
[[ -f "$CFG/compose.yml" ]] || { echo "ERROR: missing $CFG/compose.yml" >&2; exit 1; }
|
||||
|
||||
mode=dry-run
|
||||
[[ "$APPLY" -eq 1 ]] && mode=apply
|
||||
echo "STATUS=start MODE=$mode HOST=$(hostname -s) OLD_DIR=$OLD_DIR NEW_DIR=$NEW_DIR"
|
||||
|
||||
have_old=0
|
||||
have_new=0
|
||||
[[ -d "$OLD_DIR" ]] && have_old=1
|
||||
[[ -d "$NEW_DIR" ]] && have_new=1
|
||||
echo "STATUS=dirs OLD=$have_old NEW=$have_new"
|
||||
|
||||
ct_old=$(podman ps -a --format '{{.Names}}' 2>/dev/null | grep -Fx "$OLD_CT" || true)
|
||||
ct_new=$(podman ps -a --format '{{.Names}}' 2>/dev/null | grep -Fx "$NEW_CT" || true)
|
||||
echo "STATUS=containers OLD=${ct_old:-none} NEW=${ct_new:-none}"
|
||||
|
||||
img_old=$(podman images -q "$OLD_IMG" 2>/dev/null || true)
|
||||
img_new=$(podman images -q "$NEW_IMG" 2>/dev/null || true)
|
||||
echo "STATUS=images OLD=${img_old:-none} NEW=${img_new:-none}"
|
||||
|
||||
dir_err=0
|
||||
if [[ "$have_old" -eq 0 && "$have_new" -eq 1 ]]; then
|
||||
echo "STATUS=already_cutover DIR=$NEW_DIR"
|
||||
elif [[ "$have_old" -eq 0 && "$have_new" -eq 0 ]]; then
|
||||
echo "STATUS=warn neither $OLD_DIR nor $NEW_DIR (ok for laptop dry-run; apply needs koopa)"
|
||||
dir_err=1
|
||||
elif [[ "$have_old" -eq 1 && "$have_new" -eq 1 ]]; then
|
||||
echo "STATUS=warn both dirs exist — resolve manually before --apply"
|
||||
dir_err=1
|
||||
fi
|
||||
|
||||
plan() { echo "PLAN: $*"; }
|
||||
|
||||
plan "stop+rm container $OLD_CT (if present)"
|
||||
plan "mv $OLD_DIR → $NEW_DIR (if old present)"
|
||||
plan "copy compose/Containerfile/entrypoint/unit from $CFG (keep space .env)"
|
||||
plan "podman tag $OLD_IMG $NEW_IMG (if new image missing)"
|
||||
plan "podman-compose up -d in $NEW_DIR"
|
||||
plan "health curl :$PORT"
|
||||
plan "optional: systemctl --user enable container-koopa-silverbullet.service"
|
||||
|
||||
if [[ "$APPLY" -ne 1 ]]; then
|
||||
echo "STATUS=dry-run NEXT=re-run on koopa with --apply (Freigabe)"
|
||||
echo "STATUS=caddy_next sudo bash $ROOT/scripts/caddy/merge-notes-site.sh"
|
||||
echo "STATUS=caddy_next sudo NOTES_AUTH_SEED=/tmp/silverbullet-basicauth-seed.txt bash $ROOT/scripts/caddy/apply-notes-basicauth.sh"
|
||||
echo "STATUS=dns_need silverbullet.hacktivism.ch → koopa"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$dir_err" -eq 1 ]]; then
|
||||
echo "ERROR: refuse --apply with ambiguous/missing live dirs" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$ct_old" ]]; then
|
||||
echo "STATUS=stop $OLD_CT"
|
||||
podman stop -t 20 "$OLD_CT" || true
|
||||
podman rm "$OLD_CT" || true
|
||||
fi
|
||||
|
||||
if [[ "$have_old" -eq 1 ]]; then
|
||||
echo "STATUS=mv $OLD_DIR → $NEW_DIR"
|
||||
mv "$OLD_DIR" "$NEW_DIR"
|
||||
fi
|
||||
|
||||
cd "$NEW_DIR"
|
||||
# Refresh stack files from admin-log; never overwrite space/ or .env
|
||||
cp -a "$CFG/compose.yml" "$NEW_DIR/compose.yml"
|
||||
cp -a "$CFG/Containerfile" "$NEW_DIR/Containerfile"
|
||||
cp -a "$CFG/entrypoint.sh" "$NEW_DIR/entrypoint.sh"
|
||||
chmod +x "$NEW_DIR/entrypoint.sh"
|
||||
cp -a "$CFG/container-koopa-silverbullet.service" "$NEW_DIR/container-koopa-silverbullet.service"
|
||||
cp -a "$CFG/.env.example" "$NEW_DIR/.env.example"
|
||||
# Drop obsolete unit name if present
|
||||
rm -f "$NEW_DIR/container-koopa-freenotes.service"
|
||||
echo "STATUS=refreshed_stack_files DIR=$NEW_DIR"
|
||||
|
||||
if [[ -z "$img_new" ]]; then
|
||||
if [[ -n "$img_old" ]]; then
|
||||
echo "STATUS=tag $OLD_IMG → $NEW_IMG"
|
||||
podman tag "$OLD_IMG" "$NEW_IMG"
|
||||
else
|
||||
echo "STATUS=build missing image via build.sh"
|
||||
bash "$ROOT/scripts/notes/build.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$ct_new" ]]; then
|
||||
echo "STATUS=rm stale $NEW_CT"
|
||||
podman stop -t 10 "$NEW_CT" || true
|
||||
podman rm "$NEW_CT" || true
|
||||
fi
|
||||
|
||||
echo "STATUS=compose_up"
|
||||
podman-compose up -d
|
||||
|
||||
ok=0
|
||||
for i in $(seq 1 40); do
|
||||
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 5 "http://127.0.0.1:${PORT}/" || true)
|
||||
echo "STATUS=health try=$i code=$code"
|
||||
case "$code" in
|
||||
2*|3*) ok=1; break ;;
|
||||
esac
|
||||
sleep 3
|
||||
done
|
||||
[[ "$ok" -eq 1 ]] || { echo "ERROR: no answer on :${PORT}" >&2; podman-compose ps; exit 1; }
|
||||
|
||||
# User systemd unit (optional; ignore failures)
|
||||
UNIT_SRC="$NEW_DIR/container-koopa-silverbullet.service"
|
||||
UNIT_DST="${HOME}/.config/systemd/user/container-koopa-silverbullet.service"
|
||||
if [[ -f "$UNIT_SRC" ]] && command -v systemctl >/dev/null; then
|
||||
mkdir -p "${HOME}/.config/systemd/user"
|
||||
cp -a "$UNIT_SRC" "$UNIT_DST"
|
||||
systemctl --user daemon-reload || true
|
||||
systemctl --user disable --now container-koopa-freenotes.service 2>/dev/null || true
|
||||
systemctl --user enable --now container-koopa-silverbullet.service 2>/dev/null || true
|
||||
echo "STATUS=systemd_user unit=container-koopa-silverbullet.service"
|
||||
fi
|
||||
|
||||
echo "STATUS=ok CONTAINER=$NEW_CT DIR=$NEW_DIR PORT=$PORT"
|
||||
echo "STATUS=caddy_next sudo bash $ROOT/scripts/caddy/merge-notes-site.sh"
|
||||
echo "STATUS=caddy_next sudo NOTES_AUTH_SEED=/tmp/silverbullet-basicauth-seed.txt bash $ROOT/scripts/caddy/apply-notes-basicauth.sh"
|
||||
echo "STATUS=dns_need silverbullet.hacktivism.ch → koopa"
|
||||
25
scripts/notes/prepare-basicauth-seed.sh
Executable file
25
scripts/notes/prepare-basicauth-seed.sh
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copy silverbullet basicauth SoT → /tmp seed for apply-notes-basicauth.sh.
|
||||
# Run on magikoopa (or any host with unlocked koopa-admin-secrets-enc).
|
||||
# Does NOT touch /etc/caddy (no sudo). Does NOT print secret contents.
|
||||
#
|
||||
# bash scripts/notes/prepare-basicauth-seed.sh
|
||||
# # then on koopa (after scp seed or shared path):
|
||||
# sudo NOTES_AUTH_SEED=/tmp/silverbullet-basicauth-seed.txt \
|
||||
# bash …/scripts/caddy/apply-notes-basicauth.sh
|
||||
set -euo pipefail
|
||||
|
||||
OUT="${NOTES_AUTH_SEED_OUT:-/tmp/silverbullet-basicauth-seed.txt}"
|
||||
SOT_DEFAULT="${HOME}/git/koopa-admin-secrets-enc/koopa/home-hernani/caddy/silverbullet-basicauth.txt"
|
||||
SOT="${NOTES_AUTH_SOT:-$SOT_DEFAULT}"
|
||||
|
||||
[[ -f "$SOT" ]] || {
|
||||
echo "ERROR: missing SoT $SOT (unlock git-crypt / set NOTES_AUTH_SOT=)" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
umask 077
|
||||
cp -a "$SOT" "$OUT"
|
||||
chmod 600 "$OUT"
|
||||
echo "STATUS=ok OUT=$OUT BYTES=$(wc -c <"$OUT" | tr -d ' ') NEXT=scp_or_use_NOTES_AUTH_SEED"
|
||||
echo "STATUS=hint scp $OUT hernani@koopa:/tmp/ && sudo NOTES_AUTH_SEED=$OUT bash …/apply-notes-basicauth.sh"
|
||||
39
scripts/notes/status.sh
Executable file
39
scripts/notes/status.sh
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env bash
|
||||
# Thin status for SilverBullet stack on koopa (hernani). Safe read-only.
|
||||
# bash scripts/notes/status.sh
|
||||
set -euo pipefail
|
||||
|
||||
OLD_DIR="${HOME}/koopa-freenotes"
|
||||
NEW_DIR="${HOME}/koopa-silverbullet"
|
||||
OLD_CT=koopa-freenotes
|
||||
NEW_CT=koopa-silverbullet
|
||||
OLD_IMG=localhost/koopa-freenotes:cachyos
|
||||
NEW_IMG=localhost/koopa-silverbullet:cachyos
|
||||
PORT=9028
|
||||
|
||||
echo "STATUS=host HOST=$(hostname -s)"
|
||||
echo "STATUS=dirs OLD=$([ -d "$OLD_DIR" ] && echo 1 || echo 0) NEW=$([ -d "$NEW_DIR" ] && echo 1 || echo 0)"
|
||||
|
||||
if command -v podman >/dev/null 2>&1; then
|
||||
ct_old=$(podman ps -a --format '{{.Names}} {{.Status}}' 2>/dev/null | grep -E "^${OLD_CT} " || true)
|
||||
ct_new=$(podman ps -a --format '{{.Names}} {{.Status}}' 2>/dev/null | grep -E "^${NEW_CT} " || true)
|
||||
echo "STATUS=ct_old ${ct_old:-none}"
|
||||
echo "STATUS=ct_new ${ct_new:-none}"
|
||||
img_old=$(podman images -q "$OLD_IMG" 2>/dev/null || true)
|
||||
img_new=$(podman images -q "$NEW_IMG" 2>/dev/null || true)
|
||||
echo "STATUS=img_old ${img_old:-none}"
|
||||
echo "STATUS=img_new ${img_new:-none}"
|
||||
else
|
||||
echo "STATUS=podman missing"
|
||||
fi
|
||||
|
||||
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 "http://127.0.0.1:${PORT}/" 2>/dev/null || echo fail)
|
||||
echo "STATUS=local_http PORT=$PORT CODE=$code"
|
||||
|
||||
if [[ -d "$NEW_DIR" ]]; then
|
||||
echo "STATUS=cutover done DIR=$NEW_DIR"
|
||||
elif [[ -d "$OLD_DIR" ]]; then
|
||||
echo "STATUS=cutover pending DIR=$OLD_DIR NEXT=cutover-to-silverbullet.sh"
|
||||
else
|
||||
echo "STATUS=cutover unknown (no live dir)"
|
||||
fi
|
||||
23
scripts/notes/up.sh
Executable file
23
scripts/notes/up.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
# Bring up koopa-silverbullet / SilverBullet.
|
||||
set -euo pipefail
|
||||
DIR="${HOME}/koopa-silverbullet"
|
||||
cd "${DIR}"
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
[[ -f .env ]] && source .env
|
||||
set +a
|
||||
PORT="${HOST_PORT:-9028}"
|
||||
podman-compose up -d
|
||||
echo "== wait for :${PORT} =="
|
||||
ok=0
|
||||
for i in $(seq 1 40); do
|
||||
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 5 "http://127.0.0.1:${PORT}/" || true)
|
||||
echo "try $i: $code"
|
||||
case "$code" in
|
||||
2*|3*) ok=1; break ;;
|
||||
esac
|
||||
sleep 3
|
||||
done
|
||||
[[ "$ok" -eq 1 ]] || { echo "ERROR: no answer on ${PORT}"; podman-compose ps; exit 1; }
|
||||
echo "local ok. public (after Caddy+DNS): https://silverbullet.hacktivism.ch/"
|
||||
89
scripts/podman-rss.sh
Normal file
89
scripts/podman-rss.sh
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#!/usr/bin/env bash
|
||||
# Human-readable container RSS when rootless podman stats shows 0B / host RAM.
|
||||
# Cause on openSUSE: user@.service Delegate= (20-defaults-SUSE.conf) clears
|
||||
# memory/cpu controllers → no memory.current. CPU% from podman stats still works.
|
||||
# Usage: podman-rss.sh [--json] [NAME_FILTER…]
|
||||
set -euo pipefail
|
||||
|
||||
JSON=0
|
||||
FILTERS=()
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--json|-j) JSON=1 ;;
|
||||
-h|--help)
|
||||
echo "usage: $0 [--json] [NAME_FILTER…]" >&2
|
||||
exit 0
|
||||
;;
|
||||
*) FILTERS+=("$a") ;;
|
||||
esac
|
||||
done
|
||||
|
||||
human() {
|
||||
# stdin: kibibytes integer
|
||||
awk '
|
||||
{
|
||||
k=$1+0
|
||||
if (k >= 1048576) printf "%.1f GiB", k/1048576
|
||||
else if (k >= 1024) printf "%.0f MiB", k/1024
|
||||
else printf "%d KiB", k
|
||||
}'
|
||||
}
|
||||
|
||||
# CPU map: name -> cpu%
|
||||
declare -A CPU=()
|
||||
while IFS=$'\t' read -r name cpu; do
|
||||
[ -n "$name" ] || continue
|
||||
CPU["$name"]="$cpu"
|
||||
done < <(podman stats --no-stream --format '{{.Name}}\t{{.CPUPerc}}' 2>/dev/null || true)
|
||||
|
||||
rows=()
|
||||
total_kib=0
|
||||
while IFS= read -r cid; do
|
||||
[ -n "$cid" ] || continue
|
||||
name=$(podman inspect -f '{{.Name}}' "$cid" 2>/dev/null || true)
|
||||
[ -n "$name" ] || continue
|
||||
if [ ${#FILTERS[@]} -gt 0 ]; then
|
||||
ok=0
|
||||
for f in "${FILTERS[@]}"; do
|
||||
case "$name" in *"$f"*) ok=1; break ;; esac
|
||||
done
|
||||
[ "$ok" -eq 1 ] || continue
|
||||
fi
|
||||
# podman top rss is KiB per process; sum
|
||||
kib=$(podman top "$cid" -o rss 2>/dev/null | awk 'NR>1{s+=$1} END{print s+0}')
|
||||
cpu="${CPU[$name]:-?}"
|
||||
rows+=("${kib}|${name}|${cpu}")
|
||||
total_kib=$((total_kib + kib))
|
||||
done < <(podman ps -q)
|
||||
|
||||
IFS=$'\n' sorted=($(printf '%s\n' "${rows[@]:-}" | sort -t'|' -k1,1nr))
|
||||
unset IFS
|
||||
|
||||
if [ "$JSON" -eq 1 ]; then
|
||||
python3 - "$total_kib" "${sorted[@]:-}" <<'PY'
|
||||
import json, sys
|
||||
total = int(sys.argv[1]) if len(sys.argv) > 1 else 0
|
||||
items = []
|
||||
for row in sys.argv[2:]:
|
||||
if not row.strip():
|
||||
continue
|
||||
kib, name, cpu = row.split("|", 2)
|
||||
items.append({"name": name, "rss_kib": int(kib), "cpu": cpu})
|
||||
print(json.dumps({"ok": True, "total_rss_kib": total, "containers": items}, indent=2))
|
||||
PY
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf '%-44s %10s %8s\n' "NAME" "RSS" "CPU%"
|
||||
printf '%-44s %10s %8s\n' "----" "---" "----"
|
||||
for row in "${sorted[@]:-}"; do
|
||||
[ -n "$row" ] || continue
|
||||
kib="${row%%|*}"
|
||||
rest="${row#*|}"
|
||||
name="${rest%%|*}"
|
||||
cpu="${rest##*|}"
|
||||
hum=$(printf '%s' "$kib" | human)
|
||||
printf '%-44s %10s %8s\n' "$name" "$hum" "$cpu"
|
||||
done
|
||||
printf '%-44s %10s\n' "TOTAL" "$(printf '%s' "$total_kib" | human)"
|
||||
echo "# note: RSS via podman top (no memory cgroup); CPU via podman stats" >&2
|
||||
654
scripts/upgrade-containers.sh
Executable file
654
scripts/upgrade-containers.sh
Executable file
|
|
@ -0,0 +1,654 @@
|
|||
#!/usr/bin/env bash
|
||||
# Upgrade hernani podman containers on koopa: per-target dry-run (prod-safe),
|
||||
# then full backup (compose + mounts/volumes + DB dumps), then apply.
|
||||
#
|
||||
# Run on koopa as hernani:
|
||||
# ~/upgrade-containers.sh # dry-run only (default)
|
||||
# ~/upgrade-containers.sh --apply # dry-run → backup → upgrade per target
|
||||
# ~/upgrade-containers.sh --apply --images-only
|
||||
# ~/upgrade-containers.sh --apply --os-only
|
||||
# ~/upgrade-containers.sh --apply --recreate-only
|
||||
# ~/upgrade-containers.sh --apply --only koopa-forgejo,koopa-notes
|
||||
# ~/upgrade-containers.sh --apply --skip 'taler-hacktivism*,goa-regio-ng'
|
||||
# ~/upgrade-containers.sh --apply --taler-debs
|
||||
# ~/upgrade-containers.sh --apply --rebuild-local
|
||||
# ~/upgrade-containers.sh --apply --include-stopped
|
||||
# ~/upgrade-containers.sh --apply --backup-max-gb 80
|
||||
# ~/upgrade-containers.sh --apply --no-backup # emergency only
|
||||
#
|
||||
# Per-target --apply flow:
|
||||
# 1) dry-run checks that do not recreate/restart or mutate packages
|
||||
# 2) if dry-run fails → skip that target (others continue)
|
||||
# 3) full backup under ~/backups/container-upgrades/<ts>/<name>/
|
||||
# 4) if backup fails → skip apply for that target
|
||||
# 5) pull / recreate / OS upgrade as selected
|
||||
set -euo pipefail
|
||||
|
||||
APPLY=0
|
||||
DO_IMAGES=1
|
||||
DO_RECREATE=1
|
||||
DO_OS=1
|
||||
DO_TALER_DEBS=0
|
||||
REBUILD_LOCAL=0
|
||||
INCLUDE_STOPPED=0
|
||||
DO_BACKUP=1
|
||||
BACKUP_MAX_GB=50
|
||||
ONLY_RAW=""
|
||||
SKIP_RAW=""
|
||||
HOME_DIR="${HOME:-/home/hernani}"
|
||||
ADMIN_LOG="${ADMIN_LOG:-$HOME_DIR/koopa-admin-log}"
|
||||
BACKUP_ROOT="${BACKUP_ROOT:-$HOME_DIR/backups/container-upgrades}"
|
||||
RUN_TS="$(date '+%Y%m%d-%H%M%S')"
|
||||
BACKUP_RUN_DIR="$BACKUP_ROOT/$RUN_TS"
|
||||
|
||||
LOG_TS() { date '+%Y-%m-%dT%H:%M:%S%z'; }
|
||||
log() { printf '[%s] %s\n' "$(LOG_TS)" "$*"; }
|
||||
run() {
|
||||
if [[ "$APPLY" -eq 1 ]]; then
|
||||
log "RUN: $*"
|
||||
"$@"
|
||||
else
|
||||
log "DRY: $*"
|
||||
fi
|
||||
}
|
||||
|
||||
usage() {
|
||||
sed -n '2,28p' "$0" | sed 's/^# \{0,1\}//'
|
||||
exit "${1:-0}"
|
||||
}
|
||||
|
||||
match_glob_list() {
|
||||
local name="$1" raw="$2" g
|
||||
[[ -z "$raw" ]] && return 1
|
||||
raw="${raw//,/ }"
|
||||
for g in $raw; do
|
||||
# shellcheck disable=SC2254
|
||||
case "$name" in
|
||||
$g) return 0 ;;
|
||||
esac
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h|--help) usage 0 ;;
|
||||
--apply) APPLY=1; shift ;;
|
||||
--dry-run) APPLY=0; shift ;;
|
||||
--images-only) DO_IMAGES=1; DO_RECREATE=0; DO_OS=0; shift ;;
|
||||
--os-only) DO_IMAGES=0; DO_RECREATE=0; DO_OS=1; shift ;;
|
||||
--recreate-only) DO_IMAGES=0; DO_RECREATE=1; DO_OS=0; shift ;;
|
||||
--taler-debs) DO_TALER_DEBS=1; shift ;;
|
||||
--rebuild-local) REBUILD_LOCAL=1; shift ;;
|
||||
--include-stopped) INCLUDE_STOPPED=1; shift ;;
|
||||
--no-backup) DO_BACKUP=0; shift ;;
|
||||
--backup-max-gb) BACKUP_MAX_GB="${2:-50}"; shift 2 ;;
|
||||
--backup-max-gb=*) BACKUP_MAX_GB="${1#*=}"; shift ;;
|
||||
--only) ONLY_RAW="${2:-}"; shift 2 ;;
|
||||
--skip) SKIP_RAW="${2:-}"; shift 2 ;;
|
||||
--only=*) ONLY_RAW="${1#*=}"; shift ;;
|
||||
--skip=*) SKIP_RAW="${1#*=}"; shift ;;
|
||||
*) log "ERROR: unknown arg: $1"; usage 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$(id -un)" != "hernani" ]] && [[ "${FORCE_USER:-}" != "1" ]]; then
|
||||
log "WARN: expected user hernani (got $(id -un)); set FORCE_USER=1 to override"
|
||||
fi
|
||||
|
||||
declare -a NAMES=()
|
||||
declare -A IMG=()
|
||||
declare -A CDIR=()
|
||||
declare -A STATUS=() # ok|dry_fail|backup_fail|upgraded|skipped
|
||||
declare -A NOTE=()
|
||||
|
||||
list_cmd=(podman ps --format '{{.Names}}|{{.Image}}')
|
||||
if [[ "$INCLUDE_STOPPED" -eq 1 ]]; then
|
||||
list_cmd=(podman ps -a --format '{{.Names}}|{{.Image}}')
|
||||
fi
|
||||
|
||||
while IFS='|' read -r name image; do
|
||||
[[ -z "$name" ]] && continue
|
||||
if [[ ! "$name" =~ ^(koopa-|taler-|goa-|mytops-) ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ -n "$ONLY_RAW" ]] && ! match_glob_list "$name" "$ONLY_RAW"; then
|
||||
continue
|
||||
fi
|
||||
if match_glob_list "$name" "$SKIP_RAW"; then
|
||||
log "SKIP name=$name (matched --skip)"
|
||||
continue
|
||||
fi
|
||||
NAMES+=("$name")
|
||||
IMG["$name"]="$image"
|
||||
dir=$(podman inspect -f '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' "$name" 2>/dev/null || true)
|
||||
CDIR["$name"]="$dir"
|
||||
STATUS["$name"]="pending"
|
||||
done < <("${list_cmd[@]}")
|
||||
|
||||
if [[ ${#NAMES[@]} -eq 0 ]]; then
|
||||
log "No matching containers."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "MODE=$([ "$APPLY" -eq 1 ] && echo apply || echo dry-run) images=$DO_IMAGES recreate=$DO_RECREATE os=$DO_OS backup=$DO_BACKUP backup_max_gb=$BACKUP_MAX_GB"
|
||||
log "Targets (${#NAMES[@]}): ${NAMES[*]}"
|
||||
if [[ "$APPLY" -eq 1 && "$DO_BACKUP" -eq 1 ]]; then
|
||||
mkdir -p "$BACKUP_RUN_DIR"
|
||||
log "BACKUP_DIR=$BACKUP_RUN_DIR"
|
||||
fi
|
||||
|
||||
is_pinned_tag() {
|
||||
local ref="$1" tag="${1##*:}"
|
||||
[[ "$ref" != *:* ]] && return 1
|
||||
case "$tag" in
|
||||
latest|stable|stable-*|main|master|landing|live|setup|rootless|*-rootless|alpine|*-alpine|[0-9]|[0-9][0-9]|[0-9].*-alpine|[0-9][0-9]-alpine) return 1 ;;
|
||||
esac
|
||||
if [[ "$tag" =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- prod-safe dry-run for one container (no recreate, no package mutate) ---
|
||||
dry_run_one() {
|
||||
local name="$1" ref="${IMG[$name]}" dir="${CDIR[$name]}"
|
||||
local rc=0
|
||||
local tmp
|
||||
tmp=$(mktemp)
|
||||
|
||||
# running?
|
||||
if ! podman inspect "$name" >/dev/null 2>&1; then
|
||||
log "DRYFAIL $name: inspect failed"
|
||||
NOTE["$name"]="inspect failed"
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# image phase checks
|
||||
if [[ "$DO_IMAGES" -eq 1 ]]; then
|
||||
if [[ "$ref" == localhost/* ]]; then
|
||||
if ! podman image exists "$ref" 2>/dev/null; then
|
||||
log "DRYFAIL $name: local image missing $ref"
|
||||
NOTE["$name"]="local image missing"
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
log "DRYOK $name: local image present $ref"
|
||||
else
|
||||
# pull --dry-run is not universal; probe registry without replacing running CT
|
||||
if podman pull --help 2>&1 | grep -q -- '--quiet'; then
|
||||
:
|
||||
fi
|
||||
if ! podman image exists "$ref" 2>/dev/null; then
|
||||
log "DRYWARN $name: image not local yet ($ref) — apply will pull"
|
||||
else
|
||||
log "DRYOK $name: image present locally $ref"
|
||||
fi
|
||||
# non-mutating registry probe when possible
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
:
|
||||
fi
|
||||
# Try a no-restart pull into local store is mutating cache but not prod CT.
|
||||
# For dry-run we only validate the ref string / inspect local digest.
|
||||
if is_pinned_tag "$ref"; then
|
||||
log "DRYOK $name: pinned tag $ref (compose bump needed to move)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# compose config validate (no up)
|
||||
if [[ "$DO_RECREATE" -eq 1 ]]; then
|
||||
if [[ -n "$dir" ]]; then
|
||||
if [[ ! -f "$dir/compose.yml" && ! -f "$dir/compose.yaml" && ! -f "$dir/docker-compose.yml" ]]; then
|
||||
log "DRYFAIL $name: compose dir without compose file: $dir"
|
||||
NOTE["$name"]="no compose file"
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
if ! (cd "$dir" && podman-compose config >/dev/null 2>"$tmp"); then
|
||||
log "DRYFAIL $name: podman-compose config failed"
|
||||
sed -n '1,20p' "$tmp" | while IFS= read -r line; do log " $line"; done
|
||||
NOTE["$name"]="compose config failed"
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
log "DRYOK $name: compose config ok ($dir)"
|
||||
else
|
||||
log "DRYOK $name: no compose label — recreate will be skipped"
|
||||
fi
|
||||
fi
|
||||
|
||||
# OS simulate (no real upgrade)
|
||||
if [[ "$DO_OS" -eq 1 ]]; then
|
||||
if podman ps --format '{{.Names}}' | grep -qx "$name"; then
|
||||
local os_script
|
||||
os_script=$(cat <<'INNER'
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update -qq
|
||||
apt-get -s -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold upgrade >/tmp/os-dry.out
|
||||
echo OS_DRY=apt ok
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
apk update >/dev/null
|
||||
apk upgrade --simulate >/tmp/os-dry.out 2>&1 || apk upgrade -s >/tmp/os-dry.out 2>&1 || true
|
||||
echo OS_DRY=apk ok
|
||||
elif command -v zypper >/dev/null 2>&1; then
|
||||
zypper -n refresh >/dev/null
|
||||
zypper -n update -D >/tmp/os-dry.out 2>&1 || true
|
||||
echo OS_DRY=zypper ok
|
||||
elif command -v microdnf >/dev/null 2>&1; then
|
||||
echo OS_DRY=microdnf none-sim
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
dnf -y check-update >/tmp/os-dry.out 2>&1 || true
|
||||
echo OS_DRY=dnf ok
|
||||
else
|
||||
echo OS_DRY=none
|
||||
fi
|
||||
INNER
|
||||
)
|
||||
if podman exec -u 0 "$name" bash -lc "$os_script" >"$tmp" 2>&1 \
|
||||
|| podman exec -u 0 "$name" sh -lc "$os_script" >"$tmp" 2>&1; then
|
||||
log "DRYOK $name: OS simulate ($(grep -E '^OS_DRY=' "$tmp" | tail -1 || echo ok))"
|
||||
else
|
||||
# many distroless/app images have no pkg manager — treat as ok/skip
|
||||
if grep -q 'OS_DRY=none' "$tmp" 2>/dev/null; then
|
||||
log "DRYOK $name: no package manager (OS skip)"
|
||||
else
|
||||
log "DRYWARN $name: OS simulate soft-fail (will try apply carefully)"
|
||||
sed -n '1,8p' "$tmp" | while IFS= read -r line; do log " $line"; done
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log "DRYOK $name: not running — OS phase will skip"
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f "$tmp"
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
# --- full backup with data ---
|
||||
backup_one() {
|
||||
local name="$1"
|
||||
local dest="$BACKUP_RUN_DIR/$name"
|
||||
local meta="$dest/META.txt"
|
||||
local max_bytes=$((BACKUP_MAX_GB * 1024 * 1024 * 1024))
|
||||
mkdir -p "$dest/binds" "$dest/volumes" "$dest/db" "$dest/compose"
|
||||
|
||||
{
|
||||
echo "name=$name"
|
||||
echo "image=${IMG[$name]}"
|
||||
echo "compose_dir=${CDIR[$name]:-}"
|
||||
echo "ts=$RUN_TS"
|
||||
} >"$meta"
|
||||
|
||||
podman inspect "$name" >"$dest/inspect.json" 2>/dev/null || true
|
||||
|
||||
# compose project files
|
||||
local dir="${CDIR[$name]}"
|
||||
if [[ -n "$dir" && -d "$dir" ]]; then
|
||||
for f in compose.yml compose.yaml docker-compose.yml docker-compose.yaml .env; do
|
||||
if [[ -f "$dir/$f" ]]; then
|
||||
cp -a "$dir/$f" "$dest/compose/$f"
|
||||
fi
|
||||
done
|
||||
# shallow copy of known config sidecars (not whole tree)
|
||||
for f in Caddyfile Containerfile Dockerfile; do
|
||||
[[ -f "$dir/$f" ]] && cp -a "$dir/$f" "$dest/compose/$f"
|
||||
done
|
||||
fi
|
||||
|
||||
# bind mounts + named volumes from inspect
|
||||
local mounts
|
||||
mounts=$(podman inspect -f '{{range .Mounts}}{{.Type}}|{{.Name}}|{{.Source}}|{{.Destination}}|{{.RW}}{{println}}{{end}}' "$name" 2>/dev/null || true)
|
||||
while IFS='|' read -r mtype mname src dst rw; do
|
||||
[[ -z "$mtype" ]] && continue
|
||||
# skip ephemeral / host tz
|
||||
case "$dst" in
|
||||
/etc/localtime|/etc/timezone|/run|/run/*|/tmp|/tmp/*|/dev|/dev/*|/proc|/proc/*|/sys|/sys/*) continue ;;
|
||||
esac
|
||||
case "$src" in
|
||||
/usr/share/zoneinfo/*|/etc/localtime) continue ;;
|
||||
esac
|
||||
|
||||
if [[ "$mtype" == "bind" ]]; then
|
||||
if [[ ! -e "$src" ]]; then
|
||||
log "BACKUPWARN $name: bind missing $src"
|
||||
continue
|
||||
fi
|
||||
local sz
|
||||
sz=$(du -sb "$src" 2>/dev/null | awk '{print $1}')
|
||||
if [[ -n "$sz" && "$sz" -gt "$max_bytes" ]]; then
|
||||
log "BACKUPWARN $name: skip huge bind $src (${sz}B > ${BACKUP_MAX_GB}G); raise --backup-max-gb"
|
||||
echo "SKIP_HUGE_BIND $src size=$sz" >>"$meta"
|
||||
continue
|
||||
fi
|
||||
local base
|
||||
base=$(echo "$dst" | sed 's#^/##; s#/#_#g')
|
||||
[[ -z "$base" ]] && base="root"
|
||||
log "BACKUP $name: bind $src → binds/${base}.tar.gz"
|
||||
if ! tar -C "$(dirname "$src")" -czf "$dest/binds/${base}.tar.gz" "$(basename "$src")" 2>>"$dest/backup.err"; then
|
||||
log "BACKUPFAIL $name: tar bind $src"
|
||||
NOTE["$name"]="backup bind failed: $src"
|
||||
return 1
|
||||
fi
|
||||
echo "BIND $src -> binds/${base}.tar.gz" >>"$meta"
|
||||
elif [[ "$mtype" == "volume" ]]; then
|
||||
local vname="${mname:-}"
|
||||
if [[ -z "$vname" ]]; then
|
||||
# fallback: export via source path
|
||||
if [[ -d "$src" ]]; then
|
||||
local vbase
|
||||
vbase=$(basename "$(dirname "$src")")
|
||||
log "BACKUP $name: volume-path $src → volumes/${vbase}.tar.gz"
|
||||
if ! tar -C "$src" -czf "$dest/volumes/${vbase}.tar.gz" . 2>>"$dest/backup.err"; then
|
||||
log "BACKUPFAIL $name: tar volume path $src"
|
||||
NOTE["$name"]="backup volume failed"
|
||||
return 1
|
||||
fi
|
||||
echo "VOLPATH $src -> volumes/${vbase}.tar.gz" >>"$meta"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
local sz
|
||||
sz=$(du -sb "$src" 2>/dev/null | awk '{print $1}')
|
||||
if [[ -n "$sz" && "$sz" -gt "$max_bytes" ]]; then
|
||||
log "BACKUPWARN $name: skip huge volume $vname (${sz}B > ${BACKUP_MAX_GB}G)"
|
||||
echo "SKIP_HUGE_VOL $vname size=$sz" >>"$meta"
|
||||
continue
|
||||
fi
|
||||
log "BACKUP $name: volume $vname → volumes/${vname}.tar"
|
||||
if podman volume export "$vname" -o "$dest/volumes/${vname}.tar" 2>>"$dest/backup.err"; then
|
||||
gzip -f "$dest/volumes/${vname}.tar" 2>/dev/null || true
|
||||
echo "VOLUME $vname -> volumes/${vname}.tar.gz" >>"$meta"
|
||||
else
|
||||
# fallback tar of mount source
|
||||
if [[ -d "$src" ]] && tar -C "$src" -czf "$dest/volumes/${vname}.tar.gz" . 2>>"$dest/backup.err"; then
|
||||
echo "VOLPATH $src -> volumes/${vname}.tar.gz" >>"$meta"
|
||||
else
|
||||
log "BACKUPFAIL $name: volume $vname"
|
||||
NOTE["$name"]="backup volume failed: $vname"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done <<<"$mounts"
|
||||
|
||||
# DB dumps when obvious
|
||||
if podman ps --format '{{.Names}}' | grep -qx "$name"; then
|
||||
local img_l
|
||||
img_l=$(echo "${IMG[$name]}" | tr '[:upper:]' '[:lower:]')
|
||||
case "$img_l" in
|
||||
*postgres*|*postgis*)
|
||||
local dbuser
|
||||
dbuser=$(podman exec "$name" printenv POSTGRES_USER 2>/dev/null || echo postgres)
|
||||
local dbname
|
||||
dbname=$(podman exec "$name" printenv POSTGRES_DB 2>/dev/null || echo postgres)
|
||||
log "BACKUP $name: pg_dumpall"
|
||||
if podman exec -u 0 "$name" bash -lc "pg_dumpall -U '$dbuser'" >"$dest/db/pg_dumpall.sql" 2>>"$dest/backup.err"; then
|
||||
gzip -f "$dest/db/pg_dumpall.sql"
|
||||
echo "PG_DUMPALL ok db=$dbname user=$dbuser" >>"$meta"
|
||||
else
|
||||
# single-db fallback
|
||||
if podman exec -u 0 "$name" bash -lc "pg_dump -U '$dbuser' '$dbname'" >"$dest/db/pg_dump.sql" 2>>"$dest/backup.err"; then
|
||||
gzip -f "$dest/db/pg_dump.sql"
|
||||
echo "PG_DUMP ok db=$dbname" >>"$meta"
|
||||
else
|
||||
log "BACKUPWARN $name: postgres dump failed (mounts still saved)"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
*mariadb*|*mysql*)
|
||||
local muser mpass mdb
|
||||
muser=$(podman exec "$name" printenv MYSQL_USER 2>/dev/null || podman exec "$name" printenv MARIADB_USER 2>/dev/null || echo root)
|
||||
mpass=$(podman exec "$name" printenv MYSQL_ROOT_PASSWORD 2>/dev/null || podman exec "$name" printenv MARIADB_ROOT_PASSWORD 2>/dev/null || podman exec "$name" printenv MYSQL_PASSWORD 2>/dev/null || true)
|
||||
mdb=$(podman exec "$name" printenv MYSQL_DATABASE 2>/dev/null || podman exec "$name" printenv MARIADB_DATABASE 2>/dev/null || echo "")
|
||||
log "BACKUP $name: mariadb/mysql dump"
|
||||
if [[ -n "$mpass" ]]; then
|
||||
if podman exec -u 0 "$name" bash -lc "MYSQL_PWD='$mpass' mysqldump -u root --all-databases" >"$dest/db/mysql_all.sql" 2>>"$dest/backup.err" \
|
||||
|| podman exec -u 0 "$name" bash -lc "MYSQL_PWD='$mpass' mariadb-dump -u root --all-databases" >"$dest/db/mysql_all.sql" 2>>"$dest/backup.err"; then
|
||||
gzip -f "$dest/db/mysql_all.sql"
|
||||
echo "MYSQL_DUMPALL ok" >>"$meta"
|
||||
else
|
||||
log "BACKUPWARN $name: mysql dump failed (mounts still saved)"
|
||||
fi
|
||||
else
|
||||
log "BACKUPWARN $name: no mysql password in env — skip dump"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo "BACKUP_OK" >>"$meta"
|
||||
log "BACKUPOK $name → $dest"
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- apply phases for one container ---
|
||||
apply_images_one() {
|
||||
local name="$1" ref="${IMG[$name]}"
|
||||
[[ "$DO_IMAGES" -eq 1 ]] || return 0
|
||||
if [[ "$ref" == localhost/* ]]; then
|
||||
log "LOCAL image=$ref (container=$name) — pull skipped"
|
||||
return 0
|
||||
fi
|
||||
if is_pinned_tag "$ref"; then
|
||||
log "PINNED image=$ref (container=$name) — pull same tag only"
|
||||
fi
|
||||
run podman pull "$ref"
|
||||
}
|
||||
|
||||
rebuild_one_local() {
|
||||
local name="$1" ref="${IMG[$name]}" dir="${CDIR[$name]}"
|
||||
[[ "$ref" == localhost/* ]] || return 0
|
||||
local cf=""
|
||||
if [[ -n "$dir" && -f "$dir/Containerfile" ]]; then
|
||||
cf="$dir/Containerfile"
|
||||
elif [[ -n "$dir" && -f "$dir/Dockerfile" ]]; then
|
||||
cf="$dir/Dockerfile"
|
||||
fi
|
||||
case "$name" in
|
||||
koopa-nym)
|
||||
[[ -x "$ADMIN_LOG/scripts/nym/build.sh" ]] && { run bash "$ADMIN_LOG/scripts/nym/build.sh"; return; }
|
||||
;;
|
||||
koopa-tor-relay)
|
||||
if [[ -f "$HOME_DIR/koopa-tor-relay/build/Containerfile" ]]; then
|
||||
run podman build -t localhost/koopa-tor-relay:latest -f "$HOME_DIR/koopa-tor-relay/build/Containerfile" "$HOME_DIR/koopa-tor-relay/build"
|
||||
return
|
||||
fi
|
||||
;;
|
||||
koopa-paivana)
|
||||
if [[ -f "$dir/Containerfile" ]]; then
|
||||
run podman build -t localhost/koopa-paivana:latest -f "$dir/Containerfile" "$dir"
|
||||
return
|
||||
fi
|
||||
;;
|
||||
koopa-bbb-front)
|
||||
[[ -x "$ADMIN_LOG/scripts/bbb/build.sh" ]] && { run bash "$ADMIN_LOG/scripts/bbb/build.sh"; return; }
|
||||
;;
|
||||
koopa-meet-web)
|
||||
[[ -x "$ADMIN_LOG/scripts/meet/build.sh" ]] && { run bash "$ADMIN_LOG/scripts/meet/build.sh"; return; }
|
||||
;;
|
||||
koopa-galene|koopa-paivana-galene)
|
||||
[[ -x "$ADMIN_LOG/scripts/galene/build.sh" ]] && { run bash "$ADMIN_LOG/scripts/galene/build.sh"; return; }
|
||||
;;
|
||||
koopa-cryptpad|koopa-cryptpad-edge|koopa-paivana-cryptpad)
|
||||
[[ -x "$ADMIN_LOG/scripts/cryptpad/build.sh" ]] && { run bash "$ADMIN_LOG/scripts/cryptpad/build.sh"; return; }
|
||||
;;
|
||||
koopa-notes|koopa-silverbullet)
|
||||
[[ -x "$ADMIN_LOG/scripts/notes/build.sh" ]] && { run bash "$ADMIN_LOG/scripts/notes/build.sh"; return; }
|
||||
;;
|
||||
esac
|
||||
if [[ -n "$cf" ]]; then
|
||||
run podman build -t "$ref" -f "$cf" "$(dirname "$cf")"
|
||||
else
|
||||
log "NO-REBUILD path for $name ($ref)"
|
||||
fi
|
||||
}
|
||||
|
||||
apply_recreate_one() {
|
||||
local name="$1" dir="${CDIR[$name]}"
|
||||
[[ "$DO_RECREATE" -eq 1 ]] || return 0
|
||||
if [[ -z "$dir" ]]; then
|
||||
log "NO-COMPOSE container=$name — recreate skipped"
|
||||
return 0
|
||||
fi
|
||||
if [[ ! -f "$dir/compose.yml" && ! -f "$dir/compose.yaml" && ! -f "$dir/docker-compose.yml" ]]; then
|
||||
log "WARN: compose dir without compose file: $dir"
|
||||
return 0
|
||||
fi
|
||||
if [[ "$APPLY" -eq 1 ]]; then
|
||||
log "RUN: (cd $dir && podman-compose pull && podman-compose up -d)"
|
||||
(cd "$dir" && podman-compose pull && podman-compose up -d)
|
||||
else
|
||||
log "DRY: (cd $dir && podman-compose pull && podman-compose up -d)"
|
||||
fi
|
||||
}
|
||||
|
||||
os_upgrade_one() {
|
||||
local name="$1"
|
||||
[[ "$DO_OS" -eq 1 ]] || return 0
|
||||
if ! podman ps --format '{{.Names}}' | grep -qx "$name"; then
|
||||
log "SKIP os name=$name (not running)"
|
||||
return 0
|
||||
fi
|
||||
local script
|
||||
script=$(cat <<'INNER'
|
||||
set -e
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update -qq
|
||||
apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold upgrade
|
||||
echo OS=apt ok
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
apk update
|
||||
apk upgrade
|
||||
echo OS=apk ok
|
||||
elif command -v zypper >/dev/null 2>&1; then
|
||||
zypper -n refresh
|
||||
zypper -n update
|
||||
echo OS=zypper ok
|
||||
elif command -v microdnf >/dev/null 2>&1; then
|
||||
microdnf -y upgrade
|
||||
echo OS=microdnf ok
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
dnf -y upgrade
|
||||
echo OS=dnf ok
|
||||
else
|
||||
echo OS=none
|
||||
exit 0
|
||||
fi
|
||||
INNER
|
||||
)
|
||||
if [[ "$APPLY" -eq 1 ]]; then
|
||||
log "RUN: podman exec -u 0 $name OS-upgrade"
|
||||
if ! podman exec -u 0 "$name" bash -lc "$script"; then
|
||||
podman exec -u 0 "$name" sh -lc "$script" || log "WARN: OS upgrade failed for $name"
|
||||
fi
|
||||
else
|
||||
log "DRY: podman exec -u 0 $name OS-upgrade"
|
||||
fi
|
||||
}
|
||||
|
||||
# Deduplicate compose recreate by dir
|
||||
declare -A COMPOSE_DONE=()
|
||||
declare -A IMAGE_DONE=()
|
||||
declare -A BACKUP_DONE=()
|
||||
|
||||
process_one() {
|
||||
local name="$1"
|
||||
log "==== target: $name (${IMG[$name]}) ===="
|
||||
|
||||
# 1) always dry-run first (prod-safe)
|
||||
if ! dry_run_one "$name"; then
|
||||
STATUS["$name"]="dry_fail"
|
||||
log "SKIP apply $name (dry-run failed)"
|
||||
return 0
|
||||
fi
|
||||
STATUS["$name"]="dry_ok"
|
||||
|
||||
# global dry-run mode: stop after checks
|
||||
if [[ "$APPLY" -eq 0 ]]; then
|
||||
log "DRY-RUN only for $name (no backup/apply)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 2) full backup with data BEFORE any mutate (even after dry-run ok)
|
||||
# every container — do not skip siblings in a compose stack (DB/data)
|
||||
if [[ "$DO_BACKUP" -eq 1 ]]; then
|
||||
if [[ -z "${BACKUP_DONE[$name]:-}" ]]; then
|
||||
if ! backup_one "$name"; then
|
||||
STATUS["$name"]="backup_fail"
|
||||
log "SKIP apply $name (backup failed)"
|
||||
return 0
|
||||
fi
|
||||
BACKUP_DONE["$name"]=1
|
||||
else
|
||||
log "BACKUP skip $name (already backed this container)"
|
||||
fi
|
||||
else
|
||||
log "WARN: --no-backup set; proceeding without backup for $name"
|
||||
fi
|
||||
|
||||
# 3) apply
|
||||
if [[ -z "${IMAGE_DONE[${IMG[$name]}]:-}" ]]; then
|
||||
apply_images_one "$name" || { STATUS["$name"]="apply_fail"; NOTE["$name"]="image pull failed"; return 0; }
|
||||
IMAGE_DONE["${IMG[$name]}"]=1
|
||||
fi
|
||||
if [[ "$REBUILD_LOCAL" -eq 1 ]]; then
|
||||
rebuild_one_local "$name" || true
|
||||
fi
|
||||
local dir="${CDIR[$name]}"
|
||||
if [[ -n "$dir" ]]; then
|
||||
if [[ -z "${COMPOSE_DONE[$dir]:-}" ]]; then
|
||||
apply_recreate_one "$name" || { STATUS["$name"]="apply_fail"; NOTE["$name"]="recreate failed"; return 0; }
|
||||
COMPOSE_DONE["$dir"]=1
|
||||
else
|
||||
log "recreate skip $name (compose dir already done)"
|
||||
fi
|
||||
else
|
||||
apply_recreate_one "$name" || true
|
||||
fi
|
||||
os_upgrade_one "$name" || { STATUS["$name"]="apply_fail"; NOTE["$name"]="os upgrade failed"; return 0; }
|
||||
STATUS["$name"]="upgraded"
|
||||
log "DONE $name"
|
||||
}
|
||||
|
||||
# --- main loop: per container ---
|
||||
for name in "${NAMES[@]}"; do
|
||||
process_one "$name"
|
||||
done
|
||||
|
||||
# optional Taler GOA debs (only on --apply, after per-CT work)
|
||||
if [[ "$DO_TALER_DEBS" -eq 1 ]]; then
|
||||
log "==== phase: taler-debs ===="
|
||||
tscript="$ADMIN_LOG/scripts/taler-shared/upgrade-goa-debs.sh"
|
||||
if [[ "$APPLY" -eq 0 ]]; then
|
||||
log "DRY: bash $tscript"
|
||||
elif [[ -x "$tscript" ]]; then
|
||||
run bash "$tscript"
|
||||
else
|
||||
log "ERROR: missing $tscript"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log "==== summary ===="
|
||||
fail=0
|
||||
for name in "${NAMES[@]}"; do
|
||||
st="${STATUS[$name]:-?}"
|
||||
printf ' %-42s %-12s %s %s\n' "$name" "$st" "${IMG[$name]}" "${NOTE[$name]:-}"
|
||||
case "$st" in
|
||||
dry_fail|backup_fail|apply_fail) fail=1 ;;
|
||||
esac
|
||||
done
|
||||
if [[ "$APPLY" -eq 0 ]]; then
|
||||
log "Dry-run only (default). Re-run with --apply for backup+upgrade per target that passed dry-run."
|
||||
fi
|
||||
if [[ "$fail" -eq 1 ]]; then
|
||||
log "STATUS=partial_fail"
|
||||
exit 1
|
||||
fi
|
||||
log "STATUS=ok"
|
||||
Loading…
Add table
Add a link
Reference in a new issue