recording

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2026 License: GPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package recording owns session-recording capture and storage.

Index

Constants

View Source
const (
	EventStart    = "recording.start"
	EventFinalize = "recording.finalize"
	EventFailed   = "recording.failed"
	EventRead     = "recording.read"
	EventDelete   = "recording.delete"
)

Audit event names for recording lifecycle, kept separate from the route audit.

Variables

View Source
var ErrInvalidKey = errors.New("recording: invalid storage key")

ErrInvalidKey is returned when a storage key would escape the blob root.

Functions

func ContentType

func ContentType(format plugin.RecordingFormat) string

ContentType is the HTTP content type a recording's bytes are served with.

func ExpiryFor

func ExpiryFor(start time.Time, connectionDays, defaultDays int) *time.Time

ExpiryFor computes a recording's expiry from per-connection and default retention. Per-connection wins; nil means keep indefinitely (retention off).

func StorageKey

func StorageKey(connectionID, recordingID string, format plugin.RecordingFormat) string

StorageKey builds the blob key for a recording, namespaced by connection.

func StreamKey

func StreamKey(userID, connectionID, routeID string, params map[string]string) string

StreamKey locates a live stream's recording tap.

Types

type BlobStore

type BlobStore interface {
	// Create opens key for writing, truncating any existing object.
	Create(ctx context.Context, key string) (io.WriteCloser, error)
	// Append adds data to key, creating it if absent (chunked desktop uploads).
	Append(ctx context.Context, key string, data []byte) error
	// Open opens key for reading.
	Open(ctx context.Context, key string) (io.ReadCloser, error)
	// Size returns the byte length of key.
	Size(ctx context.Context, key string) (int64, error)
	// Delete removes key; absence is not an error.
	Delete(ctx context.Context, key string) error
}

BlobStore stores recording bytes under server-generated keys.

type Engine

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

Engine decides whether a stream is recorded and owns recording lifecycle.

func NewEngine

func NewEngine(opts Options) *Engine

NewEngine builds an Engine. Register a RecorderFactory per format before use.

func (*Engine) AbortChunked

func (e *Engine) AbortChunked(ctx context.Context, recordingID, userID string) error

AbortChunked discards an in-progress chunked recording and its bytes.

func (*Engine) AppendChunk

func (e *Engine) AppendChunk(ctx context.Context, recordingID, userID string, index int, data []byte) error

AppendChunk appends one ordered chunk to a chunked recording.

func (*Engine) BeginChunked

func (e *Engine) BeginChunked(ctx context.Context, info StreamInfo, format plugin.RecordingFormat) (models.Recording, error)

BeginChunked creates a desktop recording fed by client chunk uploads. The caller (HTTP handler) has already authorized the user for the connection.

func (*Engine) FinalizeChunked

func (e *Engine) FinalizeChunked(ctx context.Context, recordingID, userID string) (models.Recording, error)

FinalizeChunked marks a chunked recording complete.

func (*Engine) Prepare

func (e *Engine) Prepare(ctx context.Context, info StreamInfo) (*Pending, error)

Prepare decides recording from plugin capability and connection policy.

func (*Engine) ReapStaleChunked

func (e *Engine) ReapStaleChunked(ctx context.Context, maxAge time.Duration)

ReapStaleChunked aborts chunked recordings older than maxAge, freeing partial blobs left by abandoned browser captures.

func (*Engine) Register

func (e *Engine) Register(format plugin.RecordingFormat, f RecorderFactory)

Register associates a recorder factory with a format.

func (*Engine) Resize

func (e *Engine) Resize(key string, cols, rows int)

Resize records a terminal resize for the live recording identified by key (the terminal resize control channel calls this). No-op when not recording.

func (*Engine) Start

func (e *Engine) Start(ctx context.Context, key string) (models.Recording, error)

Start activates recording for a manual (idle) stream identified by key. It is a no-op if the stream is already recording.

func (*Engine) Stop

func (e *Engine) Stop(_ context.Context, key string) error

Stop finalizes a manual recording identified by key. Forced recordings cannot be stopped while their stream is live.

func (*Engine) Wrap

func (e *Engine) Wrap(ctx context.Context, client plugin.ClientStream, info StreamInfo) (plugin.ClientStream, func(), error)

Wrap is a convenience that prepares, attaches, and returns a finalize func.

type LocalBlobStore

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

LocalBlobStore stores blobs as files under a root directory.

func NewLocalBlobStore

func NewLocalBlobStore(root string) (*LocalBlobStore, error)

NewLocalBlobStore creates the root directory and returns a filesystem store.

func (*LocalBlobStore) Append

func (s *LocalBlobStore) Append(_ context.Context, key string, data []byte) error

func (*LocalBlobStore) Create

func (s *LocalBlobStore) Create(_ context.Context, key string) (io.WriteCloser, error)

func (*LocalBlobStore) Delete

func (s *LocalBlobStore) Delete(_ context.Context, key string) error

func (*LocalBlobStore) Open

func (s *LocalBlobStore) Open(_ context.Context, key string) (io.ReadCloser, error)

func (*LocalBlobStore) Size

func (s *LocalBlobStore) Size(_ context.Context, key string) (int64, error)

type Metrics

type Metrics interface {
	RecordingStarted()
	RecordingFinished()
	AddRecordingBytes(n int)
	RecordingFailed()
}

Metrics is the subset of telemetry the recording engine reports. It is satisfied by *telemetry.Metrics; a nil engine metrics falls back to noopMetrics.

type Options

type Options struct {
	Store                store.RecordingStore
	Blobs                BlobStore
	Audit                audit.Sink
	Metrics              Metrics
	DefaultRetentionDays int
	BufferEvents         int
	Now                  func() time.Time
}

Options configures an Engine.

type Pending

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

Pending is the recording decision for a stream.

func (*Pending) Attach

func (p *Pending) Attach(client plugin.ClientStream) plugin.ClientStream

Attach wraps the live client stream with the recording tap.

func (*Pending) Finish

func (p *Pending) Finish()

Finish ends the recording (if any) when the stream closes.

func (*Pending) Recording

func (p *Pending) Recording() bool

Recording reports whether this Pending will (or already does) record.

type Recorder

type Recorder interface {
	WriteOutput(ts time.Duration, p []byte) error
	WriteInput(ts time.Duration, p []byte) error
	Resize(ts time.Duration, cols, rows int) error
	Close() error
}

Recorder encodes timestamped stream events into its backing writer. Timestamps are relative to the recording start (monotonic, non-decreasing). A Recorder is driven by a single goroutine (the tap drain loop), so it need not be reentrant.

func NewAsciicastRecorder

func NewAsciicastRecorder(w io.Writer, info StartInfo) (Recorder, error)

NewAsciicastRecorder writes the header and returns a terminal Recorder.

type RecorderFactory

type RecorderFactory func(w io.Writer, info StartInfo) (Recorder, error)

RecorderFactory builds a Recorder for one recording, writing to w.

type StartInfo

type StartInfo struct {
	Title  string
	Cols   int
	Rows   int
	Env    map[string]string
	Start  time.Time
	Format plugin.RecordingFormat
}

StartInfo is the metadata a Recorder needs to emit its header.

type StreamInfo

type StreamInfo struct {
	User       models.User
	Connection models.Connection
	Manifest   plugin.Manifest
	Route      plugin.Route
	StreamID   string
	Params     map[string]string
	Cols       int
	Rows       int
	Title      string
	RemoteAddr string
}

StreamInfo describes the stream the wrapper is about to serve.

Jump to

Keyboard shortcuts

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