aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-15 14:25:00 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-15 14:25:00 +0200
commit28c77b7648b2a44a18be64ec44344d91cccfd742 (patch)
tree72e0398306639817922cf292e4da6725182c23dd /packages/meshbay-hub/src
parentac4c3302783a3137e55dcede54c466bdf8aa55b7 (diff)
downloadmeshbay-28c77b7648b2a44a18be64ec44344d91cccfd742.tar.gz
feat(auth): the recovery key never reaches the hub
Ticking "also e-mail it to me" at registration sent the recovery key R in the register body, and the hub appended it to the verification e-mail: the hub saw R in clear on its way out, and the resend door — no captcha — let anyone who knew a pending username and address have the hub mail a "recovery key" of their choosing. The box is gone. The recovery screen offers "E-mail it to myself", a mailto: link that opens the person's own mail application with the key filled in; the desktop shell hands mailto: to the system (http, https and mailto only) and the Android shell to a mail application (ACTION_SENDTO). The CLI drops --email-recovery-key and prints the key, as before. The hub mails the code alone, and refuses a register that still carries a recovery_key rather than dropping it, so an older client offering the option cannot tell someone their key was sent when it was not. A mailbox holding R remains, with the reset code that reaches the same mailbox, enough to recover the account's identities; the link says so, and it is the person's choice. Checked in Chrome and Firefox (headless, the real page against a stubbed hub) and by the CLI's end-to-end registration against a local hub. The Android change was not built here, and the desktop link was not clicked in the application. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/api/users.py24
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/mail.py27
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/auth-page.js48
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/keyderive.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/de.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/en.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/es.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/it.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js8
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/style.css8
15 files changed, 112 insertions, 83 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/api/users.py b/packages/meshbay-hub/src/meshbay_hub/api/users.py
index eb053b7..2d4e3cb 100644
--- a/packages/meshbay-hub/src/meshbay_hub/api/users.py
+++ b/packages/meshbay-hub/src/meshbay_hub/api/users.py
@@ -131,9 +131,9 @@ class RegisterRequest(BaseModel):
email: str
password: str | None = None # deprecated — legacy native clients
auth_key: str | None = None # PBKDF2-derived, new clients
- # Client-generated account recovery key (docs/MESHBAY_DESIGN.md §3.6). Pure
- # pass-through: appended to the verification e-mail so the user's mailbox
- # backs it up, then dropped. Never written to any table, never logged.
+ # Refused when present (see `register`): the hub no longer mails the
+ # recovery key. Kept as a field only so an older client that still sends it
+ # is told so, rather than having it silently dropped.
recovery_key: str | None = None
captcha_token: str | None = None
@@ -193,6 +193,16 @@ async def register(
db: AsyncSession = Depends(get_db),
):
"""Create an account. It stays inactive until its e-mail address is verified."""
+ # The recovery key never reaches the hub (docs/MESHBAY_DESIGN.md §3.6): the
+ # person mails it to themselves from their own mail application. An older
+ # client still offers "e-mail it to me" and would tell them it was sent;
+ # dropping the field quietly would leave them believing that, so the
+ # registration is refused with the reason instead.
+ if body.recovery_key:
+ raise HTTPException(
+ status_code=400,
+ detail="This hub no longer e-mails the recovery key. Update the "
+ "application, or untick the option to e-mail it.")
eh = hash_email_blind(body.email)
# Unique regardless of case: invitations and member management name people
@@ -229,8 +239,7 @@ async def register(
- timedelta(seconds=resend_cooldown),
))
if not recent.first():
- await _create_and_send_verification(
- db, found, body.email, eh, body.recovery_key)
+ await _create_and_send_verification(db, found, body.email, eh)
await db.commit()
return {"user_id": found.id, "email_verification_required": True}
raise HTTPException(status_code=409, detail="Username already taken")
@@ -275,7 +284,7 @@ async def register(
detail=body.username,
))
- await _create_and_send_verification(db, user, body.email, eh, body.recovery_key)
+ await _create_and_send_verification(db, user, body.email, eh)
await db.commit()
return {"user_id": user.id, "email_verification_required": True}
@@ -283,7 +292,6 @@ async def register(
async def _create_and_send_verification(
db: AsyncSession, user: User, email: str, eh: str,
- recovery_key: str | None = None,
) -> None:
user.status = "pending"
# Invalidate any previous pending verification for this user
@@ -307,7 +315,7 @@ async def _create_and_send_verification(
await db.flush()
await mail.send_off_loop(
db, mail.send_verification_code, email, code,
- purpose="registration", recovery_key=recovery_key)
+ purpose="registration")
class VerifyEmailRequest(BaseModel):
diff --git a/packages/meshbay-hub/src/meshbay_hub/mail.py b/packages/meshbay-hub/src/meshbay_hub/mail.py
index 6980da9..768b0d9 100644
--- a/packages/meshbay-hub/src/meshbay_hub/mail.py
+++ b/packages/meshbay-hub/src/meshbay_hub/mail.py
@@ -344,33 +344,17 @@ async def send_off_loop(db, fn, *args, purpose: str, sender: str = "", **kwargs)
return True
-def send_verification_code(to: str, code: str, recovery_key: str | None = None) -> None:
+def send_verification_code(to: str, code: str) -> None:
"""
- Registration verification e-mail. When `recovery_key` is given it is
- appended to the body so the recipient's mailbox becomes the backup for it
- (docs/MESHBAY_DESIGN.md §3.6).
-
- `recovery_key` is a **pass-through**: it is generated on the client, never
- stored anywhere on the hub, and never logged — only whether one was present.
+ Registration verification e-mail: the code and nothing else. The recovery
+ key is never in it — the hub does not see that key (docs/MESHBAY_DESIGN.md
+ §3.6); the person mails it to themselves from their own application.
"""
body = (
f"Your verification code is: {code}\n"
"\n"
"Enter this code to verify your email address.\n"
"This code expires in 24 hours.\n"
- )
- if recovery_key:
- body += (
- "\n"
- "---- Account recovery key ----\n"
- "\n"
- "Keep this message. If you ever forget your passphrase, this key is\n"
- "what restores your access to your groups. It is not stored on the\n"
- f"server and nobody at {_hub_domain} can recover it for you.\n"
- "\n"
- f" {recovery_key}\n"
- )
- body += (
"\n"
"If you did not create a MeshBay account, ignore this email.\n"
"\n"
@@ -382,8 +366,7 @@ def send_verification_code(to: str, code: str, recovery_key: str | None = None)
msg["Subject"] = f"MeshBay — Your verification code: {code}"
msg.set_content(body)
_send(msg, purpose="registration")
- log.info("Verification code sent to %s (recovery_key=%s)",
- _mask_email(to), bool(recovery_key))
+ log.info("Verification code sent to %s", _mask_email(to))
def send_email_change_code(to: str, code: str) -> None:
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js b/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js
index c5374f8..4b8ed7c 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js
@@ -339,6 +339,23 @@ function WelcomePitch() {
`;
}
+/**
+ * A `mailto:` link that has the person's own mail application send the
+ * recovery key to their own address. The hub never sees the key (§3.6): it
+ * used to append it to the verification e-mail, which put it in clear on the
+ * hub's way out. Opened in the browser by the link itself, and by the desktop
+ * and Android shells, which hand `mailto:` to the system.
+ */
+export function recoveryMailto(email, username, mnemonic) {
+ // RFC 6068: the address is percent-encoded like the rest, but '@' stays —
+ // several mail applications do not decode it back.
+ const to = encodeURIComponent(email).replace(/%40/g, '@');
+ const subject = encodeURIComponent(t('register.recovery_mail_subject'));
+ const body = encodeURIComponent(
+ t('register.recovery_mail_body', { user: username, key: mnemonic }));
+ return `mailto:${to}?subject=${subject}&body=${body}`;
+}
+
// The sign-in page's dark gradient backdrop and frosted card, without the
// pitch beside it — so Register (and its verify/recovery/done steps) sits on
// the same background and reads in the same dark theme as Login. A lone
@@ -370,9 +387,6 @@ export function RegisterPage() {
const [recoveryMnemonic, setRecoveryMnemonic] = useState('');
const [recoverySaved, setRecoverySaved] = useState(false);
const [recoveryCopied, setRecoveryCopied] = useState(false);
- // Off by default: mailing the recovery key is opt-in — the key is shown on
- // screen to save, and sending a copy is the user's own choice to make.
- const [emailRecovery, setEmailRecovery] = useState(false);
const captcha = useCaptcha();
const onSubmit = async (e) => {
@@ -393,18 +407,16 @@ export function RegisterPage() {
try {
if (window.MeshBayKeys) {
// The account recovery key (docs/MESHBAY_DESIGN.md §3.6): generated
- // here, shown once on the next screen. When the user leaves "e-mail it"
- // checked, the mnemonic goes in the register body so the hub appends it
- // to the verification e-mail (and stores it nowhere); otherwise it is
- // screen-only. The derived key is kept in `session.recoveryKey` and
- // persisted, so groups joined later — this session or a future one —
- // still leave a recovery-wrapped copy on their node.
+ // here, shown once on the next screen, and never sent to the hub —
+ // the person may mail it to themselves from their own application.
+ // The derived key is kept in `session.recoveryKey` and persisted, so
+ // groups joined later — this session or a future one — still leave a
+ // recovery-wrapped copy on their node.
const rk = window.MeshBayKeys.generateRecoveryKey();
// `name` (trimmed), not the raw field: the hub stores the trimmed
// username and every key derivation must fold in the same string.
const reg = await window.MeshBayKeys.registerUser(
- name, email, password, emailRecovery ? rk.mnemonic : null,
- await captcha.take());
+ name, email, password, await captcha.take());
// An account made in the desktop application starts without browser
// access: its identities stay on this device and nothing of them is
// left on nodes until the person turns it on, here, natively.
@@ -510,8 +522,14 @@ export function RegisterPage() {
<button class="btn-secondary" style="margin-top:8px" onClick=${copyRecovery}>
${recoveryCopied ? t('register.recovery_copied') : t('register.recovery_copy')}
</button>
+ <a class="btn-secondary recovery-mail" role="button"
+ href=${recoveryMailto(email, username.trim(), recoveryMnemonic)}
+ target=${platform.isNative ? '_blank' : undefined} rel="noopener">
+ ${t('register.recovery_mail_self')}
+ </a>
+ <p class="settings-hint" style="margin-top:6px">${t('register.recovery_mail_hint')}</p>
<p style="margin-top:12px; color:var(--text-secondary)">
- ${emailRecovery ? t('register.recovery_emailed') : t('register.recovery_not_emailed')}
+ ${t('register.recovery_shown_once')}
</p>
<p class="error-msg" style="margin-top:8px">${t('register.recovery_warning')}</p>
<label style="display:flex; gap:8px; align-items:flex-start; margin-top:12px">
@@ -589,12 +607,6 @@ export function RegisterPage() {
<${PasswordInput} placeholder=${t('register.confirm')} value=${confirm}
onInput=${e => setConfirm(e.target.value)}
autocomplete="new-password" />
- <label style="display:flex; gap:8px; align-items:flex-start; margin:4px 0 2px;
- font-size:0.88em; color:var(--text-secondary)">
- <input type="checkbox" checked=${emailRecovery}
- onChange=${e => setEmailRecovery(e.target.checked)} />
- <span>${t('register.recovery_email_opt')}</span>
- </label>
${error && html`<div class="error-msg">${error}</div>`}
<button type="submit" disabled=${loading}>
${loading ? t('register.loading') : t('register.submit')}
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js
index bd5282c..bfbb94f 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/keyderive.js
@@ -386,7 +386,7 @@ async function resealLegacyIdentity(keys, sessionKey, recoveryKey, { userId, nod
*
* Returns the raw private keys for immediate use after registration.
*/
-async function registerUser(username, email, password, recoveryMnemonic, captchaToken) {
+async function registerUser(username, email, password, captchaToken) {
// No keypair here any more. Identity keys are per node: one is generated the
// first time this account joins a given node, encrypted under the passphrase,
// and left with that node. So an operator who cracks what sits on their own
@@ -397,10 +397,8 @@ async function registerUser(username, email, password, recoveryMnemonic, captcha
const authKey = await deriveAuthKey(password, username);
const payload = { username, email, auth_key: authKey };
- // The recovery mnemonic, when the user opted to have it e-mailed: the hub
- // appends it to the verification e-mail and stores it nowhere
- // (docs/MESHBAY_DESIGN.md §3.6). Omitted when they chose to save it themselves.
- if (recoveryMnemonic) payload.recovery_key = recoveryMnemonic;
+ // Never the recovery mnemonic: the hub does not see it (docs/MESHBAY_DESIGN.md
+ // §3.6), and refuses a register that carries one.
// The proof-of-work answer, when the hub asks for one. RegisterPage
// (auth-page.js) works it out; this function just forwards it. A hub with
// the check turned off gets nothing and does not look.
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
index a7aca2c..c0135b0 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
@@ -75,9 +75,11 @@ export default {
'register.recovery_warning': 'Wenn Sie sowohl Ihre Passphrase als auch diesen Schlüssel verlieren, bleiben Ihre Dateien auf ihren Knoten, aber Sie treten jeder Gruppe mit einer neuen Identität erneut bei.',
'register.recovery_saved': 'Ich habe meinen Wiederherstellungsschlüssel an einem sicheren Ort gespeichert.',
'register.recovery_continue': 'Weiter',
- 'register.recovery_email_opt': 'Diesen Wiederherstellungsschlüssel zur Sicherung auch per E-Mail an mich senden',
- 'register.recovery_emailed': 'Eine Kopie wurde auch an Ihre E-Mail-Adresse gesendet, in derselben Nachricht wie Ihr Bestätigungscode.',
- 'register.recovery_not_emailed': 'Dies wurde nicht per E-Mail gesendet. Speichern Sie es jetzt — es wird nur dieses eine Mal angezeigt.',
+ 'register.recovery_mail_self': 'Per E-Mail an mich senden',
+ 'register.recovery_mail_hint': 'Öffnet Ihre eigene Mail-App mit eingetragenem Schlüssel. Er läuft nicht über den Hub; wer dieses Postfach lesen kann, kann ihn verwenden.',
+ 'register.recovery_shown_once': 'Speichern Sie ihn jetzt — er wird nur dieses eine Mal angezeigt.',
+ 'register.recovery_mail_subject': 'MeshBay-Wiederherstellungsschlüssel',
+ 'register.recovery_mail_body': 'Wiederherstellungsschlüssel für das MeshBay-Konto {user}:\n\n{key}\n\nEr stellt Ihre Gruppenidentitäten wieder her, wenn Sie Ihre Passphrase vergessen. Halten Sie diese Nachricht privat.',
'register.success_title': 'E-Mail bestätigen',
'register.success_msg': 'Ein Bestätigungscode wurde an Ihre E-Mail-Adresse gesendet. Geben Sie ihn unten ein, um Ihr Konto zu aktivieren.',
'register.go_login': 'Zur Anmeldung',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
index fd56d68..b32c569 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
@@ -78,9 +78,11 @@ export default {
'register.recovery_warning': 'If you lose both your passphrase and this key, your files stay on their nodes but you rejoin each group with a new identity.',
'register.recovery_saved': 'I have saved my recovery key somewhere safe.',
'register.recovery_continue': 'Continue',
- 'register.recovery_email_opt': 'Also email this recovery key to me as a backup',
- 'register.recovery_emailed': 'A copy has also been sent to your email, in the same message as your verification code.',
- 'register.recovery_not_emailed': 'This was not emailed. Save it now — this is the only time it is shown.',
+ 'register.recovery_mail_self': 'E-mail it to myself',
+ 'register.recovery_mail_hint': 'Opens your own mail app with the key filled in. It does not pass through the hub; whoever can read that mailbox can use it.',
+ 'register.recovery_shown_once': 'Save it now — this is the only time it is shown.',
+ 'register.recovery_mail_subject': 'MeshBay recovery key',
+ 'register.recovery_mail_body': 'Recovery key for the MeshBay account {user}:\n\n{key}\n\nIt restores your group identities if you forget your passphrase. Keep this message private.',
'register.success_title': 'Verify your email',
'register.success_msg': 'A verification code has been sent to your email address. Enter it below to activate your account.',
'register.go_login': 'Go to login',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
index 908dca4..6a96f4d 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
@@ -74,9 +74,11 @@ export default {
'register.recovery_warning': 'Si pierde tanto su frase de contraseña como esta clave, sus archivos permanecen en sus nodos pero se vuelve a unir a cada grupo con una nueva identidad.',
'register.recovery_saved': 'He guardado mi clave de recuperación en un lugar seguro.',
'register.recovery_continue': 'Continuar',
- 'register.recovery_email_opt': 'Enviarme también esta clave de recuperación por correo como copia de seguridad',
- 'register.recovery_emailed': 'También se ha enviado una copia a su correo electrónico, en el mismo mensaje que su código de verificación.',
- 'register.recovery_not_emailed': 'Esto no se envió por correo. Guárdela ahora: es la única vez que se muestra.',
+ 'register.recovery_mail_self': 'Enviármela por correo',
+ 'register.recovery_mail_hint': 'Abre su propia aplicación de correo con la clave ya escrita. No pasa por el hub; quien pueda leer ese buzón podrá usarla.',
+ 'register.recovery_shown_once': 'Guárdela ahora: es la única vez que se muestra.',
+ 'register.recovery_mail_subject': 'Clave de recuperación de MeshBay',
+ 'register.recovery_mail_body': 'Clave de recuperación de la cuenta de MeshBay {user}:\n\n{key}\n\nRestaura sus identidades de grupo si olvida su frase de contraseña. Mantenga este mensaje en privado.',
'register.success_title': 'Verifique su correo',
'register.success_msg': 'Se ha enviado un código de verificación a su dirección de correo electrónico. Introdúzcalo a continuación para activar su cuenta.',
'register.go_login': 'Ir al inicio de sesión',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
index 4b4e036..92ab189 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
@@ -74,9 +74,11 @@ export default {
'register.recovery_warning': "Si vous perdez à la fois votre phrase secrète et cette clé, vos fichiers restent sur leurs nœuds mais vous rejoignez chaque groupe avec une nouvelle identité.",
'register.recovery_saved': "J'ai enregistré ma clé de récupération en lieu sûr.",
'register.recovery_continue': 'Continuer',
- 'register.recovery_email_opt': 'M’envoyer aussi cette clé de récupération par e-mail comme sauvegarde',
- 'register.recovery_emailed': 'Une copie a également été envoyée à votre adresse e-mail, dans le même message que votre code de vérification.',
- 'register.recovery_not_emailed': 'Elle n’a pas été envoyée par e-mail. Enregistrez-la maintenant — c’est la seule fois où elle est affichée.',
+ 'register.recovery_mail_self': 'Me l’envoyer par e-mail',
+ 'register.recovery_mail_hint': 'Ouvre votre propre application de messagerie avec la clé déjà saisie. Elle ne passe pas par le hub ; quiconque peut lire cette boîte mail pourra l’utiliser.',
+ 'register.recovery_shown_once': 'Enregistrez-la maintenant — c’est la seule fois où elle est affichée.',
+ 'register.recovery_mail_subject': 'Clé de récupération MeshBay',
+ 'register.recovery_mail_body': 'Clé de récupération du compte MeshBay {user} :\n\n{key}\n\nElle restaure vos identités de groupe si vous oubliez votre phrase secrète. Gardez ce message privé.',
'register.success_title': 'Vérifiez votre e-mail',
'register.success_msg': 'Un code de vérification a été envoyé à votre adresse e-mail. Saisissez-le ci-dessous pour activer votre compte.',
'register.go_login': 'Aller à la connexion',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
index 27692b8..47538ae 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
@@ -75,9 +75,11 @@ export default {
'register.recovery_warning': 'Se perde sia la passphrase sia questa chiave, i suoi file restano sui rispettivi nodi ma si riunisce a ogni gruppo con una nuova identità.',
'register.recovery_saved': 'Ho salvato la mia chiave di recupero in un luogo sicuro.',
'register.recovery_continue': 'Continua',
- 'register.recovery_email_opt': 'Inviami anche questa chiave di recupero via e-mail come backup',
- 'register.recovery_emailed': 'Una copia è stata inviata anche alla sua e-mail, nello stesso messaggio del codice di verifica.',
- 'register.recovery_not_emailed': 'Non è stata inviata via e-mail. La salvi ora: è l’unica volta che viene mostrata.',
+ 'register.recovery_mail_self': 'Inviarmela via e-mail',
+ 'register.recovery_mail_hint': 'Apre la sua app di posta con la chiave già inserita. Non passa dall’hub; chiunque possa leggere quella casella potrà usarla.',
+ 'register.recovery_shown_once': 'La salvi ora: è l’unica volta che viene mostrata.',
+ 'register.recovery_mail_subject': 'Chiave di recupero MeshBay',
+ 'register.recovery_mail_body': 'Chiave di recupero dell’account MeshBay {user}:\n\n{key}\n\nRipristina le sue identità di gruppo se dimentica la passphrase. Tenga privato questo messaggio.',
'register.success_title': 'Verifichi la sua e-mail',
'register.success_msg': 'Un codice di verifica è stato inviato al suo indirizzo e-mail. Lo inserisca qui sotto per attivare il suo account.',
'register.go_login': "Vai all'accesso",
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
index 1b2c230..815435a 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
@@ -75,9 +75,11 @@ export default {
'register.recovery_warning': 'パスフレーズとこのキーの両方を失うと、ファイルは各ノードに残りますが、各グループには新しい identity で再参加することになります。',
'register.recovery_saved': 'リカバリーキーを安全な場所に保存しました。',
'register.recovery_continue': '続ける',
- 'register.recovery_email_opt': 'バックアップとして、このリカバリーキーもメールで送る',
- 'register.recovery_emailed': '確認コードと同じメールで、コピーもお使いのメールアドレスに送信されました。',
- 'register.recovery_not_emailed': 'これはメールで送信されていません。今すぐ保存してください。表示されるのはこの一度だけです。',
+ 'register.recovery_mail_self': '自分にメールで送る',
+ 'register.recovery_mail_hint': 'キーを入力済みの状態でお使いのメールアプリを開きます。ハブは経由しません。そのメールボックスを読める人は誰でもこのキーを使えます。',
+ 'register.recovery_shown_once': '今すぐ保存してください。表示されるのはこの一度だけです。',
+ 'register.recovery_mail_subject': 'MeshBay リカバリーキー',
+ 'register.recovery_mail_body': 'MeshBay アカウント {user} のリカバリーキー:\n\n{key}\n\nパスフレーズを忘れた場合、グループの identity を復元します。このメッセージは他人に見せないでください。',
'register.success_title': 'メールアドレスを確認してください',
'register.success_msg': '確認コードをメールアドレスに送信しました。以下に入力してアカウントを有効にしてください。',
'register.go_login': 'ログインへ進む',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
index 971b046..fde0941 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
@@ -75,9 +75,11 @@ export default {
'register.recovery_warning': 'Als u zowel uw wachtwoordzin als deze sleutel verliest, blijven uw bestanden op hun nodes staan, maar treedt u tot elke groep opnieuw toe met een nieuwe identiteit.',
'register.recovery_saved': 'Ik heb mijn herstelsleutel op een veilige plek bewaard.',
'register.recovery_continue': 'Doorgaan',
- 'register.recovery_email_opt': 'Stuur deze herstelsleutel ook naar mij per e-mail als back-up',
- 'register.recovery_emailed': 'Er is ook een kopie naar uw e-mailadres gestuurd, in hetzelfde bericht als uw verificatiecode.',
- 'register.recovery_not_emailed': 'Dit is niet per e-mail verstuurd. Sla het nu op — dit is de enige keer dat het wordt getoond.',
+ 'register.recovery_mail_self': 'Naar mezelf e-mailen',
+ 'register.recovery_mail_hint': 'Opent uw eigen mail-app met de sleutel al ingevuld. Hij gaat niet via de hub; wie die mailbox kan lezen, kan hem gebruiken.',
+ 'register.recovery_shown_once': 'Sla hem nu op — dit is de enige keer dat hij wordt getoond.',
+ 'register.recovery_mail_subject': 'MeshBay-herstelsleutel',
+ 'register.recovery_mail_body': 'Herstelsleutel voor het MeshBay-account {user}:\n\n{key}\n\nHij herstelt uw groepsidentiteiten als u uw wachtwoordzin vergeet. Houd dit bericht privé.',
'register.success_title': 'Verifieer uw e-mail',
'register.success_msg': 'Er is een verificatiecode naar uw e-mailadres gestuurd. Voer deze hieronder in om uw account te activeren.',
'register.go_login': 'Naar het aanmelden',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
index 5092e5c..b8dc1cd 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
@@ -78,9 +78,11 @@ export default {
'register.recovery_warning': 'Jeśli stracisz zarówno hasło-frazę, jak i ten klucz, Twoje pliki pozostaną na swoich węzłach, ale do każdej grupy dołączysz ponownie z nową tożsamością.',
'register.recovery_saved': 'Zapisałem/-am mój klucz odzyskiwania w bezpiecznym miejscu.',
'register.recovery_continue': 'Kontynuuj',
- 'register.recovery_email_opt': 'Wyślij mi też ten klucz odzyskiwania e-mailem jako kopię zapasową',
- 'register.recovery_emailed': 'Kopia została również wysłana na Twój adres e-mail, w tej samej wiadomości co kod weryfikacyjny.',
- 'register.recovery_not_emailed': 'To nie zostało wysłane e-mailem. Zapisz teraz — jest pokazywane tylko ten jeden raz.',
+ 'register.recovery_mail_self': 'Wyślij mi e-mailem',
+ 'register.recovery_mail_hint': 'Otwiera Twoją aplikację pocztową z wpisanym kluczem. Nie przechodzi przez hub; każdy, kto może czytać tę skrzynkę, może go użyć.',
+ 'register.recovery_shown_once': 'Zapisz go teraz — jest pokazywany tylko ten jeden raz.',
+ 'register.recovery_mail_subject': 'Klucz odzyskiwania MeshBay',
+ 'register.recovery_mail_body': 'Klucz odzyskiwania konta MeshBay {user}:\n\n{key}\n\nPrzywraca Twoje tożsamości w grupach, jeśli zapomnisz hasła. Zachowaj tę wiadomość w tajemnicy.',
'register.success_title': 'Zweryfikuj swój e-mail',
'register.success_msg': 'Kod weryfikacyjny został wysłany na Twój adres e-mail. Wpisz go poniżej, aby aktywować konto.',
'register.go_login': 'Przejdź do logowania',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
index 075cc31..d517387 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
@@ -76,9 +76,11 @@ export default {
'register.recovery_warning': 'Se você perder tanto a frase secreta quanto esta chave, seus arquivos permanecem em seus nós, mas você reingressa em cada grupo com uma nova identidade.',
'register.recovery_saved': 'Guardei minha chave de recuperação em um local seguro.',
'register.recovery_continue': 'Continuar',
- 'register.recovery_email_opt': 'Enviar também esta chave de recuperação para o meu e-mail como backup',
- 'register.recovery_emailed': 'Uma cópia também foi enviada para o seu e-mail, na mesma mensagem que o seu código de verificação.',
- 'register.recovery_not_emailed': 'Isto não foi enviado por e-mail. Guarde agora — esta é a única vez que é mostrada.',
+ 'register.recovery_mail_self': 'Enviar para o meu e-mail',
+ 'register.recovery_mail_hint': 'Abre o seu próprio app de e-mail com a chave já preenchida. Ela não passa pelo hub; quem puder ler essa caixa de correio poderá usá-la.',
+ 'register.recovery_shown_once': 'Guarde agora — esta é a única vez que é mostrada.',
+ 'register.recovery_mail_subject': 'Chave de recuperação do MeshBay',
+ 'register.recovery_mail_body': 'Chave de recuperação da conta MeshBay {user}:\n\n{key}\n\nEla restaura suas identidades de grupo se você esquecer sua frase secreta. Mantenha esta mensagem privada.',
'register.success_title': 'Verifique seu e-mail',
'register.success_msg': 'Um código de verificação foi enviado para o seu e-mail. Digite-o abaixo para ativar sua conta.',
'register.go_login': 'Ir para o login',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
index 0ef354e..42c56f0 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
@@ -75,9 +75,11 @@ export default {
'register.recovery_warning': '如果您同时丢失了密码短语和此密钥,您的文件仍会保留在各自的节点上,但您将以新身份重新加入每个群组。',
'register.recovery_saved': '我已将恢复密钥保存在安全的地方。',
'register.recovery_continue': '继续',
- 'register.recovery_email_opt': '同时将此恢复密钥通过邮件发送给我作为备份',
- 'register.recovery_emailed': '副本也已发送至您的邮箱,与验证码在同一封邮件中。',
- 'register.recovery_not_emailed': '这未通过邮件发送。请立即保存——它只显示这一次。',
+ 'register.recovery_mail_self': '通过邮件发给自己',
+ 'register.recovery_mail_hint': '打开您自己的邮件应用并填好密钥。它不经过 hub;任何能读取该邮箱的人都能使用它。',
+ 'register.recovery_shown_once': '请立即保存——它只显示这一次。',
+ 'register.recovery_mail_subject': 'MeshBay 恢复密钥',
+ 'register.recovery_mail_body': 'MeshBay 账户 {user} 的恢复密钥:\n\n{key}\n\n如果您忘记密码短语,它可恢复您的群组身份。请勿将此邮件透露给他人。',
'register.success_title': '验证您的邮箱',
'register.success_msg': '验证码已发送至您的邮箱。请在下方输入以激活账户。',
'register.go_login': '前往登录',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css
index 07780a2..627f2ca 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/style.css
+++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css
@@ -2661,6 +2661,14 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
}
.btn-secondary:hover { border-color: var(--border); color: var(--text); }
.btn-secondary:disabled { opacity: 0.4; cursor: not-allowed; }
+/* The recovery screen's "e-mail it to myself": a mailto: link, shaped like the
+ Copy button beside it. */
+a.recovery-mail {
+ display: inline-block;
+ margin: 8px 0 0 8px;
+ border-color: var(--border);
+ text-decoration: none;
+}
/* Text that acts as a button: the "add a description" affordance should not
compete with the group's name for attention. */