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.
123 lines
3.7 KiB
Go
123 lines
3.7 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package tentaclewire
|
|
|
|
import "slices"
|
|
|
|
const (
|
|
SessionQueueSize = 32
|
|
SessionQueueBytes = 256 * 1024
|
|
)
|
|
|
|
// RemoteGrants are independent install-time permissions, never session consent.
|
|
func RemoteGrants() []string {
|
|
return []string{"output-observe", "remote-present", "keyboard-capture", "keyboard-inject", "braille-stream", "clipboard-exchange"}
|
|
}
|
|
|
|
func ValidRemoteGrants(grants []string) bool {
|
|
seen := map[string]bool{}
|
|
for _, grant := range grants {
|
|
if seen[grant] || !slices.Contains(RemoteGrants(), grant) {
|
|
return false
|
|
}
|
|
seen[grant] = true
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ValidNegotiation keeps the 1.0 prefix contract. In 1.1 APIs are sets and
|
|
// capabilities are permissions: commands and transport APIs confer no access.
|
|
func ValidNegotiation(minor int, apis, capabilities []string) bool {
|
|
if minor == 0 {
|
|
return ValidAPIs(apis, capabilities)
|
|
}
|
|
if minor != 1 {
|
|
return false
|
|
}
|
|
a, c := map[string]bool{}, map[string]bool{}
|
|
for _, api := range apis {
|
|
if a[api] || !slices.Contains([]string{"commands", "presentation", "interaction", "reader-settings", "session-events", "remote-access"}, api) {
|
|
return false
|
|
}
|
|
a[api] = true
|
|
}
|
|
for _, capability := range capabilities {
|
|
if c[capability] || !(slices.Contains(RemoteGrants(), capability) || slices.Contains([]string{"presentation", "interaction", "reader-settings"}, capability)) {
|
|
return false
|
|
}
|
|
c[capability] = true
|
|
}
|
|
if !a["commands"] || !a["session-events"] || a["interaction"] && !a["presentation"] || a["reader-settings"] && !a["interaction"] {
|
|
return false
|
|
}
|
|
for _, api := range []string{"presentation", "interaction", "reader-settings"} {
|
|
if a[api] != c[api] {
|
|
return false
|
|
}
|
|
}
|
|
hasRemote := false
|
|
for _, grant := range RemoteGrants() {
|
|
hasRemote = hasRemote || c[grant]
|
|
}
|
|
return a["remote-access"] == hasRemote
|
|
}
|
|
|
|
// SessionEvent is deliberately limited to lifecycle metadata. Output/input
|
|
// payloads will require their own typed validators before they can be enabled.
|
|
type SessionEvent struct {
|
|
Sequence uint64 `json:"sequence"`
|
|
Kind string `json:"kind"`
|
|
Action string `json:"action,omitempty"`
|
|
Lease string `json:"lease,omitempty"`
|
|
Epoch uint64 `json:"epoch"`
|
|
Role string `json:"role,omitempty"`
|
|
Grants []string `json:"grants,omitempty"`
|
|
Reason string `json:"reason,omitempty"`
|
|
}
|
|
|
|
func ValidRole(role string) bool { return role == "controller" || role == "controlled" }
|
|
|
|
func ValidRoleGrants(role string, grants []string) bool {
|
|
if !ValidRole(role) || !ValidRemoteGrants(grants) || len(grants) == 0 {
|
|
return false
|
|
}
|
|
for _, grant := range grants {
|
|
if role == "controller" && (grant == "output-observe" || grant == "keyboard-inject") || role == "controlled" && (grant == "remote-present" || grant == "keyboard-capture") {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func ValidSessionEvent(event SessionEvent) bool {
|
|
if event.Sequence == 0 || event.Epoch == 0 {
|
|
return false
|
|
}
|
|
switch event.Kind {
|
|
case "remote.action":
|
|
return len(event.Action) == 32 && event.Lease == "" && event.Reason == "" && ValidRoleGrants(event.Role, event.Grants)
|
|
case "remote.revoked":
|
|
return event.Action == "" && len(event.Lease) == 32 && event.Role == "" && len(event.Grants) == 0 && slices.Contains([]string{"closed", "expired", "local", "replaced"}, event.Reason)
|
|
}
|
|
return false
|
|
}
|
|
|
|
type RemoteOpen struct {
|
|
Action string `json:"action"`
|
|
Epoch uint64 `json:"epoch"`
|
|
Role string `json:"role"`
|
|
Grants []string `json:"grants"`
|
|
}
|
|
|
|
type RemoteLease struct {
|
|
Lease string `json:"lease"`
|
|
Epoch uint64 `json:"epoch"`
|
|
}
|
|
|
|
type RemoteOperation struct {
|
|
Lease string `json:"lease"`
|
|
Epoch uint64 `json:"epoch"`
|
|
Sequence uint64 `json:"sequence"`
|
|
}
|