jsonoutput

package
v1.103.0-pre Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

Documentation

Overview

Package jsonoutput provides stable and versioned JSON serialisation for CLI output. This allows us to provide stable output to scripts/clients, but also make breaking changes to the output when it's useful.

Historically we only used a boolean -json flag, so changing the output could break scripts that rely on the existing format.

This package provides a SchemaVersion flag type that allows callers to pass either a boolean or a version number and get a consistent output. We'll bump the version when we make a breaking change that's likely to break scripts that rely on the existing output, e.g. if we remove a field or change the type/format. Passing just the boolean flag will always return 1, to preserve compatibility with scripts written before we versioned our output.

This package provides a Format flag type that allows callers to specify which output format the command should print. This flag provides Format.JSONBool and Format.JSONSchemaVersion methods to support combining both -format=json and -format=json-line options with either boolean or versioned -json flags.

This package also provides ResponseEnvelope which is used to provide the set of fields common to all versioned JSON output.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func PrintTailnetLockLogJSONV1 added in v1.102.0

func PrintTailnetLockLogJSONV1(out io.Writer, updates []ipnstate.TailnetLockUpdate) error

PrintTailnetLockLogJSONV1 prints the stored TKA state as a JSON object to the CLI, in a stable "v1" format.

This format includes:

  • the AUM hash as a base32-encoded string
  • the raw AUM as base64-encoded bytes
  • the expanded AUM, which prints named fields for consumption by other tools

func PrintTailnetLockStatusJSONV1 added in v1.102.0

func PrintTailnetLockStatusJSONV1(out io.Writer, status *ipnstate.TailnetLockStatus) error

PrintTailnetLockStatusJSONV1 prints the current Tailnet Lock status as a JSON object to the CLI, in a stable "v1" format.

Types

type DNSAnswer added in v1.96.0

type DNSAnswer struct {
	Name  string
	TTL   uint32
	Class string // e.g. "ClassINET"
	Type  string // e.g. "TypeA", "TypeAAAA"
	Body  string // human-readable record data
}

DNSAnswer is a single DNS resource record from a query response.

type DNSExtraRecord added in v1.96.0

type DNSExtraRecord struct {
	Name  string
	Type  string `json:",omitempty"` // empty means A or AAAA, depending on Value
	Value string // typically an IP address
}

DNSExtraRecord is the JSON form of tailscale.com/tailcfg.DNSRecord.

type DNSQueryResult added in v1.96.0

type DNSQueryResult struct {
	Name         string
	QueryType    string            // e.g. "A", "AAAA"
	Resolvers    []DNSResolverInfo `json:",omitzero"`
	ResponseCode string            // e.g. "RCodeSuccess", "RCodeNameError"
	Answers      []DNSAnswer       `json:",omitzero"`
}

DNSQueryResult is the result of a DNS query via the Tailscale internal forwarder (100.100.100.100). It is the output of:

$ tailscale dns query --json NAME
Example
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"

	"tailscale.com/cmd/tailscale/cli/jsonoutput"
)

func main() {
	cmd := exec.Command("tailscale", "dns", "query", "--json", "hello.ts.net")
	out, err := cmd.Output()
	if err != nil {
		if err, ok := errors.AsType[*exec.ExitError](err); ok {
			fmt.Fprintf(os.Stderr, "%s", err.Stderr)
		}
		panic(err)
	}

	var dnsQuery jsonoutput.DNSQueryResult
	if err := json.Unmarshal(out, &dnsQuery); err != nil {
		panic(err)
	}
	fmt.Printf("{type: %s, name: %q}\n", dnsQuery.QueryType, dnsQuery.Name)
}

type DNSResolverInfo added in v1.96.0

type DNSResolverInfo struct {
	// Addr is a plain IP, IP:port, DoH URL, or HTTP-over-WireGuard URL.
	Addr string

	// BootstrapResolution is optional pre-resolved IPs for DoT/DoH
	// resolvers whose address is not already an IP.
	BootstrapResolution []string `json:",omitempty"`
}

DNSResolverInfo is the JSON form of tailscale.com/types/dnstype.Resolver.

type DNSStatusResult added in v1.96.0

type DNSStatusResult struct {
	// TailscaleDNS is whether the Tailscale DNS configuration is
	// installed on this device (the --accept-dns setting).
	TailscaleDNS bool

	// CurrentTailnet describes MagicDNS configuration for the tailnet.
	CurrentTailnet *DNSTailnetInfo `json:",omitzero"` // nil if not connected

	// Resolvers are the DNS resolvers, in preference order. If
	// empty, the system defaults are used.
	Resolvers []DNSResolverInfo `json:",omitzero"`

	// SplitDNSRoutes maps domain suffixes to dedicated resolvers.
	// An empty resolver slice means the suffix is handled by
	// Tailscale's built-in resolver (100.100.100.100).
	SplitDNSRoutes map[string][]DNSResolverInfo `json:",omitzero"`

	// FallbackResolvers are like Resolvers but only used when
	// split DNS needs explicit default resolvers.
	FallbackResolvers []DNSResolverInfo `json:",omitzero"`

	SearchDomains []string `json:",omitzero"`

	// Nameservers are nameserver IPs.
	//
	// Deprecated: old protocol versions only. Use Resolvers.
	Nameservers []string `json:",omitzero"`

	// CertDomains are FQDNs for which the coordination server
	// provisions TLS certificates via dns-01 ACME challenges.
	CertDomains []string `json:",omitzero"`

	// ExtraRecords contains extra DNS records in the MagicDNS config.
	ExtraRecords []DNSExtraRecord `json:",omitzero"`

	// ExitNodeFilteredSet are DNS suffixes this node won't resolve
	// when acting as an exit node DNS proxy. Period-prefixed
	// entries are suffix matches; others are exact. Always
	// lowercase, no trailing dots.
	ExitNodeFilteredSet []string `json:",omitzero"`

	SystemDNS      *DNSSystemConfig `json:",omitzero"` // nil if unavailable
	SystemDNSError string           `json:",omitempty"`
}

DNSStatusResult is the full DNS status collected from the local Tailscale daemon. It is the output of:

$ tailscale dns status --json
Example
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"

	"tailscale.com/cmd/tailscale/cli/jsonoutput"
)

func main() {
	cmd := exec.Command("tailscale", "dns", "status", "--json")
	out, err := cmd.Output()
	if err != nil {
		if err, ok := errors.AsType[*exec.ExitError](err); ok {
			fmt.Fprintf(os.Stderr, "%s", err.Stderr)
		}
		panic(err)
	}

	var dnsStatus jsonoutput.DNSStatusResult
	if err := json.Unmarshal(out, &dnsStatus); err != nil {
		panic(err)
	}
	fmt.Printf("{accept-dns: %t, resolvers: %q}\n", dnsStatus.TailscaleDNS, dnsStatus.Resolvers)
}

type DNSSystemConfig added in v1.96.0

type DNSSystemConfig struct {
	Nameservers   []string `json:",omitzero"`
	SearchDomains []string `json:",omitzero"`

	// MatchDomains are DNS suffixes restricting which queries use
	// these Nameservers. Empty means Nameservers is the primary
	// resolver.
	MatchDomains []string `json:",omitzero"`
}

DNSSystemConfig is the OS DNS configuration as observed by Tailscale, mirroring tailscale.com/net/dns.OSConfig.

type DNSTailnetInfo added in v1.96.0

type DNSTailnetInfo struct {
	// MagicDNSEnabled is whether MagicDNS is enabled for the
	// tailnet. The device may still not use it if
	// --accept-dns=false.
	MagicDNSEnabled bool

	// MagicDNSSuffix is the tailnet's MagicDNS suffix
	// (e.g. "tail1234.ts.net"), without surrounding dots.
	MagicDNSSuffix string `json:",omitempty"`

	// SelfDNSName is this device's FQDN
	// (e.g. "host.tail1234.ts.net."), with trailing dot.
	SelfDNSName string `json:",omitempty"`
}

DNSTailnetInfo describes MagicDNS configuration for the tailnet, combining tailscale.com/ipn/ipnstate.TailnetStatus and tailscale.com/ipn/ipnstate.PeerStatus.

type Format added in v1.102.0

type Format struct {
	SchemaVersion
	// contains filtered or unexported fields
}

Format implements the flag.Value interface, supporting a combination of both -format and -json flags.

For some commands, like tailscale netcheck or tailscale routecheck, the user can specify the output format using the -format flag:

tailscale routecheck -format=json-line.

Setting this flag to "json" or "json-line" implies that the -json flag is set.

func (*Format) IsBoolFlag added in v1.102.0

func (f *Format) IsBoolFlag() bool

IsBoolFlag reports that this flag.Value can be set without an argument. This is the magic interface that makes -name equivalent to -name=true rather than using the next command-line argument.

func (*Format) JSONBool added in v1.102.0

func (f *Format) JSONBool() flag.Value

JSONBool returns a flag.Value for a boolean -json flag which is aware of the underlying format.

Example
package main

import (
	"flag"
	"fmt"

	"tailscale.com/cmd/tailscale/cli/jsonoutput"
)

func main() {
	var args struct {
		format jsonoutput.Format
	}

	fs := flag.NewFlagSet("ExampleFormat", flag.ExitOnError)
	fs.Var(&args.format, "format", `output format; empty (for human-readable), "json" or "json-line"`)
	fs.Var(args.format.JSONBool(), "json", "output in JSON format")

	fs.Parse([]string{"-json"})
	fmt.Printf(`{format: %q, set: %t, version: %d}`, args.format, args.format.IsSet, args.format.Version)
}
Output:
{format: "json", set: true, version: 1}

func (*Format) JSONSchemaVersion added in v1.102.0

func (f *Format) JSONSchemaVersion() flag.Value

JSONSchemaVersion returns a flag.Value for a SchemaVersion -json flag which is aware of the underlying format.

Example
package main

import (
	"flag"
	"fmt"

	"tailscale.com/cmd/tailscale/cli/jsonoutput"
)

func main() {
	var args struct {
		format jsonoutput.Format
	}

	fs := flag.NewFlagSet("ExampleFormat", flag.ExitOnError)
	fs.Var(&args.format, "format", `output format; empty (for human-readable), "json" or "json-line"`)
	fs.Var(args.format.JSONSchemaVersion(), "json", "output in JSON format")

	fs.Parse([]string{"-json=2", "-format=json-line"})
	fmt.Printf(`{format: %q, set: %t, version: %d}`, args.format, args.format.IsSet, args.format.Version)
}
Output:
{format: "json-line", set: true, version: 2}

func (*Format) Set added in v1.102.0

func (f *Format) Set(s string) error

Set is called when the user passes the flag as a command-line argument.

func (Format) String added in v1.102.0

func (f Format) String() string

String returns the default value which is printed in the CLI help text.

type ResponseEnvelope

type ResponseEnvelope struct {
	// SchemaVersion is the version of the JSON output, e.g. "1", "2", "3"
	SchemaVersion string

	// ResponseWarning tells a user if a newer version of the JSON output
	// is available.
	ResponseWarning string `json:"_WARNING,omitzero"`
}

ResponseEnvelope is a set of fields common to all versioned JSON output.

Example
package main

import (
	"encoding/json"
	"fmt"

	"tailscale.com/cmd/tailscale/cli/jsonoutput"
)

type Hello struct {
	jsonoutput.ResponseEnvelope
	Greeting string
}

func main() {
	hi := Hello{
		ResponseEnvelope: jsonoutput.ResponseEnvelope{SchemaVersion: "1"},
		Greeting:         "Hello, world",
	}
	out, err := json.MarshalIndent(hi, "", "    ")
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", out)
}
Output:
{
    "SchemaVersion": "1",
    "Greeting": "Hello, world"
}

type SchemaVersion added in v1.102.0

type SchemaVersion struct {
	// IsSet tracks if the flag was set or cleared.
	// This flag is true when set by -name or -name=true or -name=INT,
	// otherwise it is false when cleared by -name=false.
	IsSet bool

	// Version tracks the desired schema version, as set by the -name=INT flag.
	// The version defaults to 1 when implicitly set by -name or -name=true.
	Version int
}

SchemaVersion implements the flag.Value interface, tracking whether the flag has been set or cleared, and its value when set.

Example
package main

import (
	"flag"
	"fmt"

	"tailscale.com/cmd/tailscale/cli/jsonoutput"
)

var args struct {
	json jsonoutput.SchemaVersion
}

func main() {
	fs := flag.NewFlagSet("ExampleSchemaVersion", flag.ExitOnError)
	fs.Var(&args.json, "json", "output in JSON format")

	fs.Parse([]string{"-json=2"})
	fmt.Printf(`{set: %t, version: %d}`, args.json.IsSet, args.json.Version)
}
Output:
{set: true, version: 2}

func (*SchemaVersion) IsBoolFlag added in v1.102.0

func (v *SchemaVersion) IsBoolFlag() bool

IsBoolFlag reports that this flag.Value can be set without an argument. This is the magic interface that makes -name equivalent to -name=true rather than using the next command-line argument.

func (*SchemaVersion) Set added in v1.102.0

func (v *SchemaVersion) Set(s string) error

Set is called when the user passes the flag as a command-line argument.

func (SchemaVersion) String added in v1.102.0

func (v SchemaVersion) String() string

String returns the default value which is printed in the CLI help text.

Jump to

Keyboard shortcuts

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