@fnndsc/porter 0.5.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.d.ts +10 -1
- package/dist/app.js +188 -16
- package/dist/app.js.map +1 -1
- package/dist/config.d.ts +17 -0
- package/dist/config.js +23 -1
- package/dist/config.js.map +1 -1
- package/dist/greeter.d.ts +15 -1
- package/dist/greeter.js +33 -3
- package/dist/greeter.js.map +1 -1
- package/dist/notes.json +1 -1
- package/dist/porter.d.ts +10 -0
- package/dist/porter.js +110 -1
- package/dist/porter.js.map +1 -1
- package/dist/tokens.d.ts +171 -0
- package/dist/tokens.js +282 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +2 -2
package/dist/notes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"package":"@fnndsc/porter","version":"0.5.5","built":"2026-10-06T01:52:18.061Z","releases":[{"version":"0.5.5","date":"2026-10-05","merge":"9b5699be2dee","bump":"patch","changes":[{"hash":"66bfa85","headline":"A login onto an older daemon restarts it the way the RESTART pill does.","body":"The old daemon is told why and waited out before the new one boots, so a terminal attaches to the new session rather than a port that is closing; and the door now reads the versions a berth carries, so a session restarted once is current and is not restarted again.","internal":false}]},{"version":"0.5.4","date":"2026-10-05","merge":"e8e51d6f6da5","bump":"patch","changes":[{"hash":"9f4c181","headline":"A login never lands on a kernel older than the door's.","body":"A session whose daemon still runs code from before the porter's last upgrade is ended at login and boots afresh on the way in, its saved state kept, so the first command you type is answered by the release that is installed.","internal":false},{"hash":null,"headline":"Updated dependencies [9f4c181] - @fnndsc/calypso@0.19.6","body":"","internal":true}]},{"version":"0.5.3","date":"2026-10-05","merge":"d2a8f2306caa","bump":"patch","changes":[{"hash":"a4aabb7","headline":"A word the porter entry does not know is refused by name instead of starting a second door.","body":"`porter --help` lists the words, `--sessions` says what `--status` says, and a restarted porter adopts the sessions it finds only once it holds its port.","internal":false}]},{"version":"0.5.2","date":"2026-10-05","merge":"ecfe0e091342","bump":"patch","changes":[{"hash":"5a0eef5","headline":"The package ships its release notes, so a session can say what changed.","body":"They are read from CHANGELOG.md at build into dist/notes.json.","internal":false},{"hash":null,"headline":"Updated dependencies [5a0eef5]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [5a0eef5]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [5a0eef5] - @fnndsc/calypso@0.19.4 - @fnndsc/chell@5.10.3 - @fnndsc/menu@0.19.0","body":"","internal":true}]},{"version":"0.5.1","date":"2026-10-04","merge":"8b4c28c80fc2","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [73c0087] - @fnndsc/menu@0.18.0 - @fnndsc/calypso@0.19.2 - @fnndsc/chell@5.10.1","body":"","internal":true}]},{"version":"0.5.0","date":"2026-10-03","merge":"b737b6d5269e","bump":"minor","changes":[{"hash":"33e4f1d","headline":"A session restarts from its own browser and stays logged in.","body":"Porter's new `POST /restart` (cookie-gated: only the cookie's own session) tells the old calypso daemon why, ends it, and boots a fresh one with chell's new `--saved-token` login, which uses the token the identity saved and refuses rather than come up offline. The browser follows the boot on the greeter; a refused saved token ends at the door with the reason. On SIGTERM the daemon sends `closing` (restart, end or stop) to every surface before it exits, and a remote chell prints why it went. `porter --end` leaves `end` as the reason.","internal":false},{"hash":null,"headline":"Updated dependencies [51315ec]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [33e4f1d]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [fe4b6b4] - @fnndsc/calypso@0.19.0 - @fnndsc/menu@0.17.0 - @fnndsc/chell@5.10.0","body":"","internal":true}]},{"version":"0.4.6","date":"2026-10-03","merge":"ff157a55d5ed","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [685ed59] - @fnndsc/calypso@0.18.0 - @fnndsc/menu@0.16.0 - @fnndsc/chell@5.9.8","body":"","internal":true}]},{"version":"0.4.5","date":"2026-10-03","merge":"30fbc2abbc84","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [2826f2b]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [c1cf57e]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [51da853] - @fnndsc/menu@0.15.0 - @fnndsc/calypso@0.17.0 - @fnndsc/chell@5.9.7","body":"","internal":true}]},{"version":"0.4.4","date":"2026-10-01","merge":"0491d58506dd","bump":"patch","changes":[{"hash":"88437cc","headline":"A tab icon: the ChRIS mark on the page's own ground, for the surface and for the door.","body":"","internal":false},{"hash":null,"headline":"Updated dependencies [4864a34] - @fnndsc/menu@0.14.0 - @fnndsc/calypso@0.16.10 - @fnndsc/chell@5.9.6","body":"","internal":true}]},{"version":"0.4.3","date":"2026-10-01","merge":"5625f543f8f6","bump":"patch","changes":[{"hash":"b5bb197","headline":"The greeter settles a boot row where it stands: the outcome of a `[PENDING]` or `[RETRY]` step replaces that row instead of appending beneath it.","body":"","internal":false}]},{"version":"0.4.2","date":"2026-10-01","merge":"1b77ac182c25","bump":"patch","changes":[{"hash":"cbd71cc","headline":"A refused file read says why: the kernel names the status and reason (403 not yours to read, 404 no such file, 502 CUBE could not serve it), `/vfs` answers with them instead of 404 for everything, and an image pane that lost a slice reads the reason out.","body":"`porter --end <who>` ends one session so the next login boots on the current kernel.","internal":false},{"hash":null,"headline":"Updated dependencies [cbd71cc] - @fnndsc/calypso@0.16.9","body":"","internal":true}]},{"version":"0.4.1","date":"2026-09-27","merge":"f3d2d7ed3d72","bump":"patch","changes":[{"hash":"ef542cd","headline":"`PORTER_SOUNDS_DIR` actually answers: the mount hands the route `/sounds/press.mp3` with its leading slash, which the sound match did not allow, so every sound was proxied to the page's own.","body":"Route-level test added.","internal":false}]},{"version":"0.4.0","date":"2026-09-27","merge":"49033a576021","bump":"minor","changes":[{"hash":"c6d8161","headline":"`PORTER_SOUNDS_DIR`: a folder of sounds the porter serves in place of the page's own (`/s/<key>/sounds/<name>.mp3|wav|ogg`, bare names only), so a deployment's original LCARS beeps — never in a package — live outside the install and survive an upgrade.","body":"","internal":false}]},{"version":"0.3.5","date":"2026-09-26","merge":"a1a7cf9a2b2d","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [fbd3f0a]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [fbd3f0a] - @fnndsc/menu@0.13.0 - @fnndsc/chell@5.9.5 - @fnndsc/calypso@0.16.8","body":"","internal":true}]},{"version":"0.3.4","date":"2026-09-26","merge":"70016c84b4f0","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [f571a14]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [d1da8ac]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [0f9ae74]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [d8e160c] - @fnndsc/chell@5.9.4 - @fnndsc/menu@0.12.0 - @fnndsc/calypso@0.16.7","body":"","internal":true}]},{"version":"0.3.3","date":"2026-09-24","merge":"646a384af419","bump":"patch","changes":[{"hash":"98b839f","headline":"A session whose daemon died is no longer proxied to the dead port: a login that boots a new daemon replaces the stale entry, and a mount whose daemon does not answer follows the session to its new berth or sends the browser to the door, instead of a bare 500 ECONNREFUSED.","body":"","internal":false}]},{"version":"0.3.2","date":"2026-09-22","merge":"2356c7db39f7","bump":"patch","changes":[{"hash":"05b9b41","headline":"porter: `porter --status` marks the pid on a gone row as stale.","body":"The berth outlives a daemon killed with -9, and the bare number read as a process the listing had just made. - @fnndsc/calypso@0.16.2","internal":false}]},{"version":"0.3.1","date":"2026-09-22","merge":"c1b0be0099d8","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [dac2d96]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [dac2d96]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [50f8a4e]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [c82f514] - @fnndsc/chell@5.9.1 - @fnndsc/menu@0.11.0 - @fnndsc/calypso@0.16.1","body":"","internal":true}]},{"version":"0.3.0","date":"2026-09-22","merge":"61536c94c899","bump":"minor","changes":[{"hash":"b58f4da","headline":"porter: published.","body":"`npm install -g @fnndsc/porter` brings the whole stack beneath it, and `PORTER_CUBE_URL=… porter` is the full login experience from one install. `@fnndsc/menu` is a declared dependency, since the greeter serves its brain and ANSI modules from where menu is installed.","internal":false},{"hash":"2b18178","headline":"porter: `porter --status` asks for nothing but the state directory.","body":"It read the full configuration first and refused without `PORTER_CUBE_URL`, a fact a listing never uses.","internal":false}]},{"version":"0.2.0","date":"2026-09-21","merge":"576426874567","bump":"minor","changes":[{"hash":"3a8cd1b","headline":"A porter at the door: the mise display manager, slice 3 — the shape without the door.","body":"`apps/porter` logs an operator into one CUBE, trades the password for a token, starts or joins that identity's calypso (`chell --daemon --auth-token-stdin`, the token on stdin, four directories of its own under the porter's state) and mounts the session at `/s/<key>/`: page, assets, `/vfs` and the WebSocket wire proxied to the daemon on loopback with the attach token added by the porter, so the browser holds none. `GET /boot/<key>` streams the session's boot as server-sent events, one per line the daemon wrote, replayed for a late attacher. `POST /sessions` re-checks the password at CUBE every time, even when the session is up.\n\ncalypso: the attach token may ride the upgrade URL (`?token=`), the way `/vfs?token=` already does, so a front that holds the token for a browser can put it on the proxied upgrade and the page attaches with an empty one; and `berth_pathIn(runtimeDir, identity)` names a berth under a runtime directory that is not the process's own.\n\nNo cookie yet: this porter binds loopback. The door — login page, cookie, LOG OUT — is the next slice.","internal":false},{"hash":"4ee180c","headline":"A session outlives its door: the porter's lifecycle.","body":"porter: a restarted porter adopts the sessions its predecessor started, from their berths under the state directory, rather than starting rivals. Idle sessions — no wire open, nothing through the door for `PORTER_IDLE_HOURS` (a day, like the cookie) — are ended by a sweep every minute: the process only; the state directory stays and the next login boots warm. `porter --status` lists the state directory's sessions and whether each answers. `deploy/` carries a systemd unit (a `porter` user, `KillMode=process` so a stopped door keeps its sessions), an env file example, and a Caddyfile for TLS in front. Boot lines kept per session are capped at 2000.\n\ncalypso: a berth records the daemon's pid, so a host that did not start a daemon can still end it.\n\nchell: a daemon off a TTY tolerates EPIPE on its stdout and stderr — the porter that started it may stop first, and a session outlives its door.","internal":false},{"hash":"21f8d21","headline":"The door itself: the porter logs a browser in and argus leaves by the door it came in.","body":"porter: `GET /login` asks for a username and a password; `POST /login` (form or JSON) trades the password for a CUBE token, starts or joins the session, sets a signed HttpOnly SameSite=Lax cookie naming it (Secure behind TLS, a day long by default) and sends the browser to `/s/<key>/?door`. The cookie gates the boot stream, the mount and the wire: a browser reaches only the session its cookie names; without it, the door (302), a refusal (401) or a dropped upgrade. `POST /logout` clears the cookie and leaves the session running. `PORTER_SECRET` signs the cookie; given none, one is made up per start and said so.\n\nargus: a LOG OUT pill beside AUDIO and the theme, in the frame's hue, standing only on a page that came through a door; pressed, it tells the door to forget this browser and goes to the door's login. The session is not touched. Law: a-surface-leaves-by-the-door-it-came-in.","internal":false},{"hash":"1c169d7","headline":"The greeter: the door answers at once, and a boot is watched rather than waited for.","body":"porter: `POST /login` no longer holds the browser for a cold boot. A session already up is entered directly; one that must boot sends the browser to `/greet/<key>`, where the mise brain wakes — the same frames a terminal boot draws, paced by the page — and the daemon's boot rows arrive beneath it as they are written, ANSI and all, from the boot stream. `ready` rests the brain and hands the browser to the session; `failed` says why and offers the door again. The login page wears the same shell with the brain at rest. The porter serves the two modules it draws with from the installed wire package (`/greeter/brain.js`, `/greeter/ansi.js`); a booting identity is pending in the registry and reaches its mount only when its berth answers.\n\nmenu: `@fnndsc/menu/ansi` — the ANSI-to-HTML converter and the console palette, moved from the ARGUS console so a browser greeter renders a boot the way the console renders a transcript; argus keeps the DOM half and re-exports the rest.","internal":false},{"hash":null,"headline":"Updated dependencies [d1a1c0d]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [3a8cd1b]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [1bdf876]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [4ee180c]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [c1178a0]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [5b520c7]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [810f3ba]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [7bba167] - @fnndsc/calypso@0.16.0 - @fnndsc/chell@5.9.0","body":"","internal":true}]}]}
|
|
1
|
+
{"package":"@fnndsc/porter","version":"0.6.0","built":"2026-10-06T18:02:41.893Z","releases":[{"version":"0.6.0","date":"2026-10-06","merge":"1b09efadc872","bump":"minor","changes":[{"hash":"5c16c65","headline":"The door takes a token as well as a password, so a script can come through without one.","body":"A human login can mint a named door token (a JSON login asking for one, or the browser authorising a chell by one-time code on the login page); the door keeps only its hash, with the name and the day it dies (`PORTER_TOKEN_DAYS`, 30 by default), and a script presents it as `Authorization: Bearer`. A session not up boots on the login it saved. Expired and unknown tokens are refused by name, and the journal says who came through by which token.","internal":false},{"hash":"44c75f4","headline":"The porter's own command line lists, revokes and mints door tokens.","body":"`porter --tokens` shows user, name, minted, dies and last used; `porter --revoke <user> [<name>]` ends one or all of a user's; `porter --mint <user> --name <name>` mints one by the operator's hand, shown once, for a service identity or a hand-over. A running door notices a store changed under it on the token's next use, so a revocation takes effect without a restart.","internal":false},{"hash":"b4a7674","headline":"A holder can see and end their own door tokens.","body":"A token login answers whose token it was; `GET /auth/tokens` lists the identity's tokens by one of them and `DELETE /auth/token` revokes the token in hand, which is what `chell auth tokens` and `chell auth logout` call.","internal":false},{"hash":null,"headline":"Updated dependencies [b4a7674]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [8ae54c6]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [eea6238] - @fnndsc/chell@5.11.0","body":"","internal":true}]},{"version":"0.5.5","date":"2026-10-05","merge":"9b5699be2dee","bump":"patch","changes":[{"hash":"66bfa85","headline":"A login onto an older daemon restarts it the way the RESTART pill does.","body":"The old daemon is told why and waited out before the new one boots, so a terminal attaches to the new session rather than a port that is closing; and the door now reads the versions a berth carries, so a session restarted once is current and is not restarted again.","internal":false}]},{"version":"0.5.4","date":"2026-10-05","merge":"e8e51d6f6da5","bump":"patch","changes":[{"hash":"9f4c181","headline":"A login never lands on a kernel older than the door's.","body":"A session whose daemon still runs code from before the porter's last upgrade is ended at login and boots afresh on the way in, its saved state kept, so the first command you type is answered by the release that is installed.","internal":false},{"hash":null,"headline":"Updated dependencies [9f4c181] - @fnndsc/calypso@0.19.6","body":"","internal":true}]},{"version":"0.5.3","date":"2026-10-05","merge":"d2a8f2306caa","bump":"patch","changes":[{"hash":"a4aabb7","headline":"A word the porter entry does not know is refused by name instead of starting a second door.","body":"`porter --help` lists the words, `--sessions` says what `--status` says, and a restarted porter adopts the sessions it finds only once it holds its port.","internal":false}]},{"version":"0.5.2","date":"2026-10-05","merge":"ecfe0e091342","bump":"patch","changes":[{"hash":"5a0eef5","headline":"The package ships its release notes, so a session can say what changed.","body":"They are read from CHANGELOG.md at build into dist/notes.json.","internal":false},{"hash":null,"headline":"Updated dependencies [5a0eef5]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [5a0eef5]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [5a0eef5] - @fnndsc/calypso@0.19.4 - @fnndsc/chell@5.10.3 - @fnndsc/menu@0.19.0","body":"","internal":true}]},{"version":"0.5.1","date":"2026-10-04","merge":"8b4c28c80fc2","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [73c0087] - @fnndsc/menu@0.18.0 - @fnndsc/calypso@0.19.2 - @fnndsc/chell@5.10.1","body":"","internal":true}]},{"version":"0.5.0","date":"2026-10-03","merge":"b737b6d5269e","bump":"minor","changes":[{"hash":"33e4f1d","headline":"A session restarts from its own browser and stays logged in.","body":"Porter's new `POST /restart` (cookie-gated: only the cookie's own session) tells the old calypso daemon why, ends it, and boots a fresh one with chell's new `--saved-token` login, which uses the token the identity saved and refuses rather than come up offline. The browser follows the boot on the greeter; a refused saved token ends at the door with the reason. On SIGTERM the daemon sends `closing` (restart, end or stop) to every surface before it exits, and a remote chell prints why it went. `porter --end` leaves `end` as the reason.","internal":false},{"hash":null,"headline":"Updated dependencies [51315ec]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [33e4f1d]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [fe4b6b4] - @fnndsc/calypso@0.19.0 - @fnndsc/menu@0.17.0 - @fnndsc/chell@5.10.0","body":"","internal":true}]},{"version":"0.4.6","date":"2026-10-03","merge":"ff157a55d5ed","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [685ed59] - @fnndsc/calypso@0.18.0 - @fnndsc/menu@0.16.0 - @fnndsc/chell@5.9.8","body":"","internal":true}]},{"version":"0.4.5","date":"2026-10-03","merge":"30fbc2abbc84","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [2826f2b]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [c1cf57e]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [51da853] - @fnndsc/menu@0.15.0 - @fnndsc/calypso@0.17.0 - @fnndsc/chell@5.9.7","body":"","internal":true}]},{"version":"0.4.4","date":"2026-10-01","merge":"0491d58506dd","bump":"patch","changes":[{"hash":"88437cc","headline":"A tab icon: the ChRIS mark on the page's own ground, for the surface and for the door.","body":"","internal":false},{"hash":null,"headline":"Updated dependencies [4864a34] - @fnndsc/menu@0.14.0 - @fnndsc/calypso@0.16.10 - @fnndsc/chell@5.9.6","body":"","internal":true}]},{"version":"0.4.3","date":"2026-10-01","merge":"5625f543f8f6","bump":"patch","changes":[{"hash":"b5bb197","headline":"The greeter settles a boot row where it stands: the outcome of a `[PENDING]` or `[RETRY]` step replaces that row instead of appending beneath it.","body":"","internal":false}]},{"version":"0.4.2","date":"2026-10-01","merge":"1b77ac182c25","bump":"patch","changes":[{"hash":"cbd71cc","headline":"A refused file read says why: the kernel names the status and reason (403 not yours to read, 404 no such file, 502 CUBE could not serve it), `/vfs` answers with them instead of 404 for everything, and an image pane that lost a slice reads the reason out.","body":"`porter --end <who>` ends one session so the next login boots on the current kernel.","internal":false},{"hash":null,"headline":"Updated dependencies [cbd71cc] - @fnndsc/calypso@0.16.9","body":"","internal":true}]},{"version":"0.4.1","date":"2026-09-27","merge":"f3d2d7ed3d72","bump":"patch","changes":[{"hash":"ef542cd","headline":"`PORTER_SOUNDS_DIR` actually answers: the mount hands the route `/sounds/press.mp3` with its leading slash, which the sound match did not allow, so every sound was proxied to the page's own.","body":"Route-level test added.","internal":false}]},{"version":"0.4.0","date":"2026-09-27","merge":"49033a576021","bump":"minor","changes":[{"hash":"c6d8161","headline":"`PORTER_SOUNDS_DIR`: a folder of sounds the porter serves in place of the page's own (`/s/<key>/sounds/<name>.mp3|wav|ogg`, bare names only), so a deployment's original LCARS beeps — never in a package — live outside the install and survive an upgrade.","body":"","internal":false}]},{"version":"0.3.5","date":"2026-09-26","merge":"a1a7cf9a2b2d","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [fbd3f0a]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [fbd3f0a] - @fnndsc/menu@0.13.0 - @fnndsc/chell@5.9.5 - @fnndsc/calypso@0.16.8","body":"","internal":true}]},{"version":"0.3.4","date":"2026-09-26","merge":"70016c84b4f0","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [f571a14]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [d1da8ac]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [0f9ae74]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [d8e160c] - @fnndsc/chell@5.9.4 - @fnndsc/menu@0.12.0 - @fnndsc/calypso@0.16.7","body":"","internal":true}]},{"version":"0.3.3","date":"2026-09-24","merge":"646a384af419","bump":"patch","changes":[{"hash":"98b839f","headline":"A session whose daemon died is no longer proxied to the dead port: a login that boots a new daemon replaces the stale entry, and a mount whose daemon does not answer follows the session to its new berth or sends the browser to the door, instead of a bare 500 ECONNREFUSED.","body":"","internal":false}]},{"version":"0.3.2","date":"2026-09-22","merge":"2356c7db39f7","bump":"patch","changes":[{"hash":"05b9b41","headline":"porter: `porter --status` marks the pid on a gone row as stale.","body":"The berth outlives a daemon killed with -9, and the bare number read as a process the listing had just made. - @fnndsc/calypso@0.16.2","internal":false}]},{"version":"0.3.1","date":"2026-09-22","merge":"c1b0be0099d8","bump":"patch","changes":[{"hash":null,"headline":"Updated dependencies [dac2d96]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [dac2d96]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [50f8a4e]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [c82f514] - @fnndsc/chell@5.9.1 - @fnndsc/menu@0.11.0 - @fnndsc/calypso@0.16.1","body":"","internal":true}]},{"version":"0.3.0","date":"2026-09-22","merge":"61536c94c899","bump":"minor","changes":[{"hash":"b58f4da","headline":"porter: published.","body":"`npm install -g @fnndsc/porter` brings the whole stack beneath it, and `PORTER_CUBE_URL=… porter` is the full login experience from one install. `@fnndsc/menu` is a declared dependency, since the greeter serves its brain and ANSI modules from where menu is installed.","internal":false},{"hash":"2b18178","headline":"porter: `porter --status` asks for nothing but the state directory.","body":"It read the full configuration first and refused without `PORTER_CUBE_URL`, a fact a listing never uses.","internal":false}]},{"version":"0.2.0","date":"2026-09-21","merge":"576426874567","bump":"minor","changes":[{"hash":"3a8cd1b","headline":"A porter at the door: the mise display manager, slice 3 — the shape without the door.","body":"`apps/porter` logs an operator into one CUBE, trades the password for a token, starts or joins that identity's calypso (`chell --daemon --auth-token-stdin`, the token on stdin, four directories of its own under the porter's state) and mounts the session at `/s/<key>/`: page, assets, `/vfs` and the WebSocket wire proxied to the daemon on loopback with the attach token added by the porter, so the browser holds none. `GET /boot/<key>` streams the session's boot as server-sent events, one per line the daemon wrote, replayed for a late attacher. `POST /sessions` re-checks the password at CUBE every time, even when the session is up.\n\ncalypso: the attach token may ride the upgrade URL (`?token=`), the way `/vfs?token=` already does, so a front that holds the token for a browser can put it on the proxied upgrade and the page attaches with an empty one; and `berth_pathIn(runtimeDir, identity)` names a berth under a runtime directory that is not the process's own.\n\nNo cookie yet: this porter binds loopback. The door — login page, cookie, LOG OUT — is the next slice.","internal":false},{"hash":"4ee180c","headline":"A session outlives its door: the porter's lifecycle.","body":"porter: a restarted porter adopts the sessions its predecessor started, from their berths under the state directory, rather than starting rivals. Idle sessions — no wire open, nothing through the door for `PORTER_IDLE_HOURS` (a day, like the cookie) — are ended by a sweep every minute: the process only; the state directory stays and the next login boots warm. `porter --status` lists the state directory's sessions and whether each answers. `deploy/` carries a systemd unit (a `porter` user, `KillMode=process` so a stopped door keeps its sessions), an env file example, and a Caddyfile for TLS in front. Boot lines kept per session are capped at 2000.\n\ncalypso: a berth records the daemon's pid, so a host that did not start a daemon can still end it.\n\nchell: a daemon off a TTY tolerates EPIPE on its stdout and stderr — the porter that started it may stop first, and a session outlives its door.","internal":false},{"hash":"21f8d21","headline":"The door itself: the porter logs a browser in and argus leaves by the door it came in.","body":"porter: `GET /login` asks for a username and a password; `POST /login` (form or JSON) trades the password for a CUBE token, starts or joins the session, sets a signed HttpOnly SameSite=Lax cookie naming it (Secure behind TLS, a day long by default) and sends the browser to `/s/<key>/?door`. The cookie gates the boot stream, the mount and the wire: a browser reaches only the session its cookie names; without it, the door (302), a refusal (401) or a dropped upgrade. `POST /logout` clears the cookie and leaves the session running. `PORTER_SECRET` signs the cookie; given none, one is made up per start and said so.\n\nargus: a LOG OUT pill beside AUDIO and the theme, in the frame's hue, standing only on a page that came through a door; pressed, it tells the door to forget this browser and goes to the door's login. The session is not touched. Law: a-surface-leaves-by-the-door-it-came-in.","internal":false},{"hash":"1c169d7","headline":"The greeter: the door answers at once, and a boot is watched rather than waited for.","body":"porter: `POST /login` no longer holds the browser for a cold boot. A session already up is entered directly; one that must boot sends the browser to `/greet/<key>`, where the mise brain wakes — the same frames a terminal boot draws, paced by the page — and the daemon's boot rows arrive beneath it as they are written, ANSI and all, from the boot stream. `ready` rests the brain and hands the browser to the session; `failed` says why and offers the door again. The login page wears the same shell with the brain at rest. The porter serves the two modules it draws with from the installed wire package (`/greeter/brain.js`, `/greeter/ansi.js`); a booting identity is pending in the registry and reaches its mount only when its berth answers.\n\nmenu: `@fnndsc/menu/ansi` — the ANSI-to-HTML converter and the console palette, moved from the ARGUS console so a browser greeter renders a boot the way the console renders a transcript; argus keeps the DOM half and re-exports the rest.","internal":false},{"hash":null,"headline":"Updated dependencies [d1a1c0d]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [3a8cd1b]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [1bdf876]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [4ee180c]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [c1178a0]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [5b520c7]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [810f3ba]","body":"","internal":true},{"hash":null,"headline":"Updated dependencies [7bba167] - @fnndsc/calypso@0.16.0 - @fnndsc/chell@5.9.0","body":"","internal":true}]}]}
|
package/dist/porter.d.ts
CHANGED
package/dist/porter.js
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
|
-
import { porterConfig_resolve, porterStateDir_resolve, chellEntry_locate } from './config.js';
|
|
11
|
+
import { porterConfig_resolve, porterStateDir_resolve, porterTokensFile_resolve, chellEntry_locate, PORTER_DEFAULT_TOKEN_DAYS } from './config.js';
|
|
12
|
+
import { TokenStore } from './tokens.js';
|
|
13
|
+
import { identity_normalise } from '@fnndsc/calypso/berth';
|
|
12
14
|
import { ProcessHost } from './host/processHost.js';
|
|
13
15
|
import { porterApp_build } from './app.js';
|
|
14
16
|
import { berthKey_compute } from '@fnndsc/calypso/berth';
|
|
@@ -75,11 +77,84 @@ async function session_end(stateDir, who) {
|
|
|
75
77
|
if (ended)
|
|
76
78
|
console.log(' state kept; the next login boots a fresh session on this porter\'s kernel');
|
|
77
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Lists the door tokens the store holds: who, which name, when minted,
|
|
82
|
+
* when it dies, when it last let someone in. There is no plaintext to
|
|
83
|
+
* show; a lost token is revoked and minted again.
|
|
84
|
+
*
|
|
85
|
+
* @param file - The token store.
|
|
86
|
+
*/
|
|
87
|
+
function tokens_print(file) {
|
|
88
|
+
const store = new TokenStore(file, PORTER_DEFAULT_TOKEN_DAYS);
|
|
89
|
+
store.load();
|
|
90
|
+
const tokens = store.list();
|
|
91
|
+
console.log(`tokens: ${file}`);
|
|
92
|
+
if (tokens.length === 0) {
|
|
93
|
+
console.log('no door tokens');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
for (const token of tokens) {
|
|
98
|
+
const dies = Date.parse(token.expires) <= now ? `died ${token.expires.slice(0, 10)}` : `dies ${token.expires.slice(0, 10)}`;
|
|
99
|
+
const used = token.lastUsed === null ? 'never used' : `last used ${token.lastUsed.slice(0, 16).replace('T', ' ')}`;
|
|
100
|
+
console.log(`${token.user.padEnd(18)} ${token.name.padEnd(24)} minted ${token.created.slice(0, 10)} ${dies.padEnd(16)} ${used}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Revokes a user's door tokens: one by name, or every one of theirs. A
|
|
105
|
+
* running porter notices on the token's next use, since it re-reads the
|
|
106
|
+
* store when the file changed under it.
|
|
107
|
+
*
|
|
108
|
+
* @param file - The token store.
|
|
109
|
+
* @param user - The CUBE username.
|
|
110
|
+
* @param name - The token's name, or undefined for all of the user's.
|
|
111
|
+
*/
|
|
112
|
+
function token_revoke(file, user, name) {
|
|
113
|
+
const store = new TokenStore(file, PORTER_DEFAULT_TOKEN_DAYS);
|
|
114
|
+
store.load();
|
|
115
|
+
const gone = store.revoke(user, name);
|
|
116
|
+
if (gone === 0) {
|
|
117
|
+
console.error(`[!] no door token ${name === undefined ? `for ${user}` : `named "${name}" for ${user}`}; 'porter --tokens' lists them`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
console.log(`revoked ${gone} door token${gone === 1 ? '' : 's'} ${name === undefined ? `of ${user}` : `("${name}") of ${user}`}; a script carrying one is refused on its next call`);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Mints a door token for a user by the operator's hand, on the host: the
|
|
124
|
+
* way a service identity gets its key, or a user who cannot reach the
|
|
125
|
+
* login page from anywhere. Printed once; the store keeps the hash.
|
|
126
|
+
*
|
|
127
|
+
* @param env - The environment: the CUBE (`PORTER_CUBE_URL`) names the identity, `PORTER_TOKEN_DAYS` its life.
|
|
128
|
+
* @param user - The CUBE username.
|
|
129
|
+
* @param name - The token's name.
|
|
130
|
+
*/
|
|
131
|
+
function token_mint(env, user, name) {
|
|
132
|
+
const cubeUrl = env['PORTER_CUBE_URL'];
|
|
133
|
+
if (cubeUrl === undefined || cubeUrl.length === 0) {
|
|
134
|
+
console.error('[!] --mint needs PORTER_CUBE_URL: the token is for an identity at one CUBE');
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
const daysText = env['PORTER_TOKEN_DAYS'] ?? String(PORTER_DEFAULT_TOKEN_DAYS);
|
|
138
|
+
const days = Number(daysText);
|
|
139
|
+
if (!Number.isFinite(days) || days <= 0) {
|
|
140
|
+
console.error(`[!] PORTER_TOKEN_DAYS is not a span of days: ${daysText}`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
const store = new TokenStore(porterTokensFile_resolve(env), days);
|
|
144
|
+
store.load();
|
|
145
|
+
const made = store.mint(identity_normalise(user, cubeUrl.endsWith('/') ? cubeUrl : `${cubeUrl}/`), user, name);
|
|
146
|
+
console.log(`minted a door token "${name}" for ${user}; it dies ${made.record.expires.slice(0, 10)} and is shown once:`);
|
|
147
|
+
console.log(made.token);
|
|
148
|
+
console.log('hand it over out of band; the script keeps it in ~/.config/chell/doors/<door-host>.json (0600) or CHELL_DOOR_TOKEN');
|
|
149
|
+
}
|
|
78
150
|
/** The usage, one line per word the entry takes. */
|
|
79
151
|
export const PORTER_USAGE = [
|
|
80
152
|
'porter serve the door (PORTER_CUBE_URL, PORTER_PORT, … from the environment)',
|
|
81
153
|
'porter --status the sessions this porter carries (--sessions says the same)',
|
|
82
154
|
'porter --end <who> end one session: its berth key, user, or user@cube',
|
|
155
|
+
'porter --tokens the door tokens the store holds: user, name, minted, dies, last used',
|
|
156
|
+
'porter --revoke <user> [<name>] revoke one door token by name, or every one of the user\'s',
|
|
157
|
+
'porter --mint <user> --name <name> mint a door token by hand (PORTER_CUBE_URL names the CUBE); shown once',
|
|
83
158
|
'porter --help this',
|
|
84
159
|
].join('\n');
|
|
85
160
|
/**
|
|
@@ -107,6 +182,28 @@ export function porterArgs_parse(argv) {
|
|
|
107
182
|
return { refusal: `--end takes one name ('${rest[1] ?? ''}' is extra)` };
|
|
108
183
|
return { mode: 'end', who };
|
|
109
184
|
}
|
|
185
|
+
if (word === '--tokens') {
|
|
186
|
+
return rest.length === 0 ? { mode: 'tokens' } : { refusal: `--tokens takes no argument ('${rest[0] ?? ''}')` };
|
|
187
|
+
}
|
|
188
|
+
if (word === '--revoke') {
|
|
189
|
+
const user = rest[0];
|
|
190
|
+
if (user === undefined || user.startsWith('--'))
|
|
191
|
+
return { refusal: '--revoke wants <user> [<name>]' };
|
|
192
|
+
if (rest.length > 2)
|
|
193
|
+
return { refusal: `--revoke takes a user and at most a name ('${rest[2] ?? ''}' is extra)` };
|
|
194
|
+
const name = rest[1];
|
|
195
|
+
return name === undefined ? { mode: 'revoke', user } : { mode: 'revoke', user, name };
|
|
196
|
+
}
|
|
197
|
+
if (word === '--mint') {
|
|
198
|
+
const user = rest[0];
|
|
199
|
+
if (user === undefined || user.startsWith('--'))
|
|
200
|
+
return { refusal: '--mint wants <user> --name <name>' };
|
|
201
|
+
if (rest[1] !== '--name' || rest[2] === undefined || rest[2].startsWith('--'))
|
|
202
|
+
return { refusal: '--mint wants --name <name> after the user: the operator names what the token is for' };
|
|
203
|
+
if (rest.length > 3)
|
|
204
|
+
return { refusal: `--mint takes a user and a name ('${rest[3] ?? ''}' is extra)` };
|
|
205
|
+
return { mode: 'mint', user, name: rest[2] };
|
|
206
|
+
}
|
|
110
207
|
return { refusal: `unknown word '${word ?? ''}'` };
|
|
111
208
|
}
|
|
112
209
|
async function porter_start() {
|
|
@@ -127,6 +224,18 @@ async function porter_start() {
|
|
|
127
224
|
await session_end(porterStateDir_resolve(process.env), ask.who);
|
|
128
225
|
return;
|
|
129
226
|
}
|
|
227
|
+
if (ask.mode === 'tokens') {
|
|
228
|
+
tokens_print(porterTokensFile_resolve(process.env));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (ask.mode === 'revoke') {
|
|
232
|
+
token_revoke(porterTokensFile_resolve(process.env), ask.user, ask.name);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (ask.mode === 'mint') {
|
|
236
|
+
token_mint(process.env, ask.user, ask.name);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
130
239
|
let config;
|
|
131
240
|
try {
|
|
132
241
|
config = porterConfig_resolve(process.env);
|
package/dist/porter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"porter.js","sourceRoot":"","sources":["../src/porter.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"porter.js","sourceRoot":"","sources":["../src/porter.ts"],"names":[],"mappings":"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"}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/** What the plaintext starts with, so a token is recognisable in a log or a leak. */
|
|
2
|
+
export declare const DOOR_TOKEN_PREFIX: string;
|
|
3
|
+
/** How long a device code waits to be authorised, in ms. */
|
|
4
|
+
export declare const DEVICE_CODE_LIFE_MS: number;
|
|
5
|
+
/** One token the door knows: everything but the plaintext. */
|
|
6
|
+
export interface DoorToken {
|
|
7
|
+
/** The operator-readable name, unique per identity (`cron@titan`). */
|
|
8
|
+
name: string;
|
|
9
|
+
/** The canonical identity (`user@cubeUrl`) the token logs in as. */
|
|
10
|
+
identity: string;
|
|
11
|
+
/** The CUBE username, for lists and the journal. */
|
|
12
|
+
user: string;
|
|
13
|
+
/** SHA-256 of the plaintext, hex. */
|
|
14
|
+
hash: string;
|
|
15
|
+
/** ISO time it was minted. */
|
|
16
|
+
created: string;
|
|
17
|
+
/** ISO time it dies. */
|
|
18
|
+
expires: string;
|
|
19
|
+
/** ISO time it last let someone in, or null when never. */
|
|
20
|
+
lastUsed: string | null;
|
|
21
|
+
}
|
|
22
|
+
/** What the store answers for a presented token. */
|
|
23
|
+
export type TokenCheck = {
|
|
24
|
+
ok: true;
|
|
25
|
+
record: DoorToken;
|
|
26
|
+
} | {
|
|
27
|
+
ok: false;
|
|
28
|
+
why: 'unknown';
|
|
29
|
+
} | {
|
|
30
|
+
ok: false;
|
|
31
|
+
why: 'expired';
|
|
32
|
+
record: DoorToken;
|
|
33
|
+
};
|
|
34
|
+
/** Makes a fresh plaintext token: 32 random bytes, base64url, prefixed. */
|
|
35
|
+
export declare function token_make(): string;
|
|
36
|
+
/** Hashes a plaintext token the way the store keeps it. */
|
|
37
|
+
export declare function token_hash(plain: string): string;
|
|
38
|
+
/** Whether a string looks like a door token, before any lookup. */
|
|
39
|
+
export declare function token_looksLike(value: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The door's token store: minted, checked, revoked, listed, persisted.
|
|
42
|
+
*/
|
|
43
|
+
export declare class TokenStore {
|
|
44
|
+
private readonly path;
|
|
45
|
+
private readonly days;
|
|
46
|
+
private readonly now;
|
|
47
|
+
private tokens;
|
|
48
|
+
/** The file as last read or written (mtime and size), so a change made by another process is noticed. */
|
|
49
|
+
private stamp;
|
|
50
|
+
/**
|
|
51
|
+
* @param path - The JSON file; its directory is made (0700) when missing.
|
|
52
|
+
* @param days - How long a minted token lives.
|
|
53
|
+
* @param now - The clock, replaceable in tests.
|
|
54
|
+
*/
|
|
55
|
+
constructor(path: string, days: number, now?: () => Date);
|
|
56
|
+
/**
|
|
57
|
+
* Reads the file. A missing file is an empty store; a file that does not
|
|
58
|
+
* parse is refused loudly, since silently starting empty would let every
|
|
59
|
+
* script's key stop working with no word why.
|
|
60
|
+
*/
|
|
61
|
+
load(): void;
|
|
62
|
+
/** The file's mtime and size as one string; empty when there is no file. */
|
|
63
|
+
private stamp_read;
|
|
64
|
+
/**
|
|
65
|
+
* Re-reads the file when another process changed it: the porter's CLI
|
|
66
|
+
* (`--revoke`, `--mint`) writes the store from outside the running door,
|
|
67
|
+
* and a revocation the door did not notice would be no revocation.
|
|
68
|
+
*/
|
|
69
|
+
private refresh;
|
|
70
|
+
/**
|
|
71
|
+
* Mints a token for an identity. A token of the same name for the same
|
|
72
|
+
* identity is replaced, so `login` again on the same host never leaves
|
|
73
|
+
* a forgotten key behind.
|
|
74
|
+
*
|
|
75
|
+
* @param identity - The canonical identity.
|
|
76
|
+
* @param user - The CUBE username.
|
|
77
|
+
* @param name - The operator-readable name.
|
|
78
|
+
* @returns The plaintext, shown once, and the record kept.
|
|
79
|
+
*/
|
|
80
|
+
mint(identity: string, user: string, name: string): {
|
|
81
|
+
token: string;
|
|
82
|
+
record: DoorToken;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Checks a presented plaintext against the store, in constant time per
|
|
86
|
+
* record, and says why when it fails.
|
|
87
|
+
*
|
|
88
|
+
* @param plain - The token as presented.
|
|
89
|
+
* @returns The record when it lets someone in.
|
|
90
|
+
*/
|
|
91
|
+
check(plain: string): TokenCheck;
|
|
92
|
+
/**
|
|
93
|
+
* Notes that a token just let someone in.
|
|
94
|
+
*
|
|
95
|
+
* @param record - The token that did.
|
|
96
|
+
*/
|
|
97
|
+
touch(record: DoorToken): void;
|
|
98
|
+
/**
|
|
99
|
+
* Revokes a user's tokens: one by name, or all of them.
|
|
100
|
+
*
|
|
101
|
+
* @param user - The CUBE username.
|
|
102
|
+
* @param name - The token's name, or undefined for every one of the user's.
|
|
103
|
+
* @returns How many went.
|
|
104
|
+
*/
|
|
105
|
+
revoke(user: string, name?: string): number;
|
|
106
|
+
/** @returns Every token the door knows, as kept (no plaintext exists to show). */
|
|
107
|
+
list(): DoorToken[];
|
|
108
|
+
/** Writes the whole store, mode 0600, through a same-directory atomic rename. */
|
|
109
|
+
private save;
|
|
110
|
+
}
|
|
111
|
+
/** What a device code hands chell once a human authorised it. */
|
|
112
|
+
export interface DeviceGrant {
|
|
113
|
+
token: string;
|
|
114
|
+
user: string;
|
|
115
|
+
name: string;
|
|
116
|
+
expires: string;
|
|
117
|
+
}
|
|
118
|
+
/** Makes a code like `K7PD-3MXQ`. */
|
|
119
|
+
export declare function deviceCode_make(): string;
|
|
120
|
+
/** Normalises a code as a human might type it: upper case, the dash optional. */
|
|
121
|
+
export declare function deviceCode_normalise(typed: string): string;
|
|
122
|
+
/**
|
|
123
|
+
* The device codes the door has out: issued by chell, authorised by a human
|
|
124
|
+
* on the login page, taken once by chell's poll.
|
|
125
|
+
*/
|
|
126
|
+
export declare class DeviceCodes {
|
|
127
|
+
private readonly now;
|
|
128
|
+
private readonly codes;
|
|
129
|
+
constructor(now?: () => number);
|
|
130
|
+
/**
|
|
131
|
+
* Issues a code for a chell on a host.
|
|
132
|
+
*
|
|
133
|
+
* @param host - Where chell runs.
|
|
134
|
+
* @returns The code and when it stops being accepted.
|
|
135
|
+
*/
|
|
136
|
+
issue(host: string): {
|
|
137
|
+
code: string;
|
|
138
|
+
expires: string;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Looks a code up for the login page.
|
|
142
|
+
*
|
|
143
|
+
* @param code - As typed.
|
|
144
|
+
* @returns The host chell runs on, or null when the code is not out.
|
|
145
|
+
*/
|
|
146
|
+
host_of(code: string): string | null;
|
|
147
|
+
/**
|
|
148
|
+
* Authorises a code: a human logged in with it.
|
|
149
|
+
*
|
|
150
|
+
* @param code - As typed.
|
|
151
|
+
* @param grant - What chell gets.
|
|
152
|
+
* @returns Whether the code was waiting.
|
|
153
|
+
*/
|
|
154
|
+
authorise(code: string, grant: DeviceGrant): boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Chell's poll: the grant once, pending while the human has not yet
|
|
157
|
+
* logged in, unknown after the code died or was taken.
|
|
158
|
+
*
|
|
159
|
+
* @param code - As chell was given it.
|
|
160
|
+
*/
|
|
161
|
+
take(code: string): {
|
|
162
|
+
state: 'pending';
|
|
163
|
+
} | {
|
|
164
|
+
state: 'unknown';
|
|
165
|
+
} | {
|
|
166
|
+
state: 'authorised';
|
|
167
|
+
grant: DeviceGrant;
|
|
168
|
+
};
|
|
169
|
+
/** Forgets codes past their life. */
|
|
170
|
+
private sweep;
|
|
171
|
+
}
|