// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package tentacle import ( "context" "encoding/json" "sync" "git.stormux.org/storm/cthulhu/internal/tentaclewire" ) // RemoteKeyDelivery is accepted work, not evidence of native injection. A // reader input owner must recheck Context, RemoteContext(keyboard-inject), // focus/security policy and its own key ledger immediately before delivery. // Revocation must independently release native-owned keys, without relying on // this queue or a final key-up from the network child. type RemoteKeyDelivery struct { Context context.Context Lease tentaclewire.RemoteLease Sequence uint64 Key tentaclewire.RemoteKey // Release returns retained capacity after delivery/discard. Dequeuing does // not free capacity. It is idempotent and scoped to the original lease. Release func() } // EnableRemoteKeys attaches one reader-owned mailbox. It performs no native // setup and grants no authority. No production owner is attached yet, so // remote.key remains unavailable in ordinary reader sessions. func (session *Session) EnableRemoteKeys() <-chan RemoteKeyDelivery { session.mutex.Lock() defer session.mutex.Unlock() if session.remoteKeys == nil { session.remoteKeys = make(chan RemoteKeyDelivery, tentaclewire.RemoteKeyQueueSize) } return session.remoteKeys } func (session *Session) remote_key_locked(params json.RawMessage) (any, *RPCError) { var request tentaclewire.RemoteKeyRequest if decode_strict(params, &request) != nil { return nil, &RPCError{Code: -32602, Message: "Invalid session parameters"} } lease := tentaclewire.RemoteLease{Lease: request.Lease, Epoch: request.Epoch} access := session.remote_access_locked(lease, "keyboard-inject") if access == nil || access.role != "controlled" || request.Sequence <= access.sequence { return nil, &RPCError{Code: -32001, Message: "Session permission denied"} } if session.remoteKeys == nil { return nil, &RPCError{Code: -32601, Message: "Remote keyboard owner unavailable"} } // Account canonical full request bytes, including the lease/sequence. Both // admission counts include queued and in-flight work until Release. data, _ := json.Marshal(request) bytes := len(data) if access.keyItems == tentaclewire.RemoteKeyQueueSize || access.keyBytes+bytes > tentaclewire.RemoteKeyQueueBytes { session.revoke_remote_locked("local", true) return nil, &RPCError{Code: -32002, Message: "Remote keyboard queue full"} } access.keyItems++ access.keyBytes += bytes access.sequence = request.Sequence var released sync.Once delivery := RemoteKeyDelivery{Context: access.ctx, Lease: lease, Sequence: request.Sequence, Key: request.Key, Release: func() { released.Do(func() { session.mutex.Lock() defer session.mutex.Unlock() if session.remote == access { access.keyItems-- access.keyBytes -= bytes } }) }} select { case session.remoteKeys <- delivery: return struct{}{}, nil default: // Losing a release must never leave the input lease active. session.revoke_remote_locked("local", true) return nil, &RPCError{Code: -32002, Message: "Remote keyboard queue full"} } }