supervisor

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 8, 2026 License: MIT Imports: 53 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ValidateLoadedHello

func ValidateLoadedHello(raw []byte) error

ValidateLoadedHello checks that raw is a ClientHello the native-TLS dialer can mirror (parseable by the SAME utls Fingerprinter the dial uses) AND does not offer HTTP/2 in ALPN (ccwrap forces HTTP/1.1; an h2-offering hello would negotiate a protocol ccwrap cannot speak). Used at launch for fail-fast validation of CCWRAP_NATIVE_TLS_HELLO.

Types

type LaunchContext

type LaunchContext struct {
	Options    preflight.Options          // includes the four file-content snapshot fields
	Inspection *settings.InspectionResult // ~/.claude/settings*.json snapshot
	Launch     *preflight.Result          // launch's *Result — for display projection + authBootstrap + identity
	// Launch-scoped toggles (the Live toggles). Not part of the Result — they
	// come from the launcher's --capture-* / --native-tls flags. Carried here so
	// createSession can publish them in-process without a SetRoute RPC.
	CaptureBodies    bool
	CaptureTelemetry bool
	NativeTLS        bool
	NativeTLSHello   []byte
}

LaunchContext is the secret-bearing in-process snapshot the supervisor retains from launch. It is never serialized, never crosses the socket, and never appears in any SwitchOutcome/CLI output. GC'd on supervisor exit.

Inspection captures the on-disk settings snapshot from settings.InspectLaunch. Options carries the launch Options including the four file-content snapshot fields populated by the launcher (cmd/ccwrap composeLaunch) so the resolver can run on switch without reading disk for any file-backed input.

type Supervisor

type Supervisor struct {
	// contains filtered or unexported fields
}

func New

func New(paths app.Paths, idleTimeout time.Duration, launchCtx *LaunchContext) (*Supervisor, error)

func (*Supervisor) Run

func (s *Supervisor) Run(ctx context.Context) error

func (*Supervisor) SetCaptureBodies

func (s *Supervisor) SetCaptureBodies(id string, enable bool) (model.Session, error)

SetCaptureBodies atomically flips the per-session request-body capture flag. Replaces the current posture with one whose live half has captureBodies flipped (withCaptureBodies — same resolved, nothing enumerated, so no identity field like missingAuthEnv can be forgotten) and atomic-Stores it under sess.mu, matching the single-critical-section invariant the publish paths follow so no observer ever sees a split (active=B, public.CaptureBodies =A) pair.

Past bodies remain inspectable: existing RequestRecord.BodyRef entries in the 250-ring still resolve from the bodyStore (which stays alive for the session), and the 256 MB LRU budget continues to govern eviction. Toggling off only gates NEW captures; toggling back on starts capturing the next request.

Idempotent on no-op (same value): no Store, no public mutation, no event; returns the unchanged snapshot. Otherwise emits session_updated so the inspect ribbon's Bodies cell flips without a page reload. Reused by the browser POST /capture/bodies endpoint (CSRF-guarded by the profile token).

func (*Supervisor) SetCaptureTelemetry

func (s *Supervisor) SetCaptureTelemetry(id string, enable bool) (model.Session, error)

SetCaptureTelemetry atomically flips the per-session telemetry-capture flag (the opt-in transparent telemetry MITM). Mirrors SetCaptureBodies via withCaptureTelemetry: replace the posture with one whose live half has the telemetry toggle flipped, atomic-Store under sess.mu (one critical section, so no observer sees a split active/public pair). Idempotent on no-op. Reused by the browser POST /capture/telemetry endpoint (CSRF-guarded by the profile token).

func (*Supervisor) Shutdown

func (s *Supervisor) Shutdown(ctx context.Context) error

func (*Supervisor) StartedAt

func (s *Supervisor) StartedAt() time.Time

func (*Supervisor) SwitchProfile

func (s *Supervisor) SwitchProfile(sessionID, requested string) SwitchOutcome

SwitchProfile re-resolves the session's routing posture under the named profile and publishes the new posture atomically (or refuses, leaking nothing). This is the central correctness gate. Errors are INSIDE the SwitchOutcome — never returned/logged raw across any boundary; every error path runs through sanitizeSwitchError.

Per-session switch mutex (sess.switchMu) serializes concurrent calls so validate → classify → publish cannot race. The mutex is held across the full procedure but is independent of sess.mu (which gates the single- critical-section publish itself).

Fail-closed — the central correctness invariant: any error at any step leaves sess.active / sess.public / transports UNTOUCHED. The prior posture survives intact, pointer-identically.

Steps:

  1. profiles.Load + (*File).Select(requested). Missing-file / unknown / ambiguous-group ⇒ matching SwitchResult + sanitized message; nothing published. inherit-env ⇒ Profile:nil (full launch Options + Inspection + file-content snapshots reused).

  2. Build opts with Profile swapped; preflight.ResolveProfile(opts, inspect). err ⇒ RejectedInvalid via sanitizeSwitchError; nothing published.

  3. preflight.ClassifyTransition(currentLive, candidate). RelaunchNeedsRelaunch ⇒ RefusedNeedsRelaunch + pinned non-secret advice; nothing published.

  4. RelaunchLive ⇒ old.withResolved(newResolved(pre)) (new resolved ⊕ live carried forward) → atomic Store + EAGER deriveInto (one sess.mu.Lock critical section) → drainSupersededTransports(newKey) iff egress key changed → Switched + SafeProfileView(pre).

type SwitchOutcome

type SwitchOutcome struct {
	Result     SwitchResult          `json:"result"`
	Class      model.RelaunchClass   `json:"class,omitempty"`
	View       preflight.ProfileView `json:"view,omitempty"` // ALREADY userinfo-stripped via preflight.SafeProfileView
	ReasonCode string                `json:"reason_code,omitempty"`
	Message    string                `json:"message,omitempty"`
}

SwitchOutcome is the SwitchProfile return value. Errors are INSIDE the outcome — never returned/logged raw across any boundary.

JSON tags use snake_case to match the repo wire convention (model/types.go) and the SwitchOutcomeView mirror type in internal/control. The same outcome shape serializes to the CLI / web UI verbatim, so adding/renaming a field here must be coordinated with the client mirror.

type SwitchResult

type SwitchResult string

SwitchResult is the per-switch outcome enum.

const (
	SwitchResultSwitched             SwitchResult = "switched"
	SwitchResultRefusedNeedsRelaunch SwitchResult = "refused_needs_relaunch"
	SwitchResultRejectedInvalid      SwitchResult = "rejected_invalid"
	SwitchResultNoSuchSession        SwitchResult = "no_such_session"
	SwitchResultNoProfilesFile       SwitchResult = "no_profiles_file"
	SwitchResultUnknownProfile       SwitchResult = "unknown_profile"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL