source

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2026 License: LGPL-2.1 Imports: 38 Imported by: 0

README

mod/source

mod/source is the outbound network layer. It classifies a configured upstream as either a git forge or another yggvault node, downloads release archives, speaks brother RPC over HTTP CONNECT, and can read a brother's public release API when RPC is unavailable. It owns egress safety: routed dials, retry policy, credentials, redirects, SSRF checks, bounded RPC decoding, and bounded public metadata reads.

Place in the Runtime

flowchart LR
  rescan["mod/rescan"] --> source["mod/source"]
  source --> git["git API and archive URLs"]
  source --> brother["brother RPC"]
  source --> public["brother public API"]
  source --> spool["archive and blob spool"]
  mesh["mod/mesh"] --> source

Responsibilities

  • Classify sources through /health, /info, and brother Hello.
  • Fetch git releases and tags from supported providers.
  • Download release archives into a caller-owned spool path.
  • Resume interrupted downloads when validators and Range support allow it.
  • Route .pk.ygg traffic through the embedded mesh and public hosts through guarded clearnet dials.
  • Add provider credentials only for matching provider hosts.
  • Open brother sessions and pull index pages, version tree bytes, and blob batches.
  • Read public releases.json, release detail JSON, and universal archive URLs from a brother when RPC cannot be used.
  • Negotiate brother blob-fetch byte and batch limits through Brother.Hello.
  • Enforce redirect, response, gob, body, throughput, and private-address limits.

Discovery Flow

flowchart TD
  start["configured URL"] --> health["GET host /health"]
  health --> vault{"valid yggvault JSON?"}
  vault -- "yes" --> hello["Brother.Hello"]
  hello -- "ok" --> brother["brother source via RPC"]
  hello -- "dial failed" --> public["brother source via public API fallback"]
  vault -- "no" --> info["GET host /info"]
  info --> notvault{"also not vault?"}
  notvault -- "yes" --> git["git source"]
  notvault -- "unknown" --> defer["temporary unavailable"]

Large non-JSON /health or /info bodies are treated as non-vault responses. This keeps public git forges such as GitHub from being stuck in "classification deferred" when they return HTML at service-looking paths.

For a confirmed brother, the remote key is derived from the configured URL path under the prefix the remote node actually serves: discovery reads route_prefix from the brother's /info and strips that segment (for example http://host/pkg/key under route_prefix=pkg yields key). Nodes that omit route_prefix (older instances) fall back to the local web.routing.prefix, so existing deployments keep resolving keys unchanged.

Contracts

  • All dials and requests must carry deadlines.
  • Clearnet dials reject loopback, private, link-local, multicast, reserved, and Yggdrasil ranges after DNS resolution.
  • Credentials never cross from clearnet into Yggdrasil or another host.
  • Brother payloads are verified by size and BLAKE3-24 hash before they are staged.
  • Brother web and Yggdrasil addresses share the same RPC protocol; transport order is a rescan/config decision.
  • Public brother fallback accepts only universal tree-targz or tree-zip artifacts and returns the advertised tree hash so rescan can verify the downloaded archive before publishing.
  • This package does not parse archives and does not commit storage.

Important Files

  • obj.go: object shape, constructor, and public DTOs.
  • client.go: HTTP clients, routed dialer, probes, archive streaming.
  • discovery.go: source classification and remote-key derivation.
  • git.go, refs.go, tags.go: git provider listing and refs fallback.
  • public_mirror.go: public yggvault release API fallback for brother sources without RPC.
  • brother.go, brother_codec.go: brother session and bounded gob codec.
  • ssrf.go, auth.go, retry.go: egress guard, credentials, and retry policy.

Operational Notes

The download client intentionally has no global http.Client.Timeout; streaming is bounded by request context, archive size, idle timeout, and throughput floor. This allows large valid archives to finish while still stopping stalling or drip-fed responses.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsNotFound

func IsNotFound(err error) bool

IsNotFound reports a provider-level "resource missing" response. For Gitea/Forgejo, a disabled releases module looks like this and should trigger tag fallback.

Types

type BlobReqObj

type BlobReqObj struct {
	Hash      core.HashObj
	SizeBytes uint64
}

BlobReqObj carries a hash and size so batch size can be checked before sending.

type BrotherFetchResultObj

type BrotherFetchResultObj struct {
	Blobs []core.StagedBlobObj
}

BrotherFetchResultObj contains staged blobs written to the spool and verified by hash24.

type BrotherIndexEntryObj

type BrotherIndexEntryObj struct {
	Version      string
	ReleaseNotes string
	TreeHash     core.HashObj
	SourceHash   core.HashObj
	UpstreamSeq  int64 // brother source position; 0 means legacy node and local assignment
}

BrotherIndexEntryObj is one brother index entry with release notes and origin hashes. TreeHash enables index-driven skips without fetching the tree; SourceHash carries archive provenance. A zero SourceHash means the brother is old or did not report it.

type BrotherSessionInterface

type BrotherSessionInterface interface {
	Hello(ctx context.Context) (HelloResultObj, error)
	Index(ctx context.Context, page uint32) ([]BrotherIndexEntryObj, uint32, BrotherSourceInfoObj, error)
	Version(ctx context.Context, version string) (BrotherVersionObj, error)
	BlobsFetch(ctx context.Context, version string, reqs []BlobReqObj, destDir string) (BrotherFetchResultObj, error)
	FetchLimits() (uint64, uint)
	// Healthy reports whether the session has seen no transport/context error.
	Healthy() bool
	Close() error
}

BrotherSessionInterface defines the brother session contract for rescan. BrotherDial returns the interface so callers and tests do not depend on the concrete session type.

type BrotherSessionObj

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

BrotherSessionObj describes a single brother session over net/rpc via CONNECT, hijack and gob. localKey is used for diagnostics; remoteKey goes into RPC arguments.

The session is not concurrent-safe: a single goroutine owns it, and mu makes each call atomic. Invariant errors do not kill the session; transport/context errors mark it unhealthy for a rescan redial.

func (*BrotherSessionObj) BlobsFetch

func (s *BrotherSessionObj) BlobsFetch(ctx context.Context, version string, reqs []BlobReqObj, destDir string) (BrotherFetchResultObj, error)

BlobsFetch requests sized blobs, verifies their hash24 and writes them to destDir. The total declared size is checked before sending so that valid large batches fit within the codec cap. Unrequested, duplicate or oversized blobs are dropped or map to brother_rpc_invalid.

func (*BrotherSessionObj) Close

func (s *BrotherSessionObj) Close() error

Close idempotently closes the rpc client and the connection.

func (*BrotherSessionObj) FetchLimits

func (s *BrotherSessionObj) FetchLimits() (uint64, uint)

FetchLimits returns effective blob fetch limits for this session.

func (*BrotherSessionObj) Healthy

func (s *BrotherSessionObj) Healthy() bool

Healthy reports whether the session is fit for further use.

func (*BrotherSessionObj) Hello

Hello returns the protocol and instance checksums; callers validate the protocol.

func (*BrotherSessionObj) Index

Index returns one page of remoteKey versions with release notes and source info. NextPage==0 means the end; otherwise the page must strictly increase. Non-monotonic pages, oversize fields or source info map to brother_rpc_invalid.

func (*BrotherSessionObj) Version

func (s *BrotherSessionObj) Version(ctx context.Context, version string) (BrotherVersionObj, error)

Version returns the canonical tree bytes and checks transport integrity against the declared hash24.

type BrotherSourceInfoObj

type BrotherSourceInfoObj struct {
	SourceURL string
}

BrotherSourceInfoObj is normalized source info announced by a brother for a key. It is only a hint: rescan still classifies and verifies the first source itself, so only the URL is kept.

type BrotherVersionObj

type BrotherVersionObj struct {
	TreeBytes []byte
}

BrotherVersionObj carries the canonical version tree bytes; their hash24 is verified against the declared hash inside Version before return, so the hash is not re-exposed here.

type DiscoveryResultObj

type DiscoveryResultObj struct {
	Class      stcode.SourceClassType
	RemoteKey  string
	SourceURL  string
	BrotherURL string
	WebAddr    string
	YggAddr    string
}

DiscoveryResultObj is the source classification result; rescan writes it to state. For brother sources, WebAddr and YggAddr are learned public addresses used for transport fallback. SourceURL is the first source for git and the brother URL for brother sources.

type GitFetchRequestObj

type GitFetchRequestObj struct {
	Key        string
	Version    string
	ArchiveURL string
	Format     string
	DestDir    string
}

GitFetchRequestObj requests downloading one version archive into DestDir.

type GitFetchResultObj

type GitFetchResultObj struct {
	ArchivePath string
	Format      string
	SizeBytes   uint64
}

GitFetchResultObj points to the downloaded archive; rescan performs extract and publish.

type GitReleaseObj

type GitReleaseObj struct {
	Version    string
	BodyMD     string
	ArchiveURL string
	Format     string
}

GitReleaseObj is one git-source release after prereleases have been filtered out.

type HelloResultObj

type HelloResultObj struct {
	Protocol              string
	MaxFetchResponseBytes uint64
	MaxFetchBatchCount    uint
}

HelloResultObj is normalized Brother.Hello data. Only Protocol is consumed; the brother's node-wide checksums are content-derived sync fingerprints (change on every ingest), so they are not carried here.

type MeshInterface

type MeshInterface interface {
	DialContext(ctx context.Context, network, address string) (net.Conn, error)
	OwnsHost(host string) bool
	Enabled() bool
}

MeshInterface is the narrow Yggdrasil node surface required by source. OwnsHost keeps Yggdrasil suffix policy in mesh, so source does not duplicate it.

type Obj

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

Obj is the source module facade with routed HTTP clients, RPC dialing, limits, and retry policy.

func New

func New(configObj *stconf.ConfigObj, meshNode MeshInterface, optArr ...Option) (*Obj, error)

New builds clients and limits from config. meshNode may be nil, in which case Yggdrasil hosts are rejected. It also installs the routed transport into lightweigit's package-global client for metadata calls.

func (*Obj) BrotherDial

func (obj *Obj) BrotherDial(ctx context.Context, localKey, remoteKey, brotherURL string) (BrotherSessionInterface, error)

BrotherDial opens a session via routedDial, optional TLS, CONNECT /rpc and a gob rpc.Client.

func (*Obj) Close

func (obj *Obj) Close(_ context.Context) error

Close releases idle transport connections and the package-global loader client slot.

func (*Obj) Discover

func (obj *Obj) Discover(ctx context.Context, key, rootURL string) (DiscoveryResultObj, error)

Discover classifies a source and returns facts; rescan writes state. If /health confirms yggvault but Hello is invalid, this is brother unavailability, not a silent git fallback.

func (*Obj) FetchArchive

func (obj *Obj) FetchArchive(ctx context.Context, reqObj GitFetchRequestObj) (GitFetchResultObj, error)

FetchArchive downloads a version archive into DestDir with retry, backoff, and size caps. It returns the file path; rescan owns extract, publish, and spool cleanup.

func (*Obj) PublicMirrorVersions

func (obj *Obj) PublicMirrorVersions(ctx context.Context, rootURL string, remoteKey string) ([]PublicMirrorVersionObj, bool, error)

PublicMirrorVersions reads a yggvault public API release list and resolves each version's universal archive.

func (*Obj) Refs

func (obj *Obj) Refs(ctx context.Context, sourceURL string) (map[string]string, error)

Refs returns tag-to-commit SHA from a smart-HTTP refs advertisement, like `git ls-remote --tags`. It uses one anonymous GET and works across common git forges without provider dispatch.

func (*Obj) Releases

func (obj *Obj) Releases(ctx context.Context, sourceURL string, depth uint) ([]GitReleaseObj, bool, error)

Releases lists source release versions and drops prereleases by policy. depth==0 means full history; the loader channel is closed by our goroutine. The second result reports truncation at cMaxReleases, which callers must not treat as authoritative for deletion grace.

func (*Obj) Tags

func (obj *Obj) Tags(ctx context.Context, sourceURL string, depth uint) ([]GitReleaseObj, bool, error)

Tags lists source tags as release records for keys whose upstream publishes no releases. Tags carry no prerelease flag, so storable-semver tags with a prerelease suffix are dropped for parity with the release path; non-semver names pass through as raw versions. The second result reports truncation at cMaxReleases, same semantics as Releases.

type Option

type Option func(*Obj)

Option configures Obj construction.

func WithAllowLoopback

func WithAllowLoopback() Option

WithAllowLoopback permits clearnet loopback dials for tests using httptest on 127/8. It is enforced as test-only, so accidental production use does not weaken SSRF protection.

type PublicMirrorVersionObj

type PublicMirrorVersionObj struct {
	Version      string
	ReleaseNotes string
	TreeHash     core.HashObj
	ArchiveURL   string
	Format       string
}

PublicMirrorVersionObj is one version advertised by a yggvault public API fallback.

Jump to

Keyboard shortcuts

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