remote

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package remote implements homonto's remote-source trust boundary: pinned, verified, fail-closed installation of remote resources. Its single guarantee is that no target file is mutated until fetched content is pinned-verified and passes every structural safety check.

Index

Constants

View Source
const RemoteSourcePrefix = "remote:"

RemoteSourcePrefix is the source-string prefix that marks a remote resource.

Variables

View Source
var DefaultLimits = Limits{
	MaxEntries:    10_000,
	MaxEntryBytes: 64 << 20,
	MaxTotalBytes: 256 << 20,
}

DefaultLimits are chosen well above real skill/agent bundles and well below resource exhaustion.

Functions

func IsRemoteSource

func IsRemoteSource(source string) bool

IsRemoteSource reports whether a config source string names a remote resource.

func RedactLocator

func RedactLocator(raw string) string

RedactLocator returns a canonical form of a remote locator with any embedded credentials removed, so it is safe to persist in remote.lock.json or surface in an error or log line. URL userinfo (user:pass@, including a bare token as the username) is dropped entirely, and the values of known secret query parameters are replaced with a marker. A locator that does not parse as a URL falls back to a best-effort userinfo strip so a secret still cannot leak.

Types

type Cache

type Cache struct {
	Root string // e.g. .homonto/cache/remote
}

Cache is a content-addressed store for verified remote trees. A tree keyed by its canonical digest resolves offline and reproducibly.

func (*Cache) Dir

func (c *Cache) Dir(d Digest) string

algoDir/Dir lay out <Root>/<algo>/<hex>/ so different algorithms never alias.

func (*Cache) GC

func (c *Cache) GC(referenced []Digest, dryRun bool) ([]Digest, error)

GC removes cache directories whose digest is not in referenced. With dryRun it reports what would be removed without deleting. It returns the reclaimed digests.

func (*Cache) Has

func (c *Cache) Has(d Digest) bool

Has reports whether the digest's content is already materialized.

func (*Cache) Put

func (c *Cache) Put(d Digest, tree Tree) (string, error)

Put materializes a tree at its digest's cache directory atomically (staging dir + rename). A second Put of the same digest is a no-op returning the existing path. The caller is responsible for having verified the tree's digest before Put.

func (*Cache) VerifyContent

func (c *Cache) VerifyContent(d Digest) error

VerifyContent re-hashes the cached content for d and reports an error if it is missing or no longer canonically hashes to d. Used to detect on-disk tampering of materialized remote content (doctor) and to re-check a cache-race winner.

type Digest

type Digest struct {
	Algo string // always "sha256"
	Hex  string // 64 lowercase hex chars
}

Digest is a content pin. Only sha256 is accepted in the first increment.

func CanonicalDigest

func CanonicalDigest(t Tree) Digest

CanonicalDigest computes a transport-independent sha256 pin over a validated tree. The canonical form is a deterministic serialization — files in lexical path order, each emitted as:

path bytes | 0x00 | exec-bit byte | 8-byte big-endian length | file bytes

so the same content pins identically regardless of archive framing, mtimes, or ownership. Only the executable bit of the mode is significant.

func ParseDigest

func ParseDigest(s string) (Digest, error)

ParseDigest parses a "sha256:<64 lowercase hex>" pin. Anything else — a missing prefix, a non-sha256 algorithm, a wrong length, uppercase, or a non-hex character — is rejected so an invalid pin fails closed at load.

func (Digest) Equal

func (d Digest) Equal(o Digest) bool

Equal reports whether two digests pin the same content.

func (Digest) IsZero

func (d Digest) IsZero() bool

IsZero reports whether the digest is unset.

func (Digest) String

func (d Digest) String() string

String renders the canonical "sha256:<hex>" form.

type FileEntry

type FileEntry struct {
	Path string // clean, relative, forward-slash path
	Mode uint32 // executable bit preserved; others normalized at canonicalization
	Data []byte
}

FileEntry is one regular file in a validated tree.

type Limits

type Limits struct {
	MaxEntries    int   // maximum number of members
	MaxEntryBytes int64 // maximum bytes in any single regular file
	MaxTotalBytes int64 // maximum total regular-file bytes across the archive
}

Limits bound archive extraction to defeat tar/zip bombs and path attacks.

type Lock

type Lock struct {
	Entries map[string]LockEntry
}

Lock is the remote lockfile: an auditable, reproducible record of every remote install keyed by "kind/name".

func LoadLock

func LoadLock(path string) (Lock, error)

LoadLock reads .homonto/remote.lock.json. A missing file yields an empty lock.

func (Lock) Digests

func (l Lock) Digests() []Digest

Digests returns the parsed pins referenced by the lock (skipping any that do not parse, so a hand-edited lock cannot crash GC).

func (Lock) Get

func (l Lock) Get(kind, name string) (LockEntry, bool)

Get returns an entry by kind/name.

func (*Lock) Remove

func (l *Lock) Remove(kind, name string)

Remove drops an entry by kind/name.

func (Lock) Save

func (l Lock) Save(path string) error

Save writes the lock atomically as a stable-sorted array with no timestamps, so consecutive saves of unchanged state are byte-identical.

func (*Lock) Set

func (l *Lock) Set(e LockEntry)

Set inserts or replaces an entry. The locator is redacted here so a credential embedded in a remote source can never be persisted to the lockfile, regardless of the caller.

type LockEntry

type LockEntry struct {
	Kind      string `json:"kind"`
	Name      string `json:"name"`
	Locator   string `json:"locator"`
	Transport string `json:"transport"`
	Digest    string `json:"digest"`
	Size      int64  `json:"size"`
}

LockEntry records the provenance of one materialized remote install. It holds no wall-clock timestamp so the lockfile is byte-stable across runs of unchanged state.

type RemoteSource

type RemoteSource struct {
	Raw       string
	URL       string // the locator with the remote: prefix stripped
	Transport TransportKind
}

RemoteSource is a parsed remote locator (without its pin, which is a sibling config field resolved by the loader).

func ParseRemoteSource

func ParseRemoteSource(source string) (RemoteSource, error)

ParseRemoteSource parses "remote:<url>" and selects a transport by scheme. Only https (not plain http), file, and git+https/git schemes are accepted; anything else fails closed.

type Resolver

type Resolver struct {
	Cache       *Cache
	Revocations Revocations
	Limits      Limits
}

Resolver turns a pinned remote source into a local, verified cache directory. Its single invariant: no cache entry (and thus no downstream target file) is written until fetched content is validated, its canonical digest matches the pin, and the pin is not revoked.

func (*Resolver) Resolve

func (r *Resolver) Resolve(ctx context.Context, src RemoteSource, pin Digest) (string, error)

Resolve returns a local directory containing the pinned content, fetching and verifying it if not already cached. The pipeline order is:

cache hit → revocation check → return   (offline path)
otherwise: fetch → validate → canonical digest → pin match → revocation → cache

Every failure aborts before any cache write, so malformed, tampered, or revoked content fails closed.

func (*Resolver) ResolveCached

func (r *Resolver) ResolveCached(_ RemoteSource, pin Digest) (string, error)

ResolveCached returns the cached directory for a pin without any network access. It re-hashes the cached content against the pin (so a locally tampered or corrupted cache entry fails closed) and enforces revocation. It errors if the pin is not cached.

type Revocations

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

Revocations is a set of banned content digests. Any resolve of a revoked digest fails closed, even on a warm cache.

func LoadRevocations

func LoadRevocations(path string) (Revocations, error)

LoadRevocations reads a JSON array of "sha256:<hex>" strings from path. A missing file yields an empty (allow-all) list; a malformed digest entry fails closed so a corrupt revocation file cannot silently permit content.

func (Revocations) Contains

func (r Revocations) Contains(d Digest) bool

Contains reports whether the digest is revoked.

type TransportKind

type TransportKind string

TransportKind identifies how a remote source is fetched.

const (
	TransportHTTPS TransportKind = "https"
	TransportFile  TransportKind = "file"
	TransportGit   TransportKind = "git"
)

type Tree

type Tree struct {
	Files []FileEntry
}

Tree is a validated archive: regular files only, sorted by Path.

func Fetch

func Fetch(ctx context.Context, src RemoteSource, lim Limits) (Tree, int64, error)

Fetch retrieves a remote source into a validated Tree, selecting a transport by scheme. It never writes to the cache or any target; verification (pin match, revocation) is the caller's responsibility. The returned size is the fetched byte count (compressed download or on-disk archive size).

func ValidateTar

func ValidateTar(r io.Reader, lim Limits) (Tree, error)

ValidateTar streams a tar archive, rejecting absolute paths, ".." traversal, non-regular members, duplicate paths, and any entry/total/count over the limits. It reads regular-file contents into memory (bounded by the limits) and returns them as a sorted Tree. It writes nothing to disk.

func ValidateTarGz

func ValidateTarGz(r io.Reader, lim Limits) (Tree, error)

ValidateTarGz gunzips then validates a tar archive, bounding total decompressed bytes by lim.MaxTotalBytes so a decompression bomb is rejected while streaming.

Jump to

Keyboard shortcuts

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