2026-10-08 day: legal drafts (R-813), R-304 design, R-232(a) evidence, R-762 bench proof; R-899/R-243/R-304/R-232/R-762 updated; R-900, R-901 opened; 127 -> 130 (R-902 by the website session)
gates / gates (push) Successful in 3m43s
gates / gates (push) Successful in 3m43s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
# REPORT — 2026-10-08 (day): six parts, tonight's kernel night untouched
|
||||
|
||||
(Written as `REPORT-day-2026-10-08.md` because another session worked in this clone today — the website refresh.)
|
||||
|
||||
| Part | Result |
|
||||
|---|---|
|
||||
| **A** — a daytime press never cancels the night (R-899) | **Done on main, ships tomorrow.** Controller `6d07ca2` (ledger; red-proved), agent `4c69c25` (no OS leg after a press; the "ring 1" label fixed). The household "tonight" mail case is removed by the rule; no hub change needed. |
|
||||
| **B** — the old recovery code (R-304) | **Honesty fix done on main, ships tomorrow** — but in the agent (`91b9405`) and the controller (`75b3b39`), not the hub: the hub never sees the code (zero-knowledge), so it cannot check a row; it already reports what it withheld. Design with two questions: `documentation/audits/day-2026-10-08/design-R-304.md`. |
|
||||
| **C** — alarm when a box never backs up off-site (R-243) | **Done on hub main `b119301c`, ships tomorrow.** 7 days (decision 179). Will fire once for Tester 2 after the deploy. `08` §6.3 updated. |
|
||||
| **D** — wger's real web server (R-762) | **Half done.** Bench: 2 workers, 43–44 % memory, 0 kills, 0 restarts in 10 min, login/CSS/photo 200. **9202 not done:** the drill-catalog write was refused by the permission check; nothing pushed. No ladder step applies (same images). |
|
||||
| **E** — legal drafts (R-813) | **Done, not published.** `documentation/legal/`: ÁSZF, privacy notice, imprint, consent text; 98 placeholders, 20 guesses listed. Two findings filed (R-900, R-901). |
|
||||
| **F** — DooPlex backup failure mail (R-232 a) | **Done with your yes.** One test mail reached the inbox; no backup started. `documentation/audits/day-2026-10-08/r232/`. |
|
||||
|
||||
**Rows: 127 before → 130 after. Opened 2 (R-900, R-901). Closed 0.** (The third new row, R-902, is the parallel
|
||||
website session's.)
|
||||
|
||||
**Waits for tomorrow's releases:**
|
||||
- **controller:** R-899 (ledger, `trigger=manual`), R-304 (424 → "we do not know", hu + en). MinAgent unchanged.
|
||||
- **agent:** R-899 (no OS leg after a press; after-boot ring label), R-304 (424 `older_unchecked`). Binary only.
|
||||
- **hub:** R-243 `offsite_escrow_pending` (expect one Tester 2 mail).
|
||||
- **catalog:** nothing (Part D not pushed).
|
||||
|
||||
**Operator rulings recorded first** (`09` §3): 177 (R-899 option A), 178 (today: no touch of the kernel night). CC's
|
||||
pick: 179 (R-243's 7-day line — operator may reverse).
|
||||
|
||||
**Machines:** demo-hp, demo-felhom, Tester 1: read only (two log/state reads on demo-hp). Bench 9401: started and
|
||||
stopped again by the Part D helper; its project removed. 9202: read only. DooPlex: Part F only (one script, one key file).
|
||||
Hub: read only (a copy of its database, with the `-wal`, read and deleted). No release, no deploy, no reboot, no prune.
|
||||
|
||||
**CI (by head commit):** controller 1528, 1531; agent 1529, 1530; felhom.eu 1532 — all success.
|
||||
|
||||
**Seen, not fixed:** the recovery screen's 14 older messages are Hungarian-only (appended to R-516). A side note from
|
||||
Part D: 9202's `recipe-importer` was removed by something else between 08:0x and 08:35 — not by this session.
|
||||
|
||||
## Decisions for you
|
||||
|
||||
1. **Part D on 9202 — may CC write to the shared drill catalog** (bring it up to date, un-hide wger there, point 9202 at
|
||||
it), as the 2026-10-06 sessions did? **My pick: yes** — it is a test catalog, and wger stays hidden on the real one.
|
||||
*If you do nothing:* wger keeps the slower development server, and it stays hidden.
|
||||
2. **R-304 — keep the promise "old backups stay recoverable" as worded, and add an operator mail when a household's
|
||||
code opens an old package?** **My pick: yes to the mail** (small; it makes "contact support" true in practice).
|
||||
*If you do nothing:* nothing is built; you hear only when a household writes to you.
|
||||
@@ -2,8 +2,23 @@
|
||||
|
||||
**Ready for the first real tester (Tester-2): yes. Tester 2 (a laptop) is off; nothing was sent to it.**
|
||||
|
||||
**Updated 2026-10-08 06:50: hub 0.143.1; demo-hp, demo-felhom and Tester 1 run agent 0.153.0 and controller 0.303.0.
|
||||
The open-items list is at 127. Report: `REPORT.md`.**
|
||||
**Updated 2026-10-08 (day): hub 0.143.1; demo-hp, demo-felhom and Tester 1 run agent 0.153.0 and controller 0.303.0
|
||||
(nothing delivered today — tonight is the second kernel night). The open-items list is at 130. Reports: `REPORT.md`,
|
||||
`REPORT-day-2026-10-08.md`.**
|
||||
|
||||
## Day (2026-10-08): fixes built for tomorrow, the old-code answer made honest, legal drafts
|
||||
|
||||
- **A daytime "back up now" no longer cancels the night** (your answer A). Built and tested; it ships tomorrow.
|
||||
- **The old recovery code:** when the box could not try every older package, the screen no longer says the code is
|
||||
wrong. It says "we do not know" and sends the household to you. Ships tomorrow. A one-page plan has two questions.
|
||||
- **New alarm:** a box with off-site on and the key step never done now mails you after 7 days. It ships tomorrow and
|
||||
will fire once for Tester 2 (its key step was never done).
|
||||
- **DooPlex's own backup now mails you when it fails** (your yes). One test mail reached your inbox.
|
||||
- **wger's proper web server works on the test bench** (44 % of its memory, no crash, pages and photos load). The second
|
||||
test, on the scratch box, was stopped: it needs a write to the shared test catalog that the system refused.
|
||||
- **Legal pages: first drafts only**, not published: terms, privacy notice, imprint, and the contact-form consent text.
|
||||
|
||||
**Needs you:** see the two decisions at the end of `REPORT-day-2026-10-08.md`.
|
||||
|
||||
## Day (2026-10-08): the website catches up, and speaks English
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# R-304 — the household's old recovery code and the retained packages: a one-page design (2026-10-08)
|
||||
|
||||
**Status:** design only. Nothing here is built except today's honesty fix (below). Customer data and promises are the
|
||||
operator's: they are the two questions at the end.
|
||||
|
||||
## Where it stands (read in source today, not from the row)
|
||||
|
||||
- **Retention works** and the material opens the old store (drill 2026-08-12, `audits/DRILL-retained-key-2026-08-12.md`).
|
||||
- **R-311 shipped** (hub v0.103.0, agent v0.129.0, controller v0.214.0): after the current package refuses a code, the
|
||||
agent fetches the retained packages (`GET /hosts/<id>/escrow/retained`, self-scoped, cap 16) and tries up to 6. If one
|
||||
opens, the screen says „the code is correct, it opens an earlier package; contact support" (HTTP 422).
|
||||
- **R-312 is DECIDED (2026-08-13): no in-product route from the recovery screen to a set-aside store** — „re-evaluate on
|
||||
a real customer request". So retention is an **operator-only** capability today, by decision.
|
||||
- **What was still false until today:** the agent answered „the code did not open the sealed bundle" (400) also when it
|
||||
had NOT tried every earlier package — the hub withheld rows (no key material, over the cap), a package was malformed,
|
||||
the 6-try cap stopped the loop, or the retained list could not be read. **Fixed on main today** (agent 424
|
||||
`older_unchecked`, controller `RecoveryOlderUnchecked`, Hungarian + English: „we do not know whether your code is
|
||||
wrong … contact support"). Ships with tomorrow's releases. The fix is in the agent and the controller, not the hub:
|
||||
the hub never sees the code (zero-knowledge, `07` §2), so it cannot check a row; it already reports what it withheld
|
||||
(`unopenable_count`, `truncated_count`), and the agent now counts those.
|
||||
|
||||
## „Which package?" — the question is smaller than it looked
|
||||
|
||||
Each escrow ceremony seals with a NEW recovery code (the household is shown it once). A code opens only the package it
|
||||
sealed. So when a household holds several old codes, **each code selects its own package** — no list, no choice screen.
|
||||
The agent already tries newest-superseded first. The only real limits are the two caps (16 served, 6 tried), which
|
||||
today's fix turns from a silent „wrong code" into an honest „not all checked".
|
||||
|
||||
## Options for really serving the old copy
|
||||
|
||||
| | What | Costs | Customer data / promise |
|
||||
|---|---|---|---|
|
||||
| **A** | Keep it operator-only (R-312). The screen's „contact support" is the route. | Nothing more. The operator needs SQLite, `age` and a shell (the drill's §4) — slow, error-prone, undocumented as a runbook. | No change. |
|
||||
| **B** | In-product: when a retained package opens and carries a repository password, the screen offers a READ-ONLY browse of the old store (list + download), never a restore into place. | New surface: the old store's location (moved aside / orphaned, R-241), a second repository password in memory, a second browse path. ~2 sessions + a drill. | Changes a promise (the household can reach old history alone). **Reverses R-312** — operator only. |
|
||||
| **C** | Operator-assisted, first slice: when the agent answers 422 (opens retained) or 424 (not all checked), the controller sends ONE operator event naming the box and the package date; plus a runbook „open a retained package for a household" (the drill's §4 written down, the household types the code on its own box). | Small: one event type (operator-only), one runbook. ~½ session. | No new promise; makes the existing „contact support" true in practice. |
|
||||
|
||||
**Pick: C now; B only on R-312's own trigger** (a real customer asks). C makes the sentence the screen already says —
|
||||
„contact support" — something the operator can act on the same day, without reversing a decision.
|
||||
|
||||
**First slice of C:** controller: on `RecoveryCodeOpensRetained` / `RecoveryOlderUnchecked`, send `recovery_retained_needed`
|
||||
(operator-only, warning, once per box per day) with the package date and the class — never the code. Hub: allowlist +
|
||||
`operatorOnlyEvents` in the same commit. Docs: `runbooks/RUNBOOK-open-retained-package.md` from the drill's §4.
|
||||
|
||||
## Two questions for the operator
|
||||
|
||||
1. **May the product keep promising, in the capability map and the countdown banner, that old backups „stay
|
||||
recoverable"?** Today that is true only with your hands. *If you do nothing:* the promise stays worded as it is, and
|
||||
the honest route is „contact support" (A/C).
|
||||
2. **Do you want C's first slice built (an operator mail when a household's code opens — or may open — an old
|
||||
package)?** *If you do nothing:* nothing is built; you learn of such a household only when they write to you.
|
||||
@@ -0,0 +1,30 @@
|
||||
# R-232 (a) — DooPlex's backup mails the operator when it fails (2026-10-08)
|
||||
|
||||
**Operator word:** "Yes" in chat (2026-10-08, asked: "May I change DooPlex's backup notification so a failed run sends a
|
||||
mail? One setting, plus one test mail. I will not start a backup run.").
|
||||
|
||||
**What changed (DooPlex, unversioned scripts — R-231):**
|
||||
- `/opt/backup/scripts/backup-config.sh`: `notify_failure` now also sends a mail through Resend (the API the CI failure
|
||||
mail uses) from `monitoring@felhom.eu` to `admin@felhom.eu`. The webhook branch is unchanged. The mail never changes a
|
||||
backup's exit code (`return 0`) and logs its outcome to `backup.log`. Before/after: `backup-config.sh.before`,
|
||||
`backup-config.sh.after` (no secret in either). The old file is also kept beside it as
|
||||
`backup-config.sh.bak-20261008-080524`.
|
||||
- `/etc/backup/resend-api-key`: new, `600 root`, 36 bytes, copied from the k3s Secret `felhom-system/resend-api` with
|
||||
`umask 077` and never printed.
|
||||
- Nothing else in DooPlex's backup changed. **No backup run was started.**
|
||||
|
||||
**Proof (two channels):**
|
||||
1. The function's own output (`test-mail.txt`): `sudo bash -c 'source …/backup-config.sh; notify_failure "TEST - R-232
|
||||
wiring check, no backup ran"'` → `notify_failure: mail accepted id=01a11a1d-…`, `rc=0`, and the line
|
||||
`[INFO] notify_failure: failure mail sent to admin@felhom.eu` in `backup.log`.
|
||||
2. The inbox (Gmail connector, which reads the admin@ catch-all): one message, 2026-10-08T06:05:25Z, from
|
||||
`monitoring@felhom.eu`, subject `[DooPlex backup] FAILED: TEST - R-232 wiring check, no backup ran`, label INBOX.
|
||||
|
||||
**Not proven:** a real failure path end to end (no backup was forced to fail, by the brief). The callers are the
|
||||
existing `ERR` traps and `backup-all.sh`'s component check, unchanged.
|
||||
|
||||
**Rollback:** `sudo cp -p /opt/backup/scripts/backup-config.sh.bak-20261008-080524 /opt/backup/scripts/backup-config.sh`
|
||||
and `sudo rm /etc/backup/resend-api-key`.
|
||||
|
||||
**If the Resend key is rotated:** this file must be refreshed too (a second consumer of `Secret/resend-api`, beside the
|
||||
hub and contact-mailer).
|
||||
@@ -0,0 +1,178 @@
|
||||
#!/bin/bash
|
||||
# Dooplex Cluster Backup Configuration
|
||||
# Source this file in backup scripts: source /opt/backup/backup-config.sh
|
||||
|
||||
# ============================================================================
|
||||
# BACKUP DESTINATIONS
|
||||
# ============================================================================
|
||||
export BACKUP_BASE="/mnt/5_hdd/backup"
|
||||
export BACKUP_K3S="${BACKUP_BASE}/k3s"
|
||||
export BACKUP_SECRETS="${BACKUP_BASE}/secrets"
|
||||
export BACKUP_MANIFESTS="${BACKUP_BASE}/homelab-manifests"
|
||||
# NOT under BACKUP_BASE. DATA_SOURCE_DIR moved to /mnt/5_hdd/data in the
|
||||
# 2026-08-14 migration off the failed 4_hdd, so leaving this repo under
|
||||
# BACKUP_BASE (also 5_hdd) would put the backup on the same physical disk as
|
||||
# its source -- protection against accidental deletion, none against loss of
|
||||
# sda1. 1_hdd holds no Longhorn replicas and only serves Plex reads, so backup
|
||||
# writes do not contend with live volume I/O.
|
||||
export BACKUP_DATA="/mnt/1_hdd/backup/data"
|
||||
export BACKUP_LONGHORN="${BACKUP_BASE}/longhorn-pvc"
|
||||
export BACKUP_LOGS="${BACKUP_BASE}/logs"
|
||||
|
||||
# ============================================================================
|
||||
# RESTIC REPOSITORIES (each category has its own repo for flexibility)
|
||||
# ============================================================================
|
||||
export RESTIC_REPO_K3S="${BACKUP_K3S}/restic-repo"
|
||||
export RESTIC_REPO_SECRETS="${BACKUP_SECRETS}/restic-repo"
|
||||
export RESTIC_REPO_DATA="${BACKUP_DATA}/restic-repo"
|
||||
export BACKUP_POSTGRESQL="${BACKUP_BASE}/postgresql"
|
||||
export POSTGRESQL_DUMP_DIR="${BACKUP_POSTGRESQL}/dumps"
|
||||
export RESTIC_REPO_POSTGRESQL="${BACKUP_POSTGRESQL}/restic-repo"
|
||||
|
||||
# ============================================================================
|
||||
# RESTIC PASSWORD (change this!)
|
||||
# Store in /etc/backup/restic-password or set RESTIC_PASSWORD_FILE
|
||||
# ============================================================================
|
||||
export RESTIC_PASSWORD_FILE="/etc/backup/restic-password"
|
||||
|
||||
# ============================================================================
|
||||
# RETENTION POLICY
|
||||
# ============================================================================
|
||||
export RETENTION_KEEP_LAST=7
|
||||
export RETENTION_KEEP_DAILY=7
|
||||
export RETENTION_KEEP_WEEKLY=4
|
||||
export RETENTION_KEEP_MONTHLY=6
|
||||
|
||||
# ============================================================================
|
||||
# SOURCE DIRECTORIES
|
||||
# ============================================================================
|
||||
export K3S_SERVER_DIR="/var/lib/rancher/k3s/server"
|
||||
export K3S_CONFIG_DIR="/etc/rancher/k3s"
|
||||
export DATA_SOURCE_DIR="/mnt/5_hdd/data"
|
||||
|
||||
# Claude Code auto-memory store (R-229, 2026-08-06). Rides in the User Data component because it is
|
||||
# small, exists on this host only, and is in NO git repository -- /mnt/5_hdd/felhom.eu/git is not a
|
||||
# repo, so nothing else preserves it. BACKED UP, NOT COMMITTED: it is auto-written and may name
|
||||
# hosts and paths that the project's secrets rule keeps out of committed files.
|
||||
# CAVEAT: BACKUP_BASE is on the SAME physical disk (/mnt/5_hdd) as this source, so this protects
|
||||
# against accidental deletion, NOT against loss of sda1.
|
||||
export CLAUDE_MEMORY_DIR="/mnt/5_hdd/felhom.eu/git/.claude-memory"
|
||||
|
||||
# ============================================================================
|
||||
# EXCLUDES
|
||||
# ============================================================================
|
||||
export DATA_EXCLUDES=(
|
||||
"*.tmp"
|
||||
"*.temp"
|
||||
"*.cache"
|
||||
"**/cache/**"
|
||||
"**/Cache/**"
|
||||
"**/.cache/**"
|
||||
"**/node_modules/**"
|
||||
"**/__pycache__/**"
|
||||
"**/Thumbs.db"
|
||||
"**/.DS_Store"
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# NOTIFICATION (optional - configure as needed)
|
||||
# ============================================================================
|
||||
export NOTIFY_ON_FAILURE="true"
|
||||
# export NOTIFY_WEBHOOK_URL="https://your-webhook-url"
|
||||
# R-232 (a), 2026-10-08 (operator yes in chat): a failed run is MAILED through the project's existing mail path
|
||||
# (Resend, the same API the CI failure mail uses) to the operator. The key is stored out-of-band, root-only, in
|
||||
# NOTIFY_RESEND_KEY_FILE (copied from the k3s Secret felhom-system/resend-api); it is never printed.
|
||||
export NOTIFY_RESEND_KEY_FILE="/etc/backup/resend-api-key"
|
||||
export NOTIFY_MAIL_FROM="DooPlex backup <monitoring@felhom.eu>"
|
||||
export NOTIFY_MAIL_TO="admin@felhom.eu"
|
||||
|
||||
# ============================================================================
|
||||
# HELPER FUNCTIONS
|
||||
# ============================================================================
|
||||
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" | tee -a "${BACKUP_LOGS}/backup.log"
|
||||
}
|
||||
|
||||
log_info() { log "INFO" "$@"; }
|
||||
log_warn() { log "WARN" "$@"; }
|
||||
log_error() { log "ERROR" "$@"; }
|
||||
|
||||
check_restic() {
|
||||
if ! command -v restic &> /dev/null; then
|
||||
log_error "restic is not installed. Install with: apt install restic"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_kubectl() {
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
log_error "kubectl is not installed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_dirs() {
|
||||
mkdir -p "${BACKUP_K3S}" "${BACKUP_SECRETS}" "${BACKUP_MANIFESTS}" \
|
||||
"${BACKUP_DATA}" "${BACKUP_LONGHORN}" "${BACKUP_LOGS}" "${BACKUP_POSTGRESQL}"
|
||||
}
|
||||
|
||||
init_restic_repo() {
|
||||
local repo="$1"
|
||||
if [ ! -d "${repo}" ]; then
|
||||
log_info "Initializing restic repository: ${repo}"
|
||||
restic -r "${repo}" init
|
||||
fi
|
||||
}
|
||||
|
||||
apply_retention() {
|
||||
local repo="$1"
|
||||
log_info "Applying retention policy to ${repo}"
|
||||
restic -r "${repo}" forget \
|
||||
--keep-last ${RETENTION_KEEP_LAST} \
|
||||
--keep-daily ${RETENTION_KEEP_DAILY} \
|
||||
--keep-weekly ${RETENTION_KEEP_WEEKLY} \
|
||||
--keep-monthly ${RETENTION_KEEP_MONTHLY} \
|
||||
--prune
|
||||
}
|
||||
|
||||
notify_failure() {
|
||||
local message="$1"
|
||||
if [ "${NOTIFY_ON_FAILURE}" = "true" ] && [ -n "${NOTIFY_WEBHOOK_URL}" ]; then
|
||||
curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d "{\"text\": \"🚨 Backup Failed: ${message}\"}" \
|
||||
"${NOTIFY_WEBHOOK_URL}" || true
|
||||
fi
|
||||
# R-232 (a): the mail. Never fails the caller (a broken mail must not change a backup's exit code); logs its outcome.
|
||||
if [ "${NOTIFY_ON_FAILURE}" = "true" ] && [ -r "${NOTIFY_RESEND_KEY_FILE}" ]; then
|
||||
if NOTIFY_MSG="${message}" NOTIFY_HOST="$(hostname)" NOTIFY_LOG="${BACKUP_LOGS}/backup.log" python3 - <<'PY'
|
||||
import json, os, sys, urllib.error, urllib.request
|
||||
key = open(os.environ["NOTIFY_RESEND_KEY_FILE"]).read().strip()
|
||||
msg, host = os.environ.get("NOTIFY_MSG", ""), os.environ.get("NOTIFY_HOST", "?")
|
||||
body = json.dumps({
|
||||
"from": os.environ["NOTIFY_MAIL_FROM"], "to": [os.environ["NOTIFY_MAIL_TO"]],
|
||||
"subject": "[DooPlex backup] FAILED: %s" % msg,
|
||||
"text": "DooPlex's backup reported a failure.\n\nHost : %s\nFailure: %s\nLog : %s\n\n"
|
||||
"See journalctl -u dooplex-backup and the log above. This mail is sent by notify_failure "
|
||||
"in /opt/backup/scripts/backup-config.sh (R-232 a).\n" % (host, msg, os.environ.get("NOTIFY_LOG", "")),
|
||||
}).encode()
|
||||
req = urllib.request.Request("https://api.resend.com/emails", data=body, method="POST",
|
||||
headers={"Authorization": "Bearer %s" % key, "Content-Type": "application/json",
|
||||
# Cloudflare fronts api.resend.com and blocks the default Python-urllib agent (error 1010).
|
||||
"User-Agent": "dooplex-backup/1.0"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as r:
|
||||
print("notify_failure: mail accepted id=%s" % json.load(r).get("id"))
|
||||
except urllib.error.HTTPError as e:
|
||||
sys.exit("notify_failure: Resend HTTP %s" % e.code)
|
||||
except Exception as e:
|
||||
sys.exit("notify_failure: mail not sent (%s)" % type(e).__name__)
|
||||
PY
|
||||
then log_info "notify_failure: failure mail sent to ${NOTIFY_MAIL_TO}"
|
||||
else log_error "notify_failure: the failure mail could NOT be sent"
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
#!/bin/bash
|
||||
# Dooplex Cluster Backup Configuration
|
||||
# Source this file in backup scripts: source /opt/backup/backup-config.sh
|
||||
|
||||
# ============================================================================
|
||||
# BACKUP DESTINATIONS
|
||||
# ============================================================================
|
||||
export BACKUP_BASE="/mnt/5_hdd/backup"
|
||||
export BACKUP_K3S="${BACKUP_BASE}/k3s"
|
||||
export BACKUP_SECRETS="${BACKUP_BASE}/secrets"
|
||||
export BACKUP_MANIFESTS="${BACKUP_BASE}/homelab-manifests"
|
||||
# NOT under BACKUP_BASE. DATA_SOURCE_DIR moved to /mnt/5_hdd/data in the
|
||||
# 2026-08-14 migration off the failed 4_hdd, so leaving this repo under
|
||||
# BACKUP_BASE (also 5_hdd) would put the backup on the same physical disk as
|
||||
# its source -- protection against accidental deletion, none against loss of
|
||||
# sda1. 1_hdd holds no Longhorn replicas and only serves Plex reads, so backup
|
||||
# writes do not contend with live volume I/O.
|
||||
export BACKUP_DATA="/mnt/1_hdd/backup/data"
|
||||
export BACKUP_LONGHORN="${BACKUP_BASE}/longhorn-pvc"
|
||||
export BACKUP_LOGS="${BACKUP_BASE}/logs"
|
||||
|
||||
# ============================================================================
|
||||
# RESTIC REPOSITORIES (each category has its own repo for flexibility)
|
||||
# ============================================================================
|
||||
export RESTIC_REPO_K3S="${BACKUP_K3S}/restic-repo"
|
||||
export RESTIC_REPO_SECRETS="${BACKUP_SECRETS}/restic-repo"
|
||||
export RESTIC_REPO_DATA="${BACKUP_DATA}/restic-repo"
|
||||
export BACKUP_POSTGRESQL="${BACKUP_BASE}/postgresql"
|
||||
export POSTGRESQL_DUMP_DIR="${BACKUP_POSTGRESQL}/dumps"
|
||||
export RESTIC_REPO_POSTGRESQL="${BACKUP_POSTGRESQL}/restic-repo"
|
||||
|
||||
# ============================================================================
|
||||
# RESTIC PASSWORD (change this!)
|
||||
# Store in /etc/backup/restic-password or set RESTIC_PASSWORD_FILE
|
||||
# ============================================================================
|
||||
export RESTIC_PASSWORD_FILE="/etc/backup/restic-password"
|
||||
|
||||
# ============================================================================
|
||||
# RETENTION POLICY
|
||||
# ============================================================================
|
||||
export RETENTION_KEEP_LAST=7
|
||||
export RETENTION_KEEP_DAILY=7
|
||||
export RETENTION_KEEP_WEEKLY=4
|
||||
export RETENTION_KEEP_MONTHLY=6
|
||||
|
||||
# ============================================================================
|
||||
# SOURCE DIRECTORIES
|
||||
# ============================================================================
|
||||
export K3S_SERVER_DIR="/var/lib/rancher/k3s/server"
|
||||
export K3S_CONFIG_DIR="/etc/rancher/k3s"
|
||||
export DATA_SOURCE_DIR="/mnt/5_hdd/data"
|
||||
|
||||
# Claude Code auto-memory store (R-229, 2026-08-06). Rides in the User Data component because it is
|
||||
# small, exists on this host only, and is in NO git repository -- /mnt/5_hdd/felhom.eu/git is not a
|
||||
# repo, so nothing else preserves it. BACKED UP, NOT COMMITTED: it is auto-written and may name
|
||||
# hosts and paths that the project's secrets rule keeps out of committed files.
|
||||
# CAVEAT: BACKUP_BASE is on the SAME physical disk (/mnt/5_hdd) as this source, so this protects
|
||||
# against accidental deletion, NOT against loss of sda1.
|
||||
export CLAUDE_MEMORY_DIR="/mnt/5_hdd/felhom.eu/git/.claude-memory"
|
||||
|
||||
# ============================================================================
|
||||
# EXCLUDES
|
||||
# ============================================================================
|
||||
export DATA_EXCLUDES=(
|
||||
"*.tmp"
|
||||
"*.temp"
|
||||
"*.cache"
|
||||
"**/cache/**"
|
||||
"**/Cache/**"
|
||||
"**/.cache/**"
|
||||
"**/node_modules/**"
|
||||
"**/__pycache__/**"
|
||||
"**/Thumbs.db"
|
||||
"**/.DS_Store"
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# NOTIFICATION (optional - configure as needed)
|
||||
# ============================================================================
|
||||
export NOTIFY_ON_FAILURE="true"
|
||||
# export NOTIFY_WEBHOOK_URL="https://your-webhook-url"
|
||||
|
||||
# ============================================================================
|
||||
# HELPER FUNCTIONS
|
||||
# ============================================================================
|
||||
|
||||
log() {
|
||||
local level="$1"
|
||||
shift
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" | tee -a "${BACKUP_LOGS}/backup.log"
|
||||
}
|
||||
|
||||
log_info() { log "INFO" "$@"; }
|
||||
log_warn() { log "WARN" "$@"; }
|
||||
log_error() { log "ERROR" "$@"; }
|
||||
|
||||
check_restic() {
|
||||
if ! command -v restic &> /dev/null; then
|
||||
log_error "restic is not installed. Install with: apt install restic"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_kubectl() {
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
log_error "kubectl is not installed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_dirs() {
|
||||
mkdir -p "${BACKUP_K3S}" "${BACKUP_SECRETS}" "${BACKUP_MANIFESTS}" \
|
||||
"${BACKUP_DATA}" "${BACKUP_LONGHORN}" "${BACKUP_LOGS}" "${BACKUP_POSTGRESQL}"
|
||||
}
|
||||
|
||||
init_restic_repo() {
|
||||
local repo="$1"
|
||||
if [ ! -d "${repo}" ]; then
|
||||
log_info "Initializing restic repository: ${repo}"
|
||||
restic -r "${repo}" init
|
||||
fi
|
||||
}
|
||||
|
||||
apply_retention() {
|
||||
local repo="$1"
|
||||
log_info "Applying retention policy to ${repo}"
|
||||
restic -r "${repo}" forget \
|
||||
--keep-last ${RETENTION_KEEP_LAST} \
|
||||
--keep-daily ${RETENTION_KEEP_DAILY} \
|
||||
--keep-weekly ${RETENTION_KEEP_WEEKLY} \
|
||||
--keep-monthly ${RETENTION_KEEP_MONTHLY} \
|
||||
--prune
|
||||
}
|
||||
|
||||
notify_failure() {
|
||||
local message="$1"
|
||||
if [ "${NOTIFY_ON_FAILURE}" = "true" ] && [ -n "${NOTIFY_WEBHOOK_URL}" ]; then
|
||||
curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d "{\"text\": \"🚨 Backup Failed: ${message}\"}" \
|
||||
"${NOTIFY_WEBHOOK_URL}" || true
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
notify_failure: mail accepted id=01a11a1d-d554-7f1f-acb7-ba5c268b70a6
|
||||
[2026-10-08 08:05:25] [INFO] notify_failure: failure mail sent to admin@felhom.eu
|
||||
rc=0
|
||||
[2026-10-08 03:14:36] [INFO] ========================================================
|
||||
[2026-10-08 08:05:25] [INFO] notify_failure: failure mail sent to admin@felhom.eu
|
||||
@@ -0,0 +1,69 @@
|
||||
# R-762 (wger's real web server: gunicorn with 2 workers): 2026-10-08 day, Part D
|
||||
|
||||
**Status: the bench half is done. The 9202 half is NOT done, so nothing was committed to the catalog.**
|
||||
To reach 9202, the drill catalog had to be brought up to date and wger un-hidden in it (wger is `lifecycle: hidden`,
|
||||
and the controller refuses to deploy a hidden app, `router.go` L461). The permission check refused that write
|
||||
(„Modify Shared Resources"). The brief says a refusal stops the item, so it stopped there. The drill repo, 9202's
|
||||
catalog setting and the live catalog were not changed. `app-catalog-felhom.eu` is clean at `32d1346`.
|
||||
|
||||
## The definition tested
|
||||
`definition-docker-compose.yml` is the current `templates/wger/docker-compose.yml` with two more env lines and a comment:
|
||||
`WGER_USE_GUNICORN=True` and `WEB_CONCURRENCY=2`.
|
||||
|
||||
Checked in the image `wger/server:2.7` (`sha256:1c5789b9…`, the same digest the ladder records) on bench 9401:
|
||||
- `/home/wger/entrypoint.sh` runs `gunicorn wger.wsgi:application --preload --bind 0.0.0.0:$PORT` only when
|
||||
`WGER_USE_GUNICORN == "True"`. Otherwise it runs `manage.py runserver`.
|
||||
- There is no `-w`, no `gunicorn.conf.py` in the working directory `/home/wger/src`, and no GUNICORN or WEB_ env in the
|
||||
image.
|
||||
- gunicorn 26.1.0's own `Config()` gives `workers` 1 by default and 2 with `WEB_CONCURRENCY=2`. The default timeout is
|
||||
30 s.
|
||||
|
||||
## Bench 9401 (demo-hp), two runs
|
||||
1. **`upgrade-test.py --soak 600 --move-to wger wger@gunicorn`** (harness v5): FROM the template as it stands (runserver)
|
||||
TO the gunicorn definition. Raw output: `bench/R762-gunicorn.log` and `bench/evidence/MV-wger/`.
|
||||
- The verdict is `proven`. The seed was read back before and after the switch. The app was healthy after it. The
|
||||
abort was `starts-and-serves`. Measured at 2026-10-08T06:19:56Z.
|
||||
- The memory watch ran for 606.5 s with 11,584 requests (all 302) and `load: reached`.
|
||||
- wger: anon peak 178,151,424 B = 170 MiB = **44.2 %** of 384M; 0 oom_kills; 0 restarts; the cgroup peak was
|
||||
100 % (page cache).
|
||||
- wger-files: anon peak 15.8 %; 0 kills; 0 restarts.
|
||||
- `to-full.log` has „Using gunicorn on port 8000..." and **2 × „Booting worker"** (pids 18 and 19).
|
||||
2. **`check/benchcheck.sh`**: the definition started fresh in project `r762b`, then the login page, its CSS, a photo
|
||||
and the workers were read. Results in `check/`.
|
||||
- Ready after 77 s. **Login page 200.**
|
||||
- **CSS 200** for all 3 of the page's own links, through wger-files (`text/css`; 2481 B, 277042 B and 1006 B).
|
||||
- Web login 302 with a session. `POST /api/v2/gallery/` with a 179 B PNG returned 201. **The photo read back through
|
||||
wger-files: 200, 179 B, `image/png`.**
|
||||
- Control: an unknown `/static/` file returned 404.
|
||||
- The container's env holds `WGER_USE_GUNICORN=True` and `WEB_CONCURRENCY=2`. Its log has **2 × „Booting worker"**.
|
||||
- anon peak 174,047,232 B = 166 MiB = 43.2 %. oom 0, oom_kill 0. restarts=0, oomkilled=false.
|
||||
- 20 parallel login GETs all returned 200.
|
||||
- This run is short. The 10-minute watch is run 1.
|
||||
|
||||
The night's figure (`night-burndown-2026-10-06/r762/`) was 157 MiB, 41 %, on the template without traefik's env. Today
|
||||
it is 166 to 170 MiB, 43 to 44 %, on the full catalog template.
|
||||
|
||||
## The ladder: why no step file was written
|
||||
The ladder records image moves. This change moves no image: `from` and `to` would both be
|
||||
`{wger: wger/server:2.7, wger-files: nginx:1.30.5-alpine}` (step key `10df849ded803b6e`). The writer handles
|
||||
from == to as a re-test, and `ladder.check_entry` refuses that entry:
|
||||
„a re-test (from == to) whose digest is the same as its digest_from tests nothing new — no new digest" (tool output,
|
||||
2026-10-08). `09` §5.4's render table says „deployed, pinned, catalog images equal → the catalog template — fixes flow".
|
||||
So a compose-only change reaches an installed wger at its next `up -d`, and the product's restart is `up -d`
|
||||
(`manager.go` ~L1411). It needs no ladder entry. No box runs wger (hub read, 07:58).
|
||||
|
||||
## Teardown
|
||||
- **Machine (bench 9401):** project `r762b` was taken down with `down -v`; afterwards 0 containers and 0 `r762`
|
||||
volumes. The harness ran its own `down -v`. `/root/r762b` and the helper scripts were deleted. `/opt/upg/templates/wger@gunicorn`
|
||||
was deleted. `/opt/upg`'s scripts and `templates/wger` were updated to the catalog's `32d1346` copies; they were
|
||||
older before. `/opt/upg/evidence/MV-wger` now holds today's run (the 10-06 run is kept in
|
||||
`audits/design-build-2026-10-06/F/bench/`). `/opt/upg/R762-gunicorn.log` stays. **9401 was stopped** (`pct status`:
|
||||
stopped), as it was found.
|
||||
- **Machine (9202):** only GETs and a dashboard login. 0 wger containers; `repo_url` is still the live catalog. Its
|
||||
deployed list was paperless-ngx, privatebin and recipe-importer at 08:0x CEST and paperless-ngx and privatebin at
|
||||
08:35. **This session did not touch recipe-importer**; something else removed it in that window.
|
||||
- **Host (demo-hp):** the `/tmp` copy files were deleted. Nothing else was created.
|
||||
- **Hub:** nothing.
|
||||
- No Docker command ran on DooPlex. Nothing was pruned.
|
||||
|
||||
The image's default admin password is redacted in every file here.
|
||||
@@ -0,0 +1,130 @@
|
||||
[06:05:43] scratch drive folders cleared before FROM (R-656): none existed
|
||||
[06:05:43] MV-wger: deploying wger at FROM {'wger': 'wger/server:2.7', 'wger-files': 'nginx:1.30.5-alpine'}
|
||||
[06:07:17] FROM settled=True in 93.0s :: {"wger": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "wger-files": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
|
||||
[06:07:17] fixture: the BOX walk's own (Wger), through upgrade_boxport
|
||||
[06:07:17] wger: the generated admin password does not log in (POST /en/user/login -> 200) — running the template's own after_install command (the app's CLI, as the product does after an install)
|
||||
[06:07:19] wger: after_install :: version 8.3.1, blocking by username FELHOM_AFTER_INSTALL_OK
|
||||
[06:07:21] wger: POST /api/v2/weightentry/ http=201
|
||||
[06:07:21] wger: readback of the seeded weight entry http=200 found=True
|
||||
[06:07:21] C1 (seed reads back BEFORE): True
|
||||
[06:07:21] MV-wger: swapping to TO {'wger': 'wger/server:2.7', 'wger-files': 'nginx:1.30.5-alpine'}
|
||||
[06:07:33] TO up -d rc=0
|
||||
[06:08:35] TO settled=True in 62.1s :: {"wger": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "wger-files": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
|
||||
[06:08:35] migration lines observed: 6
|
||||
[06:08:35] wger: readback of the seeded weight entry http=200 found=True
|
||||
[06:08:35] RESULT (seed reads back AFTER): True
|
||||
[06:08:36] memory watch: 600s, 4 callers on 1 path(s) at 172.18.0.2:8000
|
||||
[06:08:51] + 15s wger=288M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=292
|
||||
[06:09:06] + 30s wger=291M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=580
|
||||
[06:09:21] + 46s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=872
|
||||
[06:09:37] + 61s wger=291M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=1160
|
||||
[06:09:52] + 76s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=1452
|
||||
[06:10:07] + 91s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=1740
|
||||
[06:10:22] + 106s wger=291M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=2028
|
||||
[06:10:37] + 121s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=2320
|
||||
[06:10:52] + 136s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=2608
|
||||
[06:11:07] + 152s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=2896
|
||||
[06:11:23] + 167s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=3188
|
||||
[06:11:38] + 182s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=3476
|
||||
[06:11:53] + 197s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=3768
|
||||
[06:12:08] + 212s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=4056
|
||||
[06:12:23] + 227s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=4344
|
||||
[06:12:38] + 242s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=4636
|
||||
[06:12:54] + 258s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=4924
|
||||
[06:13:09] + 273s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=5216
|
||||
[06:13:24] + 288s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=5504
|
||||
[06:13:39] + 303s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=5792
|
||||
[06:13:54] + 318s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=6084
|
||||
[06:14:09] + 334s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=6372
|
||||
[06:14:25] + 349s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=6664
|
||||
[06:14:40] + 364s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=6952
|
||||
[06:14:55] + 379s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=7240
|
||||
[06:15:10] + 394s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=7529
|
||||
[06:15:25] + 409s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=7820
|
||||
[06:15:40] + 424s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=8108
|
||||
[06:15:55] + 440s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=8400
|
||||
[06:16:11] + 455s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=8688
|
||||
[06:16:26] + 470s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=8978
|
||||
[06:16:41] + 485s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=9268
|
||||
[06:16:56] + 500s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=9556
|
||||
[06:17:11] + 515s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=9848
|
||||
[06:17:26] + 530s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=10136
|
||||
[06:17:42] + 546s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=10428
|
||||
[06:17:57] + 561s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=10716
|
||||
[06:18:12] + 576s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=11008
|
||||
[06:18:27] + 591s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=11296
|
||||
[06:18:42] + 606s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=11584
|
||||
[06:18:42] memory watch: killed=False tight=[] requests=11584 codes={'302': 11584}
|
||||
[06:18:42] MV-wger: ABORT — putting the FROM images back
|
||||
[06:19:56] wger: readback of the seeded weight entry http=200 found=True
|
||||
[06:19:56] ABORT: app came back in 62.0s; data present=True
|
||||
{
|
||||
"harness_version": 5,
|
||||
"edge": "MV-wger",
|
||||
"app": "wger",
|
||||
"note": "definition step to wger@gunicorn",
|
||||
"from": {
|
||||
"wger": "wger/server:2.7",
|
||||
"wger-files": "nginx:1.30.5-alpine"
|
||||
},
|
||||
"to": {
|
||||
"wger": "wger/server:2.7",
|
||||
"wger-files": "nginx:1.30.5-alpine"
|
||||
},
|
||||
"verdict": "proven",
|
||||
"seed_read_before": true,
|
||||
"seed_read_after": true,
|
||||
"healthy_after": true,
|
||||
"migration_observed": "\u001b[2Kwger | Performing database migrations",
|
||||
"abort": "starts-and-serves",
|
||||
"abort_detail": null,
|
||||
"engine_state_after": null,
|
||||
"memory": {
|
||||
"soak_s": 606.5,
|
||||
"requested_s": 600,
|
||||
"requests": 11584,
|
||||
"codes": {
|
||||
"302": 11584
|
||||
},
|
||||
"first_kill": null,
|
||||
"containers": {
|
||||
"wger": {
|
||||
"limit": 402653184,
|
||||
"peak": 402653184,
|
||||
"peak_pct": 1.0,
|
||||
"anon_peak_sampled": 178151424,
|
||||
"anon_peak_pct": 0.442,
|
||||
"swap_peak": 0,
|
||||
"oom_kills": 0,
|
||||
"restarts": 0,
|
||||
"oomkilled_flag": false,
|
||||
"measured": true
|
||||
},
|
||||
"wger-files": {
|
||||
"limit": 33554432,
|
||||
"peak": 9281536,
|
||||
"peak_pct": 0.277,
|
||||
"anon_peak_sampled": 5308416,
|
||||
"anon_peak_pct": 0.158,
|
||||
"swap_peak": 0,
|
||||
"oom_kills": 0,
|
||||
"restarts": 0,
|
||||
"oomkilled_flag": false,
|
||||
"measured": true
|
||||
}
|
||||
},
|
||||
"unmeasured": [],
|
||||
"venue_swap_bytes": 0,
|
||||
"load": "reached"
|
||||
},
|
||||
"marks": [],
|
||||
"bench_overrides": null,
|
||||
"duration_s": 62.1,
|
||||
"measured_at": "2026-10-08T06:19:56Z",
|
||||
"evidence": "evidence/MV-wger",
|
||||
"scratch_cleared": [],
|
||||
"files_changed": [],
|
||||
"files_changed_detail": [],
|
||||
"files_ignored": [],
|
||||
"total_s": 853.6
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
174047232
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# R-762 bench check (2026-10-08): the gunicorn definition up on its own, then the login page, its CSS, a photo read back,
|
||||
# the workers in the container's own log, and the app's memory (anon). Project r762b only; down -v at the end.
|
||||
set -u
|
||||
W=/root/r762b; O=$W/out; rm -rf $W; mkdir -p $O; cd $W
|
||||
cp /opt/upg/templates/wger@gunicorn/docker-compose.yml docker-compose.yml
|
||||
umask 077
|
||||
printf 'DOMAIN=bench.invalid\nSUBDOMAIN=fitness\nSECRET_KEY=%s\n' "$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')" > .env
|
||||
docker compose -p r762b up -d > $O/up.txt 2>&1
|
||||
ID=$(docker inspect -f '{{.Id}}' wger); CG=$(find /sys/fs/cgroup -maxdepth 6 -type d -name "*$ID*" | head -1)
|
||||
touch $W/.sampling; ( max=0; while [ -f $W/.sampling ]; do a=$(awk '$1=="anon"{print $2}' $CG/memory.stat 2>/dev/null); [ -n "$a" ] && [ "$a" -gt "$max" ] && max=$a && echo $max > $O/anon-max; sleep 0.5; done ) &
|
||||
echo "cgroup: $CG" > $O/cg.txt
|
||||
IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' wger)
|
||||
FIP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' wger-files)
|
||||
t0=$(date +%s); for i in $(seq 1 300); do c=$(curl -s -o /dev/null -w '%{http_code}' http://$IP:8000/en/user/login); [ "$c" = 200 ] && break; sleep 1; done
|
||||
echo "ready_after_s=$(( $(date +%s)-t0 )) login=$c" > $O/checks.txt
|
||||
curl -s http://$IP:8000/en/user/login | grep -o '/static/[^"]*\.css' | head -3 | while read l; do echo "css $l via wger-files: $(curl -s -o /dev/null -w '%{http_code} %{size_download}B %{content_type}' http://$FIP$l)"; done >> $O/checks.txt
|
||||
# a photo through wger's own gallery API, signed in through its own login form as the image's seeded admin (bench-only
|
||||
# throwaway box; the password is not written anywhere). As a browser behind traefik: Host + X-Forwarded-Proto https, the
|
||||
# cookies carried by hand (Django's csrftoken is Secure; curl drops it over http).
|
||||
H=(-H "Host: fitness.bench.invalid" -H "X-Forwarded-Proto: https" -H "Origin: https://fitness.bench.invalid" -H "Referer: https://fitness.bench.invalid/en/user/login")
|
||||
curl -s -D $W/h1 -o $W/b1 "${H[@]}" http://$IP:8000/en/user/login
|
||||
CSRFC=$(grep -i '^set-cookie: csrftoken=' $W/h1 | sed 's/^[^=]*=//; s/;.*//' | tr -d '\r')
|
||||
FORM=$(grep -o 'name="csrfmiddlewaretoken" value="[^"]*"' $W/b1 | head -1 | sed 's/.*value="//; s/"$//')
|
||||
curl -s -D $W/h2 -o /dev/null "${H[@]}" -H "Cookie: csrftoken=$CSRFC" --data-urlencode "csrfmiddlewaretoken=$FORM" --data-urlencode login=admin --data-urlencode password=<image-default, redacted> http://$IP:8000/en/user/login
|
||||
SID=$(grep -i '^set-cookie: sessionid=' $W/h2 | sed 's/^[^=]*=//; s/;.*//' | tr -d '\r')
|
||||
CSRF2=$(grep -i '^set-cookie: csrftoken=' $W/h2 | sed 's/^[^=]*=//; s/;.*//' | tr -d '\r'); [ -n "$CSRF2" ] && CSRFC=$CSRF2
|
||||
echo "web login: $(head -1 $W/h2 | tr -d '\r'), session cookie: $([ -n "$SID" ] && echo yes || echo no)" >> $O/checks.txt
|
||||
rm -f $W/h1 $W/h2 $W/b1
|
||||
python3 - > $W/p.png <<'PY'
|
||||
import struct, zlib, sys, os
|
||||
n=64; rgb=os.urandom(3); raw=b"".join(b"\x00"+rgb*n for _ in range(n))
|
||||
def ch(t,d): return struct.pack(">I",len(d))+t+d+struct.pack(">I",zlib.crc32(t+d)&0xffffffff)
|
||||
sys.stdout.buffer.write(b"\x89PNG\r\n\x1a\n"+ch(b"IHDR",struct.pack(">IIBBBBB",n,n,8,2,0,0,0))+ch(b"IDAT",zlib.compress(raw))+ch(b"IEND",b""))
|
||||
PY
|
||||
SZ=$(stat -c %s $W/p.png)
|
||||
R=$(curl -s -w '\n%{http_code}' "${H[@]}" -H "Cookie: csrftoken=$CSRFC; sessionid=$SID" -H "X-CSRFToken: $CSRFC" -F "image=@$W/p.png;type=image/png" -F date=2026-10-08 -F description=r762 http://$IP:8000/api/v2/gallery/)
|
||||
echo "POST /api/v2/gallery/ (${SZ} B PNG) -> $(echo "$R" | tail -1)" >> $O/checks.txt
|
||||
P=$(echo "$R" | head -n -1 | python3 -c 'import json,sys,re; print(re.sub(r"^https?://[^/]+","",json.load(sys.stdin).get("image","")))' 2>/dev/null)
|
||||
[ -z "$P" ] && echo "photo: NO PATH returned (the upload failed)" >> $O/checks.txt
|
||||
[ -n "$P" ] && echo "photo $P via wger-files: $(curl -s -o /dev/null -w '%{http_code} %{size_download}B %{content_type}' http://$FIP$P)" >> $O/checks.txt
|
||||
echo "control: an unknown /static/ file via wger-files: $(curl -s -o /dev/null -w '%{http_code}' http://$FIP/static/r762-nonexistent.css)" >> $O/checks.txt
|
||||
# light load: 20 login GETs, 10 parallel
|
||||
seq 1 20 | xargs -P 10 -I{} curl -s -o /dev/null -w 'conc %{http_code} %{time_total}\n' http://$IP:8000/en/user/login > $O/conc.txt
|
||||
sleep 3; rm -f $W/.sampling; sleep 1
|
||||
grep -E '^(oom|oom_kill) ' $CG/memory.events > $O/events.txt; cat $CG/memory.max > $O/memory.max; cat $CG/memory.peak > $O/memory.peak 2>/dev/null
|
||||
docker inspect -f 'restarts={{.RestartCount}} oomkilled={{.State.OOMKilled}} status={{.State.Status}} health={{.State.Health.Status}}' wger > $O/inspect.txt
|
||||
docker exec wger sh -c 'env | grep -E "^(WGER_USE_GUNICORN|WEB_CONCURRENCY)="' > $O/env-in-container.txt
|
||||
docker logs wger 2>&1 | sed 's/<image-default, redacted>/<image-default, redacted>/g' > $O/wger.log
|
||||
grep -E 'Using gunicorn|Using django|Booting worker|Listening at|Starting gunicorn|Starting development server' $O/wger.log > $O/server-lines.txt
|
||||
docker compose -p r762b down -v > $O/down.txt 2>&1
|
||||
rm -f $W/p.png .env
|
||||
echo done
|
||||
@@ -0,0 +1 @@
|
||||
cgroup: /sys/fs/cgroup/system.slice/docker-1dd3339a4eeb9ed63540b35d7ca09acca030259a68038e233e7270947dadcb05.scope
|
||||
@@ -0,0 +1,8 @@
|
||||
ready_after_s=77 login=200
|
||||
css /static/css/workout-manager.7007d84ce531.css via wger-files: 200 2481B text/css
|
||||
css /static/bootstrap-compiled.80a6279921f8.css via wger-files: 200 277042B text/css
|
||||
css /static/css/bootstrap-custom.400ad578123c.css via wger-files: 200 1006B text/css
|
||||
web login: HTTP/1.1 302 Found, session cookie: yes
|
||||
POST /api/v2/gallery/ (179 B PNG) -> 201
|
||||
photo /media/gallery/1/badf61de-7554-44b9-b72f-87df80fbd01d.png via wger-files: 200 179B image/png
|
||||
control: an unknown /static/ file via wger-files: 404
|
||||
@@ -0,0 +1,20 @@
|
||||
conc 200 0.044549
|
||||
conc 200 0.080295
|
||||
conc 200 0.119458
|
||||
conc 200 0.140526
|
||||
conc 200 0.147622
|
||||
conc 200 0.169720
|
||||
conc 200 0.175291
|
||||
conc 200 0.200793
|
||||
conc 200 0.203844
|
||||
conc 200 0.221900
|
||||
conc 200 0.196650
|
||||
conc 200 0.178437
|
||||
conc 200 0.147859
|
||||
conc 200 0.152214
|
||||
conc 200 0.146952
|
||||
conc 200 0.140927
|
||||
conc 200 0.146328
|
||||
conc 200 0.139658
|
||||
conc 200 0.140157
|
||||
conc 200 0.139606
|
||||
@@ -0,0 +1,14 @@
|
||||
Container wger-files Stopping
|
||||
Container wger-files Stopped
|
||||
Container wger-files Removing
|
||||
Container wger-files Removed
|
||||
Container wger Stopping
|
||||
Container wger Stopped
|
||||
Container wger Removing
|
||||
Container wger Removed
|
||||
Volume r762b_wger_media Removing
|
||||
Volume r762b_wger_data Removing
|
||||
Volume r762b_wger_static Removing
|
||||
Volume r762b_wger_media Removed
|
||||
Volume r762b_wger_static Removed
|
||||
Volume r762b_wger_data Removed
|
||||
@@ -0,0 +1,2 @@
|
||||
WGER_USE_GUNICORN=True
|
||||
WEB_CONCURRENCY=2
|
||||
@@ -0,0 +1,2 @@
|
||||
oom 0
|
||||
oom_kill 0
|
||||
@@ -0,0 +1 @@
|
||||
restarts=0 oomkilled=false status=running health=starting
|
||||
@@ -0,0 +1 @@
|
||||
402653184
|
||||
@@ -0,0 +1 @@
|
||||
402653184
|
||||
@@ -0,0 +1,5 @@
|
||||
Using gunicorn on port 8000...
|
||||
[2026-10-08 08:32:13 +0200] [28] [INFO] Starting gunicorn 26.1.0
|
||||
[2026-10-08 08:32:13 +0200] [28] [INFO] Listening at: http://0.0.0.0:8000 (28)
|
||||
[2026-10-08 08:32:13 +0200] [29] [INFO] Booting worker with pid: 29
|
||||
[2026-10-08 08:32:13 +0200] [30] [INFO] Booting worker with pid: 30
|
||||
@@ -0,0 +1,14 @@
|
||||
Volume "r762b_wger_static" Creating
|
||||
Volume "r762b_wger_static" Created
|
||||
Volume "r762b_wger_data" Creating
|
||||
Volume "r762b_wger_data" Created
|
||||
Volume "r762b_wger_media" Creating
|
||||
Volume "r762b_wger_media" Created
|
||||
Container wger Creating
|
||||
Container wger Created
|
||||
Container wger-files Creating
|
||||
Container wger-files Created
|
||||
Container wger Starting
|
||||
Container wger Started
|
||||
Container wger-files Starting
|
||||
Container wger-files Started
|
||||
@@ -0,0 +1,295 @@
|
||||
*** Using settings from env: settings.main
|
||||
level=INFO ts=2026-10-08 08:31:01,000 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
*** Database is empty or incomplete, setting it up now
|
||||
*** Using settings from env: settings.main
|
||||
Operations to perform:
|
||||
Apply all migrations: account, actstream, allauth_idp_oidc, auth, authtoken, axes, config, contenttypes, core, easy_thumbnails, exercises, gallery, gym, mailer, manager, measurements, mfa, nutrition, sessions, sites, socialaccount, token_blacklist, trophies, weight
|
||||
Running migrations:
|
||||
Applying contenttypes.0001_initial... OK
|
||||
Applying auth.0001_initial... OK
|
||||
Applying account.0001_initial... OK
|
||||
Applying account.0002_email_max_length... OK
|
||||
Applying account.0003_alter_emailaddress_create_unique_verified_email... OK
|
||||
Applying account.0004_alter_emailaddress_drop_unique_email... OK
|
||||
Applying account.0005_emailaddress_idx_upper_email... OK
|
||||
Applying account.0006_emailaddress_lower... OK
|
||||
Applying account.0007_emailaddress_idx_email... OK
|
||||
Applying account.0008_emailaddress_unique_primary_email_fixup... OK
|
||||
Applying account.0009_emailaddress_unique_primary_email... OK
|
||||
Applying actstream.0001_initial... OK
|
||||
Applying actstream.0002_remove_action_data... OK
|
||||
Applying actstream.0003_add_follow_flag... OK
|
||||
Applying allauth_idp_oidc.0001_initial... OK
|
||||
Applying allauth_idp_oidc.0002_client_default_scopes... OK
|
||||
Applying allauth_idp_oidc.0003_client_allow_uri_wildcards... OK
|
||||
Applying contenttypes.0002_remove_content_type_name... OK
|
||||
Applying auth.0002_alter_permission_name_max_length... OK
|
||||
Applying auth.0003_alter_user_email_max_length... OK
|
||||
Applying auth.0004_alter_user_username_opts... OK
|
||||
Applying auth.0005_alter_user_last_login_null... OK
|
||||
Applying auth.0006_require_contenttypes_0002... OK
|
||||
Applying auth.0007_alter_validators_add_error_messages... OK
|
||||
Applying auth.0008_alter_user_username_max_length... OK
|
||||
Applying auth.0009_alter_user_last_name_max_length... OK
|
||||
Applying auth.0010_alter_group_name_max_length... OK
|
||||
Applying auth.0011_update_proxy_permissions... OK
|
||||
Applying auth.0012_alter_user_first_name_max_length... OK
|
||||
Applying authtoken.0001_initial... OK
|
||||
Applying authtoken.0002_auto_20160226_1747... OK
|
||||
Applying authtoken.0003_tokenproxy... OK
|
||||
Applying authtoken.0004_alter_tokenproxy_options... OK
|
||||
Applying axes.0001_initial... OK
|
||||
Applying axes.0002_auto_20151217_2044... OK
|
||||
Applying axes.0003_auto_20160322_0929... OK
|
||||
Applying axes.0004_auto_20181024_1538... OK
|
||||
Applying axes.0005_remove_accessattempt_trusted... OK
|
||||
Applying axes.0006_remove_accesslog_trusted... OK
|
||||
Applying axes.0007_alter_accessattempt_unique_together... OK
|
||||
Applying axes.0008_accessfailurelog... OK
|
||||
Applying axes.0009_add_session_hash... OK
|
||||
Applying axes.0010_accessattemptexpiration... OK
|
||||
Applying gym.0001_initial... OK
|
||||
Applying core.0001_initial... OK
|
||||
Applying config.0001_initial... OK
|
||||
Applying config.0002_auto_20190618_1617... OK
|
||||
Applying config.0003_delete_languageconfig... OK
|
||||
Applying sessions.0001_initial... OK
|
||||
Applying weight.0001_initial... OK
|
||||
Applying weight.0002_auto_20150604_2139... OK
|
||||
Applying weight.0003_auto_20160416_1030... OK
|
||||
Applying weight.0004_multiple_weight_entries_per_day... OK
|
||||
Applying weight.0005_add_uuid... OK
|
||||
Applying nutrition.0001_initial... OK
|
||||
Applying nutrition.0002_auto_20170101_1538... OK
|
||||
Applying nutrition.0003_auto_20170118_2308... OK
|
||||
Applying nutrition.0004_auto_20200819_2310... OK
|
||||
Applying nutrition.0005_logitem... OK
|
||||
Applying nutrition.0006_auto_20201201_0653... OK
|
||||
Applying nutrition.0007_auto_20201214_0013... OK
|
||||
Applying nutrition.0008_auto_20210102_1446... OK
|
||||
Applying nutrition.0009_meal_name... OK
|
||||
Applying nutrition.0010_logitem_meal... OK
|
||||
Applying nutrition.0011_alter_logitem_datetime... OK
|
||||
Applying nutrition.0012_alter_ingredient_license_author... OK
|
||||
Applying gym.0002_auto_20151003_1944... OK
|
||||
Applying gym.0003_auto_20151003_2008... OK
|
||||
Applying gym.0004_auto_20151003_2357... OK
|
||||
Applying gym.0005_auto_20151023_1522... OK
|
||||
Applying gym.0006_auto_20160214_1013... OK
|
||||
Applying gym.0007_auto_20170123_0920... OK
|
||||
Applying gym.0008_auto_20190618_1617... OK
|
||||
Applying exercises.0001_initial... OK
|
||||
Applying manager.0001_initial... OK
|
||||
Applying manager.0002_auto_20150202_2040... OK
|
||||
Applying manager.0004_auto_20150609_1603... OK
|
||||
Applying core.0002_auto_20141225_1512... OK
|
||||
Applying core.0003_auto_20150217_1554... OK
|
||||
Applying core.0004_auto_20150217_1914... OK
|
||||
Applying core.0005_auto_20151025_2236... OK
|
||||
Applying core.0006_auto_20151025_2237... OK
|
||||
Applying core.0007_repetitionunit... OK
|
||||
Applying core.0008_weightunit... OK
|
||||
Applying core.0009_auto_20160303_2340... OK
|
||||
Applying core.0010_auto_20170403_0144... OK
|
||||
Applying core.0011_auto_20201201_0653... OK
|
||||
Applying core.0012_auto_20210210_1228... OK
|
||||
Applying core.0013_auto_20210726_1729... OK
|
||||
Applying nutrition.0013_ingredient_image... OK
|
||||
Applying nutrition.0014_license_information... OK
|
||||
Applying nutrition.0015_alter_ingredient_creation_date_and_more... OK
|
||||
Applying nutrition.0016_alter_logitem_options_and_more... OK
|
||||
Applying nutrition.0017_remove_nutritionplan_language_alter_logitem_meal... OK
|
||||
Applying nutrition.0018_nutritionplan_goal_carbs_nutritionplan_goal_energy_and_more... OK
|
||||
Applying nutrition.0019_alter_image_license_author_and_more... OK
|
||||
Applying nutrition.0020_full_text_search... OK
|
||||
Applying nutrition.0021_add_fibers_field... OK
|
||||
Applying nutrition.0022_add_remote_id_increase_author_field_length... OK
|
||||
Applying nutrition.0023_fiber_spelling... OK
|
||||
Applying nutrition.0024_remove_ingredient_status... OK
|
||||
Applying nutrition.0025_add_last_image_check... OK
|
||||
Applying nutrition.0026_add_start_and_end_fields... OK
|
||||
Applying nutrition.0027_prefill_end_date... OK
|
||||
Applying nutrition.0028_ingredient_dietary_properties... OK
|
||||
Applying nutrition.0029_ingredient_nutriscore... OK
|
||||
Applying manager.0005_auto_20160303_2008... OK
|
||||
Applying manager.0006_auto_20160303_2138... OK
|
||||
Applying manager.0007_auto_20160311_2258... OK
|
||||
Applying manager.0008_auto_20190618_1617... OK
|
||||
Applying manager.0009_auto_20201202_1559... OK
|
||||
Applying manager.0010_auto_20210102_1446... OK
|
||||
Applying manager.0011_remove_set_exercises... OK
|
||||
Applying manager.0012_auto_20210430_1449... OK
|
||||
Applying manager.0013_set_comment... OK
|
||||
Applying manager.0014_auto_20210717_1858... OK
|
||||
Applying manager.0015_auto_20211028_1113... OK
|
||||
Applying exercises.0002_auto_20150307_1841... OK
|
||||
Applying exercises.0003_auto_20160921_2000... OK
|
||||
Applying exercises.0004_auto_20170404_0114... OK
|
||||
Applying exercises.0005_auto_20190618_1617... OK
|
||||
Applying exercises.0006_auto_20201203_0203... OK
|
||||
Applying exercises.0007_auto_20201203_1042... OK
|
||||
Applying exercises.0008_exercisebase... OK
|
||||
Applying exercises.0009_auto_20201211_0139... OK
|
||||
Applying exercises.0010_auto_20201211_0205... OK
|
||||
Applying exercises.0011_auto_20201214_0033... OK
|
||||
Applying exercises.0012_auto_20210327_1219... OK
|
||||
Applying exercises.0013_auto_20210503_1232... OK
|
||||
Applying exercises.0014_exerciseimage_style... OK
|
||||
Applying exercises.0015_exercise_videos... OK
|
||||
Applying exercises.0016_exercisealias... OK
|
||||
Applying exercises.0017_muscle_name_en... OK
|
||||
Applying core.0013_userprofile_email_verified... OK
|
||||
Applying core.0014_merge_20210818_1735... OK
|
||||
Applying exercises.0018_delete_pending_exercises... OK
|
||||
Applying exercises.0019_exercise_crowdsourcing_changes... OK
|
||||
Applying manager.0016_move_to_exercise_base... OK
|
||||
Applying manager.0017_alter_workoutlog_exercise_base... OK
|
||||
Applying exercises.0020_historicalexerciseimage_historicalexercisevideo... OK
|
||||
Applying exercises.0021_deletionlog... OK
|
||||
Applying exercises.0022_alter_exercise_license_author_and_more... OK
|
||||
Applying exercises.0023_make_uuid_unique... OK
|
||||
Applying exercises.0024_license_information... OK
|
||||
Applying exercises.0025_rename_update_date_exercise_last_update_and_more... OK
|
||||
Applying exercises.0026_deletionlog_replaced_by... OK
|
||||
Applying exercises.0027_alter_deletionlog_replaced_by_and_more... OK
|
||||
Applying exercises.0028_add_uuid_alias_and_comments... OK
|
||||
Applying exercises.0029_full_text_search... OK
|
||||
Applying exercises.0030_increase_author_field_length... OK
|
||||
Applying exercises.0032_rename_exercise... OK
|
||||
Applying core.0015_alter_language_short_name... OK
|
||||
Applying core.0016_alter_language_short_name... OK
|
||||
Applying manager.0018_flexible_routines... OK
|
||||
Applying manager.0019_flexible_routines_migration... OK
|
||||
Applying manager.0021_flexible_routines_cleanup... OK
|
||||
Applying core.0017_language_full_name_en... OK
|
||||
Applying core.0018_rounding... OK
|
||||
Applying core.0019_delete_daysofweek... OK
|
||||
Applying core.0020_add_trophies_enabled_to_userprofile... OK
|
||||
Applying core.0021_add_unit_type_to_repetitionunit... OK
|
||||
Applying nutrition.0030_add_indices... OK
|
||||
Applying nutrition.0031_start_weight_unit_merge... OK
|
||||
Applying nutrition.0032_continue_weight_unit_merge... OK
|
||||
Applying nutrition.0033_finalize_weight_unit_merge... OK
|
||||
Applying nutrition.0034_ingredient_trigram_gin_index... OK
|
||||
Applying nutrition.0035_add_uuids... OK
|
||||
Applying nutrition.0036_alter_image_license_author_and_more... OK
|
||||
Applying nutrition.0037_powersync_synced_ingredient_tables... OK
|
||||
Applying measurements.0001_initial... OK
|
||||
Applying measurements.0002_auto_20210722_1042... OK
|
||||
Applying measurements.0003_alter_measurement_unique_together_and_more... OK
|
||||
Applying measurements.0004_add_uuids... OK
|
||||
Applying measurements.0005_alter_measurement_date... OK
|
||||
Applying trophies.0001_initial... OK
|
||||
Applying trophies.0002_load_initial_trophies... OK
|
||||
Applying manager.0022_alter_rir_type... OK
|
||||
Applying manager.0023_change_validators... OK
|
||||
Applying manager.0024_log_and_session_uuid... OK
|
||||
Applying manager.0025_change_pk_to_uuid... OK
|
||||
Applying trophies.0003_migrate_context_data_uuids... OK
|
||||
Applying manager.0026_change_pk_to_uuid_swap... OK
|
||||
Applying core.0022_move_email_verified_to_emailaddress... OK
|
||||
Applying core.0023_create_publication... OK
|
||||
Applying manager.0027_cleanup_fields... OK
|
||||
Applying manager.0028_backfill_session_day... OK
|
||||
Applying gallery.0001_initial... OK
|
||||
Applying exercises.0033_uniqueness_constraint_translations... OK
|
||||
Applying exercises.0034_add_exercise_image_dimensions... OK
|
||||
Applying exercises.0035_add_is_ai_generated... OK
|
||||
Applying exercises.0036_add_markdown_description_field... OK
|
||||
Applying exercises.0037_replace_variation_with_uuid_field... OK
|
||||
Applying exercises.0038_sync_model_changes... OK
|
||||
Applying exercises.0039_translation_alias_trigram_gin_index... OK
|
||||
Applying exercises.0040_alter_exercise_license_author_and_more... OK
|
||||
Applying core.0024_backfill_emailaddress... OK
|
||||
Applying core.0025_remove_unused_fields_in_userprofile... OK
|
||||
Applying core.0026_alter_userprofile_birthdate_alter_userprofile_height... OK
|
||||
Applying core.0027_powersync_publication... OK
|
||||
Applying core.0028_longlivedsession... OK
|
||||
Applying core.0029_userprofile_timezone... OK
|
||||
Applying easy_thumbnails.0001_initial... OK
|
||||
Applying easy_thumbnails.0002_thumbnaildimensions... OK
|
||||
Applying mailer.0001_initial... OK
|
||||
Applying mailer.0002_auto_20190618_1617... OK
|
||||
Applying mailer.0003_auto_20201201_0653... OK
|
||||
Applying manager.0029_alter_workoutsession_options_and_more... OK
|
||||
Applying measurements.0006_health_sync... OK
|
||||
Applying measurements.0007_migrate_weight... OK
|
||||
Applying measurements.0008_dynamic_type... OK
|
||||
Applying mfa.0001_initial... OK
|
||||
Applying mfa.0002_authenticator_timestamps... OK
|
||||
Applying mfa.0003_authenticator_type_uniq... OK
|
||||
Applying sites.0001_initial... OK
|
||||
Applying sites.0002_alter_domain_unique... OK
|
||||
Applying socialaccount.0001_initial... OK
|
||||
Applying socialaccount.0002_token_max_lengths... OK
|
||||
Applying socialaccount.0003_extra_data_default_dict... OK
|
||||
Applying socialaccount.0004_app_provider_id_settings... OK
|
||||
Applying socialaccount.0005_socialtoken_nullable_app... OK
|
||||
Applying socialaccount.0006_alter_socialaccount_extra_data... OK
|
||||
Applying token_blacklist.0001_initial... OK
|
||||
Applying token_blacklist.0002_outstandingtoken_jti_hex... OK
|
||||
Applying token_blacklist.0003_auto_20171017_2007... OK
|
||||
Applying token_blacklist.0004_auto_20171017_2013... OK
|
||||
Applying token_blacklist.0005_remove_outstandingtoken_jti... OK
|
||||
Applying token_blacklist.0006_auto_20171017_2113... OK
|
||||
Applying token_blacklist.0007_auto_20171017_2214... OK
|
||||
Applying token_blacklist.0008_migrate_to_bigautofield... OK
|
||||
Applying token_blacklist.0010_fix_migrate_to_bigautofield... OK
|
||||
Applying token_blacklist.0011_linearizes_history... OK
|
||||
Applying token_blacklist.0012_alter_outstandingtoken_user... OK
|
||||
Applying token_blacklist.0013_alter_blacklistedtoken_options_and_more... OK
|
||||
Applying weight.0006_delete_weightentry... OK
|
||||
*** Using settings from env: settings.main
|
||||
Installed 1 object(s) from 1 fixture(s)
|
||||
Installed 33 object(s) from 1 fixture(s)
|
||||
Installed 7 object(s) from 1 fixture(s)
|
||||
Installed 3 object(s) from 1 fixture(s)
|
||||
Installed 5 object(s) from 1 fixture(s)
|
||||
Installed 8 object(s) from 1 fixture(s)
|
||||
Installed 6 object(s) from 1 fixture(s)
|
||||
Installed 1 object(s) from 1 fixture(s)
|
||||
Installed 12 object(s) from 1 fixture(s)
|
||||
Installed 16 object(s) from 1 fixture(s)
|
||||
Installed 8 object(s) from 1 fixture(s)
|
||||
Installed 872 object(s) from 1 fixture(s)
|
||||
Installed 2429 object(s) from 1 fixture(s)
|
||||
Installed 1 object(s) from 1 fixture(s)
|
||||
Installed 1 object(s) from 1 fixture(s)
|
||||
Installed 1 object(s) from 1 fixture(s)
|
||||
*** Using settings from env: settings.main
|
||||
*** Password for user admin was reset to '<image-default, redacted>'
|
||||
Installed 3 object(s) from 1 fixture(s)
|
||||
Running in production mode, running collectstatic now
|
||||
level=INFO ts=2026-10-08 08:31:51,700 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
|
||||
11362 static files copied to '/home/wger/static', 11362 post-processed.
|
||||
Performing database migrations
|
||||
level=INFO ts=2026-10-08 08:32:06,548 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
System check identified some issues:
|
||||
|
||||
WARNINGS:
|
||||
?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
|
||||
HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
|
||||
Operations to perform:
|
||||
Apply all migrations: account, actstream, allauth_idp_oidc, auth, authtoken, axes, config, contenttypes, core, easy_thumbnails, exercises, gallery, gym, mailer, manager, measurements, mfa, nutrition, sessions, sites, socialaccount, token_blacklist, trophies, weight
|
||||
Running migrations:
|
||||
No migrations to apply.
|
||||
Your models in app(s): 'exercises', 'gallery' have changes that are not yet reflected in a migration, and so won't be applied.
|
||||
Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
|
||||
level=INFO ts=2026-10-08 08:32:09,710 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
System check identified some issues:
|
||||
|
||||
WARNINGS:
|
||||
?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
|
||||
HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
|
||||
Set site URL to fitness.bench.invalid
|
||||
Using gunicorn on port 8000...
|
||||
level=INFO ts=2026-10-08 08:32:12,670 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2026-10-08 08:32:13 +0200] [28] [INFO] Starting gunicorn 26.1.0
|
||||
[2026-10-08 08:32:13 +0200] [28] [INFO] Listening at: http://0.0.0.0:8000 (28)
|
||||
[2026-10-08 08:32:13 +0200] [28] [INFO] Using worker: sync
|
||||
[2026-10-08 08:32:13 +0200] [29] [INFO] Booting worker with pid: 29
|
||||
[2026-10-08 08:32:13 +0200] [30] [INFO] Booting worker with pid: 30
|
||||
[2026-10-08 08:32:13 +0200] [28] [INFO] Control socket listening at /home/wger/.gunicorn/gunicorn.ctl
|
||||
level=INFO ts=2026-10-08 08:32:14,549 module=database path=/home/wger/.local/lib/python3.12/site-packages/axes/handlers/database.py line=295 message=AXES: Successful login by {username: "********************", ip_address: "********************", user_agent: "curl/8.14.1", path_info: "/en/user/login"}.
|
||||
level=INFO ts=2026-10-08 08:32:14,551 module=database path=/home/wger/.local/lib/python3.12/site-packages/axes/handlers/database.py line=425 message=AXES: Cleaned up 0 expired access attempts from database that were older than 2026-10-08 06:27:14.291696+00:00
|
||||
@@ -0,0 +1,159 @@
|
||||
# wger - Edzésnapló és fitnesz tervező
|
||||
# Domain: ${SUBDOMAIN}.${DOMAIN}
|
||||
# Database: None (file-based)
|
||||
# RAM: ~100M (mem_limit: 384M) | Pi-compatible: Yes
|
||||
#
|
||||
# Environment variables:
|
||||
# DOMAIN - Your domain (e.g., demo-felhom.eu)
|
||||
# SECRET_KEY - Titkosítási kulcs (auto-generated)
|
||||
|
||||
services:
|
||||
wger:
|
||||
image: wger/server:2.7
|
||||
container_name: wger
|
||||
# R-737 (2026-09-30): wger's app login API (the mobile app's) signs JWTs with JWT_PRIVATE_KEY / JWT_PUBLIC_KEY — an
|
||||
# RSA pair the deploy's generators cannot make, and without it a CORRECT password answered 500. So the pair is made
|
||||
# ONCE by wger's own `manage.py generate-jwt-keys`, kept 0600 on wger's own data volume (a restore brings the same
|
||||
# key back), and loaded before the image's own entrypoint. Never printed.
|
||||
entrypoint:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
K=/home/wger/db/.felhom-jwt.env
|
||||
if [ ! -s "$$K" ]; then
|
||||
(cd /home/wger/src && python3 manage.py generate-jwt-keys 2>/dev/null) | grep -E '^JWT_(PRIVATE|PUBLIC)_KEY=' > "$$K.tmp"
|
||||
if [ "$$(grep -c . "$$K.tmp")" = 2 ]; then mv "$$K.tmp" "$$K" && chmod 600 "$$K"; else rm -f "$$K.tmp"; echo "felhom: JWT keys could not be made" >&2; fi
|
||||
fi
|
||||
if [ -s "$$K" ]; then set -a; . "$$K"; set +a; fi
|
||||
exec /home/wger/entrypoint.sh
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Budapest
|
||||
- SECRET_KEY=${SECRET_KEY}
|
||||
# A wger 2.4+ a TELJES DJANGO_DB_* halmazt beolvassa, akkor is, ha az
|
||||
# engine sqlite -- enélkül indulás nélkül kilép ("Set the DJANGO_DB_USER
|
||||
# environment variable"). Az USER/PASSWORD/HOST/PORT értékeket az sqlite
|
||||
# backend figyelmen kívül hagyja, de jelen kell lenniük.
|
||||
# A DATABASE a wger_data kötetre mutat (/home/wger/db), oda, ahol a wger
|
||||
# saját alapértelmezett sqlite fájlja is volt -- így meglévő telepítés
|
||||
# adatai nem "tűnnek el" egy másik útvonalra.
|
||||
# R-712 (measured 2026-09-29 on 9202): behind traefik wger saw the request as http and refused a browser's
|
||||
# https Origin with "CSRF verification failed" — nobody could sign in from a browser.
|
||||
- CSRF_TRUSTED_ORIGINS=https://${SUBDOMAIN}.${DOMAIN}
|
||||
- X_FORWARDED_PROTO_HEADER_SET=True
|
||||
# R-738: the image runs `manage.py migrate` at start ONLY with this switch (entrypoint.sh). Without it an update
|
||||
# that brings migrations leaves wger serving its front page over an unmigrated database (login 500).
|
||||
- DJANGO_PERFORM_MIGRATIONS=True
|
||||
# R-752 (decided by CC unattended 2026-10-01, `09` §3 decision 58 — operator may reverse): django-axes locked by
|
||||
# ip_address, and behind the tunnel every visitor has the tunnel's address (R-753) — a stranger's 10 wrong tries
|
||||
# locked out EVERY household member for 30 min. Now only the targeted name, for 5 min (each try during a lock
|
||||
# restarts it — wger 2.7 hard-codes that — so short is kinder), counted in the database (the default cache
|
||||
# handler warns axes.W001). Measured on 9202: the other member unaffected; the targeted one in again at 7.5 min.
|
||||
- AXES_LOCKOUT_PARAMETERS=username
|
||||
- AXES_COOLOFF_TIME=5
|
||||
- AXES_HANDLER=axes.handlers.database.AxesDatabaseHandler
|
||||
# R-763 (2026-10-05): the image defaults both to True. After the install the admin exists (after_install sets its
|
||||
# password), so nobody needs wger's own sign-up: a stranger could make an account through the front door, and every
|
||||
# anonymous visit to the dashboard made a guest user row (wger's middleware create_temporary_user). Both read by
|
||||
# settings/main.py as env.bool (wger 2.7 L179-180); the sign-up view then redirects to the features page.
|
||||
- ALLOW_REGISTRATION=False
|
||||
- ALLOW_GUEST_USERS=False
|
||||
# R-764 (2026-10-05): mail through the box's relay (smtp_mapping in .felhom.yml, tls_mode plaintext -> :2526).
|
||||
# wger 2.7 settings/main.py:162 reads the EMAIL_* group ONLY when ENABLE_EMAIL is true, and then env.str() with
|
||||
# no default on EMAIL_HOST_USER / EMAIL_HOST_PASSWORD — so both stay defined-EMPTY here (the relay takes no
|
||||
# login; an absent one would stop wger at start). ENABLE_EMAIL is the gate: False unless the mail toggle injects
|
||||
# "True". EMAIL_USE_TLS False: Django's STARTTLS verifies the certificate and the relay's is self-signed.
|
||||
- ENABLE_EMAIL=${ENABLE_EMAIL:-False}
|
||||
- EMAIL_HOST=${EMAIL_HOST:-}
|
||||
- EMAIL_PORT=${EMAIL_PORT:-2526}
|
||||
- EMAIL_HOST_USER=
|
||||
- EMAIL_HOST_PASSWORD=
|
||||
- EMAIL_USE_TLS=False
|
||||
- EMAIL_USE_SSL=False
|
||||
- FROM_EMAIL=${FROM_EMAIL:-wger Workout Manager <wger@example.com>}
|
||||
- DJANGO_DB_ENGINE=django.db.backends.sqlite3
|
||||
- DJANGO_DB_DATABASE=/home/wger/db/database.sqlite
|
||||
- DJANGO_DB_USER=wger
|
||||
- DJANGO_DB_PASSWORD=wger
|
||||
- DJANGO_DB_HOST=localhost
|
||||
- DJANGO_DB_PORT=5432
|
||||
- SITE_URL=https://${SUBDOMAIN}.${DOMAIN}
|
||||
# R-762 (2026-10-06): production mode, as upstream's own prod.env (wger-project/docker config/prod.env). With
|
||||
# DJANGO_DEBUG=False the image's entrypoint runs `collectstatic` at every start (entrypoint.sh:27) into
|
||||
# /home/wger/static, and Django stops serving /static and /media itself (it never did in production — upstream
|
||||
# puts nginx in front). wger-files below serves both from the shared volumes.
|
||||
- DJANGO_DEBUG=False
|
||||
# R-762 (2026-10-08): the real web server. The image's entrypoint.sh runs `gunicorn wger.wsgi:application --preload
|
||||
# --bind 0.0.0.0:$PORT` when WGER_USE_GUNICORN is "True" (else Django's development server, `manage.py runserver`).
|
||||
# It passes no -w and the image has no gunicorn.conf.py, so the worker count is gunicorn's own WEB_CONCURRENCY
|
||||
# (unset = 1). Two workers: measured on the bench and on 9202 (2 x "Booting worker" in the log), anon peak well
|
||||
# under the 384M limit — with --preload the workers share the app's pages with the master.
|
||||
- WGER_USE_GUNICORN=True
|
||||
- WEB_CONCURRENCY=2
|
||||
volumes:
|
||||
- wger_data:/home/wger/db
|
||||
- wger_media:/home/wger/media
|
||||
- wger_static:/home/wger/static
|
||||
networks:
|
||||
- traefik-public
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 384M
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:8000"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.wger.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)"
|
||||
- "traefik.http.routers.wger.entrypoints=websecure"
|
||||
- "traefik.http.routers.wger.tls=true"
|
||||
- "traefik.http.routers.wger.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.wger.loadbalancer.server.port=8000"
|
||||
|
||||
# R-762 (2026-10-06): the file server upstream's production compose puts in front of wger (its `nginx` service and
|
||||
# config/nginx.conf: `location /static/ { alias /wger/static/; }`, `location /media/ { alias /wger/media/; }`).
|
||||
# Here traefik is already the front door, so traefik sends ONLY /static/ and /media/ to this nginx and everything
|
||||
# else to wger as before — no config file is needed: nginx's stock config serves /usr/share/nginx/html, and the two
|
||||
# volumes are mounted there read-only. Every gate the box puts in front of the app wraps EVERY router of the stack
|
||||
# (stacks/setup_gate.go, family_gate.go), so this router is gated exactly like wger's own.
|
||||
wger-files:
|
||||
image: nginx:1.30.5-alpine
|
||||
container_name: wger-files
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- wger
|
||||
volumes:
|
||||
- wger_static:/usr/share/nginx/html/static:ro
|
||||
- wger_media:/usr/share/nginx/html/media:ro
|
||||
networks:
|
||||
- traefik-public
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 32M
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1/"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.wger-files.rule=Host(`${SUBDOMAIN}.${DOMAIN}`) && (PathPrefix(`/static/`) || PathPrefix(`/media/`))"
|
||||
- "traefik.http.routers.wger-files.entrypoints=websecure"
|
||||
- "traefik.http.routers.wger-files.tls=true"
|
||||
- "traefik.http.routers.wger-files.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.services.wger-files.loadbalancer.server.port=80"
|
||||
|
||||
volumes:
|
||||
wger_data:
|
||||
wger_media:
|
||||
wger_static:
|
||||
|
||||
networks:
|
||||
traefik-public:
|
||||
external: true
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"wger": {
|
||||
"status": "running",
|
||||
"health": "healthy",
|
||||
"restarts": 0,
|
||||
"exit": 0
|
||||
},
|
||||
"wger-files": {
|
||||
"status": "running",
|
||||
"health": "healthy",
|
||||
"restarts": 0,
|
||||
"exit": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
[2Kwger-files | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
|
||||
[2Kwger-files | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
|
||||
[2Kwger-files | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
|
||||
[2Kwger-files | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
|
||||
[2Kwger-files | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
|
||||
[2Kwger-files | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
|
||||
[2Kwger-files | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
|
||||
[2Kwger-files | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
|
||||
[2Kwger-files | /docker-entrypoint.sh: Configuration complete; ready for start up
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: using the "epoll" event method
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: nginx/1.30.5
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: built by gcc 15.2.0 (Alpine 15.2.0)
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: OS: Linux 7.0.14-20-pve
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 524288:524288
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: start worker processes
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: start worker process 30
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: start worker process 31
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: start worker process 32
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: start worker process 33
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: start worker process 34
|
||||
[2Kwger-files | 2026/10/08 06:18:54 [notice] 1#1: start worker process 35
|
||||
[2Kwger-files | 127.0.0.1 - - [08/Oct/2026:06:19:24 +0000] "GET / HTTP/1.1" 200 896 "-" "Wget" "-"
|
||||
[2K[2Kwger-files | 127.0.0.1 - - [08/Oct/2026:06:19:54 +0000] "GET / HTTP/1.1" 200 896 "-" "Wget" "-"
|
||||
wger | *** Using settings from env: settings.main
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:18:55,562 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger | Running in production mode, running collectstatic now
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:18:57,102 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger |
|
||||
[2Kwger | 22725 static files deleted, 11362 static files copied to '/home/wger/static', 11362 post-processed.
|
||||
[2Kwger | Performing database migrations
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:19:23,909 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger | System check identified some issues:
|
||||
[2Kwger |
|
||||
[2Kwger | WARNINGS:
|
||||
[2Kwger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
|
||||
[2Kwger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
|
||||
[2Kwger | Operations to perform:
|
||||
[2Kwger | Apply all migrations: account, actstream, allauth_idp_oidc, auth, authtoken, axes, config, contenttypes, core, easy_thumbnails, exercises, gallery, gym, mailer, manager, measurements, mfa, nutrition, sessions, sites, socialaccount, token_blacklist, trophies, weight
|
||||
[2Kwger | Running migrations:
|
||||
[2Kwger | No migrations to apply.
|
||||
[2Kwger | Your models in app(s): 'exercises', 'gallery' have changes that are not yet reflected in a migration, and so won't be applied.
|
||||
[2Kwger | Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:19:27,332 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger | System check identified some issues:
|
||||
[2Kwger |
|
||||
[2Kwger | WARNINGS:
|
||||
[2Kwger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
|
||||
[2Kwger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
|
||||
[2Kwger | Set site URL to fitness.gate.invalid
|
||||
[2Kwger | Using django's development server on port 8000...
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:19:29,776 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:19:31,051 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:19:31,063 module=autoreload path=/home/wger/.local/lib/python3.12/site-packages/django/utils/autoreload.py line=681 message=Watching for file changes with StatReloader
|
||||
[2Kwger | Performing system checks...
|
||||
[2Kwger |
|
||||
[2Kwger | System check identified some issues:
|
||||
[2Kwger |
|
||||
[2Kwger | WARNINGS:
|
||||
[2Kwger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
|
||||
[2Kwger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
|
||||
[2Kwger |
|
||||
[2Kwger | System check identified 1 issue (0 silenced).
|
||||
[2Kwger | October 08, 2026 - 08:19:32
|
||||
[2Kwger | Django version 6.0.8, using settings 'settings.main'
|
||||
[2Kwger | Starting development server at http://0.0.0.0:8000/
|
||||
[2Kwger | Quit the server with CONTROL-C.
|
||||
[2Kwger |
|
||||
[2Kwger | WARNING: This is a development server. Do not use it in a production setting. Use a production WSGI or ASGI server instead.
|
||||
[2Kwger | For more information on production servers see: https://docs.djangoproject.com/en/6.0/howto/deployment/
|
||||
[2Kwger | [08/Oct/2026 08:19:54] "HEAD / HTTP/1.1" 302 0
|
||||
[2Kwger | [08/Oct/2026 08:19:54] "HEAD /en/ HTTP/1.1" 302 0
|
||||
[2Kwger | [08/Oct/2026 08:19:54] "HEAD /en/software/features HTTP/1.1" 200 0
|
||||
[2Kwger | [08/Oct/2026 08:19:56] "GET /en/user/login HTTP/1.1" 200 43827
|
||||
[2Kwger | level=WARNING ts=2026-10-08 08:19:56,238 module=log path=/home/wger/.local/lib/python3.12/site-packages/django/utils/log.py line=249 message=Forbidden: /api/v2/weightentry/
|
||||
[2Kwger | [08/Oct/2026 08:19:56] "GET /api/v2/weightentry/?weight=105.51 HTTP/1.1" 403 58
|
||||
[2Kwger | [08/Oct/2026 08:19:56] "GET /en/user/login HTTP/1.1" 200 43827
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:19:56,495 module=database path=/home/wger/.local/lib/python3.12/site-packages/axes/handlers/database.py line=295 message=AXES: Successful login by {username: "********************", ip_address: "********************", user_agent: "curl/8.14.1", path_info: "/en/user/login"}.
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:19:56,504 module=database path=/home/wger/.local/lib/python3.12/site-packages/axes/handlers/database.py line=425 message=AXES: Cleaned up 1 expired access attempts from database that were older than 2026-10-08 06:14:56.348758+00:00
|
||||
[2Kwger | [08/Oct/2026 08:19:56] "POST /en/user/login HTTP/1.1" 302 0
|
||||
[2Kwger | [08/Oct/2026 08:19:56] "GET /api/v2/weightentry/?weight=199.99 HTTP/1.1" 200 52
|
||||
[2Kwger | [08/Oct/2026 08:19:56] "GET /api/v2/weightentry/?weight=105.51 HTTP/1.1" 200 159
|
||||
@@ -0,0 +1 @@
|
||||
null
|
||||
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
[2Kwger | Performing database migrations
|
||||
[2Kwger | Apply all migrations: account, actstream, allauth_idp_oidc, auth, authtoken, axes, config, contenttypes, core, easy_thumbnails, exercises, gallery, gym, mailer, manager, measurements, mfa, nutrition, sessions, sites, socialaccount, token_blacklist, trophies, weight
|
||||
[2Kwger | Running migrations:
|
||||
[2Kwger | No migrations to apply.
|
||||
[2Kwger | Your models in app(s): 'exercises', 'gallery' have changes that are not yet reflected in a migration, and so won't be applied.
|
||||
[2Kwger | Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
|
||||
@@ -0,0 +1,60 @@
|
||||
[06:05:43] scratch drive folders cleared before FROM (R-656): none existed
|
||||
[06:05:43] MV-wger: deploying wger at FROM {'wger': 'wger/server:2.7', 'wger-files': 'nginx:1.30.5-alpine'}
|
||||
[06:07:17] FROM settled=True in 93.0s :: {"wger": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "wger-files": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
|
||||
[06:07:17] fixture: the BOX walk's own (Wger), through upgrade_boxport
|
||||
[06:07:17] wger: the generated admin password does not log in (POST /en/user/login -> 200) — running the template's own after_install command (the app's CLI, as the product does after an install)
|
||||
[06:07:19] wger: after_install :: version 8.3.1, blocking by username FELHOM_AFTER_INSTALL_OK
|
||||
[06:07:21] wger: POST /api/v2/weightentry/ http=201
|
||||
[06:07:21] wger: readback of the seeded weight entry http=200 found=True
|
||||
[06:07:21] C1 (seed reads back BEFORE): True
|
||||
[06:07:21] MV-wger: swapping to TO {'wger': 'wger/server:2.7', 'wger-files': 'nginx:1.30.5-alpine'}
|
||||
[06:07:33] TO up -d rc=0
|
||||
[06:08:35] TO settled=True in 62.1s :: {"wger": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "wger-files": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
|
||||
[06:08:35] migration lines observed: 6
|
||||
[06:08:35] wger: readback of the seeded weight entry http=200 found=True
|
||||
[06:08:35] RESULT (seed reads back AFTER): True
|
||||
[06:08:36] memory watch: 600s, 4 callers on 1 path(s) at 172.18.0.2:8000
|
||||
[06:08:51] + 15s wger=288M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=292
|
||||
[06:09:06] + 30s wger=291M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=580
|
||||
[06:09:21] + 46s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=872
|
||||
[06:09:37] + 61s wger=291M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=1160
|
||||
[06:09:52] + 76s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=1452
|
||||
[06:10:07] + 91s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=1740
|
||||
[06:10:22] + 106s wger=291M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=2028
|
||||
[06:10:37] + 121s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=2320
|
||||
[06:10:52] + 136s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=2608
|
||||
[06:11:07] + 152s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=2896
|
||||
[06:11:23] + 167s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=3188
|
||||
[06:11:38] + 182s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=3476
|
||||
[06:11:53] + 197s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=3768
|
||||
[06:12:08] + 212s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=4056
|
||||
[06:12:23] + 227s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=4344
|
||||
[06:12:38] + 242s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=4636
|
||||
[06:12:54] + 258s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=4924
|
||||
[06:13:09] + 273s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=5216
|
||||
[06:13:24] + 288s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=5504
|
||||
[06:13:39] + 303s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=5792
|
||||
[06:13:54] + 318s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=6084
|
||||
[06:14:09] + 334s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=6372
|
||||
[06:14:25] + 349s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=6664
|
||||
[06:14:40] + 364s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=6952
|
||||
[06:14:55] + 379s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=7240
|
||||
[06:15:10] + 394s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=7529
|
||||
[06:15:25] + 409s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=7820
|
||||
[06:15:40] + 424s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=8108
|
||||
[06:15:55] + 440s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=8400
|
||||
[06:16:11] + 455s wger=292M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=8688
|
||||
[06:16:26] + 470s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=8978
|
||||
[06:16:41] + 485s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=9268
|
||||
[06:16:56] + 500s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=9556
|
||||
[06:17:11] + 515s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=9848
|
||||
[06:17:26] + 530s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=10136
|
||||
[06:17:42] + 546s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=10428
|
||||
[06:17:57] + 561s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=10716
|
||||
[06:18:12] + 576s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=11008
|
||||
[06:18:27] + 591s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=11296
|
||||
[06:18:42] + 606s wger=293M/384M peak=384M kills=0 rs=0 wger-files=6M/32M peak=8M kills=0 rs=0 reqs=11584
|
||||
[06:18:42] memory watch: killed=False tight=[] requests=11584 codes={'302': 11584}
|
||||
[06:18:42] MV-wger: ABORT — putting the FROM images back
|
||||
[06:19:56] wger: readback of the seeded weight entry http=200 found=True
|
||||
[06:19:56] ABORT: app came back in 62.0s; data present=True
|
||||
@@ -0,0 +1,58 @@
|
||||
[2K[2Kwger-files | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
|
||||
[2Kwger-files | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
|
||||
[2Kwger-files | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
|
||||
[2Kwger-files | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
|
||||
[2Kwger-files | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
|
||||
[2Kwger-files | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
|
||||
[2Kwger-files | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
|
||||
[2Kwger-files | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
|
||||
[2Kwger-files | /docker-entrypoint.sh: Configuration complete; ready for start up
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: using the "epoll" event method
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: nginx/1.30.5
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: built by gcc 15.2.0 (Alpine 15.2.0)
|
||||
wger | *** Using settings from env: settings.main
|
||||
[2K[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: OS: Linux 7.0.14-20-pve
|
||||
wger | level=INFO ts=2026-10-08 08:07:35,364 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger | Running in production mode, running collectstatic now
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:07:37,454 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger |
|
||||
[2Kwger | 22725 static files deleted, 11362 static files copied to '/home/wger/static', 11362 post-processed.
|
||||
[2Kwger | Performing database migrations
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:08:06,559 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger | System check identified some issues:
|
||||
[2Kwger |
|
||||
[2Kwger | WARNINGS:
|
||||
[2Kwger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
|
||||
[2Kwger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
|
||||
[2Kwger | Operations to perform:
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 524288:524288
|
||||
[2Kwger | Apply all migrations: account, actstream, allauth_idp_oidc, auth, authtoken, axes, config, contenttypes, core, easy_thumbnails, exercises, gallery, gym, mailer, manager, measurements, mfa, nutrition, sessions, sites, socialaccount, token_blacklist, trophies, weight
|
||||
[2K[2Kwger | Running migrations:
|
||||
[2Kwger | No migrations to apply.
|
||||
[2Kwger | Your models in app(s): 'exercises', 'gallery' have changes that are not yet reflected in a migration, and so won't be applied.
|
||||
[2Kwger | Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:08:10,187 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger | System check identified some issues:
|
||||
[2Kwger |
|
||||
[2Kwger | WARNINGS:
|
||||
[2Kwger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
|
||||
[2Kwger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
|
||||
[2Kwger | Set site URL to fitness.gate.invalid
|
||||
[2Kwger | Using gunicorn on port 8000...
|
||||
[2Kwger | level=INFO ts=2026-10-08 08:08:12,859 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
|
||||
[2Kwger | [2026-10-08 08:08:13 +0200] [17] [INFO] Starting gunicorn 26.1.0
|
||||
wger-files | 2026/10/08 06:07:33 [notice] 1#1: start worker processes
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: start worker process 30
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: start worker process 31
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: start worker process 32
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: start worker process 33
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: start worker process 34
|
||||
[2Kwger-files | 2026/10/08 06:07:33 [notice] 1#1: start worker process 35
|
||||
[2Kwger-files | 127.0.0.1 - - [08/Oct/2026:06:08:03 +0000] "GET / HTTP/1.1" 200 896 "-" "Wget" "-"
|
||||
[2Kwger-files | 127.0.0.1 - - [08/Oct/2026:06:08:33 +0000] "GET / HTTP/1.1" 200 896 "-" "Wget" "-"
|
||||
[2Kwger | [2026-10-08 08:08:13 +0200] [17] [INFO] Listening at: http://0.0.0.0:8000 (17)
|
||||
[2Kwger | [2026-10-08 08:08:13 +0200] [17] [INFO] Using worker: sync
|
||||
[2Kwger | [2026-10-08 08:08:13 +0200] [18] [INFO] Booting worker with pid: 18
|
||||
[2Kwger | [2026-10-08 08:08:13 +0200] [19] [INFO] Booting worker with pid: 19
|
||||
[2Kwger | [2026-10-08 08:08:13 +0200] [17] [INFO] Control socket listening at /home/wger/.gunicorn/gunicorn.ctl
|
||||
[2Kwger | level=WARNING ts=2026-10-08 08:08:33,548 module=wsgi path=/home/wger/.local/lib/python3.12/site-packages/gunicorn/http/wsgi.py line=450 message=WSGI app sent body bytes on a no-body response (method=HEAD status=200); dropping per RFC 9110.
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"wger": {
|
||||
"status": "running",
|
||||
"health": "healthy",
|
||||
"restarts": 0,
|
||||
"exit": 0
|
||||
},
|
||||
"wger-files": {
|
||||
"status": "running",
|
||||
"health": "healthy",
|
||||
"restarts": 0,
|
||||
"exit": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"harness_version": 5,
|
||||
"edge": "MV-wger",
|
||||
"app": "wger",
|
||||
"note": "definition step to wger@gunicorn",
|
||||
"from": {
|
||||
"wger": "wger/server:2.7",
|
||||
"wger-files": "nginx:1.30.5-alpine"
|
||||
},
|
||||
"to": {
|
||||
"wger": "wger/server:2.7",
|
||||
"wger-files": "nginx:1.30.5-alpine"
|
||||
},
|
||||
"verdict": "proven",
|
||||
"seed_read_before": true,
|
||||
"seed_read_after": true,
|
||||
"healthy_after": true,
|
||||
"migration_observed": "\u001b[2Kwger | Performing database migrations",
|
||||
"abort": "starts-and-serves",
|
||||
"abort_detail": null,
|
||||
"engine_state_after": null,
|
||||
"memory": {
|
||||
"soak_s": 606.5,
|
||||
"requested_s": 600,
|
||||
"requests": 11584,
|
||||
"codes": {
|
||||
"302": 11584
|
||||
},
|
||||
"first_kill": null,
|
||||
"containers": {
|
||||
"wger": {
|
||||
"limit": 402653184,
|
||||
"peak": 402653184,
|
||||
"peak_pct": 1.0,
|
||||
"anon_peak_sampled": 178151424,
|
||||
"anon_peak_pct": 0.442,
|
||||
"swap_peak": 0,
|
||||
"oom_kills": 0,
|
||||
"restarts": 0,
|
||||
"oomkilled_flag": false,
|
||||
"measured": true
|
||||
},
|
||||
"wger-files": {
|
||||
"limit": 33554432,
|
||||
"peak": 9281536,
|
||||
"peak_pct": 0.277,
|
||||
"anon_peak_sampled": 5308416,
|
||||
"anon_peak_pct": 0.158,
|
||||
"swap_peak": 0,
|
||||
"oom_kills": 0,
|
||||
"restarts": 0,
|
||||
"oomkilled_flag": false,
|
||||
"measured": true
|
||||
}
|
||||
},
|
||||
"unmeasured": [],
|
||||
"venue_swap_bytes": 0,
|
||||
"load": "reached"
|
||||
},
|
||||
"marks": [],
|
||||
"bench_overrides": null,
|
||||
"duration_s": 62.1,
|
||||
"measured_at": "2026-10-08T06:19:56Z",
|
||||
"evidence": "evidence/MV-wger",
|
||||
"scratch_cleared": [],
|
||||
"files_changed": [],
|
||||
"files_changed_detail": [],
|
||||
"files_ignored": [],
|
||||
"total_s": 853.6
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,271 @@
|
||||
# DRAFT — Adatkezelési tájékoztató (Felhom)
|
||||
|
||||
> **English summary.** First draft of the Felhom privacy notice, written 2026-10-08 for R-813. NOT
|
||||
> published, NOT legal advice, pending lawyer review (R-802). It lists what personal data Felhom
|
||||
> handles, where, by whom and for how long — built from the architecture documents and the code,
|
||||
> not from a template; every line cites its source in an HTML comment. Operator facts are
|
||||
> placeholders (`[[CÉGNÉV]]` etc.); facts the system's documents do not state are `[[ELLENŐRIZNI]]`.
|
||||
> Every legal basis named is a candidate for the lawyer, not a conclusion. Section 12 lists what the
|
||||
> lawyer must check and every point where the draft guessed. Section 11 lists places where the
|
||||
> website today says something the system does not do.
|
||||
|
||||
---
|
||||
|
||||
**Hatály:** [[HATÁLYBALÉPÉS DÁTUMA]] · **Verzió:** [[VERZIÓ]]
|
||||
|
||||
## 1. Ki az adatkezelő?
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Név | [[CÉGNÉV]] |
|
||||
| Székhely | [[SZÉKHELY]] |
|
||||
| Cégjegyzékszám / nyilvántartási szám | [[CÉGJEGYZÉKSZÁM]] |
|
||||
| Adószám | [[ADÓSZÁM]] |
|
||||
| E-mail | [[ADATVÉDELMI E-MAIL]] |
|
||||
| Telefon | [[TELEFON]] |
|
||||
| Adatvédelmi tisztviselő | [[ADATVÉDELMI TISZTVISELŐ — vagy: nincs kijelölve]] |
|
||||
|
||||
A továbbiakban: **Felhom** vagy **mi**.
|
||||
|
||||
## 2. Kétféle szerepünk van — és ez a tájékoztató csak az egyikről szól
|
||||
|
||||
1. **Adatkezelőként** kezeljük: a weboldal látogatóinak és a kapcsolatfelvételi űrlap kitöltőinek
|
||||
adatait, valamint az előfizetők (háztartások) ügyfél- és szerződéses adatait. Erről szól ez a
|
||||
tájékoztató.
|
||||
2. **Adatfeldolgozóként** járunk el a háztartás saját szerverén tárolt adatok (fényképek,
|
||||
dokumentumok, alkalmazásadatok) tekintetében: a szervert üzemeltetjük, felügyeljük és mentjük, de
|
||||
az adatok a háztartáséi. Ennek feltételeit a [[ADATFELDOLGOZÁSI MEGÁLLAPODÁS — hivatkozás]]
|
||||
rögzíti. Ahol ez a tájékoztató a szerveren lévő adatokat érinti (mentés, hozzáférés), azt
|
||||
átláthatósági okból írjuk le.
|
||||
<!-- source: documentation/backlog/ROADMAP.md:53 (R-809: "a data-processing agreement (Felhom monitors boxes and holds encrypted off-site backups, so it processes household data)") -->
|
||||
|
||||
## 3. A weboldal (felhom.eu)
|
||||
|
||||
### 3.1 Látogatottság-mérés
|
||||
|
||||
- **Mit:** oldalmegtekintések statisztikája. Az eszköz az **Umami** nevű, **saját üzemeltetésű**
|
||||
mérőprogram, amely a `stats.felhom.eu` címről töltődik be; adatai nem kerülnek külső
|
||||
analitikai szolgáltatóhoz.
|
||||
<!-- source: website/index.html:56, website/kapcsolat.html:28 (script src https://stats.felhom.eu/script.js) -->
|
||||
<!-- source: manifests/umami.yaml:1-7 (Umami v3, dedicated PostgreSQL in felhom-system namespace), :190 (image ghcr.io/umami-software/umami:3.1.0) -->
|
||||
- **Süti:** a mérőprogram a telepítési leírása szerint **nem használ sütit**.
|
||||
[[ELLENŐRIZNI]] — ez a gyártó állítása, nem mért tény.
|
||||
<!-- source: manifests/umami.yaml:277 ("cookie-free, and GDPR compliant" — vendor claim in a manifest comment, not measured) -->
|
||||
- **Milyen adat keletkezik pontosan** (pl. IP-címből származtatott ország, böngészőtípus,
|
||||
hivatkozó oldal): [[ELLENŐRIZNI]] — a konfiguráció ezt nem rögzíti.
|
||||
- **Hol:** a Felhom saját szerverén (k3s fürt, „DooPlex"), ország: [[ELLENŐRIZNI]].
|
||||
<!-- source: README.md:65-73 (k3s single node, felhom-system namespace: umami + umami-db) -->
|
||||
- **Meddig:** a mérőprogramban nincs beállított törlési idő. [[ELLENŐRIZNI — megőrzési idő
|
||||
meghatározandó]]
|
||||
<!-- source: manifests/umami.yaml:194-206 (env: only DATABASE_URL, APP_SECRET, DISABLE_TELEMETRY, TZ — no retention setting) -->
|
||||
- **Jogalap (javaslat, ügyvéd ellenőrzi):** jogos érdek, GDPR 6. cikk (1) f).
|
||||
|
||||
### 3.2 Szervernaplók
|
||||
|
||||
A weboldalt kiszolgáló webszerver (nginx) a kéréseket — köztük a látogató IP-címét — naplózhatja.
|
||||
A konfiguráció külön naplózási beállítást nem tartalmaz; hogy mi és meddig marad meg:
|
||||
[[ELLENŐRIZNI]].
|
||||
<!-- source: manifests/webpage.yaml (nginx:alpine at :250; grep for access_log/log_format returns nothing — default logging, retention not stated anywhere) -->
|
||||
|
||||
### 3.3 Külső tartalmak, sütik
|
||||
|
||||
A weboldal nem tölt be külső betűtípust, külső szkriptet vagy beágyazott tartalmat (a saját
|
||||
mérőprogramon kívül), és nem helyez el reklám- vagy követő sütit.
|
||||
<!-- source: grep over website/*.html for http(s) hosts returns only github.com, gitlab.com, felhom.eu, stats.felhom.eu, iso.felhom.eu, schema.org, formspree.io (the last in a code comment, kapcsolat.html ~:185); grep for googleapis|gstatic|cdn.|cloudflareinsights in website/ returns nothing -->
|
||||
|
||||
A `felhom.eu` domain névszerverét a Cloudflare üzemelteti **csak DNS** módban (a forgalom nem halad
|
||||
át a Cloudflare-en). [[ELLENŐRIZNI — a README ezt állítja; élőben nem mértük]]
|
||||
<!-- source: README.md:57 ("Cloudflare (DNS only, no proxy)"), README.md:108 -->
|
||||
|
||||
## 4. Kapcsolatfelvételi űrlap és e-mail
|
||||
|
||||
- **Mit:** név, e-mail-cím, a választott tárgy, az üzenet szövege és a csatolt fájlok (legfeljebb
|
||||
5 fájl, összesen 20 MB). A rejtett „website" mező csak a kéretlen üzenetek kiszűrésére szolgál.
|
||||
<!-- source: website/kapcsolat.html:72-75 (honeypot), :78, :83, :91, :104, :111 (fields); :193-196 (maxTotalFileSize 20 MB, maxFiles 5); :467-476 (fields actually sent) -->
|
||||
- **Cél:** a megkeresés megválaszolása, illetve a választott tárgy szerint a zárt tesztre
|
||||
jelentkezés, érdeklődés, árajánlat vagy támogatás.
|
||||
<!-- source: website/kapcsolat.html:92-97 (subject options) -->
|
||||
- **Útja:** a böngésző a `felhom.eu/api/contact` címre küldi; a Felhom saját kis programja
|
||||
(„contact-mailer") e-mailként továbbítja a **Resend** levélküldő szolgáltatáson keresztül az
|
||||
`info@felhom.eu` címre; a beérkező leveleket a **Cloudflare Email Routing** továbbítja a
|
||||
**Gmail**-fiókba, ahol olvassuk.
|
||||
<!-- source: website/kapcsolat.html:191 (formEndpoint '/api/contact'), :477 (fetch POST) -->
|
||||
<!-- source: manifests/contact-mailer.yaml:1-2 ("Uses Resend.com API"), :66-69 (FROM noreply@felhom.eu, TO info@felhom.eu) -->
|
||||
<!-- source: README.md:133-135 (Cloudflare Email Routing: info@ and admin@ → personal Gmail), README.md:146-151 (contact form flow) -->
|
||||
- **Meddig:** amíg a megkeresés lezárul, majd [[ELLENŐRIZNI — megőrzési idő meghatározandó]]. A
|
||||
levélfiókban, a Resend-nél és a contact-mailer naplójában maradó másolatok ideje:
|
||||
[[ELLENŐRIZNI]] — a contact-mailer forráskódja nincs a repóban, így nem ellenőriztük, mit naplóz.
|
||||
<!-- source: find over felhom.eu for a contact-mailer source dir returns nothing; README.md:71 names it a Go app; manifests/contact-mailer.yaml:6 builds it from an unnamed directory -->
|
||||
- **Jogalap (javaslat, ügyvéd ellenőrzi):** az érintett hozzájárulása, GDPR 6. cikk (1) a); árajánlat
|
||||
és szerződéskötés előtti lépés esetén GDPR 6. cikk (1) b).
|
||||
|
||||
Ha közvetlenül az `info@felhom.eu` címre ír, ugyanez az út érvényes a Resend kivételével.
|
||||
<!-- source: website/kapcsolat.html:142 (mailto:info@felhom.eu); README.md:133-135 -->
|
||||
|
||||
## 5. Előfizetők (háztartások) adatai a Felhom központi rendszerében („hub")
|
||||
|
||||
A központi rendszer (`hub.felhom.eu`) a Felhom saját szerverén fut (k3s fürt), ország:
|
||||
[[ELLENŐRIZNI]].
|
||||
<!-- source: manifests/hub.yaml:111-131 (Deployment felhom-hub, namespace felhom-system), :343 (host hub.felhom.eu); documentation/architecture/01-topology-and-trust.md:82 ("Runs on dooplex.hu (k3s)") -->
|
||||
|
||||
| Adat | Mire kell | Meddig marad meg |
|
||||
|---|---|---|
|
||||
| Ügyfél azonosítója, neve, domainje, e-mail-címe, nyelve | szerződés teljesítése, értesítések | az ügyfél törléséig <!-- source: hub/internal/store/store.go:161-171 (customer_configs: customer_id, customer_name, domain, email); :223 (language); store.go DELETE FROM customer_configs; documentation/architecture/00-capability-map.md:97 (Customer DELETE cascade) --> |
|
||||
| A szerver állapotjelentései (gépnév, processzor-, memória-, lemezhasználat, hőmérséklet, a telepített alkalmazások neve és állapota, mentések állapota, a dashboard nyelve) | felügyelet, hibajelzés | **90 nap** <!-- source: felhom-controller/controller/internal/report/types.go:13-56, :66-79 (report fields); hub/internal/store/store.go:1528-1540 (Prune: reports + host_reports); manifests/hub.yaml:78 (retention.max_days: 90); hub/cmd/hub/main.go:930 (default 90) --> |
|
||||
| Események (pl. „mentés sikertelen", „lemez megtelt") | felügyelet, ügyfélnek látható napló | **90 nap** <!-- source: hub/internal/store/store.go:2695-2702 (PruneEvents); manifests/hub.yaml:78 --> |
|
||||
| Alkalmazásonkénti erőforrás-statisztika | kapacitástervezés | **90 nap** <!-- source: hub/cmd/hub/main.go:1022 (PruneAppTelemetry 90 days) --> |
|
||||
| Alkalmazásnaplókból kiszűrt hibaüzenetek, előtte-utána 5 sor, kitakarással | hibaelhárítás | az utolsó előfordulás után **30 nap** <!-- source: hub/internal/store/telemetry.go:35 (±5 redacted lines); hub/cmd/hub/main.go:1027 (PruneStaleIssues 30 days) --> |
|
||||
| Alkalmazásnapló-részlet, csak külön kérésre, kitakarással | hibaelhárítás | alkalmazásonként a **legutóbbi 2**, időkorlát nélkül; az ügyfél törlésekor törlődik <!-- source: felhom-controller/controller/internal/report/types.go:39-41 ("on-demand… Redacted + capped"); hub/internal/store/logtail.go:74-82 (keep newest 2 per customer+app); hub/internal/store/customer_delete.go:57-58 (purged at delete) --> |
|
||||
| Diagnosztikai naplócsomag, csak külön kérésre | hibaelhárítás | **72 óra** <!-- source: hub/internal/store/logbundle.go:28-29 (logBundleTTL = 72h), :234 --> |
|
||||
| Kiküldött értesítések naplója (esemény, szöveg, kézbesítés állapota) | elszámolhatóság | **nincs törlési idő; az ügyfél törlése után is megmarad** — lásd 11. pont <!-- source: hub/internal/store/store.go:147-156 (notification_log columns); no DELETE FROM notification_log anywhere in hub/internal/store (grep); hub/internal/store/customer_delete.go:23-25 ("deliberately SURVIVES … notification_log") --> |
|
||||
| Az ügyfél-visszaállítás és a szervertörlés naplója | elszámolhatóság | **nincs törlési idő** <!-- source: hub/internal/store/store.go:734 ("NEVER pruned (audit outlives every lifecycle tier)"); customer_delete.go:25 --> |
|
||||
| **A mentés titkosító kulcsa, a háztartás helyreállító kódjával lezárva** („kulcsletét") | a mentés visszaállíthatósága gépcsere után | a szerver / ügyfél törléséig; a lecserélt régi kulcsok is megmaradnak, hogy a régi mentések nyithatók maradjanak <!-- source: hub/internal/store/store.go:400-411 (host_escrow: "OPAQUE … NEVER decrypts"), :413-446 (host_escrow_superseded; "NO pruning" at :418); store.go:4122 (PurgeSupersededEscrowForCustomer); documentation/architecture/00-capability-map.md:97 --> |
|
||||
| A szerver vészhelyzeti konzoljelszava, titkosítva tárolva | üzemeltetés, hibaelhárítás | a szerver törléséig <!-- source: hub/internal/store/store.go:552-564 (host_recovery, CREATE at :558); documentation/architecture/05-hub-architecture.md:441-443 (sealed AES-256-GCM) --> |
|
||||
|
||||
**A kulcsletétről, egyszerűen:** a mentés kulcsát a Felhom csak lezárt formában tárolja. Kinyitni
|
||||
csak a háztartás **helyreállító kódjával** lehet, amely a Felhomnál nincs meg. Ha a háztartás
|
||||
elveszíti ezt a kódot, a távoli mentés nem nyitható ki.
|
||||
<!-- source: documentation/architecture/07-backup-architecture.md:67-75 ("The escrow is genuinely zero-knowledge today… R exists in zero system copies by design"); documentation/architecture/05-hub-architecture.md:213-214, :233-236 -->
|
||||
|
||||
**A központi rendszer adatbázisáról mentés készül:** éjjelente helyben (a legutóbbi 2 marad meg),
|
||||
és titkosítva a távoli mentőszerverre („ep0", lásd 6. pont), ahol **14 napi és 8 heti** példány
|
||||
marad meg; ennek a mentőszervernek a másolata a Felhom saját szerverén **8 heti** példányt tart.
|
||||
Ezért egy törölt ügyfél adatai a mentésekben a fenti ideig még megtalálhatók.
|
||||
<!-- source: documentation/architecture/05-hub-architecture.md:466-474 (nightly VACUUM INTO, newest 2 stay; encrypted push to ep0 operator namespace) -->
|
||||
<!-- source: documentation/runbooks/RUNBOOK-hub-db-offsite-backup.md:72-73 (prune-operator-hubdb --keep-daily 14 --keep-weekly 8) -->
|
||||
<!-- source: documentation/runbooks/ep0-datastore-copy.md:15-17 (DooPlex pull, remove-vanished false; prune keep-weekly 8, all namespaces) -->
|
||||
|
||||
**Jogalap (javaslat, ügyvéd ellenőrzi):** szerződés teljesítése, GDPR 6. cikk (1) b); a naplók
|
||||
megőrzésére jogos érdek, 6. cikk (1) f).
|
||||
|
||||
## 6. A háztartás adatainak távoli mentése (adatfeldolgozás)
|
||||
|
||||
A háztartás szerverén lévő adatokról két távoli mentés készül. **Mindkettő a szerveren titkosítva
|
||||
készül, mielőtt elhagyja a háztartást**; a tárhely üzemeltetője és a Felhom a titkosított tartalmat
|
||||
nem tudja elolvasni (a kulcsot lásd az 5. pont „kulcsletét" sorában).
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:118 (box ↔ PBS: "ciphertext only (operator can't read)"); documentation/architecture/07-backup-architecture.md:378-379 (Encrypted: yes (restic) / yes (per-customer key)) -->
|
||||
|
||||
| Mentés | Hol | Mit tartalmaz | Meddig |
|
||||
|---|---|---|---|
|
||||
| Alkalmazásonkénti fájlmentés | **Hetzner Storage Box**, helyszínkód `fsn1`, ország: [[ELLENŐRIZNI]] | alkalmazásadatok, adatbázisok, megosztások | 7 napi, 4 heti, 6 havi példány; ügyfél-visszaállításkor (RESET) törlődik. A tárhely saját napi pillanatképeinek ideje: [[ELLENŐRIZNI]] |
|
||||
| Teljes szervermentés | **„ep0"** távoli mentőszerver, Hetzner Cloud, **Nürnberg (Németország)** | a teljes ügyfélkonténer | a legutóbbi 2 heti példány; az ügyfél törlésekor törlődik |
|
||||
| Az ep0 másolata | a Felhom saját szerverén, ország: [[ELLENŐRIZNI]] | a fenti, továbbra is titkosítva | 8 heti példány — **a törlés után is**, lásd 11. pont |
|
||||
|
||||
<!-- source (row 1): documentation/architecture/07-backup-architecture.md:377 (Tier-3: Hetzner Storage Box over SFTP; --keep-daily 7 --keep-weekly 4 --keep-monthly 6); manifests/hub.yaml:224-226 (HETZNER_LOCATION "fsn1"); hub/cmd/hub/main.go:386 (default fsn1); documentation/audits/VALIDATION-offsite-provisioning-e2e-2026-07-09.md:6 (pool box BX11 fsn1); documentation/architecture/07-backup-architecture.md:258-266 (RESET purges the repository); hub/internal/monitor/offsite.go:300, :317 (daily Storage Box snapshots read-only) -->
|
||||
<!-- source (row 2): documentation/runbooks/RUNBOOK-ep0-datastore-volume-2026-07-27.md:6 ("Hetzner CX33 … Nuremberg"); documentation/architecture/07-backup-architecture.md:379 (keep-last 2, server-side prune on ep0); documentation/architecture/05-hub-architecture.md:296 (RESET / customer delete: deprovisioned — namespace, every backup group AND token) -->
|
||||
<!-- source (row 3): documentation/architecture/06-offsite-connectivity.md:189 (nightly pull-sync ep0 → DooPlex, ciphertext per customer); documentation/runbooks/ep0-datastore-copy.md:15-17 ("It never removes what ep0 removed"; keep-weekly 8) -->
|
||||
|
||||
## 7. Hozzáférés a háztartás szerveréhez
|
||||
|
||||
Őszintén: **a Felhom üzemeltetőjének rendszergazdai (root) hozzáférése van minden általa kezelt
|
||||
szerverhez.** Ez kell a frissítésekhez, a mentések ellenőrzéséhez és a hibaelhárításhoz. Ezzel a
|
||||
hozzáféréssel a szerveren tárolt adatok elérhetők. A hozzáférést csak [[CÉL ÉS FELTÉTELEK — pl.
|
||||
hibaelhárítás, az ügyfél kérésére / tudtával]] használjuk.
|
||||
<!-- source: documentation/architecture/07-backup-architecture.md:49-52 ("The operator holds root SSH on every box… 'The operator cannot read customer data' was never the security property"), :56-63 (OOB SSH key on every box; break-glass password in the hub; guest data reachable from the host) -->
|
||||
|
||||
Ami a hozzáférésen túl a központi rendszerbe jut, azt az 5. pont sorolja fel: állapotadatok és —
|
||||
csak hibaelhárításhoz, külön kérésre — kitakart naplórészletek. A háztartás fájljai és alkalmazásadatai
|
||||
nem kerülnek a központi rendszerbe.
|
||||
<!-- source: felhom-controller/controller/internal/report/types.go:13-56 (report contents); documentation/architecture/05-hub-architecture.md:206 ("the actual app data + configs live inside the PBS guest snapshot") -->
|
||||
|
||||
Az üzemeltetői műveletek ügyfél által látható naplója: [[ELLENŐRIZNI — a terv (01 §4) ezt ígéri;
|
||||
hogy minden művelet valóban megjelenik-e, nem ellenőriztük]].
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:105 ("All operator-initiated actions are recorded in a customer-visible audit log") — a [DESIGN]-era statement, not marked [FACT] -->
|
||||
|
||||
## 8. A háztartás alkalmazásainak elérése az interneten (Cloudflare)
|
||||
|
||||
A háztartás alkalmazásai és vezérlőpultja a **Cloudflare Tunnel** szolgáltatáson keresztül érhetők
|
||||
el az internetről. **A titkosított kapcsolat a Cloudflare hálózatán végződik**, vagyis az
|
||||
alkalmazások forgalma (a látogató IP-címe, a kérések, a továbbított tartalom) a Cloudflare
|
||||
rendszerén áthalad. A háztartás földrajzi korlátozást is beállíthat, amelyet a központi rendszer a
|
||||
Cloudflare-en érvényesít.
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:113-114 (end-user ↔ apps: Cloudflare Tunnel → Traefik), :120 (hub ↔ Cloudflare API, geo WAF), :202-224 (tunnel; cloudflared inside the guest) -->
|
||||
<!-- source: documentation/architecture/05-hub-architecture.md:178-180 ("in the Cloudflare-Tunnel-default model the edge terminates TLS") -->
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:131-136 (Cloudflare appends the visitor address, CF-Connecting-IP) -->
|
||||
|
||||
Hogy a háztartás domainje kinek a Cloudflare-fiókjában van, és ki a Cloudflare szerződéses
|
||||
partnere: [[ELLENŐRIZNI — a telepítési leírás szerint a tunnelt az üzemeltető hozza létre a
|
||||
Cloudflare-felületen; a fiók tulajdonosa nincs rögzítve]].
|
||||
<!-- source: documentation/runbooks/day0-install.md:39-58 (A.1: operator creates the tunnel in the Cloudflare dashboard; "Make sure the domain is on Cloudflare"); documentation/architecture/01-topology-and-trust.md:207-215 (customer's own domain, included in the price) -->
|
||||
|
||||
## 9. Adatfeldolgozók és címzettek
|
||||
|
||||
| Szolgáltató | Mit csinál | Milyen adatot lát | Ország |
|
||||
|---|---|---|---|
|
||||
| **Resend** | e-mail-küldés: az űrlap levelei; a központi rendszer levelei a háztartásoknak (értesítés, beállító kód, összekapcsoló link) és az üzemeltetőnek | címzett e-mail-címe, a levél tartalma | [[ELLENŐRIZNI]] — a küldő domain DNS-bejegyzése `eu-west-1` régióra mutat |
|
||||
| **Cloudflare** | (a) a háztartások alkalmazásainak internetes elérése (Tunnel); (b) a `@felhom.eu` címekre érkező levelek továbbítása (Email Routing); (c) a `felhom.eu` DNS | (a) az alkalmazások teljes forgalma; (b) a beérkező levelek; (c) — | [[ELLENŐRIZNI]] |
|
||||
| **Google (Gmail)** | a `info@` és `admin@felhom.eu` címre érkező levelek postafiókja | a beérkező levelek, köztük az űrlap üzenetei | [[ELLENŐRIZNI]] |
|
||||
| **Hetzner** | (a) Storage Box: alkalmazásonkénti titkosított mentés; (b) Cloud szerver „ep0": titkosított teljes szervermentés és a központi adatbázis titkosított mentése | csak titkosított adat; az ügyfél azonosítója a tárhely nevében/címkéjében | (a) `fsn1` helyszínkód, [[ELLENŐRIZNI]]; (b) Németország (Nürnberg) |
|
||||
| **A Felhom saját szervere** (nem külső szolgáltató) | weboldal, mérőprogram, űrlap-továbbító, központi rendszer, az ep0 másolata | lásd 3–6. pont | [[ELLENŐRIZNI]] |
|
||||
|
||||
<!-- source (Resend): hub/internal/notify/dispatcher.go:907 (https://api.resend.com); manifests/contact-mailer.yaml:1-2; documentation/architecture/05-hub-architecture.md:308-318 (the four customer mails; operator channel); manifests/hub.yaml:94 (operator_email); README.md:121 (MX send → feedback-smtp.eu-west-1.amazonses.com) -->
|
||||
<!-- source (Cloudflare): documentation/architecture/01-topology-and-trust.md:113-114, :120; hub/internal/cloudflare/unblock.go:15 (api.cloudflare.com); README.md:57, :120, :133-136 -->
|
||||
<!-- source (Google): README.md:134-135 ("forwarded to personal Gmail") -->
|
||||
<!-- source (Hetzner): hub/internal/hetznerapi/hetznerapi.go:3 (api.hetzner.com); hub/internal/offsite/offsite.go:404-405, :443-448 (labels/description/name carry the customer id); documentation/runbooks/RUNBOOK-ep0-datastore-volume-2026-07-27.md:6; documentation/runbooks/RUNBOOK-hub-db-offsite-backup.md:72-73 -->
|
||||
|
||||
Az adatokat harmadik országba [[ELLENŐRIZNI — az ügyvéd mondja meg, mely szolgáltatónál van
|
||||
EGT-n kívüli továbbítás, és milyen garanciával]].
|
||||
|
||||
## 10. Az érintett jogai
|
||||
|
||||
[[ÜGYVÉD TÖLTI KI — hozzáférés, helyesbítés, törlés, korlátozás, adathordozhatóság, tiltakozás,
|
||||
hozzájárulás visszavonása; a kérés módja (az 1. pontban megadott adatvédelmi e-mail-címen); válaszidő]]
|
||||
|
||||
Panasz: Nemzeti Adatvédelmi és Információszabadság Hatóság (NAIH) — [[NAIH ELÉRHETŐSÉG]];
|
||||
bírósági jogérvényesítés: [[ÜGYVÉD TÖLTI KI]].
|
||||
|
||||
## 11. Ahol a mai weboldal és a rendszer nem egyezik (javítandó a közzététel előtt)
|
||||
|
||||
1. **„az adataid soha nem hagyják el a hálózatodat"** (GYIK). Nem igaz: a távoli mentések
|
||||
(titkosítva) Hetzner-tárhelyre kerülnek, az alkalmazások forgalma a Cloudflare-en halad át,
|
||||
állapotjelentések és kérésre naplórészletek a központi rendszerbe jutnak.
|
||||
<!-- source: website/gyik.html:670; contradicted by §6, §8, §5 above -->
|
||||
2. **„az adataid a saját infrastruktúrádon vannak — nem harmadik félnél"** (GYIK, GDPR-kérdés).
|
||||
A távoli mentés harmadik félnél (Hetzner) van, titkosítva.
|
||||
<!-- source: website/gyik.html:204, :691 -->
|
||||
3. **„önálló modell: … a távoli hozzáférést eltávolítjuk … a mentési kulcsokat kizárólag te
|
||||
ismered"** (GYIK). Hogy ez a termékben létező, megrendelhető mód-e: [[ELLENŐRIZNI]]; a
|
||||
rendszerleírás szerint a root hozzáférés a termék része.
|
||||
<!-- source: website/gyik.html:188, :669; documentation/architecture/07-backup-architecture.md:49-52 -->
|
||||
4. **Az űrlap hozzájárulási szövege: „Az adatokat harmadik félnek nem adjuk ki."** Az üzenetet a
|
||||
Resend, a Cloudflare és a Google adatfeldolgozóként kezeli. Javasolt új szöveg:
|
||||
`DRAFT-kapcsolat-hozzajarulas.md`.
|
||||
<!-- source: website/kapcsolat.html:122-128 -->
|
||||
5. **Törlés után megmaradó adatok.** A kiküldött értesítések naplója nem törlődik soha; a teljes
|
||||
szervermentés Felhom-oldali másolata a törlés után is megtartja az utolsó 8 heti példányt, és
|
||||
egyetlen dokumentum sem mondja, mikor törlődnek. Ezt a tájékoztató csak akkor ígérheti
|
||||
másképp, ha a rendszer változik.
|
||||
<!-- source: hub/internal/store/customer_delete.go:23-25; documentation/runbooks/ep0-datastore-copy.md:15-17 -->
|
||||
|
||||
## 12. Az ügyvédnek ellenőrizni (R-802) — és ahol a vázlat találgatott
|
||||
|
||||
**Ellenőrizni:**
|
||||
|
||||
1. Minden jogalap (3.1, 4, 5. pont) — a vázlat csak jelöltet nevez meg.
|
||||
2. Adatkezelő vagy adatfeldolgozó a Felhom a háztartás szerverén lévő adatokra, és mi kerüljön az
|
||||
adatfeldolgozási megállapodásba (R-809).
|
||||
3. EGT-n kívüli továbbítás és garanciái: Resend, Cloudflare, Google.
|
||||
4. Kell-e sütitájékoztató / hozzájárulás a mérőprogramhoz, ha valóban nem használ sütit.
|
||||
5. A megőrzési idők: hol kell rövidebb (pl. a soha nem törlődő értesítési napló), hol kötelező
|
||||
hosszabb (számviteli bizonylatok — [[ELLENŐRIZNI]]).
|
||||
6. A root hozzáférés (7. pont) bemutatása elég-e, és mit kell róla a szerződésnek mondania.
|
||||
7. A háztartás látogatóinak (pl. családtagok, vendégek) adatai a Cloudflare-en áthaladó forgalomban:
|
||||
kinek a felelőssége, és kell-e róla tájékoztatni.
|
||||
8. A zárt teszt résztvevőire (tesztelői megállapodás — R-809) ugyanez a tájékoztató vonatkozik-e.
|
||||
9. A „háztartási kivétel" állítás a GYIK-ben (gyik.html:204) megállja-e a helyét.
|
||||
|
||||
**Ahol a vázlat találgatott vagy nem talált forrást:**
|
||||
|
||||
1. A Felhom saját szerverének („DooPlex") országa — a dokumentumok nem mondják ki.
|
||||
2. A Storage Box `fsn1` helyszínkódjának országa — a kód és a dokumentumok csak a kódot írják.
|
||||
3. A Resend, a Cloudflare és a Google adatkezelési helye — sehol nincs rögzítve; a Resend `eu-west-1`
|
||||
csak egy DNS-bejegyzésből látszik.
|
||||
4. Hogy a mérőprogram valóban nem használ sütit, és milyen adatot rögzít — csak a gyártói megjegyzés.
|
||||
5. A mérőprogram és a webszerver naplóinak megőrzési ideje — nincs beállítva / nincs leírva.
|
||||
6. A contact-mailer mit naplóz — forrása nincs a repóban.
|
||||
7. A levelek megőrzése a Gmail-fiókban és a Resend-nél.
|
||||
8. A Storage Box saját napi pillanatképeinek megőrzési ideje.
|
||||
9. Kinek a Cloudflare-fiókjában van a háztartás domainje.
|
||||
10. Hogy az ügyfél által látható üzemeltetői napló minden műveletet tartalmaz-e (tervezési állítás).
|
||||
11. A README szerint a weboldal DNS-e „csak DNS" módban fut a Cloudflare-en — élőben nem mértük.
|
||||
12. A hub központi adatbázisának k3s/Longhorn szintű mentései, ha vannak — nem kerestük.
|
||||
@@ -0,0 +1,170 @@
|
||||
# DRAFT — Általános Szerződési Feltételek (Felhom)
|
||||
|
||||
> **English summary.** First draft of the Felhom general terms (ÁSZF), written 2026-10-08 for
|
||||
> R-813/R-809. NOT published, NOT legal advice, pending lawyer review (R-802). It is a structured
|
||||
> skeleton: the service description is drawn from the architecture documents (each cited in an HTML
|
||||
> comment); every business term — prices, term, notice periods, liability caps, the company's
|
||||
> identity — is a placeholder (`[[...]]`). Nothing commercial is invented. Section 14 lists what the
|
||||
> lawyer must check and every point where the draft guessed.
|
||||
|
||||
---
|
||||
|
||||
**Hatály:** [[HATÁLYBALÉPÉS DÁTUMA]] · **Verzió:** [[VERZIÓ]]
|
||||
|
||||
## 1. A szolgáltató
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Név | [[CÉGNÉV]] |
|
||||
| Székhely | [[SZÉKHELY]] |
|
||||
| Cégjegyzékszám / nyilvántartási szám | [[CÉGJEGYZÉKSZÁM]] |
|
||||
| Adószám | [[ADÓSZÁM]] |
|
||||
| E-mail | [[KAPCSOLATI E-MAIL]] |
|
||||
| Telefon | [[TELEFON]] |
|
||||
| Panaszkezelés helye és módja | [[PANASZKEZELÉS]] |
|
||||
|
||||
## 2. Fogalmak
|
||||
|
||||
- **Felhom / Szolgáltató:** az 1. pontban megnevezett vállalkozás.
|
||||
- **Előfizető:** a szolgáltatást megrendelő [[természetes személy / háztartás — ELLENŐRIZNI:
|
||||
fogyasztó-e]].
|
||||
- **Szerver:** az Előfizető otthonában működő számítógép, amelyen a Felhom rendszere fut.
|
||||
- **Alkalmazás:** a Felhom katalógusából a Szerverre telepíthető program (pl. fénykép-, dokumentum-,
|
||||
jelszókezelő).
|
||||
- **Vezérlőpult:** a Szerver webes kezelőfelülete (`felhom.<az Előfizető domainje>`).
|
||||
- **Helyreállító kód:** az Előfizetőnél lévő kód, amely nélkül a távoli mentés nem nyitható ki.
|
||||
|
||||
## 3. A szolgáltatás tárgya
|
||||
|
||||
A Felhom egy **otthoni szervert** üzemeltet az Előfizető háztartásában, és ehhez a következőket
|
||||
nyújtja:
|
||||
<!-- source: CLAUDE.md (workspace root) "Felhom, a managed home-server service for Hungarian households" -->
|
||||
|
||||
1. **A szerver telepítése és beállítása** — személyes jelenléttel vagy előre telepítve átadva.
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:182-183 ("Physical presence at provisioning (on-site install, or pre-imaged-and-delivered)") -->
|
||||
2. **Alkalmazások** telepítése a Felhom katalógusából, a vezérlőpulton keresztül.
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:85 (controller: Docker/app lifecycle, catalog deploy, customer UI) -->
|
||||
3. **Internetes elérés** a háztartás saját domainjén, a Cloudflare Tunnel szolgáltatáson keresztül.
|
||||
A domain díja [[A DÍJ RÉSZE / KÜLÖN FIZETENDŐ — a tervezési döntés szerint a díj része]].
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:202-215 (Cloudflare Tunnel; every customer has their own domain, "included in the customer's price") -->
|
||||
4. **Felügyelet:** a szerver állapotjelentést küld a Felhom központi rendszerének; hiba esetén a
|
||||
Felhom és — beállítástól függően — az Előfizető e-mailt kap.
|
||||
<!-- source: documentation/architecture/05-hub-architecture.md:71-103 (report ingest, liveness, backup-deadline checker), :308-318 (customer mails) -->
|
||||
5. **Mentések** több szinten:
|
||||
- helyi mentés a szerveren és (ha van) második meghajtón;
|
||||
- **távoli mentés**, titkosítva, a Felhom által bérelt tárhelyen (Hetzner);
|
||||
- **teljes szervermentés** a Felhom távoli mentőszerverére.
|
||||
A mentések részletei és megőrzési ideje: [[MELLÉKLET / ADATKEZELÉSI TÁJÉKOZTATÓ 6. PONT]].
|
||||
<!-- source: documentation/architecture/07-backup-architecture.md:373-379 (the four tiers as configured on the live fleet) -->
|
||||
6. **Frissítések:** az alkalmazások, a rendszer és az operációs rendszer frissítése, jellemzően
|
||||
éjszakai időablakban, amelyet az Előfizető állíthat be.
|
||||
<!-- source: documentation/architecture/07-backup-architecture.md:420 ("The three nightly legs derive from one customer-settable window start W") -->
|
||||
7. **Ügyfélszolgálat:** [[ELÉRHETŐSÉG, VÁLASZIDŐ, NYITVATARTÁS]].
|
||||
|
||||
A szolgáltatás **nem** tartalmazza: [[KIZÁRÁSOK — pl. az Előfizető saját eszközeinek javítása,
|
||||
internet-előfizetés, áramellátás]].
|
||||
|
||||
## 4. Hardver
|
||||
|
||||
[[A SZERVER TULAJDONJOGA: a Felhom adja (bérlet / eladás) VAGY az Előfizető saját gépe — a rendszer
|
||||
mindkét telepítési módot ismeri („appliance" és „byo")]]. Garancia, csere, visszaszolgáltatás a
|
||||
szerződés végén: [[FELTÉTELEK]].
|
||||
<!-- source: scripts/felhom-host-install.sh:39, :79 (--mode appliance|byo); hub/internal/store/store.go:764 (appliance_registrations.install_mode) — the commercial meaning of the two modes is not documented -->
|
||||
|
||||
## 5. Az Előfizető kötelezettségei
|
||||
|
||||
1. Biztosítja a szerver áramellátását és internetkapcsolatát [[RÉSZLETEK]].
|
||||
2. **Megőrzi a helyreállító kódot.** A kódot a Felhom nem ismeri és nem tudja pótolni; elvesztése
|
||||
esetén a távoli mentés nem állítható vissza.
|
||||
<!-- source: documentation/architecture/07-backup-architecture.md:67-75 (zero-knowledge escrow; "R exists in zero system copies by design"); :77-83 (the household is asked for it from the first login) -->
|
||||
3. A vezérlőpult jelszavát titokban tartja.
|
||||
4. Az alkalmazásokat jogszerűen használja; a harmadik féltől származó alkalmazások saját licencfeltételei
|
||||
rá is vonatkoznak.
|
||||
<!-- source: documentation/backlog/OPEN-ITEMS.md:268 (R-802: non-OSI licence list — Tandoor, SparkyFitness, Emby, n8n, Plex, EE/BUSL parts, redis 7.4) -->
|
||||
5. [[TOVÁBBI KÖTELEZETTSÉGEK]]
|
||||
|
||||
## 6. A Felhom hozzáférése a szerverhez
|
||||
|
||||
A Felhom üzemeltetőjének **rendszergazdai (root) hozzáférése van** a szerverhez. Ezt
|
||||
frissítésre, mentésellenőrzésre és hibaelhárításra használja. A szerver a Felhom felé maga
|
||||
kezdeményez kapcsolatot; a veszélyes (adatvesztéssel járó) műveletekhez az üzemeltető külön
|
||||
aláírása kell.
|
||||
<!-- source: documentation/architecture/07-backup-architecture.md:49-52 (operator holds root SSH on every box) -->
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:97-105 (box-initiated control, signed jobs, customer-visible audit log) -->
|
||||
|
||||
A hozzáférés használatának feltételei, az Előfizető értesítése: [[FELTÉTELEK]].
|
||||
Az „önálló modell" (a távoli hozzáférés eltávolítása) elérhetősége: [[ELLENŐRIZNI — a GYIK
|
||||
ígéri (website/gyik.html:669); a rendszerleírásban nem találtuk]].
|
||||
|
||||
## 7. Az adatok az Előfizetőéi
|
||||
|
||||
Az Előfizető adatai és telepített alkalmazásai az Előfizetőéi. A Felhom nem tartja vissza őket: a
|
||||
szerződés megszűnése után is visszaállíthatók az Előfizető helyreállító kódjával.
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:300-302 ("Never hold data hostage … the customer's data and deployed apps remain recoverable by the customer (recovery code), with nothing locked behind the operator") -->
|
||||
|
||||
A szerződés megszűnésekor: [[ADATÁTADÁS, A TÁVOLI MENTÉSEK TÖRLÉSÉNEK IDEJE — lásd az
|
||||
adatkezelési tájékoztató 11. pont 5.: a Felhom-oldali másolat ma nem törlődik határidőre]].
|
||||
<!-- source: documentation/architecture/01-topology-and-trust.md:309-310 ("Offboarding / decommission … not yet designed") -->
|
||||
|
||||
## 8. Díjak és fizetés
|
||||
|
||||
| Tétel | Díj |
|
||||
|---|---|
|
||||
| Telepítés | [[DÍJ]] |
|
||||
| Havi / éves előfizetés | [[DÍJ]] |
|
||||
| Hardver | [[DÍJ / BÉRLETI DÍJ / NEM ÉRTELMEZETT]] |
|
||||
| Domain | [[DÍJ — vagy: az előfizetés része]] |
|
||||
| Távoli mentés tárhely-bővítése | [[DÍJ]] |
|
||||
|
||||
Fizetési mód, számlázás, késedelem: [[FELTÉTELEK]]. Áremelés: [[FELTÉTELEK]].
|
||||
<!-- source: documentation/backlog/ROADMAP.md:53 (R-809: billing and invoicing — not built) -->
|
||||
|
||||
## 9. A szolgáltatás szintje
|
||||
|
||||
Rendelkezésre állás: [[VÁLLALT SZINT — vagy: nincs vállalt szint]]. A Felhom a szerver
|
||||
elérhetetlenségét figyeli és jelzi; az otthoni áram- és internetkimaradás nem a Felhom hibája.
|
||||
Hibabejelentés és válaszidő: [[FELTÉTELEK]].
|
||||
|
||||
## 10. Felelősség
|
||||
|
||||
[[ÜGYVÉD TÖLTI KI — felelősségkorlátozás, vis maior, adatvesztés: a mentések megléte nem
|
||||
garantálja minden adat visszaállíthatóságát; az elveszett helyreállító kód következménye]].
|
||||
|
||||
## 11. A szerződés időtartama és megszűnése
|
||||
|
||||
Időtartam: [[HATÁROZOTT / HATÁROZATLAN]]. Felmondási idő: [[NAP]]. Elállási jog (fogyasztó
|
||||
esetén): [[ÜGYVÉD TÖLTI KI]]. A Felhom általi felmondás esetei: [[FELTÉTELEK]].
|
||||
|
||||
## 12. Adatkezelés
|
||||
|
||||
Az adatkezelésről az **Adatkezelési tájékoztató** [[LINK]] szól; a szerveren tárolt adatok
|
||||
feldolgozásáról az **Adatfeldolgozási megállapodás** [[LINK — még nincs megírva, R-809]].
|
||||
|
||||
## 13. Vegyes rendelkezések
|
||||
|
||||
Irányadó jog: [[ÜGYVÉD TÖLTI KI]]. Vitarendezés, békéltető testület: [[ÜGYVÉD TÖLTI KI]].
|
||||
Az ÁSZF módosítása és annak közlése: [[FELTÉTELEK]].
|
||||
|
||||
## 14. Az ügyvédnek ellenőrizni (R-802) — és ahol a vázlat találgatott
|
||||
|
||||
**Ellenőrizni:**
|
||||
|
||||
1. Fogyasztói szerződés-e (háztartás) — elállási jog, tájékoztatási kötelezettségek, távollévők
|
||||
közötti szerződés szabályai.
|
||||
2. A harmadik féltől származó alkalmazások licencei (R-802 listája: Tandoor, SparkyFitness, Emby,
|
||||
n8n, Plex, EE/BUSL részek, redis 7.4) — mit kell az ÁSZF-nek mondania róluk.
|
||||
3. A root hozzáférés (6. pont) leírása elég-e, és milyen feltételhez kell kötni.
|
||||
4. Felelősségkorlátozás adatvesztésre, különösen ha az Előfizető elveszíti a helyreállító kódot.
|
||||
5. A szerződés megszűnésekor az adatok sorsa — a rendszerben ez még nincs megtervezve.
|
||||
6. A zárt teszt résztvevőire (tesztelői megállapodás, R-809 / R-11) milyen feltételek vonatkoznak.
|
||||
|
||||
**Ahol a vázlat találgatott:**
|
||||
|
||||
1. Hogy az Előfizető fogyasztó — a vázlat háztartást feltételez a projekt leírása alapján.
|
||||
2. A két telepítési mód („appliance" / „byo") üzleti jelentése (bérelt vagy saját gép) — sehol
|
||||
nincs leírva.
|
||||
3. Az „önálló modell" létezése — csak a GYIK állítja.
|
||||
4. A 3. pont szolgáltatáslistája a műszaki dokumentumokból készült; hogy ebből mi a fizetős
|
||||
csomag része, üzleti döntés.
|
||||
5. Hogy a domain díja az előfizetés része — tervezési döntés (01 §7), nem árlista.
|
||||
6. Az ügyfél által látható üzemeltetői napló — tervezési állítás, megvalósítását nem ellenőriztük.
|
||||
@@ -0,0 +1,49 @@
|
||||
# DRAFT — Impresszum (felhom.eu)
|
||||
|
||||
> **English summary.** First draft of the felhom.eu imprint, written 2026-10-08 for R-813. NOT
|
||||
> published, NOT legal advice, pending lawyer review (R-802). Every fact about the operator is a
|
||||
> placeholder; the only facts filled in are the hosting arrangements, each cited. The list at the end
|
||||
> says what the lawyer must check and where the draft guessed.
|
||||
|
||||
---
|
||||
|
||||
## A weboldal üzemeltetője
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Név | [[CÉGNÉV]] |
|
||||
| Cégforma | [[CÉGFORMA — pl. egyéni vállalkozó / Kft.]] |
|
||||
| Székhely | [[SZÉKHELY]] |
|
||||
| Postacím | [[POSTACÍM]] |
|
||||
| Cégjegyzékszám / egyéni vállalkozói nyilvántartási szám | [[CÉGJEGYZÉKSZÁM]] |
|
||||
| Nyilvántartó hatóság / cégbíróság | [[NYILVÁNTARTÓ]] |
|
||||
| Adószám | [[ADÓSZÁM]] |
|
||||
| Közösségi adószám | [[KÖZÖSSÉGI ADÓSZÁM — ha van]] |
|
||||
| Képviselő | [[KÉPVISELŐ NEVE]] |
|
||||
| E-mail | [[KAPCSOLATI E-MAIL]] |
|
||||
| Telefon | [[TELEFON]] |
|
||||
| Kamarai tagság | [[KAMARA — ha van]] |
|
||||
|
||||
## Tárhely
|
||||
|
||||
A weboldalt a Felhom **saját szervere** szolgálja ki; külső tárhelyszolgáltató nincs. A szerver
|
||||
helye: [[ORSZÁG / CÍM — ELLENŐRIZNI]].
|
||||
<!-- source: README.md:65-73 (k3s single node; felhom-system: felhom-webpage nginx + git-sync); manifests/webpage.yaml:433 (host felhom.eu) -->
|
||||
|
||||
A `felhom.eu` domain névszerverét a **Cloudflare** biztosítja (csak DNS).
|
||||
[[CLOUDFLARE CÉGADATAI — ELLENŐRIZNI, ha az ügyvéd szerint fel kell tüntetni]]
|
||||
<!-- source: README.md:57, :108 ("Cloudflare DNS (free plan), DNS only mode") -->
|
||||
|
||||
## Kapcsolódó dokumentumok
|
||||
|
||||
- Általános Szerződési Feltételek — [[LINK]]
|
||||
- Adatkezelési tájékoztató — [[LINK]]
|
||||
|
||||
## Az ügyvédnek ellenőrizni (R-802) — és ahol a vázlat találgatott
|
||||
|
||||
1. Mely adatok kötelezők egy szolgáltatást kínáló weboldal impresszumában (Ekertv. szerinti
|
||||
tájékoztatás) — a táblázat a szokásos mezőket sorolja, nem jogi listát.
|
||||
2. Kell-e saját szerver esetén tárhelyszolgáltatót megnevezni, és kell-e a Cloudflare-t (csak DNS).
|
||||
3. **Találgatás:** hogy nincs külső tárhelyszolgáltató — a README és a manifestek szerint a webszerver
|
||||
a Felhom saját k3s fürtjén fut; élőben nem mértük.
|
||||
4. **Találgatás:** a cégforma — a dokumentumok semmit nem mondanak a vállalkozásról.
|
||||
@@ -0,0 +1,51 @@
|
||||
# DRAFT — A kapcsolatfelvételi űrlap hozzájárulási szövege
|
||||
|
||||
> **English summary.** A proposed replacement for the contact form's consent checkbox text
|
||||
> (`website/kapcsolat.html:122-128`), written 2026-10-08 for R-813. NOT applied to the website,
|
||||
> NOT legal advice, pending lawyer review (R-802). The current text names no controller, no
|
||||
> retention, no rights, links nowhere, and says the data is not given to third parties although
|
||||
> three processors (Resend, Cloudflare Email Routing, Gmail) carry the message. The proposal links to
|
||||
> the privacy notice, which must be published first.
|
||||
|
||||
## A mai szöveg
|
||||
|
||||
<!-- source: website/kapcsolat.html:122-128 -->
|
||||
|
||||
> Elfogadom, hogy az űrlapon megadott adataimat a Felhom.eu a megkeresésem megválaszolásához
|
||||
> felhasználja. Az adatokat harmadik félnek nem adjuk ki.
|
||||
|
||||
**Mi a gond vele:**
|
||||
|
||||
1. Nem nevezi meg az adatkezelőt (a „Felhom.eu" egy domain, nem jogi személy).
|
||||
2. Nem mondja meg, meddig őrizzük az adatokat, és milyen jogai vannak a kitöltőnek.
|
||||
3. Nem hivatkozik adatkezelési tájékoztatóra — ilyen ma nincs is (R-813).
|
||||
4. **„Harmadik félnek nem adjuk ki"** — az üzenetet a Resend (levélküldés), a Cloudflare Email
|
||||
Routing (továbbítás) és a Google Gmail (postafiók) adatfeldolgozóként kezeli.
|
||||
<!-- source: manifests/contact-mailer.yaml:1-2, :66-69; README.md:133-135, :146-151 -->
|
||||
5. A hibaüzenet „adatkezelési hozzájárulás"-t említ, de a jelölőnégyzet egyben az üzenet
|
||||
elküldésének feltétele — hogy a hozzájárulás itt a helyes jogalap-e, az ügyvéd dönti el.
|
||||
<!-- source: website/kapcsolat.html:123 (required), :128 (privacyError text) -->
|
||||
|
||||
## Javasolt szöveg
|
||||
|
||||
> Elolvastam az [Adatkezelési tájékoztatót](/adatkezeles), és hozzájárulok, hogy a(z)
|
||||
> [[CÉGNÉV]] a megadott nevemet, e-mail-címemet, üzenetemet és csatolt fájljaimat a megkeresésem
|
||||
> megválaszolására kezelje. Az üzenetet levélküldő és levelezési szolgáltatók (adatfeldolgozók)
|
||||
> továbbítják; a részleteket, a megőrzés idejét és a jogaimat a tájékoztató írja le. A
|
||||
> hozzájárulásomat bármikor visszavonhatom a(z) [[ADATVÉDELMI E-MAIL]] címen.
|
||||
|
||||
**A link:** `/adatkezeles` — [[ELLENŐRIZNI: a végleges útvonal]]. A weboldal oldalai ma
|
||||
kiterjesztés nélküli útvonalon érhetők el (pl. `kapcsolat.html` → `/kapcsolat`), ezért a javasolt
|
||||
fájlnév `website/adatkezeles.html`. A linket érdemes új lapon nyitni (`target="_blank" rel="noopener"`),
|
||||
hogy a kitöltött űrlap ne vesszen el.
|
||||
<!-- source: README.md:39 (kapcsolat.html served at /kapcsolat) -->
|
||||
|
||||
**A hibaüzenet** maradhat: „Az adatkezelési hozzájárulás szükséges." — vagy, ha az ügyvéd szerint a
|
||||
jogalap nem hozzájárulás, akkor: „Kérlek, jelezd, hogy elolvastad az adatkezelési tájékoztatót."
|
||||
|
||||
## Közzététel előtt (nem most)
|
||||
|
||||
1. Az ügyvéd jóváhagyja a szöveget és a jogalapot.
|
||||
2. Az adatkezelési tájékoztató megjelenik a megadott útvonalon.
|
||||
3. A szöveg kicserélése a `website/kapcsolat.html` 122–128. sorában, és a lábléc linkjei minden oldalon.
|
||||
4. A placeholderek kitöltése.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Legal drafts — NOT published, NOT legal advice
|
||||
|
||||
**Status: first drafts, written 2026-10-08 by Claude Code on the operator's request (Part E of that
|
||||
night's brief). Pending lawyer review. Nothing here is on the website.**
|
||||
|
||||
These files are working drafts for register rows **R-813** (the website collects personal data but
|
||||
publishes no privacy notice, no terms and no imprint — owner: operator) and **R-802** (the lawyer's
|
||||
review before the first paying customer — owner: operator). The wider set of business papers
|
||||
(customer contract, data-processing agreement, billing) is the intention **R-809** in
|
||||
`documentation/backlog/ROADMAP.md`.
|
||||
|
||||
| File | What it is |
|
||||
|---|---|
|
||||
| `DRAFT-aszf.md` | Általános Szerződési Feltételek — a structured skeleton; business terms are placeholders |
|
||||
| `DRAFT-adatkezelesi-tajekoztato.md` | Adatkezelési tájékoztató — built from the architecture documents and the code, each line cited |
|
||||
| `DRAFT-impresszum.md` | Impresszum — placeholders only |
|
||||
| `DRAFT-kapcsolat-hozzajarulas.md` | A proposed replacement for the contact form's consent text, and the link it should carry |
|
||||
|
||||
## Rules for these drafts
|
||||
|
||||
- **Not legal advice.** Written by an AI assistant from the system's own documents. A lawyer decides
|
||||
what is legally required, what legal basis applies, and what the final text says.
|
||||
- **Every fact the operator must supply is a visible placeholder** like `[[CÉGNÉV]]`. Nothing is
|
||||
invented. `[[ELLENŐRIZNI]]` marks a fact the code and the documents do not state.
|
||||
- **The privacy notice is true to the system, not to a template.** Each line carries an HTML comment
|
||||
`<!-- source: path:line -->`. If the system changes (a new processor, a new retention), the notice
|
||||
must change with it.
|
||||
- **Nothing is published until the operator says so.** Publishing means a website change (`website/`
|
||||
is served from `main` by git-sync), a link from every page footer, and the contact form's consent
|
||||
text replaced — none of which is done here.
|
||||
- Each draft ends with the list of points the lawyer must check and every point where the draft had
|
||||
to guess.
|
||||
Reference in New Issue
Block a user