| D-M2-40-1 |
2026-10-14 |
terminal.read_own is All(HumanSession, ByKind{terminal_session: Any(Self, Owner), host: Auth}): on a host, "self or owner" means "own sessions on a host you can see, owners all"; the list query of M2-44 restricts the rows to the caller's user_id for people who are not owners, so on the host only visibility counts. A terminal_session is visible to its person and to effective owners whatever the requirement (else 404 with not_self), so the Owner gets forbidden not_self on terminal.attach of a foreign session. |
A host has no owner of its own sessions; the requirement can only gate the list, the rows are the use case's filter. |
| D-M2-40-2 |
2026-10-14 |
Fresh step-up evaluates only its own branch in step 8: the quick reattach first (only terminal.attach of a detached session from the same device and login session within terminal.quick_reattach_window, equal allowed), else a fresh step-up (terminal.step_up_max_age, equal allowed, passkey or device_key, not consumed). A console session fails closed until M6-18 adds the passkey rule with userVerification = required. identity.StepUp.ConsumeFresh checks the freshness again on the locked session row (the first domain row lock of the action's transaction) with the rule of authorize (authz.FreshStepUpCheck), never on the actor of the request. |
Two requests with one confirmation both pass authorize; only the lock orders them, so the second opening sees the consumption (08 6.3 point 4, 03 6.3). Console sessions do not exist before M6-01. |
| D-M2-40-3 |
2026-10-14 |
The guards of terminal.open and terminal.attach run after step 8 in the order SR10 server host, SR10 assignee, SR11, SR12; the first failure wins. |
The order of 07 section 5 and 24 6.1 step 2; a server host is closed to every person who is not an owner, assigned or not, so its reason tells the person what would help; a person without a fresh step-up learns step_up_required first. |
| D-M2-40-4 |
2026-10-14 |
The difference of access.changed and the preview effects use TerminalEffective (07 2.12 point 3 with the I6 clause): a switch to personal gives removed: [{kind: host, id}] for every other owner, a switch back added with capabilities: [terminal]. SR10 and SR12 are guards, not part of effective terminal, and never appear in the difference; a state or assignee change still writes no access.changed (D-M2-39-5). |
The declaration raises the rights version (03), so the I6 clause belongs to the view; 04 8.2 reports the additional right itself, and SR10 depends on reported_is_server_host, which raises no rights version. |
| D-M2-40-5 |
2026-10-14 |
I6 is verified in phase 3.1 of every rights change, like I1 (M1-29): for every host with llm_credentials = personal the grants valid at t (not revoked, expires_at after t, active subject, live group); a group grant there is always a violation, a grant to a user other than the holder too; owners do not count. |
user.enable of a person whose grant became dormant before the switch to personal would make a grant to another person valid again; 03 6.8 names only the actions where a violation starts from the request. |
| D-M2-40-6 |
2026-10-14 |
With several violations in one change the problem code is the first of last_owner, i1_violation, i6_violation, and conflicts lists the entries of that code; the deny entry carries the violations of that code (details.violations with host_id for I6). The brief's order (i1, i6, last_owner) is not taken. |
M1-29 already ruled last_owner before i1_violation (without an owner no personal agent can be in order either); keeping that and putting I6 last changes no existing answer. |
| D-M2-40-7 |
2026-10-14 |
setHostLlmCredentials refuses credential_holder_id with none or organization with the new errors[].code not_allowed (documented in HostLlmCredentialsSet and 24 13); a retired host answers 409 conflict; every other state accepts a declaration, and confirmHost (M2-41) sets it again at the confirmation. ApplyLlmDeclaration writes the declaration on a locked host and ends the foreign sessions; the caller audits and appends host.updated, so confirmHost can reuse it with its own audit. |
retired is final (24 3.1); a silently ignored holder would hide a client error. |
| D-M2-40-8 |
2026-10-14 |
RecordSessionUserReport compares a report with the stored one by the set of names and each name's ordered workdirs; an equal report writes nothing and raises no rights version. A different report locks the host, compares again, writes the report with the next host version, ends with access_revoked the sessions of every person whose assigned name vanished and appends host.updated; it writes no host.manage audit entry (host.pair and host.connect audit their own calls). |
A host repeats its report after every reconnect, and a version bump per reconnect would empty every rights cache. |
| D-M2-40-9 |
2026-10-14 |
The rights hook accepts host.pair and host.connect as the action of a declared rights change, although 05 marks their column "Permissions" no. |
03 (grants, paragraph on permission_version) counts reported_session_users as rights state, which only these actions write; 05 rule 1.7 says that actions with "Permissions" yes raise the version, not that no other may. |
| D-M2-40-10 |
2026-10-14 |
updateHost that changes or clears the assignee ends, in its unit of work, the sessions of the old assignee on the host with access_revoked unless the old assignee is an active owner (People.IsActiveOwner, bound to identity.Directory.User without authorize); sessions of owners and of the new assignee stay. A change of the owner status or of reported_is_server_host is M2-46's. |
Only the old assignee can lose SR10 by this change; every other person who is not an owner was already refused by SR10. |
| D-M2-40-11 |
2026-10-14 |
The preview of a grant on a host renders summary.host, then denial, I6 violation, terminal_not_effective, group reach, missing expiry; no abilities and no capabilities line. The cause of terminal_not_effective for a person is the first failing guard (server host, assignee, session user); a group gets one hint with the number of its active members in effect whom SR10 or SR12 would deny, none for 0; no such hint when an I6 violation names the host. |
The abilities and capabilities lines describe levels in the tree, which a host has not; the I6 refusal makes the hint moot. |
| D-M2-40-12 |
2026-10-14 |
07 4.1 gains the reason terminal_no_session_user (SR12), section 5 the rule SR12; 19 SEC-AUD-5 the I6 form {code, host_id, credential_holder_id, principal_ids} of violations. |
The brief names both as present, but the merged chapter lacked them (spec gaps). |
| D-M2-40-13 |
2026-10-14 |
Names that differ from the brief: kernel/authorize.Principal gains StepUpMethodName, StepUpConsumedTime and ClientComponentName (only actor.Actor implements it); the domain mirrors authorize.TerminalFacts as TerminalSessionFacts and takes the action as a string, because authz/domain imports no kernel port; domain.HostFacts gains the stored host facts and stands for the brief's host facts of the guard; ValidateDeclaration returns one kernel/errs field error like M2-39's checks; terminal.SetLlmCredentials carries a Precondition instead of IfMatch int64; actor.Actor stands for actor.Principal; wire.IdentityWrites.Fresh passes the FreshStepUpCheck; ActiveGrantsOnHosts of M2-39 serves for the brief's ActiveGrantsOnHost; the work is built on feat/M2-40-terminal-authz from int/m1-wave15 and not merged locally (task of the workflow over the brief's local merge). |
The merged names and types of M1 and M2-39 win over the brief (Boundaries). |
| D-M2-40-14 |
2026-10-18 |
Port onto main from the integration squash of M2-40 on top of M2-39: German chapter names and labels in code, comments and goldens (requirements_table.go, rightschange.go, sources.go) follow the English chapters; the spec additions of M2-40 to 07, 08, 19 and 24 and the terminal README are carried over in English; the ruling table of M2-40 is its own section; the generated files (REST, SDKs, configuration reference, config schema) are regenerated. Tests: the placeholder stubs of stepup_fresh_test.go come from stepup_new_test.go of main (stubAudit for stubRecorder), decide_rules_test.go no longer expects terminal_session without a visibility rule (M2-40 adds it), and German request reasons in tests are English. |
The English documents on main win (PR lane task); the integration branch still carries the German chapters. |