remote

package
v1.9.0 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

Documentation

Overview

Package remote resolves Rack remote configuration and credentials for the Spool CLI without ever persisting secrets to durable repository state.

Index

Constants

View Source
const CorrelationIDHeader = "X-Correlation-Id"

CorrelationIDHeader is the HTTP header the CLI attaches to every outbound Rack request, and that Rack's audit log records, so an operator can correlate a single CLI invocation's requests (healthz, push, pull, ...) with the audit events it produced remotely.

View Source
const RedactedPlaceholder = "***redacted***"

RedactedPlaceholder replaces any redacted secret value.

Variables

View Source
var (
	// ErrBranchAlreadyExists reports that Rack rejected a branch creation
	// because a branch with that name already exists.
	ErrBranchAlreadyExists = errors.New("rack branch already exists")
	// ErrBranchSourceNotFound reports that Rack rejected a branch creation
	// because its named source branch or commit does not exist.
	ErrBranchSourceNotFound = errors.New("rack branch source not found")
	// ErrBranchNotFound reports that Rack has no such branch for the
	// configured repository.
	ErrBranchNotFound = errors.New("rack remote has no such branch")
	// ErrBranchRejected reports that Rack rejected a branch lifecycle
	// request for a reason other than the typed cases above.
	ErrBranchRejected = errors.New("rack rejected the branch request")
	// ErrBranchSourceRequired reports a branch creation request that named
	// neither a source branch nor a source commit.
	ErrBranchSourceRequired = errors.New("branch source is required")
	// ErrBranchSourceAmbiguous reports a branch creation request that named
	// both a source branch and a source commit.
	ErrBranchSourceAmbiguous = errors.New("branch source must identify a branch or commit, not both")
)
View Source
var (
	// ErrInvalidConfig reports a remote configuration that fails validation.
	ErrInvalidConfig = errors.New("remote configuration is invalid")
	// ErrSecretLikeValue reports a non-secret field whose value resembles a
	// credential and must not be persisted to repository configuration.
	ErrSecretLikeValue = errors.New("value looks like a credential and cannot be stored as remote configuration")
)
View Source
var ErrCredentialNotFound = errors.New("no credential available for remote authentication")

ErrCredentialNotFound reports that no credential could be resolved from any configured source.

View Source
var ErrInvalidPullEnvelope = errors.New("rack pull response is invalid")

ErrInvalidPullEnvelope indicates Rack's pull response body was malformed, non-canonically encoded, or internally inconsistent (a declared pack length or hash that does not match the transmitted bytes).

View Source
var ErrPullBranchNotFound = errors.New("rack remote has no such branch")

ErrPullBranchNotFound reports that Rack has no such branch for the configured repository.

View Source
var ErrPullRejected = errors.New("rack rejected the pull")

ErrPullRejected reports that Rack rejected a pull for a reason other than divergence or a missing branch.

View Source
var ErrPushRejected = errors.New("rack rejected the push")

ErrPushRejected reports that Rack rejected a push for a reason other than a non-fast-forward base (e.g. a malformed pack or invalid metadata).

View Source
var ErrRemoteUnreachable = errors.New("rack remote is unreachable")

ErrRemoteUnreachable reports that the configured Rack endpoint could not be reached or returned an unusable response.

Functions

func DeleteBranch

func DeleteBranch(ctx context.Context, client *Client, cfg Config, credential string, name string) error

DeleteBranch deletes the named remote branch from cfg.Endpoint/cfg.RepoID. credential, when non-empty, is attached per cfg.AuthMode. An attempt to delete the repository's protected default branch is returned as a *ProtectedBranchError (use errors.As); a missing branch is wrapped in ErrBranchNotFound; any other rejection is wrapped in ErrBranchRejected.

func EnvVar

func EnvVar(mode AuthMode) (string, error)

EnvVar returns the conventional environment variable name used to resolve a credential for mode.

func Redact

func Redact(text string, secrets ...string) string

Redact scrubs every non-empty value in secrets from text, replacing each occurrence with RedactedPlaceholder. It is used to keep bearer tokens, API keys, and sensitive tenant identifiers out of errors, JSON stdout, and structured logs.

func StreamAsset added in v1.8.0

func StreamAsset(ctx context.Context, client *Client, cfg Config, credential, hash string) (io.ReadCloser, int64, string, error)

StreamAsset fetches an asset blob from Spool Rack on demand.

func UploadAsset added in v1.8.0

func UploadAsset(ctx context.Context, client *Client, cfg Config, credential, hash, contentType string, r io.Reader) error

UploadAsset streams an asset blob to Spool Rack.

Types

type AssetNegotiationRequest added in v1.8.0

type AssetNegotiationRequest struct {
	Hashes []string         `json:"hashes"`
	Sizes  map[string]int64 `json:"sizes,omitempty"`
}

AssetNegotiationRequest specifies candidate asset hashes and optional sizes.

type AssetNegotiationResult added in v1.8.0

type AssetNegotiationResult struct {
	Missing          []string          `json:"missing"`
	Existing         []string          `json:"existing"`
	UploadAuthorized bool              `json:"uploadAuthorized"`
	Tokens           map[string]string `json:"tokens,omitempty"`
}

AssetNegotiationResult is returned by Spool Rack during pre-flight push negotiation.

func NegotiateAssets added in v1.8.0

func NegotiateAssets(ctx context.Context, client *Client, cfg Config, credential string, req AssetNegotiationRequest) (AssetNegotiationResult, error)

NegotiateAssets executes pre-flight negotiation with Spool Rack to identify missing asset blobs.

type AuthMode

type AuthMode string

AuthMode names a supported Rack authentication scheme.

const (
	// AuthModeBearer authenticates with a bearer token.
	AuthModeBearer AuthMode = "bearer"
	// AuthModeAPIKey authenticates with a static API key.
	AuthModeAPIKey AuthMode = "api_key"
)

type BranchCreateRequest

type BranchCreateRequest struct {
	Name         string `json:"name"`
	SourceBranch string `json:"sourceBranch,omitempty"`
	SourceCommit string `json:"sourceCommit,omitempty"`
}

BranchCreateRequest describes a new remote branch to create. Exactly one of SourceBranch or SourceCommit must be set, matching the local branch.Source contract.

type BranchListEntry

type BranchListEntry struct {
	Name       string `json:"name"`
	HeadCommit string `json:"headCommit"`
	Default    bool   `json:"default,omitempty"`
}

BranchListEntry is one branch in a BranchListResult.

type BranchListResult

type BranchListResult struct {
	Branches []BranchListEntry `json:"branches"`
}

BranchListResult is Rack's response to a remote branch list request.

func ListBranches

func ListBranches(ctx context.Context, client *Client, cfg Config, credential string) (BranchListResult, error)

ListBranches lists every remote branch for cfg.Endpoint/cfg.RepoID. credential, when non-empty, is attached per cfg.AuthMode.

type BranchResult

type BranchResult struct {
	Name       string `json:"name"`
	HeadCommit string `json:"headCommit"`
}

BranchResult identifies a remote branch and the wire commit ID it currently points at.

func CreateBranch

func CreateBranch(ctx context.Context, client *Client, cfg Config, credential string, req BranchCreateRequest) (BranchResult, error)

CreateBranch creates a remote branch named req.Name from exactly one of req.SourceBranch or req.SourceCommit against cfg.Endpoint/cfg.RepoID. credential, when non-empty, is attached per cfg.AuthMode. An existing branch of the same name is wrapped in ErrBranchAlreadyExists; a missing source is wrapped in ErrBranchSourceNotFound; any other rejection is wrapped in ErrBranchRejected; an unreachable or malformed-response remote is wrapped in ErrRemoteUnreachable.

func DefaultBranch

func DefaultBranch(ctx context.Context, client *Client, cfg Config, credential string) (BranchResult, error)

DefaultBranch discovers the default branch for cfg.Endpoint/cfg.RepoID. credential, when non-empty, is attached per cfg.AuthMode.

type Client

type Client struct {
	HTTPClient *http.Client
	// CorrelationID is sent as CorrelationIDHeader on every request this
	// Client issues. Callers should construct one Client per CLI command
	// invocation (via NewClient) and reuse it for every remote call that
	// invocation makes, so Rack's audit log can be correlated end to end.
	CorrelationID string
}

Client performs HTTP calls against a configured Rack endpoint.

func NewClient

func NewClient() *Client

NewClient returns a Client with a bounded default timeout and a freshly generated CorrelationID.

type CloneResult added in v1.6.0

type CloneResult struct {
	Branch        string
	DefaultBranch string
	HeadCommit    string
	Packs         [][]byte
	Empty         bool
}

CloneResult is the outcome of a successful clone HTTP call.

func Clone added in v1.6.0

func Clone(ctx context.Context, client *Client, cfg Config, credential string, branch string) (CloneResult, error)

Clone fetches the complete history for branch (or the remote default branch if empty) from cfg.Endpoint/cfg.WorkspaceOrRepoID(). If the remote server does not yet support the direct clone endpoint (returns 404), Clone transparently falls back to discovering the default branch via DefaultBranch and fetching its full history via Pull.

type Config

type Config struct {
	// Endpoint is the Rack HTTP(S) base URL.
	Endpoint string `toml:"endpoint"`
	// TenantID is the optional Rack tenant identity.
	TenantID string `toml:"tenant_id,omitempty"`
	// WorkspaceID is the Rack workspace identity.
	WorkspaceID string `toml:"workspace_id,omitempty"`
	// RepoID is the legacy Rack repository identity kept for backwards compatibility.
	RepoID string `toml:"repo_id,omitempty"`
	// AuthMode selects how credentials for this remote are authenticated.
	AuthMode AuthMode `toml:"auth_mode"`
}

Config is the versioned, non-secret Rack remote configuration persisted in a repository's control state. It never contains credentials.

func (Config) Validate

func (c Config) Validate() error

Validate reports whether c is a well-formed, non-secret remote configuration. It rejects missing fields, malformed endpoints, unsupported auth modes, and values that look like pasted-in credentials.

func (Config) WorkspaceOrRepoID added in v1.6.0

func (c Config) WorkspaceOrRepoID() string

WorkspaceOrRepoID returns WorkspaceID if set, falling back to RepoID.

type Credential

type Credential struct {
	Value  string
	Source CredentialSource
}

Credential is a resolved secret value and the source it came from. It is never persisted to disk.

func ResolveCredential

func ResolveCredential(repoID string, mode AuthMode, opts ResolveOptions) (Credential, error)

ResolveCredential resolves a credential for repoID under mode, trying the OS keychain, then the conventional environment variable, then an interactive prompt, in that order. The resolved value is never persisted to disk. It returns ErrCredentialNotFound if no source yields a value.

type CredentialSource

type CredentialSource string

CredentialSource identifies where a resolved credential came from.

const (
	// CredentialSourceKeychain reports a credential resolved from the OS
	// keychain or secret store.
	CredentialSourceKeychain CredentialSource = "keychain"
	// CredentialSourceEnv reports a credential resolved from an explicit
	// process environment variable.
	CredentialSourceEnv CredentialSource = "env"
	// CredentialSourceInteractive reports a credential resolved from an
	// interactive TTY prompt.
	CredentialSourceInteractive CredentialSource = "interactive"
)

type DivergedError

type DivergedError struct {
	ActualHead    string
	Guidance      string
	CorrelationID string
}

DivergedError reports that the caller's supplied known commit is not an ancestor of Rack's current branch head. ActualHead is Rack's current wire head commit ID for the branch. CorrelationID is the value Rack's audit log recorded for this request.

func (*DivergedError) Error

func (e *DivergedError) Error() string

type FieldReport

type FieldReport struct {
	Local  uint32      `json:"local"`
	Remote *uint32     `json:"remote,omitempty"`
	Status FieldStatus `json:"status"`
}

FieldReport is the negotiated status of one graphcontract version field.

type FieldStatus

type FieldStatus string

FieldStatus reports how a single graphcontract version field compares against this build's constants.

const (
	// FieldStatusMatch reports that the remote's reported version equals
	// this build's constant.
	FieldStatusMatch FieldStatus = "match"
	// FieldStatusMismatch reports that the remote's reported version
	// differs from this build's constant.
	FieldStatusMismatch FieldStatus = "mismatch"
	// FieldStatusUnknown reports that the remote's response omitted the
	// field, so no comparison could be made.
	FieldStatusUnknown FieldStatus = "unknown"
)

type KeychainStore

type KeychainStore interface {
	Lookup(service, account string) (string, error)
}

KeychainStore resolves a credential from an OS-native keychain or secret store. Lookup must return ErrCredentialNotFound when no entry exists.

type KeyringStore

type KeyringStore struct{}

KeyringStore resolves credentials from the operating system's native keychain or secret store via github.com/zalando/go-keyring.

func (KeyringStore) Lookup

func (KeyringStore) Lookup(service, account string) (string, error)

Lookup returns the secret stored for service/account, or ErrCredentialNotFound if no entry exists or the platform has no available secret store. It never returns platform-specific error types to callers.

type NonFastForwardError

type NonFastForwardError struct {
	ActualHead    string
	Guidance      string
	CorrelationID string
}

NonFastForwardError reports that Rack rejected a push because the supplied BaseCommit is no longer the branch's actual head. ActualHead is Rack's current wire head commit ID for the branch; Guidance is a human-readable message from Rack explaining the rejection; CorrelationID is the value Rack's audit log recorded for this request.

func (*NonFastForwardError) Error

func (e *NonFastForwardError) Error() string

type PromptFunc

type PromptFunc func(label string) (string, error)

PromptFunc requests a secret value interactively, typically reading hidden input from a TTY. Implementations must return ErrCredentialNotFound (or wrap it) when no interactive input is available, rather than blocking indefinitely.

func TerminalPrompt

func TerminalPrompt() PromptFunc

TerminalPrompt returns a PromptFunc that reads hidden input from stdin when it is an interactive terminal, echoing the label to stdout first. It returns ErrCredentialNotFound (wrapped) when stdin is not a terminal, rather than blocking on a pipe or redirected input.

type ProtectedBranchError

type ProtectedBranchError struct {
	Guidance      string
	CorrelationID string
}

ProtectedBranchError reports that Rack refused to delete a branch because it is the repository's protected default branch. CorrelationID lets an operator match this failure against Rack's audit log.

func (*ProtectedBranchError) Error

func (e *ProtectedBranchError) Error() string

type PullResult

type PullResult struct {
	UpToDate   bool
	HeadCommit string
	Packs      [][]byte
}

PullResult is the outcome of a successful pull HTTP call. When UpToDate is true, Packs is empty and HeadCommit is the branch's current wire commit ID. Otherwise Packs holds every hash-verified, canonically-framed v2 pack Rack sent, oldest-to-newest, and HeadCommit is the wire commit ID the caller advances to once every pack is installed.

func Pull

func Pull(ctx context.Context, client *Client, cfg Config, credential string, branch, knownCommit string) (PullResult, error)

Pull fetches new commits for branch from cfg.Endpoint/cfg.RepoID, starting after knownCommit (empty to fetch the branch's entire history). credential, when non-empty, is attached per cfg.AuthMode. A diverged known commit is returned as a *DivergedError (use errors.As); a missing branch is wrapped in ErrPullBranchNotFound; any other rejection is wrapped in ErrPullRejected; an unreachable or malformed-response remote is wrapped in ErrRemoteUnreachable or ErrInvalidPullEnvelope.

type PushCommitRecord

type PushCommitRecord struct {
	ID     string               `json:"id"`
	Commit graphcontract.Commit `json:"commit"`
}

PushCommitRecord is one commit to register with Rack as part of a push, matching Rack's expected `metadata.commits[]` wire shape byte-for-byte.

type PushRequest

type PushRequest struct {
	Branch       string
	BaseCommit   string
	TargetCommit string
	Commits      []PushCommitRecord
	PackHash     string
	PackFormat   uint32
	PackData     []byte
	AssetHashes  []string
}

PushRequest describes a native push to send to a Rack remote. Branch, BaseCommit, TargetCommit, Commits, PackHash, and PackFormat become the JSON `metadata` multipart part; PackData becomes the binary `pack` part. Callers (the CLI command layer) build this from a repository.PushPack.

type PushResult

type PushResult struct {
	Branch     string `json:"branch"`
	HeadCommit string `json:"headCommit"`
}

PushResult is Rack's success response to a push: the branch that advanced and the wire commit ID it now points at.

func Push

func Push(ctx context.Context, client *Client, cfg Config, credential string, req PushRequest) (PushResult, error)

Push sends req to cfg.Endpoint/cfg.RepoID and returns Rack's response. credential, when non-empty, is attached per cfg.AuthMode. A non-fast- forward rejection is returned as a *NonFastForwardError (use errors.As); any other rejection is wrapped in ErrPushRejected; an unreachable or malformed-response remote is wrapped in ErrRemoteUnreachable.

type RackError

type RackError struct {
	Code          string `json:"error"`
	Message       string `json:"message"`
	CorrelationID string `json:"correlationId,omitempty"`
	CurrentHead   string `json:"currentHead,omitempty"`
}

RackError is the decoded form of Rack's JSON error envelope:

{"error": "...", "message": "...", "correlationId": "...", "currentHead": "..."}

Code is a stable, machine-readable identifier; Message is a human-readable description; CorrelationID lets an operator match this failure against Rack's audit log; CurrentHead, when present, is the branch's actual wire head commit ID (populated on conflict/non-fast-forward responses).

func (*RackError) Error

func (e *RackError) Error() string

Error implements the error interface, preferring the human-readable message but falling back to the machine-readable code.

type ResolveOptions

type ResolveOptions struct {
	// Keychain resolves a credential from an OS secret store.
	Keychain KeychainStore
	// Getenv resolves a credential from the process environment. Defaults to
	// os.Getenv when nil via ResolveCredential's caller.
	Getenv func(string) string
	// Prompt resolves a credential interactively as a last resort.
	Prompt PromptFunc
}

ResolveOptions configures ResolveCredential's credential sources. Any field may be nil/zero to skip that source.

type VersionReport

type VersionReport struct {
	PackFormatVersion         FieldReport `json:"packFormatVersion"`
	PackIndexFormatVersion    FieldReport `json:"packIndexFormatVersion"`
	PackManifestFormatVersion FieldReport `json:"packManifestFormatVersion"`
}

VersionReport is the full graphcontract version negotiation result for one remote.

func NegotiateVersions

func NegotiateVersions(ctx context.Context, client *Client, cfg Config, credential string) (VersionReport, error)

NegotiateVersions probes cfg.Endpoint's /healthz and compares any reported graphcontract version fields against this build's constants. credential, when non-empty, is attached to the request per cfg.AuthMode. A remote response that omits a version field is reported explicitly as FieldStatusUnknown rather than causing an error; a remote that cannot be reached at all, or that returns a non-200/undecodable response, causes NegotiateVersions to return a wrapped ErrRemoteUnreachable instead of a VersionReport, which callers should handle explicitly (e.g. `spl remote show` reports this as an "unreachable" status rather than failing).

Jump to

Keyboard shortcuts

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