hub v0.119.0 — English households get English words for their codes (R-597); R-596/R-598 closed
gates / gates (push) Successful in 24s

The setup code and the owner passphrase now follow the household's language,
one word longer in English so the entropy never drops (setup 3 hu / 4 en,
passphrase 5 hu / 6 en). List and count are chosen together so a caller cannot
pair an English list with a Hungarian count. Hungarian is byte-unchanged.

Three claims in the row were wrong and are recorded as such:
  - the RECOVERY CODE is minted by felhom-agent from the EFF list and has
    always been English; the hub does not own it and no row was added.
  - no claim mail states a word count; the only count wording was the bind
    page's passphrase hint, whose English half is now count-free.
  - the proposed phone-safe filter removes 68% of the list (5270 of 7772
    words) and was measured, then declined, with the reason in source.

Also: guide_quote_gate binds the English volunteer guide's three quoted
messages to the controller's English bundle — nothing did, so the guide would
have gone on quoting Hungarian after the fix. Seven decoys, all convicting,
including the name-for-fact one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-21 07:56:56 +02:00
parent a499327236
commit e02bc03819
18 changed files with 8859 additions and 50 deletions
+16 -5
View File
@@ -708,8 +708,14 @@ func (s *Server) handleConfigCreate(w http.ResponseWriter, r *http.Request) {
return
}
// Generate credentials
retrievalPassword, err := configgen.RandomPassphrase(5)
// Generate credentials.
//
// R-597: the Owner passphrase follows the language the operator is choosing ON THIS FORM, not
// the one in the store — there is no stored customer yet, and CustomerLanguage would answer
// Hungarian for every English household created here. The store's own createdLanguage applies
// the same default to an absent or unknown value, so the two agree.
createLang := i18n.Normalize(strings.TrimSpace(r.FormValue("language")))
retrievalPassword, err := configgen.RandomPassphraseFor(createLang, configgen.UseOwnerPassphrase)
if err != nil {
http.Error(w, "Internal error", http.StatusInternalServerError)
return
@@ -1032,7 +1038,10 @@ func (s *Server) handleConfigPreview(w http.ResponseWriter, r *http.Request, cus
// handleConfigRegenPassword regenerates the retrieval password.
func (s *Server) handleConfigRegenPassword(w http.ResponseWriter, r *http.Request, customerID string) {
newPassword, err := configgen.RandomPassphrase(5)
// R-597: a regenerated Owner passphrase follows the household's language exactly as their mails
// do — CustomerLanguage's order is last-reported → created-with → Hungarian, so a household that
// switched their own dashboard to English gets English words on the next regeneration.
newPassword, err := configgen.RandomPassphraseFor(s.store.CustomerLanguage(customerID), configgen.UseOwnerPassphrase)
if err != nil {
http.Error(w, "Internal error", http.StatusInternalServerError)
return
@@ -1445,8 +1454,10 @@ func (s *Server) handleCreateConfigFromReport(w http.ResponseWriter, r *http.Req
name = customer.CustomerName
}
// Generate credentials
retrievalPassword, _ := configgen.RandomPassphrase(5)
// Generate credentials. The config below states Language: i18n.Default for this path (no
// operator is present), so the passphrase is drawn for the SAME language — reading it from the
// one line that decides it, rather than restating the choice.
retrievalPassword, _ := configgen.RandomPassphraseFor(i18n.Default, configgen.UseOwnerPassphrase)
apiKey, _ := configgen.RandomHex(32)
cfg := &store.CustomerConfig{