skeleton

package
v0.13.22 Latest Latest
Warning

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

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

Documentation

Overview

Package skeleton is the Epic 8 daemon ASSEMBLY: the wiring that turns the three independently-built layers into the running walking skeleton (GG-1). It composes

  • internal/daemon — the lifecycle authority (flock singleton, crash-safe launch, reconnect-on-restart) that OWNS the client socket;
  • internal/protocol — the client-facing RPC + attach data plane, served on the daemon's own socket via the daemon's ConnHandler knob (no second socket);
  • internal/engine — the status-detection authority, driven by the fallback poll and fed hook callbacks demuxed off that same socket.

It cannot live in internal/daemon: protocol imports daemon, so an assembly there would be an import cycle. skeleton imports all three and is what `swarm daemon` (cmd/swarm.runDaemon) runs.

SOCKET OWNERSHIP: the daemon binds and owns the singleton socket (flock-before- bind, stale-socket reclaim under the lock — S12 all stay in daemon). Its accept loop hands each connection to this package's ConnHandler, which DEMUXES the one socket on an EXPLICIT first byte (see conn.go): a version probe leads with daemon.VersionProbeTag ('V'), a hook post with '{', and a wire frame with 0x00 (its length MSB). The three are disjoint, so a single first-byte read routes each connection immediately — no timing window, no change to the hook or frame wire.

Index

Constants

This section is empty.

Variables

View Source
var ErrHookDrainGap = errors.New("skeleton: hook drain observed a spool gap")

ErrHookDrainGap is returned by DrainOnce whenever the shim's spool reports a gap. recoveringGap distinguishes the first, durably reset observation (the caller should retry from 0) from the same boundary surviving that reset (the unreadable-tail case).

Functions

func AuthIdentityForHome added in v0.13.16

func AuthIdentityForHome(agentType, home string) string

AuthIdentityForHome is CurrentAuthIdentity against an explicit home -- the launch stamp uses the HOME the agent will actually inherit (a session with a per-session HOME reads that home's credentials, not the daemon's), while the watcher polls the daemon's.

func AuthProbedAgents added in v0.13.16

func AuthProbedAgents() []string

AuthProbedAgents enumerates the production providers whose adapters have a characterized credentials layout -- the set the watcher watches.

func AuthWatchDisabled added in v0.13.16

func AuthWatchDisabled(stateDir string) bool

AuthWatchDisabled reports whether the automatic watcher is opted out at stateDir -- the ONE reading, shared by the daemon's tick and `swarm relogin` (which acts itself exactly when the watcher will not). Missing = enabled (the feature is the default); present but unreadable/unparseable = disabled, because ambiguous config must fail toward inaction for a component that kills sessions on its own.

func CurrentAuthIdentity added in v0.13.16

func CurrentAuthIdentity(agentType string) string

CurrentAuthIdentity resolves the account identity agentType's on-disk credentials carry RIGHT NOW under the daemon's own home, or "" when there is no probe, no home, or no readable/parseable credentials file. "" gates conservatively everywhere it lands: a launch stamps no identity, the watcher holds.

func EndpointIDForStateDir added in v0.13.16

func EndpointIDForStateDir(stateDir string) string

EndpointIDForStateDir exposes the derivation to `swarm relogin`, which welds local state-dir reads to a dialled daemon and must refuse when the two are not the same daemon (SWARM_DAEMON_SOCK can point anywhere -- audit M4).

func SetAuthWatchDisabled added in v0.13.16

func SetAuthWatchDisabled(stateDir string, disabled bool) error

SetAuthWatchDisabled records the opt-out (`swarm relogin --auto off`) or removes it (`--auto on`; absence IS the enabled state, so re-enabling leaves no file behind).

Types

type Config

type Config struct {
	StateDir, SocketPath, LockPath, LogPath, ShimBinary string
	MaxSessions                                         int
	PollInterval                                        time.Duration // engine fallback-poll cadence (E10.8); 0 = no cadence
	StalenessThreshold                                  time.Duration
	FakeAgentBin                                        string // DEV/TEST ONLY: resolves the reserved agent "fake"

	// RemoteSocketPath, when non-empty, stands up the dedicated REMOTE-tier UDS the
	// gateway dials (R-GW.8 / amendment D.0-A1), distinct from the owner-trusted main
	// SocketPath. Every connection on it is unconditionally remote-origin, so every
	// mutating op is authorized against the pinned device registry (R-POL.9) before any
	// action. Empty => no remote socket. cmd/swarm fills it from gatewaySocket(), the same
	// definition the gateway's supervision unit dials, and leaves it empty on a machine
	// that was never provisioned for remote (ADR-007 B15).
	RemoteSocketPath string
	// ItemClock is the append floor's clock seam (remotegw.ItemAdmissionConfig.Now),
	// nil => time.Now, which is every production caller.
	//
	// It exists because ADR-010 §7's merge window is WALL-CLOCK. Whether two increments
	// of one item fold into one lossless append (IS-DELTA-1/-2) or ship as two records
	// therefore depends on how much real time passed between two Offer calls -- so a test
	// that must prove the MERGED item is admitted cannot get that from a real clock: a
	// scheduler stall of one window between the two offers is enough to release the first
	// increment alone, and CI's parallelism produces exactly that (docs/verification/
	// r0-flake-rootcause.md). Pinning the clock is what makes "these two are inside one
	// window" a fact of the test rather than a coincidence of the machine.
	ItemClock func() time.Time
	// contains filtered or unexported fields
}

Config parameterizes the assembly. The socket/lock/log/state paths and shim binary are the daemon's; PollInterval/StalenessThreshold tune the engine; FakeAgentBin is the dev/test-only resolver for the reserved agent "fake".

type Daemon

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

Daemon is the assembled, running walking skeleton: the core lifecycle daemon, the protocol server bound to its socket, the status engine, and the roster event source, with one Close that tears all four down cleanly.

func Serve

func Serve(cfg Config) (*Daemon, error)

Serve performs the full assembly and begins serving on cfg.SocketPath. On success the daemon is live: clients can Dial the socket, launch/list/attach, and hook posts route to the engine. The caller owns the returned *Daemon and closes it with Close.

func (*Daemon) Close

func (d *Daemon) Close() error

Close tears the assembly down cleanly: stop the engine, stop the core (which closes the socket and releases the singleton lock so a fresh daemon can take over), disconnect clients, and stop the roster poller. Running shims are independent and survive (S1). It is idempotent.

func (*Daemon) Core

func (d *Daemon) Core() *daemon.Daemon

Core exposes the underlying lifecycle authority — the walking-skeleton launch seam the in-process tests drive directly.

func (*Daemon) SocketPath

func (d *Daemon) SocketPath() string

SocketPath is the path clients dial (the daemon's singleton socket).

type HookDrainer

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

HookDrainer drains one session's shim-owned hook spool into the daemon.

func NewHookDrainer

func NewHookDrainer(d *Daemon, sessionID, hookSocketPath, cursorPath string) *HookDrainer

NewHookDrainer builds a drainer for sessionID against hookSocketPath, persisting its fold cursor at cursorPath -- reopened by a later daemon incarnation to resume exactly where the prior one left off.

func (*HookDrainer) DrainOnce

func (hd *HookDrainer) DrainOnce() (applied, skipped int, err error)

DrainOnce performs one dial+drain+apply+persist cycle: it requests every record past the persisted cursor, folding at the shim everything already durably applied through that SAME cursor -- never past it, so the cursor this call is about to advance to can never be compacted out from under a reader that has not yet observed it -- applies each returned record in order, and persists the advanced cursor after each one. skipped counts a record whose apply was rejected (engine.HandleCallback refused it, most commonly a genuinely stale/replayed redelivery from the crash-mid-batch case this design tolerates -- already logged by ingestHookBytes) so that outcome is OBSERVABLE rather than folded silently into applied's count; the cursor still advances past a skipped record either way, since it was durably read from the spool and must never be requested again.

func (*HookDrainer) FinalDrain

func (hd *HookDrainer) FinalDrain()

FinalDrain is the LAST drain of a session's life, run when its loop is stopped (hookdrainloop.go's stopHookDrain, on the OnSessionEnd path, BEFORE the engine retires the session so an applied callback still authenticates). It tries the live socket first -- a session ending for a reason that left the shim up still has one -- and then always reads the spool file, which is the only path left once the agent has been reaped and hookServer has shut down with it.

Errors are the ordinary case here, not a fault: a dead socket and an absent spool file are both what "this session had no structured channel, or it is already fully drained" looks like. A first gap is different: DrainOnce has made its boundary and reset durable, so the disk pass below gets one chance to fold the retained side. Only the same boundary surviving that reset is terminal.

func (*HookDrainer) SetSpoolPath

func (hd *HookDrainer) SetSpoolPath(path string)

SetSpoolPath configures the session's hooks.spool file, which drainFromSpoolFile reads directly when no live shim is left to serve a DRAIN. The zero value (never called) disables the disk path entirely -- the same "unset means disabled" convention the hook socket itself follows.

func (*HookDrainer) SetToken

func (hd *HookDrainer) SetToken(token string)

SetToken configures the drain-auth token this drainer presents on every DRAIN request (shim.HookDrainRequest.Token, checked against the shim's own configured Config.HookToken). The zero value (never called) means "no token" -- matching the shim side's own compat default, so a caller that predates the shim's token gate keeps working unchanged against a shim whose HookToken is equally unset.

Jump to

Keyboard shortcuts

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