Skip to content

Client API

maxlandon edited this page Jul 18, 2026 · 1 revision

Client API · [Dev]

A guided tour of the public github.com/reeflective/team/client package. Full signatures and per-method docs: https://pkg.go.dev/github.com/reeflective/team/client. This page tells you when to reach for each call.

The teamclient core is deliberately minimal: it drives connect/disconnect through a Dialer, and answers exactly two questions about a (possibly remote) server — its users and its version. It contains no server-side code.


Constructor & lifecycle

teamclient, err := client.New("myapp", client.WithDialer(dialer))

New(app string, opts ...Options) (*Client, error) builds the core (filesystem + loggers only). It is not connected to any server yet — connection is deferred until a command or your code needs it.

Method What it does
Connect(opts ...Options) error Connect via the dialer. Guarded by a sync.Once (one connect per run). Resolves a config (may load/select from disk and prompt), then dialer.Init + dialer.Dial. No dialer ⇒ no-op, no error.
Disconnect() error Close the connection (dialer.Close). No-op under WithNoDisconnect(). Resets the connect-once so the client can reconnect.
Name() string The application name.

The generated commands call Connect()/Disconnect() for you; you only call them directly when driving the client from your own code. See Getting Started → a client-only binary.


Queries

Three questions, resolved through the team.Client backend (the dialer, or a WithTeamClient object):

Method Notes
Users() ([]team.User, error) Users of the connected server. ErrNoTeamclient if no backend is set.
VersionServer() (team.Version, error) The remote server's version — traverses the transport. ErrNoTeamclient if no backend.
VersionClient() (team.Version, error) The local build info of this client binary. Computed locally from the binary's own version — it never touches the transport and needs no connection.

The VersionServer vs VersionClient split is the usual source of confusion: version commands print both so operators can spot a client/server mismatch, but only VersionServer requires a live connection.


The Dialer interface

type Dialer interface {
    Init(c *Client) error // transport-agnostic prep (read config, build credentials)
    Dial() error          // transport-specific connect
    Close() error         // tear down
}

Register a dialer with WithDialer. If it also implements team.Client (Users/VersionServer) — the common case for RPC transports — the core adopts it as the query backend automatically, so WithDialer(...) alone is enough; no separate WithTeamClient. Full walkthrough and the gRPC reference dialer: Writing a Transport.


Configs & TLS

A Config is the JSON connection file an operator imports (user, host, port, token, CA cert, client cert + key). The struct is documented in Users & Authentication → The connection config file.

cfg := teamclient.Config()             // current config (empty, not nil, if none loaded yet)
cfgs := teamclient.GetConfigs()        // map of configs found on disk (keyed "user@host (digest)")
cfg, err := teamclient.ReadConfig(path)
err = teamclient.SaveConfig(cfg)       // ErrConfigNoUser if cfg.User == ""
Method Notes
Config() *Config The current remote-server config; empty (not nil) if none loaded.
GetConfigs() map[string]*Config Configs in ~/.<app>/teamclient/configs/. Always uses the on-disk FS, even in memory mode.
ReadConfig(path string) (*Config, error) Parse a config file.
SaveConfig(*Config) error Persist a config (rejects an empty User).
SelectConfig() *Config Return the sole config, or prompt to pick one (may block).
NewTLSConfigFrom(caCert, cert, key string) (*tls.Config, error) Build the client-side mutual-TLS config from a config's three credential fields. Used inside a dialer's Init.

To skip disk lookup and the prompt entirely (automation, tests), construct with WithConfig(cfg).


Logging

Thin façade over the log package; styling and formats in Logging.

Method Notes
NamedLogger(pkg, stream string) *slog.Logger Tagged logger; never nil.
SetLogWriter(stdout, stderr io.Writer) Redirect the console streams — how the CLI runners point logging at a cobra command's own stdout/stderr.
SetLogLevel(level int) Adjust console + file levels (no-op for a WithLogger handler).
SetLogFormat(format log.Format) Console stream format: console / text / json.

Directories & filesystem

Method Notes
HomeDir() / TeamDir() / LogsDir() / ConfigsDir() App directories, created on demand.
Filesystem() *assets.FS On-disk, or in-memory under WithInMemory().

Note: config read/write always uses the on-disk filesystem even when the client is in memory mode, so imported server configs survive an in-memory client.


Options

Pass at New(...) (and, for the repeatable ones, at Connect(...)). Full reference table in Configuration → Client options.

Option Purpose
WithDialer(Dialer) The connection backend. If it also implements team.Client, it becomes the query backend too.
WithTeamClient(team.Client) Set the query backend explicitly, when it is a distinct object from the dialer (e.g. server.Self() uses this so a server is a client of itself).
WithConfig(*Config) Use a given config; no disk lookup, no prompt.
WithInMemory() Route the filesystem to memory (also disables file logs).
WithHomeDirectory(path) / WithTeamDirectory(name) Relocate ~/.<app>/ and the teamclient subdir.
WithNoLogs(bool) / WithLogFile(path) Silence / relocate file logging.
WithLogger(slog.Handler) Replace the console+file backend (disables runtime level/output knobs).
WithConsoleOptions(func(*log.ConsoleOptions)) Restyle the built-in console, keep the loggers.
WithLogFormat(log.Format) Console stream format: console / text / json.
WithNoDisconnect() For closed-loop / readline apps: don't disconnect after each command. Use with PreRunNoDisconnect.

Errors

Sentinel errors you can match with errors.Is:

ErrNoTeamclient, ErrConfig, ErrNoConfig, ErrConfigNoUser, ErrClient.


Command tree & runners

root := client_commands.Generate(teamclient) // *cobra.Command: import, users, version

client/commands.Generate(cli *Client) *cobra.Command returns the teamclient tree. The package also exports the reusable cobra runners the tree is built from — use them as models for wiring the teamclient into your own commands:

  • PreRun(cli, opts...) — connect before the command.
  • PreRunNoDisconnect(cli, opts...) — connect without arming a post-run disconnect (for WithNoDisconnect apps).
  • PostRun(cli) — disconnect after the command.

See Common Workflows → embedded console app and CLI Reference.


Related: Server API → · Writing a Transport → · Users & Authentication → · Configuration →

Clone this wiki locally