models

package
v0.27.0-rc.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: Apache-2.0 Imports: 9 Imported by: 20

Documentation

Index

Constants

View Source
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.

View Source
const (
	// InstallKeyWebhookDefaultTimeout / MaxTimeout bound the synchronous webhook request.
	InstallKeyWebhookDefaultTimeout = 5
	InstallKeyWebhookMaxTimeout     = 15
	// InstallKeyWebhookDefaultCallbackTTL / MaxCallbackTTL bound the deferred-decision token's validity
	// (1 hour default, 24 hours max).
	InstallKeyWebhookDefaultCallbackTTL = 3600
	InstallKeyWebhookMaxCallbackTTL     = 86400
)

Webhook tuning bounds/defaults (seconds). A stored 0 means "use the default".

View Source
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.

View Source
const (
	// SSHAccessModeLegacy is the key/firewall model: access is a public key with
	// an ACL plus, on Cloud/Enterprise, firewall rules. This is the default.
	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.

View Source
const DefaultAnnouncementMessage = `` /* 1274-byte string literal not displayed */

default Announcement Message for the shellhub namespace

View Source
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.

Variables

This section is empty.

Functions

func IsTypePersonal added in v0.18.0

func IsTypePersonal(typeNamespace string) bool

func IsTypeTeam added in v0.18.0

func IsTypeTeam(typeNamespace string) bool

Types

type APIKey added in v0.15.0

type APIKey struct {
	// ID is the unique identifier of the API key. It is a SHA256 hash of a UUID.
	ID 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 ID and key 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.

func (*APIKey) IsValid added in v0.16.0

func (a *APIKey) IsValid() bool

IsValid reports whether an API key is valid or not.

type APIKeyConflicts added in v0.16.0

type APIKeyConflicts struct {
	ID   string
	Name string
}

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"`

	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"`
}

type AuthClaims added in v0.2.0

type AuthClaims struct {
	Claims string `json:"claims"`
}

type Billing added in v0.8.0

type Billing struct {
	// Active indicates if the subscription is active.
	// IT IS THE SOURCE OF TRUTH THAT DEFINES WHETHER A SUBSCRIPTION IS ACTIVE OR NOT and change due to the status of
	// the subscription.
	//
	// A subscription is active if its status is `active`, `trailing`, `past_due` or `to_cancel_at_end_of_period`.
	// `past_due` is a temporary status that occurs when a payment to renew the subscription fails, but the subscription
	// has not been canceled yet.
	// `to_cancel_at_end_of_period` is a custom status used by this package to indicate that the subscription is set to
	// cancel at the end of the period.
	// A subscription is not active if its status is `incomplete`, `incomplete_expired`, `canceled`, `unpaid` or `paused`.
	// TODO: evaluate if `paused` should be considered active.
	Active bool `json:"active"`
	// Status is the current status of the subscription.
	Status BillingStatus `json:"status"`
	// Customer is the ID of the customer the subscription belongs to.
	// Customer string `json:"customer"`
	CustomerID string `json:"customer_id"`
	// SubscriptionID is the ID of the subscription.
	SubscriptionID string `json:"subscription_id"`
	// CurrentPeriodEnd is the end of the current period.
	CurrentPeriodEnd int64 `json:"current_period_end"`
	// CreatedAt is the time at which this billing was created.
	// It must follow the RFC 3339 format.
	CreatedAt string `json:"created_at"`
	// UpdatedAt is the time at which this billing was last updated.
	// It must follow the RFC 3339 format.
	UpdatedAt string `json:"updated_at"`
}

Billing contains information about the ShellHub's subscription.

func NewBilling added in v0.12.4

func NewBilling(status BillingStatus, customer, subscription string, currentPeridoEnd int64) *Billing

func (*Billing) HasCurrentPeriodEnd added in v0.12.4

func (b *Billing) HasCurrentPeriodEnd() bool

func (*Billing) HasCutomer added in v0.12.4

func (b *Billing) HasCutomer() bool

func (*Billing) HasSubscription added in v0.12.4

func (b *Billing) HasSubscription() bool

func (*Billing) IsActive added in v0.12.4

func (b *Billing) IsActive() bool

IsActive indicates if the subscription is active.

func (*Billing) IsNil added in v0.12.4

func (b *Billing) IsNil() bool

func (*Billing) SetCurrentPeriodEnd added in v0.12.4

func (b *Billing) SetCurrentPeriodEnd(end int64)

func (*Billing) SetCustomer added in v0.12.4

func (b *Billing) SetCustomer(id string)

func (*Billing) SetSubscription added in v0.12.4

func (b *Billing) SetSubscription(id string, status BillingStatus)

func (*Billing) UpdateBillingStatus added in v0.12.4

func (b *Billing) UpdateBillingStatus(status BillingStatus)

UpdateBillingStatus updates the status of the billing.

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"`
}

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.

https://stripe.com/docs/api/subscriptions/object#subscription_object-status https://stripe.com/docs/billing/subscriptions/overview#subscription-lifecycle

const (
	// BillingStatusInactive represents inactive status.
	BillingStatusInactive BillingStatus = "inactive"
	// 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.

func (BillingStatus) IsActive added in v0.12.4

func (s BillingStatus) IsActive() bool

IsActive returns true if the subscription is active. It is active if its status is `active`, `past_due`, `trailing` or `to_cancel_at_end_of_period`.

type Decision

type Decision struct {
	Allowed bool `json:"allowed"`
	// 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 `json:"require_reauth"`
	// 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   `json:"reauth_period"`
	Reason       string `json:"reason"`
}

Decision is the outcome of an Access Policy authorization check.

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 install 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 install key at enrollment. Only meaningful when Ephemeral is true.
	EphemeralTimeout int `json:"ephemeral_timeout,omitempty"`
	// InstallKeyID is the digest of the install key the device enrolled with (a real key or the
	// namespace's legacy key). It attributes the device to its enrollment source.
	InstallKeyID string `json:"install_key_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"`
}

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"`
	// InstallKey is an optional install 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.
	InstallKey string `json:"install_key,omitempty" hash:"-"`
}

type DeviceAuthRequest

type DeviceAuthRequest struct {
	Info     *DeviceInfo `json:"info"`
	Sessions []string    `json:"sessions,omitempty"`
	*DeviceAuth
}

type DeviceAuthResponse

type DeviceAuthResponse struct {
	UID       string `json:"uid"`
	Token     string `json:"token"`
	Name      string `json:"name"`
	Namespace string `json:"namespace"`
	// 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"`
}

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"`
}

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"`
}

type DeviceLoginCode

type DeviceLoginCode struct {
	Code      string `json:"code"`
	ExpiresIn int    `json:"expires_in_seconds"`
}

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"`
}

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"`
}

type DeviceStatus added in v0.11.8

type DeviceStatus string
const (
	DeviceStatusAccepted DeviceStatus = "accepted"
	DeviceStatusPending  DeviceStatus = "pending"
	DeviceStatusRejected DeviceStatus = "rejected"
	DeviceStatusRemoved  DeviceStatus = "removed"
	DeviceStatusUnused   DeviceStatus = "unused"
	DeviceStatusEmpty    DeviceStatus = ""
)

type DeviceTag added in v0.14.0

type DeviceTag struct {
	Tag string `validate:"required,min=3,max=255,alphanum,ascii,excludes=/@&:"`
}

func NewDeviceTag added in v0.14.0

func NewDeviceTag(tag string) DeviceTag

type Endpoints

type Endpoints struct {
	API string `json:"api"`
	SSH string `json:"ssh"`
}

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 interface{} `json:"params,omitempty"`
}

Filter is a helper struct to filter results from the database. TODO: Gives a better explanation about the filter and how to use it.

func (*Filter) UnmarshalJSON added in v0.3.2

func (f *Filter) UnmarshalJSON(data []byte) error

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 FirewallFilter added in v0.9.1

type FirewallFilter struct {
	Hostname string   `json:"hostname,omitempty" validate:"required_without=Tags,excluded_with=Tags,regexp"`
	Tags     []string `` /* 142-byte string literal not displayed */
}

FirewallFilter contains the filter rule of a Public Key.

A FirewallFilter can contain either Hostname, string, or Tags, slice of strings never both.

type FirewallRule added in v0.3.3

type FirewallRule struct {
	ID       string `json:"id,omitempty"`
	TenantID string `json:"tenant_id"`
	FirewallRuleFields
}

type FirewallRuleFields added in v0.3.3

type FirewallRuleFields struct {
	Priority int            `json:"priority"`
	Action   string         `json:"action" validate:"required,oneof=allow deny"`
	Active   bool           `json:"active"`
	SourceIP string         `json:"source_ip" validate:"required,regexp"`
	Username string         `json:"username" validate:"required,regexp"`
	Filter   FirewallFilter `json:"filter" validate:"required"`
}

func (*FirewallRuleFields) Validate added in v0.3.3

func (f *FirewallRuleFields) Validate() error

type FirewallRuleUpdate added in v0.3.3

type FirewallRuleUpdate struct {
	FirewallRuleFields
}

type ID added in v0.5.0

type ID struct {
	ID string
}

type Info added in v0.2.1

type Info struct {
	Version   string    `json:"version"`
	Endpoints Endpoints `json:"endpoints"`
}

type InstallKey

type InstallKey struct {
	// ID is the unique identifier of the install 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 install_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 install key's namespace ID.
	TenantID string `json:"tenant_id"`
	// Mode is the enrollment policy applied to devices that enroll with the key.
	Mode InstallKeyMode `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:"-"`
	// AllowedMACs is the set of device MACs accepted when Mode is allowlist. Any MAC outside it is
	// rejected.
	AllowedMACs []string `json:"allowed_macs"`
	// 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"`
	// 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 InstallKeyType `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"`
}

InstallKey is a reusable, revocable, namespace-scoped credential that decides how a device is enrolled. Its InstallKey.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 InstallKey.IsValid to verify a key can still enroll.

func (*InstallKey) IsPairing

func (s *InstallKey) 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 (*InstallKey) IsSystem

func (s *InstallKey) 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 (*InstallKey) IsValid

func (s *InstallKey) IsValid() bool

IsValid reports whether the install key can still enroll a device: it must not be revoked, disabled, expired, or overused.

func (*InstallKey) ReconcilableOnAuth

func (s *InstallKey) 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 (*InstallKey) WebhookCallbackTTLOrDefault

func (s *InstallKey) WebhookCallbackTTLOrDefault() int

WebhookCallbackTTLOrDefault returns the deferred-decision token TTL in seconds, clamped and defaulted.

func (*InstallKey) WebhookTimeoutOrDefault

func (s *InstallKey) WebhookTimeoutOrDefault() int

WebhookTimeoutOrDefault returns the synchronous webhook timeout in seconds, clamped to the allowed range and defaulted when unset.

type InstallKeyConflicts

type InstallKeyConflicts struct {
	ID   string
	Name string
}

InstallKeyConflicts holds install key attributes that must be unique per tenant ID and can be used in queries to identify conflicts.

type InstallKeyEvent

type InstallKeyEvent struct {
	// ID is the unique identifier of the event.
	ID string `json:"id"`
	// InstallKeyID is the digest of the install key the device enrolled with.
	InstallKeyID string `json:"install_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"`
	// MAC is the enrolled device's MAC at enrollment time. It may be empty.
	MAC string `json:"mac"`
	// 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"`
}

InstallKeyEvent is one row in an install 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 InstallKeyMode

type InstallKeyMode string

InstallKeyMode is the per-key enrollment policy: it decides a device's initial status when the device enrolls with the key.

const (
	// InstallKeyModeAutomatic accepts the device on enrollment (the classic install-key behavior).
	InstallKeyModeAutomatic InstallKeyMode = "automatic"
	// InstallKeyModeManual lands the device pending for manual review. The legacy/system key is always
	// this mode.
	InstallKeyModeManual InstallKeyMode = "manual"
	// InstallKeyModeWebhook defers the decision to an integrator's endpoint, called at enrollment.
	InstallKeyModeWebhook InstallKeyMode = "webhook"
	// InstallKeyModeAllowlist accepts the device when its MAC is in AllowedMACs, otherwise rejects it.
	InstallKeyModeAllowlist InstallKeyMode = "allowlist"
)

type InstallKeyType

type InstallKeyType string

InstallKeyType 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 (
	// InstallKeyTypeUser is a normal user-created key.
	InstallKeyTypeUser InstallKeyType = "user"
	// InstallKeyTypeLegacy is the tenant-only keyless enrollment source (a device presenting only a
	// tenant ID, no install key). Manual mode: such devices land pending.
	InstallKeyTypeLegacy InstallKeyType = "legacy"
	// InstallKeyTypePairing 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.
	InstallKeyTypePairing InstallKeyType = "pairing"
)

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"`
	// Type mirrors the member's user account type (human or service). It is denormalized from
	// the joined users row so authorization can exclude service accounts from human-oriented
	// policy subjects (e.g. all-members) without a second query. Empty for legacy rows loaded
	// without the users join; treat empty as human.
	Type UserType `json:"type,omitempty"`
	// 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"`
}

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,omitempty"`
}

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:"-"`
}

func (MembershipInvitation) IsExpired added in v0.21.4

func (m MembershipInvitation) IsExpired() bool

func (MembershipInvitation) IsPending added in v0.21.4

func (m MembershipInvitation) IsPending() bool

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
const (
	MembershipInvitationStatusPending   MembershipInvitationStatus = "pending"
	MembershipInvitationStatusAccepted  MembershipInvitationStatus = "accepted"
	MembershipInvitationStatusRejected  MembershipInvitationStatus = "rejected"
	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"`
}

func (*Namespace) FindMember added in v0.14.0

func (n *Namespace) FindMember(id string) (*Member, bool)

FindMember checks if a member with the specified ID exists in the namespace.

func (*Namespace) HasMaxDevices added in v0.11.8

func (n *Namespace) HasMaxDevices() bool

HasMaxDevices checks if the namespace has a maximum number of devices.

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 (*Namespace) HasMaxDevicesReached added in v0.11.8

func (n *Namespace) HasMaxDevicesReached() bool

HasMaxDevicesReached checks if the namespace has reached the maximum number of devices. Only counts accepted devices. Removed devices no longer count towards the limit, allowing immediate slot reuse after deletion.

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 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"`
}

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"`
}

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"
	// PolicySubjectAllMembers grants every member of the namespace; Value is empty.
	PolicySubjectAllMembers PolicySubjectType = "all-members"
)

type PrivateKey added in v0.5.0

type PrivateKey struct {
	Data        []byte    `json:"data"`
	Fingerprint string    `json:"fingerprint"`
	CreatedAt   time.Time `json:"created_at"`
}

type PropertyParams added in v0.3.2

type PropertyParams struct {
	Name     string      `json:"name"`
	Operator string      `json:"operator"`
	Value    interface{} `json:"value"`
}

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
}

type PublicKeyAuthRequest added in v0.5.0

type PublicKeyAuthRequest struct {
	Fingerprint string `json:"fingerprint"`
	Data        string `json:"data"`
}

type PublicKeyAuthResponse added in v0.5.0

type PublicKeyAuthResponse struct {
	Signature string `json:"signature"`
}

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"`
}

func (*PublicKeyFields) Validate added in v0.6.1

func (p *PublicKeyFields) Validate() error

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.

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 regexp matched against the device name, 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
}

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"`
}

NOTE: This struct has been moved to the cloud repo as it is only used in a cloud context; however, it is also utilized by migrations. For this reason, we must maintain the struct here ensure everything continues to function as expected. TODO: Remove this struct when it is no longer needed for migrations.

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"`
	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 prints it in the terminal banner.

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 terminal banner (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   SSHApprovalState = "pending"
	SSHApprovalConfirmed SSHApprovalState = "confirmed"
	SSHApprovalRejected  SSHApprovalState = "rejected"
)

type SSHApprovalStatus

type SSHApprovalStatus struct {
	State  SSHApprovalState `json:"state"`
	UserID string           `json:"user_id,omitempty"`
}

SSHApprovalStatus is what the SSH gateway polls while it holds the login open. 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"`
}

type SSHExitStatus added in v0.19.0

type SSHExitStatus struct {
	Status uint32 `json:"status"`
}

type SSHIdentity

type SSHIdentity struct {
	ID       string `json:"id"`
	TenantID string `json:"-"`
	// PrincipalID is the id of the bound principal (a row in the users table,
	// human or service account).
	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. PrincipalType tells a human's key apart from a service
	// account's.
	PrincipalName  string   `json:"principal_name"`
	PrincipalEmail string   `json:"principal_email"`
	PrincipalType  UserType `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; SingleUse is only
	// offered to a service account, whose one key serves one 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 human user or a service account) 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.

func (*SSHIdentity) Active

func (i *SSHIdentity) Active(now time.Time) bool

Active reports whether the key is still usable at now: neither consumed nor past its expiry. A nil ExpiresAt never expires, so a key created without a TTL is always active.

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:"-"`
}

NOTE: SSHPty cannot use SSHWindowChange inside itself due [ssh.Unmarshal] issues.

type SSHPtyOutput added in v0.19.0

type SSHPtyOutput struct {
	Output string `json:"output"`
}

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"`
}

type SSHSubsystem added in v0.19.0

type SSHSubsystem struct {
	Subsystem string `json:"subsystem"`
}

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"`
}

type ServiceAccount

type ServiceAccount struct {
	// ID is the underlying service user's id.
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	CreatedAt time.Time `json:"created_at"`
	// Identities are the SSH keys enrolled for this service account in the namespace.
	Identities []SSHIdentity `json:"identities"`
}

ServiceAccount is a non-human principal for automated systems (CI, backups, config management): a service-typed user (see UserTypeService) plus a namespace membership that holds one or more SSH identities. It never signs in to the console and is not an API principal, existing only for the SSH identity scheme. It is authorized by the same Access Policies as human members; the human/service distinction lives in the user's type, not in the policy.

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.
	UserID        string          `json:"user_id,omitempty"`
	IPAddress     string          `json:"ip_address"`
	StartedAt     time.Time       `json:"started_at"`
	LastSeen      time.Time       `json:"last_seen"`
	Active        bool            `json:"active"`
	Closed        bool            `json:"-"`
	Authenticated bool            `json:"authenticated"`
	Recorded      bool            `json:"recorded"`
	Type          string          `json:"type"`
	Term          string          `json:"term"`
	Web           bool            `json:"web"`
	Position      SessionPosition `json:"position"`
	Events        SessionEvents   `json:"events"`
}

type SessionEvent added in v0.18.0

type SessionEvent struct {
	// Session is the session UID where the event occurred.
	Session string `json:"session"`
	// 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 a generic structure containing data of the event, normally the unmarshaling data of the request.
	Data any `json:"data"`
	// 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
const (
	// ShellHub custom requests.
	SessionEventTypePtyOutput SessionEventType = "pty-output"

	// Terminal (PTY) request types
	SessionEventTypePtyRequest   SessionEventType = "pty-req"
	SessionEventTypeWindowChange SessionEventType = "window-change"
	SessionEventTypeExitCode     SessionEventType = "exit-code"

	// Process-related requests
	SessionEventTypeExitStatus SessionEventType = "exit-status"
	SessionEventTypeExitSignal SessionEventType = "exit-signal"

	// Environment and Shell requests
	SessionEventTypeEnv       SessionEventType = "env"
	SessionEventTypeShell     SessionEventType = "shell"
	SessionEventTypeExec      SessionEventType = "exec"
	SessionEventTypeSubsystem SessionEventType = "subsystem"

	// Signal and forwarding requests
	SessionEventTypeSignal       SessionEventType = "signal"
	SessionEventTypeTcpipForward SessionEventType = "tcpip-forward"
	SessionEventTypeAuthAgentReq SessionEventType = "auth-agent-req"
)

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"`
}

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"`
}

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"`
	Type          *string `json:"type"`
}

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"`
}

type Status added in v0.7.3

type Status struct {
	Authenticated bool `json:"authenticated"`
}

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"`
}

type SystemAuthentication added in v0.18.0

type SystemAuthentication struct {
	Local *SystemAuthenticationLocal `json:"local"`
}

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"`
}

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"`
}

type TagConflicts added in v0.21.0

type TagConflicts struct {
	Name string
}

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
}

type Type added in v0.18.0

type Type string
const (
	TypePersonal Type = "personal"
	TypeTeam     Type = "team"
)

func NewDefaultType added in v0.18.0

func NewDefaultType() Type

type UID

type UID string

type User

type User struct {
	ID string `json:"id,omitempty"`
	// Type distinguishes a human user from a service account. It defaults to
	// [UserTypeHuman]; service accounts are created only through the service-account flow.
	Type UserType `json:"type"`
	// 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
	// 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"`
}

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
const (
	// UserAuthMethodLocal indicates that the user can authenticate using an email and password.
	UserAuthMethodLocal UserAuthMethod = "local"

	// UserAuthMethodManual 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          string           `json:"role"`
	MFA           bool             `json:"mfa"`
	MaxNamespaces int              `json:"max_namespaces"`
	Admin         bool             `json:"admin"`
}

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"`
}

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
}

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"`
}

type UserInvitationStatus

type UserInvitationStatus string
const (
	UserInvitationStatusPending  UserInvitationStatus = "pending"
	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
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:"-"`
}

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"`
}

type UserStatus added in v0.17.0

type UserStatus string
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"`
}

NOTE: This struct has been moved to the cloud repo as it is only used in a cloud context; however, it is also utilized by migrations. For this reason, we must maintain the struct here ensure everything continues to function as expected. TODO: Remove this struct when it is no longer needed for migrations.

type UserType

type UserType string
const (
	// UserTypeHuman is a regular person: signs in to the console, may hold API keys, and is
	// authorized by their membership role.
	UserTypeHuman UserType = "human"

	// UserTypeService is a service account: a non-human principal that only holds an SSH
	// identity for automated systems. It never signs in to the console and is not an API
	// principal. This type is the human/service discriminator, not the membership role, so it
	// stays valid if roles ever become groups.
	UserTypeService UserType = "service"
)

func (UserType) String

func (t UserType) String() string

type Username added in v0.5.0

type Username struct {
	ID string
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL