142 lines
8.7 KiB
Markdown
142 lines
8.7 KiB
Markdown
# 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](tentacle-sessions.md#reader-owned-consent-interactions).
|
|
|
|
## 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 Device`
|
|
property. The atom must exist on the server; unknown capability is denied.
|
|
- Its `Device Node` is a bounded `/dev/input/event` path 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](https://github.com/XQuartz/xorg-server/blob/master/Xext/xtest.c).
|
|
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:
|
|
|
|
```sh
|
|
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](../../cthulhu-tentacles/remote/command.md) 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.
|