relay

package
v0.139.0 Latest Latest
Warning

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

Go to latest
Published: Jun 21, 2026 License: MIT Imports: 43 Imported by: 0

Documentation

Index

Constants

View Source
const (
	SustainedRate = 375_000 // 3 Mbit/s — all tiers

)

Tier rate/cap constants. All tiers share the same sustained rate (3 Mbit/s) — this is a reasonable ceiling for compressed terminal PTY, not a paid differentiator. The only paid gate is the monthly cap: free = 1 GiB, pro/team = unlimited.

Variables

This section is empty.

Functions

func GenerateECKey added in v0.108.0

func GenerateECKey() (*ecdsa.PrivateKey, string, error)

GenerateECKey creates a new P-256 private key and returns it along with its base64-DER encoding (suitable for storing in wing.yaml).

func IssueHandoffJWT added in v0.108.0

func IssueHandoffJWT(key *ecdsa.PrivateKey, userID, email, orgRole string) (string, error)

IssueHandoffJWT creates a short-lived ES256 JWT for browser direct-mode connections.

func IssueWingJWT

func IssueWingJWT(key *ecdsa.PrivateKey, userID, publicKey, wingID string) (string, time.Time, error)

IssueWingJWT creates an ES256-signed JWT for a wing connection.

func MarshalECPublicKey added in v0.108.0

func MarshalECPublicKey(pub *ecdsa.PublicKey) (string, error)

MarshalECPublicKey returns the base64-encoded DER form of an ECDSA public key.

func NewLoginProxy added in v0.23.0

func NewLoginProxy(loginAddr string) *httputil.ReverseProxy

NewLoginProxy creates a reverse proxy to the login node for edge nodes. Edge nodes proxy API, auth, and page requests to the login node while serving WebSocket connections directly.

func ParseECKeyFromEnv added in v0.108.0

func ParseECKeyFromEnv(envValue string) (*ecdsa.PrivateKey, error)

ParseECKeyFromEnv parses a P-256 private key from an environment variable value. Accepts PEM or base64-encoded DER. Returns an error if the value is empty or invalid.

func ParseECPublicKey added in v0.108.0

func ParseECPublicKey(data string) (*ecdsa.PublicKey, error)

ParseECPublicKey parses a base64-encoded DER ECDSA public key.

Types

type BandwidthMeter

type BandwidthMeter struct {
	// contains filtered or unexported fields
}

BandwidthMeter applies per-user rate limiting on relay traffic and periodically syncs usage to the DB.

func NewBandwidthMeter

func NewBandwidthMeter(bytesPerSec int, burst int, db *sql.DB) *BandwidthMeter

NewBandwidthMeter creates a meter with the given sustained rate (bytes/sec) and burst (bytes).

func (*BandwidthMeter) AddUsage added in v0.23.0

func (b *BandwidthMeter) AddUsage(userID string, bytes int64)

AddUsage adds external usage bytes (reported by edge nodes) to the local counters.

func (*BandwidthMeter) DrainCounters added in v0.23.0

func (b *BandwidthMeter) DrainCounters() map[string]int64

DrainCounters returns per-user byte counts accumulated since the last drain, then resets them. Used by edge nodes to report usage to login via the sync protocol.

func (*BandwidthMeter) ExceededMonthly added in v0.23.0

func (b *BandwidthMeter) ExceededMonthly(userID string) bool

ExceededMonthly returns true if a free-tier user has exceeded their monthly cap. Always returns false for pro/team users.

func (*BandwidthMeter) ExceededUsers added in v0.23.0

func (b *BandwidthMeter) ExceededUsers() []string

ExceededUsers returns user IDs that have exceeded their monthly bandwidth cap. Only meaningful on the login node where counters reflect cluster-wide totals.

func (*BandwidthMeter) InvalidateUser added in v0.23.0

func (b *BandwidthMeter) InvalidateUser(userID string)

func (*BandwidthMeter) IsExceeded added in v0.23.0

func (b *BandwidthMeter) IsExceeded(userID string) bool

IsExceeded returns true if a user has been flagged as over their monthly cap.

func (*BandwidthMeter) MonthlyUsage added in v0.23.0

func (b *BandwidthMeter) MonthlyUsage(userID string) int64

MonthlyUsage returns the user's current month bandwidth usage in bytes.

func (*BandwidthMeter) SeedFromDB added in v0.23.0

func (b *BandwidthMeter) SeedFromDB()

SeedFromDB loads current month's bandwidth totals from the DB into memory. Called on login startup to avoid resetting counters to zero.

func (*BandwidthMeter) SetExceeded added in v0.23.0

func (b *BandwidthMeter) SetExceeded(userIDs []string)

SetExceeded replaces the set of users who exceeded their monthly cap (pushed from login via sync).

func (*BandwidthMeter) SetTierLookup added in v0.23.0

func (b *BandwidthMeter) SetTierLookup(fn TierLookup)

SetTierLookup sets the function used to look up user tiers for rate differentiation.

func (*BandwidthMeter) StartSync

func (b *BandwidthMeter) StartSync(ctx context.Context, interval time.Duration)

StartSync syncs per-user bandwidth to the DB every interval. Only writes users with changes.

func (*BandwidthMeter) Wait

func (b *BandwidthMeter) Wait(ctx context.Context, userID string, n int) error

Wait blocks until the user's rate limiter allows n bytes, or ctx is done. Rejects immediately if the user has exceeded their monthly bandwidth cap.

type ConnectedWing

type ConnectedWing struct {
	ID           string
	UserID       string
	WingID       string
	PublicKey    string
	OrgID        string // org ID this wing serves
	Locked       bool
	AllowedCount int
	Conn         *websocket.Conn
	LastSeen     time.Time
}

ConnectedWing represents a wing connected via WebSocket.

type DeviceCodeRow

type DeviceCodeRow struct {
	Code      string
	UserCode  string
	UserID    *string
	DeviceID  string
	PublicKey *string
	CreatedAt time.Time
	ExpiresAt time.Time
	Claimed   bool
}

type Entitlement added in v0.23.0

type Entitlement struct {
	ID             string
	UserID         string
	SubscriptionID string
	CreatedAt      time.Time
}

type EntitlementCache added in v0.23.0

type EntitlementCache struct {
	// contains filtered or unexported fields
}

EntitlementCache caches user tier info on edge nodes, polling the login node periodically.

func NewEntitlementCache added in v0.23.0

func NewEntitlementCache(loginAddr string) *EntitlementCache

func (*EntitlementCache) GetTier added in v0.23.0

func (c *EntitlementCache) GetTier(userID string) string

GetTier returns the cached tier for a user, defaulting to "free".

func (*EntitlementCache) StartSync added in v0.23.0

func (c *EntitlementCache) StartSync(ctx context.Context, interval time.Duration)

StartSync begins periodic polling of the login node for entitlement data.

type EntitlementEntry added in v0.23.0

type EntitlementEntry struct {
	UserID string `json:"user_id"`
	Tier   string `json:"tier"`
}

EntitlementEntry is a user's entitlement info for edge node caching.

type HandoffClaims added in v0.108.0

type HandoffClaims struct {
	jwt.RegisteredClaims
	Email   string `json:"email,omitempty"`
	OrgRole string `json:"org_role,omitempty"`
}

HandoffClaims are short-lived JWT claims for browser direct-mode connections.

type NtfyConfig added in v0.52.0

type NtfyConfig struct {
	Topic  string
	Token  string
	Events string // comma-separated: "attention,exit"
}

NtfyConfig holds a user's push notification settings.

type Org added in v0.23.0

type Org struct {
	ID          string
	Name        string
	Slug        string
	OwnerUserID string
	MaxSeats    int
	CreatedAt   time.Time
}

Org represents an organization that can share wings among members.

type OrgInvite added in v0.23.0

type OrgInvite struct {
	ID        string
	OrgID     string
	Email     string
	Token     string
	InvitedBy string
	Role      string
	CreatedAt time.Time
	ClaimedAt *time.Time
}

OrgInvite represents a pending invite to an org.

type OrgMember added in v0.23.0

type OrgMember struct {
	OrgID     string
	UserID    string
	Role      string // "owner", "admin", "member"
	CreatedAt time.Time
}

OrgMember represents a user's membership in an org.

type PTYRoute added in v0.44.4

type PTYRoute struct {
	BrowserConn *websocket.Conn            // controller (can send input)
	Viewers     map[string]*websocket.Conn // viewer_id → spectator conn (read-only)
	UserID      string                     // bandwidth metering only
	WingID      string                     // machine ID for offline notification
	Agent       string                     // agent name for ntfy notifications
	CWD         string                     // working directory for ntfy notifications
	// contains filtered or unexported fields
}

PTYRoute is a minimal routing entry for wing→browser output forwarding. No session metadata — the wing owns all session intelligence.

type PTYRoutes added in v0.44.4

type PTYRoutes struct {
	// contains filtered or unexported fields
}

PTYRoutes tracks active PTY routing entries.

func NewPTYRoutes added in v0.44.4

func NewPTYRoutes() *PTYRoutes

func (*PTYRoutes) AddViewer added in v0.137.0

func (r *PTYRoutes) AddViewer(sessionID, viewerID string, conn *websocket.Conn)

AddViewer adds a spectator connection to a session route.

func (*PTYRoutes) ClearBrowser added in v0.44.4

func (r *PTYRoutes) ClearBrowser(conn *websocket.Conn)

ClearBrowser nils the BrowserConn and removes spectator entries for this connection.

func (*PTYRoutes) Get added in v0.44.4

func (r *PTYRoutes) Get(sessionID string) *PTYRoute

func (*PTYRoutes) IsSpectator added in v0.137.0

func (r *PTYRoutes) IsSpectator(conn *websocket.Conn) bool

IsSpectator returns true if conn is a spectator on any session.

func (*PTYRoutes) NotifyWingOffline added in v0.49.0

func (r *PTYRoutes) NotifyWingOffline(wingID string)

NotifyWingOffline sends a wing.offline message to all PTY browsers connected to the given wing.

func (*PTYRoutes) Remove added in v0.44.4

func (r *PTYRoutes) Remove(sessionID string)

func (*PTYRoutes) RemoveViewer added in v0.137.0

func (r *PTYRoutes) RemoveViewer(sessionID, viewerID string)

RemoveViewer removes a spectator connection from a session route.

func (*PTYRoutes) Set added in v0.44.4

func (r *PTYRoutes) Set(sessionID string, route *PTYRoute)

type PasskeyCredential added in v0.34.0

type PasskeyCredential struct {
	ID           string
	UserID       string
	CredentialID []byte
	PublicKey    []byte // raw P-256: 64 bytes (X||Y)
	SignCount    int
	Label        string
	CreatedAt    time.Time
}

PasskeyCredential represents a stored WebAuthn credential.

type RateLimiter added in v0.12.0

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter applies per-IP request rate limiting. "Friends and family" limits — just enough to prevent abuse.

func NewRateLimiter added in v0.12.0

func NewRateLimiter(reqPerSec float64, burst int) *RateLimiter

NewRateLimiter creates a per-IP rate limiter. reqPerSec is the sustained rate, burst is the max burst size.

func (*RateLimiter) Allow added in v0.12.0

func (rl *RateLimiter) Allow(ip string) bool

Allow returns true if the request is within rate limits for the given IP.

func (*RateLimiter) Middleware added in v0.12.0

func (rl *RateLimiter) Middleware(next http.Handler) http.Handler

Middleware wraps an http.Handler with rate limiting.

type RelayStore

type RelayStore struct {
	// contains filtered or unexported fields
}

func OpenRelay

func OpenRelay(dsn string) (*RelayStore, error)

func (*RelayStore) AddOrgMember added in v0.23.0

func (s *RelayStore) AddOrgMember(orgID, userID, role string) error

AddOrgMember adds a user to an org, enforcing max_seats.

func (*RelayStore) AppendAudit

func (s *RelayStore) AppendAudit(userID, event string, detail *string) error

func (*RelayStore) BackfillProUsers added in v0.23.0

func (s *RelayStore) BackfillProUsers() error

func (*RelayStore) ClaimDeviceCode

func (s *RelayStore) ClaimDeviceCode(code, userID string) error

func (*RelayStore) Close

func (s *RelayStore) Close() error
func (s *RelayStore) ConsumeMagicLink(token string) (string, error)

func (*RelayStore) ConsumeOrgInvite added in v0.23.0

func (s *RelayStore) ConsumeOrgInvite(token string) (string, string, string, error)

ConsumeOrgInvite validates a token, marks it claimed, returns email, orgID, and role.

func (*RelayStore) CountEntitlementsBySub added in v0.23.0

func (s *RelayStore) CountEntitlementsBySub(subID string) (int, error)

func (*RelayStore) CountOrgMembers added in v0.23.0

func (s *RelayStore) CountOrgMembers(orgID string) (int, error)

CountOrgMembers returns the number of members in an org.

func (*RelayStore) CountOrgsOwnedByUser added in v0.32.1

func (s *RelayStore) CountOrgsOwnedByUser(userID string) (int, error)

CountOrgsOwnedByUser returns the number of orgs a user has created.

func (*RelayStore) CreateDeviceCode

func (s *RelayStore) CreateDeviceCode(code, userCode, deviceID string, expiresAt time.Time) error

func (*RelayStore) CreateDeviceCodeWithKey

func (s *RelayStore) CreateDeviceCodeWithKey(code, userCode, deviceID, publicKey string, expiresAt time.Time) error

func (*RelayStore) CreateDeviceToken

func (s *RelayStore) CreateDeviceToken(token, userID, deviceID string, expiresAt *time.Time) error

func (*RelayStore) CreateEntitlement added in v0.23.0

func (s *RelayStore) CreateEntitlement(ent *Entitlement) error

func (*RelayStore) CreateLocalUser added in v0.7.4

func (s *RelayStore) CreateLocalUser() (*User, string, error)

CreateLocalUser creates the local-mode user and a non-expiring device token. Returns the social user, device token string, and whether the user already existed.

func (s *RelayStore) CreateMagicLink(id, email, token string, expiresAt time.Time) error

func (*RelayStore) CreateOrg added in v0.23.0

func (s *RelayStore) CreateOrg(id, name, slug, ownerUserID string) error

CreateOrg creates a new org and adds the owner as an owner member.

func (*RelayStore) CreateOrgInvite added in v0.23.0

func (s *RelayStore) CreateOrgInvite(id, orgID, email, token, invitedBy, role string) error

CreateOrgInvite creates a pending invite.

func (*RelayStore) CreatePasskeyCredential added in v0.34.0

func (s *RelayStore) CreatePasskeyCredential(id, userID string, credentialID, publicKey []byte, label string) error

CreatePasskeyCredential stores a new passkey credential.

func (*RelayStore) CreateServiceUser added in v0.63.0

func (s *RelayStore) CreateServiceUser() (*User, string, error)

CreateServiceUser creates a service user for the roost wing goroutine. Idempotent: returns existing user + token if already created.

func (*RelayStore) CreateSession

func (s *RelayStore) CreateSession(token, userID string, expiresAt time.Time) error

func (*RelayStore) CreateSubscription added in v0.23.0

func (s *RelayStore) CreateSubscription(sub *Subscription) error

func (*RelayStore) CreateUser

func (s *RelayStore) CreateUser(id string) error

func (*RelayStore) CreateUserDev added in v0.23.0

func (s *RelayStore) CreateUserDev() (*User, error)

CreateUserDev creates a dev-mode social user if one doesn't exist.

func (*RelayStore) DB

func (s *RelayStore) DB() *sql.DB

DB returns the underlying database connection.

func (*RelayStore) DeleteEntitlementByUserAndSub added in v0.23.0

func (s *RelayStore) DeleteEntitlementByUserAndSub(userID, subID string) error

func (*RelayStore) DeleteEntitlementsBySub added in v0.23.0

func (s *RelayStore) DeleteEntitlementsBySub(subID string) ([]string, error)

func (*RelayStore) DeleteLabel added in v0.23.0

func (s *RelayStore) DeleteLabel(targetID, scopeType, scopeID string) error

DeleteLabel removes a label for a target at a given scope.

func (*RelayStore) DeleteOrg added in v0.32.5

func (s *RelayStore) DeleteOrg(orgID string) error

DeleteOrg removes an org and all its members and invites.

func (*RelayStore) DeletePasskeyCredential added in v0.34.0

func (s *RelayStore) DeletePasskeyCredential(id, userID string) error

DeletePasskeyCredential removes a passkey credential by ID, scoped to user.

func (*RelayStore) DeleteSession

func (s *RelayStore) DeleteSession(token string) error

func (*RelayStore) DeleteToken

func (s *RelayStore) DeleteToken(token string) error

func (*RelayStore) GetActiveOrgSubscription added in v0.23.0

func (s *RelayStore) GetActiveOrgSubscription(orgID string) (*Subscription, error)

func (*RelayStore) GetActivePersonalSubscription added in v0.23.0

func (s *RelayStore) GetActivePersonalSubscription(userID string) (*Subscription, error)

func (*RelayStore) GetDeviceCode

func (s *RelayStore) GetDeviceCode(code string) (*DeviceCodeRow, error)

func (*RelayStore) GetDeviceCodeByUserCode

func (s *RelayStore) GetDeviceCodeByUserCode(userCode string) (*DeviceCodeRow, error)

GetDeviceCodeByUserCode finds a device code by user_code.

func (*RelayStore) GetInviteByToken added in v0.32.4

func (s *RelayStore) GetInviteByToken(token string) (*OrgInvite, error)

GetInviteByToken returns an invite by its token (claimed or not).

func (*RelayStore) GetNtfyConfig added in v0.52.0

func (s *RelayStore) GetNtfyConfig(userID string) (NtfyConfig, error)

GetNtfyConfig returns the ntfy config for a user.

func (*RelayStore) GetOrCreateUserByEmail added in v0.23.0

func (s *RelayStore) GetOrCreateUserByEmail(email string) (*User, error)

func (*RelayStore) GetOrgByID added in v0.23.0

func (s *RelayStore) GetOrgByID(id string) (*Org, error)

GetOrgByID returns an org by ID.

func (*RelayStore) GetOrgBySlug added in v0.23.0

func (s *RelayStore) GetOrgBySlug(slug string) (*Org, error)

GetOrgBySlug returns an org by slug.

func (*RelayStore) GetOrgMemberRole added in v0.23.0

func (s *RelayStore) GetOrgMemberRole(orgID, userID string) string

GetOrgMemberRole returns the role of a user in an org, or "" if not a member.

func (*RelayStore) GetRelayConfig

func (s *RelayStore) GetRelayConfig(key string) (string, error)

GetRelayConfig reads a value from the relay_config table.

func (*RelayStore) GetSession

func (s *RelayStore) GetSession(token string) (*User, error)

func (*RelayStore) GetUserByEmail added in v0.23.0

func (s *RelayStore) GetUserByEmail(email string) (*User, error)

GetUserByEmail returns a user by email.

func (*RelayStore) GetUserByID added in v0.23.0

func (s *RelayStore) GetUserByID(id string) (*User, error)

GetUserByID returns a user by their ID.

func (*RelayStore) GetUserByProvider added in v0.23.0

func (s *RelayStore) GetUserByProvider(provider, providerID string) (*User, error)

func (*RelayStore) HasPersonalSubscription added in v0.38.0

func (s *RelayStore) HasPersonalSubscription(userID string) bool

HasPersonalSubscription returns true if the user has an active personal (non-org) subscription.

func (*RelayStore) IsOrgMember added in v0.23.0

func (s *RelayStore) IsOrgMember(orgID, userID string) bool

IsOrgMember returns true if the user is a member of the org.

func (*RelayStore) IsUserPro added in v0.23.0

func (s *RelayStore) IsUserPro(userID string) bool

func (*RelayStore) ListOrgMembers added in v0.23.0

func (s *RelayStore) ListOrgMembers(orgID string) ([]*OrgMember, error)

ListOrgMembers returns all members of an org with their user info.

func (*RelayStore) ListOrgsForUser added in v0.23.0

func (s *RelayStore) ListOrgsForUser(userID string) ([]*Org, error)

ListOrgsForUser returns all orgs a user belongs to.

func (*RelayStore) ListPasskeyCredentials added in v0.34.0

func (s *RelayStore) ListPasskeyCredentials(userID string) ([]*PasskeyCredential, error)

ListPasskeyCredentials returns all passkey credentials for a user.

func (*RelayStore) ListPendingInvites added in v0.23.0

func (s *RelayStore) ListPendingInvites(orgID string) ([]*OrgInvite, error)

ListPendingInvites returns unclaimed invites for an org.

func (*RelayStore) RemoveOrgMember added in v0.23.0

func (s *RelayStore) RemoveOrgMember(orgID, userID string) error

RemoveOrgMember removes a user from an org.

func (*RelayStore) ResolveLabel added in v0.23.0

func (s *RelayStore) ResolveLabel(targetID, userID, orgID string) string

ResolveLabel returns the best label for a target: org-scoped first, then personal.

func (*RelayStore) ResolveLabels added in v0.23.0

func (s *RelayStore) ResolveLabels(targetIDs []string, userID, orgID string) map[string]string

ResolveLabels batch-resolves labels for multiple targets.

func (*RelayStore) ResolveOrg added in v0.33.0

func (s *RelayStore) ResolveOrg(ref, userID string) (*Org, error)

ResolveOrg resolves an org reference (ID or slug) for a specific user. It tries ID first, then slug. If multiple orgs match the slug, it returns an error listing the ambiguous matches.

func (*RelayStore) RevokeOrgInvite added in v0.32.4

func (s *RelayStore) RevokeOrgInvite(token string) error

RevokeOrgInvite deletes a pending invite by token.

func (*RelayStore) SetLabel added in v0.23.0

func (s *RelayStore) SetLabel(targetID, scopeType, scopeID, label string) error

SetLabel upserts a label for a target (wing or session).

func (*RelayStore) SetNtfyConfig added in v0.52.0

func (s *RelayStore) SetNtfyConfig(userID string, cfg NtfyConfig) error

SetNtfyConfig updates the ntfy config for a user.

func (*RelayStore) SetOrgMaxSeats added in v0.23.0

func (s *RelayStore) SetOrgMaxSeats(orgID string, seats int) error

SetOrgMaxSeats updates the max seat count for an org.

func (*RelayStore) SetRelayConfig

func (s *RelayStore) SetRelayConfig(key, value string) error

SetRelayConfig writes a value to the relay_config table.

func (*RelayStore) UpdatePasskeySignCount added in v0.34.0

func (s *RelayStore) UpdatePasskeySignCount(id string, count int) error

UpdatePasskeySignCount increments the sign count for a credential.

func (*RelayStore) UpdateSubscriptionSeats added in v0.23.0

func (s *RelayStore) UpdateSubscriptionSeats(subID string, seats int) error

UpdateSubscriptionSeats updates the seat count on a subscription.

func (*RelayStore) UpdateSubscriptionStatus added in v0.23.0

func (s *RelayStore) UpdateSubscriptionStatus(subID, status string) error

func (*RelayStore) UpdateUserEmail added in v0.23.0

func (s *RelayStore) UpdateUserEmail(userID, email string) error

UpdateUserEmail sets the email for a user.

func (*RelayStore) UpdateUserTier added in v0.23.0

func (s *RelayStore) UpdateUserTier(userID, tier string) error

UpdateUserTier sets the tier for a user.

func (*RelayStore) UpsertUser added in v0.23.0

func (s *RelayStore) UpsertUser(u *User) error

func (*RelayStore) ValidateToken

func (s *RelayStore) ValidateToken(token string) (userID string, deviceID string, err error)

type Server

type Server struct {
	Store          *RelayStore
	Config         ServerConfig
	DevTemplateDir string // if set, re-read templates from disk on each request
	DevMode        bool   // if set, auto-claim device codes with test-user
	LocalMode      bool   // if set, bypass auth — single-user, zero-config
	RoostMode      bool   // if set, all authenticated users can access all wings (self-hosted)

	Wings     *WingRegistry
	PTY       *PTYRoutes
	Bandwidth *BandwidthMeter
	RateLimit *RateLimiter

	// Cluster routing (multi-node)
	WingMap *WingMap

	EntitlementCache *EntitlementCache
	// contains filtered or unexported fields
}

func NewServer

func NewServer(store *RelayStore, cfg ServerConfig) *Server

func (*Server) GetSessionCache added in v0.44.20

func (s *Server) GetSessionCache() *SessionCache

GetSessionCache returns the session cache (edge nodes only).

func (*Server) GracefulShutdown added in v0.23.0

func (s *Server) GracefulShutdown(httpSrv *http.Server, timeout time.Duration) error

GracefulShutdown sends relay.restart to all connected WebSockets, then shuts down the HTTP server.

func (*Server) InitJWTKey added in v0.108.0

func (s *Server) InitJWTKey() error

InitJWTKey loads the ES256 signing key. In server mode (non-local, non-roost), WT_JWT_KEY env var is required and the app refuses to start without it. In local/roost mode, the caller provides the key via Config.JWTKey (from wing.yaml).

func (*Server) IsEdge added in v0.23.0

func (s *Server) IsEdge() bool

IsEdge returns true if this node is an edge relay (no SQLite).

func (*Server) IsLogin added in v0.23.0

func (s *Server) IsLogin() bool

IsLogin returns true if this node is the login/DB node.

func (*Server) JWTKey added in v0.108.0

func (s *Server) JWTKey() *ecdsa.PrivateKey

JWTKey returns the ES256 private key for JWT signing.

func (*Server) JWTPubKey added in v0.108.0

func (s *Server) JWTPubKey() *ecdsa.PublicKey

JWTPubKey returns the ES256 public key for JWT verification.

func (*Server) MachineID added in v0.23.0

func (s *Server) MachineID() string

MachineID returns this node's unique machine identifier.

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Server) SetJWTKey added in v0.108.0

func (s *Server) SetJWTKey(key *ecdsa.PrivateKey)

SetJWTKey directly sets the signing key (used by roost mode after loading from wing.yaml).

func (*Server) SetLocalUser added in v0.7.4

func (s *Server) SetLocalUser(u *User)

func (*Server) SetLoginProxy added in v0.23.0

func (s *Server) SetLoginProxy(p http.Handler)

SetLoginProxy sets the reverse proxy used by edge nodes to forward requests to the login node.

func (*Server) SetSessionCache added in v0.23.0

func (s *Server) SetSessionCache(sc *SessionCache)

SetSessionCache sets the session cache for edge nodes.

func (*Server) StartEdgeSync added in v0.23.0

func (s *Server) StartEdgeSync(ctx context.Context, loginAddr string, interval time.Duration)

StartEdgeSync runs the edge-to-login reconcile loop.

type ServerConfig

type ServerConfig struct {
	BaseURL            string
	AppHost            string // e.g. "app.wingthing.ai" — serve SPA at root
	WSHost             string // e.g. "ws.wingthing.ai" — WebSocket only
	JWTKey             string // PEM or base64-DER EC P-256 private key; overrides DB-stored key
	GitHubClientID     string
	GitHubClientSecret string
	GoogleClientID     string
	GoogleClientSecret string
	SMTPHost           string
	SMTPPort           string
	SMTPUser           string
	SMTPPass           string
	SMTPFrom           string
	NodeRole           string // "login", "edge", or "" (single node)
	LoginNodeAddr      string // internal address of login node (for edge nodes)
	FlyMachineID       string // from FLY_MACHINE_ID env var
	FlyRegion          string // from FLY_REGION env var
	FlyAppName         string // from FLY_APP_NAME env var
	HeroVideo          string // path to hero video file on disk (not embedded)
}

type SessionCache added in v0.23.0

type SessionCache struct {
	// contains filtered or unexported fields
}

SessionCache caches session token → user info on edge nodes. Each validated session is cached for 5 minutes to avoid hitting the login node on every request.

func NewSessionCache added in v0.23.0

func NewSessionCache() *SessionCache

func (*SessionCache) ActiveUserIDs added in v0.44.20

func (sc *SessionCache) ActiveUserIDs() []string

ActiveUserIDs returns the deduplicated user IDs of all valid cached sessions.

func (*SessionCache) StartOrgSync added in v0.44.20

func (sc *SessionCache) StartOrgSync(ctx context.Context, loginAddr string, interval time.Duration)

StartOrgSync periodically bulk-refreshes org memberships for all cached sessions.

func (*SessionCache) UpdateUserOrgs added in v0.44.20

func (sc *SessionCache) UpdateUserOrgs(userID string, orgIDs []string)

UpdateUserOrgs updates the cached org IDs for all sessions belonging to userID.

func (*SessionCache) Validate added in v0.23.0

func (sc *SessionCache) Validate(token, loginAddr string) *User

Validate checks the cache or calls the login node to validate a session token.

type SessionValidation added in v0.23.0

type SessionValidation struct {
	UserID      string   `json:"user_id"`
	DisplayName string   `json:"display_name"`
	Tier        string   `json:"tier"`
	OrgIDs      []string `json:"org_ids"`
}

SessionValidation is the response from the session validation endpoint.

type Subscription added in v0.23.0

type Subscription struct {
	ID                   string
	UserID               *string
	OrgID                *string
	Plan                 string
	Status               string
	Seats                int
	StripeSubscriptionID *string
	CreatedAt            time.Time
	UpdatedAt            time.Time
}

type TierLookup added in v0.23.0

type TierLookup func(userID string) string

TierLookup returns a user's tier string ("free", "pro", "team").

type User added in v0.23.0

type User struct {
	ID          string
	Provider    string
	ProviderID  string
	DisplayName string
	AvatarURL   *string
	Email       *string
	Tier        string // "free", "pro", "team"
	IsPro       bool
	CreatedAt   time.Time
	OrgIDs      []string // transient: populated by session cache on edge nodes
}

User represents an authenticated user (via OAuth, magic link, or local mode).

type WingClaims

type WingClaims struct {
	jwt.RegisteredClaims
	PublicKey string `json:"pub,omitempty"`
	WingID    string `json:"wing,omitempty"`
}

WingClaims are the JWT claims for a wing connection.

func ValidateWingJWT

func ValidateWingJWT(pubKey *ecdsa.PublicKey, tokenString string) (*WingClaims, error)

ValidateWingJWT verifies an ES256 JWT and returns the claims.

type WingEvent added in v0.7.5

type WingEvent struct {
	Type         string `json:"type"` // "wing.online", "wing.offline", "session.attention"
	WingID       string `json:"wing_id"`
	PublicKey    string `json:"public_key,omitempty"`
	SessionID    string `json:"session_id,omitempty"`
	Locked       *bool  `json:"locked,omitempty"`
	AllowedCount *int   `json:"allowed_count,omitempty"`
	UserID       string `json:"user_id,omitempty"`
	Owner        string `json:"owner,omitempty"`
}

WingEvent is sent to dashboard subscribers for wing/session lifecycle events.

type WingLocation added in v0.44.9

type WingLocation struct {
	MachineID    string
	UserID       string
	OrgID        string
	PublicKey    string
	Locked       bool
	AllowedCount int
	RegisteredAt time.Time
}

WingLocation tracks where a wing is connected across the cluster.

type WingMap added in v0.44.9

type WingMap struct {
	// contains filtered or unexported fields
}

WingMap is the global registry of all wings, stored on the login node.

func NewWingMap added in v0.44.9

func NewWingMap() *WingMap

func (*WingMap) All added in v0.44.9

func (m *WingMap) All() map[string]WingLocation

All returns a snapshot of all wings.

func (*WingMap) Deregister added in v0.44.9

func (m *WingMap) Deregister(wingID string)

func (*WingMap) EdgeIDs added in v0.44.9

func (m *WingMap) EdgeIDs() []string

EdgeIDs returns all known edge machine IDs, expiring dead edges (30s timeout).

func (*WingMap) Locate added in v0.44.9

func (m *WingMap) Locate(wingID string) (WingLocation, bool)

func (*WingMap) ReconcileFull added in v0.44.13

func (m *WingMap) ReconcileFull(machineID string, activeWings map[string]bool, snapshotAt time.Time)

ReconcileFull replaces wing state for a machine using the edge's authoritative snapshot. Wings registered AFTER snapshotAt are preserved (arrived via real-time event after snapshot).

func (*WingMap) Register added in v0.44.9

func (m *WingMap) Register(wingID string, loc WingLocation)

type WingRegistry

type WingRegistry struct {
	// contains filtered or unexported fields
}

WingRegistry tracks all connected wings.

func NewWingRegistry

func NewWingRegistry() *WingRegistry

func (*WingRegistry) Add

func (r *WingRegistry) Add(w *ConnectedWing)

func (*WingRegistry) All added in v0.23.0

func (r *WingRegistry) All() []*ConnectedWing

All returns all connected wings.

func (*WingRegistry) BroadcastAll added in v0.23.0

func (r *WingRegistry) BroadcastAll(ctx context.Context, data []byte)

BroadcastAll sends a message to every connected wing.

func (*WingRegistry) CloseAll added in v0.23.0

func (r *WingRegistry) CloseAll()

CloseAll closes all connected wing WebSockets.

func (*WingRegistry) CountForUser

func (r *WingRegistry) CountForUser(userID string) int

CountForUser returns the number of wings connected for a given user.

func (*WingRegistry) FindByID

func (r *WingRegistry) FindByID(wingID string) *ConnectedWing

func (*WingRegistry) ListForUser

func (r *WingRegistry) ListForUser(userID string) []*ConnectedWing

ListForUser returns all wings connected for a given user.

func (*WingRegistry) Remove

func (r *WingRegistry) Remove(id string) *ConnectedWing

func (*WingRegistry) Subscribe added in v0.7.5

func (r *WingRegistry) Subscribe(userID string, orgIDs []string, ch chan WingEvent)

Subscribe registers a dashboard subscriber with its org memberships. Events are delivered if the subscriber's userID matches the wing owner OR if the wing's orgID appears in the subscriber's orgIDs.

func (*WingRegistry) Touch

func (r *WingRegistry) Touch(wingID string)

func (*WingRegistry) Unsubscribe added in v0.7.5

func (r *WingRegistry) Unsubscribe(userID string, ch chan WingEvent)

func (*WingRegistry) UpdateConfig added in v0.37.0

func (r *WingRegistry) UpdateConfig(id string, locked bool, allowedCount int) *ConnectedWing

UpdateConfig updates a wing's lock state. Returns the wing for event dispatch.

func (*WingRegistry) UpdateUserOrgs added in v0.44.6

func (r *WingRegistry) UpdateUserOrgs(userID string, orgIDs []string) bool

UpdateUserOrgs updates the org list for all active subscribers of a user. Returns true if the user had any active subscribers (i.e., was worth updating).

Jump to

Keyboard shortcuts

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