Add Remote session foundations and feasibility checks

Add opt-in Tentacle protocol 1.1, explicit remote grants, lifecycle IPC and expiring core leases in both SDKs. Revoke access before update preparation, including failed updates.

Add transient secret entries, shared Braille cell export and isolated Xorg recovery experiments with focused tests and documented limitations. Production consent, desktop services and live interoperability remain pending.
This commit is contained in:
ak47 committed 2026-11-03 22:33:26 -05:00
1 parent a47253d697
commit 573d15d7b4
45 files changed
+3634 -80

No files matched your search

+122
View File
@@ -0,0 +1,122 @@
# Remote Braille rendering feasibility
Recorded 2026-11-03 against reader `testing` base
`a47253d697c1aa44dea80646cad99a4fe163c7f1` plus the working-tree changes;
liblouis 3.39.0. This is a Phase A prerequisite, not an available Remote feature.
## Result and implementation
The existing helper viewport can translate, pan and route a separate virtual
display without opening BrlAPI or requiring a controlled-side physical display.
`internal/braille/cells.go` adds an internal cell-export operation which shares
the physical writer's Unicode Braille and attribute-mask conversion. The
existing physical writer now calls that shared conversion with the same inputs
and masks as before. There is no new helper mode, reader command, session API,
network connection or installed binary in this increment.
The native tests use real liblouis translation, the existing `viewport.setFrame`,
`pan` and `route` methods, and the new `rawCells` method. They never construct or
open a BrlAPI backend. Each viewport has its own width, offset, pan history,
cursor, frame identity and source-position map. One owner must serialize access
to each viewport; these tests do not establish concurrent liblouis safety.
The eight new tests establish:
- Exact mask conversion for all 256 dot patterns, including eight-dot cells;
attribute bits are ORed in without allowing BRLTTY to translate the raw cell.
- Existing ordinary text retains its BRLTTY translation masks. Invalid UTF-8
and mismatched indicator lengths are rejected.
- A separate four-cell virtual viewport and eight-cell local viewport can share
source text while keeping independent wrapping, panning, routing and width.
- Real contracted output for `the cat`, source routing through the contraction,
partial selection attributes, rejection of routing on wrapped padding, and
pan-left restoration of the earlier page.
- Cursor mapping after contraction, cursor exclusion off the visible page,
and a routable synthetic end-of-line cell. Cursor shape in this test is an
explicitly supplied static `0xc0` mask.
- Replacement width/frame state does not modify the local viewport. Returned
cells are independent snapshots; replacement and clearing leave no old routes
in the viewport. This is not host rejection of a queued stale routing event.
- An explicitly selected computer-Braille table can use the same liblouis path.
Missing tables fail without replacing the existing viewport.
- Raw export rejects untranslated nonblank text and widths outside 1–4,096.
A peer advertising zero must disable its viewport in the future session owner;
zero is not replaced with an invented display width here.
## Remaining boundaries and proposed helper approach
Ordinary uncontracted output currently goes to BRLTTY as text. Its configured
translation table is not reproduced by this increment. The feasibility test sets
`Frame.Contract` to select the existing liblouis path and explicitly supplies
`en-us-comp8.ctb`; that flag does not itself make the chosen table contract.
Production integration needs an explicit virtual translation-table policy and
schema. Silently treating ordinary characters as dots, or assuming this table
matches a user's BRLTTY configuration, would be incorrect.
Likewise, physical cursor shape and blinking are currently rendered by BRLTTY.
The virtual cell operation can apply an explicit cursor mask, but does not
discover or reproduce BRLTTY's cursor preferences. Resolve the virtual cursor
policy before describing these bytes as identical to a user's physical output.
Keep translation and its native failure boundary in the disposable Braille
helper. The next proposed step is an explicit virtual-only helper mode which
does not call `openWithRetry`, with bounded typed requests for width, frame,
panning and routing, and bounded final-cell responses. A separately supervised
virtual helper would avoid coupling remote rendering to physical display
availability or blocking native calls; process overhead and lifecycle behavior
still need testing. No second translator or ASCII lookup table is needed.
Before exposing this through a Tentacle, complete helper protocol/version
validation, cancellation and backpressure; independent physical/virtual helper
supervision; host-issued lease/epoch checks; lock/secure-input/handoff exclusion;
output arbitration; raw-cell receiving/restoration; and stale routing rejection
at delivery. Native AT-SPI routes must remain in the reader. The cell-export
operation alone provides none of those authorization or lifecycle guarantees.
Actual physical displays, non-Latin translation behavior, cursor blinking,
unplug/replug, NVDA display negotiation and Braille input remain unverified.
Phase A and all release gates remain incomplete.
## Automated verification
From the reader checkout:
```sh
GOCACHE=/tmp/cthulhu-go-cache GOPROXY=off go test -race ./internal/braille -count=1 -timeout=60s
GOCACHE=/tmp/cthulhu-go-cache GOPROXY=off go test -race -tags brlapi ./internal/braille -count=1 -timeout=60s
GOCACHE=/tmp/cthulhu-go-cache GOPROXY=off go vet -tags brlapi ./internal/braille
GOCACHE=/tmp/cthulhu-go-cache GOPROXY=off go build -tags brlapi -o /tmp/cthulhu-remote-braille-check ./cmd/cthulhu-braille
```
Both package suites passed, including existing viewport, helper lifecycle,
translation, routing and table checks. Vet and the helper build passed. These
checks neither launch the helper nor connect to the active reader or display.
The adjacent Tentacles repository's `make check` also passed, including its
existing command-host fixtures. Its unchanged Go tests used cached results.
## Pending local Braille regression check
The only changed active path is mask construction in the physical writer.
Human diff review and this physical check remain necessary before submission.
There is no live Remote workflow to test yet. When a display is available:
1. Quit Cthulhu with Cthulhu+Q.
2. Press Ctrl+Escape to enter i3 personal mode. Then press Shift+G to rebuild
and launch the reader and Braille helper. Ctrl+Escape and Shift+G are separate
key presses. The launcher opens a test terminal.
3. In an accessible editor, enter a harmless line longer than the display.
Confirm ordinary uncontracted Braille matches the previous behavior. Pan
right and left; expect the same text pages and no change to keyboard focus.
4. Move the caret, including to the end of the line, and select a few characters.
Expect the usual physical cursor and selection dots, with no extra or missing
dots. Route to a visible character and expect the editor caret at that source
character. Ordinary keyboard editing and speech should remain unchanged.
5. If contraction is normally used, repeat with `the cat` in the usual English
table and then a longer line. Expect the existing contracted cells, cursor
positioning, panning and routing behavior. Restore any test preference.
6. Report the table, display width, exact affected cells/indicator dots, caret
and focus position, speech, and keys that differed from those expectations.
For unexpected results, inspect `~/devel/cthulhu-go.log` before restarting.
These steps check the shared physical writer only; they cannot establish remote
Braille acceptance before the session/helper integration exists.
+104
View File
@@ -0,0 +1,104 @@
# Remote Xorg input feasibility
This is an isolated prototype and a record of limitations, not a Remote input
implementation. No reader input behavior or installed executable changes.
The source baseline is `a47253d697c1aa44dea80646cad99a4fe163c7f1` on `testing`,
with the existing secret-entry work preserved. The branch was checked against
its upstream on 2026-11-03 before these tests were added.
## Run the private checks
From the reader checkout:
```sh
python3 -B tests/native_remote_input.py
```
Requirements are Python 3, Xvfb/xvfb-run, D-Bus/dbus-run-session, Xlib, XTEST,
xdotool, xev, Go and the reader's usual native build dependencies. Missing tools
or failed checks fail the command; they do not count as skipped acceptance.
The launcher starts a 24-bit Xvfb display before private D-Bus, with disposable
runtime/config directories. It never connects the fixture to the desktop
display or the active reader. Invoke it without private fixture arguments.
The Python tests create a focused X window, a capture process and independent
writer/helper processes. Only the writer retains the private control pipe's
write end. The harness kills or stops that writer to simulate a dead or hung
reader, while retaining the ability to reap both fixture children. This models
descriptor ownership; it does not test the real reader's process launch tree.
The prototype helper uses fixed two-byte records, a held-key set and a
three-second monotonic watchdog renewed only by explicit heartbeats. It releases
owned keys on EOF, timeout, malformed input or an already-held-key collision.
The same private session runs three Go race tests against the real Xorg owners:
the new injected-key observation check and the existing internal-form capture
and learn-mode application-isolation tests.
## Evidence recorded on 2026-11-03
All ten private Python experiments and the three native Go race tests passed.
The adjacent Tentacles repository's `make check` also passed, including existing
command-host checks; its unchanged Go package tests used cached results.
- An exclusive `XGrabKeyboard` consumed the test press and release. The focused
application received neither. Killing the grab owner made the grab available
again, and a fresh key reached the application.
- The existing internal-form and learn-mode tests retained their capture and
restoration behavior with the race detector enabled. This does not establish
coexistence with a new Remote mode or a desktop window manager.
- Closing an XTEST client's X connection left its injected Shift down. X
connection lifetime alone is therefore insufficient for injected-key cleanup
on this server.
- Killing the writer released the helper's Shift and Control keys and restored
application input. The initial passing run observed EOF cleanup in about
0.010 seconds. Stopping the writer without closing its descriptor triggered
cleanup in about 3.019 seconds. These are observations from a private Xvfb
run, not hard real-time or physical-desktop recovery guarantees; each run
prints its own timings and enforces a four-second test bound.
- Heartbeats extended the watchdog. Duplicate downs, unmatched ups, fragmented
input ending at EOF and invalid records did not leave fixture-owned keys held.
- A key already down through another client was not adopted or released. A
collision revoked the prototype and released its other owned keys.
- A second XTEST client pressing the same code **after** the helper's down was
not separately represented by the global key bitmap: helper cleanup cleared
that code. The corresponding test records this limitation, not safe collision
handling. No physical key device participates in this test.
- Injected events had `send_event == False`, and the reader's current XRecord
listener delivered them through the ordinary input event stream. Neither that
flag nor the existing event representation proves local physical origin.
## Decisions and remaining gates
An independent reader-owned helper with an exclusive inherited control
descriptor is a viable cleanup direction for the tested failures. It must not
be a child of the network Tentacle or be killed with that Tentacle's process
group before releasing keys. Reader shutdown must close/revoke the helper and
allow bounded cleanup before forced termination. Prevent inherited write ends
in unrelated descendants; otherwise reader death will not produce EOF.
Do not promote this Python fixture to a production helper. It uses synchronous
Xlib calls and blocking test acknowledgements, and has no lease, epoch, sequence,
input-origin or reader-owner integration. A production Go/native helper needs
bounded IPC and X-server failure handling, stale-work invalidation, explicit
error reporting, restart policy and lifecycle tests. Simultaneous helper death
or a blocked X server is not recovered by this experiment.
Trusted input origin remains a release blocker. The existing reader event
schema contains keycode, transition and timestamp but no source device or
core-owned injection identity. Do not grant local consent based on an XRecord
event, `send_event`, or a timing-only match. Investigate device-aware events and
core-owned injection routing, including exact-once target reader commands,
echo suppression and exclusion from consent/clipboard/trust actions.
Physical/injected same-key collisions also remain a blocker. Checking the
global bitmap before a down only handles an already-visible collision, with
a check/inject race; it does not resolve a physical press arriving while a
remote key is held. Do not clear all modifiers or assume the synthetic
two-client result establishes physical-device behavior.
Still pending: Remote capture within the command owner; local escape and
balanced bypass chords; remapping, learn/UI/lock/handoff arbitration; production
helper parent-death and blocked-pipe tests; held-key collisions with actual
devices; Wayland capability behavior; virtual Braille feasibility; and all live
NVDA/Orca, audible-speech and physical-Braille acceptance. Phase A remains open.
There is no live Remote command to launch or user desktop test for this increment.