hookclient

package
v0.13.23 Latest Latest
Warning

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

Go to latest
Published: Sep 2, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package hookclient is the Epic 10 hook transport (E10.1, G4): the thin poster that `swarm hook <event>` uses to send an authenticated status callback to the daemon socket, plus the daemon-side Decode that reverses it.

A hook invocation reads its per-session authentication from the environment injected at spawn — session id, live token, daemon socket path, and a monotonic sequence — and composes an engine.Callback (FromEnv). Post writes it to the daemon socket; the daemon Decodes it and feeds engine.HandleCallback, which authenticates it (S6/G5) and applies the status. Post/Decode are an inverse pair; the wire encoding is theirs to choose (JSON here). The token is installed per invocation and never persisted, so a hook cannot be spoofed by another local process (ADR-004).

Index

Constants

View Source
const (
	EnvSessionID = "SWARM_SESSION_ID"
	EnvToken     = "SWARM_HOOK_TOKEN"
	// EnvSocket intentionally reuses the daemon's socket variable
	// (daemon.EnvSocket == "SWARM_DAEMON_SOCK") so the hook dials the same socket
	// the daemon serves.
	EnvSocket = "SWARM_DAEMON_SOCK"
	// EnvSequenceFile names the per-session monotonic counter FILE the daemon
	// injects at spawn (G5). Each `swarm hook` invocation atomically increments it
	// to obtain a strictly increasing sequence, so per-event callbacks are never
	// rejected as replays. This is the sole sequence source.
	EnvSequenceFile = "SWARM_HOOK_SEQ_FILE"
	// EnvCapture lists the session adapter's capture=raw events (ADR-010 §6),
	// comma-separated. A hook invocation keeps the CLI's own event body only for an
	// event named here.
	//
	// It is injected for the same reason the token is: the hook runs as a child of
	// the AGENT and knows its event name but not the adapter that declared it, so
	// the core resolves adapter.CaptureEvents once at spawn and hands down the
	// answer. An empty or absent value means this session captures nothing, which is
	// every adapter that implements no capture extension.
	EnvCapture = "SWARM_HOOK_CAPTURE"
)

Environment keys the daemon injects into a session's hook environment at spawn.

View Source
const EnvHookSocket = "SWARM_SHIM_HOOK_SOCK"

EnvHookSocket names the per-session shim hook-socket path injected at spawn -- the hookclient-side twin of EnvSocket. Unset (an old shim, or a daemon that has not yet wired the hook socket) means "no shim hook socket": PostSmart goes straight to Post, unchanged from today.

Variables

This section is empty.

Functions

func CaptureEnv

func CaptureEnv(events []string) string

CaptureEnv encodes capture rows for EnvCapture; CapturesRaw decodes them. They are an inverse pair, like Post/Decode, so the separator is one package's business and not a convention two packages have to agree on by hand.

func CapturesRaw

func CapturesRaw(getenv func(string) string, event string) bool

CapturesRaw reports whether event is one of the capture rows the core declared for this session. It is total: an unset, empty or malformed value captures nothing, which is the safe answer — a body kept for nobody is bytes on the socket, and the daemon's own adapter lookup is what ultimately admits it.

func Decode

func Decode(r io.Reader) (engine.Callback, error)

Decode reads a single callback that Post wrote. It is the daemon-side inverse of Post.

func FromEnv

func FromEnv(getenv func(string) string, event string, payload map[string]string) (engine.Callback, error)

FromEnv composes a Callback from the injected environment plus the event name and payload the hook wiring supplies. getenv abstracts os.Getenv for testing. A missing token fails rather than compose a tokenless (unauthenticatable) callback (S6, client side).

func Post

func Post(socketPath string, cb engine.Callback) error

Post dials the daemon's unix socket and writes cb, then closes the connection.

func PostToShim

func PostToShim(hookSocketPath string, cb engine.Callback) (acked bool, err error)

PostToShim dials hookSocketPath and posts cb with the same JSON encoding Post uses, then waits up to postAckTimeout for the shim's single ack byte.

  • a dial failure returns acked=false with a non-nil err.
  • a successful post with no ack observed before the deadline returns acked=false, err=nil -- reachable, not (yet) confirmed; the caller MAY retry.
  • a successful post whose ack byte arrives returns acked=true, err=nil.

Types

type HookPath

type HookPath string

HookPath names which transport actually carried a post.

const (
	HookPathShim   HookPath = "shim"
	HookPathDaemon HookPath = "daemon"
)

func PostSmart

func PostSmart(hookSocketPath, daemonSocketPath string, cb engine.Callback) (HookPath, error)

PostSmart is the CLI's one entrypoint (requirement 7). hookSocketPath=="" skips straight to the daemon path -- no dial attempt at all, so an old shim never pays a timeout. Otherwise: try PostToShim; a dial failure falls back to the daemon path immediately (no retry against a socket that plainly is not there); an ack-less-but-reachable outcome is retried against the SAME shim socket with the SAME cb up to hookShimPostRetries times before giving up and falling back. The returned HookPath names whichever transport actually carried the post.

Jump to

Keyboard shortcuts

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