tapper

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Apr 28, 2026 License: Apache-2.0 Imports: 41 Imported by: 0

Documentation

Overview

Package tapper — browser-based OAuth2 PKCE login flow for hub auth.

AuthLogin drives the end-to-end handshake:

  1. discover the hub's authorize/token endpoints via RFC 8414 (/.well-known/oauth-authorization-server)
  2. generate PKCE verifier/challenge + CSRF state
  3. bind a loopback listener on 127.0.0.1:0 and derive redirect_uri
  4. open the user's browser to the discovered authorization endpoint
  5. accept exactly one callback, validate state, extract the code
  6. exchange the code at the discovered token endpoint for an access token

The flow returns a populated AuthEntry on success. It never touches the on-disk AuthStore — persistence is the caller's job — so callers can compose the flow into a dry-run, an inspect command, or the `tap auth login` CLI path without the flow knowing which.

All external dependencies (listener, browser opener, HTTP client, entropy source) are injected via AuthLoginOptions and default to production values when nil. That keeps the flow trivially testable against an httptest.Server mock hub.

Package tapper — hub authentication store.

AuthStore is the on-disk credentials cache used by the hub-auth flow. It maps a canonical hub URL to an access token and token metadata. The store is intentionally minimal — it does not know how to refresh tokens, talk to a hub, or check expiry. Those concerns live in the caller so the store can stay time-agnostic (no clock dependency) and therefore trivially testable under a frozen clock.

File layout: YAML at <StateRoot>/auth.yaml, mode 0600, parent dir 0700 when we create it. Writes go through rt.AtomicWriteFile so a crash or concurrent reader never sees a half-written credential file.

Package tapper — PKCE (RFC 7636) primitives and CSRF state helpers.

These are the pure, dependency-free building blocks for the hub auth flow. They accept an io.Reader so tests can inject a deterministic entropy source; production callers pass crypto/rand.Reader.

Why kept separate from auth_flow.go: PKCE math has well-known RFC 7636 test vectors we want to pin in isolation. A bug in the challenge derivation would otherwise hide behind the full login flow's mocks.

Index

Constants

View Source
const (
	TapConfigSchemaURL = "https://raw.githubusercontent.com/jlrickert/tapper/main/schemas/tap-config.json"

	// DefaultHubURL is the compiled-in fallback hub used by ResolveLoginHubURL
	// when no explicit hub, defaultHub, or single-entry hub is configured and
	// the implicit default has not been disabled. The constant exists so the
	// fallback target is auditable: a deployment that needs to prove no
	// implicit network calls happen sets disableDefaultHub: true (or
	// TAP_DISABLE_DEFAULT_HUB=1) and the chain errors out instead of falling
	// through here.
	DefaultHubURL = "https://keg.foldwise.ai"
)
View Source
const (
	// DefaultAppName is the base directory name used for Tapper user
	// configuration. Helpers use this value to build platform specific config
	// paths, for example:
	//   $XDG_CONFIG_HOME/tapper   (or ~/.config/tapper) on Unix-like systems
	//   %APPDATA%\tapper          on Windows
	// Example config file:
	//   $XDG_CONFIG_HOME/tapper/aliases.yaml
	DefaultAppName = "tapper"

	// DefaultLocalConfigDir is the directory name used for repository or
	// project local configuration.
	DefaultLocalConfigDir = ".tapper"
)

Config version strings identify KEG configuration schema versions. Each constant is a stable identifier for a particular config schema. When a new schema is introduced add a new constant and update the Config alias to point to the latest version. These values are used by parsing and migration code (for example ParseConfigData) to detect older formats and perform upgrades. Use a YYYY-MM format for easy sorting and human readability.

View Source
const (
	OrientTierMin = 0
	OrientTierMax = 2
)

OrientTierMin / OrientTierMax are the valid tier bounds for Tap.Orient. Tier 0 is purpose + active keg + rules summary; tier 1 adds linking and snapshot policy; tier 2 adds the full canonical body plus the rendered host artifact when a host is supplied. Inputs outside this range clamp to the nearest valid tier rather than erroring.

Variables

View Source
var ConfigExplainFields = []string{
	"defaultKeg",
	"fallbackKeg",
	"logFile",
	"logLevel",
	"defaultHub",
	"disableDefaultHub",
	"kegSearchPaths",
}

ConfigExplainFields lists the scalar config fields eligible for explain.

View Source
var ErrDefaultHubDisabled = errors.New("no hub configured; implicit default disabled")

ErrDefaultHubDisabled is returned by ResolveLoginHubURL when the chain would fall through to the compiled-in DefaultHubURL but the deployment has opted out via Config.DisableDefaultHub. Callers surface it verbatim so SOC2-conscious users see a stable string they can grep for.

Functions

func CanonicalHubURL added in v0.20.0

func CanonicalHubURL(s string) string

CanonicalHubURL returns the canonical form used as the AuthStore key. It strips trailing slashes, lowercases the scheme and host, and preserves the path exactly (per RFC 3986 the path is case-sensitive). Invalid URLs fall through to TrimRight'd input so callers don't have to pre-validate — they'll hit a clearer error at the login step.

func GeneratePKCEVerifier added in v0.20.0

func GeneratePKCEVerifier(reader io.Reader) (string, error)

GeneratePKCEVerifier returns a fresh RFC 7636 code_verifier drawn from reader. The verifier is base64url-encoded without padding so it is URL-safe and matches the wire format used by the challenge helper.

reader is injectable so tests can seed a deterministic source; the normal caller passes crypto/rand.Reader. Any short read is surfaced as an error — we never quietly fall back to a weaker source.

func GenerateState added in v0.20.0

func GenerateState(reader io.Reader) (string, error)

GenerateState returns a CSRF state token drawn from reader. It is base64url-encoded (no padding) so it's safe to round-trip through a URL query string without further escaping.

We don't expose the byte length as a knob: 16 bytes is the minimum that's both conventional and plenty for a short-lived state value, and callers that need more entropy can trivially generate their own.

func IntegrateHosts added in v0.19.0

func IntegrateHosts() []string

IntegrateHosts returns the sorted list of hosts that Integrate can install. Since OrientableHosts now derives directly from the adapter registry (filtered on OrientPath() != ""), the install set is the same set: an adapter is installable iff it declares an orient artifact. Callers that build CLI completion lists consult this.

func LocalGitData

func LocalGitData(ctx context.Context, rt *toolkit.Runtime, projectPath, key string) ([]byte, error)

LocalGitData attempts to run `git -C projectPath config --local --get key`.

If git is not present or the command fails it returns an error. The returned bytes are trimmed of surrounding whitespace. The function logs diagnostic messages using the logger from rt.

func NewAuthStoreTokenResolver added in v0.20.0

func NewAuthStoreTokenResolver(store *AuthStore) keg.TokenResolver

NewAuthStoreTokenResolver returns a keg.TokenResolver backed by store. A nil store yields a resolver that always returns "", matching the nil-safe contract of AuthStore itself.

func OrientableHosts added in v0.19.0

func OrientableHosts() []string

OrientableHosts returns the host names that have a configured orient surface, sorted lexicographically. Callers that enumerate (host, tier) pairs for resource registration consult this instead of walking the adapter registry themselves. The set is derived from integrations.DefaultAdapters() filtered on OrientPath() != "".

func PKCEChallenge added in v0.20.0

func PKCEChallenge(verifier string) string

PKCEChallenge derives the S256 code_challenge for a given verifier: base64url(SHA-256(verifier)), no padding. Per RFC 7636 §4.2, the hash is taken over the ASCII bytes of the verifier string itself — not over the raw entropy the verifier was encoded from.

func ResolveLoginHubURL added in v0.20.0

func ResolveLoginHubURL(cfg *Config, explicit string) (string, error)

ResolveLoginHubURL returns the hub URL the login flow should target, applying the five-step resolution chain documented in keg-dev/1035:

  1. explicit non-empty → canonicalize and use
  2. cfg.DefaultHub names a Hubs entry → use that entry's URL
  3. cfg.Hubs has exactly one entry → use it
  4. cfg.DisableDefaultHub is true → ErrDefaultHubDisabled
  5. fall back to DefaultHubURL

A misconfigured DefaultHub (set, but no matching Hubs entry) is a hard error rather than a silent fall-through to step 3 — typos should surface, not silently route to a different hub.

Returned URLs are canonicalized via CanonicalHubURL so callers can compare them against AuthStore keys without re-canonicalizing.

Types

type AuthEntry added in v0.20.0

type AuthEntry struct {
	AccessToken string    `yaml:"access_token"`
	TokenType   string    `yaml:"token_type,omitempty"`
	ExpiresAt   time.Time `yaml:"expires_at,omitempty"`
	Scope       string    `yaml:"scope,omitempty"`
}

AuthEntry is a single hub's cached credential. Plain value type — callers hold copies returned from Get, and pass values into Set.

Fields match the typical OAuth 2.0 shape so we can store whatever a hub returns without transformation. TokenType / ExpiresAt / Scope are all optional: a hub that only returns a bare bearer token will serialize as a single access_token field.

func AuthLogin added in v0.20.0

func AuthLogin(ctx context.Context, rt *toolkit.Runtime, opts AuthLoginOptions) (*AuthEntry, error)

AuthLogin runs the browser-based PKCE flow against the hub described by opts and returns a populated AuthEntry. The store is not touched — the caller persists the result via AuthStore.Set + Save.

func AuthLoginDevice added in v0.20.0

func AuthLoginDevice(ctx context.Context, rt *toolkit.Runtime, opts AuthLoginDeviceOptions) (*AuthEntry, error)

AuthLogin runs the device authorization grant against the hub described by opts and returns a populated AuthEntry. The store is not touched — the caller persists the result via AuthStore.Set + Save, mirroring the browser-based AuthLogin contract.

type AuthLoginDeviceOptions added in v0.20.0

type AuthLoginDeviceOptions struct {
	// HubURL is the hub base (e.g. "https://hub.example.com"). Required.
	HubURL string

	// ClientID is the OAuth2 public client identifier the hub will validate
	// against its registered clients. Required.
	ClientID string

	// Scope is an optional space-separated scope string. Omitted from the
	// device_authorization request when empty.
	Scope string

	// Timeout bounds the whole flow including the user's browser action;
	// zero uses defaultDeviceTimeout.
	Timeout time.Duration

	// HTTPClient executes the metadata GET, device_authorization POST, and
	// token POSTs. Default: http.DefaultClient.
	HTTPClient *http.Client

	// PromptOut is the writer that receives the user-facing prompt
	// ("Open <URL> and enter <CODE>"). Default: os.Stderr via the runtime
	// stream when Out is nil; an in-memory buffer in tests.
	PromptOut io.Writer

	// Now returns the current time. Default: time.Now. Tests inject a fake
	// clock to control timeout behavior deterministically.
	Now func() time.Time

	// Sleep blocks for d. Default: time.Sleep. Tests inject a no-op or
	// channel-coordinated stub so polling loops don't actually wait.
	Sleep func(d time.Duration)
}

AuthLoginDeviceOptions is the dependency envelope for AuthLoginDevice. Required: HubURL, ClientID. Everything else has a production default.

type AuthLoginOptions added in v0.20.0

type AuthLoginOptions struct {
	// HubURL is the hub base (e.g. "https://hub.example.com"). Required.
	// Trailing slashes are stripped; http/https schemes are enforced.
	HubURL string

	// ClientID is the OAuth2 client identifier to send to the hub. Required.
	ClientID string

	// Scope is an optional space-separated scope string. Omitted from
	// the authorize request when empty.
	Scope string

	// Timeout bounds the whole handshake; zero uses defaultAuthTimeout.
	Timeout time.Duration

	// ListenerFactory returns the loopback listener that receives the
	// /callback request. Default: net.Listen("tcp", "127.0.0.1:0").
	// Injection lets tests pin a specific address or inspect the listener.
	ListenerFactory func() (net.Listener, error)

	// BrowserOpener opens authURL in the user's browser. Default:
	// openBrowser (platform-specific exec). Tests substitute a goroutine
	// that drives authURL via http.Get.
	BrowserOpener func(ctx context.Context, rt *toolkit.Runtime, url string) error

	// HTTPClient executes the metadata GET and token POST. Default: http.DefaultClient.
	HTTPClient *http.Client

	// RandReader is the entropy source for verifier + state. Default:
	// crypto/rand.Reader. Never swap this for math/rand in production.
	RandReader io.Reader
}

AuthLoginOptions is the dependency envelope for AuthLogin. All nil / zero values use production defaults so the CLI caller can pass just HubURL + ClientID; tests fill in the injectable hooks.

type AuthLogoutOptions added in v0.20.0

type AuthLogoutOptions struct {
	// Hub is the raw or canonical hub URL. When empty and exactly one
	// hub is stored, auto-resolves to that single entry; otherwise an
	// empty Hub with multiple stored hubs surfaces a directed error.
	Hub string
}

AuthLogoutOptions selects which hub to log out of. Flat (no KegTargetOptions) because auth state is a user-level concern that spans kegs — a login is per-hub, not per-keg.

type AuthLogoutResult added in v0.20.0

type AuthLogoutResult struct {
	// Removed is true when an entry was actually deleted from the store.
	// False when the store was empty or the hub was not found; both of
	// those cases are soft-successes (no error returned).
	Removed bool
	// HubURL is the canonical hub that was targeted, or "" when the
	// store was empty.
	HubURL string
	// Formatted is the authoritative output line terminated with \n.
	// CLI routes to stdout when Removed=true, stderr otherwise.
	Formatted string
}

AuthLogoutResult is the pre-formatted output plus structured fields for callers that need to route streams or surface structured output. Formatted is authoritative: the CLI emits it verbatim to stdout when Removed=true and stderr otherwise.

type AuthStatusOptions added in v0.20.0

type AuthStatusOptions struct {
	// Hub is a raw or canonical hub URL. When empty and exactly one
	// hub is stored, AuthStatus auto-resolves to that single entry;
	// otherwise an empty Hub with zero or multiple stored hubs surfaces
	// a directed error/empty message (see AuthStatus docs).
	Hub string
}

AuthStatusOptions selects which hub to report on. Flat (no KegTargetOptions) because auth state is a user-level concern that spans kegs — a login is per-hub, not per-keg.

type AuthStatusResult added in v0.20.0

type AuthStatusResult struct {
	// Present is false when no matching entry exists (empty store
	// with no --hub, or --hub that isn't in the store).
	Present bool

	// HubURL is the canonical key that matched. Zero-value when the
	// caller omitted --hub and the store is empty.
	HubURL string

	// TokenSuffix is the last 4 chars of the access token, prefixed
	// with "..." — or "[set]" when the token is shorter than 4 chars.
	// The raw token is never exposed here.
	TokenSuffix string

	// TokenType mirrors the stored entry (e.g. "Bearer"). Empty when
	// the hub returned a bare token with no type.
	TokenType string

	// Scope mirrors the stored entry. Empty when no scope was granted.
	Scope string

	// ExpiresAt mirrors the stored entry; zero when no expiry is known.
	ExpiresAt time.Time

	// ExpiryStatus is a three-way tag: "unknown" | "valid" | "expired".
	// Made explicit so callers (and future JSON consumers) don't have
	// to re-derive from ExpiresAt vs clock.
	ExpiryStatus string

	// Formatted is the exact string the CLI prints; MCP returns it
	// verbatim as text content. Terminated with a trailing newline.
	Formatted string
}

AuthStatusResult is the pre-formatted human-readable status line plus structured fields for the MCP surface and any future renderers. Formatted is authoritative: both CLI and MCP emit it verbatim.

type AuthStore added in v0.20.0

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

AuthStore is the opaque wrapper exposing getters/setters over an authStoreDTO. All methods are nil-safe on the receiver: a (*AuthStore)(nil) reads as an empty store and writes are no-ops. This lets callers skip the "did Load return something?" dance when the file didn't exist.

func LoadAuthStore added in v0.20.0

func LoadAuthStore(ctx context.Context, rt *toolkit.Runtime, path string) (*AuthStore, error)

LoadAuthStore reads the auth store file at path and parses it. A missing file is NOT an error: we return an empty store and nil so first-run callers can treat "no file" and "empty file" identically. Every other read error is wrapped and returned.

func ParseAuthStore added in v0.20.0

func ParseAuthStore(raw []byte) (*AuthStore, error)

ParseAuthStore parses raw YAML bytes into an AuthStore. Pure: no I/O, no clock. Empty input (including whitespace-only) is treated as an empty store rather than a YAML error — first-run and "user cleared the file" look the same to us.

func (*AuthStore) Delete added in v0.20.0

func (s *AuthStore) Delete(hubURL string) bool

Delete removes the entry for hubURL. Returns whether it existed — callers can use this to decide whether to log "logged out" vs "was already logged out".

func (*AuthStore) Get added in v0.20.0

func (s *AuthStore) Get(hubURL string) (*AuthEntry, bool)

Get returns a copy of the entry for hubURL and a present flag. We return a value (not a pointer into the map) so callers can't mutate the stored entry by accident — all edits must go through Set.

func (*AuthStore) Hubs added in v0.20.0

func (s *AuthStore) Hubs() []string

Hubs returns the hub URL keys, sorted, for enumeration. Sorted output makes CLI listings stable without callers having to re-sort.

func (*AuthStore) IsEmpty added in v0.20.0

func (s *AuthStore) IsEmpty() bool

IsEmpty reports whether the store has no hubs. Used by Save to decide whether to delete the file rather than write an empty document.

func (*AuthStore) Save added in v0.20.0

func (s *AuthStore) Save(ctx context.Context, rt *toolkit.Runtime, path string) error

Save writes the store to path. When the store is empty we remove the file instead of writing an empty YAML doc — keeps the filesystem tidy after a `tap auth logout` of the last hub and makes "no file" the canonical empty state (matches LoadAuthStore's contract). Parent directory is created at 0700 if it doesn't exist. If it already exists we don't touch its mode — users/admins may have intentionally widened it, and tightening on every Save would surprise them.

func (*AuthStore) Set added in v0.20.0

func (s *AuthStore) Set(hubURL string, entry AuthEntry)

Set inserts or overwrites the entry for hubURL. Nil receiver is a no-op (matches the "read as empty" contract). We store a copy so later caller mutations don't reach into our map.

type BacklinksOptions

type BacklinksOptions struct {
	KegTargetOptions

	// NodeIDs are the target nodes to inspect incoming links for.
	// Results from all node IDs are merged and deduplicated.
	NodeIDs []string

	// Format to use. %i is node id
	// %d is date
	// %t is node title
	// %% for literal %
	Format string

	IdOnly bool

	Reverse bool

	// Limit caps the number of results returned. 0 means no limit.
	Limit int

	// Offset skips the first N results before applying limit. Must be >= 0.
	Offset int
}

type CatOptions

type CatOptions struct {
	// NodeIDs are the node identifiers to read (e.g., "0", "42").
	// Multiple IDs produce concatenated output separated by blank lines.
	NodeIDs []string

	// Tag is an optional tag expression (same syntax as tap tags) used to
	// select nodes. Mutually exclusive with NodeIDs.
	Tag string

	KegTargetOptions

	// Edit opens the node in the editor instead of printing output.
	Edit bool

	// ContentOnly displays content only.
	ContentOnly bool

	// StatsOnly displays stats only.
	StatsOnly bool

	// MetaOnly displays metadata only.
	MetaOnly bool

	// Stream carries stdin piping information when editing.
	Stream *toolkit.Stream

	// LockToken is an optional cross-process lock token for edit operations.
	LockToken string
}

type Config

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

Config represents the user's tapper configuration.

Config is a data-only model. We do not preserve YAML comments or original document formatting.

func DefaultProjectConfig

func DefaultProjectConfig(user, userKegRepo string) *Config

DefaultProjectConfig returns a project-scoped config with sensible defaults. The provided user value is used as the default/fallback alias, and the optional userKegRepo is added to kegSearchPaths.

func DefaultUserConfig

func DefaultUserConfig(name string, userRepos string) *Config

DefaultUserConfig returns a sensible default Config for a new user.

The returned Config is a fully populated in-memory config suitable as a starting point when no on-disk config is available. The DefaultHub is set to "knut", default/fallback aliases are initialized to name, and kegSearchPaths starts with userRepos.

func MergeConfig

func MergeConfig(cfgs ...*Config) *Config

MergeConfig merges multiple Config values into a single configuration.

Merge semantics:

  • Later configs override earlier values for scalar keys.
  • kegSearchPaths are appended in order with deduplication.
  • KegMap entries are appended in order, but entries with the same alias are replaced by later entries.
  • The returned Config will have a Kegs map and a KegMap slice.

func ParseConfig

func ParseConfig(raw []byte) (*Config, error)

ParseConfig parses raw YAML into a Config data model.

func ReadConfig

func ReadConfig(rt *toolkit.Runtime, path string) (*Config, error)

ReadConfig reads the YAML file at path and returns a parsed Config.

When the file does not exist the function returns a Config value and an error that wraps keg.ErrNotExist so callers can detect no-config cases.

func (*Config) AddKeg

func (cfg *Config) AddKeg(alias string, target keg.Target) error

AddKeg adds or updates a keg entry in the Config.

func (*Config) AddKegMap

func (cfg *Config) AddKegMap(entry KegMapEntry) error

AddKegMap adds or updates a keg map entry in the Config. Entries are matched by alias + pathPrefix + pathRegex. An entry with the same alias but a different path pattern is treated as a separate mapping.

func (*Config) Clone

func (cfg *Config) Clone() *Config

Clone produces a deep copy of the Config.

func (*Config) DefaultHub added in v0.20.0

func (cfg *Config) DefaultHub() string

DefaultHub returns the default hub name. The value is looked up by name in Hubs() to find the corresponding URL.

func (*Config) DefaultKeg

func (cfg *Config) DefaultKeg() string

DefaultKeg returns the alias to use when no explicit keg is provided.

func (*Config) DisableDefaultHub added in v0.20.0

func (cfg *Config) DisableDefaultHub() bool

DisableDefaultHub returns true when the compiled-in DefaultHubURL fallback is suppressed. Used by ResolveLoginHubURL to fail closed at step 4 of the resolution chain instead of falling through to step 5.

func (*Config) FallbackKeg added in v0.2.0

func (cfg *Config) FallbackKeg() string

FallbackKeg returns the last-resort keg alias.

func (*Config) Hubs added in v0.20.0

func (cfg *Config) Hubs() []KegHub

Hubs return the list of configured hubs.

func (*Config) KegMap

func (cfg *Config) KegMap() []KegMapEntry

KegMap returns the list of path/regex to keg alias mappings.

func (*Config) KegSearchPaths added in v0.2.0

func (cfg *Config) KegSearchPaths() []string

KegSearchPaths returns local discovery paths for file-backed kegs.

func (*Config) Kegs

func (cfg *Config) Kegs() map[string]keg.Target

Kegs returns a map of keg aliases to their targets.

func (*Config) ListKegs

func (cfg *Config) ListKegs() []string

ListKegs returns a sorted slice of all keg names in the configuration. Returns an empty slice if the config or its data is nil.

func (*Config) LogFile

func (cfg *Config) LogFile() string

LogFile returns the log file path.

func (*Config) LogLevel

func (cfg *Config) LogLevel() string

LogLevel returns the log level.

func (*Config) LookupAlias

func (cfg *Config) LookupAlias(rt *toolkit.Runtime, projectRoot string) string

LookupAlias returns the keg alias matching the given project root path. It first checks regex patterns in KegMap entries, then prefix matches. For multiple prefix matches, the longest matching prefix wins. Returns empty string if no match is found or config data is nil.

func (*Config) LookupAliasForTarget added in v0.6.0

func (cfg *Config) LookupAliasForTarget(rt *toolkit.Runtime, target string) string

LookupAliasForTarget returns the alias whose configured target matches the given target string. Returns empty string if no match is found.

func (*Config) PrimaryKegSearchPath added in v0.2.0

func (cfg *Config) PrimaryKegSearchPath() string

PrimaryKegSearchPath returns the first configured local discovery path.

func (*Config) RemoveKeg added in v0.4.0

func (cfg *Config) RemoveKeg(alias string) error

RemoveKeg removes a keg entry from the Config by alias.

Returns an error when the alias is not registered.

func (*Config) ResolveAlias

func (cfg *Config) ResolveAlias(alias string) (*keg.Target, error)

ResolveAlias looks up the keg by alias and returns a parsed Target.

Returns (nil, error) when not found or parse fails.

func (*Config) ResolveDefault

func (cfg *Config) ResolveDefault(rt *toolkit.Runtime) (*keg.Target, error)

ResolveDefault resolves the current DefaultKeg alias to a target.

func (*Config) ResolveKegMap

func (cfg *Config) ResolveKegMap(rt *toolkit.Runtime, projectRoot string) (*keg.Target, error)

ResolveKegMap chooses the appropriate keg (via alias) based on path.

Precedence rules:

  1. Regex entries in KegMap have the highest precedence.
  2. PathPrefix entries are considered next; when multiple prefixes match the longest prefix wins.
  3. If no entry matches, resolution returns an alias-not-found error.

The function expands env vars and tildes prior to comparisons, so stored prefixes and patterns may contain ~ or $VAR values.

func (*Config) SetDefaultHub added in v0.20.0

func (cfg *Config) SetDefaultHub(_ context.Context, hub string) error

SetDefaultHub sets the default hub.

func (*Config) SetDefaultKeg

func (cfg *Config) SetDefaultKeg(keg string) error

SetDefaultKeg sets the alias used when no explicit keg is provided.

func (*Config) SetDisableDefaultHub added in v0.20.0

func (cfg *Config) SetDisableDefaultHub(disable bool) error

SetDisableDefaultHub toggles the compiled-in DefaultHubURL fallback.

func (*Config) SetFallbackKeg added in v0.2.0

func (cfg *Config) SetFallbackKeg(keg string) error

SetFallbackKeg sets the fallback keg alias.

func (*Config) SetKegSearchPaths added in v0.2.0

func (cfg *Config) SetKegSearchPaths(paths []string) error

SetKegSearchPaths sets local discovery paths for file-backed kegs.

func (*Config) SetLogFile

func (cfg *Config) SetLogFile(_ context.Context, path string) error

SetLogFile sets the log file path.

func (*Config) SetLogLevel

func (cfg *Config) SetLogLevel(level string) error

SetLogLevel sets the log level.

func (*Config) ToYAML

func (cfg *Config) ToYAML() ([]byte, error)

ToYAML serializes the Config to YAML bytes.

func (*Config) Touch

func (cfg *Config) Touch(rt *toolkit.Runtime)

Touch updates the Updated timestamp on the Config using the runtime clock.

func (*Config) Updated

func (cfg *Config) Updated() time.Time

Updated returns the last update timestamp.

func (*Config) Write

func (cfg *Config) Write(rt *toolkit.Runtime, path string) error

Write writes the Config back to path using atomic replacement.

type ConfigEditOptions

type ConfigEditOptions struct {
	// Project indicates whether to edit local config instead of user config
	Project bool

	User bool

	ConfigPath string

	Stream *toolkit.Stream
}

ConfigEditOptions configures behavior for Tap.ConfigEdit.

type ConfigExplainOptions added in v0.17.0

type ConfigExplainOptions struct {
	// Field limits the result to a single field. Empty means all fields.
	Field string
}

ConfigExplainOptions configures behavior for Tap.ConfigExplain.

type ConfigExplainResult added in v0.17.0

type ConfigExplainResult struct {
	Field  string // field name (e.g. "defaultKeg")
	Value  string // resolved value in the merged config
	Source string // which provider set this value ("user config", "project config", "env vars", "default")
}

ConfigExplainResult describes the provenance of a single config field.

type ConfigLoadWarning added in v0.17.0

type ConfigLoadWarning struct {
	Source  string // "user config" or "project config"
	Path    string // file path that caused the issue
	Message string // human-readable description
	Err     error  // underlying error
}

ConfigLoadWarning represents a non-fatal issue encountered while loading config.

type ConfigOptions

type ConfigOptions struct {
	// Project indicates whether to display project config
	Project bool

	// User indicates whether to display user config
	User bool

	// ConfigPath directly selects a config file to display.
	ConfigPath string
}

type ConfigService

type ConfigService struct {
	Runtime *toolkit.Runtime

	PathService *PathService

	// ConfigPath is the path to the config file.
	ConfigPath string

	// LoadWarnings accumulates non-fatal issues from the last Config() call.
	// Missing config files are not warnings (graceful degradation). Corrupt
	// YAML, permission errors, etc. are recorded here.
	LoadWarnings []ConfigLoadWarning

	// ResolvedSources lists provider names that contributed to the merged config,
	// most-specific first. Populated after Config() runs the cascade.
	ResolvedSources []string
	// contains filtered or unexported fields
}

ConfigService loads, merges, and resolves tapper configuration state.

func NewConfigService

func NewConfigService(root string, rt *toolkit.Runtime) (*ConfigService, error)

NewConfigService builds a ConfigService rooted at root.

func (*ConfigService) Config

func (s *ConfigService) Config(cache bool) (*Config, error)

Config returns the merged user and project configuration with optional caching. If cache is true and a merged config exists, it returns the cached version. Otherwise, it uses a cfgcascade.Cascade to resolve configuration from three providers in rank order: user config file, project config file, TAP_* env vars. When ConfigPath is set, it directly reads that file and bypasses the cascade.

func (*ConfigService) DiscoveredKegAliases added in v0.2.0

func (s *ConfigService) DiscoveredKegAliases(cache bool) ([]string, error)

DiscoveredKegAliases returns aliases discovered from configured kegSearchPaths.

func (*ConfigService) ProjectConfig

func (s *ConfigService) ProjectConfig(cache bool) (*Config, error)

ProjectConfig returns the project-level configuration with optional caching. If cache is true and a cached config exists, it returns the cached version. Otherwise, it reads the config from the local config root and caches the result.

func (*ConfigService) ResetCache

func (s *ConfigService) ResetCache()

ResetCache clears cached user, project, and merged configs.

func (*ConfigService) ResolveTarget

func (s *ConfigService) ResolveTarget(alias string, cache bool) (*keg.Target, error)

ResolveTarget resolves an alias to a keg target. Resolution order is: explicit configured alias, discovered local keg alias. When alias is empty it uses defaultKeg, then fallbackKeg.

func (*ConfigService) UserConfig

func (s *ConfigService) UserConfig(cache bool) (*Config, error)

UserConfig returns the global user configuration.

type ConfigTemplateOptions added in v0.4.0

type ConfigTemplateOptions struct {
	Project bool
}

type ConfigWarning added in v0.17.0

type ConfigWarning struct {
	Field   string // config field name (e.g., "kegMap[0]", "logLevel")
	Message string // human-readable description
}

ConfigWarning represents a semantic issue found during config validation.

func ValidateConfig added in v0.17.0

func ValidateConfig(cfg *Config) []ConfigWarning

ValidateConfig checks a Config for semantic issues that are valid YAML but likely mistakes. It returns warnings, not errors — the config is still usable.

type CreateOptions

type CreateOptions struct {
	KegTargetOptions

	Title  string
	Lead   string
	Tags   []string
	Attrs  map[string]string
	Stream *toolkit.Stream
}

type DeleteFileOptions added in v0.2.0

type DeleteFileOptions struct {
	KegTargetOptions
	NodeID string
	Name   string
}

DeleteFileOptions configures behavior for Tap.DeleteFile.

type DeleteImageOptions added in v0.2.0

type DeleteImageOptions struct {
	KegTargetOptions
	NodeID string
	Name   string
}

DeleteImageOptions configures behavior for Tap.DeleteImage.

type DirOptions

type DirOptions struct {
	KegTargetOptions

	NodeID string
}

type DoctorOptions added in v0.5.0

type DoctorOptions struct {
	KegTargetOptions
}

DoctorOptions configures behavior for Tap.Doctor.

type DownloadFileOptions added in v0.2.0

type DownloadFileOptions struct {
	KegTargetOptions
	NodeID string
	Name   string
	Dest   string
}

DownloadFileOptions configures behavior for Tap.DownloadFile.

type DownloadImageOptions added in v0.2.0

type DownloadImageOptions struct {
	KegTargetOptions
	NodeID string
	Name   string
	Dest   string
}

DownloadImageOptions configures behavior for Tap.DownloadImage.

type EditOptions

type EditOptions struct {
	// NodeID is the node identifier to edit (e.g., "0", "42")
	NodeID string

	KegTargetOptions

	// LockToken is an optional cross-process lock token. When provided, the
	// command validates it against any held lock before proceeding.
	LockToken string

	// Stream carries stdin piping information.
	Stream *toolkit.Stream
}

type ExportOptions added in v0.4.0

type ExportOptions struct {
	KegTargetOptions
	NodeIDs     []string
	WithHistory bool
	OutputPath  string
}

type ForceUnlockOptions added in v0.11.0

type ForceUnlockOptions struct {
	NodeID string
	KegTargetOptions
}

ForceUnlockOptions configures behavior for Tap.ForceUnlock.

type GraphOptions added in v0.4.0

type GraphOptions struct {
	KegTargetOptions

	// BundleJS is the compiled browser renderer injected into the generated page.
	BundleJS []byte
}

GraphOptions configures graph HTML generation for a resolved keg.

type GrepOptions

type GrepOptions struct {
	KegTargetOptions

	// Query is the regex pattern used to search nodes.
	Query string

	// Format to use. %i is node id
	// %d is date
	// %t is node title
	// %% for literal %
	Format string

	IdOnly bool

	Reverse bool

	// IgnoreCase enables case-insensitive regex matching.
	IgnoreCase bool

	// MaxLines caps the number of matched lines returned per node.
	// 0 means unlimited. When > 0, only the first MaxLines matching lines
	// are included per node.
	MaxLines int

	// Limit caps the number of results returned. 0 means no limit.
	Limit int

	// Offset skips the first N results before applying limit. Must be >= 0.
	Offset int
}

type ImportFromKegOptions added in v0.4.0

type ImportFromKegOptions struct {
	// Source is the source keg to copy nodes from.
	Source KegTargetOptions
	// Target is the destination keg; defaults to the resolved default keg.
	Target KegTargetOptions
	// NodeIDs lists the source node IDs to import. Values may be bare integers
	// ("5") or cross-keg references ("keg:pub/5"). All must resolve to Source.
	// When empty and TagQuery is also empty, all non-zero nodes are imported.
	NodeIDs []string
	// TagQuery is a boolean tag expression (same syntax as tap tags EXPR) that
	// selects additional source nodes; combined with NodeIDs as a union.
	TagQuery string
	// LeaveStubs writes a forwarding stub at each source node location after import.
	LeaveStubs bool
	// SkipZeroNode skips the source keg's node 0 (the index/root node).
	SkipZeroNode bool
}

ImportFromKegOptions controls how ImportFromKeg copies nodes from one live keg into another.

type ImportOptions added in v0.4.0

type ImportOptions struct {
	KegTargetOptions
	Input string
}

type ImportedNode added in v0.4.0

type ImportedNode struct {
	SourceID keg.NodeId
	TargetID keg.NodeId
}

ImportedNode records the source → target ID mapping for one imported node.

type IndexCatOptions added in v0.2.0

type IndexCatOptions struct {
	KegTargetOptions

	// Name is the index file name to dump, e.g. "changes.md" or "nodes.tsv".
	// A leading "dex/" prefix is stripped automatically.
	Name string
}

type IndexOptions

type IndexOptions struct {
	KegTargetOptions

	// NoUpdate skips updating node meta information
	NoUpdate bool
}

type InfoOptions

type InfoOptions struct {
	KegTargetOptions

	// Minimal strips large sections (tags, entities, indexes) from the output,
	// returning only core config fields. Useful for MCP tools where response
	// size must stay small.
	Minimal bool
}

InfoOptions configures behavior for Tap.Info.

type InitOptions

type InitOptions struct {
	// Destination selection. Exactly one group may be set.
	Project bool
	User    bool
	Cwd     bool   // use cwd as the project root base instead of git root
	Path    string // explicit filesystem path; implies local destination
	Hub     string // non-empty selects hub destination; value is the hub name

	// Hub-specific options.
	UserName string // hub namespace
	TokenEnv string

	Creator string
	Title   string
	Keg     string
}

func (InitOptions) LocalDestination added in v0.4.0

func (o InitOptions) LocalDestination() bool

type IntegrateOptions added in v0.19.0

type IntegrateOptions struct {
	KegTargetOptions

	// Host selects which rendered tree to install. Must name a
	// registered adapter that also has an orient artifact (see
	// OrientableHosts).
	Host string

	// DryRun, when true, causes Integrate to return the target paths
	// it would write without actually writing any files.
	DryRun bool

	// Target overrides the default install directory for Host. When
	// empty, the per-host default under the user's home directory is
	// used (see defaultIntegrateTarget).
	Target string
}

IntegrateOptions is the input to Tap.Integrate.

type Issue added in v0.5.0

type Issue struct {
	Level   string // "error" or "warning"
	Kind    string // category: "tag-missing", "entity-missing", "broken-link", etc.
	NodeID  string // "" for keg-level issues
	Message string
}

Issue represents a single problem found during a doctor check.

type KegConfigEditOptions added in v0.2.0

type KegConfigEditOptions struct {
	KegTargetOptions
	Stream *toolkit.Stream
}

KegConfigEditOptions configures behavior for Tap.KegConfigEdit.

type KegHub added in v0.20.0

type KegHub struct {
	Name     string `yaml:"name,omitempty"`
	Url      string `yaml:"url,omitempty"`
	Token    string `yaml:"token,omitempty"`
	TokenEnv string `yaml:"tokenEnv,omitempty"`
}

KegHub describes a named hub configuration entry.

type KegInfoOptions

type KegInfoOptions struct {
	KegTargetOptions
}

KegInfoOptions configures behavior for Tap.KegInfo.

type KegMapEntry

type KegMapEntry struct {
	Alias      string `yaml:"alias,omitempty"`
	PathPrefix string `yaml:"pathPrefix,omitempty"`
	PathRegex  string `yaml:"pathRegex,omitempty"`
}

KegMapEntry is an entry mapping a path prefix or regex to a keg alias.

type KegService

type KegService struct {
	// Runtime provides filesystem and environment access used to resolve kegs.
	Runtime *toolkit.Runtime

	// ConfigService resolves configured keg aliases and targets.
	ConfigService *ConfigService
	// contains filtered or unexported fields
}

KegService resolves keg targets from config, project paths, and explicit filesystem locations.

func (*KegService) Resolve

func (s *KegService) Resolve(ctx context.Context, opts ResolveKegOptions) (*keg.Keg, error)

Resolve returns a keg using explicit path, project, alias, or configured fallback resolution.

type KegTargetOptions

type KegTargetOptions struct {
	// Keg is the configured alias.
	Keg string

	// Project resolves using project-local keg discovery.
	Project bool

	// Cwd resolves project keg at the current working directory instead of git root.
	// Works standalone or combined with Project.
	Cwd bool

	// Path is an explicit local project path used for project keg discovery.
	Path string

	// Flight is a cross-keg work-scope identifier. It is mutually
	// exclusive with Keg, Project, Cwd, and Path at the CLI: a flight
	// names a manifest that spans kegs, while the other fields pin
	// resolution to a single keg. Today only Tap.Orient consults it;
	// other surfaces hold the slot for future manifest-aware commands.
	Flight string
}

KegTargetOptions describes how a command should resolve a keg target.

type LinksOptions added in v0.5.0

type LinksOptions struct {
	KegTargetOptions

	// NodeIDs are the source nodes to inspect outgoing links for.
	// Results from all node IDs are merged and deduplicated.
	NodeIDs []string

	// Format to use. %i is node id
	// %d is date
	// %t is node title
	// %% for literal %
	Format string

	IdOnly bool

	Reverse bool

	// Limit caps the number of results returned. 0 means no limit.
	Limit int

	// Offset skips the first N results before applying limit. Must be >= 0.
	Offset int
}

type ListFilesOptions added in v0.2.0

type ListFilesOptions struct {
	KegTargetOptions
	NodeID string
}

type ListImagesOptions added in v0.2.0

type ListImagesOptions struct {
	KegTargetOptions
	NodeID string
}

ListImagesOptions configures behavior for Tap.ListImages.

type ListOptions

type ListOptions struct {
	KegTargetOptions

	// Query is an optional boolean expression that filters nodes. Supports both
	// plain tag names ("golang") and key=value attribute predicates
	// ("entity=plan"). When empty, all nodes are listed.
	Query string

	// Format to use. %i is node id, %d
	// %i is node id
	// %d is date
	// %t is node title
	// %% for literal %
	Format string

	IdOnly bool

	Reverse bool

	// Sort selects the sort order. Empty string means sort by node ID (default).
	Sort ListSortType

	// Limit caps the number of results returned. 0 means no limit.
	Limit int

	// Offset skips the first N results before applying limit. Must be >= 0.
	Offset int
}

type ListSortType added in v0.5.0

type ListSortType string

ListSortType controls the ordering of listed nodes.

const (
	SortByDefault  ListSortType = ""         // default: same as SortByID
	SortByID       ListSortType = "id"       // ascending node ID
	SortByUpdated  ListSortType = "updated"  // ascending by last-updated timestamp
	SortByCreated  ListSortType = "created"  // ascending by creation timestamp
	SortByAccessed ListSortType = "accessed" // ascending by last-accessed timestamp
)

type LockOptions added in v0.11.0

type LockOptions struct {
	NodeID string
	KegTargetOptions
}

LockOptions configures behavior for Tap.Lock.

type LockStatusOptions added in v0.11.0

type LockStatusOptions struct {
	NodeID string
	KegTargetOptions
}

LockStatusOptions configures behavior for Tap.LockStatus.

type MetaOptions

type MetaOptions struct {
	// NodeID is the node identifier to inspect (e.g., "0", "42")
	NodeID string

	KegTargetOptions

	// LockToken is an optional cross-process lock token. When provided, the
	// command validates it against any held lock before proceeding.
	LockToken string

	// Edit opens metadata in the editor.
	Edit bool

	// Stream carries stdin piping information.
	Stream *toolkit.Stream
}

MetaOptions configures behavior for Tap.Meta.

type MoveOptions

type MoveOptions struct {
	KegTargetOptions

	SourceID string
	DestID   string
}

type NodeHistoryOptions added in v0.4.0

type NodeHistoryOptions struct {
	KegTargetOptions
	NodeID string
}

type NodeRestoreOptions added in v0.4.0

type NodeRestoreOptions struct {
	KegTargetOptions
	NodeID string
	Rev    string
}

type NodeSnapshotOptions added in v0.4.0

type NodeSnapshotOptions struct {
	KegTargetOptions
	NodeID  string
	Message string
}

type OrientOptions added in v0.19.0

type OrientOptions struct {
	KegTargetOptions

	// Host, if set, causes tier-2 payloads to include the rendered
	// host artifact (SKILL.md, AGENTS.md, etc.). An unknown host
	// returns an error.
	Host string

	// Tier selects payload depth in [OrientTierMin, OrientTierMax].
	// Out-of-range values clamp to the nearest valid tier.
	Tier int
}

OrientOptions is the input to Tap.Orient. Every field is optional: a zero-valued call returns the tier-0 payload with the target keg resolved from KegTargetOptions and no host-specific content.

Flight is part of the embedded KegTargetOptions rather than a top-level field so the CLI's root persistent --flight flag and the MCP tool's flight parameter flow through the same plumbing every other keg-target field uses.

type PathNotFoundError added in v0.5.0

type PathNotFoundError struct {
	Path string
}

PathNotFoundError indicates that the explicit --path target does not exist on disk.

func (*PathNotFoundError) Error added in v0.5.0

func (e *PathNotFoundError) Error() string

type PathService

type PathService struct {
	*appctx.AppPaths
}

func NewPathService

func NewPathService(rt *toolkit.Runtime, root string) (*PathService, error)

func (*PathService) AuthStorePath added in v0.20.0

func (s *PathService) AuthStorePath() string

AuthStorePath returns the on-disk location of the hub-auth credentials store. The file is stored under StateRoot (not ConfigRoot) because it holds mutable per-host tokens rather than user-edited configuration.

func (*PathService) Project

func (s *PathService) Project() string

func (*PathService) ProjectConfig

func (s *PathService) ProjectConfig() string

func (*PathService) UserConfig

func (s *PathService) UserConfig() string

type ProjectKegNotFoundError

type ProjectKegNotFoundError struct {
	Tried []string
}

ProjectKegNotFoundError indicates project-local keg discovery failed. Tried contains the concrete keg-file locations that were checked.

func (*ProjectKegNotFoundError) Error

func (e *ProjectKegNotFoundError) Error() string

func (*ProjectKegNotFoundError) UserMessage added in v0.13.0

func (e *ProjectKegNotFoundError) UserMessage(debug bool) string

UserMessage returns a CLI-context-aware message. When debug is true and search paths are available, they are included in the output.

type RemoveOptions

type RemoveOptions struct {
	KegTargetOptions

	// NodeIDs lists explicit node IDs to remove.
	NodeIDs []string

	// Query is an optional boolean expression (tags and/or key=value attr
	// predicates) that selects additional nodes to remove.
	Query string
}

type RemoveRepoOptions added in v0.4.0

type RemoveRepoOptions struct {
	// Alias is the keg alias to remove from the user config.
	Alias string

	// Force allows removing an alias that is currently set as the
	// defaultKeg or fallbackKeg in the user config.
	Force bool
}

RemoveRepoOptions configures which keg alias to remove.

type ResolveKegOptions

type ResolveKegOptions struct {
	// Root is the base path used for project and fallback resolution.
	Root string
	// Keg is the explicit keg alias to resolve.
	Keg string
	// Project resolves a keg from project-local locations.
	Project bool
	// Cwd limits project resolution to the current working directory.
	Cwd bool
	// Path resolves a keg from an explicit filesystem path.
	Path string
	// NoCache disables in-memory keg caching for this resolution.
	NoCache bool
}

ResolveKegOptions controls how KegService resolves a keg target.

type ServeHandler added in v0.15.0

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

ServeHandler wraps the HTTP handler for serving KEG pages. It implements http.Handler and provides a Close method to release background resources such as the filesystem watcher used for proactive dex invalidation.

func (*ServeHandler) Close added in v0.15.0

func (h *ServeHandler) Close()

Close releases background resources and waits for all background goroutines to drain. It is safe to call multiple times.

func (*ServeHandler) ServeHTTP added in v0.15.0

func (h *ServeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

type ServeOptions added in v0.11.0

type ServeOptions struct {
	KegTargetOptions

	// Host is the bind address (default: 127.0.0.1).
	Host string

	// Port is the port to listen on (default: 0 for random).
	Port int

	// Title overrides the site title. If empty, the keg summary or URL is used.
	Title string

	// BaseURL is the base URL for absolute links. Defaults to "/".
	BaseURL string

	// Watch enables the filesystem watcher and SSE endpoint for automatic
	// browser refresh when node files change. When nil, defaults to true.
	Watch *bool
}

ServeOptions configures the embedded HTTP server.

type ServeResult added in v0.11.0

type ServeResult struct {
	URL string
}

ServeResult is returned when the server shuts down.

type SiteOptions added in v0.11.0

type SiteOptions struct {
	KegTargetOptions

	// Output is the directory where the site will be written.
	Output string

	// Title overrides the site title. If empty, the keg summary or URL is used.
	Title string

	// BaseURL is the base URL for absolute links. Defaults to "/".
	BaseURL string

	// NoSearch skips Pagefind search indexing.
	NoSearch bool
}

SiteOptions configures static site generation.

type SiteResult added in v0.11.0

type SiteResult struct {
	OutputDir string
	NodeCount int
	TagCount  int
	HasSearch bool
}

SiteResult summarizes what the site generator produced.

type StatsOptions

type StatsOptions struct {
	// NodeID is the node identifier to inspect (e.g., "0", "42")
	NodeID string

	KegTargetOptions
}

type TagsOptions

type TagsOptions struct {
	KegTargetOptions

	// Query is an optional boolean expression that filters nodes. Supports both
	// plain tag names ("golang") and key=value attribute predicates
	// ("entity=plan"). When non-empty it takes precedence over Tag.
	Query string

	// Tag filters nodes by tag expression. Deprecated: use Query instead.
	// When empty and Query is also empty, all tags are listed.
	Tag string

	// Format to use. %i is node id
	// %d is date
	// %t is node title
	// %% for literal %
	Format string

	IdOnly bool

	Reverse bool

	// Limit caps the number of results returned. 0 means no limit.
	Limit int

	// Offset skips the first N results before applying limit. Must be >= 0.
	Offset int
}

type Tap

type Tap struct {
	Root string
	// Runtime carries process-level dependencies.
	Runtime *toolkit.Runtime

	PathService   *PathService
	ConfigService *ConfigService
	KegService    *KegService
}

func NewTap

func NewTap(opts TapOptions) (*Tap, error)

func (*Tap) AuthLogout added in v0.20.0

func (t *Tap) AuthLogout(ctx context.Context, opts AuthLogoutOptions) (*AuthLogoutResult, error)

AuthLogout removes the cached credential for a hub from the on-disk auth store. Unlike AuthStatus this method is intentionally NOT exposed over MCP — an agent should never be able to yank a user's hub token out from under them. The CLI surface is the only consumer.

Resolution precedence mirrors AuthStatus:

  1. Hub set → canonicalize and delete that hub.
  2. Hub empty AND single hub stored → auto-resolve to that hub.
  3. Hub empty AND zero hubs stored → soft-success, "No hub logins stored.".
  4. Hub empty AND multiple hubs stored → error (caller must pick).

Missing entries (hub was provided but not stored) are NOT errors — they return Result{Removed: false} with a clear Formatted line. The command is idempotent by design so cleanup scripts can re-run without special-casing the already-logged-out state.

func (*Tap) AuthStatus added in v0.20.0

func (t *Tap) AuthStatus(ctx context.Context, opts AuthStatusOptions) (*AuthStatusResult, error)

AuthStatus reports the login status for a stored hub.

Resolution precedence:

  1. Empty store AND empty Hub → not-present, directed hint message.
  2. Hub set → canonicalize and look up exactly that hub.
  3. Hub empty AND single hub stored → auto-resolve to that hub.
  4. Hub empty AND multiple hubs stored → error (caller must pick).

Missing entries (hub was provided but not stored) are NOT errors — they return a Result{Present: false} with a clear Formatted line. That keeps `tap auth status --hub X` usable from scripts without requiring caller-side error-matching.

func (t *Tap) Backlinks(ctx context.Context, opts BacklinksOptions) ([]string, error)

func (*Tap) Cat

func (t *Tap) Cat(ctx context.Context, opts CatOptions) (string, error)

func (*Tap) Config

func (t *Tap) Config(opts ConfigOptions) (string, error)

Config displays the merged or project configuration.

func (*Tap) ConfigEdit

func (t *Tap) ConfigEdit(ctx context.Context, opts ConfigEditOptions) error

ConfigEdit edits the selected tap config file.

If stdin is piped with non-empty content, the piped YAML is validated and written directly without opening an editor. Otherwise the file is opened in the configured editor.

func (*Tap) ConfigExplain added in v0.17.0

func (t *Tap) ConfigExplain(ctx context.Context, opts ConfigExplainOptions) ([]ConfigExplainResult, error)

ConfigExplain returns provenance for config fields, showing which source set each value. It loads each tier individually and walks from most-specific to least-specific to determine the effective source.

func (*Tap) ConfigTemplate added in v0.4.0

func (t *Tap) ConfigTemplate(opts ConfigTemplateOptions) (string, error)

ConfigTemplate returns starter YAML for either user or project config.

func (*Tap) Create

func (t *Tap) Create(ctx context.Context, opts CreateOptions) (keg.NodeId, error)

func (*Tap) DeleteFile added in v0.2.0

func (t *Tap) DeleteFile(ctx context.Context, opts DeleteFileOptions) error

DeleteFile removes a file attachment from a node.

func (*Tap) DeleteImage added in v0.2.0

func (t *Tap) DeleteImage(ctx context.Context, opts DeleteImageOptions) error

DeleteImage removes an image from a node.

func (*Tap) Dir

func (t *Tap) Dir(ctx context.Context, opts DirOptions) (string, error)

func (*Tap) Doctor added in v0.5.0

func (t *Tap) Doctor(ctx context.Context, opts DoctorOptions) ([]Issue, error)

Doctor scans the resolved keg and reports health issues.

func (*Tap) DoctorConfig added in v0.17.0

func (t *Tap) DoctorConfig() []Issue

DoctorConfig validates the tapper configuration (not keg-level) and returns issues. This does not require a keg to be resolved.

func (*Tap) DownloadFile added in v0.2.0

func (t *Tap) DownloadFile(ctx context.Context, opts DownloadFileOptions) (string, error)

DownloadFile retrieves a node file attachment and writes it to a local path. Returns the destination path.

func (*Tap) DownloadImage added in v0.2.0

func (t *Tap) DownloadImage(ctx context.Context, opts DownloadImageOptions) (string, error)

DownloadImage retrieves a node image and writes it to a local path. Returns the destination path.

func (*Tap) Edit

func (t *Tap) Edit(ctx context.Context, opts EditOptions) error

Edit opens a node in an editor. When the repository is an FsRepo, the real README.md is opened directly for in-place editing. Otherwise a temporary file with frontmatter is used and changes are split back on save.

The temp file format (non-FsRepo) is:

---
<meta yaml>
---
<markdown body>

If stdin is piped, it seeds the content directly without opening an editor.

func (*Tap) Export added in v0.4.0

func (t *Tap) Export(ctx context.Context, opts ExportOptions) (string, error)

func (*Tap) ForceUnlock added in v0.11.0

func (t *Tap) ForceUnlock(ctx context.Context, opts ForceUnlockOptions) error

ForceUnlock unconditionally removes a cross-process lock on a node.

func (*Tap) Graph added in v0.4.0

func (t *Tap) Graph(ctx context.Context, opts GraphOptions) (string, error)

Graph renders a self-contained HTML page for the resolved keg graph.

func (*Tap) Grep

func (t *Tap) Grep(ctx context.Context, opts GrepOptions) ([]string, error)

func (*Tap) Import added in v0.4.0

func (t *Tap) Import(ctx context.Context, opts ImportOptions) ([]keg.NodeId, error)

func (*Tap) ImportFromKeg added in v0.4.0

func (t *Tap) ImportFromKeg(ctx context.Context, opts ImportFromKegOptions) ([]ImportedNode, error)

ImportFromKeg copies nodes from a source keg into the target keg. Each node is assigned a fresh ID via targetRepo.Next() and all links in the copied content are rewritten according to the six rules described in the plan.

func (*Tap) Index

func (t *Tap) Index(ctx context.Context, opts IndexOptions) (string, error)

Index rebuilds all indices for a keg (nodes.tsv, tags, links, backlinks) from scratch.

func (*Tap) IndexCat added in v0.2.0

func (t *Tap) IndexCat(ctx context.Context, opts IndexCatOptions) (string, error)

IndexCat returns the raw contents of a named dex index file. opts.Name may include or omit a leading "dex/" prefix; both are accepted.

func (*Tap) Info

func (t *Tap) Info(ctx context.Context, opts InfoOptions) (string, error)

Info displays the keg metadata (keg.yaml file contents).

func (*Tap) InitKeg

func (t *Tap) InitKeg(ctx context.Context, options InitOptions) (*keg.Target, error)

InitKeg creates a keg with the alias specified in options.Keg.

It validates destination flags and initializes one of three destinations:

  • user: filesystem-backed keg under the first configured kegSearchPaths entry
  • project: filesystem-backed keg under project path or explicit --path
  • hub: API target entry written to config only

func (*Tap) Integrate added in v0.19.0

func (t *Tap) Integrate(ctx context.Context, opts IntegrateOptions) ([]string, error)

Integrate copies the embedded rendered tree for the specified host into the host's on-disk install location. It returns the absolute target paths, one per file copied. When DryRun is true, no writes happen and the returned paths describe what would be written.

Every copy flows through the Runtime so the command honors sandboxed tests and the project's Runtime Abstraction Rule. Parent directories are created on demand by rt.WriteFile.

func (*Tap) KegConfigEdit added in v0.2.0

func (t *Tap) KegConfigEdit(ctx context.Context, opts KegConfigEditOptions) error

KegConfigEdit opens the keg configuration file in the default editor.

func (*Tap) KegInfo

func (t *Tap) KegInfo(ctx context.Context, opts KegInfoOptions) (string, error)

KegInfo displays diagnostics for a resolved keg.

func (t *Tap) Links(ctx context.Context, opts LinksOptions) ([]string, error)

func (*Tap) List

func (t *Tap) List(ctx context.Context, opts ListOptions) ([]string, error)

func (*Tap) ListFiles added in v0.2.0

func (t *Tap) ListFiles(ctx context.Context, opts ListFilesOptions) ([]string, error)

ListFiles returns the names of file attachments for a node.

func (*Tap) ListImages added in v0.2.0

func (t *Tap) ListImages(ctx context.Context, opts ListImagesOptions) ([]string, error)

ListImages returns the names of images for a node.

func (*Tap) ListIndexes added in v0.2.0

func (t *Tap) ListIndexes(ctx context.Context, opts IndexCatOptions) ([]string, error)

ListIndexes returns the names of available index files for a keg (e.g. "changes.md", "nodes.tsv").

func (*Tap) ListKegs

func (t *Tap) ListKegs(cache bool) ([]string, error)

ListKegs returns available keg aliases from local discovery paths and config. When cache is true, cached config values may be used.

func (*Tap) Lock added in v0.11.0

func (t *Tap) Lock(ctx context.Context, opts LockOptions) (keg.LockToken, error)

Lock acquires a cross-process lock on a node and returns the token.

func (*Tap) LockStatus added in v0.11.0

func (t *Tap) LockStatus(ctx context.Context, opts LockStatusOptions) (keg.LockInfo, error)

LockStatus returns the lock state for a node.

func (*Tap) LookupKeg

func (t *Tap) LookupKeg(ctx context.Context, kegAlias string) (*keg.Keg, error)

func (*Tap) Meta

func (t *Tap) Meta(ctx context.Context, opts MetaOptions) (string, error)

func (*Tap) Move

func (t *Tap) Move(ctx context.Context, opts MoveOptions) error

func (*Tap) NewServeHandler added in v0.11.0

func (t *Tap) NewServeHandler(ctx context.Context, opts ServeOptions) (*ServeHandler, error)

NewServeHandler builds a ServeHandler that dynamically renders KEG pages. Templates are parsed once at creation time. Each request reads fresh data from the keg. If the keg is backed by a filesystem repository, a background watcher proactively invalidates the dex cache when node files change. Callers should call Close when the handler is no longer needed.

func (*Tap) NodeHistory added in v0.4.0

func (t *Tap) NodeHistory(ctx context.Context, opts NodeHistoryOptions) ([]keg.Snapshot, error)

func (*Tap) NodeRestore added in v0.4.0

func (t *Tap) NodeRestore(ctx context.Context, opts NodeRestoreOptions) error

func (*Tap) NodeSnapshot added in v0.4.0

func (t *Tap) NodeSnapshot(ctx context.Context, opts NodeSnapshotOptions) (keg.Snapshot, error)

func (*Tap) Orient added in v0.19.0

func (t *Tap) Orient(ctx context.Context, opts OrientOptions) (string, error)

Orient returns a tapper orientation payload at the requested tier. See OrientTierMin / OrientTierMax for tier semantics. MCP tool, MCP Resources, and the eventual `tap orient` CLI all delegate here so every surface produces identical bytes at matching inputs.

Active-keg resolution runs against the live KegService so the payload names the keg the next mcp__tapper__* call would actually hit, rather than a placeholder hint about auto-detection. Resolution failures are not propagated — orient is a bootstrap surface and must still describe tapper when no keg exists.

func (*Tap) Remove

func (t *Tap) Remove(ctx context.Context, opts RemoveOptions) error

func (*Tap) RemoveRepo added in v0.4.0

func (t *Tap) RemoveRepo(ctx context.Context, opts RemoveRepoOptions) error

RemoveRepo removes a registered keg alias from the user configuration.

Safety checks (bypassed with Force):

  • Refuses to remove the configured defaultKeg alias without --force.
  • Refuses to remove the configured fallbackKeg alias without --force.

func (*Tap) Serve added in v0.11.0

func (t *Tap) Serve(ctx context.Context, opts ServeOptions) (*ServeResult, error)

Serve starts an HTTP server that dynamically renders KEG pages on each request. It blocks until ctx is cancelled or an OS interrupt signal is received. The URL is printed to stdout immediately after binding.

func (*Tap) Site added in v0.11.0

func (t *Tap) Site(ctx context.Context, opts SiteOptions) (*SiteResult, error)

Site generates a static HTML website from the resolved keg.

func (*Tap) Stats

func (t *Tap) Stats(ctx context.Context, opts StatsOptions) (string, error)

func (*Tap) Tags

func (t *Tap) Tags(ctx context.Context, opts TagsOptions) ([]string, error)

func (*Tap) Unlock added in v0.11.0

func (t *Tap) Unlock(ctx context.Context, opts UnlockOptions) error

Unlock releases a cross-process lock on a node.

func (*Tap) UploadFile added in v0.2.0

func (t *Tap) UploadFile(ctx context.Context, opts UploadFileOptions) (string, error)

UploadFile reads a local file and stores it as a node file attachment. Returns the stored filename.

func (*Tap) UploadImage added in v0.2.0

func (t *Tap) UploadImage(ctx context.Context, opts UploadImageOptions) (string, error)

UploadImage reads a local file and stores it as a node image. Returns the stored filename.

type TapOptions

type TapOptions struct {
	Root       string
	ConfigPath string
	Runtime    *toolkit.Runtime
}

type UnlockOptions added in v0.11.0

type UnlockOptions struct {
	NodeID string
	Token  string
	KegTargetOptions
}

UnlockOptions configures behavior for Tap.Unlock.

type UploadFileOptions added in v0.2.0

type UploadFileOptions struct {
	KegTargetOptions
	NodeID   string
	FilePath string
	Name     string
}

UploadFileOptions configures behavior for Tap.UploadFile.

type UploadImageOptions added in v0.2.0

type UploadImageOptions struct {
	KegTargetOptions
	NodeID   string
	FilePath string
	Name     string
}

UploadImageOptions configures behavior for Tap.UploadImage.

Jump to

Keyboard shortcuts

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