ident
All repositories: gitoria
8.5 KB
// project.hl — ident.worldapi.org: THE MAP. Config, routes and wiring; the logic lives in lib/. Concept: CONCEPT.md// (the creator's).//// WHERE THINGS ARE (one line per feature; README.md "Files" has the full list):// the login: email → code, limits per address / IP, the code page lib/login.hl (codes, IP buckets: lib/login-helpers.hl)// the account, its sessions, sign out everywhere, time zone lib/accounts.hl// identities, the short id, avatar lib/identities.hl (checks, label: lib/identities-helpers.hl)// apps, connections, the login button + exchange, an invite's login lib/apps.hl (checks, return URL: lib/apps-helpers.hl)// the identity selector (pick ids, the choice) lib/selector.hl (browser half: selector.js)// invites (ident#22) lib/invites.hl (checks: lib/invites-helpers.hl)// notifications: kinds, sending, inbox, per-app switches lib/notify.hl (checks, orders: lib/notify-helpers.hl)// mail (codes, invitations; IDENT_MAIL_SINK) lib/mail.hl// the function routes (/login, /api/*, /invite/:token, /code) lib/api.hl (plumbing: lib/api-helpers.hl, lib/jsoncheck.hl)// shared small helpers (storage dir, env, lists, text checks) lib/util.hl// pages: shell, sign-in + identities, start, apps, inbox, per-app components/*.hl; CSS components/styles.hl//// THE FLOWS (README "How apps use ident", "How apps send notifications", "Invites"):// GET /login?key=<api key>&return=<url> → error page, or → /signin/<rid> (sign in, choose the identity,// → <return url>?ident_code=<one-time code>)// POST /api/exchange { key, secret, code } → { identity } (the identity's short id only)// GET /selector.js, GET /api/selector/identities?key=, POST /api/selector/choose?key= {identity} → { code }// POST /api/code { email } → { email } | 400/429 { error } (a function route: the per-IP limit needs the request)// GET /code, /signin/<rid>/code the code form for this session's pending address, or → / resp. /signin/<rid>// Deploy (Byrodin): HL_HOST=127.0.0.1 binds loopback only (hl:web reads HL_HOST, hybriel#24),// IDENT_PORT the port.import WebFramework from 'hl:web'import { env } from 'hl:proc'import Styles from './components/styles.hl'import { dark, darker } from './shared/tokens.hl'import { appLogin, apiExchange, apiMigrateIds, apiCode, apiSelectorIdentities, apiSelectorChoose, apiInvites, apiInvitesList, apiInvitesGet, apiInvitesRevoke, inviteLink, apiKinds, apiNotify, apiOnline, notFoundApi, codePage } from './lib/api.hl'import Mail from './lib/mail.hl'import { sessionHooks, dropUserSessions } from './lib/accounts.hl'import { shortIdsBackfilled } from './lib/identities.hl'// ident#23: statics run on first use — touching it here gives every older identity its short id at bootconsole.log('ident: ' + shortIdsBackfilled + ' identities got their short id')import Home from './components/home.hl'import Start from './components/start.hl'import Apps from './components/apps.hl'import Inbox from './components/inbox.hl'import AppSettings from './components/appsettings.hl'static siteName = "ident"appTitle = siteNamestyles = Styles// ---- THE INSTALLABLE APP (mission 046, as calendar#3): hl:web generates the web app manifest// (/__hl/manifest.webmanifest, linked in every head with the apple-touch-icon and theme-color)// from these, and the service worker (/__hl/sw.js) from `offline`. No JavaScript of ours.// Icons: icons/ (icon.svg is the source, README "PWA"). Theme colour = the header's colour (token// `darker`), background `dark` — the same in every app (mission 046); the icon carries the accent.appThemeColor = darker.valueappBackgroundColor = dark.valueappFavicon = '/favicon.ico'appTouchIcon = '/icons/apple-touch-icon.png'appIcons = [{ src = '/icons/icon-192.png' sizes = '192x192' purpose = 'any' }{ src = '/icons/icon-512.png' sizes = '512x512' purpose = 'any' }{ src = '/icons/icon-192.png' sizes = '192x192' purpose = 'maskable' }{ src = '/icons/icon-512.png' sizes = '512x512' purpose = 'maskable' }]// OFFLINE, WITHOUT PERSONAL DATA: the worker keeps only `/start` (components/start.hl, a page// with no data — never `/`, whose copy would hold the signed-in address and identities) and the// shell's assets. The installed app starts at `/start` (appManifest); online the shell's probe// (main.hl) sends it on to `/`, offline the header says it is offline. Every other page offline// is the worker's "Unavailable offline" (README "PWA").offline = [ Start ]appManifest = { start_url = '/start' }// ---- THE ROUTES: files, the function routes (lib/api.hl), the pages (components/) ----------------routes = [{ pattern = "/favicon.ico" file = "./icons/favicon.ico" headers = { 'Cache-Control' = 'no-cache' } }{ pattern = "/icons/icon-192.png" file = "./icons/icon-192.png" headers = { 'Cache-Control' = 'no-cache' } }{ pattern = "/icons/icon-512.png" file = "./icons/icon-512.png" headers = { 'Cache-Control' = 'no-cache' } }{ pattern = "/icons/apple-touch-icon.png" file = "./icons/apple-touch-icon.png" headers = { 'Cache-Control' = 'no-cache' } }{ pattern = "/icons/icon.svg" file = "./icons/icon.svg" headers = { 'Cache-Control' = 'no-cache' } }{ pattern = "/login" function = appLogin }{ pattern = "/api/exchange" function = apiExchange }{ pattern = "/api/migrate-ids" function = apiMigrateIds }{ pattern = "/api/code" function = apiCode }{ pattern = "/api/selector/identities" function = apiSelectorIdentities }{ pattern = "/api/selector/choose" function = apiSelectorChoose }{ pattern = "/api/invites" function = apiInvites }{ pattern = "/api/invites/list" function = apiInvitesList }{ pattern = "/api/invites/get" function = apiInvitesGet }{ pattern = "/api/invites/revoke" function = apiInvitesRevoke }{ pattern = "/invite/:token" function = inviteLink }{ pattern = "/api/kinds" function = apiKinds }{ pattern = "/api/notify" function = apiNotify }{ pattern = "/selector.js" file = "./selector.js" headers = { 'Cache-Control' = 'no-cache' } }{ pattern = "/avatar.js" file = "./avatar.js" headers = { 'Cache-Control' = 'no-cache' } }// the offline probe of the shell (components/main.hl): 204{ pattern = "/api/online" function = apiOnline }{ pattern = "/api/*" function = notFoundApi }// THE CODE PAGE (ident#20): renders Home through the framework's page render — it needs the server, BY REFERENCE{ pattern = "/code" function = (route, req) => { return codePage(route, req, &server, Home) } }{ pattern = "/signin/:rid/code" function = (route, req) => { return codePage(route, req, &server, Home) } }{ pattern = "/" component = Home }{ pattern = "/start" component = Start }{ pattern = "/signin/:rid" component = Home }{ pattern = "/apps" component = Apps }{ pattern = "/inbox" component = Inbox }{ pattern = "/inbox/:cid" component = AppSettings }]// the mailer's result handlers live on an INSTANCE of lib/mail.hlmail = new Mail()sessionDir = env('IDENT_SESSIONS') != null ? env('IDENT_SESSIONS') : nullport = env('IDENT_PORT') != null ? toNumber(env('IDENT_PORT')) : 8351// THE LISTENER's interface: hl:web reads HL_HOST (or HOST) itself (hybriel#24): 127.0.0.1 on// Byrodin behind nginx; unset = 0.0.0.0 (dev on Loreana).// IDENT_WATCH=0 turns the dev watcher off (the container: a deploy is an rsync + restart,// half-copied .hl files must not be re-analysed); unset = on, as before.watching = env('IDENT_WATCH') != '0'// WHO A PUSHED EVENT IS FOR: "signed out everywhere" reaches every open connection whose// session is the account's (checked before dropUserSessions clears the sessions).audience = {signedOutAll = (accountId, session) => { return session != null && session.user != null && session.user.id == accountId }}// ident's OWN COOKIE NAME. Cookies are per host, not per port: with the default `hlsid` an app// on the same host (tickets on :8350) and ident would overwrite each other's session cookie// → hl:web's `sessionCookie` setting (hybriel#10/#17).sessionCookie = 'identsid'server = new WebFramework(routes = routes, styles = styles, audience = audience, minify = true, port = port, watchMode = watching, sessionCookie = sessionCookie)// SIGN OUT EVERYWHERE deletes every session of the account (ticket #19, lib/accounts.hl dropUserSessions): the// session store is this server'ssessionHooks.dropUser = (userId) => { return dropUserSessions(&server, userId) }
Branches
- mainmain branch
Latest commits
- 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