docs: the Windows port, its inventory struck, its test record and the As built block (P11c)
packaging.md: each row of the Unix-only inventory is struck with where it landed. architecture.md: the local transport and the Windows arms. testing.md: the P11c record with its For-P12 paragraph (R-G); every Windows test is marked unrun until the first Windows job. README.md: the Windows caveat is now "from a source build, not packaged". served.md: the unit's As built block (field 8). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
1 parent
1c3a48899d
commit
2baadb7482
5 files changed
+121
-15
No files matched your search
@@ -248,6 +248,29 @@ request (its task, or while it holds the guard) sets `stopped`, and every
|
||||
later request, on every session, is refused until a restart, which
|
||||
re-verifies the journal.
|
||||
|
||||
## The local transport and the Windows arms (`ledger-mcp::ipc`, `ledger-store::winsec`, P11c, D-62)
|
||||
|
||||
The books server, the host and everything that talks to them meet on
|
||||
channels only this account can open (plan 3.9). `ledger_mcp::ipc` is the
|
||||
one seam: `Endpoint` (a socket in the root's `run/` on Unix; the pipe
|
||||
`\\.\pipe\wanderbooks-<hash of run/>-<name>` on Windows), `Listener`
|
||||
(`bind`, `accept`), `connect` and `connect_std` (blocking, with a read
|
||||
limit), the `Peer` trait (`peer_is_us`: the effective uid on Unix; the
|
||||
user SID of the process at the other end of the pipe on Windows), and
|
||||
`stop_signals`. No other file names a socket or a pipe, a peer check or a
|
||||
signal. HTTP to the server goes through `ledger_host::channel::http_builder`
|
||||
(reqwest's Unix socket or named pipe).
|
||||
|
||||
`ledger_store::winsec` (Windows only) holds the rest of the Windows FFI:
|
||||
the owner-only DACL (`set_owner_only`, `is_private`), the account's SID,
|
||||
the shell's known folders, `MoveFileExW` for the move that must not
|
||||
replace, and the pipe process ids. `ledger_store::lock` is the one file
|
||||
lock seam (std's, which is `flock` and `LockFileEx`). `seccomp` and
|
||||
`close_range` remain Linux-only, doing nothing elsewhere (D-32). Windows
|
||||
CI is the evidence for every arm that cannot run on the Linux machines
|
||||
that wrote it; `cargo check --target x86_64-pc-windows-msvc` (the target is
|
||||
pinned in `mise.toml`) type-checks them, clippy included.
|
||||
|
||||
## bookspec (`bookspec/`, P13)
|
||||
|
||||
bookspec is the formal accounting specification (D-34): every rule stated
|
||||
|
||||
+33
-10
@@ -26,16 +26,39 @@ keyring on every OS (tokens are 0600 files under the data root, unit U0e),
|
||||
so nothing about tokens is OS-specific now. The Windows host is a port,
|
||||
not a `cfg` gap. Today Unix-only, found by grep (2026-10-29):
|
||||
|
||||
- `ledger-host`: `bridge`, `host`, `start`, `opener` and others are
|
||||
- ~~`ledger-host`: `bridge`, `host`, `start`, `opener` and others are
|
||||
`#[cfg(unix)]` modules; `host.rs`/`start.rs` use `setsid`, `umask`,
|
||||
`close_range`, `geteuid`, Unix sockets and `libc` unguarded;
|
||||
`commands.rs` calls `isatty`/`tcgetpgrp`.
|
||||
- `ledger-mcp`: the owner-only socket check reads peer credentials
|
||||
`commands.rs` calls `isatty`/`tcgetpgrp`.~~ Landed (P11c): every module
|
||||
builds on every OS. The channels are `ledger_mcp::ipc` (below); the host
|
||||
starts detached (`DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP`, leaving
|
||||
the opener's job if it lets it) where Unix uses `setsid`, with a
|
||||
Windows environment list in `start.rs::host_env`; `close_range` and the
|
||||
`umask` have nothing to do there (a Windows child inherits no handle it
|
||||
is not given); the terminal checks use `IsTerminal`; the stop signals
|
||||
are Ctrl+C, Ctrl+Break, console close and shutdown
|
||||
(`ipc::stop_signals`); a replaced program is told by its size and last
|
||||
write, since a running program cannot be replaced in place.
|
||||
- ~~`ledger-mcp`: the owner-only socket check reads peer credentials
|
||||
(`SO_PEERCRED`, `getpeereid`); `admin`/`local` listen on Unix sockets.
|
||||
Windows needs named pipes with a peer SID check (plan 3.9).
|
||||
- `ledger-store`: `ops.rs` takes `flock` locks; `root.rs` has no owner-only
|
||||
DACL (`set_mode` is a no-op off Unix) and the data root falls back to
|
||||
`%USERPROFILE%`, not the known-folder `%LOCALAPPDATA%`; `refuse.rs`
|
||||
(seccomp) and `renameat2` are Linux-only (macOS needs its own
|
||||
equivalents or `cfg` fences).
|
||||
- The `portable` job runs Windows as allowed-to-fail until that port lands.
|
||||
Windows needs named pipes with a peer SID check (plan 3.9).~~ Landed
|
||||
(P11c): `ledger_mcp::ipc` is the one seam. A pipe is
|
||||
`\\.\pipe\wanderbooks-<hash of the root's run/ folder>-<name>`, made with
|
||||
a DACL of one allow entry (this account's SID), remote clients refused,
|
||||
the first instance made once, and 16 idle instances waiting; each end
|
||||
checks the other's process (`GetNamedPipeClientProcessId` /
|
||||
`GetNamedPipeServerProcessId`, then that process's token user) against
|
||||
its own SID. Clients open with `SECURITY_IDENTIFICATION`.
|
||||
- ~~`ledger-store`: `ops.rs` takes `flock` locks; `root.rs` has no
|
||||
owner-only DACL (`set_mode` is a no-op off Unix) and the data root falls
|
||||
back to `%USERPROFILE%`, not the known-folder `%LOCALAPPDATA%`;
|
||||
`refuse.rs` (seccomp) and `renameat2` are Linux-only (macOS needs its
|
||||
own equivalents or `cfg` fences).~~ Landed (P11c): `lock.rs` is the one
|
||||
lock seam (std's `try_lock`, `LockFileEx` on Windows); `winsec.rs` sets
|
||||
and reads the owner-only DACL and finds the profile and `LocalAppData`
|
||||
from the shell's known folders; `rename_new` is `MoveFileExW` without
|
||||
the replace flag. `refuse.rs` (seccomp, a test fault injector) and the
|
||||
host's `close_range` stay Linux-only on purpose (D-32: nothing here
|
||||
confines a process, so nothing is promised where they are absent).
|
||||
macOS, which has no `renameat2`, still looks before it moves.
|
||||
- The `portable` job runs Windows as a gate (`experimental: false`), since the port landed (P11c).
|
||||
@@ -3669,3 +3669,61 @@ it); the digest's `since_seq` comparison (`>`), its `as_of` bound, the
|
||||
`zero_net_accounts` branch and the `never_reconciled` split;
|
||||
`report_status`'s three open conditions; the open-drafts refusal's
|
||||
`<= as_of` and its decision; the attention `awaiting_evidence` branch.
|
||||
|
||||
## P11c: the native Windows port (2026-10-29, D-62)
|
||||
|
||||
The Windows arms were written on Linux and type-checked and linted for
|
||||
`x86_64-pc-windows-msvc` (`cargo check` and `cargo clippy --workspace
|
||||
--all-targets --target x86_64-pc-windows-msvc -D warnings`, both clean);
|
||||
they have not run. The `portable (windows-latest)` job, now a gate, is the
|
||||
evidence, and its first run is the first run of every test marked
|
||||
"Windows" below.
|
||||
|
||||
- **`ledger-store` `tests/windows_root.rs`** (Windows only):
|
||||
`the_default_root_sits_under_local_app_data`,
|
||||
`setup_makes_a_private_root_and_its_folders` (the DACL of the root and of
|
||||
`books`, `run`, `legacy`), `a_root_others_can_open_is_refused_and_setup_makes_it_private_again`
|
||||
(Everyone granted with `icacls`, then refused, then tightened),
|
||||
`what_is_made_in_a_private_folder_is_private` (inheritance),
|
||||
`a_file_made_owner_only_is_private_and_a_loosened_one_is_not`,
|
||||
`a_rename_never_goes_over_what_is_there`,
|
||||
`this_process_runs_as_the_account_that_the_token_says`.
|
||||
- **`ledger-store` `src/ops.rs`:** `a_viewer_s_presence_is_a_shared_lock_others_can_see`
|
||||
(every OS; the lock seam).
|
||||
- **`ledger-mcp` `src/ipc.rs`** (every OS):
|
||||
`a_listener_and_a_client_talk_and_each_knows_the_other_is_us`,
|
||||
`a_blocking_client_times_out_and_reads_the_end_when_the_server_goes`,
|
||||
`many_clients_at_once_all_connect` (48 at once: the idle pool),
|
||||
`nothing_listening_is_not_found`; Windows only:
|
||||
`a_pipe_is_per_root_and_cannot_be_taken_twice`.
|
||||
`admin.rs`'s fake-server tests (`an_answer_is_one_line_of_at_most_four_mib`,
|
||||
`no_server_means_nothing_was_sent`) now run over `ipc` on every OS.
|
||||
- **`ledger-host` `tests/windows_pipe.rs`:** `a_second_user_cannot_open_the_pipe`
|
||||
(Windows only). A second account cannot be made on the runner, so it
|
||||
checks the two things that keep one out: the pipe's DACL names this
|
||||
account alone, and a SYSTEM process (`services.exe`) is not taken for
|
||||
this account by the SID check.
|
||||
- **`ledger-host`:** `start.rs` `windows_tests`, `host.rs`
|
||||
`a_folder_the_host_can_write_isnt_a_sandbox` and
|
||||
`only_a_file_has_an_identity_and_a_replaced_one_another` (Windows
|
||||
variants of the Unix tests of the same names).
|
||||
- **The over-the-wire suite** (`ledger-cli` `tests/common/mod.rs`) now
|
||||
runs on Windows: `Home` finds its root by `default_root`, children get
|
||||
`clean_env`, `mcp_http` reaches the server over its pipe, folders are
|
||||
made private by `private_folder`. Tests that signal processes
|
||||
(`libc::kill`), use Unix sockets, modes, symlinks or `/proc` stay
|
||||
`cfg(unix)` / `cfg(target_os = "linux")`. `one_executable.rs`'s
|
||||
`without_local_sockets_the_modes_that_hold_the_books_say_so` is gone (the
|
||||
modes work); `the_books_server_needs_a_root` runs everywhere.
|
||||
`ledger-gui` `read_only` marks files read-only (not folders) on Windows.
|
||||
|
||||
**For P12 (mutation targets, none run, D-33):** `winsec::is_private`'s
|
||||
three refusals (owner, missing DACL, an allow entry for anyone else) and the
|
||||
ACE types it skips; `ipc::Endpoint::new`'s hash input (a mutant that drops
|
||||
the lower-casing or the prefix strip splits one root into two pipes: found
|
||||
only by two processes spelling the path differently); the Windows
|
||||
`StdStream::read_some`'s deadline comparison and its `BROKEN_PIPE`
|
||||
arm; `Listener`'s `IDLE_INSTANCES` pool (a mutant that makes it 1 passes
|
||||
every test but `many_clients_at_once_all_connect`); `start.rs::host_env`'s
|
||||
list. None of the Windows arms is reachable by `cargo mutants` on Linux:
|
||||
they run on the Windows job only.
|
||||
Reference in new issue
Block a user