Documentation
¶
Overview ¶
Package keymgmtcmd implements the key & keystore management commands — `setup`, the `key` group (add/bind/use/rename/remove/list/show/set-base-url/ set-default-account-seq), and the `keystore` group (status/migrate/default). It runs against the clienv.Cmd seam, so the cli dispatches into it without it importing cli. The keystore results render their own human output via FormatText (textout.TextFormatter); the key/setup path additionally exports KeyContext + RunKeyCommand so the mcp server can drive the exact CLI key-creation path and capture its JSON document (see keycmd.go).
Index ¶
- Constants
- func RunKeyCommand(c *spec.Command, flags map[string]string, positionals []string, ctx KeyContext) error
- func RunKeystore(cx *clienv.Cmd, c *spec.Command, cmd *cobra.Command, args []string) error
- func RunSetup(flags map[string]string, positionals []string, ctx KeyContext) error
- type ClaimUpdate
- type KeyContext
Constants ¶
const (
PermAll = permTrading | permTransfers // 127
)
permTrading is the default permission bitmask prefilled into the registration deep link: all read scopes plus place-orders — readBalances(1)|readOrders(2)|writeOrders(4)|readDeposits(8)|readWithdrawals(32) = 47. The deposit/withdrawal *write* bits (writeDeposits=16, writeWithdrawals=64) are off by default — a plain trading key never requests withdrawal permission. `setup`/`key add --with-transfers` opts those bits in (PermAll) for users who need `deposit generate`, `withdraw request/cancel`, or the `krw` push commands. The human still reviews and adjusts the boxes on the portal before confirming.
Variables ¶
This section is empty.
Functions ¶
func RunKeyCommand ¶
func RunKeyCommand(c *spec.Command, flags map[string]string, positionals []string, ctx KeyContext) error
RunKeyCommand dispatches the builtin `key *` subcommands. flags holds the set string-flag values; output goes to stdout, diagnostics to stderr. It is exported so the mcp server can drive the same path with a buffer-backed KeyContext and capture the emitted JSON document. It handles only `key *` (`setup` has its own entry, RunSetup).
func RunKeystore ¶
RunKeystore handles the `keystore` builtin group (status, migrate, default). It is special-cased in dispatch like doctor/ip/logs because it manages the keystore backends themselves rather than signing a request with a key.
func RunSetup ¶
func RunSetup(flags map[string]string, positionals []string, ctx KeyContext) error
RunSetup implements the `setup` command (key name defaults to "default"). Every path that ends already-configured runs the complementary health check (see runSetupDoctor). The full behavior matrix:
situation result
----------------------------------- ------------------------------------------
setup (new key) generate + registration link, no doctor
setup (unbound key exists) resume (re-print link), no doctor
setup (bound key exists) already configured + doctor
setup --wait (new or unbound key) print link, poll, then bind + doctor on
claim (headless; resume doc on stop/timeout)
setup --api-key (new key) error — register the public key first
setup --api-key (unbound key) bind + doctor
setup --api-key (bound, same id) no-op (already configured) + doctor
setup --api-key (bound, diff id) error — setup never rebinds
setup --api-key SANDBOX_… error — not created via setup
inline KORBIT_CLI_API_KEY_* set report env credential + doctor; --api-key errors
The awaiting rows (new/unbound, no --api-key/--wait) finish per the wired path: the on-TTY interactive prompt, else print-and-exit. --wait selects the headless poll instead and is a no-op on an already-finished (bound/inline) key.
Types ¶
type ClaimUpdate ¶
ClaimUpdate is one event from the background auto-claim poll, surfaced by the interactive runner; it mirrors setupui.ClaimUpdate so this package stays independent of the UI layer (root.go maps between the two). A non-terminal update carries only Status. A terminal update sets exactly one of Done (success: Result holds the lines to show before the session ends) or Stop (polling stopped without a claim; the prompt stays open and Status says why). Prefill (Stop only) is the claimed api key id to drop into the empty input, so a fall-back after the id was already retrieved doesn't make the user re-find it.
type KeyContext ¶
type KeyContext struct {
IO output.IO
JSONMode bool
Compact bool
Home string
KM *keys.Manager
// IP, when set (by `setup`), carries the probed public IP(s) so the
// guidance can name the exact allowlist entries to register.
IP *probe.Report
// Perms is the permission bitmask to prefill into the registration deep link.
// Zero means "unset" — treated as permTrading (the safe default).
Perms int
// BaseURL / BaseURLSet carry an explicit --base-url. `setup` (only when it
// first creates the key) and `key add` persist it as the new key's default
// endpoint, mirroring `key set-base-url`. Unset leaves the key on the default
// host.
BaseURL string
BaseURLSet bool
// WSBaseURL / WSBaseURLSet carry an explicit --ws-base-url for `key
// set-base-url` and the create commands: when set, it is persisted verbatim;
// when unset, the WebSocket URL is derived from the REST base URL.
WSBaseURL string
WSBaseURLSet bool
// Verify, when set, smoke-tests the REST + WebSocket endpoints after `key
// set-base-url` stores them. Nil when the check is skipped (--no-verify) or
// not applicable to the command.
Verify func(restURL, wsBaseURL string) probe.EndpointVerification
// Getenv reads the process environment; `setup` uses it to detect an inline
// credential (KORBIT_CLI_API_KEY_*). May be nil (the check is then skipped).
Getenv func(string) string
// Doctor, when set, runs the read-only health check for a just-configured
// BOUND key and returns its report. It is advisory: setup embeds the report in
// its result and surfaces problems as warnings, but a failing check NEVER
// changes setup's exit code. Nil when the check is skipped.
Doctor func(keyName string) *doctorcmd.Report
// VerifyAPIKey, when set, signs a read-only whoami for the named key using a
// CANDIDATE api key id — with no binding persisted — to confirm the id is
// registered and the key can sign with it. Both setup lanes call it before
// binding. wait selects the tolerance: false (manual paste) is one-shot — any
// rejection (KEY_NOT_FOUND, signature mismatch) is returned so the prompt can
// retry without ever writing a bad id; true (auto-claim) rides out the
// just-registered KEY_NOT_FOUND window within a short budget and is best-effort
// (the caller treats a failure as soft and binds anyway). ctx lets a session
// cancel abort the wait. nil means an id is bound without checking.
VerifyAPIKey func(ctx context.Context, name, apiKeyID string, wait bool) error
// RunInteractive, when set, takes over the awaiting-registration outcome of
// `setup` on a TTY: instead of printing the registration link and exiting, it
// shows intro (the lead-in prose + the public key when there is no link) and
// prompts for the issued key id, then calls submit, which binds the key and runs
// the health check. registrationURL is the portal deep link (empty when none
// could be built); the UI renders it as its own width-wrapped, clickable block
// and offers copy/QR shortcuts for it, so it is passed separately rather than
// baked into intro. submit returns the lines to display, done=true when setup is
// complete, or a retryable error to show while keeping the prompt open. The UI
// renders on stderr; the final result document is still emitted to stdout after
// it closes. canceled=true means the user aborted with Ctrl-C: the caller emits
// nothing (matching the non-interactive SIGINT abort). Nil disables interactive
// setup (a pipe, --json, the MCP server, --no-interactive), and setup falls back
// to print-and-exit.
//
// autoClaim, when non-nil, runs concurrently with the paste prompt: the runner
// starts it, shows its status, and lets it complete setup automatically when it
// detects the registered key (see ClaimUpdate). nil leaves manual paste as the
// only path.
RunInteractive func(intro []string, registrationURL string, submit func(token string) (lines []string, done bool, err error), autoClaim func(ctx context.Context) <-chan ClaimUpdate) (canceled bool, err error)
// PollClaim, when set, runs the background auto-claim poll for the freshly
// generated key `name`, signing the keyless GET /v2/keys/claim with the key's
// not-yet-bound private key. It calls onStatus with a short live status for
// transient states (e.g. a brief back-off) and returns one of:
// - (id, "", nil) the key was claimed — the caller binds it
// - ("", code, nil) polling stopped without a claim; code is the API error
// code (e.g. KEY_CLAIM_CONFLICT, IP_NOT_ALLOWED,
// KEY_DEACTIVATED), "" for a generic/malformed stop. The
// caller renders the user notice (claimStopNotice).
// - ("", "", err) ctx was canceled (session ending) or an unexpected fault
// It journals nothing and logs through the standard logger. nil disables
// auto-claim (setup falls back to manual paste only).
PollClaim func(ctx context.Context, name string, onStatus func(status string)) (apiKeyID, stopCode string, err error)
// WaitForClaim enables the non-interactive headless auto-claim path (`setup
// --wait`): instead of printing the registration link and exiting, setup prints
// the link, then blocks polling PollClaim until the key is claimed (or
// WaitTimeout elapses) and binds + health-checks it automatically — no prompt,
// no manual paste. It reuses the same PollClaim / VerifyAPIKey / Doctor seams as
// the interactive auto-claim and works with or without a TTY (so an agent driving
// the CLI never has to copy the issued id back). Ignored unless PollClaim is set.
WaitForClaim bool
// WaitTimeout bounds the headless wait; zero means wait indefinitely. Only
// consulted when WaitForClaim is set.
WaitTimeout time.Duration
}
KeyContext carries the dependencies the builtin key/setup commands need. It is exported so both cli root dispatch and the mcp server can drive the exact CLI key-creation path: mcp substitutes a buffer-backed IO to capture the JSON document for its tool result (see RunKeyCommand).