Documentation
¶
Index ¶
- Constants
- Variables
- func AuthMiddleware(srv *Server) func(http.Handler) http.Handler
- func ExecWith(ctx context.Context, vars map[string]string, argv []string) error
- func Install(ctx context.Context, tool Tool) error
- func Uninstall(name string) error
- type Arg
- type CallerContextKey
- type CallerIdentity
- type ComposeClient
- func (cc *ComposeClient) Build(ctx context.Context, name string) (Job, error)
- func (cc *ComposeClient) Down(ctx context.Context, name string) (Job, error)
- func (cc *ComposeClient) Project(ctx context.Context, name string) (map[string]any, error)
- func (cc *ComposeClient) Projects(ctx context.Context) ([]map[string]any, error)
- func (cc *ComposeClient) Pull(ctx context.Context, name string) (Job, error)
- func (cc *ComposeClient) Restart(ctx context.Context, name string) (Job, error)
- func (cc *ComposeClient) Up(ctx context.Context, name, composefile string) (Job, error)
- type DirEntry
- type DockerClient
- func (dc *DockerClient) Available(ctx context.Context) (bool, error)
- func (dc *DockerClient) Compose() *ComposeClient
- func (dc *DockerClient) Container(ctx context.Context, id string) (map[string]any, error)
- func (dc *DockerClient) Containers(ctx context.Context) ([]map[string]any, error)
- func (dc *DockerClient) Images(ctx context.Context) ([]map[string]any, error)
- func (dc *DockerClient) Logs(ctx context.Context, id string, tail int) (string, error)
- func (dc *DockerClient) Pull(ctx context.Context, ref string) (Job, error)
- func (dc *DockerClient) Remove(ctx context.Context, id string) (Job, error)
- func (dc *DockerClient) Restart(ctx context.Context, id string) (Job, error)
- func (dc *DockerClient) Start(ctx context.Context, id string) (Job, error)
- func (dc *DockerClient) Stats(ctx context.Context, id string) (map[string]any, error)
- func (dc *DockerClient) Stop(ctx context.Context, id string) (Job, error)
- func (dc *DockerClient) Swarm() *SwarmClient
- type FilesClient
- type FleetClient
- type FleetMetricsClient
- func (fm *FleetMetricsClient) All(ctx context.Context) (map[string]map[string]any, map[string]error)
- func (fm *FleetMetricsClient) CPU(ctx context.Context) (map[string]map[string]any, map[string]error)
- func (fm *FleetMetricsClient) Memory(ctx context.Context) (map[string]map[string]any, map[string]error)
- type FleetVarsClient
- type Job
- type JobResult
- type JobStatus
- type MetricsClient
- func (mc *MetricsClient) All(ctx context.Context) (map[string]any, error)
- func (mc *MetricsClient) Available(ctx context.Context) (bool, error)
- func (mc *MetricsClient) CPU(ctx context.Context) (map[string]any, error)
- func (mc *MetricsClient) Disk(ctx context.Context) ([]map[string]any, error)
- func (mc *MetricsClient) Host(ctx context.Context) (map[string]any, error)
- func (mc *MetricsClient) Memory(ctx context.Context) (map[string]any, error)
- func (mc *MetricsClient) Network(ctx context.Context) ([]map[string]any, error)
- func (mc *MetricsClient) Processes(ctx context.Context) ([]map[string]any, error)
- type NodeClient
- func (n *NodeClient) Docker() *DockerClient
- func (n *NodeClient) ExecJob(ctx context.Context, jobID string) (JobResult, error)
- func (n *NodeClient) ExecWait(ctx context.Context, jobID string) (JobResult, error)
- func (n *NodeClient) Files() *FilesClient
- func (n *NodeClient) HasTool(ctx context.Context, name, minVersion string) (bool, error)
- func (n *NodeClient) Metrics() *MetricsClient
- func (n *NodeClient) Send(ctx context.Context, req SendRequest) (SendResult, error)
- func (n *NodeClient) SendDir(ctx context.Context, req SendDirRequest) ([]SendResult, error)
- func (n *NodeClient) Systemd() *SystemdClient
- func (n *NodeClient) Tools(ctx context.Context) ([]Tool, error)
- func (n *NodeClient) Vars(project, env string) *VarsClient
- type NodeInfo
- type SendDirRequest
- type SendRequest
- type SendResult
- type Server
- type ServerConfig
- type SwarmClient
- type SystemdClient
- func (sc *SystemdClient) Available(ctx context.Context) (bool, error)
- func (sc *SystemdClient) Disable(ctx context.Context, unit string) (Job, error)
- func (sc *SystemdClient) Enable(ctx context.Context, unit string) (Job, error)
- func (sc *SystemdClient) Journal(ctx context.Context, unit string, lines int) ([]map[string]any, error)
- func (sc *SystemdClient) Reload(ctx context.Context, unit string) (Job, error)
- func (sc *SystemdClient) Restart(ctx context.Context, unit string) (Job, error)
- func (sc *SystemdClient) Start(ctx context.Context, unit string) (Job, error)
- func (sc *SystemdClient) Stop(ctx context.Context, unit string) (Job, error)
- func (sc *SystemdClient) SystemJournal(ctx context.Context, lines int) ([]map[string]any, error)
- func (sc *SystemdClient) Unit(ctx context.Context, unit string) (map[string]any, error)
- func (sc *SystemdClient) UnitFile(ctx context.Context, unit string) (string, error)
- func (sc *SystemdClient) Units(ctx context.Context) ([]map[string]any, error)
- type Tool
- type VarsClient
- func (vc *VarsClient) Delete(ctx context.Context, key string) error
- func (vc *VarsClient) Env(ctx context.Context) (string, error)
- func (vc *VarsClient) Get(ctx context.Context, key string) (string, error)
- func (vc *VarsClient) List(ctx context.Context) (map[string]string, error)
- func (vc *VarsClient) Set(ctx context.Context, key, value string) error
Constants ¶
const ( // PatternIdentifier matches container names, service names, etc. PatternIdentifier = `^[a-zA-Z0-9_-]+$` // PatternPath matches absolute unix paths. PatternPath = `^(/[a-zA-Z0-9_./-]+)+$` // PatternSemver matches semantic version strings. PatternSemver = `^v?[0-9]+\.[0-9]+\.[0-9]+$` // PatternIP matches IPv4 addresses. PatternIP = `^(\d{1,3}\.){3}\d{1,3}$` // PatternPort matches valid port numbers. PatternPort = `^([1-9][0-9]{0,4})$` // PatternFilename matches safe filenames. PatternFilename = `^[a-zA-Z0-9_.,-]+$` )
Variables ¶
var ( ErrReceiveNotConfigured = errors.New("tailkit: node has no files.toml (receive not configured)") ErrToolNotFound = errors.New("tailkit: tool not installed on node") ErrCommandNotFound = errors.New("tailkit: command not registered by tool") ErrVarScopeNotFound = errors.New("tailkit: project/env scope not in vars.toml") ErrPermissionDenied = errors.New("tailkit: ACL cap or node config blocked the operation") )
Functions ¶
func AuthMiddleware ¶
AuthMiddleware authenticates every inbound request via Tailscale's WhoIs API.
func ExecWith ¶
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 Install ¶
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.
Types ¶
type Arg ¶
type Arg struct {
// Name is the template variable name used in ExecParts (e.g. "container").
Name string `json:"name"`
// Type is the value type: "string", "int", "bool".
Type string `json:"type"`
// Required indicates the arg must be supplied by the caller.
Required bool `json:"required"`
// Pattern is a regex the value must match before substitution.
// Use the PatternXxx constants or a custom expression.
Pattern string `json:"pattern,omitempty"`
}
Arg describes a single parameter accepted by a Command.
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.
type DirEntry ¶
type DirEntry struct {
Name string `json:"name"`
Size int64 `json:"size"`
IsDir bool `json:"is_dir"`
ModTime time.Time `json:"mod_time"`
Mode string `json:"mode"`
}
DirEntry is a single entry in a directory listing.
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) Containers ¶
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) Download ¶
func (fc *FilesClient) Download(ctx context.Context, remotePath, localPath string) error
Download fetches a file from the node and writes it to localPath.
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 AllNodes ¶
func AllNodes(srv *Server) *FleetClient
AllNodes returns a FleetClient that discovers all online tailkitd peers and fans out requests to them with bounded parallelism (10 concurrent).
func (*FleetClient) Metrics ¶
func (f *FleetClient) Metrics() *FleetMetricsClient
func (*FleetClient) Vars ¶
func (f *FleetClient) Vars(project, env string) *FleetVarsClient
type FleetMetricsClient ¶
type FleetMetricsClient struct {
// contains filtered or unexported fields
}
FleetMetricsClient fans out metrics requests to all nodes.
type FleetVarsClient ¶
type FleetVarsClient struct {
// contains filtered or unexported fields
}
FleetVarsClient fans out var operations across all nodes.
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.
type Job ¶
type Job struct {
// JobID is the opaque identifier used to poll for results.
JobID string `json:"job_id"`
Status JobStatus `json:"status"`
}
Job is the immediate response from a fire-and-forget exec invocation.
type JobResult ¶
type JobResult struct {
JobID string `json:"job_id"`
Status JobStatus `json:"status"`
ExitCode int `json:"exit_code"`
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
DurationMs int64 `json:"duration_ms"`
// Error is set when the job failed to start (not when the command exits non-zero).
Error string `json:"error,omitempty"`
}
JobResult is returned when polling a completed job.
type MetricsClient ¶
type MetricsClient struct {
// contains filtered or unexported fields
}
MetricsClient provides typed access to the /integrations/metrics endpoints.
func (*MetricsClient) Available ¶
func (mc *MetricsClient) Available(ctx context.Context) (bool, 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) ExecWait ¶
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 ¶
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) Send ¶
func (n *NodeClient) Send(ctx context.Context, req SendRequest) (SendResult, error)
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 (*NodeClient) SendDir ¶
func (n *NodeClient) SendDir(ctx context.Context, req SendDirRequest) ([]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 (*NodeClient) Systemd ¶
func (n *NodeClient) Systemd() *SystemdClient
func (*NodeClient) Tools ¶
func (n *NodeClient) Tools(ctx context.Context) ([]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 NodeInfo ¶
type NodeInfo struct {
// Name is the Tailscale hostname of the node.
Name string
// TailscaleIP is the node's Tailscale IP address (100.x.x.x).
TailscaleIP string
// Tool is the matching Tool entry found on the node.
Tool Tool
}
NodeInfo is returned by Discover — it identifies a tailnet peer that has a specific tool installed.
type SendDirRequest ¶
type SendDirRequest struct {
// ToolName is the name of the tool that sent the directory.
ToolName string `json:"tool_name"`
// LocalDir is the absolute path to the source directory on the caller's machine.
LocalDir string
// DestPath is the absolute destination directory path on the node.
DestPath string
}
SendDirRequest describes a directory tree to push to a remote node.
type SendRequest ¶
type SendRequest struct {
// ToolName is the name of the tool that sent the file.
ToolName string `json:"tool_name"`
// LocalPath is the absolute path to the file on the caller's machine.
LocalPath string
// DestPath is the absolute path the file should be written to on the node.
DestPath string
}
SendRequest describes a single file to push to a remote node.
type SendResult ¶
type SendResult struct {
// ToolName is the name of the tool that sent the file.
ToolName string `json:"tool_name"`
LocalPath string `json:"local_path"`
// Success indicates whether the file was successfully sent.
Success bool `json:"success"`
// WrittenTo is the absolute path the file was written to on the node.
WrittenTo string `json:"written_to"`
// BytesWritten is the number of bytes written.
BytesWritten int64 `json:"bytes_written"`
// DestMachine is the hostname of the machine the file was sent to.
DestMachine string `json:"dest_machine"`
//Error is set when the file was not successfully sent.
Error string `json:"error,omitempty"`
}
SendResult is the response from a Send or SendDir operation.
func Broadcast ¶
func Broadcast(ctx context.Context, srv *Server, req SendRequest) ([]SendResult, map[string]error)
Broadcast pushes a file to all online nodes concurrently. Each node that has a matching write rule receives the file. Nodes that are offline or have no matching write rule are skipped — their errors are collected and returned, not propagated.
type 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 ¶
ListenAndServe starts a plain HTTP server on the tsnet listener.
func (*Server) ListenAndServeTLS ¶
ListenAndServeTLS starts an HTTPS server on the tsnet listener.
type ServerConfig ¶
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.
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) SystemJournal ¶
type Tool ¶
type Tool struct {
// Name is a unique identifier for the tool across the tailnet.
Name string `json:"name"`
// Version is the tool's current version string (semver recommended).
Version string `json:"version"`
// TsnetHost is the tsnet hostname this tool registers on the tailnet.
TsnetHost string `json:"tsnet_host"`
}
Tool is the registration record written to /etc/tailkitd/tools/{name}.json by tailkit.Install and read by tailkitd to populate its tool registry.
type VarsClient ¶
type VarsClient struct {
// contains filtered or unexported fields
}
VarsClient provides typed access to the /vars endpoints on a node.
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.