More work on tentacles.

This commit is contained in:
Storm Dragon committed 2026-11-03 23:48:31 -05:00
1 parent 573d15d7b4
commit 4cf3335017
29 files changed
+1194 -66

No files matched your search

+41 -15
View File
@@ -22,27 +22,37 @@ const remoteApprovalTimeout = 30 * time.Second
var ErrPermission = errors.New("tentacle permission denied")
// RemoteGrants returns install permissions, never current consent.
func (session *Session) RemoteGrants() []string { return slices.Clone(session.spec.RemoteGrants) }
// Owned by Session.mutex. No network identity or secret belongs here.
type remoteAccess struct {
action string
lease string
epoch uint64
sequence uint64
role string
grants []string
ctx context.Context
cancel context.CancelFunc
deadline time.Time
timer *time.Timer
action string
lease string
epoch uint64
sequence uint64
role string
grants []string
ctx context.Context
cancel context.CancelFunc
deadline time.Time
timer *time.Timer
stopPolicy func() bool
}
// AuthorizeRemote is a core-only hook for a future verified local consent UI.
// It is intentionally not exposed by any child RPC or production command yet.
// Install grants, network readiness and injected shortcuts are not local consent.
// A fresh action revokes the previous lease and consumes a new host-owned epoch.
// AuthorizeRemote is the unrestricted protocol-fixture hook. Reader managers
// use RemotePolicy and AuthorizeRemoteWithApproval instead; child RPCs cannot
// call either authorizer.
func (session *Session) AuthorizeRemote(role string, grants []string) (tentaclewire.SessionEvent, error) {
session.mutex.Lock()
defer session.mutex.Unlock()
if session.manager.remotePolicy != nil {
return tentaclewire.SessionEvent{}, ErrPermission
}
return session.authorize_remote_locked(role, grants, session.outputContext)
}
func (session *Session) authorize_remote_locked(role string, grants []string, parent context.Context) (tentaclewire.SessionEvent, error) {
if session.state != Active || session.ctx.Err() != nil || !session.spec.SessionEvents {
return tentaclewire.SessionEvent{}, ErrUnavailable
}
@@ -79,10 +89,19 @@ func (session *Session) AuthorizeRemote(role string, grants []string) (tentaclew
return tentaclewire.SessionEvent{}, ErrUnavailable
}
session.remoteEpoch++
ctx, cancel := context.WithCancel(session.outputContext)
ctx, cancel := context.WithCancel(parent)
access := &remoteAccess{action: hex.EncodeToString(token[:]), epoch: session.remoteEpoch,
role: role, grants: slices.Clone(grants), ctx: ctx, cancel: cancel}
session.remote = access
// Parent cancellation invalidates delivery immediately. Retirement/event
// cleanup acquires the session lock afterwards and cannot retire a newer epoch.
access.stopPolicy = context.AfterFunc(parent, func() {
session.mutex.Lock()
defer session.mutex.Unlock()
if session.remote == access {
session.revoke_remote_locked("local", true)
}
})
session.refresh_remote_locked(remoteApprovalTimeout)
event := tentaclewire.SessionEvent{Kind: "remote.action", Action: access.action, Epoch: access.epoch, Role: role, Grants: slices.Clone(grants)}
if !session.remote_event_locked(&event) {
@@ -121,12 +140,19 @@ func (session *Session) release_remote_owner() {
}
func (session *Session) revoke_remote_locked(reason string, notify bool) {
if session.remoteApproval != nil {
session.remoteApproval.cancel()
session.remoteApproval = nil
}
access := session.remote
if access == nil {
return
}
session.remote = nil
access.cancel()
if access.stopPolicy != nil {
access.stopPolicy()
}
if access.timer != nil {
access.timer.Stop()
}