nsresolve

package
v0.7.22 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2026 License: AGPL-3.0 Imports: 7 Imported by: 0

Documentation

Overview

Package nsresolve is the transport-free namespace-resolution core behind the config-handshake redesign. It turns the project facts a client sends (POST /v1/handshake) into the one namespace that caller should use, applying the precedence pin > env > declared > derive > key-default > server-default.

It deliberately owns no I/O: the only way it reaches the pin table is the PinLookup callback the caller supplies, so the package stays a pure function of (facts, settings, callback results) — the same inputs always produce the same Result, which is what lets the handshake be deterministic and side-effect-free. Its only dependencies are internal/store (for the ClientSettings type that carries namespace_scope/namespace_prefix) and internal/httputil (namespace normalize/validate, standard-library only).

Index

Constants

View Source
const (
	SourcePin           = "pin"
	SourceEnv           = "env"
	SourceDeclared      = "declared"
	SourceRemote        = "remote"
	SourceToplevel      = "toplevel"
	SourceCwd           = "cwd"
	SourceKeyDefault    = "key_default"
	SourceServerDefault = "server_default"
)

Namespace-source labels, echoed back as HandshakeResponse.namespace_source. These are the spec's enum values (api/openapi.yaml) — snake_case throughout, no dashes on the wire.

Variables

View Source
var ErrInvalidInput = errors.New("nsresolve: invalid input")

ErrInvalidInput is returned by Resolve when the namespace a rule resolves to fails httputil.ValidateNamespace. The wrapped message names the offending fact (e.g. "declared_namespace", "remote_url") so the caller can turn it into a 400 that tells the operator exactly which input was bad. Callers map it with errors.Is.

Functions

func CanonicalRemote

func CanonicalRemote(url string) string

CanonicalRemote reduces a raw git remote URL to a stable, credential-free pin key of the form "host/owner/repo": scheme, user@, and :port are stripped, scp-style "host:path" colons become slashes, a trailing "/" and ".git" are removed, and the whole result is lowercased so trivial formatting or case differences between two clones of the same repo resolve to one key. Returns "" for an empty/unparseable URL.

Unlike remotePathSegments (which discards the host — only the tail matters for derivation), the pin key KEEPS the host, so github.com/acme/app and gitlab.com/acme/app are distinct pins. And unlike derivation, it lowercases: a pin must match regardless of how a remote happens to be cased.

func PinKeys

func PinKeys(f Facts) []string

PinKeys returns the pin lookup keys for these facts, in preference order: the canonical-remote key first (a repo's identity travels with it across clones and folder moves), then the toplevel-path key. Either may be absent when its fact is; the result is empty when the facts carry neither a remote nor a toplevel path (a bare directory, which can only ever derive).

Shared by Resolve (which looks these up to honor a pin) and the pin PUT/DELETE handlers (which write/delete under the same keys), so a pin created for a project is found by that same project's next handshake.

Types

type Facts

type Facts struct {
	// RemoteURL is the raw `git remote get-url origin`, unnormalized.
	RemoteURL string
	// ToplevelPath is the absolute git toplevel directory (the path pin key).
	ToplevelPath string
	// ToplevelBasename is basename(ToplevelPath), the toplevel derivation fallback.
	ToplevelBasename string
	// CwdBasename is basename(cwd), the last-resort derivation fallback.
	CwdBasename string
	// Agent is an optional per-agent suffix (sanitized, appended as a segment).
	Agent string
	// EnvNamespace is the client's MEMINI_NAMESPACE, sent so a pin can still
	// beat it server-side.
	EnvNamespace string
	// DeclaredNamespace is a namespace a gateway/CI caller declares directly.
	DeclaredNamespace string
}

Facts is what a client knows about its project and itself at handshake time. Every field is optional except that a usable resolution needs at least a cwd_basename (the last-resort derivation fallback). Raw, unnormalized values exactly as the client observed them — Resolve does the normalizing.

type PinLookup

type PinLookup func(ctx context.Context, keys []string) (namespace string, key string, ok bool, err error)

PinLookup resolves the first of keys (given in preference order) that has a pin, returning its namespace and the key that matched. ok is false when none of the keys is pinned. A nil PinLookup, or one that reports a backend without the pin capability, means "no pins" — Resolve then falls through to derivation, exactly as it does for an unpinned project.

type Result

type Result struct {
	Namespace string
	Source    string
	PinKey    string
}

Result is a resolved namespace plus why it was chosen. PinKey is set (to the pin key that matched) only when Source == SourcePin.

func Resolve

func Resolve(ctx context.Context, f Facts, pins PinLookup, s store.ClientSettings, keyDefault, serverDefault string) (Result, error)

Resolve turns project facts into the one namespace a caller should use, applying the precedence (highest first):

  1. pin — an operator-created pin for this project.
  2. env — the client's MEMINI_NAMESPACE.
  3. declared — a namespace a gateway/CI caller stated outright.
  4. derive — from the git remote (repo name, or owner-repo slug under namespace_scope=owner_repo), else the toplevel basename, else the cwd basename; then namespace_prefix is prepended and the agent suffix appended.
  5. key-default — the caller's per-key default namespace.
  6. server-default— the server-wide default namespace.

pin/env/declared/key-default/server-default are returned VERBATIM: normalized and validated, but with NO prefix and NO agent suffix — those shape only the derived name. Every path ends by normalizing (httputil.NormalizeNamespace) and validating (httputil.ValidateNamespace) the chosen value; a failure is ErrInvalidInput naming the offending fact.

It performs no writes and, given the same inputs and the same PinLookup results, always returns the same Result — the property that lets the handshake be deterministic and side-effect-free.

Jump to

Keyboard shortcuts

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