8.7 KiB
Reader-owned Remote authorization
Implementation increment: 2026-11-03. The reader now has a core consent path,
device-aware sensitive forms, and lock/handoff cancellation. A subsequent
2026-11-04 increment registers the external source-built Remote connection
command and typed consent interactions. There is still no reader desktop service.
This is not a usable Remote release, and physical-keyboard approval remains unverified.
Base revisions: reader testing at 573d15d, Tentacles master at 63e84e9;
both still matched their maintained remote tips when checked for this increment.
Core ownership and lifetime
Every production Tentacle manager is bound to the focus gate's RemotePolicy.
An I38lock secure-input transition or XTerm handoff synchronously cancels the
current policy context. Restoring availability creates a new context. Neither
unlock nor returning from XTerm revives an old dialog, action or lease.
The cancellation path takes no session, registry or output lock. Existing delivery contexts become invalid before session event cleanup can acquire its lock. Cleanup checks the original access object before retiring it; a delayed callback cannot revoke a newer epoch. An already-running open/heartbeat can race with a response, but it cannot restore the cancelled delivery context. Future desktop owners must still recheck context and their own policy at delivery.
The core tentacleRuntime.authorize_remote procedure resolves an active installed
session and role, checks its install grants, reserves the interaction slot, and
captures a session-bound RemoteApproval before opening the consent form.
The form focuses Cancel initially and leaves every permission checkbox unchecked.
It describes direction, relay visibility and the limits of peer identities.
Only explicit approval of a nonempty subset issues the existing remote.action.
The user can decline keyboard and clipboard permissions independently.
Tickets are core objects, not RPC parameters. They bind the session, role,
requested grant ceiling and availability lifetime. Reuse, another session,
changed role, increased grants and an intervening lock/handoff are denied.
Opening a replacement consent dialog retires previous access. Cancel, timeout,
session failure/disable and package revocation cancel pending consent too.
In particular, failed update preparation must not leave an earlier dialog able
to issue a new action. The old unrestricted AuthorizeRemote hook is rejected
by policy-bound managers; plain development hosts retain it for protocol tests.
No child RPC, install grant, certificate review, saved key, relay event or
injected shortcut is itself consent. A typed Interaction.remote can now request
this core procedure for the exact requesting process session while retaining its
already-held interaction slot. Next receives the matching Submission.remote
only after explicit core approval; the external source command also matches the
SDK event stream before opening its connection controls. Interaction completion
revokes only its approved epoch, and bounded child cancellation cleans up pending
inspection before a lease exists. See the public interaction contract.
Sensitive form input
internalUI.OpenLocal uses CommandListener.BeginLocalUI, a separate XI2
keyboard grab on the existing Xlib owner thread. Unsupported backends fail
without falling back to ordinary forms. Ordinary forms retain their core input
path, bindings, learn mode, text editor and clipboard behavior.
Local capture derives the master keyboard from the client's pointer attachment; it does not assume device IDs. It admits an event only when:
- It is the actual XI2 key event for this capture's master keyboard, rather than a core event, an XRecord observation, or a SendEvent event.
- Its enabled source is an attached slave keyboard with no
XTEST Deviceproperty. The atom must exist on the server; unknown capability is denied. - Its
Device Nodeis a bounded/dev/input/eventpath naming a character device. Sources without a device node remain ineligible. - It is a fresh transition in this capture, rather than repeat, duplicate down or unmatched release. A key already held before capture prevents acquisition.
Source metadata is checked on each transition. Device names and timestamp matching play no role. Modifier state is reconstructed from admitted keys, so an injected Shift cannot reverse a local Tab action. Rejected keys do not enter the chord router, compose renderer, fields, clipboard actions or approval. Hierarchy changes cancel capture and retire held-key bookkeeping, including devices whose releases can no longer arrive. X errors during source queries, capture acquisition or release deny access rather than terminating the reader.
The X server marks XTEST devices using a non-deletable property whose setter rejects changes; see the server implementation. This protects routing from the planned core XTEST injection path. It is not an OS security boundary against malicious same-user X clients, privileged device creation, virtual evdev devices, or a compromised X server. A device node alone does not prove physical hardware. Nested servers, Bluetooth keyboards, layouts, accessibility input devices and actual hot unplug still require acceptance work. No Wayland input provenance is claimed. The production injection owner must retain XTEST virtual-device sourcing; it must not retarget fake input to a physical source device.
All transient forms from a Tentacle with remote grants use this path, including certificate trust and Remember/Forget key forms. Their interaction lifetime is bound to the original policy context, so a short lock/unlock transition cannot preserve them even if native polling misses it. Ordinary Tentacle forms remain unchanged. No public form schema flag lets a child choose its own trust level.
Verification and remaining acceptance
From the reader checkout:
GOCACHE=/tmp/cthulhu-go-cache GOPROXY=off go test -race ./internal/tentacle ./internal/tentaclewire ./internal/interaction ./sdk/... -count=1 -timeout=240s
GOCACHE=/tmp/cthulhu-go-cache GOPROXY=off go test -race ./internal/app -run 'Tentacle|InternalUI|ScreenLock|XTermHandoff|Remote|CommandLoop' -count=1 -timeout=180s
python3 -B tests/native_remote_input.py
python3 tests/native_internal_ui.py
python3 tests/test_build_script.py -v
The native input harness uses private Xvfb/D-Bus, ten recovery experiments and
six native Go race tests. It rejects injected navigation, submission, entry,
paste and Escape, rejects pre-held approval, cancels on hierarchy and secure-input
changes, restores ordinary forms, and retains learn-mode and XTerm handoff
regressions. It requires xinput as well as the existing X11 test tools.
The separate internal-form harness covers real ordinary/secret entry, action
menus, emoji and clipboard behavior. Neither harness uses the active reader.
Both SDK process fixtures cover policy cancellation of pending actions and live leases, stale open/heartbeat denial, revocation events, single-use tickets and fresh approval after resuming. The app fixture verifies unchecked grants stay denied and dialogs cannot survive revocation. A held session lock demonstrates that effect-context cancellation does not wait for worker/event cleanup.
Existing host/wire/interaction race suites, focused application regressions,
vet, reader build, shellcheck, build-script tests and the external Tentacles
repository's make check passed. The complete host race suite took about 124
seconds. libXi is now an explicit build/package dependency; no package was
installed, published or reloaded.
The source command is now exposed for disposable-session acceptance. Verify actual local keyboard navigation, approval, cancellation and modifier handling through this capture path on Xorg. Then exercise certificate review and Remember key with physical input. During lock and XTerm handoff, verify immediate disconnect, no revival on return, and no stale trust/approval after a failed update. Recheck ordinary entries, secret masking/paste, learn mode, remapped commands and application key delivery. The source-command procedure supplies the development launch list and focused physical-input checks. There is still no Remote desktop session to launch.
The current lock integration follows the reader's existing I38lock secure mode. Other lockers/session switches are not established by this work. Positive hardware-origin acceptance, audible speech, physical Braille, input injection recovery/collision handling, typed desktop services and two-desktop/NVDA/Orca interoperability remain open. Do not mark Phase B or any release gate complete.