Accept the eighteen browser, volume, media and launch identities in the input vocabulary and both SDKs. Exercise media-key capture on the private Xorg harness, and permit only the new remember-host checkbox in the connection form integration test. Validated: affected-package race tests, Tentacles make check, private native_remote_input.py and native_remote_keys.py. Physical input, live peer interoperability, speech and Braille acceptance remain pending. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
161 lines
9.3 KiB
Markdown
161 lines
9.3 KiB
Markdown
# Experimental Remote keyboard IPC prerequisite
|
||
|
||
The protocol 1.1 host recognizes `remote.key`, with public Go types and matching
|
||
Python validation. A bounded incoming-key mailbox is attached in production
|
||
for sessions installed with `keyboard-inject`, and the external Remote command
|
||
forwards controller keys when keyboard control is approved for the session.
|
||
See the production attachment section below; live acceptance is pending.
|
||
|
||
## Wire contract
|
||
|
||
One child-to-host request carries one logical transition:
|
||
|
||
```json
|
||
{"lease":"0123456789abcdef0123456789abcdef","epoch":1,"sequence":2,
|
||
"key":{"name":"Control_R","pressed":false}}
|
||
```
|
||
|
||
All fields are required, including boolean `pressed` on release. Field names
|
||
are exact; unknown fields, duplicate keys, nulls, missing values, nonboolean
|
||
press state and invalid numeric identities are rejected. Epoch and sequence
|
||
are positive uint64 values. Lease identifiers have the existing 32-byte bound;
|
||
only the actual token issued to this process can authorize work.
|
||
|
||
`name` identifies one canonical unshifted logical key. It is neither text to
|
||
type nor an X keycode, Windows VK/scan code, script path or command. The bounded
|
||
allowlist matches the external mapper's canonical subset:
|
||
|
||
- Lowercase Latin letter names, top-row digits, F1–F24 and KP_0–KP_9.
|
||
- Left/right Shift, Control, Alt and Super; Menu, lock keys, Pause and Print.
|
||
- Backspace, Tab, Return, Escape and Space; main navigation, Insert and Delete.
|
||
- Keypad Enter, navigation, Begin, Insert, Delete and arithmetic/separator keys.
|
||
- Eighteen browser, volume, media and launch keys (`XF86Back`, `XF86Forward`,
|
||
`XF86Reload`, `XF86Stop`, `XF86Search`, `XF86Favorites`, `XF86HomePage`,
|
||
`XF86AudioMute`, `XF86AudioLowerVolume`, `XF86AudioRaiseVolume`,
|
||
`XF86AudioNext`, `XF86AudioPrev`, `XF86AudioStop`, `XF86AudioPlay`,
|
||
`XF86Mail`, `XF86AudioMedia`, `XF86MyComputer`, `XF86Calculator`), matching
|
||
Windows VK 0xA6–0xB7. Other XF86 keys, such as power and eject, stay unsupported.
|
||
- The eleven explicit punctuation identities supported by the external US
|
||
profile. The owner must resolve the destination layout; this does not grant
|
||
permission to guess a matching physical key or change the global keymap.
|
||
|
||
Aliases such as `Prior`, uppercase text, shifted character names, AltGr,
|
||
dead/IME keys, arbitrary Unicode and unrecognized names remain unsupported.
|
||
For exact case/spelling, see `input.ValidRemoteKeyName` in
|
||
`internal/input/remote_key_names.go`, used by `ValidRemoteKey` in the wire/SDK
|
||
layer. No implicit modifier transitions, text
|
||
insertion or fallback commands are synthesized.
|
||
|
||
Go exposes `RemoteKey`, `RemoteKeyRequest`, `ValidRemoteKey` and
|
||
`ValidRemoteKeyRequest` through `sdk/go/tentacle`. Python exposes
|
||
`valid_remote_key` and `valid_remote_key_request` from
|
||
`cthulhu_tentacle.session`. They validate representation, not consent or native
|
||
availability. Generic `SessionClient.Call`/`call` still permits method-specific
|
||
host errors; callers may use the validators before submitting work. Older hosts
|
||
return unsupported-operation for this addition.
|
||
|
||
## Authorization and admission
|
||
|
||
The host requires the current process session's live controlled-role lease and
|
||
the separately approved `keyboard-inject` grant. Installation grants, observer
|
||
consent, controller role, relay membership and a child's claimed origin cannot
|
||
authorize injection. Parameters contain no local-origin or consent override.
|
||
|
||
`remote.key` shares the strictly increasing operation sequence with heartbeat,
|
||
close and other lease operations. An accepted key advances it without renewing
|
||
the heartbeat deadline. Replayed, foreign, expired or cancelled operations are
|
||
denied. Invalid parameters and missing-owner errors do not consume the lease
|
||
sequence or enqueue work.
|
||
|
||
`Session.EnableRemoteKeys` is a reader API, not a child RPC. It attaches one
|
||
256-item mailbox. Retained queued plus in-flight keys are bounded by 256 items
|
||
and 256 KiB of canonical encoded request bytes, including lease/sequence
|
||
metadata. Receiving a delivery does not return capacity. Its idempotent
|
||
`Release` returns capacity after delivery or discard, only to the original
|
||
lease. A release from a retired worker cannot alter replacement accounting.
|
||
|
||
The RPC result `{}` acknowledges mailbox admission only. It does not acknowledge
|
||
XTEST, application delivery or key recovery. Saturation revokes the lease and
|
||
drains pending work rather than dropping a key-up and continuing. Heartbeat,
|
||
revocation and shutdown do not wait for a native worker.
|
||
|
||
## Ownership and retirement
|
||
|
||
`RemoteKeyDelivery` retains the original lease, operation sequence, typed key
|
||
and cancellation context. A future input owner must check that context and
|
||
`Session.RemoteContext(lease, "keyboard-inject")` immediately before applying
|
||
each effect, alongside current focus, lock/handoff and native policy. Context
|
||
alone does not replace the deadline check. No native callback is invoked while
|
||
the host session mutex is held.
|
||
|
||
Revoke, explicit close, replacement, expiry, policy cancellation, disable and
|
||
session failure cancel accepted work and clear pending keys. Local policy
|
||
cancellation invalidates the delivery context synchronously, before asynchronous
|
||
notification/drain callbacks acquire the session lock. Restoring availability
|
||
never revives the original lease. A new explicit approval creates a new epoch.
|
||
|
||
The native owner must keep its own destination key ledger, resolve repeats and
|
||
lock state, reject physical/injected collisions and guarantee cleanup even if
|
||
the Tentacle, reader or network disappears. Cleanup must not depend on this
|
||
mailbox admitting a last up. The external outgoing encoder's transport write
|
||
acknowledgements and this API's mailbox admission are separate contracts;
|
||
neither establishes actual destination input completion.
|
||
|
||
## Verification and remaining gates
|
||
|
||
Strict representation tests compare Go and Python validators, including both
|
||
transitions, all letter/digit/function ranges, uint64 boundaries, missing/null
|
||
values, exact field names and rejected metadata. Real Go/Python SDK child
|
||
processes exercise owner-unavailable, admission, permission/role/process checks,
|
||
shared heartbeat ordering, replay, malformed requests, capacity retention,
|
||
concurrent idempotent release, overflow and retirement/replacement. The external
|
||
bridge verifies that every canonical mapper result satisfies the public SDK
|
||
contract. None of these tests injects a desktop key.
|
||
|
||
An unconnected [destination-key prerequisite](testing/remote-key-target.md) now
|
||
retains native codes through layout changes and uncertain effects, with a
|
||
quarantined Xorg resolver/backend. It is not attached to this mailbox and supplies
|
||
no physical collision guarantee. A subsequent [source-built helper](testing/remote-input-helper.md)
|
||
now supplies private process-lifetime/EOF/watchdog cleanup. A quarantined
|
||
reader-side Supervisor now launches it with serialized heartbeats and an
|
||
original-epoch cleanup/process-exit barrier; unconfirmed retirement blocks
|
||
replacement. The quarantined `app.run_remote_keyboard` worker now joins this
|
||
supervisor to the authenticated mailbox with a required policy callback; see
|
||
[its lifecycle and native evidence](testing/remote-input-helper.md#quarantined-leasemailbox-worker).
|
||
`start_remote_keyboard` is now its production caller; see below.
|
||
|
||
No capture event API, production Xorg input owner or supervised helper attachment,
|
||
core escape/bypass integration or installed Remote keyboard command is
|
||
provided. Physical-source approval, physical collision/recovery, audible speech,
|
||
Braille and real NVDA/Orca interoperability remain unverified. Private IPC and
|
||
relay checks close no live release gate.
|
||
|
||
This increment passed the full wire/host/both-SDK race suites (host: about
|
||
128 seconds), focused application Remote connection/speech race tests, the
|
||
existing speech regression harness, affected-package vet, a separate reader
|
||
build and Tentacles `make check`. The lifecycle regression now checks malformed
|
||
typed keys and keeps its unsupported-operation check on Braille. No full reader
|
||
suite or native/hardware acceptance was performed; no active reader was replaced.
|
||
|
||
## Production attachment (2026-11-06)
|
||
|
||
Local launch and managed-session startup now call `start_remote_keyboard`
|
||
for every session installed with `keyboard-inject`, next to the speech owner.
|
||
It attaches the mailbox before the child becomes active and runs the existing
|
||
lease/mailbox worker with one `remoteinput.Supervisor` retained for the reader
|
||
lifetime, so the uncertainty latch survives session replacement. The helper is
|
||
`cthulhu-input-helper` beside the reader executable; `build-cthulhu.sh` and both
|
||
Arch packages now build and install it. A missing helper or a session without
|
||
the grant leaves `remote.key` owner-unavailable and does not affect startup.
|
||
|
||
Delivery-time policy adds reader lock, XTerm-handoff suspension and active
|
||
internal reader UI to the worker's lease, grant and deadline checks; a denied
|
||
key revokes the original lease. A clean helper failure retires only that lease
|
||
and is announced; unconfirmed cleanup is announced as requiring a reader
|
||
restart. Real SDK-process tests cover delivery and lock-policy retirement,
|
||
handoff denial before helper launch, and the no-grant and missing-helper cases.
|
||
|
||
This makes injection reachable from an approved controlled session. It does not
|
||
add physical-collision safety, injection provenance, lock/AltGr policy or live
|
||
acceptance, which remain required before keyboard control is declared ready.
|