gitoriaLog in with ident

ident

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit5fdbb6b25fdbb6b2ident mission 009: report — scratch folder notemre5fdbb6b2/lib/login-helpers.hl

2.7 KB

  1. // lib/login-helpers.hl — what lib/login.hl looks up: making a six-digit code and its hash, and the client's IP
  2. // BUCKET of the per-IP limit. No table access. Statics only.
  3. import { randomBytes, sha256 } from 'hl:crypto'
  4. // ---- codes --------------------------------------------------------------------------
  5. // six digits from the kernel CSPRNG (randomBytes answers hex)
  6. static newOtp = () => {
  7. hex = randomBytes(8)
  8. let n = 0
  9. let i = 0
  10. while (i < 12) {
  11. n = (n * 16 + '0123456789abcdef'.indexOf(hex[i])) % 1000000
  12. i = i + 1
  13. }
  14. let s = '' + n
  15. while (s.length < 6) { s = '0' + s }
  16. return s
  17. }
  18. static otpHash = (email, code) => { return sha256(email + ':' + code) }
  19. // ---- the client's IP bucket (mission 006 (old 010)) -------------------------------------------
  20. // `ip` is the X-Client-IP header nginx sets on Byrodin (CF-Connecting-IP when the request
  21. // really came through Cloudflare, else the TCP peer; nginx OVERWRITES what a client sent),
  22. // or null when there is none (dev without nginx: hl:web's req.remoteAddress would be nginx's; hl:http1 did not expose the
  23. // connection's peer address). The bucket:
  24. // no header / empty → 'direct' ONE SHARED BUCKET for every header-less request
  25. // IPv4 (also ::ffff:a.b.c.d) → the address
  26. // IPv6 → its /64 prefix ('2a01:4f9:3080:1126::/64'): one client
  27. // usually owns a whole /64, so per-address would not limit it
  28. // anything else → the text itself (lowercased, cut to 64 chars)
  29. static hexDigits = '0123456789abcdef'
  30. static stripZeros = (g) => {
  31. let t = g
  32. while (t.length > 1 && t[0] == '0') { t = t.slice(1, t.length) }
  33. return t
  34. }
  35. static ipBucket = (ip) => {
  36. if (ip == null || hlTypeName(ip) != 'String') { return 'direct' }
  37. let t = ip.trim().toLowerCase()
  38. if (t == '') { return 'direct' }
  39. if (t.length > 64) { t = t.slice(0, 64) }
  40. if (!t.includes(':')) { return t }
  41. // an IPv4-mapped IPv6 address counts as its IPv4 address
  42. if (t.includes('.')) { return t.slice(t.lastIndexOf(':') + 1, t.length) }
  43. let head = t
  44. let tail = ''
  45. gap = t.indexOf('::')
  46. if (gap >= 0) {
  47. head = t.slice(0, gap)
  48. tail = t.slice(gap + 2, t.length)
  49. }
  50. groups = head == '' ? [] : head.split(':')
  51. rest = tail == '' ? [] : tail.split(':')
  52. if (gap >= 0) {
  53. let fill = 8 - groups.length - rest.length
  54. while (fill > 0) {
  55. groups.push('0')
  56. fill = fill - 1
  57. }
  58. }
  59. for (g of rest) { groups.push(g) }
  60. if (groups.length != 8) { return t }
  61. out = []
  62. let i = 0
  63. while (i < 4) {
  64. let g = groups[i]
  65. if (g == '' || g.length > 4) { return t }
  66. let j = 0
  67. while (j < g.length) {
  68. if (!hexDigits.includes(g[j])) { return t }
  69. j = j + 1
  70. }
  71. out.push(stripZeros(g))
  72. i = i + 1
  73. }
  74. return out.join(':') + '::/64'
  75. }

Branches

Latest commits

  • 5fdbb6b2ident mission 009: report — scratch folder notemre
  • 74235815ident mission 009 (4/4): docs (README files map + same-output test, STATUS, LOG), report, tests/letcount.py + realdata-baseline/comparemre
  • fe183516ident mission 009 (3/4): let only where reassigned — 293 never-reassigned lets are plain declarations; kept: reassigned, loop bodies, names of a file member, a name declared twice in one function; same outputmre
  • d2e7f91bident mission 009 (2/4): one lib file per topic (login, accounts, identities, apps, invites, selector, notify + helpers, util), function routes as thin wrappers in lib/api.hl, project.hl = the map; same outputmre
  • 91017164ident mission 009 (1/4): file moves — the root .hl files into lib/ (api.hl → lib/api-helpers.hl), styles.hl → components/styles.hl; imports adjusted, no other changemre
  • f8bdcbc2ident: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); all gates greenmre
  • ff78726cident: Hybriel master 190aa11d (fc838894 GC correctness, #127, #126 closure scopes); gates all greenmre
  • a3a7d21aident: Hybriel master 8efba065 (#126 GC by bytes, #48 lambda params copy); session-writing lambdas take &sessionmre
  • 98226b41antcolony#40: mission references point to the moved missionsmre
  • ff805b9aantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
  • 51a7bcdfident: Hybriel master 73267707 (#122); /code uses the new page() signature; pending address passed as parameter; once-checksmre
  • 836f644fident#24: installable app (manifest, service worker, data-free offline /start), own iconmre
  • 8bebbbf2deploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
  • cc063ea2deploy.sh: never send .git or .gitignore to Byrodinmre
  • 81b15b7bState of 2026-09-27, before the move to gitoriamre