attach

package
v0.2.32-rc1 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: MPL-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package attach is the seam behind an attached transport: a live resource inside a workspace bound to one external connection for as long as a lease allows, with the stack having authorized the binding ONCE rather than running on every byte.

Two Kinds exist. "pty" — `workspace://<name>/attach` binds a pseudo-terminal to the WebSocket session whose run made the op, and from then on the personality pumps frames straight to and from the process. "service" — `workspace://<name>/connect` binds a byte stream to one of the workspace's own loopback services (a VNC display, say), named from a chassis-owned table; the same registry, lease, metering and teardown funnel carry it, and the personality cannot tell the two apart.

The processor creates bindings (it has the tenant, the workspace manager and the usage sink); the websocket personality pumps them (it has the socket). Neither imports the other's package for this — both import this one, the way processor.Unit.Workspaces is shared.

Index

Constants

View Source
const (
	KindPTY     = "pty"
	KindService = "service"
)

Kinds of attached resource.

View Source
const (
	ControlResize = "resize"
	ControlSignal = "signal"
	ControlDetach = "detach" // handled by the personality: never reaches Conn
)

Control message types a client may send on the text channel; the personality decodes the envelope and calls Conn.Control with the type and the raw JSON.

Variables

View Source
var (
	ErrAlreadyBound = errors.New("attach: session already has an attachment")
	ErrNotBound     = errors.New("attach: session has no attachment")
)

Errors the Registry returns.

Functions

This section is empty.

Types

type Attachment

type Attachment struct {
	ID        string
	Tenant    string
	AppStack  string
	Workspace string
	Kind      string
	Service   string

	SessionID string
	NodeID    string
	RunID     string
	Computer  string

	StartedAt time.Time
	ExpiresAt time.Time

	// Conn is the live resource; set by the caller right after New (the
	// session is started on Context, which New must create first).
	Conn Conn

	// BytesIn counts client → resource bytes, BytesOut resource → client;
	// the pump adds, the heartbeat reads.
	BytesIn  atomic.Int64
	BytesOut atomic.Int64
	// contains filtered or unexported fields
}

Attachment is one granted binding: who holds it, what it is bound to, how long it may live, and the counters the lease's metering reads.

func New

func New(parent context.Context, p Params) *Attachment

New builds an attachment whose lifetime is its own context, derived from parent WITHOUT its cancellation: the op that grants a binding returns in milliseconds, and the binding must outlive that run. It ends on Close — detach, expiry, session teardown, shutdown.

func (*Attachment) Close

func (a *Attachment) Close(reason string)

Close ends the attachment once: cancels the context (killing the process), closes the Conn, and records why. Safe from any goroutine.

func (*Attachment) Closed

func (a *Attachment) Closed() bool

Closed reports whether Close has run.

func (*Attachment) Context

func (a *Attachment) Context() context.Context

Context ends when the attachment does. Start the session's process on it so that ending the attachment kills the process.

func (*Attachment) Expired

func (a *Attachment) Expired(now time.Time) bool

Expired reports whether the lease's max_duration has passed at now.

func (*Attachment) Reason

func (a *Attachment) Reason() string

Reason is why the attachment ended ("" while it lives).

type Conn

type Conn interface {
	io.ReadWriteCloser
	Control(ctx context.Context, typ string, raw []byte) error
	Wait() (exit int, err error)
}

Conn is one live resource bound to one external transport. Read is resource → client, Write is client → resource; Control carries the typed out-of-band messages (resize, signal); Wait reports how the resource ended. Close ends it, whatever it is doing, and is idempotent.

func NewNet

func NewNet(c net.Conn) Conn

NewNet binds a dialed service connection as a Conn.

func NewPTY

func NewPTY(sess workspace.ExecSession) Conn

NewPTY binds a running TTY session as a Conn: Read drains the terminal, Write feeds it, resize and signal are the two controls.

type Params

type Params struct {
	ID        string // the lease id; the handle in logs and admin views
	Tenant    string
	AppStack  string // the workspace's owning stack (workspace.AppStack)
	Workspace string
	Kind      string // KindPTY | KindService
	Service   string // KindService: the service name the binding reached

	SessionID string // the WebSocket session holding the socket
	NodeID    string // the chassis node holding that socket
	RunID     string // the workspace run the resource lives in
	Computer  string // the provider's reference for the workspace

	StartedAt time.Time
	ExpiresAt time.Time
}

Params are the plain fields New needs to build an Attachment. Kept separate from Attachment so the atomics never travel by value (go vet).

type Registry

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

Registry maps a session to its one attachment. A session may hold at most one — a second attach on a bound session is refused, and the terminal's own multiplexing (tmux) is where several processes go.

func NewRegistry

func NewRegistry() *Registry

NewRegistry builds an empty registry.

func (*Registry) Bind

func (r *Registry) Bind(a *Attachment) error

Bind registers a for its session; ErrAlreadyBound if one is live there.

func (*Registry) CloseAll

func (r *Registry) CloseAll(reason string)

CloseAll ends every attachment (shutdown).

func (*Registry) List

func (r *Registry) List(tenant string) []*Attachment

List snapshots the live attachments, every tenant when tenant is "".

func (*Registry) Lookup

func (r *Registry) Lookup(tenant, sid string) (*Attachment, bool)

Lookup returns the live attachment for a tenant's session. A session of another tenant is indistinguishable from an unbound one.

func (*Registry) Unbind

func (r *Registry) Unbind(tenant, sid string) (*Attachment, bool)

Unbind removes the session's attachment from the registry and returns it; the caller closes it. ok is false when there was none.

Jump to

Keyboard shortcuts

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