tailkit

package module
v0.3.3 Latest Latest
Warning

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

Go to latest
Published: Apr 5, 2026 License: MIT Imports: 29 Imported by: 0

README

tailkit

Go library for building Tailscale-native tools. tailkit has two distinct concerns:

  1. tsnet utilities — useful for any tailnet tool regardless of tailkitd
  2. tailkitd client SDK — typed HTTP client for every tailkitd endpoint

Tools built with tailkit get consistent auth, peer discovery, and access to node-level integrations (Docker, systemd, metrics, files, vars, exec) across every node running tailkitd.


Install

go get github.com/wf-pro-dev/tailkit

Quick start

srv, err := tailkit.NewServer(tailkit.ServerConfig{
    Hostname: "devbox",
    AuthKey:  os.Getenv("TS_AUTHKEY"),
})
defer srv.Close()

// register this tool with tailkitd on startup
tailkit.Install(ctx, types.Tool{Name: "devbox", Version: build.Version, TsnetHost: "devbox"})

// single node
containers, err := tailkit.Node(srv, "vps-1").Docker().Containers(ctx)

// fleet
peers, err := tailkit.OnlinePeers(ctx, srv)
cpuByNode, errs := tailkit.Nodes(srv, peers).Metrics().CPU(ctx)

Docs

Document Description
server.md NewServer, ServerConfig, TLS helpers, AuthMiddleware
node.md Node, Tools, Files, Vars, Docker, Systemd, Metrics
fleet.md Nodes, Discover, Broadcast, peer discovery primitives
errors.md Typed errors and how to check them

## License

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	Peers map[key.NodePublic]*ipnstate.PeerStatus

	TTL = 15 * time.Minute
)

Functions

func AllPeers added in v0.1.12

func AllPeers(ctx context.Context, srv *Server) ([]types.Peer, error)

func AuthMiddleware

func AuthMiddleware(srv *Server) func(http.Handler) http.Handler

AuthMiddleware authenticates every inbound request via Tailscale's WhoIs API.

func Discover

func Discover(ctx context.Context, srv *Server, toolName string) ([]types.Peer, error)

Discover finds all online tailnet peers that have the named tool installed. An empty minVersion matches any version.

func DiscoverVersion

func DiscoverVersion(ctx context.Context, srv *Server, toolName, minVersion string) ([]types.Peer, error)

DiscoverVersion is like Discover but requires at least minVersion.

func ExecWith

func ExecWith(ctx context.Context, vars map[string]string, argv []string) error

ExecWith injects vars into the environment of a local subprocess and runs it. Vars are set as KEY=VALUE environment variables. The subprocess inherits the current process's environment with the vars overlaid on top.

Secrets exist only in the child process environment and disappear when it exits — they are never written to disk.

Example:

vars, err := tailkit.Node(srv, "vps-1").Vars("myapp", "prod").List(ctx)
err = tailkit.ExecWith(ctx, vars, []string{"/usr/bin/node", "server.js"})

func GetConfig added in v0.3.0

func GetConfig(n *NodeClient, ctx context.Context, integration string, config any) error

func GetPeerTools added in v0.1.12

func GetPeerTools(ctx context.Context, srv *Server, tailscaleIP string) ([]types.Tool, error)

func GetPeers added in v0.1.12

func GetPeers(ctx context.Context, srv *Server) (map[key.NodePublic]*ipnstate.PeerStatus, error)

func GetTailkitHostname added in v0.2.2

func GetTailkitHostname(hostname string) string

func GetTailkitPeer added in v0.1.12

func GetTailkitPeer(ctx context.Context, srv *Server, hostname string) (*types.TailkitPeer, error)

func Install

func Install(ctx context.Context, tool types.Tool) error

Install writes a Tool registration file to /etc/tailkitd/tools/{name}.json.

Call Install once at install time and again on every tool upgrade. tailkitd reads this file to populate its tool registry and exec command list. The write is atomic — tailkitd will never read a partially-written file.

Install validates:

  • Tool.Name is non-empty and matches [a-zA-Z0-9_-]+
  • Tool.Version is non-empty
  • Each Command.Name is non-empty
  • Each Command.ExecParts is non-empty and ExecParts[0] exists on disk
  • Each Command.Timeout is positive
  • Each Arg.Pattern (if set) is a valid regular expression

It creates /etc/tailkitd/tools/ if it does not exist.

func OnlinePeers added in v0.1.12

func OnlinePeers(ctx context.Context, srv *Server) ([]types.Peer, error)

OnlinePeers returns all online tailnet peers running tailkitd (hostname starts with "tailkitd-"), querying via the system Tailscale daemon.

func TailkitPeers added in v0.1.12

func TailkitPeers(ctx context.Context, srv *Server) ([]types.TailkitPeer, error)

func Uninstall

func Uninstall(name string) error

Uninstall removes the tool registration file for the named tool.

If the file does not exist, Uninstall returns nil — it is safe to call Uninstall when the tool may or may not be installed.

Types

type CallerContextKey

type CallerContextKey struct{}

CallerContextKey is the exported context key type for CallerIdentity.

type CallerIdentity

type CallerIdentity struct {
	Hostname    string
	TailscaleIP string
	UserLogin   string
	Caps        map[string]bool
}

CallerIdentity holds the verified identity of the caller on an inbound request.

func CallerFromContext

func CallerFromContext(ctx context.Context) (CallerIdentity, bool)

CallerFromContext retrieves the CallerIdentity injected by AuthMiddleware.

func (CallerIdentity) HasCap

func (id CallerIdentity) HasCap(cap string) bool

HasCap reports whether the caller was granted the given ACL capability.

type ComposeClient

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

ComposeClient provides access to Docker Compose operations.

func (*ComposeClient) Build

func (cc *ComposeClient) Build(ctx context.Context, name string) (types.Job, error)

func (*ComposeClient) Down

func (cc *ComposeClient) Down(ctx context.Context, name string) (types.Job, error)

func (*ComposeClient) Project

func (cc *ComposeClient) Project(ctx context.Context, name string) (types.ComposeService, error)

func (*ComposeClient) Projects

func (cc *ComposeClient) Projects(ctx context.Context) ([]types.ComposeService, error)

func (*ComposeClient) Pull

func (cc *ComposeClient) Pull(ctx context.Context, name string) (types.Job, error)

func (*ComposeClient) Restart

func (cc *ComposeClient) Restart(ctx context.Context, name string) (types.Job, error)

func (*ComposeClient) Up

func (cc *ComposeClient) Up(ctx context.Context, name, composefile string) (types.Job, error)

type DockerClient

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

DockerClient provides typed access to the /integrations/docker endpoints.

func (*DockerClient) Available

func (dc *DockerClient) Available(ctx context.Context) (bool, error)

Available returns false if Docker is not configured or the daemon is down. Never returns a Go error — callers can use it as a boolean check.

func (*DockerClient) Compose

func (dc *DockerClient) Compose() *ComposeClient

func (*DockerClient) Config added in v0.3.0

func (*DockerClient) Container

func (dc *DockerClient) Container(ctx context.Context, id string) (container.InspectResponse, error)

func (*DockerClient) Containers

func (dc *DockerClient) Containers(ctx context.Context) ([]container.Summary, error)

func (*DockerClient) Images

func (dc *DockerClient) Images(ctx context.Context) ([]image.Summary, error)

func (*DockerClient) Logs

func (dc *DockerClient) Logs(ctx context.Context, id string, tail int) (string, error)

func (*DockerClient) Pull

func (dc *DockerClient) Pull(ctx context.Context, ref string) (types.Job, error)

func (*DockerClient) Remove

func (dc *DockerClient) Remove(ctx context.Context, id string) (types.Job, error)

func (*DockerClient) Restart

func (dc *DockerClient) Restart(ctx context.Context, id string) (types.Job, error)

func (*DockerClient) Start

func (dc *DockerClient) Start(ctx context.Context, id string) (types.Job, error)

func (*DockerClient) Stop

func (dc *DockerClient) Stop(ctx context.Context, id string) (types.Job, error)

func (*DockerClient) Swarm

func (dc *DockerClient) Swarm() *SwarmClient

type FilesClient

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

FilesClient provides typed access to the /files endpoints on a node. Obtain via NodeClient.Files().

func (*FilesClient) Config added in v0.3.0

func (*FilesClient) Download

func (fc *FilesClient) Download(ctx context.Context, remotePath, localPath string) error

Download fetches a file from the node and writes it to localPath.

func (*FilesClient) List

func (fc *FilesClient) List(ctx context.Context, dirPath string) ([]types.DirEntry, error)

List returns the directory listing for path on the node.

func (*FilesClient) Read

func (fc *FilesClient) Read(ctx context.Context, path string) (string, error)

Read returns the content of a file on the node as a string.

func (*FilesClient) Send added in v0.3.0

Send pushes a local file to the node. Returns a SendResult; if a post_recv hook was triggered, SendResult.JobID is set and can be polled with ExecJob.

func (*FilesClient) SendDir added in v0.3.0

func (fc *FilesClient) SendDir(ctx context.Context, req types.SendDirRequest) ([]types.SendResult, error)

SendDir pushes all files in a local directory to the node recursively. Returns one SendResult per file; errors are collected, not propagated.

func (*FilesClient) Stat added in v0.3.0

func (fc *FilesClient) Stat(ctx context.Context, path string) (types.FileStat, error)

type FleetClient

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

FleetClient fans out operations to all online tailkitd nodes. Obtain via tailkit.AllNodes(srv).

func Nodes added in v0.1.12

func Nodes(srv *Server, peers []types.Peer) *FleetClient

Nodes returns a FleetClient that fans out requests to the given peers. fans out requests to them with bounded parallelism (10 concurrent).

func (*FleetClient) Files added in v0.3.0

func (f *FleetClient) Files() *FleetFilesClient

func (*FleetClient) Metrics

func (f *FleetClient) Metrics() *FleetMetricsClient

func (*FleetClient) Vars

func (f *FleetClient) Vars(project, env string) *FleetVarsClient

type FleetFilesClient added in v0.3.0

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

FleetFilesClient fans out files requests to all nodes.

func (*FleetFilesClient) Config added in v0.3.0

func (*FleetFilesClient) Download added in v0.3.0

func (ff *FleetFilesClient) Download(ctx context.Context, path string, localPath string) (map[string]string, map[string]error)

func (*FleetFilesClient) List added in v0.3.0

func (ff *FleetFilesClient) List(ctx context.Context, path string) (map[string][]types.DirEntry, map[string]error)

func (*FleetFilesClient) Read added in v0.3.0

func (ff *FleetFilesClient) Read(ctx context.Context, path string) (map[string]string, map[string]error)

func (*FleetFilesClient) Send added in v0.3.0

func (*FleetFilesClient) SendDir added in v0.3.0

func (*FleetFilesClient) Stat added in v0.3.0

func (ff *FleetFilesClient) Stat(ctx context.Context, path string) (map[string]types.FileStat, map[string]error)

type FleetMetricsClient

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

FleetMetricsClient fans out metrics requests to all nodes.

func (*FleetMetricsClient) All

func (fm *FleetMetricsClient) All(ctx context.Context) (map[string]map[string]any, map[string]error)

func (*FleetMetricsClient) CPU

func (fm *FleetMetricsClient) CPU(ctx context.Context) (map[string]map[string]any, map[string]error)

func (*FleetMetricsClient) Config added in v0.3.0

func (*FleetMetricsClient) Memory

func (fm *FleetMetricsClient) Memory(ctx context.Context) (map[string]map[string]any, map[string]error)

type FleetVarsClient

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

FleetVarsClient fans out var operations across all nodes.

func (*FleetVarsClient) Config added in v0.3.0

func (*FleetVarsClient) List

func (fv *FleetVarsClient) List(ctx context.Context) (map[string]map[string]string, map[string]error)

List reads the scope from every node. Nodes where the scope is not configured return ErrVarScopeNotFound in the error map.

func (*FleetVarsClient) Set

func (fv *FleetVarsClient) Set(ctx context.Context, key, value string) map[string]error

Set writes a var to the given scope on every node that has it configured. Errors are collected per-node and returned together — one node failing does not prevent writes to other nodes.

type MetricsClient

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

MetricsClient provides typed access to the /integrations/metrics endpoints.

func (*MetricsClient) All

func (mc *MetricsClient) All(ctx context.Context) (map[string]any, error)

func (*MetricsClient) Available

func (mc *MetricsClient) Available(ctx context.Context) (bool, error)

func (*MetricsClient) CPU

func (mc *MetricsClient) CPU(ctx context.Context) (map[string]any, error)

func (*MetricsClient) Config added in v0.3.0

func (*MetricsClient) Disk

func (mc *MetricsClient) Disk(ctx context.Context) ([]map[string]any, error)

func (*MetricsClient) Host

func (mc *MetricsClient) Host(ctx context.Context) (map[string]any, error)

func (*MetricsClient) Memory

func (mc *MetricsClient) Memory(ctx context.Context) (map[string]any, error)

func (*MetricsClient) Network

func (mc *MetricsClient) Network(ctx context.Context) ([]map[string]any, error)

func (*MetricsClient) Processes

func (mc *MetricsClient) Processes(ctx context.Context) ([]map[string]any, error)

type NodeClient

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

NodeClient is the entry point for all operations on a single tailkitd node. Obtain one via tailkit.Node(srv, "hostname").

func Node

func Node(srv *Server, hostname string) *NodeClient

Node returns a NodeClient that communicates with the tailkitd instance running on the named node. The hostname is the node's Tailscale hostname (e.g. "warehouse-13-1") — tailkit prepends "tailkitd-" to form the tsnet hostname "tailkitd-warehouse-13-1.<tailnet>.ts.net".

Node construction is free — no network calls are made until a method is called on the returned client or one of its sub-clients.

func (*NodeClient) Docker

func (n *NodeClient) Docker() *DockerClient

func (*NodeClient) ExecJob

func (n *NodeClient) ExecJob(ctx context.Context, jobID string) (types.JobResult, error)

ExecJob polls for the result of a previously submitted job.

func (*NodeClient) ExecWait

func (n *NodeClient) ExecWait(ctx context.Context, jobID string) (types.JobResult, error)

ExecWait fires a command and blocks until it completes or ctx is cancelled. Cancelling ctx stops polling but does not cancel the running job on the node.

func (*NodeClient) Files

func (n *NodeClient) Files() *FilesClient

Files returns a FilesClient for this node.

func (*NodeClient) HasTool

func (n *NodeClient) HasTool(ctx context.Context, name string, minVersion string) (bool, error)

HasTool reports whether the node has a specific tool installed at or above the given minimum version. An empty minVersion matches any version.

func (*NodeClient) Metrics

func (n *NodeClient) Metrics() *MetricsClient

func (*NodeClient) Systemd

func (n *NodeClient) Systemd() *SystemdClient

func (*NodeClient) Tools

func (n *NodeClient) Tools(ctx context.Context) ([]types.Tool, error)

Tools returns all tools registered on the node.

func (*NodeClient) Vars

func (n *NodeClient) Vars(project, env string) *VarsClient

Vars returns a VarsClient scoped to project/env.

type Server

type Server struct {
	*tsnet.Server
}

Server is a tailkit-managed tsnet server.

func NewServer

func NewServer(cfg ServerConfig) (*Server, error)

NewServer constructs and starts a tsnet server.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(addr string, handler http.Handler) error

ListenAndServe starts a plain HTTP server on the tsnet listener.

func (*Server) ListenAndServeTLS

func (s *Server) ListenAndServeTLS(addr string, handler http.Handler) error

ListenAndServeTLS starts an HTTPS server on the tsnet listener.

func (*Server) TLSConfig

func (s *Server) TLSConfig() *tls.Config

TLSConfig returns a *tls.Config using Tailscale-issued certificates.

type ServerConfig

type ServerConfig struct {
	Hostname  string
	AuthKey   string
	StateDir  string
	Ephemeral bool
}

ServerConfig holds configuration for a tailkit-managed tsnet server.

type SwarmClient

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

SwarmClient provides access to Docker Swarm read operations.

func (*SwarmClient) Nodes

func (sc *SwarmClient) Nodes(ctx context.Context) ([]swarm.Node, error)

func (*SwarmClient) Services

func (sc *SwarmClient) Services(ctx context.Context) ([]swarm.Service, error)

type SystemdClient

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

SystemdClient provides typed access to the /integrations/systemd endpoints.

func (*SystemdClient) Available

func (sc *SystemdClient) Available(ctx context.Context) (bool, error)

func (*SystemdClient) Config added in v0.3.0

func (*SystemdClient) Disable

func (sc *SystemdClient) Disable(ctx context.Context, unit string) (types.Job, error)

func (*SystemdClient) Enable

func (sc *SystemdClient) Enable(ctx context.Context, unit string) (types.Job, error)

func (*SystemdClient) Journal

func (sc *SystemdClient) Journal(ctx context.Context, unit string, lines int) ([]map[string]any, error)

func (*SystemdClient) Reload

func (sc *SystemdClient) Reload(ctx context.Context, unit string) (types.Job, error)

func (*SystemdClient) Restart

func (sc *SystemdClient) Restart(ctx context.Context, unit string) (types.Job, error)

func (*SystemdClient) Start

func (sc *SystemdClient) Start(ctx context.Context, unit string) (types.Job, error)

func (*SystemdClient) Stop

func (sc *SystemdClient) Stop(ctx context.Context, unit string) (types.Job, error)

func (*SystemdClient) SystemJournal

func (sc *SystemdClient) SystemJournal(ctx context.Context, lines int) ([]map[string]any, error)

func (*SystemdClient) Unit

func (sc *SystemdClient) Unit(ctx context.Context, unit string) (map[string]any, error)

func (*SystemdClient) UnitFile

func (sc *SystemdClient) UnitFile(ctx context.Context, unit string) (string, error)

func (*SystemdClient) Units

func (sc *SystemdClient) Units(ctx context.Context) ([]dbus.UnitStatus, error)

type VarsClient

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

VarsClient provides typed access to the /vars endpoints on a node.

func (*VarsClient) Config added in v0.3.0

func (*VarsClient) Delete

func (vc *VarsClient) Delete(ctx context.Context, key string) error

Delete removes a var from the scope.

func (*VarsClient) Env

func (vc *VarsClient) Env(ctx context.Context) (string, error)

Env returns all vars rendered as sorted KEY=VALUE lines suitable for sourcing in a shell script or writing to a .env file.

func (*VarsClient) Get

func (vc *VarsClient) Get(ctx context.Context, key string) (string, error)

Get returns the value of a single var.

func (*VarsClient) List

func (vc *VarsClient) List(ctx context.Context) (map[string]string, error)

List returns all vars in the scope as a map.

func (*VarsClient) Set

func (vc *VarsClient) Set(ctx context.Context, key, value string) error

Set writes a var to the scope.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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