agentproto

package
v1.3.4 Latest Latest
Warning

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

Go to latest
Published: May 20, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package agentproto defines the CBOR wire protocol shared by local runners and mproxy-agent. It receives command, stream, file-operation, logging, and recording state from remoteexec and the agent, and feeds broker, remote backends, workspacefs, pathstub, and cmd/mproxy-agent with stable frame and RPC types.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AgentConfig

type AgentConfig struct {
	EnvKeep   []string `cbor:"env_keep"`
	EnvRemove []string `cbor:"env_remove"`
}

AgentConfig carries the agent's runtime configuration, sent via FrameConfig before any FrameExec. Patterns are globs or /regex/ delimited strings.

type CommandEnd

type CommandEnd struct {
	SeqNum   uint32 `cbor:"seq"`
	EndTime  int64  `cbor:"end_time"`
	ExitCode int    `cbor:"exit_code"`
}

CommandEnd marks the end of a command execution.

type CommandStart

type CommandStart struct {
	SeqNum    uint32  `cbor:"seq"`
	StartTime int64   `cbor:"start_time"`
	Exec      ExecMsg `cbor:"exec"`
}

CommandStart marks the beginning of a command execution.

type Decoder

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

Decoder reads CBOR-encoded frames.

func NewDecoder

func NewDecoder(r io.Reader) *Decoder

NewDecoder returns a Decoder that reads from r.

func (*Decoder) Decode

func (d *Decoder) Decode() (*Frame, error)

Decode reads the next frame. Returns io.EOF at end of stream.

type Direction

type Direction uint8

Direction indicates whether a frame was sent or received.

const (
	DirSend Direction = 1 // local → agent
	DirRecv Direction = 2 // agent → local
)

type Encoder

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

Encoder writes CBOR-encoded frames. It is safe for concurrent use.

func NewEncoder

func NewEncoder(w io.Writer) *Encoder

NewEncoder returns an Encoder that writes to w.

func (*Encoder) Encode

func (e *Encoder) Encode(f *Frame) error

Encode writes a single frame.

type ExecMsg

type ExecMsg struct {
	Path     string   `cbor:"path"`
	Argv     []string `cbor:"argv"`
	Env      []string `cbor:"env"`
	Cwd      string   `cbor:"cwd"`
	ExtraFDs []uint32 `cbor:"fds,omitempty"`
}

ExecMsg carries the command details in a FrameExec.

type FileDirEntry added in v1.2.0

type FileDirEntry struct {
	Stat FileStat `cbor:"st"`
}

FileDirEntry is one entry in a FileOpReadDir response.

type FileOp added in v1.2.0

type FileOp uint8

FileOp identifies a remote file operation. Operations are RPC-style: the caller assigns a unique ReqID and expects exactly one FileOpResp back with the same ReqID. File handles are agent-side state; the client treats them as opaque uint32 tokens returned by Open/Create.

const (
	FileOpOpen      FileOp = 1  // input: Path, Flags  → output: Handle
	FileOpCreate    FileOp = 2  // input: Path         → output: Handle (O_WRONLY|O_CREATE|O_TRUNC)
	FileOpOpenFile  FileOp = 3  // input: Path, Flags  → output: Handle
	FileOpClose     FileOp = 4  // input: Handle
	FileOpReadAt    FileOp = 5  // input: Handle, Offset, Size  → output: Data, EOF
	FileOpWriteAt   FileOp = 6  // input: Handle, Offset, Data  → output: Size (bytes written)
	FileOpStat      FileOp = 7  // input: Path → output: Stat
	FileOpLstat     FileOp = 8  // input: Path → output: Stat, does not follow symlinks
	FileOpReadDir   FileOp = 9  // input: Path → output: Entries
	FileOpMkdir     FileOp = 10 // input: Path
	FileOpMkdirAll  FileOp = 11 // input: Path
	FileOpRemove    FileOp = 12 // input: Path
	FileOpRename    FileOp = 13 // input: Path, NewPath
	FileOpChmod     FileOp = 14 // input: Path, Mode
	FileOpTruncate  FileOp = 15 // input: Path, Size
	FileOpGetwd     FileOp = 16 //             → output: Path
	FileOpFsync     FileOp = 17 // input: Handle
	FileOpReadlink  FileOp = 18 // input: Path → output: Path
	FileOpSymlink   FileOp = 19 // input: Path target, NewPath linkpath
	FileOpLink      FileOp = 20 // input: Path old, NewPath new
	FileOpChown     FileOp = 21 // input: Path, UID, GID
	FileOpChtimes   FileOp = 22 // input: Path, AtimeNanos, MTimeNanos
	FileOpStatfs    FileOp = 23 // input: Path → output: Statfs
	FileOpFstat     FileOp = 24 // input: Handle → output: Stat
	FileOpFtruncate FileOp = 25 // input: Handle, Size
)

type FileOpReq added in v1.2.0

type FileOpReq struct {
	ReqID      uint32 `cbor:"id"`
	Op         FileOp `cbor:"op"`
	Path       string `cbor:"p,omitempty"`
	NewPath    string `cbor:"np,omitempty"`
	Flags      int32  `cbor:"fl,omitempty"`
	Mode       uint32 `cbor:"mo,omitempty"`
	UID        uint32 `cbor:"uid,omitempty"`
	GID        uint32 `cbor:"gid,omitempty"`
	Handle     uint32 `cbor:"h,omitempty"`
	Offset     int64  `cbor:"of,omitempty"`
	Size       int64  `cbor:"sz,omitempty"`
	AtimeNanos int64  `cbor:"at,omitempty"`
	MTimeNanos int64  `cbor:"mt,omitempty"`
	Data       []byte `cbor:"d,omitempty"`
}

FileOpReq is the request half of a file-op RPC. Only fields relevant to the chosen Op should be set.

type FileOpResp added in v1.2.0

type FileOpResp struct {
	ReqID   uint32         `cbor:"id"`
	Errno   uint32         `cbor:"er,omitempty"`
	ErrMsg  string         `cbor:"em,omitempty"`
	Handle  uint32         `cbor:"h,omitempty"`
	Data    []byte         `cbor:"d,omitempty"`
	N       int64          `cbor:"n,omitempty"`   // bytes read/written
	EOF     bool           `cbor:"eof,omitempty"` // for ReadAt
	Path    string         `cbor:"p,omitempty"`   // for Getwd
	Stat    *FileStat      `cbor:"st,omitempty"`
	Statfs  *FileStatfs    `cbor:"sf,omitempty"`
	Entries []FileDirEntry `cbor:"en,omitempty"`
}

FileOpResp is the response half. Errno (when nonzero) is a POSIX errno value the client maps back to an os.Err* sentinel; ErrMsg is a human-readable description for logs.

type FileOpServer added in v1.2.0

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

FileOpServer is the agent-side implementation of file ops. It is stateful: Open/Create return uint32 handles that subsequent ReadAt/ WriteAt/Close requests reference. Handles are local to the server.

func NewFileOpServer added in v1.2.0

func NewFileOpServer() *FileOpServer

NewFileOpServer constructs an empty server.

func (*FileOpServer) CloseAll added in v1.2.0

func (s *FileOpServer) CloseAll()

CloseAll releases all open handles. Call this when the file-op service loop exits so the agent doesn't leak fds.

func (*FileOpServer) Handle added in v1.2.0

func (s *FileOpServer) Handle(req *FileOpReq) *FileOpResp

Handle dispatches a request to the matching os.* primitive and returns a response. Errors are surfaced through Resp.Errno + ErrMsg so the client can map them back to os.Err* sentinels.

type FileStat added in v1.2.0

type FileStat struct {
	Name       string `cbor:"n"`
	Size       int64  `cbor:"s"`
	Mode       uint32 `cbor:"m"`
	MTimeNanos int64  `cbor:"t,omitempty"`
	ATimeNanos int64  `cbor:"at,omitempty"`
	CTimeNanos int64  `cbor:"ct,omitempty"`
	UID        uint32 `cbor:"uid,omitempty"`
	GID        uint32 `cbor:"gid,omitempty"`
	Nlink      uint32 `cbor:"nl,omitempty"`
	Rdev       uint32 `cbor:"rd,omitempty"`
	Blocks     uint64 `cbor:"bl,omitempty"`
	Blksize    uint32 `cbor:"bs,omitempty"`
	Ino        uint64 `cbor:"ino,omitempty"`
	IsDir      bool   `cbor:"d,omitempty"`
}

FileStat is a backend-agnostic snapshot of os.FileInfo fields.

type FileStatfs added in v1.3.0

type FileStatfs struct {
	Blocks  uint64 `cbor:"b,omitempty"`
	Bfree   uint64 `cbor:"bf,omitempty"`
	Bavail  uint64 `cbor:"ba,omitempty"`
	Files   uint64 `cbor:"f,omitempty"`
	Ffree   uint64 `cbor:"ff,omitempty"`
	Bsize   uint32 `cbor:"bs,omitempty"`
	Frsize  uint32 `cbor:"fr,omitempty"`
	NameLen uint32 `cbor:"nl,omitempty"`
}

FileStatfs is a backend-agnostic statfs/statvfs snapshot.

type Frame

type Frame struct {
	Type     FrameType    `cbor:"t"`
	Stream   uint32       `cbor:"s,omitempty"` // 0=stdin, 1=stdout, 2=stderr, 3+=extra fds
	Data     []byte       `cbor:"d,omitempty"`
	Signal   int          `cbor:"sig,omitempty"`
	Code     int          `cbor:"c,omitempty"`
	Error    string       `cbor:"e,omitempty"`
	Exec     *ExecMsg     `cbor:"x,omitempty"`
	Log      *LogEntry    `cbor:"l,omitempty"`
	Config   *AgentConfig `cbor:"cfg,omitempty"`
	Query    *PathQuery   `cbor:"q,omitempty"`
	Info     *PathInfo    `cbor:"i,omitempty"`
	FileOp   *FileOpReq   `cbor:"fop,omitempty"`
	FileResp *FileOpResp  `cbor:"frp,omitempty"`
}

Frame is a single message on the CBOR mux. Fields are omitted when zero-valued so only the relevant subset appears on the wire.

type FrameType

type FrameType uint8

FrameType identifies the kind of message on the CBOR mux.

const (
	FrameExec      FrameType = 1  // local → agent: start command
	FrameData      FrameType = 2  // bidirectional: data for a stream
	FrameEOF       FrameType = 3  // sender closes a stream
	FrameSignal    FrameType = 4  // local → agent: deliver signal to child pgid
	FrameExit      FrameType = 5  // agent → local: child exited (terminal frame)
	FrameError     FrameType = 6  // agent → local: internal error
	FrameLog       FrameType = 7  // agent → local: log message
	FrameConfig    FrameType = 8  // local → agent: serialized config
	FramePathQuery FrameType = 9  // local → agent: enumerate PATH executables
	FramePathInfo  FrameType = 10 // agent → local: PATH enumeration result
	FrameFileOp    FrameType = 11 // local → agent: file-op request
	FrameFileResp  FrameType = 12 // agent → local: file-op response
)

type LogEntry

type LogEntry struct {
	Level int      `cbor:"lvl"` // slog.Level value
	Msg   string   `cbor:"msg"`
	Attrs []string `cbor:"a,omitempty"` // key=value pairs
}

LogEntry carries a structured log record from the agent.

type Mux

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

Mux multiplexes CBOR frames over a reader/writer pair. Incoming frames are dispatched to registered handlers; Send serialises outgoing frames.

func NewMux

func NewMux(r io.Reader, w io.Writer) *Mux

NewMux creates a mux over the given reader (incoming frames) and writer (outgoing frames). Typically r and w are the two ends of an SSH session's stdio.

func (*Mux) DecodeOne

func (m *Mux) DecodeOne() (*Frame, error)

DecodeOne reads a single frame from the underlying reader without dispatching it. Useful for reading the initial FrameExec before the read loop starts.

func (*Mux) FileOp added in v1.2.0

func (m *Mux) FileOp(ctx context.Context, req *FileOpReq) (*FileOpResp, error)

FileOp performs a client-side file-op RPC: encodes req with a fresh ReqID, sends it, and waits for the matching FileOpResp. Multiple FileOp calls are safe concurrently.

func (*Mux) OnFileOp added in v1.2.0

func (m *Mux) OnFileOp(fn func(*FileOpReq) *FileOpResp)

OnFileOp registers the agent-side handler that services file-op requests. The handler receives the request and must return a fully populated FileOpResp (ReqID is propagated automatically).

func (*Mux) OnLog

func (m *Mux) OnLog(fn func(*LogEntry))

OnLog sets the callback for FrameLog messages (agent log forwarding).

func (*Mux) OnSignal

func (m *Mux) OnSignal(fn func(int))

OnSignal sets the callback for FrameSignal messages.

func (*Mux) ReadLoop

func (m *Mux) ReadLoop(ctx context.Context) (exitCode int, err error)

ReadLoop reads and dispatches frames until the context is cancelled, a FrameExit is received, or the underlying reader returns an error. It returns the exit code from a FrameExit, or -1 if the loop ended without one.

func (*Mux) RegisterStream

func (m *Mux) RegisterStream(id uint32, h StreamHandler)

RegisterStream adds a handler for the given stream ID.

func (*Mux) Send

func (m *Mux) Send(f *Frame) error

Send writes a frame to the underlying writer. Safe for concurrent use.

func (*Mux) SendData

func (m *Mux) SendData(stream uint32, data []byte) error

SendData is a convenience for sending a FrameData.

func (*Mux) SendEOF

func (m *Mux) SendEOF(stream uint32) error

SendEOF is a convenience for sending a FrameEOF.

func (*Mux) SetRecorder

func (m *Mux) SetRecorder(rec *Recorder, seq uint32)

SetRecorder enables recording of all frames passing through this mux. seq is the command sequence number assigned by the Recorder.

type MuxLogHandler

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

MuxLogHandler is a slog.Handler that sends log records over the CBOR mux as FrameLog frames. This lets agent-side logs appear on the machineproxy process where the operator can see them.

func NewMuxLogHandler

func NewMuxLogHandler(mux *Mux, level slog.Level) *MuxLogHandler

NewMuxLogHandler creates a handler that forwards log records through mux.

func (*MuxLogHandler) Enabled

func (h *MuxLogHandler) Enabled(_ context.Context, level slog.Level) bool

func (*MuxLogHandler) Handle

func (h *MuxLogHandler) Handle(_ context.Context, r slog.Record) error

func (*MuxLogHandler) WithAttrs

func (h *MuxLogHandler) WithAttrs(attrs []slog.Attr) slog.Handler

func (*MuxLogHandler) WithGroup

func (h *MuxLogHandler) WithGroup(name string) slog.Handler

type PathInfo added in v1.1.0

type PathInfo struct {
	Entries []PathInfoEntry `cbor:"entries"`
}

PathInfo carries the agent's enumeration result. Entries are deduped by Name (first-found wins per PATH precedence) and listed in the same order as the input Paths.

type PathInfoEntry added in v1.1.0

type PathInfoEntry struct {
	Name       string `cbor:"name"`
	RemotePath string `cbor:"path"`
	Mode       uint32 `cbor:"mode"`
	Size       int64  `cbor:"size"`
	MTimeNanos int64  `cbor:"mtime,omitempty"`
}

PathInfoEntry describes one stub entry returned by FramePathInfo.

type PathQuery added in v1.1.0

type PathQuery struct {
	Paths []string `cbor:"paths,omitempty"`
}

PathQuery requests enumeration of executable files reachable through the listed PATH-style directories. An empty Paths slice tells the agent to use its own $PATH at the time of the request.

type Record

type Record struct {
	Type     RecordType     `cbor:"type"`
	Header   *SessionHeader `cbor:"header,omitempty"`
	CmdStart *CommandStart  `cbor:"cmd_start,omitempty"`
	Frame    *RecordedFrame `cbor:"frame,omitempty"`
	CmdEnd   *CommandEnd    `cbor:"cmd_end,omitempty"`
	Footer   *SessionFooter `cbor:"footer,omitempty"`
}

Record is the top-level envelope written to a recording file. Exactly one of the optional fields is set per record.

func ReadRecording

func ReadRecording(r io.Reader) ([]Record, error)

ReadRecording decodes all records from a recording stream.

type RecordType

type RecordType uint8

RecordType tags each record in a recording file.

const (
	RecordSessionHeader RecordType = 1
	RecordCommandStart  RecordType = 2
	RecordFrame         RecordType = 3
	RecordCommandEnd    RecordType = 4
	RecordSessionFooter RecordType = 5
)

type RecordedFrame

type RecordedFrame struct {
	SeqNum    uint32    `cbor:"seq"`
	Timestamp int64     `cbor:"ts"`
	Direction Direction `cbor:"dir"`
	Frame     Frame     `cbor:"frame"`
}

RecordedFrame wraps an agentproto Frame with recording metadata.

type Recorder

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

Recorder writes session recording data. Safe for concurrent use.

func NewRecorder

func NewRecorder(path string) (*Recorder, error)

NewRecorder creates a Recorder that writes to path. The file is created (or truncated) immediately.

func NewRecorderWriter

func NewRecorderWriter(w io.Writer) *Recorder

NewRecorderWriter creates a Recorder that writes to w. The caller is responsible for closing w.

func (*Recorder) Close

func (r *Recorder) Close() error

Close writes the session footer and closes the underlying file.

func (*Recorder) WriteCommandEnd

func (r *Recorder) WriteCommandEnd(seq uint32, exitCode int) error

WriteCommandEnd marks the completion of a command.

func (*Recorder) WriteCommandStart

func (r *Recorder) WriteCommandStart(exec *ExecMsg) (uint32, error)

WriteCommandStart marks the beginning of a new command and returns its sequence number.

func (*Recorder) WriteFrame

func (r *Recorder) WriteFrame(seq uint32, dir Direction, f *Frame) error

WriteFrame records a single agentproto frame with direction metadata.

func (*Recorder) WriteSessionFooter

func (r *Recorder) WriteSessionFooter() error

WriteSessionFooter writes the session-level summary. Call once before closing the recorder.

func (*Recorder) WriteSessionHeader

func (r *Recorder) WriteSessionHeader(h *SessionHeader) error

WriteSessionHeader writes the session-level metadata. Call once at the start of a recording.

type SessionFooter

type SessionFooter struct {
	EndTime      int64  `cbor:"end_time"`
	CommandCount uint32 `cbor:"command_count"`
}

SessionFooter is written once at the end of a recording file.

type SessionHeader

type SessionHeader struct {
	Version     string `cbor:"version"`
	StartTime   int64  `cbor:"start_time"` // unix nanos
	LocalUser   string `cbor:"local_user"`
	LocalPID    int    `cbor:"local_pid"`
	BackendType string `cbor:"backend_type,omitempty"`
	BackendAddr string `cbor:"backend_addr,omitempty"`
	AgentPath   string `cbor:"agent_path"`
}

SessionHeader is written once at the start of a recording file.

type StreamHandler

type StreamHandler interface {
	Write(data []byte) error
	EOF()
}

StreamHandler receives data and EOF events for a single stream ID.

func WriterHandler

func WriterHandler(w io.WriteCloser) StreamHandler

WriterHandler returns a StreamHandler that writes to w and closes it on EOF.

Jump to

Keyboard shortcuts

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