gitoriaLog in with ident

calendar

All repositories: gitoria

ReadmeCodePull requestsReleasesTicketsSettings
Commit3b2a4cd03b2a4cd0calendar mission 001 (3/4): code order — let only where reassigned (147 dropped incl. components/month-view; 110 left: 67 reassigned, 43 loop-bound); gates 78/0 (3 of 4 runs; 1 known 'next month' flake) + 18/0, live-data run = step 2mre3b2a4cd0/README.md

10.6 KB

  1. # calendar.worldapi.org
  2. The creator's calendar (CONCEPT.md), in Hybriel on hl:web. The month view at `/` — prev / next buttons on top, month name
  3. and year between them, today marked, weeks start on Monday, names in the browser's language — and, since ticket #2,
  4. **login with ident and private events**. https://calendar.worldapi.org
  5. ## Use
  6. - Signed out: the month view and "Log in with ident" (top right; the identity selector or the button).
  7. - Signed in: click a day to add an event, click an event to edit or delete it. An event has a title, "At a time" (from /
  8. to, date and time) or "All day" (from / to, dates), a place and notes. It shows on every day it covers (a day shows four
  9. chips, the fourth says "+N more"). Only the owner sees, changes or deletes an event.
  10. - Events are stored as **WorldAPI Events** (https://worldapi.org/standards/event): `$type $version $kind $title
  11. $description $start $end $timezone $location $status $visibility $created $updated` plus `owner` (users `@id`). Timed
  12. events: local wall-clock `$start`/`$end` + `$timezone` (the browser's); all-day: `$kind = 'allday'`, date `$start`, exclusive
  13. date `$end` (left out for one day); `$created`/`$updated` are UTC instants; `$visibility` is always `private`.
  14. - **Short ids (ident#23, antcolony mission 039)**: `users.identity` holds what ident's exchange answers — since ident#23 the identity's
  15. public 5-character short id (`a68sz`), before that the per-app id (32 hex); `users.hl isIdentId` accepts both (the old
  16. `isHex` check refused short ids). The switch: one-off `tools/migrate-short-ids.hl` (old → short id, idempotent, never
  17. `finish`), gate `node tests/short-id-switch.mjs` (ports 8722/8723, no browser), runbook
  18. `antcolony-docs/docs/short-id-switch.md` (Byrodin: `/CONTAINERS/projects/antcolony/docs/short-id-switch.md` once synced).
  19. ## Reminders and notifications (ticket #4)
  20. - **Reminder settings** (button under the month, signed in): the calendar's default reminders — 1 hour before and the day before at
  21. 20:00 until changed. Every event without reminders of its own gets them. In an event, "Calendar's" / "Own" chooses; "Own" is a list
  22. of rows (N minutes before, N hours before, N days before at HH:MM), also none at all.
  23. - Stored as WorldAPI Event `$reminders` (`{ $before: 'PT1H' }`, `{ $days_before: 1, $time: '20:00' }`); no `$reminders` = inherit; the
  24. defaults are the Calendar's `$reminders` in `storage/mpackdb/settings.db` (one row per owner).
  25. - The page keeps the events of the next days with the reminders that apply (`soon`, part of the page state — the offline layer stores it)
  26. and sets one browser timer per reminder of the next 7 days (`arm()`; set again after every save / delete / paging and every 10 minutes
  27. of use). A timer shows the notification (hl:web `notify`, through the service worker). "Turn on notifications" is the click the browser
  28. wants before it allows them.
  29. - **Offline:** the timers need no network, and the offline page has `soon`. An event saved while the server cannot be reached shows at once,
  30. its reminders are set, and the save waits in hl:web's emit queue until the connection is back.
  31. - **Limit (Hybriel):** a timer lives while the app (or its tab) is open; a fully closed app cannot be woken without a server push (later,
  32. via ident's push notifications, ident#8). `notifyAt` cannot cancel, so `arm()` uses `setTimeout` directly and calls `notify`.
  33. - Code: `components/month-view/reminders.hl` (rows ↔ `$reminders`, describe, time-zone instants, the plan) shared by `events.hl` (settings
  34. table, `upcoming`) and `month-view.hl` (editor, timers).
  35. ## Layout
  36. - `project.hl` routes, the ident login return (`/login/callback`, `/login/failed`) · `styles.hl` all CSS (tokens from
  37. `shared/tokens.hl`, accent = light blue) · `components/main.hl` shell with the login · `components/calendar.hl` the page
  38. and the faces `calLoad` / `calSave` / `calDelete` · `components/loginfailed.hl`.
  39. - `users.hl` users table (`storage/mpackdb/users.db`, ident identity → user `@id`) and the ident exchange ·
  40. `events.hl` events table (`storage/mpackdb/events.db`, index `owner`), strict validation, every function takes the owner.
  41. - `components/month-view/` the month view, vendored from components.hybriel.worldapi.org and EXTENDED here (events on the
  42. days, the editor; `dates.hl` = date helpers shared with the server; it carries its own `Style`). Re-vendoring needs these changes carried over.
  43. - `login.js` ident selector ↔ shell bridge (the only hand-written JavaScript). The month view reads the browser's today / language / time zone itself (Hybriel `Date` / `Intl` in a handler, on the first pointer / focus / touch), no `month-view.js`.
  44. - PWA (ticket #3): installable and offline through hl:web's own support — `appIcons`, `appThemeColor`, `offline = [ Calendar ]` in project.hl (manifest link in the head, `/__hl/manifest.webmanifest`, `/__hl/sw.js`); icons in `icons/`. No JavaScript of ours.
  45. - `bin/hybriel` + `plugins/` (core crypto data fetch fs http http1 mpackdb proc time web) are vendored = hybriel **master
  46. 06617221** (2026-10-03, antcolony mission 074: plugin allocators 3a781359 + 413f60e4 (#126, plugins allocate with malloc via
  47. plugin_api.zig), mpackdb 2cb7ae5e, http1 773de63e, f0ac2d2d (plugin ABI field — bin and .so must match); no lambda semantics
  48. change), sha256 `21059cc7…d77bdb`, built the same way from `git archive 06617221` (`~/scratch-074/src`, removed); previous copy
  49. (190aa11d) in `.scratch/pre-074/`. Gates: `CALENDAR_GATE_PORT=8760 CALENDAR_GATE_IDENT_PORT=8761 CALENDAR_GATE_CHROME=8762-8769
  50. node tests/browser.mjs` 78/0, `CALENDAR_SWITCH_IDENT_PORT=8760 CALENDAR_SWITCH_PORT=8761 node tests/short-id-switch.mjs` 18/0.
  51. Memory: `LOADS=N PAGES=N node .scratch/w074/memtest.mjs <app dir> <mpackdb copy> <label> 0muh8y8myb5z` (port 8760, Chrome
  52. 8761–8769; RSS marks every 250): 1000 loads + 1000 pagings new 194 → 214 → 218 MB, old 190aa11d 194 → 227 → 231 MB.
  53. Before: master 190aa11d (2026-10-02, antcolony mission 072: fc838894 GC correctness (string index / plugin error read freed memory),
  54. 038d84b3 #126 returned closure scopes, #127 d98926c6/04df4428; no lambda semantics change since 8efba065), sha256
  55. `860f5e61…0a23626`, built read-only from `git archive 190aa11d`; previous copy (8efba065) in `.scratch/pre-072/`. Gates:
  56. `CALENDAR_GATE_PORT=8720 CALENDAR_GATE_IDENT_PORT=8721 CALENDAR_GATE_CHROME=8722-8729 node tests/browser.mjs` 78/0,
  57. `CALENDAR_SWITCH_IDENT_PORT=8730 CALENDAR_SWITCH_PORT=8731 node tests/short-id-switch.mjs` 18/0. Memory:
  58. `.scratch/w072/memtest.mjs` (port 8720, Chrome 8721-8728, same args): 200+200 new 194 → 214 → 215 MB (old 283 → 368 → 376),
  59. 1000+1000 new 229 → 233 MB — flat.
  60. Before: master 8efba065 (2026-10-02, antcolony mission 069: #126 memory freed by bytes too, #48 lambda parameters COPY; earlier ff51cf46 #113,
  61. #115/#116, #118, client SVG namespace, hl:markdown #111; #103–#106, #94, #83, #82, #95–#97). Built from a
  62. read-only `git archive master` (`zig build -Doptimize=ReleaseFast -Dtarget=x86_64-linux-gnu.2.39` in `native/`, zig =
  63. hybriel's `native/zig-toolchain` link), sha256 `56b108f3…f4f201ea`. **No local patch**: re-vendor = copy binary + the
  64. plugins above, run both gates. Previous copy (ff51cf46): `.scratch/pre-069/`.
  65. - **Lambdas copy their arguments (hybriel #48)**: `f = (x) => { x.n = 1 }` changes a COPY; a lambda that must change the
  66. caller's hybrid/list takes `&x`. Audit 2026-10-02 (`python3 .scratch/w069/lambdas.py` / `lambdas2.py`, 64 lambdas): none
  67. writes a parameter the caller reads — `project.hl` failed/loginCallback save `req.session` themselves, the rest build new
  68. values. Probe: `bin/hybriel .scratch/w069/probe48.hl`. New lambdas: use `&` when the caller relies on the change.
  69. - Memory check (#126): `node .scratch/w069/memtest.mjs <app copy> <mpackdb copy> <label> <users @id>` (port 8730, Chrome
  70. 8722-8729; env LOADS/PAGES): RSS at boot, after N signed-in page loads, after N month pagings (calLoad), after 5 s idle.
  71. - `components/calendar.hl` `validWindow` / `soonOf` are plain members called from the server faces (no `static` since #115/#116,
  72. antcolony mission 048; the gate checks both).
  73. - Language of the first render (calendar#6): `components/calendar.hl` declares `headers = null` (hybriel #105) and hands
  74. `headers['accept-language']` to `MonthView { acceptLanguage = requestLanguage }`; `names.hl` knows en de fr es it nl pt,
  75. any other → neutral names until the browser fills in its own. Names written by the server stay while paging.
  76. - Reminder timers: `arm()` creates the `setTimeout` closure inline in its `while` loop — `let p` is a new binding per pass
  77. (hybriel #106). On an older Hybriel the closures shared `p` and only the last reminder fired (antcolony mission 037 proved it:
  78. gate on 13ef4f9b → "the notifications came on time" FAIL, only "Has its own reminder" came).
  79. ## Run, test, deploy
  80. - `CALENDAR_PORT=8700 ./bin/hybriel project.hl` (env: `CALENDAR_WATCH=0` no watcher, `HL_HOST`, `CALENDAR_SESSIONS`,
  81. `CALENDAR_STORAGE`, `CALENDAR_PUBLIC_URL` default https://calendar.worldapi.org, `IDENT_URL`, `IDENT_EXCHANGE_URL`,
  82. `IDENT_API_KEY`, `IDENT_API_SECRET` — `.env` beside `project.hl`, never read by workers). A login needs the ident key.
  83. - `node tests/browser.mjs` — the gate: own ident (a copy without `.env`), own server + storage, headless Chrome
  84. (ports 8700 app, 8701 ident, 8702–8709 Chrome; `CALENDAR_GATE_PORT`, `CALENDAR_GATE_IDENT_PORT`, `CALENDAR_GATE_CHROME`).
  85. Second gate: `node tests/short-id-switch.mjs` (ports 8722/8723; `CALENDAR_SWITCH_IDENT_PORT`, `CALENDAR_SWITCH_PORT`).
  86. `GATE_SKIP_EVENTS=1` skips the events part (40 checks run instead of 76, ~1.5 min instead of ~4). The language checks (`languagePart`)
  87. open a tab per language with CDP `Network.setExtraHTTPHeaders` Accept-Language de / en and the browser's own language
  88. (Intl locale + navigator.language) set to the OTHER one; first page, server HTML, next/next/prev.
  89. - `./deploy.sh` on Loreana (architect): gate → backup → rsync → `docker compose up -d --build` on Byrodin (127.0.0.1:45005).
  90. ## First deploy of ticket #2 (architect)
  91. Register the app in ident with the origin `https://calendar.worldapi.org` and put `IDENT_API_KEY` / `IDENT_API_SECRET`
  92. into `.env` on Byrodin (as notes does), otherwise the login button has no key.
  93. ## History and worker briefs
  94. - `LOG.md` — append-only history, one dated line per step (moved here from the antcolony LOG on 2026-10-01).
  95. - `missions/NNN-*.md` — worker briefs for this app; `reports/NNN-*.md` — their reports (same name). Numbered per
  96. project since 2026-10-01 (antcolony#40); older text, code comments and commits use the old antcolony numbers →
  97. map: `/media/STORAGE/projects/antcolony-docs/docs/mission-map.md` (Byrodin: `/CONTAINERS/projects/antcolony/docs/mission-map.md`).

Branches

Latest commits

  • 3b2a4cd0calendar mission 001 (3/4): code order — let only where reassigned (147 dropped incl. components/month-view; 110 left: 67 reassigned, 43 loop-bound); gates 78/0 (3 of 4 runs; 1 known 'next month' flake) + 18/0, live-data run = step 2mre
  • f5ce6b1dcalendar mission 001 (2/4): code order — topics, map, thin faces: lib/util.hl, lib/events(-helpers).hl (+calendarView/settingsView/soonOf), lib/settings.hl, lib/users.hl (+userOfLoginCode, tagOf), lib/api(-helpers).hl; project.hl = map; login route takes &req/&sessions (failed-login reason now kept); gates 78/0 + 18/0mre
  • c6fbd011calendar mission 001 (1/4): code order — files moved: lib/events.hl, lib/users.hl, components/styles.hl (imports only); gates 78/0 + 18/0, live-data run identicalmre
  • d6c59290calendar: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gates 78/0 + 18/0mre
  • 7bd0337ccalendar: Hybriel master 190aa11d (fc838894 GC correctness, #126 closure scopes, #127); gates 78/0 + 18/0mre
  • ff41310ccalendar: Hybriel master 8efba065 (#126 memory, #48 lambda copy; audit: no & needed)mre
  • 14ba08c7antcolony#40: mission references point to the moved missionsmre
  • 99c73346antcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
  • 76edaa62calendar: Hybriel master ff51cf46 (re-vendor round, static workaround removed)mre
  • 90a3fc2cdeploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
  • 6722b72ddeploy.sh: never send .git or .gitignore to Byrodinmre
  • be099807State of 2026-09-27, before the move to gitoriamre