Repository navigation
session: a login is a session under its owner - #1035
Conversation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
Review of #1035, round 1 (head ab7da5b, code 34dc3a7)ArchitectureThe shape matches revision 8 and item 55. Each login session has one owner, Net growth is about +2050 production lines (including in-file unit tests and the lockfile) against −250, plus about 440 lines of tests. The largest new pieces are the supervisor's Four defects remain. Each is either a consequence of removing FindingsBLOCKER 1 — A
Before this change, any launch a
The fix needs one rule: a BLOCKER 2 — The compositor's launcher thread holds a mutex across a blocking launch, and the supervisor keeps a dead session's launcher port alive.
The launcher thread locks
The fix:
BLOCKER 3 — The reopen budget is keyed per opener row with one
R2 says only a reopen after a failure is counted, and ordinary logins never are. sshserver holds one session per connection, so the single flag means:
The ordering is also off. The host law tests only one session at a time, so it cannot expose this. The fix:
BLOCKER 4 — The
The PR says "The supervisor checks whose session it is", and the track lists " The fix is one of:
The current form is machinery that exists for its test. BLOCKER 5 — The sshserver half is written against a design main has deleted.
The branch sits on Before this can land, the sshserver changes must be rewritten on main's server, and that is new code for re-review. sshserver had only host tests here, so no guest evidence is lost. Its sign-out on drop, and the "reap ended programs" behaviour #1026 added, need checking together. SUGGESTION — refuse launches in a session that has ended.
SUGGESTION — keep the desktop's reopen from being crowded out.
SUGGESTION — stop the supervisor spinning on a bad link.
A SUGGESTION — fix
The doc says "one per restart of a compositor". A restarted compositor's old control has already hung up and been removed, so the bound counts live controls only. Also, a refused ninth control looks like a session end to a compositor, which then opens a second desktop session. SUGGESTION — replace the roster polling if kill is synchronous.
The test polls the roster with a 20 ms sleep. If the kernel's kill is synchronous, the holder is gone (or a zombie, which Evidence
SEND BACK |
5a03a11 to
c7feb40
Compare
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
ab7da5b to
8644f1c
Compare
- A `login` row holds no launcher of the machine's session (`holds_machine_launcher`): its launches are its sessions' alone. The compositor started with no desktop session opens one, and again at each launch asked for while none is open, saying why it is refused; the supervisor refuses every launch into a session that has ended. A `login` test-runner opens one session for its jobs and hands them its launcher. - A session's launcher acceptor lives in its `Login` alone, so its end closes the port and a launch connected to it reads its end. The screen's row is handed its session's launcher and control minted for each start. The compositor's launcher thread takes a copy of the reopened launcher and holds no lock across a launch (`Namespace::duplicate`). - Reopens are budgeted on failures: a login authority whose sessions failed more than three times in ten seconds is refused its next open, and no open spends anything, so one session's failure or a sibling's sign-out changes no other login. - The last session slot is the desktop's, and an owner that breaks its link is killed, its session ending as a failure. - `session-admin` has no user dimension: the machine has one user, so a login session's `logout` acts on every session. - sshserver's session half is rewritten on #1026's server: a connection opens its session with its first program and signs it out when it ends, and a channel's end asks its program to quit with a hang-up through a handle of its own (`spawn::Ender`), on a host still killing it. - `app_view` loses its machine-session grants arm, since a `login` test-runner's jobs launch in its session. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
… host EXIT=0, --build-only EXIT=0, QEMU regressions EXIT=0, session_lifecycle EXIT=0 at 7455cffe8, libc_children main-red, seven mutations red and restored, metal staged Paths masked.
- A `login` row holds no launcher of the machine's session (`holds_machine_launcher`): its launches are its sessions' alone. The compositor started with no desktop session opens one, and again at each launch asked for while none is open, saying why it is refused; the supervisor refuses every launch into a session that has ended. A `login` test-runner opens one session for its jobs and hands them its launcher. - A session's launcher acceptor lives in its `Login` alone, so its end closes the port and a launch connected to it reads its end. The screen's row is handed its session's launcher and control minted for each start. The compositor's launcher thread takes a copy of the reopened launcher and holds no lock across a launch (`Namespace::duplicate`). - Reopens are budgeted on failures: a login authority whose sessions failed more than three times in ten seconds is refused its next open, and no open spends anything, so one session's failure or a sibling's sign-out changes no other login. - The last session slot is the desktop's, and an owner that breaks its link is killed, its session ending as a failure. - `session-admin` has no user dimension: the machine has one user, so a login session's `logout` acts on every session. - sshserver's session half is rewritten on #1026's server: a connection opens its session with its first program and signs it out when it ends, and a channel's end asks its program to quit with a hang-up through a handle of its own (`spawn::Ender`), on a host still killing it. - `app_view` loses its machine-session grants arm, since a `login` test-runner's jobs launch in its session. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
8644f1c to
4592ce5
Compare
|
Review round 1, answered at
|
Review of #1035, round 2 (head 4592ce5, on #1024's edd1371)ArchitectureThe answer keeps revision 8's shape and removes what round 1 objected to.
The round's delta is +388 −283, and most of it replaces code rather than adding it. Round 1's blockers
The suggestions taken are all correct:
The roster poll stays, with a reason: from the kill alone, the guest cannot tell when the end is published. That is acceptable. Races I checked
FindingsBLOCKER — two sshserver comments contradict ToyOS's behaviour.
SUGGESTION — move the session open off the compositor's loop.
SUGGESTION — don't let the screen's row open a second desktop session.
NOTE — app_view's machine-session grants arm is gone.
Evidence
SEND BACK |
edd1371 to
bfc9b97
Compare
Login sessions were opened by a launch: a `login` row's launch from the machine's session opened one, so the compositor opened a session for each program its taskbar started, nothing ended one, and a detached app was the supervisor's child whatever session it was in. Now each login session has one owner, `/system/bin/session`, which the supervisor starts as its child for it. - The owner accepts each connection on the session's own launcher port and hands it to the supervisor over its link with the connection's badge (`toyos_session::MSG_HANDOVER`). The supervisor serves the launch as it serves its own launcher's, in the link's session, never the badge's (`toyos_supervisor::sessions::handed_over`). - A detached app in a login session is spawned under the place the owner gives the supervisor first on its link, so the owner's end takes it down; only in the machine's session is it the supervisor's. - The supervisor watches every app started in a session, on threads of its own a poller's width each, and tells the owner of each start, with a handle that may quit and kill it, and of each end. - Sign-out (`session::Owner`) asks every app to quit with a hang-up and reports what stays to the session's controls and the supervisor; a cancel keeps the session, a force kills what stays, and the owner exits once nothing is left. Nothing in it reads a clock. - The supervisor opens the desktop's session as it starts the screen's row, and endows it the session's launcher and a control connection, again on a restart while the session lives. Any `login` row may open one on the new `login` port; the compositor does when its session ends, and sshserver for each connection, launching its shell and `shell -c` there detached through `CommandExt::launcher`, and signing it out when the connection goes. - An owner's end other than its sign-out's is the session's failure, and only the next open by the same login authority after one spends the restart budget (`toyos_supervisor::sessions::Reopens`). - `/system/bin/logout`, the one row the build lets receive `session-admin`, lists its user's sessions and signs one out, the supervisor checking whose session it is (`sign_out_target`). - The compositor signs its session out on Ctrl+Alt+Q, shows what the sign-out waits for above the taskbar, and takes Escape as cancel and Enter as force. - `may_start` is a permission check alone; `login` names a login authority. An image with a `login` row declares `session`, which only the supervisor starts. sshserver starts only `shell`. The SDK gains `Connection::into_raw`, an endowable `Connection` and a safe `Namespace::received`; std gains `CommandExt::launcher`, and `toyos::spawn` refuses a helper one. `session_lifecycle` (QEMU x86-64, `tests/images/sessioncase.toml`) holds the desktop's sign-out to its keys and a job's session, opened through test-runner's `login` connector, to the owner's rules; `app_view` opens its grants' session the same way. The owner's unasked end has no guest arm, since nothing kills a process by its number; the kernel's parent rule and the supervisor's host laws cover it. The SSH arms wait for a T14 row. The `lifecycle` port moves to the permission service's stage with its first consumer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
- A `login` row holds no launcher of the machine's session (`holds_machine_launcher`): its launches are its sessions' alone. The compositor started with no desktop session opens one, and again at each launch asked for while none is open, saying why it is refused; the supervisor refuses every launch into a session that has ended. A `login` test-runner opens one session for its jobs and hands them its launcher. - A session's launcher acceptor lives in its `Login` alone, so its end closes the port and a launch connected to it reads its end. The screen's row is handed its session's launcher and control minted for each start. The compositor's launcher thread takes a copy of the reopened launcher and holds no lock across a launch (`Namespace::duplicate`). - Reopens are budgeted on failures: a login authority whose sessions failed more than three times in ten seconds is refused its next open, and no open spends anything, so one session's failure or a sibling's sign-out changes no other login. - The last session slot is the desktop's, and an owner that breaks its link is killed, its session ending as a failure. - `session-admin` has no user dimension: the machine has one user, so a login session's `logout` acts on every session. - sshserver's session half is rewritten on #1026's server: a connection opens its session with its first program and signs it out when it ends, and a channel's end asks its program to quit with a hang-up through a handle of its own (`spawn::Ender`), on a host still killing it. - `app_view` loses its machine-session grants arm, since a `login` test-runner's jobs launch in its session. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
4592ce5 to
5271393
Compare
- The compositor's opens of a session are its launcher thread's: the loop asks for one and takes the control connection it makes off a pipe it watches, so no open is a round trip on the loop. - The screen's row is answered its open desktop session on `login`, as `desktop_for` does, so there is one desktop session at a time. - sshserver's `Program::end` and a channel's end say what they do: ask a hang-up on ToyOS, kill on a host. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
…build-only, session_lifecycle and eight QEMU regressions EXIT=0; libc_children main-red; metal staged Paths masked.
Review of #1035, round 3 (head b3b2ad3, on #1024's bfc9b97)As asked, I looked only at four things: the comment fix, the compositor's opens moving to its launcher thread, Verified
FindingsBLOCKER 1 — A refused open leaves
BLOCKER 2 —
Evidence
SEND BACK |
- The compositor's launcher thread answers every open it is asked, a refusal included, over the channel and pipe a session comes back on, so the loop stops waiting on a refused open and the next launch asks again (`toyos_desktop::Opening`). - The supervisor hands a desktop session again only while its owner runs: one whose end waits to be read is no longer reused, by `desktop_for` or `serve_login` (`toyos_supervisor::sessions::desktop_session`). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q
…--ci host, --build-only, session_lifecycle and compositor QEMU tests EXIT=0; libc_children on this head, on #1024 bfc9b9793 and on main 89f92a4bd, each spawn_children_cap; metal staged Paths masked.
Review of #1035, round 4 (head f3f45a8)As asked, I checked only round 3's two blockers and the code that fixes them. Round 3 blocker 1: a refused open now ends the wait
Round 3 blocker 2: an ended owner's desktop is not handed out again
Evidence (p1b/r4, all at f3f45a8)
Suggestion (optional)
LAND |
Problem
Login sessions were opened by launches:
loginrow's launch from the machine's session opened one. So the compositor opened a session for each program its taskbar started.This is the second part of the process track's stage 5. It is stacked on #1024 (rebased onto
mainatbfc9b9793) and follows the permission design's revision 8, which the coordinator approved; its amendments A–D are named below.Resulting behaviour
One owner per login session,
/system/bin/session, which the supervisor starts as its child.lifecycleport that would announce these moves to the permission service's stage, with its first consumer (B).Signing out, in the owner and with no clock:
Opening sessions:
loginrow may open one on the supervisor's newloginport:shell -cthere, detached, throughCommandExt::launcher(D). The connection's end signs the session out. A channel's end asks its program to quit with a hang-up through a handle of its own (spawn::Ender); on a host build it still kills the program.loginrow holds no launcher of the machine's session. Its launches are its sessions' alone. The compositor, started without a desktop session, opens one, and opens one again for each launch it is asked for while none is open; when that is refused, it says why and starts nothing. The supervisor refuses every launch into a session that has ended. Alogintest-runner opens one session for its jobs and hands them that session's launcher.logout, the one row the build lets receivesession-admin, lists the open sessions and signs one out. It is reached aslaunch logout. The machine has one user, so a login session'slogoutacts on every session; there is no ownership check.The compositor signs its session out on Ctrl+Alt+Q and shows what the sign-out waits for above the taskbar. Escape cancels; Enter forces.
Smaller changes:
may_startis a permission check alone, andloginnow means "a login authority".loginrow must declaresession, which only the supervisor may start (a build gate).shell.Connection::into_raw, an endowableConnectionand a safeNamespace::received.CommandExt::launcher, whichtoyos::spawnrefuses for a helper.Decisions worth a look
WRITEa place needs, and rights only narrow.loginrow is endowed the port however it was started. Where SSH logins come from is owner item 65. Until it is ruled, a detached sshserver opens its sessions from inside the desktop's, whichissues/four-shares-take-a-file-server.mdrecords. Either way it holds no machine launcher.Loginalone. Its end closes the port, so a launch connected to it reads the end. The screen's row is handed its session's launcher and control afresh for each start. The compositor's opens of a session and its launches are its launcher thread's. The loop asks for an open and takes the control connection it makes off a pipe, so no open is a round trip on the loop. The screen's row is answered its open desktop session onlogin, asdesktop_fordoes.Checks and evidence
Round 3, at
f3f45a814. Evidencep1b/r4/andp1b/muts3/: https://github.com/ToyOSOrg/evidence/tree/a7168f805fc5086ab10dc6269ceed3ae0b111deaA refused open is answered. The launcher thread sends it back over the same channel and pipe a session comes back on, so the loop stops waiting and the next launch asks again (
toyos_desktop::Opening, lawa_refused_open_is_asked_again).A desktop session is handed again only while its owner runs, by
desktop_forand byserve_loginalike (desktop_session, lawa_desktop_whose_owner_ended_is_not_handed_again).Mutations, each built, failed by name with exit 101, and restored:
--ci hostexit 0 and--build-onlyexit 0.session_lifecycle,desktop_without_x2apic,the_compositor_drives_the_virtio_gpuanda_window_is_drawn_at_the_scale_and_relaid_at_the_next: exit 0 each.libc_childrenfails onlyspawn_children_capin three runs, each a different form of the guest running out of memory:posix_spawnansweringENOMEM;bfc9b9793: abort 134,Poller::newrefusedResourceExhausted;main89f92a4bd: child 240 ended bySIGSEGV.The form varies run to run and none is this change's.
Metal is staged at
f3f45a814.Round 2, at
b3b2ad355(restacked on #1024'sbfc9b9793). Evidencep1b/r3/: https://github.com/ToyOSOrg/evidence/tree/9345c76a8eb41e837ede2f75de3cbd0fc1782d71--ci hostexit 0 and--build-onlyexit 0.session_lifecycleexit 0. Its desktop arm runs through the launcher thread's opens.a_launch_gives_each_unsent_stdio_slot_a_null,a_usb_key_reaches_the_console,desktop_without_x2apic,the_compositor_drives_the_virtio_gpu,a_window_is_drawn_at_the_scale_and_relaid_at_the_next,stream_holders,stream_exported_to_a_child.libc_children: exit 1 onspawn_children_capalone,main's.p1b/muts2/) carry over, because their files are unchanged since.b3b2ad355.Round 1, at
3dcd5855e.4592ce5f5adds only a fix to thesession_lifecyclejob, which now finds its session by the line that names the holder ending, and one comment. That job was re-run at that tree (7455cffe8). Evidence: https://github.com/ToyOSOrg/evidence/tree/c506f99899c3c1b42a12ddc7fff5cdf9f14889e7 (p1b/r2/,p1b/muts2/)cargo run -- --ci host: exit 0.cargo run -- --build-only: exit 0.session_lifecycle(QEMU x86-64): exit 0 at7455cffe8. The desktop arm and the job's arms pass, now among test-runner's own session. The run at3dcd5855ered only on the job's old guess of its session's number.a_launch_gives_each_unsent_stdio_slot_a_null,a_usb_key_reaches_the_console,desktop_without_x2apic,the_compositor_drives_the_virtio_gpu,a_window_is_drawn_at_the_scale_and_relaid_at_the_next.libc_children: exit 1, onspawn_children_capalone (guest memory), as onmain.a_handed_over_launch_runs_in_its_links_sessionfails;failures_are_budgeted_across_an_authoritys_sessionsfails;a_detached_app_is_placed_under_its_sessions_ownerfails;loginrow holding the machine's launcher:a_login_row_holds_no_machine_launcherfails;session_lifecyclestalls at the desktop's sign-out;session-admin: the build's gate test fails.b9d8a83ef, one commit before the head they were reported against. They are superseded by this round's.process_tree) and the supervisor's host laws.app_viewloses its machine-session grants arm, because alogintest-runner's jobs launch in its session.3dcd5855e: the sharedtestcasesboot,testcases-watchdogandproctreecase.4592ce5f5changes no binary those images carry.Not here (P1c and later)
powerprotocol, with acpiserver's second press.inspectlisting sessions.🤖 Generated with Claude Code
https://claude.ai/code/session_01PXEk8BBMDT8tXu4AnENB9Q