Phase-8+ feature. Allow unregistered users (level 0) to self-register at level 20 (reg) without operator action. Two channels: in-hub command and web/API endpoint.
Why
Lowers the onboarding bottleneck for community hubs running reg_only = false. Today the only path is +reg via an operator (level 60+). For open hubs that's unnecessary friction.
Hard preconditions
reg_only = false (otherwise unregged can't connect at all to send +selfreg).
- Default OFF: cfg flag
selfreg_enabled = false so existing deployments are unchanged on upgrade.
Channels
A. Hub command +selfreg <password>
- Available to level 0 (unreg) only; rejected for level >= 20.
- Uses the connected user's already-claimed nick (the BINF nick).
- Stores reg with level 20, marks
email_verified = false until verify-link is clicked.
- Reply:
"Check <email> for the verification link, then reconnect."
B. REST endpoint POST /selfreg (depends on #82)
Email verification (mandatory, both channels)
New core/mail.lua (or plugin etc_mail.lua) wrapping LuaSocket SMTP:
- cfg keys:
mail_smtp_host, mail_smtp_port, mail_smtp_user, mail_smtp_pass, mail_from.
- Verify token: 32 random bytes via
adclib.random_bytes, base32 in URL.
- Token stored in
cfg/selfreg_pending.tbl (encrypted via cfg_secret, same Phase-7f channel as user.tbl).
- Token TTL: cfg-tunable, default 1h.
- Verify URL clicked -> sets
email_verified = true, moves profile from pending to live _regusers.
Captcha (web channel only)
Pluggable: hCaptcha or reCAPTCHA via cfg-keyed site/secret. Hub-command path skips captcha (in-hub user is already past the per-IP connection cap from Phase 7c F-NET-1).
Per-IP rate limit (mandatory)
Sybil / squatting concerns
Audit logging (depends on #84)
Every self-reg attempt and outcome (success / verify-fail / rate-limit / squat-rejection) fires the audit event so operators can review onboarding patterns.
Sub-tasks (checklist)
Out of scope for v1
- OAuth / SSO / external identity providers.
Dependencies
Acceptance
+selfreg <password> rejects level >= 20, accepts level 0, sends verify mail.
- Reconnect after verify succeeds at level 20 with the chosen password.
- Without
reg_only = false and selfreg_enabled = true, the command is unavailable.
- REST endpoint passes captcha, sends mail, verify URL promotes profile.
- Rate-limit kicks in after N attempts per IP per hour.
- All Phase 7 invariants intact (user.tbl LDC1 magic, smoke 12+/12+ PASS).
Phase-8+ feature. Allow unregistered users (level 0) to self-register at level 20 (reg) without operator action. Two channels: in-hub command and web/API endpoint.
Why
Lowers the onboarding bottleneck for community hubs running
reg_only = false. Today the only path is+regvia an operator (level 60+). For open hubs that's unnecessary friction.Hard preconditions
reg_only = false(otherwise unregged can't connect at all to send+selfreg).selfreg_enabled = falseso existing deployments are unchanged on upgrade.Channels
A. Hub command
+selfreg <password>email_verified = falseuntil verify-link is clicked."Check <email> for the verification link, then reconnect."B. REST endpoint
POST /selfreg(depends on #82){ nick, password, email, captcha_token }.selfreg_http_listen/selfreg_http_bind.Email verification (mandatory, both channels)
New
core/mail.lua(or pluginetc_mail.lua) wrapping LuaSocket SMTP:mail_smtp_host,mail_smtp_port,mail_smtp_user,mail_smtp_pass,mail_from.adclib.random_bytes, base32 in URL.cfg/selfreg_pending.tbl(encrypted viacfg_secret, same Phase-7f channel as user.tbl).email_verified = true, moves profile from pending to live_regusers.Captcha (web channel only)
Pluggable: hCaptcha or reCAPTCHA via cfg-keyed site/secret. Hub-command path skips captcha (in-hub user is already past the per-IP connection cap from Phase 7c F-NET-1).
Per-IP rate limit (mandatory)
selfreg_attempts, e.g.3 per IP per hour.Sybil / squatting concerns
+delregby an op). Combined with the Pre-auth DoS: registered users kicked on nick collision before HPAS challenge in reg_only mode #91 takeover gate, an attacker who self-regs a popular nick locks out the legitimate person forever. Mitigations:selfreg_reserved_nicks = { \"admin\", \"support\", ... }rejected unconditionally.selfreg_invite_required = true, ops generate one-shot codes).Audit logging (depends on #84)
Every self-reg attempt and outcome (success / verify-fail / rate-limit / squat-rejection) fires the audit event so operators can review onboarding patterns.
Sub-tasks (checklist)
core/mail.luaSMTP wrapper + cfg keys + smoke test (loopback SMTP fake).selfreg_pending.tblvia cfg_secret.+selfreg <password>(level-0 path)._regusers).POST /selfregendpoint (separate listener from Local HTTP API: read-only first, write-endpoints later #82).+selfreg, capture verify URL from fake SMTP, click it, confirm reg promoted).docs/SECURITY.md: threat-model section on self-reg + Sybil/squatting mitigations.Out of scope for v1
Dependencies
core/mail.lua(or plugin), which other features can reuse later (password-reset, ban-notification, etc).Acceptance
+selfreg <password>rejects level >= 20, accepts level 0, sends verify mail.reg_only = falseandselfreg_enabled = true, the command is unavailable.