Documentation
¶
Index ¶
- Constants
- func IsTypePersonal(typeNamespace string) bool
- func IsTypeTeam(typeNamespace string) bool
- func MatchPattern(pattern, value string) (bool, error)
- type APIKey
- type APIKeyConflicts
- type AccessPolicy
- type ActiveSession
- type AuthClaims
- type Billing
- func (b *Billing) ClearSubscription()
- func (b *Billing) Clone() *Billing
- func (b *Billing) HasCustomer() bool
- func (b *Billing) HasSubscription() bool
- func (b *Billing) IsActive() bool
- func (b *Billing) IsNil() bool
- func (b *Billing) SetCustomer(id string)
- func (b *Billing) SetSubscription(id string, status BillingStatus, currentPeriodEnd int64)
- func (b *Billing) SetSubscriptionStatus(status BillingStatus)
- type BillingBlockReason
- type BillingEvaluation
- type BillingStatus
- type BillingSubscription
- type Decision
- type DenialReason
- type Device
- type DeviceAuth
- type DeviceAuthRequest
- type DeviceAuthResponse
- type DeviceAuthStatus
- type DeviceConflicts
- type DeviceIdentity
- type DeviceInfo
- type DeviceLoginCode
- type DeviceLoginCodePreview
- type DevicePairing
- type DevicePairingAccepted
- type DevicePairingRequest
- type DevicePairingStatus
- type DevicePosition
- type DeviceStatus
- type DeviceTag
- type Endpoints
- type Filter
- type FirewallConnection
- type ID
- type Info
- type InstanceAPIKey
- type Member
- type MemberDeparted
- type MemberView
- type MembershipInvitation
- type MembershipInvitationNotification
- type MembershipInvitationStatus
- type Namespace
- type NamespaceConflicts
- type NamespaceDeviceLimit
- type NamespaceSettings
- type OperatorParams
- type PolicyAction
- type PolicySubject
- type PolicySubjectType
- type Principal
- type PrincipalKind
- type PrivateKey
- type PropertyParams
- type ProvisioningKey
- type ProvisioningKeyConflicts
- type ProvisioningKeyEvent
- type ProvisioningKeyMode
- type ProvisioningKeyType
- type PublicKey
- type PublicKeyAuthRequest
- type PublicKeyAuthResponse
- type PublicKeyFields
- type PublicKeyFilter
- type PublicKeyUpdate
- type RecordedSession
- type SSHApproval
- type SSHApprovalConfirmation
- type SSHApprovalCreated
- type SSHApprovalKind
- type SSHApprovalRequest
- type SSHApprovalState
- type SSHApprovalStatus
- type SSHCommand
- type SSHExitStatus
- type SSHIdentity
- type SSHIdentitySource
- type SSHPty
- type SSHPtyOutput
- type SSHSignal
- type SSHSubsystem
- type SSHWindowChange
- type Session
- type SessionEvent
- type SessionEventType
- type SessionEvents
- type SessionPosition
- type SessionSeat
- type SessionUpdate
- type Stats
- type Status
- type System
- type SystemAuthentication
- type SystemAuthenticationLocal
- type Tag
- type TagConflicts
- type Taggable
- type Tenant
- type Type
- type UID
- type User
- type UserAuthIdentifier
- type UserAuthMethod
- type UserAuthResponse
- type UserData
- type UserInfo
- type UserInvitation
- type UserInvitationStatus
- type UserMFA
- type UserOrigin
- type UserPassword
- type UserPreferences
- type UserStatus
- type UserTokenRecover
- type Username
- type WebEndpoint
- type WebEndpointTLS
Constants ¶
const ( // DeviceLoginCodeKindDevice is a code bound to an existing pending device // in a namespace (agent had a tenant). DeviceLoginCodeKindDevice = "device" // DeviceLoginCodeKindPairing is a code for a tenant-less agent; the device // does not exist yet and the user picks the namespace at accept time. DeviceLoginCodeKindPairing = "pairing" )
Kinds of codes the accept-device page can resolve.
const ( // MemberStatusActive is a completed, confirmed account that is a full member. MemberStatusActive = "active" // MemberStatusAwaitingApproval is a completed account still waiting for a system admin to // approve it; it is already a member but cannot sign in yet (see the auth login gate). MemberStatusAwaitingApproval = "awaiting_approval" // MemberStatusNotConfirmed is a member whose account was provisioned by the invite but not // yet completed; the invitee still has to finish setting it up. MemberStatusNotConfirmed = "not-confirmed" )
Member status values used by MemberView.Status. They flatten a member's account state into a single field the members list renders. Both statuses derive from core-only concepts (the users.awaiting_approval flag and the login gate). Cloud/enterprise may extend the view with additional statuses (e.g. pending invitations) in its own response type — kept out of core.
const ( // SSHAccessModeLegacy is the key/firewall model: access is a public key with // an ACL plus, on Cloud/Enterprise, firewall rules. SSHAccessModeLegacy = "legacy" // SSHAccessModeIdentity is the identity model: access is a ShellHub identity // (established by the out-of-band browser approval) plus Access Policies // deciding who may reach what, as which login. The legacy key ACL and // firewall checks are bypassed. SSHAccessModeIdentity = "identity" )
SSHAccessMode selects how a namespace authorizes SSH access.
const ( // ProvisioningKeyWebhookDefaultTimeout / MaxTimeout bound the synchronous webhook request. ProvisioningKeyWebhookDefaultTimeout = 5 ProvisioningKeyWebhookMaxTimeout = 15 // ProvisioningKeyWebhookDefaultCallbackTTL / MaxCallbackTTL bound the deferred-decision token's validity // (1 hour default, 24 hours max). ProvisioningKeyWebhookDefaultCallbackTTL = 3600 ProvisioningKeyWebhookMaxCallbackTTL = 86400 )
Webhook tuning bounds/defaults (seconds). A stored 0 means "use the default".
const EnrollmentReconcileInterval = 1 * time.Minute
EnrollmentReconcileInterval throttles re-evaluation of a still-pending enrollment on the agent's periodic AuthDevice. It is a server-side anti-hammer floor whose only job is to bound a fast crash-looping agent to one integrator call per interval; the real reconcile cadence is the agent's ping (~10m), well above this. Kept short (1m) so a legitimate restart/reconnect reconciles promptly instead of being silently skipped, while a per-second re-auth loop is still capped at 1/min.
const InstanceAPIKeyPrefix = "sh_admin_"
InstanceAPIKeyPrefix marks a plaintext key as an instance API key. Authentication branches on it before reaching a store, so the credential itself states which kind of key it is instead of the server inferring it from a failed namespace lookup.
Variables ¶
This section is empty.
Functions ¶
func IsTypePersonal ¶ added in v0.18.0
IsTypePersonal reports whether the raw string names the personal type. An unrecognized value is not personal, and neither predicate is the negation of the other.
func IsTypeTeam ¶ added in v0.18.0
IsTypeTeam reports whether the raw string names the team type. An unrecognized value is not a team, so a corrupted or future value degrades to the more restrictive answer.
func MatchPattern ¶
MatchPattern reports whether value satisfies an access rule's pattern. The pattern is a regular expression that must match value in full, so the rule "staging" selects the device named "staging" and neither "notstaging" nor "staging-db"; an empty pattern imposes no restriction and matches any value. It returns an error when the pattern does not compile.
It is the shared matcher for every administrator-written selector that decides access — device filters, public-key username restrictions and firewall rules — so that all of them read the way their author wrote them.
Types ¶
type APIKey ¶ added in v0.15.0
type APIKey struct {
// ID is the key's surrogate identifier, and what anything owned by the key points at. It
// is stable across a rename and carries nothing derived from the credential.
ID string `json:"id"`
// Digest is the SHA256 hash of the key's plaintext, and is what identifies the key
// everywhere it is resolved. The plaintext is never persisted.
Digest string `json:"-"`
// Name is an external identifier for a given API key. It is not unique per document but
// is unique per tenant ID.
Name string `json:"name"`
// TenantID is the API key's namespace ID.
TenantID string `json:"tenant_id"`
// Role defines the permissions of the API key. It must be equal to or less than the creator's role.
Role authorizer.Role `json:"role" validate:"required,oneof=administrator operator observer"`
// CreatedBy is the ID of the user who created the API key.
CreatedBy string `json:"created_by"`
// CreatedAt is the creation date of the API key.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is the last update date of the API key.
UpdatedAt time.Time `json:"updated_at"`
// ExpiresIn is the expiration date of the API key. An expired key cannot be used for
// authentication. When equals or less than 0 it means that are no expiration date.
ExpiresIn int64 `json:"expires_in"`
}
APIKey is used to authenticate a request. It is similar to [UserAuthClaims] but only for namespace information, which means that user-related routes are blocked for use with api keys. The digest and the key itself are never returned to the end user; the "external" identification must be made by name and tenant only.
Expired keys cannot be used for authentication. Use APIKey.IsValid to verify its validity.
type APIKeyConflicts ¶ added in v0.16.0
APIKeyConflicts holds API keys attributes that must be unique for each item (per tenant ID) and can be utilized in queries to identify conflicts.
type AccessPolicy ¶
type AccessPolicy struct {
ID string `json:"id"`
TenantID string `json:"-"`
Name string `json:"name"`
Subject PolicySubject `json:"subject"`
Filter PublicKeyFilter `json:"filter"`
// Logins are the unix logins this policy covers: exact names, or ["*"] for
// any login.
Logins []string `json:"logins"`
// SourceIP restricts the policy to connections from these CIDRs (a client IP
// in any of them matches). Empty matches any IP. A single host is a /32 (or
// /128 for IPv6).
SourceIP []string `json:"source_ip"`
// Action is whether this policy grants (allow) or blocks (deny) the covered
// access. Defaults to allow.
Action PolicyAction `json:"action"`
// RequireReauth gates access granted by this policy on a fresh
// re-authentication (an out-of-band confirmation), even when the connecting
// key is already an identity. Off by default; the identity alone is the norm.
RequireReauth bool `json:"require_reauth"`
// ReauthPeriod is the freshness window for RequireReauth, in seconds: a
// re-authentication is only demanded when the identity has not re-authed
// within it. The freshness belongs to the identity, not to a connection, so
// within the window every login with that key goes straight through. nil or 0
// means "always", the only setting that is genuinely per login. Only
// meaningful when RequireReauth is set.
ReauthPeriod *int `json:"reauth_period"`
// SubjectMatches reports whether anything Subject names could reach a device. A policy for
// which nothing can is inert: an allow grants nothing and a deny blocks nothing. A subject
// may name nobody at all, or name only principals Authorize refuses before it reads a
// policy: a member whose role lacks authorizer.DeviceConnect, or an expired API key.
// Computed when a policy is read, never stored, so it follows membership and expiry
// without a write.
SubjectMatches bool `json:"subject_matches"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
AccessPolicy is a namespace-scoped authorization rule for the identity-based SSH access mode: for a subject (user, role, or all members) reaching the devices selected by Filter as the unix logins listed in Logins, it either grants (Effect allow) or blocks (Effect deny) access. Evaluation is default-deny and deny-first: a matching deny wins over any allow, and access is authorized iff some allow grants it and no deny blocks it.
func NewOwnerAccessPolicy ¶
func NewOwnerAccessPolicy(tenantID, ownerID string) *AccessPolicy
NewOwnerAccessPolicy is the starter policy for the identity access mode: it grants the namespace owner every login on every device. Seeded when a namespace is born identity (creation) or switches to identity with no policies (legacy toggle), so default-deny never locks the owner out while every other member starts with no access.
type ActiveSession ¶
type ActiveSession struct {
UID UID `json:"uid"`
LastSeen time.Time `json:"last_seen"`
TenantID string `json:"tenant_id"`
}
ActiveSession is the liveness half of a session, written on the keep-alive path. It is kept apart from Session so a heartbeat does not rewrite the whole record.
type AuthClaims ¶ added in v0.2.0
type AuthClaims struct {
Claims string `json:"claims"`
}
AuthClaims carries the kind of claims a JWT holds ("user", "device"), which decides how the rest of the token is read.
type Billing ¶ added in v0.8.0
type Billing struct {
// CustomerID is the ID of the customer on the payment gateway.
CustomerID string `json:"customer_id"`
// Subscription is the namespace's subscription, or nil when the namespace has a customer but
// never completed checkout. A nil Subscription is not a failed subscription: the namespace
// keeps the free tier, exactly as a namespace with no Billing at all does.
Subscription *BillingSubscription `json:"subscription,omitempty"`
// CreatedAt is the time at which this billing was created.
// It follows the RFC 3339 format, and it is empty on records written before the store began
// to maintain it.
CreatedAt string `json:"created_at,omitempty"`
// UpdatedAt is the time at which this billing was last updated.
// It follows the RFC 3339 format, and it is empty on records written before the store began
// to maintain it.
UpdatedAt string `json:"updated_at,omitempty"`
}
Billing contains information about the ShellHub's subscription.
func NewBilling ¶ added in v0.12.4
NewBilling returns a billing record for a namespace that has a customer on the payment gateway but no subscription yet. The store stamps CreatedAt and UpdatedAt when it writes the record.
func (*Billing) ClearSubscription ¶
func (b *Billing) ClearSubscription()
ClearSubscription detaches the subscription, returning the namespace to the free tier.
func (*Billing) Clone ¶
Clone returns a deep copy, so that a caller can build the next billing state without touching the one the namespace still holds.
func (*Billing) HasCustomer ¶
HasCustomer reports whether the namespace is known to the payment provider. A namespace can have a customer and no subscription — that is a namespace that once paid, or is about to.
func (*Billing) HasSubscription ¶ added in v0.12.4
HasSubscription reports whether a subscription is attached, saying nothing about whether it is paid up. Use IsActive for that.
func (*Billing) IsActive ¶ added in v0.12.4
IsActive indicates whether the namespace has a subscription that grants full service.
func (*Billing) IsNil ¶ added in v0.12.4
IsNil reports whether the receiver is nil, so a caller holding a *Billing can ask without a nil check of its own. The other predicates here are nil-safe for the same reason.
func (*Billing) SetCustomer ¶ added in v0.12.4
SetCustomer records the payment provider's customer id. It panics on a nil receiver, unlike the predicates above.
func (*Billing) SetSubscription ¶ added in v0.12.4
func (b *Billing) SetSubscription(id string, status BillingStatus, currentPeriodEnd int64)
SetSubscription attaches a subscription to the billing, replacing any subscription already there.
func (*Billing) SetSubscriptionStatus ¶
func (b *Billing) SetSubscriptionStatus(status BillingStatus)
SetSubscriptionStatus updates the status of the subscription, and does nothing when the billing has no subscription.
type BillingBlockReason ¶
type BillingBlockReason string
BillingBlockReason names why a billing evaluation denies acceptance or connection.
const ( // BillingBlockedQuota means the namespace uses every device its plan allows. BillingBlockedQuota BillingBlockReason = "quota" // BillingBlockedSubscription means the namespace's subscription denies the operation, // whatever the device count is. BillingBlockedSubscription BillingBlockReason = "subscription" )
type BillingEvaluation ¶ added in v0.12.4
type BillingEvaluation struct {
// CanAccept indicates if the namespace can accept a new device.
CanAccept bool `json:"can_accept"`
// CanConnect indicates if the namespace can create a new connection SSH.
CanConnect bool `json:"can_connect"`
// Blocked names what denies the operation, and is empty when nothing does. The two reasons
// need different answers from the user, so a caller must report them as different errors.
Blocked BillingBlockReason `json:"blocked,omitempty"`
}
BillingEvaluation contains information about the billing evaluation of acceptance and connection. It is used to evaluate if a device can be accepted or a connection SSH can be created. Its idea is simplify the check the state of the namespace when related to billing.
type BillingStatus ¶ added in v0.12.4
type BillingStatus string
BillingStatus represents the status of a subscription on the payment gateway.
https://stripe.com/docs/api/subscriptions/object#subscription_object-status https://stripe.com/docs/billing/subscriptions/overview#subscription-lifecycle
const ( // BillingStatusActive represents active status without any issues. BillingStatusActive BillingStatus = "active" // BillingStatusTrialing represents active status without any issues, but the subscription is in trial period. BillingStatusTrialing BillingStatus = "trialing" // BillingStatusIncomplete represents incomplete status. // If the initial payment attempt fails, the status of the subscription becomes incomplete. // If payment fails because of a card error, such as a decline, the status of the PaymentIntent is // requires_card and the subscription is incomplete. BillingStatusIncomplete BillingStatus = "incomplete" // BillingStatusIncompleteExpired represents incomplete_expired status. // If the first invoice is not paid within 23 hours, the status of the subscription becomes incomplete_expired. BillingStatusIncompleteExpired BillingStatus = "incomplete_expired" // BillingStatusPastDue represents past_due status. // The subscription’s status remains active as long as automatic payments succeed. If automatic payment fails, the // subscription updates to past_due and Stripe attempts to recover payment based on your retry rules. If payment // recovery fails, you can set the subscription status to canceled, unpaid, or leave it past_due. BillingStatusPastDue BillingStatus = "past_due" // BillingStatusCanceled represents canceled status. BillingStatusCanceled BillingStatus = "canceled" // BillingStatusUnpaid represents unpaid status. // If the retry attempts are exhausted, the status of the subscription becomes unpaid, depending on your subscriptions settings. BillingStatusUnpaid BillingStatus = "unpaid" // BillingStatusPaused represents paused status. BillingStatusPaused BillingStatus = "paused" // BillingStatusToCancelAtEndOfPeriod represents to_cancel_at_end_of_period status. // BillingStatusToCancelAtEndOfPeriod is not a Stripe status, but a custom status used by this package to indicate that the subscription is set to cancel at the end of the period. BillingStatusToCancelAtEndOfPeriod BillingStatus = "to_cancel_at_end_of_period" )
Represents the possible statuses of a subscription.
There is no "no subscription" status: a namespace that never completed checkout carries a Billing with a nil Subscription instead.
func (BillingStatus) IsActive ¶ added in v0.12.4
func (s BillingStatus) IsActive() bool
IsActive returns true if the subscription grants full service.
`past_due` counts as active because Stripe is still retrying the payment, and `to_cancel_at_end_of_period` still has a paid period left to run.
type BillingSubscription ¶
type BillingSubscription struct {
// ID is the ID of the subscription on the payment gateway.
ID string `json:"id"`
// Status is the current status of the subscription.
Status BillingStatus `json:"status"`
// CurrentPeriodEnd is the end of the current period.
CurrentPeriodEnd int64 `json:"current_period_end"`
}
BillingSubscription is the namespace's subscription on the payment gateway.
type Decision ¶
type Decision struct {
Allowed bool
// RequireReauth is set when access is allowed by a policy that carries the
// re-auth flag; the gateway must run a fresh re-authentication before
// proceeding, subject to ReauthPeriod.
RequireReauth bool
// ReauthPeriod is the matched policy's freshness window in seconds (nil/0 =
// always). The gateway skips the re-auth when the identity re-authed within
// it. Only meaningful when RequireReauth is set.
ReauthPeriod *int
// Reason is the cause of the refusal, empty when Allowed.
Reason DenialReason
// PolicyName is the policy that produced the refusal. Set for
// ReasonDeniedByPolicy and ReasonPolicyUnevaluable only.
PolicyName string
// Login is the login the request asked for. Set for ReasonNoGrant only.
Login string
}
Decision is the outcome of an Access Policy authorization check.
type DenialReason ¶
type DenialReason string
DenialReason is the cause of a denied Decision, as a stable value operators can count and filter on. The human sentence is derived from it (see Decision.Message), never the other way round, so rewording never breaks a query.
const ( // ReasonNotAMember refuses because the user does not belong to the device's // namespace, so no policy in it could ever apply to them. ReasonNotAMember DenialReason = "not_a_member" // ReasonDeniedByPolicy refuses because a deny policy matched. Carries PolicyName. ReasonDeniedByPolicy DenialReason = "denied_by_policy" // ReasonPolicyUnevaluable refuses because a deny policy could not be evaluated, // and evaluation fails closed. Carries PolicyName. ReasonPolicyUnevaluable DenialReason = "policy_unevaluable" // ReasonNoGrant refuses because no allow policy grants the requested login on // the device, leaving default-deny to stand. Carries Login. ReasonNoGrant DenialReason = "no_grant" // ReasonRoleCannotConnect refuses because the member's namespace role does not hold // authorizer.DeviceConnect, which no policy can grant. An API key never reaches it: // it holds no membership, so Authorize skips the role check and goes straight to // the policies. ReasonRoleCannotConnect DenialReason = "role_cannot_connect" // ReasonKeyExpired refuses because the API key the connection acts as has expired. // Deleting a key removes the identities it enrolled; expiry does not, so the key's // own validity has to be asked at connect time. ReasonKeyExpired DenialReason = "key_expired" )
type Device ¶
type Device struct {
// UID is the unique identifier for a device.
UID string `json:"uid"`
CreatedAt time.Time `json:"created_at"`
RemovedAt *time.Time `json:"removed_at"`
Name string `json:"name" validate:"required,device_name"`
Identity *DeviceIdentity `json:"identity"`
Info *DeviceInfo `json:"info"`
PublicKey string `json:"public_key"`
TenantID string `json:"tenant_id"`
// LastSeen represents the timestamp of the most recent ping from the device to the server.
LastSeen time.Time `json:"last_seen"`
// DisconnectedAt stores the timestamp when the device disconnected from the server.
// When nil, it indicates the device is potentially online.
//
// Due to potential network issues, this field might be nil even when the device
// is actually offline. For reliable connection status, check both this and
// [Device.LastSeen] fields.
DisconnectedAt *time.Time `json:"-"`
// Online indicates whether the device is currently connected. This field is not
// persisted to the database but is computed based on both [Device.LastSeen] and
// [Device.DisconnectedAt] fields to determine the current connection status.
Online bool `json:"online"`
Namespace string `json:"namespace"`
Status DeviceStatus `json:"status" validate:"oneof=accepted rejected pending unused"`
StatusUpdatedAt time.Time `json:"status_updated_at"`
RemoteAddr string `json:"remote_addr"`
Position *DevicePosition `json:"position"`
Acceptable bool `json:"acceptable"`
CustomFields map[string]string `json:"custom_fields,omitempty"`
// Ephemeral reports whether the device was enrolled with an ephemeral provisioning key and should be
// removed automatically once it stays offline past EphemeralTimeout.
Ephemeral bool `json:"ephemeral"`
// EphemeralTimeout is how many minutes the device may stay offline before removal, copied from
// the provisioning key at enrollment. Only meaningful when Ephemeral is true.
EphemeralTimeout int `json:"ephemeral_timeout,omitempty"`
// ProvisioningKeyID is the digest of the provisioning key the device enrolled with (a real key or the
// namespace's legacy key). It attributes the device to its enrollment source.
ProvisioningKeyID string `json:"provisioning_key_id,omitempty"`
// OwnerID is the member who paired the device. The device leaves the namespace when they lose
// the ability to accept devices in it. Empty for a team device, which leaves with nobody.
OwnerID string `json:"owner_id,omitempty"`
// LastEnrollmentAttemptAt is when the enrollment policy was last (re-)evaluated for the device. It
// throttles reconciliation of a still-pending enrollment on the agent's periodic AuthDevice. Nil
// until the first re-evaluation.
LastEnrollmentAttemptAt *time.Time `json:"last_enrollment_attempt_at,omitempty"`
Taggable `json:",inline"`
}
Device is an enrolled machine. Its UID is derived from the identity the agent presents, so two agents presenting the same identity are the same device rather than two of them.
type DeviceAuth ¶
type DeviceAuth struct {
Hostname string `json:"hostname,omitempty" validate:"required_without=Identity,omitempty,hostname_rfc1123" hash:"-"`
Identity *DeviceIdentity `json:"identity,omitempty" validate:"required_without=Hostname,omitempty"`
PublicKey string `json:"public_key"`
TenantID string `json:"tenant_id"`
// ProvisioningKey is an optional provisioning key presented at install time to auto-accept the device. It is
// excluded from the UID hash so it never changes a device's identity.
ProvisioningKey string `json:"provisioning_key,omitempty" hash:"-"`
}
DeviceAuth is the part of an authentication request the device's UID is hashed from. A field tagged hash:"-" is deliberately outside that hash: changing it must not give the agent a new identity.
type DeviceAuthRequest ¶
type DeviceAuthRequest struct {
Info *DeviceInfo `json:"info"`
Sessions []string `json:"sessions,omitempty"`
*DeviceAuth
}
DeviceAuthRequest is what an agent sends to enroll or to re-authenticate. It repeats on every agent restart, so handling it must be idempotent.
type DeviceAuthResponse ¶
type DeviceAuthResponse struct {
UID string `json:"uid"`
Token string `json:"token"`
Name string `json:"name"`
Namespace string `json:"namespace"`
// TenantID is the namespace the device was enrolled into. An agent that authenticated with a
// provisioning key alone learns its namespace here, having had no tenant to send. Additive and
// optional: older agents that don't read it are unaffected.
TenantID string `json:"tenant_id,omitempty"`
// Status is the device's enrollment status after this auth (accepted/pending/rejected). It lets a
// current agent react to its authorization state (e.g. stop opening the tunnel when not accepted)
// instead of connecting blind. Additive and optional: older agents that don't read it are
// unaffected.
Status DeviceStatus `json:"status,omitempty"`
// Config holds device-specific configuration settings.
// This can include various parameters that the device needs to operate correctly.
// The structure of this map can vary depending on the device type and its requirements.
// Example configurations might include network settings, operational modes, or feature toggles.
// It's designed to be flexible to accommodate different device needs.
Config map[string]any `json:"config,omitempty"`
}
DeviceAuthResponse is what an agent receives on a successful authentication: the token it authenticates with from then on, and the identity the server assigned it.
type DeviceAuthStatus ¶
type DeviceAuthStatus struct {
Status DeviceStatus `json:"status"`
}
DeviceAuthStatus is the device's current status as reported to the device itself while it waits for acceptance.
type DeviceConflicts ¶ added in v0.19.0
type DeviceConflicts struct {
Name string
}
DeviceConflicts holds user attributes that must be unique for each itam and can be utilized in queries to identify conflicts.
func (*DeviceConflicts) Distinct ¶ added in v0.19.0
func (c *DeviceConflicts) Distinct(device *Device)
Distinct removes the c's attributes whether it's equal to the device attribute.
type DeviceIdentity ¶ added in v0.2.1
type DeviceIdentity struct {
MAC string `json:"mac"`
}
DeviceIdentity is the hardware identity an agent claims. It feeds the device's UID, so a machine that keeps its MAC keeps its device across reinstalls.
type DeviceInfo ¶ added in v0.2.1
type DeviceInfo struct {
ID string `json:"id"`
PrettyName string `json:"pretty_name"`
Version string `json:"version"`
Arch string `json:"arch"`
Platform string `json:"platform"`
}
DeviceInfo is what the agent reports about the operating system it runs on. It is descriptive only: nothing authorizes on it, and the agent is free to change it between authentications.
type DeviceLoginCode ¶
DeviceLoginCode is a short-lived code that deep-links a pending device into the console's accept page. It carries no authority by itself: accepting the device still requires an authenticated user with the DeviceAccept permission in the device's namespace.
type DeviceLoginCodePreview ¶
type DeviceLoginCodePreview struct {
Kind string `json:"kind"`
UID string `json:"uid,omitempty"`
Name string `json:"name"`
Identity *DeviceIdentity `json:"identity"`
Info *DeviceInfo `json:"info"`
Namespace string `json:"namespace,omitempty"`
TenantID string `json:"tenant_id,omitempty"`
Status DeviceStatus `json:"status,omitempty"`
}
DeviceLoginCodePreview is what an authenticated user sees when resolving a device login code before accepting the device. For pairing codes the device does not exist yet, so UID, Namespace, TenantID and Status are empty.
type DevicePairing ¶
type DevicePairing struct {
Code string `json:"code,omitempty"`
ExpiresIn int `json:"expires_in_seconds,omitempty"`
Status DeviceStatus `json:"status"`
TenantID string `json:"tenant_id,omitempty"`
}
DevicePairing is the response to a pairing creation request. When the device (identified by its public key) was already accepted into a namespace, the server resolves it immediately: Status is "accepted" and TenantID is set, so the agent learns its tenant without waiting on a code. Otherwise a Code is returned to poll.
type DevicePairingAccepted ¶
type DevicePairingAccepted struct {
UID string `json:"uid"`
TenantID string `json:"tenant_id"`
Namespace string `json:"namespace"`
OwnerID string `json:"owner_id,omitempty"`
}
DevicePairingAccepted is the response to a pairing accept request.
type DevicePairingRequest ¶
type DevicePairingRequest struct {
Hostname string `json:"hostname,omitempty"`
Identity *DeviceIdentity `json:"identity,omitempty"`
Info *DeviceInfo `json:"info"`
PublicKey string `json:"public_key"`
Code string `json:"code,omitempty"`
}
DevicePairingRequest is the identity payload a tenant-less agent submits to start a pairing. It mirrors the fields of a device auth request minus the tenant, which the user chooses at accept time.
Code carries a pre-authorized pairing code the agent was given at install time. When set, the server claims it and accepts the device into the pre-authorized namespace instead of returning a code to poll.
type DevicePairingStatus ¶
type DevicePairingStatus struct {
Status DeviceStatus `json:"status"`
TenantID string `json:"tenant_id,omitempty"`
UID string `json:"uid,omitempty"`
Name string `json:"name,omitempty"`
}
DevicePairingStatus is what a tenant-less agent — or the console page that minted a pre-authorized code — polls while waiting for the device to be accepted. TenantID is set once accepted; UID and Name identify the resulting device so the console can link straight to it.
type DevicePosition ¶ added in v0.8.0
type DevicePosition struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
DevicePosition is the device's geolocation, resolved from the address it connected from. It is a guess from a GeoIP database, not something the device reports.
type DeviceStatus ¶ added in v0.11.8
type DeviceStatus string
DeviceStatus is where a device sits in the enrollment lifecycle. It decides whether the device may open a tunnel and whether it counts against the namespace's device limit.
const ( // DeviceStatusAccepted is a device allowed to connect. Only accepted devices count against a // namespace's device limit. DeviceStatusAccepted DeviceStatus = "accepted" // DeviceStatusPending is a device that has authenticated but is waiting for a member to accept it. DeviceStatusPending DeviceStatus = "pending" // DeviceStatusRejected is a device a member turned away. It may re-authenticate, and stays // rejected until someone changes that. DeviceStatusRejected DeviceStatus = "rejected" // DeviceStatusRemoved is a device deleted from the namespace. It frees its slot against the // device limit immediately, and the row is kept so the same machine returning is recognised. DeviceStatusRemoved DeviceStatus = "removed" // DeviceStatusUnused is an accepted device that has never connected. DeviceStatusUnused DeviceStatus = "unused" // DeviceStatusEmpty is the zero value, which matches every status in a query rather than none. DeviceStatusEmpty DeviceStatus = "" )
type DeviceTag ¶ added in v0.14.0
type DeviceTag struct {
Tag string `validate:"required,min=3,max=255,alphanum,ascii,excludes=/@&:"`
}
DeviceTag is a single tag as it is validated, which is where the character restrictions live — a tag ends up in an SSH address, so "/@&:" would make one ambiguous.
func NewDeviceTag ¶ added in v0.14.0
NewDeviceTag wraps a raw tag for validation. It does not validate on its own: pass the result to the validator.
type Endpoints ¶
Endpoints are the addresses a client connects back on, as the server sees itself from outside — behind a reverse proxy they come from configuration, not from the request.
type Filter ¶ added in v0.3.0
type Filter struct {
// Type os the filter. Type can be "property" or "operator". When Type is "property", the Params field must is set
// to PropertyParams structure and when set "operator", the Params field must be set to OperatorParams structure.
Type string `json:"type,omitempty"`
// Params is the filter params. Params can be either PropertyParams or OperatorParams.
Params any `json:"params,omitempty"`
}
Filter is one node of a query filter as it arrives from a client. It is a tagged union: Type picks which shape Params holds, and UnmarshalJSON is what enforces the pairing — build one by unmarshalling rather than by hand, or Params ends up a map instead of a params struct.
func (*Filter) UnmarshalJSON ¶ added in v0.3.2
UnmarshalJSON decodes Params into the struct Type names. An unrecognized Type is not an error: it leaves Params nil, so a filter the server does not understand narrows nothing rather than failing the request.
type FirewallConnection ¶
type FirewallConnection struct {
// Namespace is the namespace name, not its tenant ID.
Namespace string `json:"namespace"`
// Hostname is the device name within the namespace.
Hostname string `json:"hostname"`
// Username is the user being requested on the device, not the ShellHub user.
Username string `json:"username"`
IPAddress string `json:"ip_address"`
}
FirewallConnection describes the SSH connection attempt a firewall rule matches against.
type ID ¶ added in v0.5.0
type ID struct {
ID string
}
ID carries the authenticated user's id as the API's gateway reads it out of a request's context. It is a struct rather than a string so a missing id is a nil pointer, not "".
type Info ¶ added in v0.2.1
Info is what an unauthenticated client reads to learn the server's version and where to reach it, so it is the one payload that must stay stable across upgrades.
type InstanceAPIKey ¶
type InstanceAPIKey struct {
// ID is the unique identifier of the key. It is a SHA256 hash of the prefixed plaintext key.
ID string `json:"-"`
// Name is an external identifier for a given key. It is unique across the instance.
Name string `json:"name"`
// CreatedBy is the ID of the instance administrator who created the key.
CreatedBy string `json:"created_by"`
// CreatedAt is the creation date of the key.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is the last update date of the key.
UpdatedAt time.Time `json:"updated_at"`
// ExpiresAt is the expiration date of the key. Unlike [APIKey], it is always set: an instance
// key cannot be created without one.
ExpiresAt time.Time `json:"expires_at"`
}
InstanceAPIKey authenticates a request as an instance administrator. Unlike APIKey it carries neither a namespace nor a role, and it is honoured only on the admin surface.
The ID is a SHA256 digest of the plaintext key and is never returned to the end user, so a key is identified externally by its name alone. A key stops authenticating when it expires or when the user in CreatedBy is no longer an instance administrator; use InstanceAPIKey.IsValid for the former.
func (*InstanceAPIKey) IsValid ¶
func (a *InstanceAPIKey) IsValid() bool
IsValid reports whether the key has not expired yet.
type Member ¶ added in v0.5.0
type Member struct {
ID string `json:"id,omitempty"`
AddedAt time.Time `json:"added_at"`
Email string `json:"email" validate:"email"`
Role authorizer.Role `json:"role" validate:"required,oneof=administrator operator observer"`
// AccountStatus is the member's underlying user account status (confirmed or
// not-confirmed). A not-confirmed member still has to finish setting up their account. It
// is the account status, not the membership-invitation status (accepted/pending), which is
// a separate concept.
AccountStatus UserStatus `json:"account_status,omitempty"`
// AwaitingApproval mirrors the member's user account flag: true while a namespace admin
// provisioned them but a system admin has not approved the account yet. The account cannot
// sign in until an admin approves it.
AwaitingApproval bool `json:"awaiting_approval,omitempty"`
}
Member ties a user to a namespace with a role. It is the only thing that grants a user access to a namespace, and it denormalizes enough of the user row that authorization needs no second query.
type MemberDeparted ¶
type MemberDeparted struct {
TenantID string
MemberID string
// RemovedDevices are the UIDs of the devices that left with the member.
RemovedDevices []string
// APIKeyDigests are the digests of the API keys the member had created in the namespace.
APIKeyDigests []string
}
MemberDeparted is what ending a member's standing in a namespace removed from the database, which the caller still has to evict from the tunnels and caches once the transaction commits.
type MemberView ¶
type MemberView struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Username string `json:"username,omitempty"`
Email string `json:"email"`
Role authorizer.Role `json:"role"`
// Status is MemberStatusActive or MemberStatusAwaitingApproval.
Status string `json:"status"`
AddedAt time.Time `json:"added_at"`
}
MemberView is the enriched, list-friendly member representation returned by GET /api/namespaces/members. Unlike Member it carries the user's name/username and a flattened account Status, joining the users table.
type MembershipInvitation ¶ added in v0.21.4
type MembershipInvitation struct {
ID string `json:"-"`
TenantID string `json:"-"`
UserID string `json:"-"`
InvitedBy string `json:"invited_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ExpiresAt *time.Time `json:"expires_at"`
Status MembershipInvitationStatus `json:"status"`
StatusUpdatedAt time.Time `json:"status_updated_at"`
Role authorizer.Role `json:"role"`
Invitations int `json:"-"`
// Sig is the one-time signature that ties the invitation link to this row. It
// replaces the former Redis "invite={sig}" token; validity is the row's ExpiresAt.
Sig string `json:"-"`
// NamespaceName isn't saved on the database
NamespaceName string `json:"-"`
// UserEmail isn't saved on the database
UserEmail string `json:"-"`
}
MembershipInvitation is a pending offer of membership in a namespace. It becomes a Member only when accepted; until then it grants nothing. Sig is what makes the invitation link usable, so it is never serialized.
func (MembershipInvitation) IsExpired ¶ added in v0.21.4
func (m MembershipInvitation) IsExpired() bool
IsExpired reports whether the invitation is past its deadline. An invitation with no ExpiresAt never expires.
func (MembershipInvitation) IsPending ¶ added in v0.21.4
func (m MembershipInvitation) IsPending() bool
IsPending reports whether the invitation is unanswered, which is not the same as usable — an expired invitation is still pending.
type MembershipInvitationNotification ¶
type MembershipInvitationNotification struct {
// Signature is the invitation's one-time signature; the accept-invite link is keyed by it.
Signature string `json:"signature"`
// ExpiresAt is when the invitation stops resolving, shown to the recipient as the link expiry.
ExpiresAt time.Time `json:"expires_at"`
// RecipientEmail is the invited address, already lowercased.
RecipientEmail string `json:"recipient_email"`
// RecipientName is the invitee's display name, empty for a not-yet-registered invitee (exactly
// as before).
RecipientName string `json:"recipient_name"`
// ForwardedProto and ForwardedHost come from the originating request's X-Forwarded-* headers and
// build the accept-invite link in the email.
ForwardedProto string `json:"forwarded_proto"`
ForwardedHost string `json:"forwarded_host"`
}
MembershipInvitationNotification is the typed, email-relevant snapshot of a membership invitation event. It is assembled once by the membership-intake flow and carried — via the OnMembershipInvited hook and the internal client — to the worker that renders and sends the invitation email, which reads it without a single store round-trip.
It is the single contract across the shellhub↔cloud seam: JSON-encoded over the worker's []byte transport, replacing the former positional colon-delimited string. It deliberately carries only what the email template consumes — not the role or namespace name, which the template uses neither of.
type MembershipInvitationStatus ¶ added in v0.21.4
type MembershipInvitationStatus string
MembershipInvitationStatus is where an invitation sits. Expiry is not one of the values: an expired invitation stays pending, and IsExpired answers separately.
const ( // MembershipInvitationStatusPending is an invitation nobody has answered. It is also the status // of an expired invitation, so check IsExpired before treating one as open. MembershipInvitationStatusPending MembershipInvitationStatus = "pending" // MembershipInvitationStatusAccepted is an invitation the invitee took, which is what created // their membership. MembershipInvitationStatusAccepted MembershipInvitationStatus = "accepted" // MembershipInvitationStatusRejected is an invitation the invitee declined. MembershipInvitationStatusRejected MembershipInvitationStatus = "rejected" // MembershipInvitationStatusCancelled is an invitation the namespace withdrew before it was // answered. MembershipInvitationStatusCancelled MembershipInvitationStatus = "cancelled" )
type Namespace ¶ added in v0.5.0
type Namespace struct {
Name string `json:"name" validate:"required,hostname_rfc1123,excludes=.,lowercase"`
Owner string `json:"owner"`
TenantID string `json:"tenant_id"`
Members []Member `json:"members"`
Settings *NamespaceSettings `json:"settings"`
Devices int `json:"-"`
DevicesAcceptedCount int64 `json:"devices_accepted_count"`
DevicesPendingCount int64 `json:"devices_pending_count"`
DevicesRejectedCount int64 `json:"devices_rejected_count"`
DevicesRemovedCount int64 `json:"devices_removed_count"`
Sessions int `json:"-"`
MaxDevices int `json:"max_devices"`
CreatedAt time.Time `json:"created_at"`
Billing *Billing `json:"billing"`
Type Type `json:"type"`
}
Namespace is a tenant: the unit devices, members and billing all hang off. Its TenantID is what every scoped query filters on, and its Name is what an SSH address resolves through, so the two are equally load-bearing and only the name may change.
func (*Namespace) DeviceLimit ¶
func (n *Namespace) DeviceLimit() NamespaceDeviceLimit
DeviceLimit returns the namespace's ceiling paired with the count it is measured against, so a caller asking both questions reads one consistent snapshot.
func (*Namespace) FindMember ¶ added in v0.14.0
FindMember checks if a member with the specified ID exists in the namespace.
func (*Namespace) HasMaxDevices ¶ added in v0.11.8
HasMaxDevices reports whether the namespace has a finite device ceiling at all. Community instances and unbilled namespaces carry -1, which is no ceiling.
func (*Namespace) HasMaxDevicesReached ¶ added in v0.11.8
HasMaxDevicesReached reports whether the ceiling is used up. Ask HasMaxDevices first: with no ceiling this compares against -1 and answers true.
type NamespaceConflicts ¶ added in v0.20.1
type NamespaceConflicts struct {
Name string
}
NamespaceConflicts holds namespace attributes that must be unique for each document and can be utilized in queries to identify conflicts.
func (*NamespaceConflicts) Distinct ¶ added in v0.20.1
func (c *NamespaceConflicts) Distinct(namespace *Namespace)
Distinct removes the c attributes whether it's equal to the namespace attribute.
type NamespaceDeviceLimit ¶
NamespaceDeviceLimit is a namespace's device ceiling and the accepted-device count it is measured against, carrying the rule so it stays defined once.
func (NamespaceDeviceLimit) HasMax ¶
func (l NamespaceDeviceLimit) HasMax() bool
HasMax reports whether the namespace has a finite device ceiling.
Generally, a namespace has a MaxDevices value greater than 0 when the ShellHub is either in community version or the namespace does not have a billing plan enabled, because, in this case, we set this value to -1.
func (NamespaceDeviceLimit) IsReached ¶
func (l NamespaceDeviceLimit) IsReached() bool
IsReached reports whether the accepted-device count has caught up with the ceiling. Only counts accepted devices. Removed devices no longer count towards the limit, allowing immediate slot reuse after deletion.
type NamespaceSettings ¶ added in v0.5.0
type NamespaceSettings struct {
SessionRecord bool `json:"session_record"`
ConnectionAnnouncement string `json:"connection_announcement"`
// SSHAccessMode selects the SSH authorization model for the namespace. In
// "identity" mode every SSH login is gated on an out-of-band browser approval
// (no device credential required) and governed by Access Policies; the legacy
// key ACL and firewall checks are bypassed. "legacy" keeps the key/firewall
// behavior unchanged. New namespaces are born "identity"; namespaces that
// predate identity-first default to "legacy".
SSHAccessMode string `json:"ssh_access_mode"`
// SSHLegacyAllowed marks a namespace that predates identity-first
// (grandfathered): only these may switch the SSH access mode back to legacy.
// Namespaces born identity have it false and can never leave identity mode.
SSHLegacyAllowed bool `json:"ssh_legacy_allowed"`
}
NamespaceSettings is the per-namespace policy the SSH gateway reads on every connection. A nil Settings on a Namespace means the defaults, not "everything off".
func (*NamespaceSettings) IsIdentityAccess ¶
func (s *NamespaceSettings) IsIdentityAccess() bool
IsIdentityAccess reports whether the namespace uses the identity-based SSH access mode. It is nil-safe so call sites can use it without a prior guard.
type OperatorParams ¶ added in v0.3.2
type OperatorParams struct {
Name string `json:"name"`
}
OperatorParams joins the filters around it ("and", "or"), so it is what makes a filter list a tree rather than a conjunction.
type PolicyAction ¶
type PolicyAction string
PolicyAction is whether an Access Policy grants access (allow) or blocks it (deny).
const ( // PolicyActionAllow grants access to the subject; the default. PolicyActionAllow PolicyAction = "allow" // PolicyActionDeny blocks access. Deny is evaluated before allow and wins // over any allow, however specific: it is a subtractive blocklist carved out // of the broad grants, not a base layer (default-deny already blocks the rest). PolicyActionDeny PolicyAction = "deny" )
type PolicySubject ¶
type PolicySubject struct {
Type PolicySubjectType `json:"type"`
Value string `json:"value"`
}
PolicySubject identifies who an Access Policy grants access to.
type PolicySubjectType ¶
type PolicySubjectType string
PolicySubjectType enumerates who an Access Policy grants access to.
const ( // PolicySubjectUser grants a single user, identified by user id in Value. PolicySubjectUser PolicySubjectType = "user" // PolicySubjectRole grants every member holding a role, named in Value. PolicySubjectRole PolicySubjectType = "role" // PolicySubjectAPIKey grants a single API key, identified by its id in Value. An // automation is not a member, so all-members and role never reach one. PolicySubjectAPIKey PolicySubjectType = "api-key" // PolicySubjectAllMembers grants every member of the namespace; Value is empty. PolicySubjectAllMembers PolicySubjectType = "all-members" )
type Principal ¶
type Principal struct {
Kind PrincipalKind `json:"kind"`
ID string `json:"id"`
}
Principal is who a request or a connection is acting as. Carrying the kind beside the id is what keeps a caller from having to guess which table the id belongs to.
func PrincipalOf ¶
PrincipalOf is the principal a row carrying both ids acts as: the API key when one is set, since a key acts on behalf of the member who created it and is the credential that was presented, and otherwise the user. It is nil when neither is set.
type PrincipalKind ¶
type PrincipalKind string
PrincipalKind is what sort of thing acted: a person, or an automation. It is deliberately not the membership role. The role says what a principal may do; the kind says what it is, and the two were the same field once, which is the mistake this type exists to prevent.
const ( // PrincipalUser is a person, a row in users, authorized by their membership role. PrincipalUser PrincipalKind = "user" // PrincipalAPIKey is an automation, a row in api_keys. It holds no membership and no role // over SSH: where it may connect is decided by access policies alone. PrincipalAPIKey PrincipalKind = "api-key" )
type PrivateKey ¶ added in v0.5.0
type PrivateKey struct {
Data []byte `json:"data"`
Fingerprint string `json:"fingerprint"`
CreatedAt time.Time `json:"created_at"`
}
PrivateKey is a private key the server holds, kept with the fingerprint callers look it up by. Data is the key material itself: never log or serialize a PrivateKey into a response.
type PropertyParams ¶ added in v0.3.2
type PropertyParams struct {
Name string `json:"name"`
Operator string `json:"operator"`
Value any `json:"value"`
}
PropertyParams compares one field against one value — the leaf of a filter tree.
type ProvisioningKey ¶
type ProvisioningKey struct {
// ID is the unique identifier of the provisioning key: the SHA256 digest of the plaintext key. The
// plaintext itself is never returned, but the digest is safe to expose (it can't be reversed) and
// lets a device's provisioning_key_id be matched back to its key.
ID string `json:"id"`
// Name is an external identifier. It is unique per tenant ID, not globally.
Name string `json:"name"`
// TenantID is the provisioning key's namespace ID.
TenantID string `json:"tenant_id"`
// Mode is the enrollment policy applied to devices that enroll with the key.
Mode ProvisioningKeyMode `json:"mode"`
// WebhookURL is the integrator endpoint called at enrollment when Mode is webhook.
WebhookURL string `json:"webhook_url"`
// WebhookSecret signs the webhook request (HMAC-SHA256) so the integrator can trust it. It is
// internal-only and never serialized to clients.
WebhookSecret string `json:"-"`
// AllowedIdentities is the set of device identities accepted when Mode is allowlist, matched
// against the identity the agent claims (DeviceIdentity.MAC), which is the interface address
// only when SHELLHUB_PREFERRED_IDENTITY did not replace it. Anything outside it is rejected.
AllowedIdentities []string `json:"allowed_identities"`
// WebhookTimeout is how long (seconds) the synchronous webhook call may take before failing closed
// to pending. Zero means the default.
WebhookTimeout int `json:"webhook_timeout"`
// WebhookCallbackTTL is how long (seconds) the deferred-decision callback token stays valid. Zero
// means the default.
WebhookCallbackTTL int `json:"webhook_callback_ttl"`
// Reusable reports whether the key may enroll more than one device.
Reusable bool `json:"reusable"`
// UsageLimit caps how many devices may enroll with the key. Zero means unlimited.
UsageLimit int `json:"usage_limit"`
// UsedTimes is how many devices have enrolled with the key.
UsedTimes int `json:"used_times"`
// PendingDevices counts the key's devices still awaiting a decision.
PendingDevices int `json:"pending_devices"`
// LastUsedAt is when a device last enrolled with the key.
LastUsedAt *time.Time `json:"last_used_at"`
// Ephemeral marks devices enrolled with the key for automatic removal once offline past
// EphemeralTimeout.
Ephemeral bool `json:"ephemeral"`
// EphemeralTimeout is how many minutes an ephemeral device may stay offline before removal
// (1-10). Only meaningful when Ephemeral is true.
EphemeralTimeout int `json:"ephemeral_timeout"`
// Tags are the names of the namespace tags applied to devices enrolled with the key.
Tags []string `json:"tags"`
// Revoked reports whether the key has been permanently revoked. Revocation is one-way: a revoked
// key can never enroll again. For a reversible pause, use Disabled instead.
Revoked bool `json:"revoked"`
// Disabled reports whether the key is temporarily paused. Unlike Revoked, it is reversible: a
// disabled key stops enrolling but can be re-enabled at any time.
Disabled bool `json:"disabled"`
// Type discriminates the key's origin: a user-created key, or one of the namespace's two
// auto-managed system keys (legacy, pairing). System keys are always valid and are not presentable
// by an agent; see IsSystem.
Type ProvisioningKeyType `json:"type"`
// KeyEncrypted holds the plaintext key encrypted at rest (AES-GCM), so an admin can reveal it
// later. It is internal-only and never serialized to clients (reveal returns the decrypted value
// through its own endpoint).
KeyEncrypted string `json:"-"`
// KeyHint is a short, non-secret prefix of the plaintext key, used to render a recognizable
// masked fingerprint in the list without exposing the secret.
KeyHint string `json:"key_hint"`
// CreatedBy is the ID of the user who created the key.
CreatedBy string `json:"created_by"`
// CreatedAt is the creation date of the key.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is the last update date of the key.
UpdatedAt time.Time `json:"updated_at"`
// ExpiresAt is the absolute date the key expires. A nil value means the key never expires.
ExpiresAt *time.Time `json:"expires_at"`
}
ProvisioningKey is a reusable, revocable, namespace-scoped credential that decides how a device is enrolled. Its ProvisioningKey.Mode is the policy: a device enrolling with the key lands accepted, pending, or rejected according to the mode. The device inherits the key's tags and is marked ephemeral when the key is.
The plaintext key is returned only once, at creation. Only its SHA256 hash is stored, so the key cannot be recovered afterwards. Use ProvisioningKey.IsValid to verify a key can still enroll.
func (*ProvisioningKey) IsPairing ¶
func (s *ProvisioningKey) IsPairing() bool
IsPairing reports whether this is the namespace's auto-managed pairing key: the source attributed to devices accepted through the tenant-less pairing-code flow.
func (*ProvisioningKey) IsSystem ¶
func (s *ProvisioningKey) IsSystem() bool
IsSystem reports whether this is one of the namespace's auto-managed system keys (legacy or pairing), as opposed to a user-created key. Checked positively (not `!= user`) so a zero-valued Type — an in-memory key built before persistence defaults it to user — reads as a user key.
func (*ProvisioningKey) IsValid ¶
func (s *ProvisioningKey) IsValid() bool
IsValid reports whether the provisioning key can still enroll a device: it must not be revoked, disabled, expired, or overused.
func (*ProvisioningKey) ReconcilableOnAuth ¶
func (s *ProvisioningKey) ReconcilableOnAuth() bool
ReconcilableOnAuth reports whether a still-pending device enrolled with this key should have its enrollment policy re-evaluated on a later AuthDevice. Only webhook and allowlist can leave a device pending on a recoverable condition (a deferred/failed integrator, or an accept blocked by the license limit), so only those are retried; automatic/manual have no such recoverable pending state.
func (*ProvisioningKey) WebhookCallbackTTLOrDefault ¶
func (s *ProvisioningKey) WebhookCallbackTTLOrDefault() int
WebhookCallbackTTLOrDefault returns the deferred-decision token TTL in seconds, clamped and defaulted.
func (*ProvisioningKey) WebhookTimeoutOrDefault ¶
func (s *ProvisioningKey) WebhookTimeoutOrDefault() int
WebhookTimeoutOrDefault returns the synchronous webhook timeout in seconds, clamped to the allowed range and defaulted when unset.
type ProvisioningKeyConflicts ¶
ProvisioningKeyConflicts holds provisioning key attributes that must be unique per tenant ID and can be used in queries to identify conflicts.
type ProvisioningKeyEvent ¶
type ProvisioningKeyEvent struct {
// ID is the unique identifier of the event.
ID string `json:"id"`
// ProvisioningKeyID is the digest of the provisioning key the device enrolled with.
ProvisioningKeyID string `json:"provisioning_key_id"`
// TenantID is the enrolling device's namespace ID.
TenantID string `json:"tenant_id"`
// DeviceUID is the enrolled device's UID at enrollment time.
DeviceUID string `json:"device_uid"`
// Hostname is the enrolled device's hostname at enrollment time.
Hostname string `json:"hostname"`
// Identity is the identity the device claimed at enrollment time, which is what an
// allowlist key is matched against. It may be empty.
Identity string `json:"identity"`
// Info is the enrolled device's system info at enrollment time. It may be nil.
Info *DeviceInfo `json:"info"`
// SourceIP is the device's remote address at enrollment time. It may be empty (a pairing accept
// materializes the device without an IP).
SourceIP string `json:"source_ip"`
// PublicKey is the enrolled device's public key (PEM) at enrollment time. It identifies the exact
// credential: re-keying yields a new key here (and a new device), so it tells re-keyed enrollments
// apart. May be empty for events recorded before this was captured.
PublicKey string `json:"public_key,omitempty"`
// Fingerprint is the SHA256 fingerprint of PublicKey, computed at read time (not stored). Empty when
// PublicKey is absent or unparseable.
Fingerprint string `json:"fingerprint,omitempty"`
// Ephemeral reports whether the key marked the device ephemeral. Ephemeral enrollments are kept in
// the history (audit completeness) so the UI can mark or filter them rather than drop them.
Ephemeral bool `json:"ephemeral"`
// ReRegistration reports whether this was a re-registration of a previously removed device rather
// than a first registration.
ReRegistration bool `json:"re_registration"`
// Timestamp is when the enrollment was recorded.
Timestamp time.Time `json:"timestamp"`
// DeviceStatus is the enrolled device's *current* status (accepted/pending/rejected), joined live
// at list time so the history can offer an accept/reject action. It is empty when the device no
// longer exists (hard-deleted). It is not stored on the event row.
DeviceStatus DeviceStatus `json:"device_status"`
// DecidedStatus and DecidedAt freeze the enrollment's outcome on the event: the terminal status
// (accepted/rejected) and when it was set. They are stamped once, when the device is accepted or
// rejected, so the audit survives the device being removed (the live status can't). Nil/empty while
// the enrollment is still pending.
DecidedStatus DeviceStatus `json:"decided_status,omitempty"`
DecidedAt *time.Time `json:"decided_at,omitempty"`
// IsCurrent reports whether this is the device's newest enrollment event. A device removed and
// re-registered shares one device row across several events, so the live status/action belongs to
// the newest one alone; older events are historical. Computed at read time; drives the accept/reject
// action only (the decision itself is frozen per-event above).
IsCurrent bool `json:"is_current"`
}
ProvisioningKeyEvent is one row in a provisioning key's append-only enrollment history: it records a single device enrolling with the key. The device facts are captured and denormalized at enrollment time so the audit survives a later device rename or removal (including ephemeral devices, which are auto-removed). The enrollment facts are immutable; the outcome (DecidedStatus/DecidedAt) is stamped once when the device is accepted/rejected. Rows are never deleted by the application.
type ProvisioningKeyMode ¶
type ProvisioningKeyMode string
ProvisioningKeyMode is the per-key enrollment policy: it decides a device's initial status when the device enrolls with the key.
const ( // ProvisioningKeyModeAutomatic accepts the device on enrollment (the classic provisioning-key behavior). ProvisioningKeyModeAutomatic ProvisioningKeyMode = "automatic" // ProvisioningKeyModeManual lands the device pending for manual review. The legacy/system key is always // this mode. ProvisioningKeyModeManual ProvisioningKeyMode = "manual" // ProvisioningKeyModeWebhook defers the decision to an integrator's endpoint, called at enrollment. ProvisioningKeyModeWebhook ProvisioningKeyMode = "webhook" // ProvisioningKeyModeAllowlist accepts the device when the identity it presents is in // AllowedIdentities, otherwise rejects it. ProvisioningKeyModeAllowlist ProvisioningKeyMode = "allowlist" )
type ProvisioningKeyType ¶
type ProvisioningKeyType string
ProvisioningKeyType discriminates a key's origin: a user-created key, or one of the two auto-managed system keys every namespace has. The system types are told apart by this field (not by name), and neither is presentable by an agent nor freely editable by a user.
const ( // ProvisioningKeyTypeUser is a normal user-created key. ProvisioningKeyTypeUser ProvisioningKeyType = "user" // ProvisioningKeyTypeLegacy is the tenant-only keyless enrollment source (a device presenting only a // tenant ID, no provisioning key). Manual mode: such devices land pending. ProvisioningKeyTypeLegacy ProvisioningKeyType = "legacy" // ProvisioningKeyTypePairing is the code-pairing enrollment source (a tenant-less agent accepted via its // printed code). Devices accepted through the pairing flow attribute here, not to the legacy key. ProvisioningKeyTypePairing ProvisioningKeyType = "pairing" )
type PublicKey ¶ added in v0.5.0
type PublicKey struct {
Data []byte `json:"data"`
Fingerprint string `json:"fingerprint"`
CreatedAt time.Time `json:"created_at"`
TenantID string `json:"tenant_id"`
PublicKeyFields
}
PublicKey is an SSH key registered in a namespace, and the ACL entry attached to it. Data is the key in wire format; Fingerprint is what the SSH gateway looks it up by during authentication.
type PublicKeyAuthRequest ¶ added in v0.5.0
type PublicKeyAuthRequest struct {
Fingerprint string `json:"fingerprint"`
Data string `json:"data"`
}
PublicKeyAuthRequest is what the SSH gateway sends to have a key challenge signed on behalf of a session.
type PublicKeyAuthResponse ¶ added in v0.5.0
type PublicKeyAuthResponse struct {
Signature string `json:"signature"`
}
PublicKeyAuthResponse carries the signature produced for a PublicKeyAuthRequest.
type PublicKeyFields ¶ added in v0.5.0
type PublicKeyFields struct {
Name string `json:"name"`
Username string `json:"username" validate:"regexp"`
Filter PublicKeyFilter `json:"filter" validate:"required"`
}
PublicKeyFields is the editable part of a public key: who it logs in as and which devices it reaches. The key material itself is not here, so an update can change the rule without touching the key.
func (*PublicKeyFields) Validate ¶ added in v0.6.1
func (p *PublicKeyFields) Validate() error
Validate checks the fields, including that Username and the filter's Hostname compile as regular expressions — they are patterns, not literals, and an uncompilable one would silently match nothing. It does not require them to be anchored: MatchPattern anchors them at match time.
type PublicKeyFilter ¶ added in v0.9.1
type PublicKeyFilter struct {
Hostname string `json:"hostname,omitempty" validate:"required_without=Tags,excluded_with=Tags,regexp"`
Taggable `json:",inline"`
}
PublicKeyFilter contains the filter rule of a Public Key.
A PublicKeyFilter can contain either Hostname, string, or Tags, slice of strings never both. Hostname is a regexp matched against the whole device name; see MatchPattern.
func (PublicKeyFilter) Matches ¶
func (f PublicKeyFilter) Matches(device *Device) (bool, error)
Matches reports whether the given device satisfies the filter. A filter is either a hostname pattern matched against the whole device name (see MatchPattern), or a tag set matched by intersection against the device's tag ids; an empty filter matches every device. It is the shared device-selector matcher used by both the public-key ACL and Access Policies.
The device must already carry its tag ids (Taggable.TagIDs) for the tag branch; callers resolving a device from an agent-sent payload must populate them first, since the agent does not send tag ids.
type PublicKeyUpdate ¶ added in v0.5.0
type PublicKeyUpdate struct {
PublicKeyFields
}
PublicKeyUpdate is what an edit may change: the rule, never the key material. Replacing a key means deleting and re-adding it, so its fingerprint stays the identity.
type RecordedSession ¶ added in v0.4.0
type RecordedSession struct {
UID UID `json:"uid"`
Message string `json:"message"`
TenantID string `json:"tenant_id"`
Time time.Time `json:"time"`
Width int `json:"width"`
Height int `json:"height"`
}
RecordedSession is one frame of a recorded terminal session. Recording is a cloud feature and the type lives there too; this copy exists because migrations reference it, so it cannot move until they no longer do.
type SSHApproval ¶
type SSHApproval struct {
Code string `json:"code"`
TenantID string `json:"tenant_id"`
Kind SSHApprovalKind `json:"kind"`
SessionUID string `json:"session_uid"`
SSHID string `json:"sshid"`
DeviceUID string `json:"device_uid"`
DeviceName string `json:"device_name"`
Username string `json:"username"`
IPAddress string `json:"ip_address"`
Fingerprint string `json:"fingerprint"`
Data []byte `json:"data"`
// ReauthPeriod is the policy's window in seconds, on a reauth approval. Nil or
// zero means the policy asks every time.
ReauthPeriod *int `json:"reauth_period"`
State SSHApprovalState `json:"state"`
// DecidedBy is the account that resolved the approval. On an identity
// approval it is the account the key binds to, and the gateway adopts it as
// the session's identity.
DecidedBy string `json:"decided_by"`
// ConfirmationCode is minted when the approval is confirmed and shown only in
// the console, to the person who confirmed it. They type it at the terminal,
// which is what proves they reached the console rather than merely following
// a link. Empty until confirmed.
ConfirmationCode string `json:"confirmation_code"`
RequestedAt time.Time `json:"requested_at"`
ExpiresAt time.Time `json:"expires_at"`
}
SSHApproval is a decision the SSH gateway parked while it holds a pure-OpenSSH login open, for a member to resolve in the console. The code is its identity and its secret: the gateway shows it on its approval prompt.
type SSHApprovalConfirmation ¶
type SSHApprovalConfirmation struct {
ConfirmationCode string `json:"confirmation_code"`
}
SSHApprovalConfirmation is the answer to confirming an approval: the code the person carries from the console to the terminal their login is waiting at.
type SSHApprovalCreated ¶
type SSHApprovalCreated struct {
Code string `json:"code"`
ExpiresIn int `json:"expires_in_seconds"`
}
SSHApprovalCreated is the response to creating an approval: the short code the gateway prints, and the window the user has to decide.
type SSHApprovalKind ¶
type SSHApprovalKind string
SSHApprovalKind is what confirming an approval actually does. A native SSH login can wait on either, and both act on the identity, not on the session: the session is only registered once the auth pipeline clears.
const ( // SSHApprovalIdentity binds the presented key as a new identity. SSHApprovalIdentity SSHApprovalKind = "identity" // SSHApprovalReauth refreshes the re-auth window of an identity that already // exists, because a policy demands a fresh one. It creates nothing. SSHApprovalReauth SSHApprovalKind = "reauth" )
type SSHApprovalRequest ¶
type SSHApprovalRequest struct {
SSHID string `json:"sshid"`
DeviceName string `json:"device_name"`
Username string `json:"username"`
IPAddress string `json:"ip_address"`
RequestedAt time.Time `json:"requested_at"`
State SSHApprovalState `json:"state"`
// Code echoes the correlation code so the page can display it for the user to
// visually match against their approval prompt (anti-phishing).
Code string `json:"code"`
// Fingerprint is the presented key's fingerprint, shown front-and-center when
// the key is becoming an identity.
Fingerprint string `json:"fingerprint"`
// Kind is what confirming does, and it is what the console branches the whole
// screen on.
Kind SSHApprovalKind `json:"kind"`
// ReauthPeriod lets the console say how long confirming lasts, which is not
// this login: the window is per identity, so other logins with the same key
// skip the browser step until it lapses.
ReauthPeriod *int `json:"reauth_period,omitempty"`
// ExpiresIn is how much of the approval window is left, in seconds.
ExpiresIn int `json:"expires_in_seconds"`
// Namespace names where the key lands. The login carries it in the SSHID, so
// it is not the console's current namespace: a member can approve a key into
// a namespace they are not currently browsing.
Namespace string `json:"namespace"`
}
SSHApprovalRequest is the detail the console renders so the user sees which key and login they are deciding on.
type SSHApprovalState ¶
type SSHApprovalState string
SSHApprovalState is the lifecycle of an approval. There is no stored "expired" state: a row past ExpiresAt reads as unknown, and a cron prunes it later.
const ( // SSHApprovalPending is an approval nobody has answered yet. A pending row past its ExpiresAt is // no longer pending — it is unknown. SSHApprovalPending SSHApprovalState = "pending" // SSHApprovalConfirmed is an approval a member granted; the parked login proceeds. SSHApprovalConfirmed SSHApprovalState = "confirmed" // SSHApprovalRejected is an approval a member denied; the parked login is refused. SSHApprovalRejected SSHApprovalState = "rejected" )
type SSHApprovalStatus ¶
type SSHApprovalStatus struct {
State SSHApprovalState `json:"state"`
UserID string `json:"user_id,omitempty"`
// ConfirmationCode is what the person was shown in the console and has to
// type at the terminal. Empty unless State is confirmed.
ConfirmationCode string `json:"confirmation_code,omitempty"`
}
SSHApprovalStatus is what the SSH gateway reads once, after the client answers its approval prompt. UserID carries the approving account once the decision is made, so the gateway can bind it to the session.
type SSHCommand ¶ added in v0.19.0
type SSHCommand struct {
Command string `json:"command"`
}
SSHCommand is the payload of an "exec" request: the single command line the client asked to run instead of an interactive shell.
type SSHExitStatus ¶ added in v0.19.0
type SSHExitStatus struct {
Status uint32 `json:"status"`
}
SSHExitStatus is the payload of an "exit-status" request: the process's exit code, sent when it ended normally rather than on a signal.
type SSHIdentity ¶
type SSHIdentity struct {
ID string `json:"id"`
TenantID string `json:"-"`
// PrincipalID is the id of the bound principal. Which table it names depends on
// PrincipalType: a person is a row in users, an automation a row in api_keys.
PrincipalID string `json:"principal_id"`
// PrincipalName, PrincipalEmail, and PrincipalType describe the bound
// principal, resolved for the management screen. They are not stored on the
// identity row. PrincipalEmail is empty for anything that is not a person.
PrincipalName string `json:"principal_name"`
PrincipalEmail string `json:"principal_email"`
PrincipalType PrincipalKind `json:"principal_type"`
// Fingerprint is the SSH public key fingerprint in "SHA256:…" form.
Fingerprint string `json:"fingerprint"`
// Data is the OpenSSH public key the fingerprint is derived from.
Data []byte `json:"-"`
// Name is a user label for the key, e.g. "laptop".
Name string `json:"name"`
// Source is how the identity came to exist. It is a label, not a boundary:
// only the approval path is asserted by the server, so nothing may authorize
// on it without moving that decision server-side first.
Source SSHIdentitySource `json:"source"`
CreatedAt time.Time `json:"created_at"`
// LastUsedAt moves on every connect (identity resolution).
LastUsedAt *time.Time `json:"last_used_at"`
// LastReauthAt moves only on a successful re-authentication, so it can gate
// an Access Policy's reauth_period freshness window. Distinct from LastUsedAt.
LastReauthAt *time.Time `json:"last_reauth_at"`
// ExpiresAt, SingleUse, and ConsumedAt are the key's lifecycle: it is dead
// once expired or consumed. Any identity may carry a TTL; only an identity
// an API key owns may be single-use, for a one-shot automated run.
// ExpiresAt nil means it never expires.
ExpiresAt *time.Time `json:"expires_at"`
SingleUse bool `json:"single_use"`
// ConsumedAt is stamped when a single-use key is burned by its one session.
ConsumedAt *time.Time `json:"consumed_at"`
}
SSHIdentity binds an SSH public key to a principal (a person or an API key) within a namespace. In the identity SSH access mode the key is the credential: a connection whose presented key's fingerprint resolves to an identity is recognized as that principal, without a browser step. A fingerprint maps to exactly one identity per namespace (UNIQUE(namespace_id, fingerprint)); the same key may be enrolled in other namespaces, and a principal may hold many keys per namespace.
type SSHIdentitySource ¶
type SSHIdentitySource string
SSHIdentitySource is how an identity came to exist.
const ( // SSHIdentitySourceManual is a public key pasted on the SSH Identities page. SSHIdentitySourceManual SSHIdentitySource = "manual" // SSHIdentitySourceBrowser is the web terminal's own key. It is generated in // the browser and held non-extractably, so it can only ever be presented from // that browser and it dies with the browser's site data. SSHIdentitySourceBrowser SSHIdentitySource = "browser" // SSHIdentitySourceApproval is a key accepted at login, after a native client // offered one the namespace did not know yet. SSHIdentitySourceApproval SSHIdentitySource = "approval" )
type SSHPty ¶ added in v0.19.0
type SSHPty struct {
Term string `json:"term"`
Columns uint32 `json:"columns"`
Rows uint32 `json:"rows"`
Width uint32 `json:"width"`
Height uint32 `json:"height"`
// Not persisted (json:"-"); kept only so gossh.Unmarshal can consume it.
Modelist []byte `json:"-"`
}
SSHPty is the payload of a "pty-req" request, the terminal the client asks for before a shell. It repeats SSHWindowChange's fields rather than embedding it because ssh.Unmarshal maps a flat wire format onto the struct and cannot descend into a nested one.
type SSHPtyOutput ¶ added in v0.19.0
type SSHPtyOutput struct {
Output string `json:"output"`
}
SSHPtyOutput carries the bytes a terminal produced. It has no SSH request of its own — the protocol sends this as channel data — and exists so recorded output can be stored as an event alongside the requests.
type SSHSignal ¶ added in v0.19.0
type SSHSignal struct {
Name uint32 `json:"status"`
Dumped bool `json:"dumped"`
Message string `json:"message"`
Lang string `json:"lang"`
}
SSHSignal is the payload of an "exit-signal" request, sent instead of an exit status when the process was killed. Dumped says whether a core file was written.
type SSHSubsystem ¶ added in v0.19.0
type SSHSubsystem struct {
Subsystem string `json:"subsystem"`
}
SSHSubsystem is the payload of a "subsystem" request, which is how SFTP and friends are asked for by name rather than by command line.
type SSHWindowChange ¶ added in v0.19.0
type SSHWindowChange struct {
Columns uint32 `json:"columns"`
Rows uint32 `json:"rows"`
Width uint32 `json:"width"`
Height uint32 `json:"height"`
}
SSHWindowChange is the payload of a "window-change" request, sent every time the client's terminal is resized. Columns and rows are what the application sees; width and height are pixels and are usually zero.
type Session ¶
type Session struct {
UID string `json:"uid"`
DeviceUID UID `json:"device_uid,omitempty"`
Device *Device `json:"device"`
TenantID string `json:"tenant_id"`
Username string `json:"username"`
// UserID is the ShellHub account that authorized this session via browser
// approval. Empty for password/public-key logins and web-terminal sessions.
// It is not serialized; readers get Principal.
UserID string `json:"-"`
// APIKeyID is the API key this session acts as, when an automation opened it. Like UserID, it
// is written here and read through Principal.
APIKeyID string `json:"-"`
// Principal is who opened the session: the person or the API key that presented the
// credential, projected from UserID and APIKeyID. It is absent, rather than empty, under the
// legacy access model, where a session has no principal by design.
Principal *Principal `json:"principal,omitempty"`
IPAddress string `json:"ip_address"`
StartedAt time.Time `json:"started_at"`
LastSeen time.Time `json:"last_seen"`
Active bool `json:"active"`
Authenticated bool `json:"authenticated"`
Recorded bool `json:"recorded"`
Web bool `json:"web"`
Position SessionPosition `json:"position"`
Events SessionEvents `json:"events"`
}
Session is one SSH connection to one device, live or finished. It is created when the connection is established and outlives it: Active says whether it is still running.
type SessionEvent ¶ added in v0.18.0
type SessionEvent struct {
// Session is the session UID where the event occurred. It is not serialized.
Session string `json:"-"`
// Type of the session. Normally, it is the SSH request name.
Type SessionEventType `json:"type"`
// Timestamp contains the time when the event was logged.
Timestamp time.Time `json:"timestamp"`
// Data is the event's payload. Its shape follows the request: an object for one that carries
// fields, and an empty string for one the protocol defines as having no body, such as shell.
// It is omitted when the event carried no payload at all.
Data any `json:"data,omitempty"`
// Seat is the seat where the event occurred.
Seat int `json:"seat"`
}
SessionEvent represents a session event.
type SessionEventType ¶ added in v0.19.0
type SessionEventType string
SessionEventType names the SSH request an event came from. The values are the wire names from the SSH protocol, not names of our own, so they can be matched against a packet capture.
const ( SessionEventTypePtyOutput SessionEventType = "pty-output" SessionEventTypePtyRequest SessionEventType = "pty-req" SessionEventTypeWindowChange SessionEventType = "window-change" SessionEventTypeExitCode SessionEventType = "exit-code" SessionEventTypeExitStatus SessionEventType = "exit-status" SessionEventTypeExitSignal SessionEventType = "exit-signal" SessionEventTypeEnv SessionEventType = "env" SessionEventTypeShell SessionEventType = "shell" SessionEventTypeExec SessionEventType = "exec" SessionEventTypeSubsystem SessionEventType = "subsystem" SessionEventTypeSignal SessionEventType = "signal" SessionEventTypeTcpipForward SessionEventType = "tcpip-forward" SessionEventTypeAuthAgentReq SessionEventType = "auth-agent-req" )
The event types a session can record. All but pty-output are SSH request names as they appear on the wire; pty-output is ours, carrying the bytes the terminal produced, which SSH itself sends as channel data rather than as a request.
type SessionEvents ¶ added in v0.18.0
type SessionEvents struct {
// Types field is a set of sessions type to simplify the indexing on the database.
Types []string `json:"types"`
// Seats contains a list of seats of events.
Seats []int `json:"seats"`
// First is the type of the event the session opened with, taken from the request types that
// open a channel: pty-req, shell, exec and subsystem. It is empty when the session recorded
// none of them.
First SessionEventType `json:"first,omitempty"`
// Items is the session's timeline, oldest first, carried by the by-uid request alone and
// never by the list. It excludes terminal output and is capped, so it is not necessarily
// every event the session recorded, and it is absent both when there is nothing to show and
// when the read failed.
Items []SessionEvent `json:"items,omitempty"`
}
SessionEvents stores the events registered in a session.
type SessionPosition ¶ added in v0.10.0
type SessionPosition struct {
Longitude float64 `json:"longitude"`
Latitude float64 `json:"latitude"`
}
SessionPosition is where the client connected from, resolved from its address by GeoIP. It is recorded once at session start and not refreshed.
type SessionSeat ¶ added in v0.19.0
type SessionSeat struct {
// ID is the identifier of session's seat.
ID int `json:"id"`
}
SessionSeat stores a session's seat.
type SessionUpdate ¶ added in v0.16.0
type SessionUpdate struct {
Recorded *bool `json:"recorded"`
Authenticated *bool `json:"authenticated"`
}
SessionUpdate is a partial update to a session: a nil field is left alone, which is why every field is a pointer.
type Stats ¶
type Stats struct {
RegisteredDevices int `json:"registered_devices"`
OnlineDevices int `json:"online_devices"`
ActiveSessions int `json:"active_sessions"`
PendingDevices int `json:"pending_devices"`
RejectedDevices int `json:"rejected_devices"`
}
Stats is the dashboard's counter set for one namespace. The device counts partition by status, so registered, pending and rejected do not overlap and do not sum to the namespace's limit.
type Status ¶ added in v0.7.3
type Status struct {
Authenticated bool `json:"authenticated"`
}
Status is the authentication state of a session, as the agent reports it back once the SSH handshake has completed.
type System ¶ added in v0.17.1
type System struct {
Setup bool `json:"setup"`
// InstanceTenantID binds the instance to its namespace in single-namespace (Community)
// deployments. When set, the store refuses any further namespace creation. Enterprise/Cloud
// leave it empty (the store wrapper strips it) to keep multi-tenant behavior.
InstanceTenantID string `json:"instance_tenant_id"`
// Authentication manages the settings for available authentication methods.
Authentication *SystemAuthentication `json:"authentication"`
}
System is the instance-wide configuration, of which exactly one row exists. It is read on paths that must work before any namespace exists, such as setup and login.
type SystemAuthentication ¶ added in v0.18.0
type SystemAuthentication struct {
Local *SystemAuthenticationLocal `json:"local"`
}
SystemAuthentication groups the authentication methods an instance offers. A nil method is disabled, which is why each is a pointer.
type SystemAuthenticationLocal ¶ added in v0.18.0
type SystemAuthenticationLocal struct {
// Enabled indicates whether manual authentication using a username and password is enabled or
// not.
Enabled bool `json:"enabled" bool:"enabled"`
}
SystemAuthenticationLocal configures username-and-password authentication.
type Tag ¶ added in v0.21.0
type Tag struct {
ID string `json:"-"`
TenantID string `json:"tenant_id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Tag is a named label owned by a namespace. Devices reference it by ID, so renaming a tag keeps every device that carries it.
type TagConflicts ¶ added in v0.21.0
type TagConflicts struct {
Name string
}
TagConflicts names the fields that must be unique within a namespace, and is how a store reports which one collided.
type Taggable ¶ added in v0.21.0
type Taggable struct {
// TagIDs contains the IDs of associated tags. It is used only for database storage
// and relationship management. The field is not exposed in JSON responses to keep
// the API focused on meaningful tag data rather than internal identifiers.
TagIDs []string `json:"-"`
// Tags contains the complete Tag objects associated with this resource. This field
// is populated from TagIDs when retrieving data from the database, but is not
// stored directly. It is used only for JSON serialization to provide clients
// with full tag information.
Tags []Tag `json:"tags"`
}
Taggable is an embeddable struct that adds tagging capability to other models.
Example usage:
type Device struct {
Taggable // Embed the Taggable struct
Name string // Other device fields
}
type Tenant ¶ added in v0.3.3
type Tenant struct {
ID string
}
Tenant carries the namespace a request acts on, as the API's gateway reads it out of the request context. It is a struct rather than a string so an absent tenant is a nil pointer, which is the difference between "no namespace" and "the empty namespace".
type Type ¶ added in v0.18.0
type Type string
Type is a namespace's kind. It decides whether the namespace can hold more than one member, and cloud billing reads it to pick a plan.
func NewDefaultType ¶ added in v0.18.0
func NewDefaultType() Type
NewDefaultType returns the type a namespace is created with when the caller does not choose one.
type UID ¶
type UID string
UID identifies a device or a session — the opaque, externally visible identifier a client addresses a resource by, not the store's primary key.
type User ¶
type User struct {
ID string `json:"id,omitempty"`
// Origin specifies the the user's signup method.
Origin UserOrigin `json:"-"`
// ExternalID represents the user's identifier in an external system. It is always empty when [User.Origin]
// is [UserOriginLocal].
ExternalID string `json:"-"`
Status UserStatus `json:"status"`
// MaxNamespaces represents the count of namespaces that the user can owns.
MaxNamespaces int `json:"max_namespaces"`
CreatedAt time.Time `json:"created_at"`
LastLogin time.Time `json:"last_login"`
EmailMarketing bool `json:"email_marketing"`
UserData
// MFA contains attributes related to a user's MFA settings. Use [UserMFA.Enabled] to
// check if MFA is active for the user.
//
// NOTE: MFA is available as a cloud-only feature and must be ignored in community.
MFA UserMFA `json:"mfa"`
Preferences UserPreferences `json:"preferences"`
Password UserPassword `json:"-"`
// Admin indicates whether the user has administrative privileges.
Admin bool `json:"admin"`
// AwaitingApproval marks a provisioned account that a namespace admin created but a system
// admin has not approved yet. While true the account is inert: only an admin can mint its
// activation link. It is set false when an admin creates the account directly or approves it.
AwaitingApproval bool `json:"awaiting_approval"`
}
User is an account on the instance. It exists independently of any namespace: membership is what ties a user to one, so a user with no memberships is valid and simply sees nothing.
type UserAuthIdentifier ¶ added in v0.14.0
type UserAuthIdentifier string
UserAuthIdentifier is an username or email used to authenticate.
func (*UserAuthIdentifier) IsEmail ¶ added in v0.14.0
func (i *UserAuthIdentifier) IsEmail() bool
IsEmail checks if the identifier is an email.
type UserAuthMethod ¶ added in v0.18.0
type UserAuthMethod string
UserAuthMethod is a way a user may authenticate. A user can hold several at once, so this is a set rather than a mode.
const ( // UserAuthMethodLocal indicates that the user can authenticate using an email and password. UserAuthMethodLocal UserAuthMethod = "local" // UserAuthMethodSAML indicates that the user can authenticate using a third-party SAML application. UserAuthMethodSAML UserAuthMethod = "saml" )
func (UserAuthMethod) String ¶ added in v0.18.0
func (a UserAuthMethod) String() string
type UserAuthResponse ¶
type UserAuthResponse struct {
Token string `json:"token"`
User string `json:"user"`
Origin string `json:"origin"`
AuthMethods []UserAuthMethod `json:"auth_methods"`
Name string `json:"name"`
ID string `json:"id"`
Tenant *string `json:"tenant"`
Email string `json:"email"`
RecoveryEmail string `json:"recovery_email"`
Role *authorizer.Role `json:"role"`
MFA bool `json:"mfa"`
MaxNamespaces int `json:"max_namespaces"`
Admin bool `json:"admin"`
}
UserAuthResponse is what a successful login returns: the token the client authenticates with from then on, plus enough of the user and their current namespace for the console to render without a second round trip. Tenant and Role are nil together when the user holds no membership, which is every user between confirming their account and joining a namespace.
type UserData ¶ added in v0.8.0
type UserData struct {
Name string `json:"name" validate:"required,name"`
Username string `json:"username" validate:"required,username"`
Email string `json:"email" validate:"required,email"`
// RecoveryEmail is a custom, non-unique email address that a user can use to recover their account
// when they lose access to all other methods. It must never be equal to [UserData.Email].
//
// NOTE: Recovery email is available as a cloud-only feature and must be ignored in community.
RecoveryEmail string `json:"recovery_email" validate:"omitempty,email"`
}
UserData is the identifying half of a user, split out because these are the fields the API lets a user change and the fields uniqueness is enforced on.
type UserInfo ¶ added in v0.17.0
type UserInfo struct {
// OwnedNamespaces are the namespaces where the user is the owner.
OwnedNamespaces []Namespace
// AssociatedNamespaces are the namespaces where the user is a member.
AssociatedNamespaces []Namespace
}
UserInfo is the namespaces a user can reach, split by whether they own them. A namespace appears in exactly one of the two lists.
type UserInvitation ¶
type UserInvitation struct {
ID string `json:"id"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Invitations int `json:"invitations"`
Status UserInvitationStatus `json:"status"`
}
UserInvitation is an offer to create an account on the instance, addressed to an email that has none. It is distinct from MembershipInvitation, which offers an existing account a place in a namespace.
type UserInvitationStatus ¶
type UserInvitationStatus string
UserInvitationStatus is where an invitation to create an account sits. Unlike a membership invitation it cannot be rejected: it is either open or used.
const ( // UserInvitationStatusPending is an invitation whose account has not been created yet. UserInvitationStatusPending UserInvitationStatus = "pending" // UserInvitationStatusAccepted is an invitation that has been used to create an account, and // cannot be used again. UserInvitationStatusAccepted UserInvitationStatus = "accepted" )
type UserMFA ¶ added in v0.16.0
type UserMFA struct {
// Enabled reports whether MFA is enabled for the user.
Enabled bool `json:"enabled"`
// Secret is the key used for authenticating with the OTP server.
Secret string `json:"-"`
// RecoveryCodes are recovery tokens that the user can use to regain account access if they lose their MFA device.
RecoveryCodes []string `json:"-"`
}
UserMFA represents the attributes related to MFA for a user.
type UserOrigin ¶ added in v0.18.0
type UserOrigin string
UserOrigin records how the account came into existence, which decides who owns the password: a SAML user has none here, and authenticates against the IdP instead.
const ( // UserOriginLocal indicates that the user was created through the standard signup process, without // using third-party integrations like SSO IdPs. UserOriginLocal UserOrigin = "local" // UserOriginSAML indicates that the user was created using a SAML method. UserOriginSAML UserOrigin = "SAML" )
func (UserOrigin) String ¶ added in v0.18.0
func (o UserOrigin) String() string
type UserPassword ¶ added in v0.8.0
type UserPassword struct {
// Plain contains the plain text password.
Plain string `json:"password" validate:"required,password"`
// Hash contains the hashed pasword from plain text.
Hash string `json:"-"`
}
UserPassword holds a password in both forms. Plain is populated on the way in from a request and never persisted or serialized; Hash is what the store keeps. Build one with HashUserPassword rather than setting Hash yourself.
func HashUserPassword ¶ added in v0.15.0
func HashUserPassword(plain string) (UserPassword, error)
HashUserPassword receives a plain password and hash it, returning a UserPassword.
func (*UserPassword) Compare ¶ added in v0.14.0
func (p *UserPassword) Compare(plain string) bool
Compare reports whether a plain password matches with hash.
For compatibility purposes, it can compare using both SHA256 and bcrypt algorithms. Hashes starting with "$" are assumed to be a bcrypt hash; otherwise, they are treated as SHA256 hashes.
type UserPreferences ¶ added in v0.16.0
type UserPreferences struct {
// PreferredNamespace represents the namespace the user most recently authenticated with.
PreferredNamespace string `json:"-"`
// AuthMethods indicates the authentication methods that the user can use to authenticate.
AuthMethods []UserAuthMethod `json:"auth_methods"`
}
UserPreferences is per-user state that changes how the console behaves for them, and carries no authorization weight: nothing here decides what the user may do.
type UserStatus ¶ added in v0.17.0
type UserStatus string
UserStatus is where a user sits in the sign-up flow. It gates authentication, so a user who is not confirmed cannot sign in even with the right password.
const ( // UserStatusNotConfirmed applies to cloud-only instances. This status is assigned to a user who has registered // but has not yet confirmed their email address. UserStatusNotConfirmed UserStatus = "not-confirmed" // UserStatusConfirmed indicates that the user has completed the registration process and confirmed their email address. // Users in community and enterprise instances will always be created with this status. UserStatusConfirmed UserStatus = "confirmed" )
func (UserStatus) String ¶ added in v0.17.0
func (s UserStatus) String() string
type UserTokenRecover ¶ added in v0.7.2
type UserTokenRecover struct {
Token string `json:"uid"`
User string `json:"user_id"`
CreatedAt time.Time `json:"created_at"`
}
UserTokenRecover is a password-recovery token. Recovery is a cloud feature and the type lives there too; this copy exists because migrations reference it, so it cannot move until they no longer do.
type Username ¶ added in v0.5.0
type Username struct {
ID string
}
Username carries the authenticated user's username as the API's gateway reads it out of a request's context, alongside ID and Tenant.
type WebEndpoint ¶
type WebEndpoint struct {
Address string `json:"address"`
Namespace string `json:"namespace"`
DeviceUID string `json:"device_uid"`
Host string `json:"host"`
Port int `json:"port"`
TLS WebEndpointTLS `json:"tls"`
}
WebEndpoint is what the HTTP proxy needs to route a request to a device: which namespace and device to dial, and which backend address to ask that device for. It is deliberately narrower than the stored endpoint — the proxy has no use for its expiration or creation time.
type WebEndpointTLS ¶
type WebEndpointTLS struct {
Enabled bool `json:"enabled"`
Verify bool `json:"verify"`
// Domain doubles as the Host header override and, with TLS enabled, the SNI
// sent during the handshake.
Domain string `json:"domain"`
}
WebEndpointTLS carries how the HTTP proxy should reach the backend behind a web endpoint.
Source Files
¶
- ID.go
- access-policy.go
- api_key.go
- billing.go
- device.go
- filter.go
- info.go
- instance_api_key.go
- member.go
- membership_invitation.go
- namespace.go
- pattern.go
- principal.go
- privatekey.go
- provisioning_key.go
- provisioning_key_event.go
- publickey.go
- session.go
- ssh-approval.go
- ssh-identity.go
- ssh.go
- stats.go
- system.go
- tags.go
- tenant.go
- type.go
- types.go
- user.go
- user_invitation.go
- username.go
- web_endpoint.go