cmd/tailscale/cli: add whoami subcommand

Add a "tailscale whoami" subcommand that is equivalent to running
"tailscale whois $(tailscale ip -4)" but more ergonomic. It supports
the --json flag just like whois, and shares the WhoIsResponse
rendering code with whois.

Fixes #19907

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Change-Id: I8f33ba7a5608bab7dffa8213303beb5f345936d3
This commit is contained in:
Brad Fitzpatrick
2026-05-28 17:26:43 +00:00
committed by Brad Fitzpatrick
parent 9d126aec34
commit c086992f4f
3 changed files with 61 additions and 1 deletions

View File

@@ -299,6 +299,7 @@ func newRootCmd(tb ...testenv.TB) *ffcli.Command {
exitNodeCmd(),
nilOrCall(maybeUpdateCmd),
whoisCmd,
whoamiCmd,
debugCmd(),
nilOrCall(maybeDriveCmd),
idTokenCmd,

View File

@@ -0,0 +1,52 @@
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package cli
import (
"context"
"errors"
"flag"
"fmt"
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
)
var whoamiCmd = &ffcli.Command{
Name: "whoami",
ShortUsage: "tailscale whoami [--json]",
ShortHelp: "Show the machine and user identity of the current machine",
LongHelp: strings.TrimSpace(`
'tailscale whoami' shows the machine and user identity of the current machine.
It is equivalent to running 'tailscale whois' against one of the current machine's own Tailscale IP addresses.
`),
Exec: runWhoami,
FlagSet: func() *flag.FlagSet {
fs := newFlagSet("whoami")
fs.BoolVar(&whoamiArgs.json, "json", false, "output in JSON format")
return fs
}(),
}
var whoamiArgs struct {
json bool // output in JSON format
}
func runWhoami(ctx context.Context, args []string) error {
if len(args) > 0 {
return errors.New("too many arguments, expected none")
}
st, err := localClient.StatusWithoutPeers(ctx)
if err != nil {
return err
}
if len(st.TailscaleIPs) == 0 {
return fmt.Errorf("no current Tailscale IP address; state: %v", st.BackendState)
}
who, err := localClient.WhoIsProto(ctx, "", st.TailscaleIPs[0].String())
if err != nil {
return err
}
return printWhoIs(who, whoamiArgs.json)
}

View File

@@ -13,6 +13,7 @@
"text/tabwriter"
"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/client/tailscale/apitype"
)
var whoisCmd = &ffcli.Command{
@@ -46,7 +47,13 @@ func runWhoIs(ctx context.Context, args []string) error {
if err != nil {
return err
}
if whoIsArgs.json {
return printWhoIs(who, whoIsArgs.json)
}
// printWhoIs prints the WhoIsResponse to Stdout, either as JSON (if asJSON is
// true) or in a human-readable form.
func printWhoIs(who *apitype.WhoIsResponse, asJSON bool) error {
if asJSON {
ec := json.NewEncoder(Stdout)
ec.SetIndent("", " ")
ec.Encode(who)