Documentation
¶
Index ¶
- Constants
- Variables
- func BaseDir() (string, error)
- func CheckAndClean(name string) (bool, error)
- func ContextPinApprovalsPath() (string, error)
- func ContextPinsPath() (string, error)
- func Delete(name string) error
- func EnsureLogDir() error
- func EnsureTelemetryServerDir(stackName, serverName string) error
- func FindOrphan(port int) (int, bool, error)
- func Home() (string, error)
- func HomeOverridden() bool
- func HookPinApprovalsPath() (string, error)
- func HookPinsPath() (string, error)
- func IsRunning(state *DaemonState) bool
- func KillDaemon(state *DaemonState) error
- func LockPath(name string) (string, error)
- func LogDir() (string, error)
- func LogPath(name string) (string, error)
- func PinApprovalsPath(name string) (string, error)
- func PinsDir() (string, error)
- func PinsPath(name string) (string, error)
- func RunsDir() (string, error)
- func Save(state *DaemonState) error
- func SkillPinApprovalsPath(name string) (string, error)
- func SkillPinsPath(name string) (string, error)
- func StacksDir() (string, error)
- func StateDir() (string, error)
- func StatePath(name string) (string, error)
- func TelemetryDir() (string, error)
- func TelemetryServerDir(stackName, serverName string) (string, error)
- func TelemetryServerPath(stackName, serverName, signal string) (string, error)
- func VaultDir() (string, error)
- func VerifyPID(pid int) bool
- func VersionsDir() (string, error)
- func WithLock(name string, timeout time.Duration, fn func() error) error
- func WithLockContext(ctx context.Context, name string, fn func(context.Context) error) error
- type DaemonState
Constants ¶
const DaemonStateSchemaVersion = 2
DaemonStateSchemaVersion is the current schema of the per-stack daemon state file. Version 1 is the pre-versioned file (schema_version absent); version 2 adds the schema_version and home fields. Readers refuse a newer version rather than guess (Article XVII). Advertised by `gridctl info --json`.
const HomeEnv = "GRIDCTL_HOME"
HomeEnv is the environment variable that replaces the home directory for every gridctl-derived path: the ~/.gridctl state tree and the client projection targets (~/.claude, ~/.gemini, ...) alike. It is a $HOME replacement, not a data-dir override: pointing it at /tmp/demo gives a fully isolated instance whose projections land under /tmp/demo/.claude and never touch the real client directories.
The --home global flag is sugar that sets this variable in-process before any path resolves; the daemon child inherits it through os.Environ(), which is why the env var is the primary mechanism.
Variables ¶
var ErrNewerSchema = errors.New("state file written by a newer gridctl")
ErrNewerSchema marks a state file written by a newer gridctl. Callers must refuse to act on it, and above all must not "clean it up": the file belongs to a daemon this binary cannot represent.
Functions ¶
func BaseDir ¶
BaseDir returns the base gridctl directory (<home>/.gridctl). It errors rather than falling back to a relative path when the home cannot be resolved: every caller below joins onto this value, and a destructive operation aimed at a relative ".gridctl" would target the working directory.
func CheckAndClean ¶
CheckAndClean checks if a state file exists and if the process is running. If the process is dead, it removes the state file and returns true (cleaned). If the process is running, it returns false (not cleaned). If no state file exists, it returns false.
func ContextPinApprovalsPath ¶
ContextPinApprovalsPath returns the approvals log for context pins (<home>/.gridctl/pins/approvals/context/global.json). The directory keeps the log disjoint from the pin file and from stack-scoped approval logs.
func ContextPinsPath ¶
ContextPinsPath returns the stackless context pin file (<home>/.gridctl/pins/context/global.json). A subdirectory, not pins/context.json, keeps the file disjoint from a stack literally named "context" (PinsPath("context") would otherwise be the same path).
func EnsureLogDir ¶
func EnsureLogDir() error
EnsureLogDir creates the log directory if it doesn't exist.
func EnsureTelemetryServerDir ¶
EnsureTelemetryServerDir creates the per-server telemetry directory with mode 0700, matching the vault/state convention. Any new directories on the path inherit the same restrictive permissions because lumberjack will not chmod them on its own.
func FindOrphan ¶
FindOrphan looks for an orphan gridctl daemon listening on port — a process that owns the port but has no managed state file (because e.g. an earlier shutdown deleted state mid-way, or the daemon was started in a mode that doesn't write state, like 'serve --foreground').
Returns (pid, true, nil) only when all three signals agree:
- The /health endpoint on port responds 200.
- Exactly one process (after excluding our own PID) holds the TCP listen socket for that port.
- That process's executable basename is "gridctl".
Any ambiguity — health probe fails, no listener, multiple listeners after self-exclusion, or executable name mismatch — returns ok=false so the caller falls through to the legacy behavior rather than acting on guesswork.
func Home ¶
Home resolves the directory gridctl treats as the user's home: GRIDCTL_HOME when set (must be absolute), otherwise the OS home. It never falls back to a relative path: a home that cannot be resolved is an error, so downstream destructive operations (gridctl reset --purge does an os.RemoveAll under this path) can never target the working directory by accident.
func HomeOverridden ¶
func HomeOverridden() bool
HomeOverridden reports whether the active home comes from GRIDCTL_HOME rather than the OS home. Commands that mutate state print a one-line stderr disclosure when this is true.
func HookPinApprovalsPath ¶
HookPinApprovalsPath returns the approvals log for hook pins (<home>/.gridctl/pins/approvals/hooks/global.json).
func HookPinsPath ¶
HookPinsPath returns the stackless hook pin file (<home>/.gridctl/pins/hooks/global.json). A subdirectory, not pins/hooks.json, keeps the file disjoint from a stack literally named "hooks" (PinsPath("hooks") would otherwise be the same path). Hook pins are not stack-scoped. Approval history is HookPinApprovalsPath, which already uses the same layout.
func IsRunning ¶
func IsRunning(state *DaemonState) bool
IsRunning checks if the daemon process is still running.
func KillDaemon ¶
func KillDaemon(state *DaemonState) error
KillDaemon sends SIGTERM to the daemon process, waits up to 5 seconds for graceful shutdown, then sends SIGKILL if the process is still running.
func PinApprovalsPath ¶
PinApprovalsPath returns the approvals log for a stack's tool-schema pins (<home>/.gridctl/pins/approvals/tools/{name}.json). The directory, not a filename suffix, keeps the namespace disjoint from both pin files.
func PinsPath ¶
PinsPath returns the path to the pin file for a stack (<home>/.gridctl/pins/{name}.json).
func RunsDir ¶
RunsDir returns the root directory for opt-in run records (<home>/.gridctl/runs). Subtree layout: <stack>/runs.jsonl plus rotated segments. Stack-level, not per-server.
func Save ¶
func Save(state *DaemonState) error
Save writes a daemon state file, stamping the current schema version and the resolved home the daemon runs under.
func SkillPinApprovalsPath ¶
SkillPinApprovalsPath returns the approvals log for a stack's skill pins (<home>/.gridctl/pins/approvals/skills/{name}.json). The directory, not a filename suffix, keeps the namespace disjoint from both pin files: pin paths do not validate the stack name, so a suffix beside the pin file would also be the pin file of a stack named with that suffix.
func SkillPinsPath ¶
SkillPinsPath returns the path to the skill pin file for a stack (<home>/.gridctl/pins/skills/{name}.json). A subdirectory, not a filename suffix, keeps the namespace disjoint from tool pins: any suffix scheme inside PinsDir would collide with a stack literally named with that suffix (PinsPath("x.skills") == a suffix-based SkillPinsPath("x")). Skill pins track registry documents, not live tool sets, and the two stores version independently.
func TelemetryDir ¶
TelemetryDir returns the root directory for opt-in telemetry persistence (<home>/.gridctl/telemetry). Subtree layout: <stack>/<server>/{logs,metrics,traces}.jsonl.
func TelemetryServerDir ¶
TelemetryServerDir returns the per-server directory under TelemetryDir for the given stack and server.
func TelemetryServerPath ¶
TelemetryServerPath returns the path to a single signal file for a server. signal must be "logs", "metrics", or "traces"; any string is accepted but only those three are produced by the daemon.
func VersionsDir ¶
VersionsDir returns the directory for retained upgrade binaries (<home>/.gridctl/versions). It sits under BaseDir, so GRIDCTL_HOME, --home, and gridctl reset --purge apply.
Types ¶
type DaemonState ¶
type DaemonState struct {
// SchemaVersion identifies the state-file schema. Absent (0) in
// files written before versioning; treated as version 1 on read.
SchemaVersion int `json:"schema_version,omitempty"`
StackName string `json:"stack_name"`
StackFile string `json:"stack_file"`
PID int `json:"pid"`
Port int `json:"port"`
StartedAt time.Time `json:"started_at"`
// Home is the resolved home directory the daemon was started under
// (see Home()). Subcommands compare it against their own resolved
// home so a GRIDCTL_HOME mismatch surfaces as a named warning
// instead of a confusing empty state.
Home string `json:"home,omitempty"`
// AuthToken and AuthHeader carry the gateway's inbound credentials so
// local subcommands can authenticate against the API they already know
// the port of. Empty when gateway.auth is not configured.
//
// The daemon records the token already resolved, because the config
// loader expands ${VAR} references before the value reaches it. The
// alternative — each subcommand loading the stack and expanding it —
// would turn a `gridctl status` into a vault passphrase prompt.
//
// This does place a resolved secret on disk. The state file is written
// 0600 (see SaveDaemonState), matching the vault's own plaintext
// secrets.json and the machine key in pkg/mcpauth, so it is consistent
// with the existing posture rather than a new exposure.
AuthToken string `json:"auth_token,omitempty"`
AuthHeader string `json:"auth_header,omitempty"`
AuthType string `json:"auth_type,omitempty"`
// AuthIssuerHost is the OAuth issuer URL's host, optionally with a port.
// It carries no scheme, path, query, or token. Empty in bearer and api_key modes.
AuthIssuerHost string `json:"auth_issuer_host,omitempty"`
// BinaryVersion is the gridctl version that wrote this file. Empty in
// files written before the field existed. Additive: no schema bump
// (Article IX). Absent files load unchanged.
BinaryVersion string `json:"binary_version,omitempty"`
}
DaemonState represents the state of a running daemon.