Documentation
¶
Index ¶
- Constants
- Variables
- func AuthenticateWithKey(ctx context.Context, hostname string, keyPair *cloudauth.KeyPair) (string, error)
- func GetActiveConfigPath() string
- func GetConfigDirPath() string
- func NormalizeIssuerURL(server string) string
- func RevokeRefreshToken(ctx context.Context, issuer, accessToken, refreshToken string) error
- type BeginAuthRequest
- type BeginAuthResponse
- type ClusterConfig
- func (c *ClusterConfig) CloudEndpoint(config *Config) (string, error)
- func (c *ClusterConfig) RPCOptions(ctx context.Context, config *Config) ([]rpc.StateOption, error)
- func (c *ClusterConfig) RPCOptionsWithName(ctx context.Context, config *Config, clusterName string) ([]rpc.StateOption, error)
- func (c *ClusterConfig) State(ctx context.Context, config *Config, opts ...rpc.StateOption) (*rpc.State, error)
- type CompleteAuthRequest
- type CompleteAuthResponse
- type Config
- func (c *Config) ActiveCluster() string
- func (c *Config) ClearActiveCluster()
- func (c *Config) GetActiveCluster() (*ClusterConfig, error)
- func (c *Config) GetCluster(name string) (*ClusterConfig, error)
- func (c *Config) GetClusterCount() int
- func (c *Config) GetClusterNames() []string
- func (c *Config) GetClusterSource(name string) string
- func (c *Config) GetIdentity(name string) (*IdentityConfig, error)
- func (c *Config) GetIdentityCount() int
- func (c *Config) GetIdentityNames() []string
- func (c *Config) GetIdentitySource(name string) string
- func (c *Config) GetKey(name string) (*KeyConfig, error)
- func (c *Config) GetKeyNames() []string
- func (c *Config) GetLeafConfigNames() []string
- func (c *Config) GetPrivateKeyPEM(identity *IdentityConfig) (string, error)
- func (c *Config) HasAnyClusters() bool
- func (c *Config) HasCluster(name string) bool
- func (c *Config) HasIdentities() bool
- func (c *Config) HasIdentity(name string) bool
- func (c *Config) HasKey(name string) bool
- func (c *Config) IsEmpty() bool
- func (c *Config) IterateClusters(fn func(name string, cluster *ClusterConfig) error) error
- func (c *Config) RPCOptions(ctx context.Context) ([]rpc.StateOption, error)
- func (c *Config) RemoveCluster(name string) error
- func (c *Config) Save() error
- func (c *Config) SaveTo(path string) error
- func (c *Config) SaveToHome() error
- func (c *Config) SetActiveCluster(name string) error
- func (c *Config) SetCluster(name string, cluster *ClusterConfig)
- func (c *Config) SetIdentity(name string, identity *IdentityConfig)
- func (c *Config) SetKey(name string, key *KeyConfig)
- func (c *Config) SetLeafConfig(name string, configData *ConfigData)
- func (c *Config) SetTheme(theme string)
- func (c *Config) SourcePath() string
- func (c *Config) State(ctx context.Context, opts ...rpc.StateOption) (*rpc.State, error)
- func (c *Config) Theme() string
- func (c *Config) TokenForIdentity(ctx context.Context, name string, identity *IdentityConfig, ...) (string, error)
- func (c *Config) UnmarshalYAML(unmarshal func(any) error) error
- func (c *Config) Validate() error
- type ConfigData
- type IdentityConfig
- type IdentityType
- type KeyConfig
Constants ¶
const ( // DefaultConfigPath is the default path for the config file in user's home directory DefaultConfigPath = ".config/miren/clientconfig.yaml" // EnvConfigPath is the environment variable name for custom config path EnvConfigPath = "MIREN_CONFIG" )
const ( // EnvInCluster marks a process as running inside a Miren sandbox. It is the // signal InCluster keys off: probing for the token file instead would fire // anywhere the path happened to exist. EnvInCluster = "MIREN_IN_CLUSTER" // EnvAPIAddress is the cluster API, as a literal IP:port. Deliberately not // MIREN_SERVER_ADDRESS, which already means the address a server binds to. EnvAPIAddress = "MIREN_API_ADDRESS" // EnvIdentityTokenPath is the sandbox's workload identity token. EnvIdentityTokenPath = "MIREN_IDENTITY_TOKEN_PATH" // EnvCACertPath is the cluster CA, for verifying the API certificate. EnvCACertPath = "MIREN_CA_CERT_PATH" )
Environment injected into every sandbox by the sandbox controller. See controllers/sandbox/sandbox.go.
const APIServerName = "api.miren"
APIServerName is the name in-cluster clients verify the API certificate against. A sandbox dials the API by address, because the address it uses -- its bridge router, or the coordinator on a distributed runner -- cannot be a certificate SAN. Nothing resolves this name; it is only ever a TLS server name. Must match coordinate.APIServerName.
const CloudRPCPath = "/api/v1/clusters/%s/rpc"
CloudRPCPath is where Miren Cloud relays RPC for a cluster. The cluster's XID is the only thing that varies, because the relay's job is to find the socket that cluster is holding open.
const InClusterName = "in-cluster"
InClusterName is the cluster name given to a config built by InCluster.
Variables ¶
var ErrLoginRequired = errors.New("login required: session expired or revoked")
ErrLoginRequired signals that a credential has expired or been revoked and the user must run `miren login` again. It is returned only for definitive server rejections (HTTP 401), never for transient failures (5xx, network errors) — treating a cloud blip as a logout would stampede every CLI user into re-authenticating at once.
var ErrNoBearerToken = errors.New("identity does not use bearer-token authentication")
ErrNoBearerToken signals that an identity does not authenticate via a bearer token (e.g. certificate identities). Callers should fall back to their own credential handling rather than treating this as a failure.
var ErrNoConfig = fmt.Errorf("no config file found")
Functions ¶
func AuthenticateWithKey ¶
func AuthenticateWithKey(ctx context.Context, hostname string, keyPair *cloudauth.KeyPair) (string, error)
AuthenticateWithKey performs the challenge-response authentication flow
func GetActiveConfigPath ¶
func GetActiveConfigPath() string
GetActiveConfigPath returns the path to the active configuration file
func GetConfigDirPath ¶
func GetConfigDirPath() string
GetConfigDirPath returns the path to the configuration directory
func NormalizeIssuerURL ¶ added in v0.12.1
NormalizeIssuerURL ensures an auth-server URL carries a scheme, defaulting to https:// but using http:// for loopback hosts so local-dev clouds work. A URL that already has a scheme is returned with any trailing slash trimmed.
func RevokeRefreshToken ¶ added in v0.12.1
RevokeRefreshToken best-effort revokes a refresh token via POST {issuer}/auth/revoke/refresh. The endpoint requires the access token as a bearer credential. Any error is returned to the caller, which should treat revocation as advisory — a failed revoke must never block logout.
Types ¶
type BeginAuthRequest ¶
type BeginAuthRequest struct {
Fingerprint string `json:"fingerprint"`
}
BeginAuthRequest is the request to begin authentication
type BeginAuthResponse ¶
type BeginAuthResponse struct {
Envelope string `json:"envelope"`
Challenge string `json:"challenge"`
}
BeginAuthResponse is the response from begin authentication
type ClusterConfig ¶
type ClusterConfig struct {
Hostname string `yaml:"hostname"`
AllAddresses []string `yaml:"all_addresses,omitempty"` // All available addresses for this cluster
Identity string `yaml:"identity,omitempty"` // Reference to an identity in the Identities section
XID string `yaml:"xid,omitempty"` // Cloud cluster XID for permission checks
CACert string `yaml:"ca_cert,omitempty"` // PEM encoded CA certificate
ClientCert string `yaml:"client_cert,omitempty"` // PEM encoded client certificate (deprecated, use identity)
ClientKey string `yaml:"client_key,omitempty"` // PEM encoded client key (deprecated, use identity)
Insecure bool `yaml:"insecure,omitempty"` // Skip TLS verification
CloudAuth bool `yaml:"cloud_auth,omitempty"` // Use cloud authentication (deprecated, use identity)
// IdentityTokenPath is a workload identity token file to authenticate with,
// re-read on each request because the sandbox controller refreshes it in
// place. Set by InCluster for code running inside a sandbox.
IdentityTokenPath string `yaml:"identity_token_path,omitempty"`
// TLSServerName overrides the name the server certificate is verified
// against, for when the dial address cannot be a certificate SAN.
TLSServerName string `yaml:"tls_server_name,omitempty"`
// ViaCloud routes RPC through Miren Cloud rather than dialing the cluster,
// for a cluster this machine has no route to. It needs XID to name the
// cluster and an identity to authenticate as; Hostname is unused.
//
// Opting in is explicit rather than a fallback from a failed direct dial,
// so that every command's answer comes from a path you chose. A silent
// fallback would also make every command wait out a dial timeout first.
ViaCloud bool `yaml:"via_cloud,omitempty"`
// CloudURL overrides where the relay lives. Normally empty: the identity's
// issuer is already the cloud that vouches for you, and pointing the two at
// different places is a mistake far more often than an intent.
CloudURL string `yaml:"cloud_url,omitempty"`
}
ClusterConfig holds the configuration for a single cluster
func (*ClusterConfig) CloudEndpoint ¶ added in v0.14.0
func (c *ClusterConfig) CloudEndpoint(config *Config) (string, error)
CloudEndpoint returns the cloud a via-cloud cluster is reached through.
An explicit cloud_url wins over the identity's issuer, which is what makes a cluster registered with one cloud reachable through another. Exported so the commands that need to name that cloud share the rule rather than restating it: two copies drift, and the copy outside this package is the one nothing else would catch.
func (*ClusterConfig) RPCOptions ¶
func (c *ClusterConfig) RPCOptions(ctx context.Context, config *Config) ([]rpc.StateOption, error)
RPCOptions returns RPC options without a cluster name (deprecated, use RPCOptionsWithName)
func (*ClusterConfig) RPCOptionsWithName ¶
func (c *ClusterConfig) RPCOptionsWithName(ctx context.Context, config *Config, clusterName string) ([]rpc.StateOption, error)
RPCOptionsWithName returns RPC options with cluster name for caching
type CompleteAuthRequest ¶
type CompleteAuthRequest struct {
Envelope string `json:"envelope"`
Signature string `json:"signature"`
}
CompleteAuthRequest is the request to complete authentication
type CompleteAuthResponse ¶
CompleteAuthResponse is the response from complete authentication
type Config ¶
type Config struct {
// contains filtered or unexported fields
}
Config represents the complete client configuration
func DecodeConfig ¶
LoadConfig loads the configuration from disk
func InCluster ¶ added in v0.14.0
InCluster builds a Config for code running inside a Miren sandbox, authenticating with the workload identity token mounted into the container. It is the analogue of Kubernetes' rest.InClusterConfig.
Returns ErrNoConfig when not running in a sandbox, so callers can fall through to an ordinary config file.
func LoadConfig ¶
LoadConfig loads the configuration from disk, falling back to the sandbox's own workload identity when there is no config file and we are running inside one. That ordering keeps an explicit config authoritative, so pointing the CLI at another cluster from inside a sandbox still works.
func LoadConfigFrom ¶
LoadConfig loads the configuration from disk
func (*Config) ActiveCluster ¶
ActiveCluster returns the name of the active cluster
func (*Config) ClearActiveCluster ¶ added in v0.11.1
func (c *Config) ClearActiveCluster()
ClearActiveCluster unsets the active cluster, leaving no cluster active. This is a valid state (used, for example, when the active cluster is being removed) and callers must re-select one before commands that need a default cluster.
func (*Config) GetActiveCluster ¶
func (c *Config) GetActiveCluster() (*ClusterConfig, error)
GetActiveCluster returns the active cluster configuration
func (*Config) GetCluster ¶
func (c *Config) GetCluster(name string) (*ClusterConfig, error)
GetCluster returns the configuration for a specific cluster
func (*Config) GetClusterCount ¶
GetClusterCount returns the number of clusters in the configuration
func (*Config) GetClusterNames ¶
GetClusterNames returns a sorted list of all cluster names
func (*Config) GetClusterSource ¶ added in v0.2.1
GetClusterSource returns the source file path for a cluster. For clusters in the main config, returns the main config path. For clusters in leaf configs, returns the leaf config path.
func (*Config) GetIdentity ¶
func (c *Config) GetIdentity(name string) (*IdentityConfig, error)
GetIdentity returns an identity configuration by name
func (*Config) GetIdentityCount ¶
GetIdentityCount returns the number of configured identities
func (*Config) GetIdentityNames ¶
GetIdentityNames returns a sorted list of all identity names
func (*Config) GetIdentitySource ¶ added in v0.12.1
GetIdentitySource returns the source file path for an identity. For identities in the main config, returns the main config path. For identities in leaf configs, returns the leaf config path. Returns "" if the identity is not found.
func (*Config) GetKeyNames ¶
GetKeyNames returns a sorted list of all key names
func (*Config) GetLeafConfigNames ¶
GetLeafConfigNames returns the names of all leaf configs
func (*Config) GetPrivateKeyPEM ¶
func (c *Config) GetPrivateKeyPEM(identity *IdentityConfig) (string, error)
GetPrivateKeyPEM resolves and returns the private key PEM for an identity. It handles both direct PrivateKey fields and KeyRef references.
func (*Config) HasAnyClusters ¶
HasAnyClusters checks if any clusters are configured
func (*Config) HasCluster ¶
HasCluster checks if a cluster exists in the configuration
func (*Config) HasIdentities ¶
HasIdentities returns true if there are any identities configured
func (*Config) HasIdentity ¶
HasIdentity checks if an identity exists in the configuration
func (*Config) IterateClusters ¶
func (c *Config) IterateClusters(fn func(name string, cluster *ClusterConfig) error) error
IterateClusters calls the provided function for each cluster in sorted order If the function returns an error, iteration stops and the error is returned
func (*Config) RPCOptions ¶
func (*Config) RemoveCluster ¶
RemoveCluster removes a cluster from the configuration.
Removing the active cluster is allowed, and drops the active pointer with it. Refusing was meant to stop the config ending up pointed at nothing, but with a single cluster it made the entry impossible to delete at all: there is nowhere to switch to first. Clearing the pointer keeps the invariant that actually mattered — active never names a cluster that isn't there — without the dead end.
func (*Config) SaveToHome ¶
func (*Config) SetActiveCluster ¶
SetActiveCluster sets the active cluster
func (*Config) SetCluster ¶
func (c *Config) SetCluster(name string, cluster *ClusterConfig)
SetCluster adds or updates a cluster in the configuration
func (*Config) SetIdentity ¶
func (c *Config) SetIdentity(name string, identity *IdentityConfig)
SetIdentity adds or updates an identity configuration
func (*Config) SetLeafConfig ¶
func (c *Config) SetLeafConfig(name string, configData *ConfigData)
SetLeafConfig adds or updates a leaf config that will be saved to clientconfig.d/name.yaml
func (*Config) SourcePath ¶
SourcePath returns the path to the config file that was loaded, if any.
func (*Config) Theme ¶ added in v0.11.1
Theme returns the configured CLI color theme preference (auto | light | dark | no), or "" if unset. An empty value means auto-detect.
func (*Config) TokenForIdentity ¶ added in v0.12.1
func (c *Config) TokenForIdentity(ctx context.Context, name string, identity *IdentityConfig, fallbackHost string) (string, error)
TokenForIdentity returns a bearer token to authenticate as the given identity.
- keypair: re-mints a JWT via the challenge-response flow (AuthenticateWithKey).
- token: returns the cached access token if still fresh; otherwise refreshes it via /auth/refresh under a cross-process lock and persists the rotated pair.
- certificate: returns ErrNoBearerToken (the caller supplies a client cert).
name identifies which identity leaf to write rotated tokens back to. Pass "" for an anonymous/in-memory identity (e.g. during login before it is named): a refresh is then used for this call only and not persisted.
fallbackHost is used as the issuer when the identity carries no Issuer.
func (*Config) UnmarshalYAML ¶
UnmarshalYAML implements the yaml.Unmarshaler interface
type ConfigData ¶
type ConfigData struct {
Active string `yaml:"active_cluster,omitempty"`
Theme string `yaml:"theme,omitempty"` // CLI color theme: auto | light | dark | no
Clusters map[string]*ClusterConfig `yaml:"clusters,omitempty"`
Identities map[string]*IdentityConfig `yaml:"identities,omitempty"`
Keys map[string]*KeyConfig `yaml:"keys,omitempty"`
}
ConfigData is used for YAML unmarshaling to handle private fields
type IdentityConfig ¶
type IdentityConfig struct {
Type IdentityType `yaml:"type"` // How this identity authenticates
Issuer string `yaml:"issuer,omitempty"` // The auth server that issued this identity (e.g., "https://miren.cloud")
KeyRef string `yaml:"key_ref,omitempty"` // Reference to a key in the Keys section (for keypair auth)
PrivateKey string `yaml:"private_key,omitempty"` // PEM encoded private key (for keypair auth, deprecated - use KeyRef)
ClientCert string `yaml:"client_cert,omitempty"` // PEM encoded client certificate (for cert auth)
ClientKey string `yaml:"client_key,omitempty"` // PEM encoded client key (for cert auth)
Token string `yaml:"token,omitempty"` // JWT access token (for token auth)
RefreshToken string `yaml:"refresh_token,omitempty"` // Refresh token used to renew the access token (for token auth)
}
IdentityConfig holds authentication credentials that can be used across clusters
type IdentityType ¶ added in v0.12.1
type IdentityType string
IdentityType names how an identity authenticates. Naming the set (rather than using a bare string) lets the exhaustive linter flag any switch that forgets an arm when a new type is added.
const ( // IdentityKeypair authenticates with an ed25519 key via challenge-response. IdentityKeypair IdentityType = "keypair" // IdentityToken authenticates with a stored JWT that is refreshed as needed. IdentityToken IdentityType = "token" // IdentityCertificate authenticates with a client TLS certificate. IdentityCertificate IdentityType = "certificate" )
type KeyConfig ¶
type KeyConfig struct {
Name string `yaml:"name"` // Name of the key (e.g., "miren-cli@hostname")
Type string `yaml:"type"` // Type of key: "ed25519", etc.
PrivateKey string `yaml:"private_key"` // PEM encoded private key
Fingerprint string `yaml:"fingerprint,omitempty"` // Fingerprint of the public key
Metadata map[string]string `yaml:"metadata,omitempty"` // Arbitrary metadata about the key
}
KeyConfig holds a reusable cryptographic key