Documentation
¶
Overview ¶
Package tapper — browser-based OAuth2 PKCE login flow for hub auth.
AuthLogin drives the end-to-end handshake:
- discover the hub's authorize/token endpoints via RFC 8414 (/.well-known/oauth-authorization-server)
- generate PKCE verifier/challenge + CSRF state
- bind a loopback listener on 127.0.0.1:0 and derive redirect_uri
- open the user's browser to the discovered authorization endpoint
- accept exactly one callback, validate state, extract the code
- 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
- Variables
- func CanonicalHubURL(s string) string
- func GeneratePKCEVerifier(reader io.Reader) (string, error)
- func GenerateState(reader io.Reader) (string, error)
- func IntegrateHosts() []string
- func LocalGitData(ctx context.Context, rt *toolkit.Runtime, projectPath, key string) ([]byte, error)
- func NewAuthStoreTokenResolver(store *AuthStore) keg.TokenResolver
- func OrientableHosts() []string
- func PKCEChallenge(verifier string) string
- func ResolveLoginHubURL(cfg *Config, explicit string) (string, error)
- type AuthEntry
- type AuthLoginDeviceOptions
- type AuthLoginOptions
- type AuthLogoutOptions
- type AuthLogoutResult
- type AuthStatusOptions
- type AuthStatusResult
- type AuthStore
- func (s *AuthStore) Delete(hubURL string) bool
- func (s *AuthStore) Get(hubURL string) (*AuthEntry, bool)
- func (s *AuthStore) Hubs() []string
- func (s *AuthStore) IsEmpty() bool
- func (s *AuthStore) Save(ctx context.Context, rt *toolkit.Runtime, path string) error
- func (s *AuthStore) Set(hubURL string, entry AuthEntry)
- type BacklinksOptions
- type CatOptions
- type Config
- func (cfg *Config) AddKeg(alias string, target keg.Target) error
- func (cfg *Config) AddKegMap(entry KegMapEntry) error
- func (cfg *Config) Clone() *Config
- func (cfg *Config) DefaultHub() string
- func (cfg *Config) DefaultKeg() string
- func (cfg *Config) DisableDefaultHub() bool
- func (cfg *Config) FallbackKeg() string
- func (cfg *Config) Hubs() []KegHub
- func (cfg *Config) KegMap() []KegMapEntry
- func (cfg *Config) KegSearchPaths() []string
- func (cfg *Config) Kegs() map[string]keg.Target
- func (cfg *Config) ListKegs() []string
- func (cfg *Config) LogFile() string
- func (cfg *Config) LogLevel() string
- func (cfg *Config) LookupAlias(rt *toolkit.Runtime, projectRoot string) string
- func (cfg *Config) LookupAliasForTarget(rt *toolkit.Runtime, target string) string
- func (cfg *Config) PrimaryKegSearchPath() string
- func (cfg *Config) RemoveKeg(alias string) error
- func (cfg *Config) ResolveAlias(alias string) (*keg.Target, error)
- func (cfg *Config) ResolveDefault(rt *toolkit.Runtime) (*keg.Target, error)
- func (cfg *Config) ResolveKegMap(rt *toolkit.Runtime, projectRoot string) (*keg.Target, error)
- func (cfg *Config) SetDefaultHub(_ context.Context, hub string) error
- func (cfg *Config) SetDefaultKeg(keg string) error
- func (cfg *Config) SetDisableDefaultHub(disable bool) error
- func (cfg *Config) SetFallbackKeg(keg string) error
- func (cfg *Config) SetKegSearchPaths(paths []string) error
- func (cfg *Config) SetLogFile(_ context.Context, path string) error
- func (cfg *Config) SetLogLevel(level string) error
- func (cfg *Config) ToYAML() ([]byte, error)
- func (cfg *Config) Touch(rt *toolkit.Runtime)
- func (cfg *Config) Updated() time.Time
- func (cfg *Config) Write(rt *toolkit.Runtime, path string) error
- type ConfigEditOptions
- type ConfigExplainOptions
- type ConfigExplainResult
- type ConfigLoadWarning
- type ConfigOptions
- type ConfigService
- func (s *ConfigService) Config(cache bool) (*Config, error)
- func (s *ConfigService) DiscoveredKegAliases(cache bool) ([]string, error)
- func (s *ConfigService) ProjectConfig(cache bool) (*Config, error)
- func (s *ConfigService) ResetCache()
- func (s *ConfigService) ResolveTarget(alias string, cache bool) (*keg.Target, error)
- func (s *ConfigService) UserConfig(cache bool) (*Config, error)
- type ConfigTemplateOptions
- type ConfigWarning
- type CreateOptions
- type DeleteFileOptions
- type DeleteImageOptions
- type DirOptions
- type DoctorOptions
- type DownloadFileOptions
- type DownloadImageOptions
- type EditOptions
- type ExportOptions
- type ForceUnlockOptions
- type GraphOptions
- type GrepOptions
- type ImportFromKegOptions
- type ImportOptions
- type ImportedNode
- type IndexCatOptions
- type IndexOptions
- type InfoOptions
- type InitOptions
- type IntegrateOptions
- type Issue
- type KegConfigEditOptions
- type KegHub
- type KegInfoOptions
- type KegMapEntry
- type KegService
- type KegTargetOptions
- type LinksOptions
- type ListFilesOptions
- type ListImagesOptions
- type ListOptions
- type ListSortType
- type LockOptions
- type LockStatusOptions
- type MetaOptions
- type MoveOptions
- type NodeHistoryOptions
- type NodeRestoreOptions
- type NodeSnapshotOptions
- type OrientOptions
- type PathNotFoundError
- type PathService
- type ProjectKegNotFoundError
- type RemoveOptions
- type RemoveRepoOptions
- type ResolveKegOptions
- type ServeHandler
- type ServeOptions
- type ServeResult
- type SiteOptions
- type SiteResult
- type StatsOptions
- type TagsOptions
- type Tap
- func (t *Tap) AuthLogout(ctx context.Context, opts AuthLogoutOptions) (*AuthLogoutResult, error)
- func (t *Tap) AuthStatus(ctx context.Context, opts AuthStatusOptions) (*AuthStatusResult, error)
- func (t *Tap) Backlinks(ctx context.Context, opts BacklinksOptions) ([]string, error)
- func (t *Tap) Cat(ctx context.Context, opts CatOptions) (string, error)
- func (t *Tap) Config(opts ConfigOptions) (string, error)
- func (t *Tap) ConfigEdit(ctx context.Context, opts ConfigEditOptions) error
- func (t *Tap) ConfigExplain(ctx context.Context, opts ConfigExplainOptions) ([]ConfigExplainResult, error)
- func (t *Tap) ConfigTemplate(opts ConfigTemplateOptions) (string, error)
- func (t *Tap) Create(ctx context.Context, opts CreateOptions) (keg.NodeId, error)
- func (t *Tap) DeleteFile(ctx context.Context, opts DeleteFileOptions) error
- func (t *Tap) DeleteImage(ctx context.Context, opts DeleteImageOptions) error
- func (t *Tap) Dir(ctx context.Context, opts DirOptions) (string, error)
- func (t *Tap) Doctor(ctx context.Context, opts DoctorOptions) ([]Issue, error)
- func (t *Tap) DoctorConfig() []Issue
- func (t *Tap) DownloadFile(ctx context.Context, opts DownloadFileOptions) (string, error)
- func (t *Tap) DownloadImage(ctx context.Context, opts DownloadImageOptions) (string, error)
- func (t *Tap) Edit(ctx context.Context, opts EditOptions) error
- func (t *Tap) Export(ctx context.Context, opts ExportOptions) (string, error)
- func (t *Tap) ForceUnlock(ctx context.Context, opts ForceUnlockOptions) error
- func (t *Tap) Graph(ctx context.Context, opts GraphOptions) (string, error)
- func (t *Tap) Grep(ctx context.Context, opts GrepOptions) ([]string, error)
- func (t *Tap) Import(ctx context.Context, opts ImportOptions) ([]keg.NodeId, error)
- func (t *Tap) ImportFromKeg(ctx context.Context, opts ImportFromKegOptions) ([]ImportedNode, error)
- func (t *Tap) Index(ctx context.Context, opts IndexOptions) (string, error)
- func (t *Tap) IndexCat(ctx context.Context, opts IndexCatOptions) (string, error)
- func (t *Tap) Info(ctx context.Context, opts InfoOptions) (string, error)
- func (t *Tap) InitKeg(ctx context.Context, options InitOptions) (*keg.Target, error)
- func (t *Tap) Integrate(ctx context.Context, opts IntegrateOptions) ([]string, error)
- func (t *Tap) KegConfigEdit(ctx context.Context, opts KegConfigEditOptions) error
- func (t *Tap) KegInfo(ctx context.Context, opts KegInfoOptions) (string, error)
- func (t *Tap) Links(ctx context.Context, opts LinksOptions) ([]string, error)
- func (t *Tap) List(ctx context.Context, opts ListOptions) ([]string, error)
- func (t *Tap) ListFiles(ctx context.Context, opts ListFilesOptions) ([]string, error)
- func (t *Tap) ListImages(ctx context.Context, opts ListImagesOptions) ([]string, error)
- func (t *Tap) ListIndexes(ctx context.Context, opts IndexCatOptions) ([]string, error)
- func (t *Tap) ListKegs(cache bool) ([]string, error)
- func (t *Tap) Lock(ctx context.Context, opts LockOptions) (keg.LockToken, error)
- func (t *Tap) LockStatus(ctx context.Context, opts LockStatusOptions) (keg.LockInfo, error)
- func (t *Tap) LookupKeg(ctx context.Context, kegAlias string) (*keg.Keg, error)
- func (t *Tap) Meta(ctx context.Context, opts MetaOptions) (string, error)
- func (t *Tap) Move(ctx context.Context, opts MoveOptions) error
- func (t *Tap) NewServeHandler(ctx context.Context, opts ServeOptions) (*ServeHandler, error)
- func (t *Tap) NodeHistory(ctx context.Context, opts NodeHistoryOptions) ([]keg.Snapshot, error)
- func (t *Tap) NodeRestore(ctx context.Context, opts NodeRestoreOptions) error
- func (t *Tap) NodeSnapshot(ctx context.Context, opts NodeSnapshotOptions) (keg.Snapshot, error)
- func (t *Tap) Orient(ctx context.Context, opts OrientOptions) (string, error)
- func (t *Tap) Remove(ctx context.Context, opts RemoveOptions) error
- func (t *Tap) RemoveRepo(ctx context.Context, opts RemoveRepoOptions) error
- func (t *Tap) Serve(ctx context.Context, opts ServeOptions) (*ServeResult, error)
- func (t *Tap) Site(ctx context.Context, opts SiteOptions) (*SiteResult, error)
- func (t *Tap) Stats(ctx context.Context, opts StatsOptions) (string, error)
- func (t *Tap) Tags(ctx context.Context, opts TagsOptions) ([]string, error)
- func (t *Tap) Unlock(ctx context.Context, opts UnlockOptions) error
- func (t *Tap) UploadFile(ctx context.Context, opts UploadFileOptions) (string, error)
- func (t *Tap) UploadImage(ctx context.Context, opts UploadImageOptions) (string, error)
- type TapOptions
- type UnlockOptions
- type UploadFileOptions
- type UploadImageOptions
Constants ¶
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" )
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.
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 ¶
var ConfigExplainFields = []string{
"defaultKeg",
"fallbackKeg",
"logFile",
"logLevel",
"defaultHub",
"disableDefaultHub",
"kegSearchPaths",
}
ConfigExplainFields lists the scalar config fields eligible for explain.
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
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
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
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
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
ResolveLoginHubURL returns the hub URL the login flow should target, applying the five-step resolution chain documented in keg-dev/1035:
- explicit non-empty → canonicalize and use
- cfg.DefaultHub names a Hubs entry → use that entry's URL
- cfg.Hubs has exactly one entry → use it
- cfg.DisableDefaultHub is true → ErrDefaultHubDisabled
- 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
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
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
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
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
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
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
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
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.
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 ¶
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 ¶
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 ¶
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 ¶
ParseConfig parses raw YAML into a Config data model.
func ReadConfig ¶
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) 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) DefaultHub ¶ added in v0.20.0
DefaultHub returns the default hub name. The value is looked up by name in Hubs() to find the corresponding URL.
func (*Config) DefaultKeg ¶
DefaultKeg returns the alias to use when no explicit keg is provided.
func (*Config) DisableDefaultHub ¶ added in v0.20.0
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
FallbackKeg returns the last-resort keg alias.
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
KegSearchPaths returns local discovery paths for file-backed kegs.
func (*Config) ListKegs ¶
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) LookupAlias ¶
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
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
PrimaryKegSearchPath returns the first configured local discovery path.
func (*Config) RemoveKeg ¶ added in v0.4.0
RemoveKeg removes a keg entry from the Config by alias.
Returns an error when the alias is not registered.
func (*Config) ResolveAlias ¶
ResolveAlias looks up the keg by alias and returns a parsed Target.
Returns (nil, error) when not found or parse fails.
func (*Config) ResolveDefault ¶
ResolveDefault resolves the current DefaultKeg alias to a target.
func (*Config) ResolveKegMap ¶
ResolveKegMap chooses the appropriate keg (via alias) based on path.
Precedence rules:
- Regex entries in KegMap have the highest precedence.
- PathPrefix entries are considered next; when multiple prefixes match the longest prefix wins.
- 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
SetDefaultHub sets the default hub.
func (*Config) SetDefaultKeg ¶
SetDefaultKeg sets the alias used when no explicit keg is provided.
func (*Config) SetDisableDefaultHub ¶ added in v0.20.0
SetDisableDefaultHub toggles the compiled-in DefaultHubURL fallback.
func (*Config) SetFallbackKeg ¶ added in v0.2.0
SetFallbackKeg sets the fallback keg alias.
func (*Config) SetKegSearchPaths ¶ added in v0.2.0
SetKegSearchPaths sets local discovery paths for file-backed kegs.
func (*Config) SetLogFile ¶
SetLogFile sets the log file path.
func (*Config) SetLogLevel ¶
SetLogLevel sets the log level.
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 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 ¶
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 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
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 ¶
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
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:
- Hub set → canonicalize and delete that hub.
- Hub empty AND single hub stored → auto-resolve to that hub.
- Hub empty AND zero hubs stored → soft-success, "No hub logins stored.".
- 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:
- Empty store AND empty Hub → not-present, directed hint message.
- Hub set → canonicalize and look up exactly that hub.
- Hub empty AND single hub stored → auto-resolve to that hub.
- 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 (*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) 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) DoctorConfig ¶ added in v0.17.0
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
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
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) 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
Graph renders a self-contained HTML page for the resolved keg graph.
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 ¶
Index rebuilds all indices for a keg (nodes.tsv, tags, links, backlinks) from scratch.
func (*Tap) IndexCat ¶ added in v0.2.0
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) InitKeg ¶
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
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) ListImages ¶ added in v0.2.0
ListImages returns the names of images for a node.
func (*Tap) ListIndexes ¶ added in v0.2.0
ListIndexes returns the names of available index files for a keg (e.g. "changes.md", "nodes.tsv").
func (*Tap) ListKegs ¶
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
Lock acquires a cross-process lock on a node and returns the token.
func (*Tap) LockStatus ¶ added in v0.11.0
LockStatus returns the lock state for a node.
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 (*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 (*Tap) Orient ¶ added in v0.19.0
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) 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) 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
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
UploadImage reads a local file and stores it as a node image. Returns the stored filename.
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.
Source Files
¶
- auth_device_flow.go
- auth_flow.go
- auth_resolver.go
- auth_store.go
- config.go
- config_env.go
- config_service.go
- config_validate.go
- constants.go
- editor_live.go
- error_types.go
- keg_service.go
- node_exists.go
- path_service.go
- pkce.go
- query_expr.go
- tap.go
- tap_archive.go
- tap_auth.go
- tap_cat.go
- tap_config.go
- tap_create.go
- tap_dir.go
- tap_doctor.go
- tap_edit.go
- tap_files.go
- tap_graph.go
- tap_import.go
- tap_index.go
- tap_info.go
- tap_init.go
- tap_integrate.go
- tap_list.go
- tap_lock.go
- tap_move.go
- tap_orient.go
- tap_remove.go
- tap_repo_rm.go
- tap_serve.go
- tap_site.go
- tap_snapshots.go
- tap_stats.go