vessel

package module
v0.0.0-...-e70dba7 Latest Latest
Warning

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

Go to latest
Published: Nov 24, 2025 License: Apache-2.0 Imports: 27 Imported by: 0

README

Vessel

Vessel is a lightweight Go library that gives AI agents or automation services a container-style workspace. It builds on containerd snapshots and images so you can safely run commands, change files, and capture snapshots for later replay.

Features

  • Workspace-first abstraction – every Vessel mounts a writable layer on top of a base image and exposes file APIs together with process execution.
  • Snapshot pipeline – create, list, delete, and restore incremental diffs that can be shipped across machines through the pluggable Storage interface.
  • Storage flexibility – ships with local filesystem storage but can be extended with your own storage implementation.
  • Agent-friendly APIManager and Vessel interfaces are small, synchronous, and easy to embed inside orchestration services.

Prerequisites

  • containerd 1.7+ running on the host, with permissions to create snapshots.
  • For rootless environments: fuse-overlayfs installed and XDG_RUNTIME_DIR exported.
  • Optional offline usage: container images saved under ~/.local/share/vessel/data/images/<image-ref>.tar(.gz).

Installation

go get github.com/lf4096/vessel

Image Setup

By default, LocalStorage looks for a file named <image-ref>.tar.gz (or .tar) under ~/.local/share/vessel/data/images/, which should be an OCI/Docker image tarball. You can prepare these files in either of the following ways:

  1. Docker
# example: ubuntu:22.04
docker pull docker.io/library/alpine:latest
docker save docker.io/library/alpine:latest | gzip > ~/.local/share/vessel/data/images/docker.io/library/alpine:latest.tar.gz
  1. containerd / nerdctl
nerdctl pull docker.io/library/alpine:latest
nerdctl save docker.io/library/alpine:latest | gzip > ~/.local/share/vessel/data/images/docker.io/library/alpine:latest.tar.gz

Usage

Quick Start
package main

import (
	"context"
	"log"

	"github.com/lf4096/vessel"
)

func main() {
	ctx := context.Background()
	mgr, err := vessel.NewManager(&vessel.ManagerConfig{
		ContainerdAddress: "/run/containerd/containerd.sock",
		Namespace:         "vessel-demo",
		Storage:           nil, // default: ~/.local/share/vessel/data
	})
	if err != nil {
		log.Fatal(err)
	}
	defer mgr.Close()

	v, err := mgr.CreateVessel(ctx, "docker.io/library/alpine:latest", "/workspace")
	if err != nil {
		log.Fatal(err)
	}
	defer v.Close()

	if _, err := v.Exec(ctx, &vessel.ExecOptions{
		Command:       []string{"bash", "-lc", "echo hello > output.txt"},
		PersistResult: true,
	}); err != nil {
		log.Fatal(err)
	}

	data, err := v.ReadFile("output.txt")
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("output: %s", data)

	if _, err := v.CreateSnapshot(ctx, "init workspace"); err != nil {
		log.Fatal(err)
	}
}
File Operations

Requires import "os".

if err := v.WriteFile("notes/hello.txt", []byte("Born ready"), 0644); err != nil {
	log.Fatal(err)
}

f, err := v.OpenFile("notes/hello.txt", os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
	log.Fatal(err)
}
defer f.Close()
if _, err := f.WriteString("\nsecond line"); err != nil {
	log.Fatal(err)
}

data, err := v.ReadFile("notes/hello.txt")
if err != nil {
	log.Fatal(err)
}
log.Printf("current content: %s", data)
Snapshot Operations
if err := v.WriteFile("notes/hello.txt", []byte("Born ready"), 0644); err != nil {
	log.Fatal(err)
}

snap, err := v.CreateSnapshot(ctx, "baseline after bootstrap")
if err != nil {
	log.Fatal(err)
}

_ = v.DeleteFile("notes/hello.txt")

if err := v.RestoreSnapshot(ctx, snap.ID); err != nil {
	log.Fatal(err)
}

data, err := v.ReadFile("notes/hello.txt")
if err != nil {
	log.Fatal(err)
}
log.Printf("restored content: %s", data)
Command Execution
_, err := v.Exec(ctx, &vessel.ExecOptions{
	Command:       []string{"bash", "-lc", "go test ./..."},
	WorkDir:       "/workspace/project",
	PersistResult: false, // discard any writes after the command finishes
})
if err != nil {
	log.Fatal(err)
}

API Reference

  • Manager: CreateVessel, OpenVessel, DeleteVessel, Close.
  • Vessel: workspace file methods, Exec, snapshot methods, GetManifest, Close.
  • Storage: handles manifests, snapshots, and image tarballs—swap in your own backend if needed.

See vessel/types.go for the full interface definitions and comments.

Architecture

  1. Manager Layer

    • Maintains the containerd client and manages a global lease.
    • Handles image fetching and import with cache.
    • Selects the appropriate snapshotter (overlayfs or fuse-overlayfs) as needed.
    • Manages the mount root directory to ensure proper workspace isolation.
  2. Vessel Layer

    • Each Vessel instance has its own lease, mount point, and manifest.
    • Manages the writable mount point and snapshot chain for the workspace.
    • Exec executes commands via a containerd task, manages IO using FIFO, and supports both persistent and ephemeral modes.
    • By default, Exec shares the host network namespace.
  3. Storage Layer

    • LocalStorage organizes manifests and snapshots under ~/.local/share/vessel.
    • The CompressedStorage decorator adds gzip compression for snapshots and images to reduce transfer and disk usage.
    • The Storage interface allows plugging in custom backends, such as S3 or databases, enabling snapshot distribution across nodes.

Documentation

Overview

Package vessel provides a containerized workspace for AI agents, built on containerd to run commands, manage snapshots, and plug in custom storage.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CompressedStorage

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

CompressedStorage wraps another Storage and transparently compresses artifacts.

func (*CompressedStorage) DeleteSnapshot

func (s *CompressedStorage) DeleteSnapshot(ctx context.Context, vesselID, snapshotID string) error

DeleteSnapshot forwards deletion to the underlying store.

func (*CompressedStorage) DeleteVessel

func (s *CompressedStorage) DeleteVessel(ctx context.Context, vesselID string) error

DeleteVessel removes vessel data in the underlying store.

func (*CompressedStorage) LoadImage

func (s *CompressedStorage) LoadImage(ctx context.Context, imageRef string) (io.ReadCloser, error)

LoadImage wraps the underlying reader with gzip decompression.

func (*CompressedStorage) LoadSnapshot

func (s *CompressedStorage) LoadSnapshot(ctx context.Context, vesselID, snapshotID string) (io.ReadCloser, error)

LoadSnapshot returns a decompressed snapshot Reader.

func (*CompressedStorage) LoadVesselManifest

func (s *CompressedStorage) LoadVesselManifest(ctx context.Context, vesselID string) ([]byte, error)

LoadVesselManifest delegates manifest retrieval without compression.

func (*CompressedStorage) SaveSnapshot

func (s *CompressedStorage) SaveSnapshot(ctx context.Context, vesselID, snapshotID string, reader io.Reader) error

SaveSnapshot compresses snapshot diffs before writing to storage.

func (*CompressedStorage) SaveVesselManifest

func (s *CompressedStorage) SaveVesselManifest(ctx context.Context, vesselID string, manifest []byte) error

SaveVesselManifest delegates manifest persistence without compression.

type ExecOptions

type ExecOptions struct {
	// Command names the command and arguments to run inside the vessel.
	Command []string
	// WorkDir overrides the working directory inside the vessel.
	WorkDir string
	// Env carries optional environment variables in KEY=VALUE form.
	Env []string
	// Terminal enables TTY mode for interactive commands.
	Terminal bool
	// Stdin supplies an optional stdin reader.
	Stdin io.Reader
	// Stdout captures stdout; defaults to discard.
	Stdout io.Writer
	// Stderr captures stderr; defaults to discard.
	Stderr io.Writer
	// PersistResult keeps filesystem changes after the command finishes.
	PersistResult bool
}

ExecOptions controls how commands run inside a vessel container.

type ExecResult

type ExecResult struct {
	// ExitCode is the exit status reported by containerd.
	ExitCode int
}

ExecResult contains the exit status returned by Exec.

type File

type File interface {
	io.Closer
	io.Reader
	io.ReaderAt
	io.Writer
	io.WriterAt
	io.Seeker
	Name() string
	Stat() (os.FileInfo, error)
	Sync() error
	Truncate(size int64) error
}

File reuses os.File-like semantics for interacting with workspace files.

type IDGenerator

type IDGenerator interface {
	NewID() string
}

IDGenerator produces unique identifiers for vessels and snapshots.

type Image

type Image struct {
	Name        string
	Labels      map[string]string
	Config      ocispec.ImageConfig
	SnapshotKey string
}

type LocalStorage

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

LocalStorage stores vessel metadata and layers on the filesystem.

func (*LocalStorage) DeleteSnapshot

func (s *LocalStorage) DeleteSnapshot(ctx context.Context, vesselID, snapshotID string) error

DeleteSnapshot removes a snapshot diff from disk.

func (*LocalStorage) DeleteVessel

func (s *LocalStorage) DeleteVessel(ctx context.Context, vesselID string) error

DeleteVessel removes all local data associated with a vessel.

func (*LocalStorage) LoadImage

func (s *LocalStorage) LoadImage(ctx context.Context, imageRef string) (io.ReadCloser, error)

LoadImage loads a persisted container image for import into containerd.

func (*LocalStorage) LoadSnapshot

func (s *LocalStorage) LoadSnapshot(ctx context.Context, vesselID, snapshotID string) (io.ReadCloser, error)

LoadSnapshot opens a stored snapshot diff for replay.

func (*LocalStorage) LoadVesselManifest

func (s *LocalStorage) LoadVesselManifest(ctx context.Context, vesselID string) ([]byte, error)

LoadVesselManifest returns the serialized manifest bytes for a vessel.

func (*LocalStorage) SaveSnapshot

func (s *LocalStorage) SaveSnapshot(ctx context.Context, vesselID, snapshotID string, reader io.Reader) error

SaveSnapshot stores a new snapshot diff stream for a vessel.

func (*LocalStorage) SaveVesselManifest

func (s *LocalStorage) SaveVesselManifest(ctx context.Context, vesselID string, manifest []byte) error

SaveVesselManifest writes the current vessel manifest to disk.

type Manager

type Manager interface {
	CreateVessel(ctx context.Context, imageRef string, workDir string) (Vessel, error)
	OpenVessel(ctx context.Context, vesselID string) (Vessel, error)
	DeleteVessel(ctx context.Context, vesselID string) error
	Close() error
}

Manager coordinates lifecycle of containerized workspaces.

func NewManager

func NewManager(config *ManagerConfig) (Manager, error)

NewManager creates a Manager backed by containerd and the provided storage.

type ManagerConfig

type ManagerConfig struct {
	// ContainerdAddress is the containerd socket path, defaults to `/run/containerd/containerd.sock`
	// or `XDG_RUNTIME_DIR/containerd/containerd.sock` for rootless usage.
	ContainerdAddress string
	// Namespace scopes images and snapshots inside containerd, defaults to "vessel".
	Namespace string
	// Rootless enables fuse-overlayfs and adjusts runtime paths for non-root usage.
	Rootless bool
	// Storage provides snapshot and manifest persistence, defaults to `LocalStorage` with `CompressedStorage` decorator.
	Storage Storage
	// MountRoot is the directory where vessel mounts are created, defaults to `~/.local/share/vessel/mounts`.
	MountRoot string
	// IDGenerator generates unique identifiers for vessels and snapshots, defaults to `UniqueIDGenerator`.
	IDGenerator IDGenerator
}

ManagerConfig configures how a Manager connects to containerd and storage.

type SnapshotManifest

type SnapshotManifest struct {
	ID         string            `json:"id"`
	VesselID   string            `json:"vesselId"`
	ParentID   string            `json:"parentId"`
	Message    string            `json:"message"`
	Checksum   string            `json:"checksum"`
	Size       int64             `json:"size"`
	CreateTime time.Time         `json:"createTime"`
	Labels     map[string]string `json:"labels"`
}

SnapshotManifest describes an immutable snapshot diff for a vessel.

type Storage

type Storage interface {
	SaveVesselManifest(ctx context.Context, vesselID string, manifest []byte) error
	LoadVesselManifest(ctx context.Context, vesselID string) ([]byte, error)

	LoadImage(ctx context.Context, imageRef string) (io.ReadCloser, error)

	SaveSnapshot(ctx context.Context, vesselID, snapshotID string, reader io.Reader) error
	LoadSnapshot(ctx context.Context, vesselID, snapshotID string) (io.ReadCloser, error)
	DeleteSnapshot(ctx context.Context, vesselID, snapshotID string) error

	DeleteVessel(ctx context.Context, vesselID string) error
}

Storage abstracts persistence for manifests, layers, and container images.

func NewCompressedStorage

func NewCompressedStorage(underlying Storage) Storage

NewCompressedStorage decorates a Storage with gzip compression.

func NewLocalStorage

func NewLocalStorage(baseDir string, compressed bool) (Storage, error)

NewLocalStorage creates a filesystem-backed Storage optionally wrapping with compression.

type UniqueIDGenerator

type UniqueIDGenerator struct{}

UniqueIDGenerator is the default implementation of IDGenerator that produces monotonic-ish hex identifiers mixing timestamp and randomness.

func NewUniqueIDGenerator

func NewUniqueIDGenerator() *UniqueIDGenerator

NewUniqueIDGenerator creates a new UniqueIDGenerator instance.

func (*UniqueIDGenerator) NewID

func (g *UniqueIDGenerator) NewID() string

NewID returns a unique 16-character hex identifier.

type Vessel

type Vessel interface {
	ID() string
	GetManifest() *VesselManifest

	OpenFile(path string, flag int, perm os.FileMode) (File, error)
	ReadFile(path string) ([]byte, error)
	WriteFile(path string, data []byte, perm os.FileMode) error
	DeleteFile(path string) error
	RenameFile(oldPath, newPath string) error
	Stat(path string) (os.FileInfo, error)

	MakeDir(path string, perm os.FileMode) error
	MakeDirAll(path string, perm os.FileMode) error
	ReadDir(path string) ([]os.DirEntry, error)

	Exec(ctx context.Context, opts *ExecOptions) (*ExecResult, error)

	CreateSnapshot(ctx context.Context, message string) (*SnapshotManifest, error)
	RestoreSnapshot(ctx context.Context, snapshotID string) error
	DeleteSnapshot(ctx context.Context, snapshotID string) error
	ListSnapshots(ctx context.Context) ([]*SnapshotManifest, error)

	Close() error
}

Vessel represents a mutable container workspace backed by snapshots.

type VesselManifest

type VesselManifest struct {
	ID                string              `json:"id"`
	BaseImageRef      string              `json:"baseImageRef"`
	CurrentSnapshotID string              `json:"currentSnapshotId"`
	WorkDir           string              `json:"workDir"`
	CreateTime        time.Time           `json:"createTime"`
	UpdateTime        time.Time           `json:"updateTime"`
	Labels            map[string]string   `json:"labels"`
	Snapshots         []*SnapshotManifest `json:"snapshots"`
}

VesselManifest tracks the vessel state persisted in storage.

Jump to

Keyboard shortcuts

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