hub 0.164.0 (unreleased): bind mail true when no box exists (W3), tries left on a wrong try (W4), use the newer mail hint (W5)
gates / gates (push) Failing after 14m55s

This commit is contained in:
2026-10-21 22:18:38 +02:00
parent 3c620f8d2e
commit f90e22293a
7 changed files with 74 additions and 8 deletions
+9
View File
@@ -7,6 +7,15 @@
from the host's newest swap event (`store.LatestHostSwapEvent`, by `details.host_id`), so repeats and a hub restart from the host's newest swap event (`store.LatestHostSwapEvent`, by `details.host_id`), so repeats and a hub restart
stay quiet. An older agent (no numbers) is not evaluated. Both in `notify.operatorOnlyEvents`. Tests stay quiet. An older agent (no numbers) is not evaluated. Both in `notify.operatorOnlyEvents`. Tests
`TestR1192_HostSwapShortIsRaisedOnceAndClearedOnce`, `TestR1192_HostSwapEventsAreOperatorOnly`. `TestR1192_HostSwapShortIsRaisedOnceAndClearedOnce`, `TestR1192_HostSwapEventsAreOperatorOnly`.
- **The rehearsal's first-hour words (W3, W4, W5).** W3 — the bind mail no longer says „Elkészült a Felhom dobozod…"
when no box exists (it is also sent when the operator deletes a dead box): „Ezzel a hivatkozással kötheted össze a
Felhom dobozodat a fiókoddal, amikor a telepítés után a doboz képernyőjén megjelenik a párosító kód. Nincs szükség
bejelentkezésre:" (EN „With this link you can link your Felhom box to your account once the box shows its pairing
code on its screen after the installation. No sign-in needed:"); mail goldens regenerated on purpose. W4 — a wrong
try says „Még N próbálkozásod van, utána a hivatkozás zárolódik." (EN „Tries left before the link locks: N."); the
same for a wrong code and a wrong passphrase (no oracle). W5 — a used or expired link says „Ha van újabb leveled a
Felhomtól, abban lévő linket használd." (EN „If you have a newer mail from Felhom, use the link in that one.") above
„Új linket kérek". Tests `TestW4_*`, `TestW5_*` (W4 red-proved).
## v0.163.0 — a rebuilt box gets the household's sealed key package back (R-1176); one run per daily job on clock-change days (R-1183); abandoning one set-aside copy keeps the other copies' packages (R-1182); the „new link" page tells a household its old box is still linked (R-1203) (2026-10-21) ## v0.163.0 — a rebuilt box gets the household's sealed key package back (R-1176); one run per daily job on clock-change days (R-1183); abandoning one set-aside copy keeps the other copies' packages (R-1182); the „new link" page tells a household its old box is still linked (R-1203) (2026-10-21)
+4 -1
View File
@@ -57,13 +57,15 @@
"mail.claim.claim.subject": "[Felhom] Your Felhom server is up — setup code", "mail.claim.claim.subject": "[Felhom] Your Felhom server is up — setup code",
"mail.claim.claim.body": "Dear Customer,\n\nYour Felhom server is up. To use the dashboard for the first time, enter the\nsetup code below, then choose your own password:\n\nSetup code: %s\n\nThe code is valid for 72 hours and can be used once. You can reach the\ndashboard here:\n\n%s\n\nIf it did not reach you in time, the dashboard's \"Request a new code\" button\ngets you a fresh one — it always arrives at this e-mail address.\n\nBest regards,\nFelhom.eu", "mail.claim.claim.body": "Dear Customer,\n\nYour Felhom server is up. To use the dashboard for the first time, enter the\nsetup code below, then choose your own password:\n\nSetup code: %s\n\nThe code is valid for 72 hours and can be used once. You can reach the\ndashboard here:\n\n%s\n\nIf it did not reach you in time, the dashboard's \"Request a new code\" button\ngets you a fresh one — it always arrives at this e-mail address.\n\nBest regards,\nFelhom.eu",
"mail.selfbind.subject": "[Felhom] Link your Felhom box to your account", "mail.selfbind.subject": "[Felhom] Link your Felhom box to your account",
"mail.selfbind.body": "Dear Customer,\n\nYour Felhom box is ready and waiting to be linked. You can link it to your\naccount yourself using the link below — no sign-in needed:\n\n%s\n\nAfter you open the link you will need to enter two things:\n\n 1. The pairing code, shown on the box's own screen (the monitor).\n 2. Your owner passphrase (the 5-word phrase) that you received from your\n Felhom operator — in person or by telephone, never by e-mail. It proves\n the account is yours.\n\nThe link is valid for 7 days. For safety it locks after 5 failed attempts —\nif that happens, contact support.\n\nIf you did not ask for this, ignore this e-mail.\n\nBest regards,\nFelhom.eu", "mail.selfbind.body": "Dear Customer,\n\nWith this link you can link your Felhom box to your account once the box shows\nits pairing code on its screen after the installation. No sign-in needed:\n\n%s\n\nAfter you open the link you will need to enter two things:\n\n 1. The pairing code, shown on the box's own screen (the monitor).\n 2. Your owner passphrase (the 5-word phrase) that you received from your\n Felhom operator — in person or by telephone, never by e-mail. It proves\n the account is yours.\n\nThe link is valid for 7 days. For safety it locks after 5 failed attempts —\nif that happens, contact support.\n\nIf you did not ask for this, ignore this e-mail.\n\nBest regards,\nFelhom.eu",
"mail.kernel.subject": "[Felhom] Your Felhom box restarts tonight", "mail.kernel.subject": "[Felhom] Your Felhom box restarts tonight",
"mail.kernel.body": "Hi,\n\nTonight your Felhom box restarts for a security update. Your apps will be away for a few\nminutes, then come back by themselves. You do not need to do anything.\n\nIf the box is not back by morning: unplug its power cable, wait 10 seconds, and plug it back\nin. The box then starts its earlier, proven system by itself.\n\nIf that does not help, reply to this e-mail and we will help.\n\nBest,\nFelhom.eu", "mail.kernel.body": "Hi,\n\nTonight your Felhom box restarts for a security update. Your apps will be away for a few\nminutes, then come back by themselves. You do not need to do anything.\n\nIf the box is not back by morning: unplug its power cable, wait 10 seconds, and plug it back\nin. The box then starts its earlier, proven system by itself.\n\nIf that does not help, reply to this e-mail and we will help.\n\nBest,\nFelhom.eu",
"bind.title": "Felhom — Link your box", "bind.title": "Felhom — Link your box",
"bind.heading": "Link your Felhom <span>box</span>", "bind.heading": "Link your Felhom <span>box</span>",
"bind.lead": "Link the Felhom box you have just installed to your account. Enter the pairing code shown on the box's screen, and your owner passphrase.", "bind.lead": "Link the Felhom box you have just installed to your account. Enter the pairing code shown on the box's screen, and your owner passphrase.",
"bind.failed": "Those details are not right. Check the pairing code and the owner passphrase, then try again.", "bind.failed": "Those details are not right. Check the pairing code and the owner passphrase, then try again.",
"bind.attempts_left.pre": "Tries left before the link locks:",
"bind.attempts_left.post": ".",
"bind.label.pairing": "Pairing code", "bind.label.pairing": "Pairing code",
"bind.placeholder.pairing": "ABC-234", "bind.placeholder.pairing": "ABC-234",
"bind.hint.pairing": "It appears on the box's monitor, after the install.", "bind.hint.pairing": "It appears on the box's monitor, after the install.",
@@ -89,6 +91,7 @@
"mail.event.app_stopped_unhealthy": "%s: the app was stopped because it kept crashing", "mail.event.app_stopped_unhealthy": "%s: the app was stopped because it kept crashing",
"bind.resend.hint": "If this was your link and your box is not linked yet, we send a fresh link to the e-mail address you gave Felhom.", "bind.resend.hint": "If this was your link and your box is not linked yet, we send a fresh link to the e-mail address you gave Felhom.",
"bind.resend.button": "Send me a new link", "bind.resend.button": "Send me a new link",
"bind.resend.newer": "If you have a newer mail from Felhom, use the link in that one.",
"bind.resent.lead": "Done.", "bind.resent.lead": "Done.",
"bind.resent.body": "If this was a real link and your box is not linked yet, a new e-mail reaches your registered address within a few minutes. If it does not, contact support.", "bind.resent.body": "If this was a real link and your box is not linked yet, a new e-mail reaches your registered address within a few minutes. If it does not, contact support.",
"bind.oldbox.lead": "Your old box is still linked to your account.", "bind.oldbox.lead": "Your old box is still linked to your account.",
+4 -1
View File
@@ -57,13 +57,15 @@
"mail.claim.claim.subject": "[Felhom] Elindult a Felhom szervered — beállító kód", "mail.claim.claim.subject": "[Felhom] Elindult a Felhom szervered — beállító kód",
"mail.claim.claim.body": "Kedves Ügyfél!\n\nElindult a Felhom szervered. A vezérlőpult első használatához add meg az\nalábbi beállító kódot, majd válassz saját jelszót:\n\nBeállító kód: %s\n\nA kód 72 óráig érvényes, és egyszer használható fel. A vezérlőpultot itt éred el:\n\n%s\n\nHa nem kaptad volna meg időben, a vezérlőpult \"Új kód kérése\" gombjával\nkérhetsz frisset — az mindig erre az e-mail címre érkezik.\n\nÜdvözlettel,\nFelhom.eu", "mail.claim.claim.body": "Kedves Ügyfél!\n\nElindult a Felhom szervered. A vezérlőpult első használatához add meg az\nalábbi beállító kódot, majd válassz saját jelszót:\n\nBeállító kód: %s\n\nA kód 72 óráig érvényes, és egyszer használható fel. A vezérlőpultot itt éred el:\n\n%s\n\nHa nem kaptad volna meg időben, a vezérlőpult \"Új kód kérése\" gombjával\nkérhetsz frisset — az mindig erre az e-mail címre érkezik.\n\nÜdvözlettel,\nFelhom.eu",
"mail.selfbind.subject": "[Felhom] Kösd össze a Felhom dobozodat", "mail.selfbind.subject": "[Felhom] Kösd össze a Felhom dobozodat",
"mail.selfbind.body": "Kedves Ügyfél!\n\nElkészült a Felhom dobozod, és készen áll az összekötésre. Az alábbi hivatkozáson\ntudod te magad összekötni a fiókoddal — nincs szükség bejelentkezésre:\n\n%s\n\nA hivatkozás megnyitása után két adatot kell megadnod:\n\n 1. A párosító kódot, amely a doboz képernyőjén (a monitoron) látható.\n 2. A tulajdonosi jelmondatodat (az 5 szóból álló kifejezést), amelyet a\n Felhom üzemeltetőjétől kaptál — személyesen vagy telefonon, e-mailben\n soha. Ez igazolja, hogy a fiók a tiéd.\n\nA hivatkozás 7 napig érvényes. Biztonsági okból 5 sikertelen próbálkozás után\nzárolódik — ilyenkor vedd fel a kapcsolatot az ügyfélszolgálattal.\n\nHa nem te kérted ezt, hagyd figyelmen kívül ezt az e-mailt.\n\nÜdvözlettel,\nFelhom.eu", "mail.selfbind.body": "Kedves Ügyfél!\n\nEzzel a hivatkozással kötheted össze a Felhom dobozodat a fiókoddal, amikor a\ntelepítés után a doboz képernyőjén megjelenik a párosító kód. Nincs szükség\nbejelentkezésre:\n\n%s\n\nA hivatkozás megnyitása után két adatot kell megadnod:\n\n 1. A párosító kódot, amely a doboz képernyőjén (a monitoron) látható.\n 2. A tulajdonosi jelmondatodat (az 5 szóból álló kifejezést), amelyet a\n Felhom üzemeltetőjétől kaptál — személyesen vagy telefonon, e-mailben\n soha. Ez igazolja, hogy a fiók a tiéd.\n\nA hivatkozás 7 napig érvényes. Biztonsági okból 5 sikertelen próbálkozás után\nzárolódik — ilyenkor vedd fel a kapcsolatot az ügyfélszolgálattal.\n\nHa nem te kérted ezt, hagyd figyelmen kívül ezt az e-mailt.\n\nÜdvözlettel,\nFelhom.eu",
"mail.kernel.subject": "[Felhom] Ma éjjel újraindul a Felhom dobozod", "mail.kernel.subject": "[Felhom] Ma éjjel újraindul a Felhom dobozod",
"mail.kernel.body": "Szia!\n\nMa éjjel a Felhom dobozod egy biztonsági frissítés miatt újraindul. Ilyenkor az alkalmazásaid\nnéhány percig nem érhetők el, utána maguktól visszajönnek. Neked nem kell semmit tenned.\n\nHa reggelre a doboz mégsem érhető el: húzd ki a tápkábelét, várj 10 másodpercet, és dugd\nvissza. A doboz ilyenkor magától a korábbi, bevált rendszerrel indul el.\n\nHa ez sem segít, válaszolj erre a levélre, és segítünk.\n\nÜdv,\nFelhom.eu", "mail.kernel.body": "Szia!\n\nMa éjjel a Felhom dobozod egy biztonsági frissítés miatt újraindul. Ilyenkor az alkalmazásaid\nnéhány percig nem érhetők el, utána maguktól visszajönnek. Neked nem kell semmit tenned.\n\nHa reggelre a doboz mégsem érhető el: húzd ki a tápkábelét, várj 10 másodpercet, és dugd\nvissza. A doboz ilyenkor magától a korábbi, bevált rendszerrel indul el.\n\nHa ez sem segít, válaszolj erre a levélre, és segítünk.\n\nÜdv,\nFelhom.eu",
"bind.title": "Felhom — Doboz összekötése", "bind.title": "Felhom — Doboz összekötése",
"bind.heading": "Felhom <span>doboz</span> összekötése", "bind.heading": "Felhom <span>doboz</span> összekötése",
"bind.lead": "Kösd össze a most telepített Felhom dobozodat a fiókoddal. Add meg a doboz képernyőjén látható párosító kódot és a tulajdonosi jelmondatodat.", "bind.lead": "Kösd össze a most telepített Felhom dobozodat a fiókoddal. Add meg a doboz képernyőjén látható párosító kódot és a tulajdonosi jelmondatodat.",
"bind.failed": "A megadott adatok nem megfelelőek. Ellenőrizd a párosító kódot és a tulajdonosi jelmondatot, majd próbáld újra.", "bind.failed": "A megadott adatok nem megfelelőek. Ellenőrizd a párosító kódot és a tulajdonosi jelmondatot, majd próbáld újra.",
"bind.attempts_left.pre": "Még",
"bind.attempts_left.post": " próbálkozásod van, utána a hivatkozás zárolódik.",
"bind.label.pairing": "Párosító kód", "bind.label.pairing": "Párosító kód",
"bind.placeholder.pairing": "ABC-234", "bind.placeholder.pairing": "ABC-234",
"bind.hint.pairing": "A doboz monitorán jelenik meg, a telepítés után.", "bind.hint.pairing": "A doboz monitorán jelenik meg, a telepítés után.",
@@ -89,6 +91,7 @@
"mail.event.app_stopped_unhealthy": "%s: az alkalmazást leállítottuk, mert újra és újra összeomlott", "mail.event.app_stopped_unhealthy": "%s: az alkalmazást leállítottuk, mert újra és újra összeomlott",
"bind.resend.hint": "Ha ez a te linked volt, és a dobozod még nincs összekötve, új linket küldünk arra az e-mail címre, amelyet a Felhomnál megadtál.", "bind.resend.hint": "Ha ez a te linked volt, és a dobozod még nincs összekötve, új linket küldünk arra az e-mail címre, amelyet a Felhomnál megadtál.",
"bind.resend.button": "Új linket kérek", "bind.resend.button": "Új linket kérek",
"bind.resend.newer": "Ha van újabb leveled a Felhomtól, abban lévő linket használd.",
"bind.resent.lead": "Kész.", "bind.resent.lead": "Kész.",
"bind.resent.body": "Ha ez egy valódi hivatkozás volt, és a dobozod még nincs összekötve, néhány percen belül új e-mailt kapsz a regisztrált címedre. Ha nem jön, szólj az ügyfélszolgálatnak.", "bind.resent.body": "Ha ez egy valódi hivatkozás volt, és a dobozod még nincs összekötve, néhány percen belül új e-mailt kapsz a regisztrált címedre. Ha nem jön, szólj az ügyfélszolgálatnak.",
"bind.oldbox.lead": "A régi dobozod még össze van kötve a fiókoddal.", "bind.oldbox.lead": "A régi dobozod még össze van kötve a fiókoddal.",
+2 -2
View File
@@ -2,8 +2,8 @@ SUBJECT: [Felhom] Link your Felhom box to your account
--- ---
Dear Customer, Dear Customer,
Your Felhom box is ready and waiting to be linked. You can link it to your With this link you can link your Felhom box to your account once the box shows
account yourself using the link below — no sign-in needed: its pairing code on its screen after the installation. No sign-in needed:
https://hub.felhom.eu/bind/FIXTURETOKEN https://hub.felhom.eu/bind/FIXTURETOKEN
+3 -2
View File
@@ -2,8 +2,9 @@ SUBJECT: [Felhom] Kösd össze a Felhom dobozodat
--- ---
Kedves Ügyfél! Kedves Ügyfél!
Elkészült a Felhom dobozod, és készen áll az összekötésre. Az alábbi hivatkozáson Ezzel a hivatkozással kötheted össze a Felhom dobozodat a fiókoddal, amikor a
tudod te magad összekötni a fiókoddal — nincs szükség bejelentkezésre: telepítés után a doboz képernyőjén megjelenik a párosító kód. Nincs szükség
bejelentkezésre:
https://hub.felhom.eu/bind/FIXTURETOKEN https://hub.felhom.eu/bind/FIXTURETOKEN
+11 -2
View File
@@ -102,6 +102,9 @@ type bindPageData struct {
State string // "form" | "success" | "expired" | "consumed" | "locked" | "resent" | "oldbox" (R-1203) State string // "form" | "success" | "expired" | "consumed" | "locked" | "resent" | "oldbox" (R-1203)
Token string // echoed into the form action (the capability itself; already in the URL) Token string // echoed into the form action (the capability itself; already in the URL)
Failed bool // generic factor-check failure (form state only) Failed bool // generic factor-check failure (form state only)
// AttemptsLeft (W4, night 2026-10-21) is how many tries the link has before it locks — said beside the failure,
// so the household is not surprised by the lock (which only support can open). 0 = not shown.
AttemptsLeft int
// Lang is the language to render in. It is the CUSTOMER'S CREATION-TIME language and nothing // Lang is the language to render in. It is the CUSTOMER'S CREATION-TIME language and nothing
// else: no box has reported yet at bind time, and this page deliberately offers no switch — // else: no box has reported yet at bind time, and this page deliberately offers no switch —
// the person opening it is a stranger holding a capability URL, exactly like the guest share // the person opening it is a stranger holding a capability URL, exactly like the guest share
@@ -249,7 +252,11 @@ func (s *Server) handleBind(w http.ResponseWriter, r *http.Request) {
s.renderBind(w, http.StatusOK, bindPageData{State: "locked", Lang: lang}) s.renderBind(w, http.StatusOK, bindPageData{State: "locked", Lang: lang})
return return
} }
s.renderBind(w, http.StatusOK, bindPageData{State: "form", Token: token, Failed: true, Lang: lang}) left := store.SelfBindMaxAttempts - attempts
if left < 0 {
left = 0
}
s.renderBind(w, http.StatusOK, bindPageData{State: "form", Token: token, Failed: true, AttemptsLeft: left, Lang: lang})
return return
} }
@@ -318,7 +325,7 @@ const bindPageHTML = `<!DOCTYPE html>
<h1>{{T "bind.heading"}}</h1> <h1>{{T "bind.heading"}}</h1>
{{if eq .State "form"}} {{if eq .State "form"}}
<p class="lead">{{T "bind.lead"}}</p> <p class="lead">{{T "bind.lead"}}</p>
{{if .Failed}}<div class="banner">{{T "bind.failed"}}</div>{{end}} {{if .Failed}}<div class="banner">{{T "bind.failed"}}{{if .AttemptsLeft}} {{T "bind.attempts_left.pre"}} {{.AttemptsLeft}}{{T "bind.attempts_left.post"}}{{end}}</div>{{end}}
<form method="POST" action="/bind/{{.Token}}"> <form method="POST" action="/bind/{{.Token}}">
<label for="pairing_code">{{T "bind.label.pairing"}}</label> <label for="pairing_code">{{T "bind.label.pairing"}}</label>
<input class="code" type="text" id="pairing_code" name="pairing_code" autocomplete="off" autocapitalize="characters" spellcheck="false" required autofocus placeholder="{{T "bind.placeholder.pairing"}}"> <input class="code" type="text" id="pairing_code" name="pairing_code" autocomplete="off" autocapitalize="characters" spellcheck="false" required autofocus placeholder="{{T "bind.placeholder.pairing"}}">
@@ -335,6 +342,7 @@ const bindPageHTML = `<!DOCTYPE html>
{{else if eq .State "consumed"}} {{else if eq .State "consumed"}}
<p class="lead">{{T "bind.consumed.lead"}}</p> <p class="lead">{{T "bind.consumed.lead"}}</p>
<p>{{T "bind.consumed.body"}}</p> <p>{{T "bind.consumed.body"}}</p>
<p class="hint">{{T "bind.resend.newer"}}</p>
<form method="POST" action="/bind/{{.Token}}/resend"><p class="hint">{{T "bind.resend.hint"}}</p><button type="submit">{{T "bind.resend.button"}}</button></form> <form method="POST" action="/bind/{{.Token}}/resend"><p class="hint">{{T "bind.resend.hint"}}</p><button type="submit">{{T "bind.resend.button"}}</button></form>
{{else if eq .State "oldbox"}} {{else if eq .State "oldbox"}}
<p class="lead">{{T "bind.oldbox.lead"}}</p> <p class="lead">{{T "bind.oldbox.lead"}}</p>
@@ -348,6 +356,7 @@ const bindPageHTML = `<!DOCTYPE html>
{{else}} {{else}}
<p class="lead">{{T "bind.invalid.lead"}}</p> <p class="lead">{{T "bind.invalid.lead"}}</p>
{{if .Token}}<p>{{T "bind.invalid.body_resend"}}</p> {{if .Token}}<p>{{T "bind.invalid.body_resend"}}</p>
<p class="hint">{{T "bind.resend.newer"}}</p>
<form method="POST" action="/bind/{{.Token}}/resend"><p class="hint">{{T "bind.resend.hint"}}</p><button type="submit">{{T "bind.resend.button"}}</button></form> <form method="POST" action="/bind/{{.Token}}/resend"><p class="hint">{{T "bind.resend.hint"}}</p><button type="submit">{{T "bind.resend.button"}}</button></form>
{{else}}<p>{{T "bind.invalid.body"}}</p>{{end}} {{else}}<p>{{T "bind.invalid.body"}}</p>{{end}}
{{end}} {{end}}
+41
View File
@@ -0,0 +1,41 @@
package web
import (
"fmt"
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)
// W4 (rehearsal word table, night 2026-10-21): a wrong try says how many tries are left before the link locks — the
// fifth locks it and only support can open it. The count is the same for a wrong code and a wrong passphrase, so the
// no-oracle rule (TestSelfBind_B_NoOracle) holds.
// RED-PROOF: with AttemptsLeft not set on the failure render the „Még N" sentence is missing → fails at attempt 1.
func TestW4_AWrongTrySaysHowManyTriesAreLeft(t *testing.T) {
s, st := newTestServer(t)
selfBindSetup(t, st, "acme", testCode)
token := mintLink(t, st, "acme", selfBindTTL)
for i := 1; i < store.SelfBindMaxAttempts; i++ {
body := bindPOST(t, s, token, "ZZZ999", "definitely wrong words indeed").Body.String()
want := fmt.Sprintf("Még %d próbálkozásod van", store.SelfBindMaxAttempts-i)
if !strings.Contains(body, want) {
t.Fatalf("attempt %d: the page must say %q", i, want)
}
}
}
// W5: on a used or expired link the page says to use the newer mail before „Új linket kérek" (a new link kills the
// one the household may be about to use — TestSelfBind_C4_SingleActive).
func TestW5_AnOldLinkPointsToTheNewerMail(t *testing.T) {
s, st := newTestServer(t)
selfBindSetup(t, st, "acme", testCode)
token := mintLink(t, st, "acme", -1) // already expired
body := bindGET(t, s, token).Body.String()
if !strings.Contains(body, "újabb leveled") || !strings.Contains(body, "Új linket kérek") {
t.Fatalf("an expired link must point to the newer mail above the button: %q", body)
}
if strings.Index(body, "újabb leveled") > strings.Index(body, "Új linket kérek") {
t.Fatal("the newer-mail hint must come BEFORE the button")
}
}