tapper

package
v0.32.0 Latest Latest
Warning

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

Go to latest
Published: Jul 15, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

Package tapper — shared OAuth2 hub helpers.

This file holds the hub-auth building blocks that are independent of any single grant type: RFC 8414 authorization-server metadata discovery, hub URL normalization/canonicalization, and opening the user's browser. The device authorization grant (auth_device_flow.go) composes these; there is no longer a loopback/PKCE flow — `tap auth login` standardizes on the device flow for browser-based login.

The metadata + browser helpers take their external dependencies (HTTP client, runtime) as arguments so the flow stays trivially testable against an httptest.Server mock hub.

Package tapper — OAuth2 refresh-token renewal for cached hub credentials.

The device-login flow now persists a refresh token (plus the client_id and token endpoint it must be presented to) alongside the short-lived access token. RefreshHubToken exchanges that refresh token for a fresh access token so a command whose cached token has expired renews silently instead of forcing the user back through `tap auth login`.

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 — hub token validation via the whoami probe.

ValidateToken confirms a bearer token resolves to a user on the hub. It backs the "paste an authentication token" login path: the token the user pastes (an API token minted on the hub's account page) is checked against GET /api/v1/whoami before it is written to the AuthStore, so a typo or a revoked token fails fast at login rather than on the first real request.

Package tapper — hub keg-administration client calls (grants + visibility).

These talk to a remote hub's per-keg admin endpoints under /api/v1/@{namespace}/kegs/{keg}: listing/creating/revoking grants and updating visibility. Like ValidateToken (auth_whoami.go) and CreateKeg (hub_kegs.go) they are slim, dependency-free functions over http.DefaultClient so tests can point hubURL at an httptest.Server.

Package tapper — hub keg-catalog client calls.

These talk to a remote hub's namespace/keg-catalog endpoints (as opposed to the per-keg operation API in pkg/keg/keg_remote.go): creating a keg and listing the kegs a user can reach. They mirror the slim, dependency-free shape of ValidateToken (auth_whoami.go) — a stand-alone function over http.DefaultClient so tests can point hubURL at an httptest.Server.

Package tapper — hub namespace-administration client calls.

These talk to a remote hub's namespace endpoints under /api/v1/namespaces and /api/v1/@{namespace}/members: listing namespaces and managing membership roles. Slim, dependency-free functions over http.DefaultClient (via doHubJSON) so tests can point hubURL at an httptest.Server.

Index

Constants

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

	// DefaultHubName is the compiled-in name of the default remote hub.
	DefaultHubName = "atlas"

	// DefaultHubURL is the compiled-in fallback hub used by ResolveLoginHubURL
	// and Config.Hub when no explicit hub, defaultHub, fallbackHub, or single
	// configured hub is available 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
	// disableAtlasHub: true (or TAP_DISABLE_ATLAS_HUB=1) and the chain
	// errors out instead of falling through here.
	DefaultHubURL = "https://atlas.foldwise.ai"

	// DefaultHubTokenEnv is the environment variable the default remote hub
	// reads its bearer token from when no explicit credential is configured.
	DefaultHubTokenEnv = "ATLAS_API_KEY"

	// LocalHubName is the reserved name of the built-in filesystem hub. Kegs
	// addressed at the "local" hub (or with namespace "local" and no hub) live
	// on disk under the hub's basePath rather than on a remote service.
	LocalHubName = "local"
)
View Source
const (
	// HubKindRemote is a read-write HTTP hub (the default, e.g. atlas).
	HubKindRemote = "remote"
	// HubKindLocal is a filesystem-backed hub on this machine.
	HubKindLocal = "local"
	// HubKindReadonly is a read-only HTTP hub. TODO: enforce read-only writes
	// in the API repository; today the kind only sets Target.Readonly.
	HubKindReadonly = "readonly"
)

Hub kinds describe how a hub's (namespace, name) pairs are backed.

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 (
	FlightRoleViewer FlightRole = "viewer"
	FlightRoleEditor FlightRole = "editor"

	FlightVisibilityPrivate = "private"
	FlightVisibilityPublic  = "public"

	FlightCapabilityManageFlights FlightCapability = "manage_flights"
	FlightCapabilityFullAccess    FlightCapability = "full_access"
)
View Source
const (
	// BootstrapKindLocal sets up only the built-in local filesystem hub.
	BootstrapKindLocal = "local"
	// BootstrapKindCloud targets the compiled-in atlas remote hub.
	BootstrapKindCloud = "cloud"
	// BootstrapKindEnterprise registers a user-supplied remote HTTP endpoint.
	BootstrapKindEnterprise = "enterprise"
)

Bootstrap deployment kinds. Each maps onto an existing hub kind/shape — there is no new config field, only a guided way to pick one.

View Source
const (

	// FlightManifestSchemaURL is the public JSON Schema used by editor
	// modelines for flight manifest YAML.
	FlightManifestSchemaURL = "https://raw.githubusercontent.com/jlrickert/tapper/main/schemas/flight-manifest.json"
)

flightsDirName is the reserved directory — a sibling of the @<namespace> dirs of a local hub — that holds flight manifests. "flights.d" is an invalid namespace (it contains a dot), so it can never collide with a keg path.

Variables

View Source
var ConfigExplainFields = []string{
	"defaultKeg",
	"fallbackKeg",
	"flight",
	"logFile",
	"logLevel",
	"defaultHub",
	"fallbackHub",
	"defaultNamespace",
	"fallbackNamespace",
	"disableAtlasHub",
	"disableLocalHub",
}

ConfigExplainFields lists the scalar config fields eligible for explain.

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

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

View Source
var ErrNotBootstrapped = errors.New("tapper is not set up on this machine; run `tap bootstrap` to get started")

ErrNotBootstrapped is returned by hub/namespace-dependent operations on the full `tap` surface when no user config exists yet — i.e. `tap bootstrap` has not been run. Explicit filesystem destinations (--path/--project/--cwd) and the pruned `keg` binary are exempt, and callers that resolve a keg by an explicit filesystem path are too.

View Source
var ErrTokenRejected = errors.New("hub rejected the token")

ErrTokenRejected marks a token the hub actively refused (HTTP 401/403), as distinct from a transport failure (hub unreachable). Callers use errors.Is to tell "your token is bad" apart from "we couldn't reach the hub" — see Tap.AuthStatus, which renders those two cases differently.

Functions

func AddNamespaceMember added in v0.23.0

func AddNamespaceMember(ctx context.Context, hubURL, token, namespace, username, role string) error

AddNamespaceMember upserts a member via POST /api/v1/@{namespace}/members. role is owner|admin|member.

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 CreateGrant added in v0.23.0

func CreateGrant(ctx context.Context, hubURL, token, namespace, alias, username, role string) error

CreateGrant upserts a grant via POST .../grants. role is viewer|editor|admin.

func CreateKeg added in v0.23.0

func CreateKeg(ctx context.Context, hubURL, token, namespace, alias, title, visibility string) error

CreateKeg asks the hub to create @namespace/alias via POST /api/v1/@{namespace}/kegs. A 409 is returned as an error wrapping keg.ErrExist so callers can detect "already exists" with errors.Is; 401/403 wrap ErrTokenRejected; other non-2xx statuses surface the hub's status line.

func DeleteHubFlight added in v0.23.0

func DeleteHubFlight(ctx context.Context, hubURL, token, namespace, slug string) error

func IntegrateHosts added in v0.19.0

func IntegrateHosts() []string

func IntegratePlugins added in v0.30.0

func IntegratePlugins(host string) ([]string, error)

IntegratePlugins returns the plugin names advertised by the host's embedded rendered marketplace. The marketplace, rather than installer code, is the source of truth as new optional plugins are added.

func KegBackendLabel added in v0.22.0

func KegBackendLabel(target *keg.Target) string

KegBackendLabel returns a stable, path-free identifier for a keg target suitable for user-facing output. It is used by surfaces that must describe "what kind of keg is this" without leaking the underlying filesystem path, remote URL, or other location-revealing details.

Mapping by scheme:

  • file-backed: "file-backed"
  • hub: "keg:@<namespace>/<kegName>"
  • http(s): "http" or "https"
  • in-memory: "in-memory"
  • other/unknown: the scheme string, or "" when target is nil

The hub label is the canonical keg reference (Target.String): the real "keg" scheme with the hub resolved from the namespace, never encoded in the string. File-backed kegs intentionally collapse to a single token: the alias is the user-visible handle, and the path lives only behind `tap info`.

func KegLocation added in v0.23.0

func KegLocation(target *keg.Target) string

KegLocation renders where a keg lives for user-facing output after `tap keg create`: "at <path>" for a file-backed keg, "on <hubURL>" for a remote keg, or "" when no concrete location is known (in-memory / nil target). Unlike KegBackendLabel it intentionally reveals the path/URL so a fresh create says exactly where the keg landed.

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, rt *toolkit.Runtime, storePath string) keg.TokenResolver

NewAuthStoreTokenResolver returns a keg.TokenResolver backed by store. rt and storePath enable in-place refresh of expired OAuth2 tokens; pass the same runtime and AuthStorePath the store was loaded from. A nil store yields a resolver that always returns "", matching AuthStore's nil-safe contract.

func OpenBrowser added in v0.23.0

func OpenBrowser(ctx context.Context, rt *toolkit.Runtime, rawURL string) error

OpenBrowser launches the platform-native "open URL" command and returns once the process has been started. We do NOT wait for exit because most browsers daemonize and exec.Wait would deadlock the CLI behind the user's window manager.

Quarantined stdlib exception (like pkg/tapper/editor_live.go): this spawns an external browser process whose timing is governed by the user's environment rather than any in-process clock. Routing through exec.CommandContext keeps cancellation working if the caller ctx is cancelled before the command launches.

On unrecognized platforms or launch failure, we print the URL to rt.Stream().Err so the user can copy-paste it manually — that's a successful fallback, not a hard error.

func RemoveNamespaceMember added in v0.23.0

func RemoveNamespaceMember(ctx context.Context, hubURL, token, namespace, username string) error

RemoveNamespaceMember removes a member via DELETE /api/v1/@{namespace}/members/@{username}.

func RenameKeg added in v0.25.0

func RenameKeg(ctx context.Context, hubURL, token, namespace, oldAlias, newAlias string) error

RenameKeg updates a keg alias in-place within the same namespace via PATCH .../settings. The hub keeps the immutable keg id and does not create redirects for the old selector.

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 hub resolution chain:

  1. explicit non-empty → canonicalize and use
  2. cfg.DefaultHub names a Hubs entry → use that entry's URL
  3. cfg.FallbackHub names a Hubs entry → use that entry's URL
  4. cfg.Hubs has exactly one remote/readonly entry → use it
  5. cfg.DisableAtlasHub is true → ErrAtlasHubDisabled
  6. fall back to DefaultHubURL

A misconfigured DefaultHub or FallbackHub 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.

func RevokeGrant added in v0.23.0

func RevokeGrant(ctx context.Context, hubURL, token, namespace, alias, username string) error

RevokeGrant removes a grant via DELETE .../grants/@{username}.

func SetKegVisibility added in v0.23.0

func SetKegVisibility(ctx context.Context, hubURL, token, namespace, alias, visibility string) error

SetKegVisibility updates a keg's visibility via PATCH .../settings. visibility is public|private.

func SetNamespaceMemberRole added in v0.23.0

func SetNamespaceMemberRole(ctx context.Context, hubURL, token, namespace, username, role string) error

SetNamespaceMemberRole changes a member's role via PATCH /api/v1/@{namespace}/members/@{username}.

func ValidateKegAlias added in v0.22.0

func ValidateKegAlias(alias string) error

ValidateKegAlias returns nil when alias matches the canonical alias shape and a wrapped keg.ErrInvalid otherwise. Empty input is rejected explicitly so callers can distinguish missing-alias errors from shape errors when reading the wrapped chain.

func ValidateNamespace added in v0.23.0

func ValidateNamespace(ns string) error

ValidateNamespace returns nil when ns is a legal namespace segment and a wrapped keg.ErrInvalid otherwise. Empty input is rejected explicitly. The "@" sigil is never part of the stored value; pass the bare namespace.

func WalkConfigsUp added in v0.23.0

func WalkConfigsUp(rt *toolkit.Runtime, start, rel string) []string

WalkConfigsUp returns the absolute paths of every existing rel file found by walking from start up to the filesystem root, ordered DEEPEST-FIRST (nearest to start first). Missing candidates are skipped. The user-global config is not included; callers layer it underneath the returned project configs.

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"`

	// RefreshToken, ClientID, and TokenEndpoint are populated by the OAuth2
	// device-login flow so the CLI can silently renew an expired AccessToken
	// without a re-login (see RefreshHubToken). All three are empty for a
	// pasted `thub_` API token, which never expires and cannot be refreshed.
	RefreshToken  string `yaml:"refresh_token,omitempty"`
	ClientID      string `yaml:"client_id,omitempty"`
	TokenEndpoint string `yaml:"token_endpoint,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 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.

func RefreshHubToken added in v0.23.0

func RefreshHubToken(ctx context.Context, rt *toolkit.Runtime, hubURL string, entry *AuthEntry) (*AuthEntry, error)

RefreshHubToken exchanges entry.RefreshToken at the hub's token endpoint and returns a new AuthEntry carrying the rotated access + refresh tokens and a recomputed expiry. The hub rotates refresh tokens single-use, so the caller must persist the returned entry (the old refresh token is now spent).

Endpoint/client resolution: the device-login flow records TokenEndpoint and ClientID on the entry, so a refresh is a single POST with no rediscovery. Entries that predate that (or omit the fields) fall back to rediscovering the token endpoint from hubURL and to the stock public client id.

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

	// DeviceLabel is an optional human-readable label for this login session.
	// Hubs may display it in account/session views.
	DeviceLabel 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

	// OnUserCode, when non-nil, is invoked once the hub has issued the
	// user_code + verification URI and before polling begins. It owns the
	// user-facing presentation: the CLI installs a handler that prints the
	// code, offers to open the browser (gh-style "press Enter to open"),
	// and falls back to printing the URL for manual copy/paste. When nil,
	// the flow prints a plain copy/paste prompt to PromptOut — the library
	// default never opens a browser, keeping pkg/tapper free of any
	// interactive/TTY dependency. A non-nil error aborts the login.
	OnUserCode func(context.Context, DeviceUserPrompt) error

	// 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 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 AuthStatusHub added in v0.28.1

type AuthStatusHub struct {
	// Present is false when no matching entry exists.
	Present bool

	// HubURL is the canonical key that matched.
	HubURL string

	// TokenPrefix is the first 12 chars of the access token followed by
	// "..." — matching the non-secret prefix the hub stores and shows in
	// its account UI — or "[set]" when the token is too short for a
	// meaningful prefix. The raw token is never exposed here.
	TokenPrefix string

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

	// Account is the username the hub reported for this token via the
	// live whoami probe. Empty when validation was skipped (--offline) or
	// failed.
	Account string

	// DisplayName is the human name the hub reported alongside Account.
	// Empty when the hub omits it or validation didn't run.
	DisplayName string

	// Valid is true when the live whoami probe confirmed the token. False
	// when the token was rejected, the hub was unreachable, or --offline
	// skipped the check.
	Valid bool

	// ValidationError is the error message from a failed/ skipped probe,
	// empty on success. Lets structured consumers branch without parsing
	// Formatted.
	ValidationError 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

	// LoginMethod records how the credential was obtained: "device" for the
	// OAuth2 browser/device flow (carries client + refresh + token endpoint),
	// "token" for a pasted API token. Lets structured consumers branch without
	// parsing Formatted.
	LoginMethod string

	// Renewable is true when a refresh token is stored — i.e. the access token
	// renews silently on expiry rather than forcing a re-login. Always false
	// for a pasted API token.
	Renewable bool

	// Formatted is the exact block rendered for this hub. Terminated with a
	// trailing newline.
	Formatted string
}

AuthStatusHub is the per-hub structured status used by AuthStatusResult.Hubs. Formatted is the exact block rendered for this hub, terminated with a trailing newline.

type AuthStatusOptions added in v0.20.0

type AuthStatusOptions struct {
	// Hub is a raw or canonical hub URL. When empty, AuthStatus reports every
	// stored hub login; an empty store surfaces a directed empty message (see
	// AuthStatus docs).
	Hub string

	// Offline skips the live whoami probe and reports purely from the
	// on-disk store (no network, no account name). Useful for scripts,
	// air-gapped use, or when an agent must not make outbound calls.
	Offline bool
}

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

	// TokenPrefix is the first 12 chars of the access token followed by
	// "..." — matching the non-secret prefix the hub stores and shows in
	// its account UI — or "[set]" when the token is too short for a
	// meaningful prefix. The raw token is never exposed here.
	TokenPrefix string

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

	// Account is the username the hub reported for this token via the
	// live whoami probe. Empty when validation was skipped (--offline) or
	// failed.
	Account string

	// DisplayName is the human name the hub reported alongside Account.
	// Empty when the hub omits it or validation didn't run.
	DisplayName string

	// Valid is true when the live whoami probe confirmed the token. False
	// when the token was rejected, the hub was unreachable, or --offline
	// skipped the check.
	Valid bool

	// ValidationError is the error message from a failed/ skipped probe,
	// empty on success. Lets structured consumers branch without parsing
	// Formatted.
	ValidationError 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

	// LoginMethod records how the credential was obtained: "device" for the
	// OAuth2 browser/device flow (carries client + refresh + token endpoint),
	// "token" for a pasted API token. Lets structured consumers branch without
	// parsing Formatted.
	LoginMethod string

	// Renewable is true when a refresh token is stored — i.e. the access token
	// renews silently on expiry rather than forcing a re-login. Always false
	// for a pasted API token.
	Renewable bool

	// Hubs contains structured per-hub status. It has one entry for single-hub
	// status responses and multiple entries when Hub was omitted with multiple
	// stored logins.
	Hubs []AuthStatusHub

	// 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 output plus structured fields for the MCP surface and any future renderers. Formatted is authoritative: both CLI and MCP emit it verbatim. Scalar fields are populated for single-hub responses; Hubs carries per-hub status entries.

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 BootstrapOptions added in v0.23.0

type BootstrapOptions struct {
	// Kind selects the deployment: local | cloud | enterprise. Empty defaults
	// to cloud (atlas is the compiled-in default hub).
	Kind string
	// Endpoint is the hub base URL; required when Kind == enterprise, ignored
	// otherwise. A bare host is upgraded to https://.
	Endpoint string
	// HubName overrides the hub key written for an enterprise endpoint. Empty
	// derives it from the endpoint host (see deriveHubName).
	HubName string
	// Namespace overrides the fallback namespace. Empty auto-derives it from
	// the OS user, then LocalHubName.
	Namespace string
}

BootstrapOptions configures Tap.Bootstrap, the first-run onboarding that materializes or refreshes the user-level tapper config around a deployment kind.

type BootstrapResult added in v0.23.0

type BootstrapResult struct {
	Path      string          // user config path written
	Created   bool            // true when a fresh file was created, false on update
	Kind      string          // normalized deployment kind
	Hub       string          // hub name written as fallbackHub
	HubURL    string          // login/display URL for cloud/enterprise; "" for local
	Namespace string          // resolved fallback namespace
	Warnings  []ConfigWarning // semantic warnings from ValidateConfig
}

BootstrapResult reports what Bootstrap wrote so the CLI can phrase its output, drive an optional login, and surface non-fatal config warnings.

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

	// Query is an optional boolean expression (same syntax as tap tags) used
	// to select nodes. Mutually exclusive with NodeIDs.
	Query 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.

Project config uses DEFAULT slots (defaultHub/defaultNamespace) — the authoritative choice for the project, overriding the user-level fallbacks.

func DefaultUserConfig

func DefaultUserConfig(name string, localKegRoot string) *Config

DefaultUserConfig returns a sensible default global/user Config.

The global user config uses the FALLBACK hub (fallbackHub) so keg references need not specify a hub. The namespace is NOT pinned by a global fallbackNamespace — it comes from the resolved hub's own namespace field, so `name` seeds the remote hub's default namespace while the local hub keeps the reserved @local. The default remote hub (atlas) and the built-in local hub are registered, the local namespace maps to the local hub, and localKegRoot seeds the local hub's basePath.

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.
  • Hubs are merged by name; later entries override earlier ones.
  • Namespaces are merged by name; later entries override earlier ones.
  • KegMap entries are appended in order, but entries with the same alias + path pattern are replaced by later entries.

func ParseConfig

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

ParseConfig parses raw YAML into a Config data model. Unknown keys are ignored by the decoder.

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) 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 (high-precedence slot).

func (*Config) DefaultKeg

func (cfg *Config) DefaultKeg() string

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

func (*Config) DefaultNamespace added in v0.23.0

func (cfg *Config) DefaultNamespace() string

DefaultNamespace returns the default namespace (high-precedence slot).

func (*Config) DeleteHub added in v0.23.0

func (cfg *Config) DeleteHub(name string) (bool, error)

DeleteHub removes a hub entry by name, reporting whether it was present.

func (*Config) DeleteNamespace added in v0.23.0

func (cfg *Config) DeleteNamespace(name string) bool

DeleteNamespace removes a namespace→hub mapping by name, reporting whether it was present. `tap hub remove` uses it to prune namespace pins that route to a hub being removed.

func (*Config) DisableAtlasHub added in v0.23.0

func (cfg *Config) DisableAtlasHub() bool

DisableAtlasHub returns true when the synthesized built-in atlas hub is suppressed.

func (*Config) DisableLocalHub added in v0.23.0

func (cfg *Config) DisableLocalHub() bool

DisableLocalHub returns true when the synthesized built-in local hub is suppressed.

func (*Config) FallbackHub added in v0.23.0

func (cfg *Config) FallbackHub() string

FallbackHub returns the fallback hub name (last-resort slot).

func (*Config) FallbackKeg added in v0.2.0

func (cfg *Config) FallbackKeg() string

FallbackKeg returns the last-resort keg alias.

func (*Config) FallbackNamespace added in v0.23.0

func (cfg *Config) FallbackNamespace() string

FallbackNamespace returns the fallback namespace (last-resort slot).

func (*Config) Flight added in v0.23.0

func (cfg *Config) Flight() string

Flight returns the persisted flight reference applied when no --flight flag is given.

func (*Config) Hub added in v0.23.0

func (cfg *Config) Hub(name string) (HubEntry, bool)

Hub returns the named hub entry. The built-in hubs "local" (filesystem) and "atlas" (the default remote hub) are synthesized when not explicitly configured — unless disabled via disableLocalHub / disableAtlasHub, in which case the synthesized built-in is suppressed and Hub reports it as not found. An explicit configuration in hubs{} always wins over (and is unaffected by the disable flag for) the built-in.

func (*Config) Hubs added in v0.20.0

func (cfg *Config) Hubs() map[string]HubEntry

Hubs returns the map of configured hubs keyed by name.

func (*Config) KegMap

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

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

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(_ *toolkit.Runtime, _ string) string

LookupAliasForTarget previously reverse-mapped a resolved target back to its configured keg alias. The namespace-centric model has no alias table, so a target no longer carries a short alias; callers fall back to the canonical @namespace/name label. Retained as a no-op so those callers need no change.

func (*Config) Namespace added in v0.23.0

func (cfg *Config) Namespace(name string) (NamespaceRef, bool)

Namespace returns the namespace reference registered under name.

func (*Config) Namespaces added in v0.23.0

func (cfg *Config) Namespaces() map[string]NamespaceRef

Namespaces returns the map of namespace name to hosting hub reference.

func (*Config) ResolveAlias

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

ResolveAlias resolves a keg selector string to a concrete Target. The selector is parsed as a keg reference (see parseKegRef) and resolved through the namespace-centric ResolveRef chain — there is no kegs alias table.

Returns (nil, error) when the selector is empty or resolution fails.

func (*Config) ResolveDefault

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

ResolveDefault resolves the current DefaultKeg reference 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) ResolveRef added in v0.23.0

func (cfg *Config) ResolveRef(rt *toolkit.Runtime, ref KegRef) (*keg.Target, error)

ResolveRef resolves a (hub, namespace, name) reference into a concrete keg.Target. It applies the namespace-centric chains via resolveNamespaceHub — namespace first (explicit → default/fallback), then the hub that hosts that namespace — and the per-kind backend mapping:

  • local: <basePath>/@<namespace>/<name> as a file target
  • remote: <hub.url>/api/v1/@<namespace>/kegs/<name> as a hub target
  • readonly: same URL as remote, with Target.Readonly set

func (*Config) SetDefaultHub added in v0.20.0

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

SetDefaultHub sets the default hub name.

func (*Config) SetDefaultKeg

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

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

func (*Config) SetDefaultNamespace added in v0.23.0

func (cfg *Config) SetDefaultNamespace(ns string) error

SetDefaultNamespace sets the default namespace.

func (*Config) SetDisableAtlasHub added in v0.23.0

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

SetDisableAtlasHub toggles the synthesized built-in atlas hub.

func (*Config) SetDisableLocalHub added in v0.23.0

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

SetDisableLocalHub toggles the synthesized built-in local hub.

func (*Config) SetFallbackHub added in v0.23.0

func (cfg *Config) SetFallbackHub(hub string) error

SetFallbackHub sets the fallback hub name.

func (*Config) SetFallbackKeg added in v0.2.0

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

SetFallbackKeg sets the fallback keg alias.

func (*Config) SetFallbackNamespace added in v0.23.0

func (cfg *Config) SetFallbackNamespace(ns string) error

SetFallbackNamespace sets the fallback namespace.

func (*Config) SetFlight added in v0.23.0

func (cfg *Config) SetFlight(flight string) error

SetFlight sets the persisted flight reference (or clears it when empty).

func (*Config) SetHub added in v0.23.0

func (cfg *Config) SetHub(name string, entry HubEntry) error

SetHub adds or replaces a hub entry by name.

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) SetNamespace added in v0.23.0

func (cfg *Config) SetNamespace(name string, ref NamespaceRef) error

SetNamespace adds or replaces the hub mapping for a namespace.

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) ProjectConfig

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

ProjectConfig returns the merged project-level configuration with optional caching. It walks from the workspace root up to the filesystem root, collecting every .tapper/config.yaml, and merges them so a deeper directory overrides a shallower one. Hub definitions and credentials are stripped from project layers — only the user config may define them — and each strip is recorded as a load warning surfaced by Config(). Returns keg.ErrNotExist when no project config exists.

func (*ConfigService) ResetCache

func (s *ConfigService) ResetCache()

ResetCache clears cached user, project, and merged configs.

func (*ConfigService) ResolveTarget

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

ResolveTarget resolves a keg selector to a keg target. When the selector is empty it uses defaultKeg, then fallbackKeg. The selector is parsed as a keg reference and turned into a concrete target by Config.ResolveAlias (the namespace-centric ResolveRef chain and per-hub-kind backend mapping).

func (*ConfigService) UserConfig

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

UserConfig returns the global user configuration.

func (*ConfigService) UserConfigExists added in v0.23.0

func (s *ConfigService) UserConfigExists() bool

UserConfigExists reports whether a user config file is present — the signal that `tap bootstrap` has been run. When an explicit config path is set (--config) it checks that file; otherwise the standard user config path. It inspects the filesystem only; it never parses, so a corrupt-but-present config still counts as bootstrapped.

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 CreateFlightOptions added in v0.23.0

type CreateFlightOptions struct {
	Ref          string
	Title        string
	Visibility   string
	Capabilities []FlightCapability
	Instructions string
	Cover        []FlightCover
}

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 DeleteFlightOptions added in v0.23.0

type DeleteFlightOptions struct {
	Ref string
}

type DeleteImageOptions added in v0.2.0

type DeleteImageOptions struct {
	KegTargetOptions
	NodeID string
	Name   string
}

DeleteImageOptions configures behavior for Tap.DeleteImage.

type DeviceUserPrompt added in v0.23.0

type DeviceUserPrompt struct {
	UserCode                string
	VerificationURI         string
	VerificationURIComplete string
	ExpiresIn               int64
}

DeviceUserPrompt is the subset of the RFC 8628 device_authorization response an OnUserCode handler needs to drive the user-facing step. It is the exported projection of deviceAuthResponse so callers in other packages (the CLI) can present the code without seeing the wire type. VerificationURIComplete (a URL with the user_code embedded) is retained from the wire response but deliberately never displayed or opened — see VerificationURL.

func (DeviceUserPrompt) VerificationURL added in v0.23.0

func (p DeviceUserPrompt) VerificationURL() string

VerificationURL returns the URL the user should open: always the bare verification_uri, never a code-bearing variant. The user_code is shown separately and typed into the page (GitHub-style, RFC 8628 §3.3.1) — embedding it in the URL would leak it into browser history and any front proxy's access logs. The hub-provided verification_uri_complete is deliberately ignored for the same reason.

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 EditFlightOptions added in v0.24.0

type EditFlightOptions struct {
	Ref string

	// Stream, when piped with non-empty content, supplies the manifest YAML
	// directly so scripts can apply a full manifest without an editor.
	Stream *toolkit.Stream
}

EditFlightOptions configures behavior for Tap.EditFlight.

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 EditSchemaOptions added in v0.25.0

type EditSchemaOptions struct {
	KegTargetOptions
	Type   string
	Stream *toolkit.Stream
}

type ExportOptions added in v0.4.0

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

type Flight added in v0.23.0

type Flight struct {
	Name         string `yaml:"-" json:"name,omitempty"`
	Namespace    string `yaml:"-" json:"namespace,omitempty"`
	Slug         string `yaml:"-" json:"slug,omitempty"`
	Source       string `yaml:"-" json:"source,omitempty"` // "local" or a hub name
	ManifestHash string `yaml:"-" json:"-"`
	FlightManifest
}

Flight is a discovered flight: its manifest plus provenance.

func (*Flight) HasCapability added in v0.30.0

func (f *Flight) HasCapability(capability FlightCapability) bool

HasCapability reports whether a validated manifest grants capability.

func (*Flight) RoleFor added in v0.23.0

func (f *Flight) RoleFor(alias, namespace, kegName string) (FlightRole, bool)

RoleFor reports the flight-scoped role for a keg. An empty cover denies all KEG access. Manifests normally arrive through normalizeFlightManifest, which folds legacy AllowedKegs into Cover; the fallback here only covers Flight values constructed by hand.

type FlightCapability added in v0.30.0

type FlightCapability string

type FlightCover added in v0.23.0

type FlightCover struct {
	Namespace string     `yaml:"namespace,omitempty" json:"namespace,omitempty"`
	Keg       string     `yaml:"keg" json:"keg"`
	Role      FlightRole `yaml:"role" json:"role"`
}

func ParseFlightCoverSpecs added in v0.23.0

func ParseFlightCoverSpecs(specs []string) ([]FlightCover, error)

ParseFlightCoverSpecs parses CLI/MCP cover specs of the form "keg", "@ns/keg", or "@ns/keg=role". Bare entries default to viewer, matching the manifest parser; editor must be requested explicitly.

type FlightManifest added in v0.23.0

type FlightManifest struct {
	Title        string             `yaml:"title,omitempty" json:"title,omitempty"`
	Visibility   string             `yaml:"visibility,omitempty" json:"visibility,omitempty"`
	Capabilities []FlightCapability `yaml:"capabilities,omitempty" json:"capabilities,omitempty"`
	Cover        []FlightCover      `yaml:"cover,omitempty" json:"cover,omitempty"`
	AllowedKegs  []string           `yaml:"allowedKegs,omitempty" json:"allowedKegs,omitempty"`
	Instructions string             `yaml:"instructions,omitempty" json:"instructions,omitempty"`
}

FlightManifest is the on-disk/API shape of a flight: explicit covered kegs plus markdown instructions. AllowedKegs is kept for backward compatibility with local manifests and is normalized into editor-cap cover entries.

type FlightRef added in v0.23.0

type FlightRef struct {
	Namespace string
	Slug      string
}

func ParseFlightRef added in v0.23.0

func ParseFlightRef(raw string, defaultNamespace string) (FlightRef, error)

func (FlightRef) Canonical added in v0.23.0

func (r FlightRef) Canonical() string

type FlightRestrictionError added in v0.23.0

type FlightRestrictionError struct {
	Flight string
	Keg    string
	Want   FlightRole
	Got    FlightRole
}

FlightRestrictionError is returned when a resolved keg falls outside the active flight's cover or role cap.

func (*FlightRestrictionError) Error added in v0.23.0

func (e *FlightRestrictionError) Error() string

type FlightRole added in v0.23.0

type FlightRole string

func (FlightRole) AtLeast added in v0.23.0

func (r FlightRole) AtLeast(want FlightRole) bool

AtLeast reports whether r grants at least want within a flight cover.

type FlightService added in v0.23.0

type FlightService struct {
	Runtime       *toolkit.Runtime
	ConfigService *ConfigService
	// KegService supplies the token resolver (and its cached auth store)
	// for hub requests. Lazily constructed when not wired by NewTap.
	KegService *KegService
	// contains filtered or unexported fields
}

FlightService discovers and loads flights for configured local and remote hubs. Local-hub flights live under <basePath>/flights.d; remote-hub flights are served by the Hub API.

func (*FlightService) GetFlight added in v0.23.0

func (s *FlightService) GetFlight(ctx context.Context, name string) (*Flight, error)

GetFlight loads a single flight by ref. Returns keg.ErrNotExist when no manifest exists. Unqualified refs first try local manifests for backward compatibility, then unique remote slug matches. Results are memoized for the life of the process (see flightCache).

func (*FlightService) GetFlightFresh added in v0.30.0

func (s *FlightService) GetFlightFresh(ctx context.Context, name string) (*Flight, error)

GetFlightFresh bypasses the process cache. MCP sessions use this before gated calls so a manifest change invalidates the pinned session instead of silently changing its authority.

func (*FlightService) ListFlights added in v0.23.0

func (s *FlightService) ListFlights(ctx context.Context, hub string, warnings *[]string) ([]string, error)

ListFlights returns canonical @namespace/+slug refs for flights discovered across configured hubs, sorted. When hub is non-empty, discovery is limited to that configured hub. Remote/auth/network errors are best-effort so shell completion and discovery remain responsive; when warnings is non-nil each skipped hub appends one message so user-facing listings can say what was left out.

func (*FlightService) StoreSessionFlight added in v0.30.0

func (s *FlightService) StoreSessionFlight(name string, f *Flight)

StoreSessionFlight pins a validated flight in the process cache for Tap's per-operation cover enforcement. MCP session invalidation is handled by the server-owned gate before these cached values are used.

type ForceUnlockOptions added in v0.11.0

type ForceUnlockOptions struct {
	NodeID string
	KegTargetOptions
}

ForceUnlockOptions configures behavior for Tap.ForceUnlock.

type GetFlightOptions added in v0.23.0

type GetFlightOptions struct {
	Name string
}

GetFlightOptions selects a single flight by name.

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 HubAddOptions added in v0.23.0

type HubAddOptions struct {
	Name     string
	URL      string
	TokenEnv string
}

HubAddOptions adds a remote hub connection to user config.

type HubEntry added in v0.23.0

type HubEntry struct {
	Kind string `yaml:"kind,omitempty"`
	// DefaultNamespace is this hub's default namespace, used when a reference
	// resolved against this hub omits its namespace. A hub hosts many namespaces;
	// this is only the default. The "@" sigil is implied — store the bare value.
	DefaultNamespace string `yaml:"defaultNamespace,omitempty"`
	URL              string `yaml:"url,omitempty"`
	BasePath         string `yaml:"basePath,omitempty"`
	Token            string `yaml:"token,omitempty"`
	TokenEnv         string `yaml:"tokenEnv,omitempty"`
}

HubEntry describes a single configured hub, keyed by name in the hubs map.

Kind selects the backend: "remote" (read-write HTTP, the default when Kind is empty), "local" (filesystem) or "readonly" (read-only HTTP). Remote and readonly hubs use URL; local hubs use BasePath as the filesystem root that holds @<namespace>/<name> keg directories.

type HubFlight added in v0.23.0

type HubFlight struct {
	Namespace    string             `json:"namespace"`
	Slug         string             `json:"slug"`
	Title        string             `json:"title"`
	Instructions string             `json:"instructions"`
	Visibility   string             `json:"visibility"`
	Capabilities []FlightCapability `json:"capabilities"`
	Cover        []HubFlightCover   `json:"cover"`
}

func CreateHubFlight added in v0.23.0

func CreateHubFlight(ctx context.Context, hubURL, token, namespace string, flight HubFlight) (*HubFlight, error)

func GetHubFlight added in v0.23.0

func GetHubFlight(ctx context.Context, hubURL, token, namespace, slug string) (*HubFlight, error)

func ListUserFlights added in v0.23.0

func ListUserFlights(ctx context.Context, hubURL, token string) ([]HubFlight, error)

func UpdateHubFlight added in v0.23.0

func UpdateHubFlight(ctx context.Context, hubURL, token, namespace, slug string, flight HubFlight) (*HubFlight, error)

type HubFlightCover added in v0.23.0

type HubFlightCover struct {
	Namespace string `json:"namespace,omitempty"`
	Keg       string `json:"keg"`
	Role      string `json:"role"`
}

type HubGrant added in v0.23.0

type HubGrant struct {
	Username string `json:"username"`
	Role     string `json:"role"` // viewer|editor|admin
}

HubGrant is one per-(user, role) grant on a keg. Mirrors the hub's handler.ListGrants JSON body — keep the two in sync. (granted_at is sent by the hub but unused here.)

func ListGrants added in v0.23.0

func ListGrants(ctx context.Context, hubURL, token, namespace, alias string) ([]HubGrant, error)

ListGrants returns the grants on @namespace/alias via GET /api/v1/@{namespace}/kegs/{keg}/grants (admin-only on the hub).

type HubInfo added in v0.23.0

type HubInfo struct {
	Name      string
	URL       string
	Kind      string
	IsDefault bool
	Source    string // "user" or "built-in"
}

HubInfo describes a configured hub for `tap hub list`.

type HubKeg added in v0.23.0

type HubKeg struct {
	Namespace  string `json:"namespace"`
	Alias      string `json:"alias"`
	Visibility string `json:"visibility"`
	Role       string `json:"role"`
}

HubKeg is one keg the hub reports the authenticated user can reach. It mirrors the hub's handler.UserKegItem JSON body — keep the two in sync.

func ListUserKegs added in v0.23.0

func ListUserKegs(ctx context.Context, hubURL, token string) ([]HubKeg, error)

ListUserKegs returns every keg the hub reports the bearer token can reach (namespace membership + grants) via GET /api/v1/kegs.

type HubListOptions added in v0.23.0

type HubListOptions struct {
	Hub string
}

HubListOptions selects which hub to enumerate. An empty Hub aggregates every configured hub the user can reach.

type HubMember added in v0.23.0

type HubMember struct {
	Username string `json:"username"`
	Role     string `json:"role"` // owner|admin|member
}

HubMember is one namespace membership row. Mirrors the hub's handler.memberWire JSON body — keep the two in sync.

func ListNamespaceMembers added in v0.23.0

func ListNamespaceMembers(ctx context.Context, hubURL, token, namespace string) ([]HubMember, error)

ListNamespaceMembers returns the member roster of a namespace via GET /api/v1/@{namespace}/members.

type HubNamespace added in v0.23.0

type HubNamespace struct {
	Name string `json:"name"`
	Kind string `json:"kind,omitempty"` // user|org
	Role string `json:"role,omitempty"` // caller's role
}

HubNamespace is one namespace the caller belongs to (or just created). Mirrors the hub's handler.namespaceWire JSON body — keep the two in sync.

func CreateNamespace deprecated added in v0.23.0

func CreateNamespace(ctx context.Context, hubURL, token, name string) (*HubNamespace, error)

CreateNamespace is disabled for remote clients. Namespace creation is a paid hub UI flow; use Tap.NamespaceCreate to get the browser handoff URL.

Deprecated: remote namespace creation is not supported.

func ListNamespaces added in v0.23.0

func ListNamespaces(ctx context.Context, hubURL, token string) ([]HubNamespace, error)

ListNamespaces returns the namespaces the caller belongs to via GET /api/v1/namespaces.

type HubRemoveOptions added in v0.23.0

type HubRemoveOptions struct {
	Name string
}

HubRemoveOptions removes a hub connection from user config.

type HubSetDefaultOptions added in v0.23.0

type HubSetDefaultOptions struct {
	Name string
	User bool
}

HubSetDefaultOptions sets the default hub. It writes project config by default and user config with User=true (mirroring `tap config edit`).

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 {
	// Local (filesystem) project destination selectors. Any of these forces a
	// project-local keg under the git root / cwd / explicit path; namespace and
	// hub resolution do not apply.
	Project bool
	User    bool   // pin the reserved @local namespace (this machine's local hub)
	Cwd     bool   // use cwd as the project root base instead of git root
	Path    string // explicit filesystem path; implies a local project destination

	// Destination overrides for the namespace-centric resolution. An empty
	// Namespace defers to config (kegs[name].Namespace → default/fallback); an
	// empty Hub defers to namespaces[ns].Hub → default/fallback. A bare name
	// thus resolves to the default namespace+hub — typically a remote create —
	// while "@local/<name>" pins the filesystem hub.
	Namespace string
	Hub       string

	TokenEnv string

	Creator string
	Title   string
	Keg     string

	// NonInteractive suppresses interactive prompts when set, forcing the
	// caller to rely on flag-driven defaults (platform user-data dir, alias
	// inferred from cwd, etc.) even when the surface is attached to a TTY.
	// The Tap method itself does not consult this field — TTY handling is a
	// CLI/MCP concern — but the option lives on InitOptions so both the CLI
	// flag and the MCP input field map to the same canonical contract.
	NonInteractive bool

	// RequireBootstrap makes a namespace/hub create (anything but an explicit
	// local destination) fail with ErrNotBootstrapped when no user config exists
	// — `tap bootstrap` has not been run. Set by the full `tap` surface and the
	// MCP server; left false for direct Tap API callers (e.g. tests), which keep
	// the unconfigured local fallback.
	RequireBootstrap bool
}

func (InitOptions) LocalDestination added in v0.4.0

func (o InitOptions) LocalDestination() bool

LocalDestination reports whether the options force a project-local filesystem keg (as opposed to the namespace-resolved user/hub destination).

type IntegrateOptions added in v0.19.0

type IntegrateOptions struct {
	KegTargetOptions
	Host    string
	DryRun  bool
	Plugins []string
	Scope   string
}

IntegrateOptions selects the host, native install scope, dry-run behavior, and optional plugins for native plugin installation. The baseline tapper plugin is always installed.

type IntegrateResult added in v0.30.0

type IntegrateResult struct {
	Root     string
	Paths    []string
	Commands [][]string
}

IntegrateResult describes the extracted marketplace and the host commands used to inspect, register, and install it.

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 KegGrantOptions added in v0.23.0

type KegGrantOptions struct {
	Keg       string
	Namespace string
	Hub       string
	User      string
	Role      string // viewer|editor|admin
}

KegGrantOptions upserts a grant on a keg.

type KegGrantsOptions added in v0.23.0

type KegGrantsOptions struct {
	Keg       string
	Namespace string
	Hub       string
}

KegGrantsOptions selects the keg whose grants to list. Keg is the selector (a bare name or @namespace/keg); empty resolves the default keg. Namespace and Hub override the resolved reference's components.

type KegInfoOptions

type KegInfoOptions struct {
	KegTargetOptions

	// JSON renders the diagnostics as JSON instead of YAML.
	JSON bool

	// Debug includes working-directory and backend resolution diagnostics.
	Debug bool
}

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 KegRef added in v0.23.0

type KegRef struct {
	Hub       string `yaml:"hub,omitempty"`
	Namespace string `yaml:"namespace,omitempty"`
	Name      string `yaml:"name,omitempty"`
	Path      string `yaml:"path,omitempty"`
}

KegRef is the (hub, namespace, name) triple a keg alias resolves to. An empty Hub falls back to defaultHub/fallbackHub; an empty Namespace falls back to defaultNamespace/fallbackNamespace (see Config.ResolveRef).

Path addresses a keg by an explicit local filesystem path. When set it takes precedence over the triple and resolves to a file target at that path. The triple addresses a keg by namespace; Path addresses one directly on disk.

func (*KegRef) UnmarshalYAML added in v0.23.0

func (r *KegRef) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML accepts the canonical mapping form ({hub, namespace, name, path}) and a scalar shorthand parsed via keg.Parse — the canonical keg shorthand "keg:@ns/name" sets {namespace, name} (the hub is resolved from the namespace, never encoded), while a bare file/url scalar maps onto a Path. To pin a hub, use the mapping form's "hub" field. Writes always serialize the canonical mapping form.

type KegRenameOptions added in v0.25.0

type KegRenameOptions struct {
	Old       string
	New       string
	Namespace string
	Hub       string
}

KegRenameOptions renames a hub-backed keg alias within one namespace.

type KegRevokeOptions added in v0.23.0

type KegRevokeOptions struct {
	Keg       string
	Namespace string
	Hub       string
	User      string
}

KegRevokeOptions revokes a user's grant on a keg.

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 keg selector: a bare name or an @namespace/keg reference.
	Keg string

	// Namespace overrides the namespace the keg resolves in when Keg is a bare
	// name (it loses to an @namespace/ already present in Keg). Empty means use
	// the configured defaultNamespace/fallbackNamespace chain.
	Namespace string

	// Hub pins the hub the keg resolves on, overriding namespace→hub resolution.
	// Empty means resolve the hub from the namespace as usual.
	Hub string

	// Project resolves using project-local keg discovery. Not exposed as a tap
	// flag; retained for the pruned `keg` binary (ForceProjectResolution) and
	// the keg-create destination flags.
	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 optional task context that can restrict which kegs are available
	// and injects agent instructions. It composes with the single-keg selectors
	// (Keg/Namespace/Hub): the selector picks a keg and the flight gates it unless
	// BypassFlightRestrictions is true.
	Flight string

	// BypassFlightRestrictions skips flight cover and role-cap checks while
	// preserving Flight for callers that still need the flight context, such as
	// orient. Leave false for MCP and other agent-facing surfaces.
	BypassFlightRestrictions bool

	// RequireBootstrap makes config-driven resolution fail with
	// ErrNotBootstrapped when no user config exists. Set by the full `tap`
	// surface and the MCP server; left false by the pruned `keg` binary and by
	// direct Tap API callers (e.g. tests).
	RequireBootstrap bool
}

KegTargetOptions describes how a command should resolve a keg target.

type KegVisibilityOptions added in v0.23.0

type KegVisibilityOptions struct {
	Keg        string
	Namespace  string
	Hub        string
	Visibility string // public|private
}

KegVisibilityOptions sets a keg's visibility.

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 ListFlightsOptions added in v0.23.0

type ListFlightsOptions struct {
	// Hub limits discovery to one configured hub. Empty discovers flights across
	// every configured hub.
	Hub string
	// Warnings, when non-nil, collects one message per hub that was skipped
	// (missing token, network failure) instead of failing the whole listing.
	// Completion paths leave it nil so discovery stays silent and best-effort.
	Warnings *[]string
}

ListFlightsOptions controls Tap.ListFlights. Flights are hub-scoped, so there is no keg target.

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 NamespaceAddMemberOptions added in v0.23.0

type NamespaceAddMemberOptions struct {
	Namespace string
	Hub       string
	User      string
	Role      string // owner|admin|member
}

NamespaceAddMemberOptions upserts a member into a namespace.

type NamespaceCreateOptions added in v0.23.0

type NamespaceCreateOptions struct {
	Name string
	Hub  string
}

NamespaceCreateOptions creates an org namespace.

type NamespaceCreateResult added in v0.23.0

type NamespaceCreateResult struct {
	Name string
	Hub  string
	URL  string
}

NamespaceCreateResult tells the caller where to create the namespace in the hub UI. Namespace creation is a paid hub feature, so the CLI/MCP surface hands the user to the browser instead of calling the API.

type NamespaceListOptions added in v0.23.0

type NamespaceListOptions struct {
	Hub string
}

NamespaceListOptions selects which hub to query for namespaces. Empty uses the resolved default hub.

type NamespaceMembersOptions added in v0.23.0

type NamespaceMembersOptions struct {
	Namespace string
	Hub       string
}

NamespaceMembersOptions selects the namespace whose members to list. Namespace is the selector; empty resolves the default namespace. Hub pins the hub.

type NamespaceRef added in v0.23.0

type NamespaceRef struct {
	Hub string `yaml:"hub,omitempty"`
}

NamespaceRef names the hub that hosts a namespace. It is the conflict resolver for namespace→hub: when a namespace could live on more than one configured hub, an entry here pins which one. An empty Hub falls back to the hub precedence chain (see Config.resolveHubForNamespace).

func (*NamespaceRef) UnmarshalYAML added in v0.23.0

func (r *NamespaceRef) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML accepts the canonical mapping form ({hub: name}) and the scalar shorthand (a bare hub name), so both `jlrickert: atlas` and `jlrickert: {hub: atlas}` load. Writes always serialize the mapping form.

type NamespaceRemoveMemberOptions added in v0.23.0

type NamespaceRemoveMemberOptions struct {
	Namespace string
	Hub       string
	User      string
}

NamespaceRemoveMemberOptions removes a member from a namespace.

type NamespaceSetRoleOptions added in v0.23.0

type NamespaceSetRoleOptions struct {
	Namespace string
	Hub       string
	User      string
	Role      string // owner|admin|member
}

NamespaceSetRoleOptions changes an existing member's role.

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 NodeSnapshotViewOptions added in v0.23.0

type NodeSnapshotViewOptions struct {
	KegTargetOptions
	NodeID string
	Rev    string
}

type OrientOptions added in v0.19.0

type OrientOptions struct {
	KegTargetOptions
}

OrientOptions is the input to Tap.Orient. Every field is optional: a zero-valued call returns the KEG system payload with the target keg resolved from KegTargetOptions.

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 ReadImageOptions added in v0.24.0

type ReadImageOptions struct {
	KegTargetOptions
	NodeID string
	Name   string
}

ReadImageOptions configures behavior for Tap.ReadImage.

type RefContext added in v0.23.0

type RefContext struct {
	CurrentKeg keg.Keg
}

RefContext carries the "current keg" so a node reference can resolve a keg-local alias against that keg's Links table and imply the hub for a qualified reference from the current keg's hub.

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 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
	// Namespace overrides the namespace component of the resolved reference when
	// the selector is a bare name. Empty uses the configured chain.
	Namespace string
	// Hub pins the hub the reference resolves on, overriding namespace→hub
	// resolution. Empty resolves the hub from the namespace as usual.
	Hub 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
	// RequireBootstrap makes config/namespace/hub-driven resolution fail with
	// ErrNotBootstrapped when no user config exists (`tap bootstrap` has not been
	// run). The full `tap` surface sets it; the pruned `keg` binary does not.
	// Explicit filesystem destinations (Project/Cwd/Path) and selectors that are
	// themselves a filesystem path are exempt.
	RequireBootstrap bool
	// NoCache disables in-memory keg caching for this resolution.
	NoCache bool
}

ResolveKegOptions controls how KegService resolves a keg target.

type SchemaOptions added in v0.25.0

type SchemaOptions struct {
	KegTargetOptions
	Type string
	Data []byte
}

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 empty, all tags are listed.
	Query 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
	FlightService *FlightService

	// AuthValidateFn is the seam AuthStatus uses for its live whoami probe.
	// Defaulted to ValidateToken in NewTap; tests override it (or point the
	// hub at an httptest server) to avoid real network I/O. Both the CLI and
	// MCP surfaces share the same *Tap, so overriding it covers both.
	AuthValidateFn func(ctx context.Context, rt *toolkit.Runtime, hubURL, token string) (*WhoAmI, error)

	// KegResolver, when non-nil, overrides config-driven keg resolution. It is
	// the seam tapper-hub uses to serve a per-user MCP connector: the hub injects
	// a resolver that opens kegs from its own Postgres catalog and enforces the
	// caller's role, bypassing the on-disk config cascade and AuthStore entirely
	// (which, in the hub server's process, hold no login). role is the access
	// level the calling operation needs — FlightRoleViewer for reads,
	// FlightRoleEditor for writes — so the resolver can map it to a catalog role
	// check. Every node read/write op funnels through resolveKegForRole, so a
	// single resolver covers the whole surface. Left nil for the CLI, which keeps
	// the standard config-driven resolution.
	KegResolver func(ctx context.Context, opts KegTargetOptions, role FlightRole) (keg.Keg, error)
}

func NewTap

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

func (*Tap) ActiveFlightName added in v0.30.0

func (t *Tap) ActiveFlightName(explicit string) string

ActiveFlightName resolves an explicit flight or the persistent project default without mutating configuration.

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) AuthRefreshAll added in v0.28.1

func (t *Tap) AuthRefreshAll(ctx context.Context)

AuthRefreshAll renews every stored hub credential whose access token is expired or within refreshSkew of expiring, persisting the rotated pairs. Entries without a refresh token (pasted API tokens) or without a known expiry are skipped. Best-effort by design: failures are logged at debug and never returned — this runs on every CLI/MCP startup and a broken hub must not block unrelated commands. Cost when all tokens are fresh: one file read, zero network calls.

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 one or more hubs stored → report every stored hub.

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) Bootstrap added in v0.23.0

func (t *Tap) Bootstrap(ctx context.Context, opts BootstrapOptions) (*BootstrapResult, error)

Bootstrap creates or refreshes the user-level config for a chosen deployment kind so plain `tap` commands resolve without per-invocation flags. It writes the FALLBACK hub (the user/global convention — project config owns the high-precedence default* slots) and always ensures the built-in local hub is present and that the reserved @local namespace maps to it.

It does not write a global fallbackNamespace or a per-user namespace→hub entry: the preferred namespace comes from the resolved hub's own namespace field. For local that is @local; for cloud/enterprise it is the logged-in user's home namespace, adopted onto the hub after login by SetBootstrapNamespace. The only namespace→hub entry written is local→localHub.

It is idempotent: an existing config is loaded and only the fallback hub, the local namespace mapping, and the kind's hub entry are touched, so user-defined kegs/kegMap survive a re-run untouched.

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) CreateFlight added in v0.23.0

func (t *Tap) CreateFlight(ctx context.Context, opts CreateFlightOptions) (*Flight, error)

func (*Tap) CreateSchema added in v0.25.0

func (t *Tap) CreateSchema(ctx context.Context, opts SchemaOptions) 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) DeleteFlight added in v0.23.0

func (t *Tap) DeleteFlight(ctx context.Context, opts DeleteFlightOptions) error

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) DeleteSchema added in v0.25.0

func (t *Tap) DeleteSchema(ctx context.Context, opts SchemaOptions) 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) EditFlight added in v0.24.0

func (t *Tap) EditFlight(ctx context.Context, opts EditFlightOptions) (*Flight, error)

EditFlight edits a Hub-backed flight's manifest as YAML.

If stdin is piped with non-empty content, the piped manifest is validated and applied directly. Otherwise the manifest opens in the configured editor; every save is validated and PUT to the hub immediately, so the editor session behaves like the rest of tapper's live-save edit flows.

The manifest carries title, visibility, capabilities, cover, and instructions. The slug and namespace are fixed by the ref: the YAML document cannot express them, and a stray "slug" key is rejected as an unknown field.

func (*Tap) EditSchema added in v0.25.0

func (t *Tap) EditSchema(ctx context.Context, opts EditSchemaOptions) error

func (*Tap) Export added in v0.4.0

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

Export writes a keg-archive of the selected nodes (all nodes when none are named) to opts.OutputPath and returns the resolved output path.

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) GetFlight added in v0.23.0

func (t *Tap) GetFlight(ctx context.Context, opts GetFlightOptions) (*Flight, error)

GetFlight loads a single flight by name.

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) HubAdd added in v0.23.0

func (t *Tap) HubAdd(_ context.Context, opts HubAddOptions) error

HubAdd registers a remote hub connection. Hub entries may only live in USER config (the trust boundary strips hubs from project config), so this always writes the user config regardless of the working directory.

func (*Tap) HubList added in v0.23.0

func (t *Tap) HubList(_ context.Context) ([]HubInfo, error)

HubList returns the configured hubs (plus the synthesized built-ins when none are configured), marking the default and the config layer each came from. It inspects local config only — it does not contact any hub.

func (*Tap) HubListKegs added in v0.23.0

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

HubListKegs lists kegs qualified as "@namespace/keg". With no --hub it aggregates across every configured hub: local hubs are scanned on disk at <basePath>/@<namespace>/<keg>; remote/readonly hubs are queried via the hub's GET /api/v1/kegs, which returns the kegs the authenticated user can reach (namespace membership + grants). With an explicit --hub only that hub is listed and its errors surface directly; in aggregate mode an unreachable or unauthenticated hub is logged and skipped so one bad hub doesn't blank the whole listing.

func (*Tap) HubRemove added in v0.23.0

func (t *Tap) HubRemove(_ context.Context, opts HubRemoveOptions) error

HubRemove deletes a hub connection (user config only) and prunes any namespace pin that routed to it.

func (*Tap) HubSetDefault added in v0.23.0

func (t *Tap) HubSetDefault(ctx context.Context, opts HubSetDefaultOptions) error

HubSetDefault sets defaultHub. Unlike the hubs map, defaultHub is allowed in project config, so the default write target is the project config, with --user to write the user config instead.

func (*Tap) Import added in v0.4.0

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

Import loads a keg-archive from a file path or http(s) URL into the resolved keg and returns the imported node ids.

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 named options.Keg and initializes it at the resolved destination. Destination resolution is namespace-centric:

  • --project/--cwd/--path → a project-local filesystem keg under the git root (or cwd / explicit path).
  • otherwise the name resolves through namespace → hub (with --namespace and --hub as overrides, and --user pinning @local): a local hub yields a filesystem keg at <basePath>/@<namespace>/<name>; a remote hub creates the keg on the hub (POST /api/v1/@<namespace>/kegs), failing if it already exists. On success the keg is recorded in user config.

func (*Tap) Integrate added in v0.19.0

func (t *Tap) Integrate(ctx context.Context, opts IntegrateOptions) (*IntegrateResult, error)

Integrate atomically refreshes the embedded marketplace for one host, reuses a matching registration, and installs the requested plugins through the host CLI. Dry-run returns the complete plan without side effects.

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) KegGrant added in v0.23.0

func (t *Tap) KegGrant(ctx context.Context, opts KegGrantOptions) error

KegGrant upserts a grant (user → role) on a keg.

func (*Tap) KegGrants added in v0.23.0

func (t *Tap) KegGrants(ctx context.Context, opts KegGrantsOptions) ([]HubGrant, error)

KegGrants lists the per-(user, role) grants on a keg.

func (*Tap) KegInfo

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

KegInfo displays diagnostics for a resolved keg.

func (*Tap) KegRename added in v0.25.0

func (t *Tap) KegRename(ctx context.Context, opts KegRenameOptions) error

KegRename renames a hub-backed keg alias within its namespace.

func (*Tap) KegRevoke added in v0.23.0

func (t *Tap) KegRevoke(ctx context.Context, opts KegRevokeOptions) error

KegRevoke removes a user's grant on a keg.

func (*Tap) KegVisibility added in v0.23.0

func (t *Tap) KegVisibility(ctx context.Context, opts KegVisibilityOptions) error

KegVisibility sets a keg's visibility to public or private.

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) ListFlights added in v0.23.0

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

ListFlights returns canonical refs discovered across configured hubs, or only opts.Hub when a filter is supplied.

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) ListSchemas added in v0.25.0

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

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) NamespaceAddMember added in v0.23.0

func (t *Tap) NamespaceAddMember(ctx context.Context, opts NamespaceAddMemberOptions) error

NamespaceAddMember upserts a member (user → role) into a namespace.

func (*Tap) NamespaceCreate added in v0.23.0

func (t *Tap) NamespaceCreate(ctx context.Context, opts NamespaceCreateOptions) (*NamespaceCreateResult, error)

NamespaceCreate returns the hub UI URL for creating an org namespace. It does not call the namespace creation API.

func (*Tap) NamespaceList added in v0.23.0

func (t *Tap) NamespaceList(ctx context.Context, opts NamespaceListOptions) ([]HubNamespace, error)

NamespaceList returns the namespaces the caller belongs to on a hub.

func (*Tap) NamespaceMembers added in v0.23.0

func (t *Tap) NamespaceMembers(ctx context.Context, opts NamespaceMembersOptions) ([]HubMember, error)

NamespaceMembers returns the member roster of a namespace.

func (*Tap) NamespaceRemoveMember added in v0.23.0

func (t *Tap) NamespaceRemoveMember(ctx context.Context, opts NamespaceRemoveMemberOptions) error

NamespaceRemoveMember removes a member from a namespace.

func (*Tap) NamespaceSetRole added in v0.23.0

func (t *Tap) NamespaceSetRole(ctx context.Context, opts NamespaceSetRoleOptions) error

NamespaceSetRole changes an existing member's role.

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) NodeSnapshotView added in v0.23.0

func (t *Tap) NodeSnapshotView(ctx context.Context, opts NodeSnapshotViewOptions) ([]byte, error)

func (*Tap) Orient added in v0.19.0

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

Orient returns one deterministic KEG system payload. It is best-effort: active-keg, flight, and hub-listing failures do not suppress the core orientation document.

func (*Tap) ReadImage added in v0.24.0

func (t *Tap) ReadImage(ctx context.Context, opts ReadImageOptions) ([]byte, string, error)

ReadImage retrieves a node image and validates that the stored bytes are still one of Tapper's supported image formats.

func (*Tap) ReadSchema added in v0.25.0

func (t *Tap) ReadSchema(ctx context.Context, opts SchemaOptions) ([]byte, error)

func (*Tap) Remove

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

func (*Tap) ResolveNodeRef added in v0.23.0

func (t *Tap) ResolveNodeRef(ctx context.Context, ref *keg.NodeRef, rc RefContext) (keg.Keg, keg.NodeId, error)

ResolveNodeRef resolves a parsed node reference into the keg that holds it and the numeric node id within that keg:

  • RefLocal: the current keg, with the bare node id.
  • RefAlias: the alias is resolved against the current keg's Links table first (so authored links travel with the keg), then the tap-config kegs map.
  • RefQualified: a (hub, namespace, keg) reference whose hub is implied from the current keg's hub; the reserved @local namespace pins the local hub regardless of the current keg's hub.

func (*Tap) SetBootstrapFlight added in v0.30.0

func (t *Tap) SetBootstrapFlight(ctx context.Context, ref string) error

SetBootstrapFlight validates ref, canonicalizes it, and persists it as the user-level flight baseline. Project config, TAP_FLIGHT, and an explicit --flight flag remain higher-precedence overrides. A blank ref is a no-op.

func (*Tap) SetBootstrapNamespace added in v0.23.0

func (t *Tap) SetBootstrapNamespace(ctx context.Context, hubName, namespace string) error

SetBootstrapNamespace adopts namespace as the named hub's default namespace, then persists the config. It is the post-login step of `tap bootstrap`: once a cloud/enterprise login resolves the authenticated user's home namespace (from the hub's whoami probe), the CLI calls this so plain references resolved against that hub land in the user's own namespace. The namespace lives on the hub entry so each hub carries its own default — there is no global fallbackNamespace and no per-user namespace→hub entry to maintain.

It is idempotent and a no-op when namespace is blank, or when hubName is unknown/blank (nothing to adopt onto), keeping the call safe in either case. The always-present local hub keeps its reserved @local namespace.

func (*Tap) SetFallbackKeg added in v0.23.0

func (t *Tap) SetFallbackKeg(ctx context.Context, ref string) error

SetFallbackKeg sets the user config's fallbackKeg to ref and persists it. It is the post-login step of `tap bootstrap`: once the user picks a keg (from the hub's list or by typing one), plain `tap` commands resolve it without per-invocation flags. It writes the FALLBACK slot (the global-user convention) rather than defaultKeg, so a project's defaultKeg or a kegMap path rule still overrides it. ref is a keg reference — a bare name, @namespace/name, keg:..., or a path — stored verbatim and resolved later by ResolveRef. A blank ref is a no-op.

func (*Tap) SetHubDefaultNamespaceByURL added in v0.23.0

func (t *Tap) SetHubDefaultNamespaceByURL(ctx context.Context, hubURL, namespace string) (string, error)

SetHubDefaultNamespaceByURL adopts namespace onto the configured hub whose URL matches hubURL. It returns the hub name that was updated. When no configured hub matches (for example, auth login fell through to the compiled-in atlas URL without a user config entry), it is a no-op.

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) UpdateFlight added in v0.23.0

func (t *Tap) UpdateFlight(ctx context.Context, opts UpdateFlightOptions) (*Flight, error)

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.

func (*Tap) Use added in v0.23.0

func (t *Tap) Use(ctx context.Context, opts UseOptions) error

Use records the project's keg + flight, or the user-wide fallback keg, in the appropriate config file. It mirrors the resolution convention: project → defaultKeg, user → fallbackKeg, with flight project-scoped.

func (*Tap) UseStatus added in v0.23.0

func (t *Tap) UseStatus(_ context.Context, opts KegTargetOptions) (string, error)

UseStatus returns a YAML summary of the resolved keg/flight context plus the configured keg slots and the scope that set each, for `tap use` with no args.

func (*Tap) Validate added in v0.25.0

func (t *Tap) Validate(ctx context.Context, opts ValidateOptions) ([]keg.SchemaValidationResult, error)

func (*Tap) WatchNode added in v0.23.0

func (t *Tap) WatchNode(ctx context.Context, opts WatchNodeOptions) (<-chan keg.NodeEvent, error)

WatchNode subscribes to live change events for a single node. The watch is scoped to ctx: events are delivered on the returned channel until ctx is cancelled, at which point the channel is closed.

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 UpdateFlightOptions added in v0.23.0

type UpdateFlightOptions struct {
	Ref          string
	Title        *string
	Visibility   *string
	Capabilities *[]FlightCapability
	Instructions *string
	Cover        *[]FlightCover
}

UpdateFlightOptions is a partial update: nil fields keep the flight's current value. The merge against the existing flight happens inside Tap.UpdateFlight against the same resolved ref the PUT targets, so a slug that exists in several places cannot read one flight and overwrite another.

type UploadFileOptions added in v0.2.0

type UploadFileOptions struct {
	KegTargetOptions
	NodeID   string
	FilePath string
	Data     []byte
	Name     string
}

UploadFileOptions configures behavior for Tap.UploadFile.

type UploadImageOptions added in v0.2.0

type UploadImageOptions struct {
	KegTargetOptions
	NodeID   string
	FilePath string
	Data     []byte
	Name     string
}

UploadImageOptions configures behavior for Tap.UploadImage.

type UseOptions added in v0.23.0

type UseOptions struct {
	// Keg is the keg reference to record (a bare name or @namespace/keg). Empty
	// leaves the keg slot untouched (unless Clear is set).
	Keg string
	// Flight is a flight reference to record for the project (@namespace/+slug,
	// +slug, or a bare slug). Empty leaves the flight untouched.
	Flight string
	// User writes the user config's fallbackKeg instead of the project config's
	// defaultKeg.
	User bool
	// ConfigPath, when set, writes that explicit config file instead of the
	// user/project file. The slot still follows User.
	ConfigPath string
	// Clear unsets the slot(s) for the chosen scope (defaultKeg + flight for the
	// project scope; fallbackKeg for the user scope).
	Clear bool
}

UseOptions configures Tap.Use, the `tap use` setter that records which keg (and flight) a project resolves, or a user-wide fallback keg.

Scope picks the keg slot: the default (project) scope writes defaultKeg; the user scope (User=true) writes fallbackKeg. Flight is project-scoped only.

type ValidateOptions added in v0.25.0

type ValidateOptions struct {
	KegTargetOptions
	NodeIDs []string
}

type WatchNodeOptions added in v0.23.0

type WatchNodeOptions struct {
	NodeID string
	KegTargetOptions
}

type WhoAmI added in v0.23.0

type WhoAmI struct {
	UserID           int64     `json:"user_id"`
	Username         string    `json:"username"`
	DisplayName      string    `json:"display_name"`
	Email            string    `json:"email"`
	CreatedAt        time.Time `json:"created_at"`
	DefaultNamespace string    `json:"default_namespace"`
	Namespaces       []string  `json:"namespaces,omitempty"`
}

WhoAmI is the authenticated user the hub reports for a token. It mirrors the hub's GET /api/v1/whoami JSON body (handler.WhoamiResponse) — keep the two structs in sync. DisplayName is the human name and is empty when the hub omits it (older hub, or no display name set), in which case callers fall back to the username alone.

DefaultNamespace is the user's home namespace as the hub sees it (its name equals the username by construction). `tap bootstrap` adopts it as the fallback namespace so the hub, not the client, decides which namespace a fresh login lands in. Namespaces lists every namespace the user belongs to (personal + orgs) and is empty when the hub omits it (older hub).

func ValidateToken added in v0.23.0

func ValidateToken(ctx context.Context, rt *toolkit.Runtime, hubURL, token string) (*WhoAmI, error)

ValidateToken calls GET {hubURL}/api/v1/whoami with the bearer token and returns the resolved user. A 401/403 is reported as a rejected token; any other non-200 surfaces the hub's status so misconfigurations are visible. The default HTTP client is used; callers that need to inject one for tests can hit an httptest.Server, whose URL works with http.DefaultClient.

Jump to

Keyboard shortcuts

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