Files
ak47 573d15d7b4 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.
2026-11-03 22:33:26 -05:00

93 lines
3.0 KiB
Go

// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
// A host diagnostic tool for external Tentacles, independent of the running reader.
package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"strings"
"time"
"git.stormux.org/storm/cthulhu/internal/tentacle"
)
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run() (runError error) {
options := flag.NewFlagSet("host", flag.ContinueOnError)
cgroupParent := options.String("cgroup-parent", "", "delegated cgroup v2 parent for resource containment")
presentation := options.Bool("presentation", false, "grant command speech/braille result capability (prints JSON only)")
interactive := options.Bool("interaction", false, "grant internal form/clipboard/focused-item capability (prints initial JSON only)")
readerSettings := options.Bool("reader-settings", false, "grant declarative reader settings capability (prints initial JSON only; does not modify a reader)")
sessionEvents := options.Bool("session-events", false, "negotiate protocol 1.1 lifecycle transport without desktop services")
remoteGrants := options.String("remote-grants", "", "comma-separated install grants for protocol 1.1; grants do not issue session consent")
command := options.String("command", "echo", "registered command to invoke")
if err := options.Parse(os.Args[1:]); err != nil {
return err
}
if options.NArg() < 1 {
return fmt.Errorf("usage: host [-cgroup-parent /delegated/parent] /absolute/program [arguments...]")
}
var manager *tentacle.Manager
var err error
if *cgroupParent == "" {
manager, err = tentacle.NewManager(tentacle.DefaultLimits(), nil)
} else {
manager, err = tentacle.NewContainedManager(tentacle.DefaultLimits(), nil, *cgroupParent, tentacle.DefaultResourceLimits())
}
if err != nil {
return err
}
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
runError = errors.Join(runError, manager.Shutdown(ctx))
}()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var grants []string
if *remoteGrants != "" {
grants = strings.Split(*remoteGrants, ",")
}
session, err := manager.Start(ctx, tentacle.Spec{SessionEvents: *sessionEvents, RemoteGrants: grants, ID: "local-example", Program: options.Args(), Presentation: *presentation, Interaction: *interactive, ReaderSettings: *readerSettings})
if err != nil {
return err
}
select {
case <-session.Ready():
case <-ctx.Done():
return ctx.Err()
}
if state, err := session.Status(); state != tentacle.Ready {
return fmt.Errorf("startup %s: %v", state, err)
}
if err := session.Activate(); err != nil {
return err
}
pending, err := session.Invoke(ctx, *command, json.RawMessage(`{"text":"Hello, é猫😀é"}`))
if err != nil {
return err
}
select {
case result := <-pending:
if result.Err != nil {
return result.Err
}
fmt.Println(string(result.Value))
return nil
case <-ctx.Done():
return ctx.Err()
}
}