relay

package
v0.30.1 Latest Latest
Warning

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

Go to latest
Published: Feb 14, 2026 License: MIT Imports: 36 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateOrLoadSecret

func GenerateOrLoadSecret(store *RelayStore, envSecret string) ([]byte, error)

GenerateOrLoadSecret returns the JWT signing secret. Priority: envSecret (from WT_JWT_SECRET) > relay_config DB > auto-generate.

func IssueWingJWT

func IssueWingJWT(secret []byte, userID, publicKey, wingID string) (string, time.Time, error)

IssueWingJWT creates a signed JWT for a wing connection.

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 SeedDefaultSkills

func SeedDefaultSkills(store *RelayStore) error

func WaitForWing added in v0.23.0

func WaitForWing(ctx context.Context, local *WingRegistry, peers *PeerDirectory, wingID string, timeout time.Duration) (machineID string, found bool)

WaitForWing waits for a wing to appear locally or via sync.

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 ChatRegistry

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

ChatRegistry tracks active chat sessions.

func NewChatRegistry

func NewChatRegistry() *ChatRegistry

func (*ChatRegistry) Add

func (r *ChatRegistry) Add(s *ChatSession)

func (*ChatRegistry) All added in v0.23.0

func (r *ChatRegistry) All() []*ChatSession

All returns all chat sessions.

func (*ChatRegistry) CountForUser

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

CountForUser returns the number of chat sessions for a given user.

func (*ChatRegistry) Get

func (r *ChatRegistry) Get(id string) *ChatSession

func (*ChatRegistry) ListForUser

func (r *ChatRegistry) ListForUser(userID string) []*ChatSession

ListForUser returns all chat sessions for a given user.

func (*ChatRegistry) Remove

func (r *ChatRegistry) Remove(id string)

type ChatSession

type ChatSession struct {
	ID          string
	WingID      string
	UserID      string
	Agent       string
	Status      string // "active", "exited"
	BrowserConn *websocket.Conn
	// contains filtered or unexported fields
}

ChatSession tracks a browser <-> wing chat connection through the relay.

type ClusterState added in v0.23.0

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

ClusterState tracks wings across all nodes (login node only). Edges send their full wing list every second; login merges and responds with the full cluster state. Dead nodes expire after 10s of silence.

func NewClusterState added in v0.23.0

func NewClusterState() *ClusterState

func (*ClusterState) All added in v0.23.0

func (c *ClusterState) All() []SyncWing

All returns all edge wings, expiring dead nodes first.

func (*ClusterState) Sync added in v0.23.0

func (c *ClusterState) Sync(nodeID string, wings []SyncWing) (others, all []SyncWing)

Sync replaces an edge's wing list, expires dead nodes, and returns:

  • others: all wings except the requesting edge's (for edge response)
  • all: all edge wings across all nodes (for login's PeerDirectory)

type ConnectedWing

type ConnectedWing struct {
	ID          string
	UserID      string
	WingID      string
	Hostname    string // display name from os.Hostname()
	Platform    string // runtime.GOOS from wing
	Version     string // build version from wing
	PublicKey   string
	Agents      []string
	Skills      []string
	Labels      []string
	Identities  []string
	Projects    []ws.WingProject
	EggConfig   string   // serialized YAML of wing's egg config
	OrgID       string   // org slug this wing serves (from --org flag)
	AllowEmails []string // explicit email allow list (from --allow flag)
	RootDir     string   // root directory constraint (from --root flag)
	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 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
	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 PTYRegistry

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

PTYRegistry tracks active PTY sessions.

func NewPTYRegistry

func NewPTYRegistry() *PTYRegistry

func (*PTYRegistry) Add

func (r *PTYRegistry) Add(s *PTYSession) bool

func (*PTYRegistry) All added in v0.23.0

func (r *PTYRegistry) All() []*PTYSession

All returns all PTY sessions.

func (*PTYRegistry) CountForUser

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

CountForUser returns the number of PTY sessions for a given user.

func (*PTYRegistry) Get

func (r *PTYRegistry) Get(id string) *PTYSession

func (*PTYRegistry) IsTombstoned added in v0.7.34

func (r *PTYRegistry) IsTombstoned(id string) bool

IsTombstoned returns true if the session was recently deleted.

func (*PTYRegistry) ListForUser

func (r *PTYRegistry) ListForUser(userID string) []*PTYSession

ListForUser returns all PTY sessions for a given user.

func (*PTYRegistry) Remove

func (r *PTYRegistry) Remove(id string)

func (*PTYRegistry) SyncFromWing added in v0.7.32

func (r *PTYRegistry) SyncFromWing(wingID, userID string, sessions []ws.SessionInfo, removeStale bool)

SyncFromWing reconciles the registry with the wing's authoritative session list. When removeStale is true (on-demand requests), removes sessions the wing no longer reports. When false (heartbeat), only adds missing sessions.

type PTYSession

type PTYSession struct {
	ID             string
	WingID         string
	UserID         string
	Agent          string
	CWD            string // working directory for this session
	EggConfig      string // serialized YAML of egg config at spawn time
	NeedsAttention bool   // terminal bell detected — session wants user attention
	Status         string // "active", "detached", "exited"
	BrowserConn    *websocket.Conn
	// contains filtered or unexported fields
}

PTYSession tracks a browser <-> wing PTY connection through the relay.

type PeerDirectory added in v0.23.0

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

PeerDirectory tracks wings on other nodes, updated via full-state sync.

func NewPeerDirectory added in v0.23.0

func NewPeerDirectory() *PeerDirectory

func (*PeerDirectory) AllWings added in v0.23.0

func (p *PeerDirectory) AllWings() []*PeerWing

AllWings returns all known remote wings.

func (*PeerDirectory) CountForUser added in v0.23.0

func (p *PeerDirectory) CountForUser(userID string) int

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

func (*PeerDirectory) FindByWingID added in v0.27.0

func (p *PeerDirectory) FindByWingID(wingID string) *PeerWing

FindByWingID looks up a peer wing by its persistent wing ID.

func (*PeerDirectory) FindWing added in v0.23.0

func (p *PeerDirectory) FindWing(wingID string) *PeerWing

FindWing looks up a wing by ID.

func (*PeerDirectory) RemoveWing added in v0.23.0

func (p *PeerDirectory) RemoveWing(wingID string)

RemoveWing removes a single wing.

func (*PeerDirectory) Replace added in v0.23.0

func (p *PeerDirectory) Replace(wings []*PeerWing) (added, removed []*PeerWing)

Replace swaps the entire wing set and returns the diff.

func (*PeerDirectory) UpdateCh added in v0.23.0

func (p *PeerDirectory) UpdateCh() <-chan struct{}

UpdateCh returns a channel signaled on updates.

type PeerWing added in v0.23.0

type PeerWing struct {
	WingID    string
	MachineID string // Fly machine hosting this wing (for fly-replay)
	NodeID    string
	WingInfo  *WingInfo
}

PeerWing represents a wing connected to a remote node.

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, error)

ConsumeOrgInvite validates a token, marks it claimed, returns the invite info.

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) 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 string) error

CreateOrgInvite creates a pending invite.

func (*RelayStore) CreateSession

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

func (*RelayStore) CreateSkill

func (s *RelayStore) CreateSkill(name, description, category, agent, tags, content, sha256, publisher, sourceURL string, weight int) 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) DeleteSession

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

func (*RelayStore) DeleteSkill

func (s *RelayStore) DeleteSkill(name string) error

func (*RelayStore) DeleteToken

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

func (*RelayStore) DequeueTask

func (s *RelayStore) DequeueTask(taskID string) error

DequeueTask deletes a task from the queue after dispatch.

func (*RelayStore) EnqueueTask

func (s *RelayStore) EnqueueTask(t *ws.QueuedTask) error

EnqueueTask stores a task in the offline queue. The payload is opaque JSON forwarded to the wing as-is — the relay never inspects it.

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) 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) GetSkill

func (s *RelayStore) GetSkill(name string) (*SkillRow, 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) 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) ListPendingInvites added in v0.23.0

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

ListPendingInvites returns unclaimed invites for an org.

func (*RelayStore) ListPendingTasks

func (s *RelayStore) ListPendingTasks(userID string) ([]*ws.QueuedTask, error)

ListPendingTasks returns queued tasks for a user that haven't been dispatched.

func (*RelayStore) ListSkills

func (s *RelayStore) ListSkills(category string) ([]*SkillRow, error)

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) SearchSkills

func (s *RelayStore) SearchSkills(query string) ([]*SkillRow, error)

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) 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) 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

	Wings     *WingRegistry
	PTY       *PTYRegistry
	Chat      *ChatRegistry
	Bandwidth *BandwidthMeter
	RateLimit *RateLimiter

	// Cluster sync (multi-node)
	Peers   *PeerDirectory
	Cluster *ClusterState

	EntitlementCache *EntitlementCache
	// contains filtered or unexported fields
}

func NewServer

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

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) 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) 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) 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) StartClusterExpiry added in v0.23.0

func (s *Server) StartClusterExpiry(ctx context.Context)

StartClusterExpiry periodically expires dead edge nodes on the login node.

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 sync loop.

func (*Server) SubmitTask

func (s *Server) SubmitTask(ctx context.Context, userID, identity, taskID string, payload []byte) error

SubmitTask routes a task to a connected wing or queues it for offline delivery. The relay only sees the task ID and routing info — never the content.

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
	JWTSecret          string // base64-encoded; overrides DB-stored secret
	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
}

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

SessionValidation is the response from the session validation endpoint.

type SkillRow

type SkillRow struct {
	Name        string
	Description string
	Category    string
	Agent       string
	Tags        string
	Content     string
	SHA256      string
	Publisher   string
	SourceURL   string
	Weight      int
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

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 SyncRequest added in v0.23.0

type SyncRequest struct {
	NodeID    string           `json:"node_id"`
	Wings     []SyncWing       `json:"wings"`
	Bandwidth map[string]int64 `json:"bandwidth,omitempty"` // userID → bytes since last sync
}

SyncRequest is sent from edge to login every sync interval.

type SyncResponse added in v0.23.0

type SyncResponse struct {
	Wings       []SyncWing `json:"wings"`
	BannedUsers []string   `json:"banned_users,omitempty"`
}

SyncResponse is the full cluster state returned from login.

type SyncWing added in v0.23.0

type SyncWing struct {
	WingID string   `json:"wing_id"`
	NodeID string   `json:"node_id"`
	Info   WingInfo `json:"info"`
}

SyncWing is a wing entry in the cluster sync protocol.

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
}

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(secret []byte, tokenString string) (*WingClaims, error)

ValidateWingJWT verifies a JWT and returns the claims.

type WingEvent added in v0.7.5

type WingEvent struct {
	Type      string           `json:"type"` // "wing.online" or "wing.offline"
	ConnID    string           `json:"conn_id"`
	WingID    string           `json:"wing_id"`
	Hostname  string           `json:"hostname,omitempty"`
	Platform  string           `json:"platform,omitempty"`
	Version   string           `json:"version,omitempty"`
	Agents    []string         `json:"agents,omitempty"`
	Labels    []string         `json:"labels,omitempty"`
	PublicKey string           `json:"public_key,omitempty"`
	Projects  []ws.WingProject `json:"projects,omitempty"`
}

WingEvent is sent to dashboard subscribers when a wing connects or disconnects.

type WingInfo added in v0.23.0

type WingInfo struct {
	UserID    string           `json:"user_id"`
	WingID    string           `json:"wing_id,omitempty"`
	Hostname  string           `json:"hostname,omitempty"`
	Platform  string           `json:"platform,omitempty"`
	Version   string           `json:"version,omitempty"`
	Agents    []string         `json:"agents,omitempty"`
	Labels    []string         `json:"labels,omitempty"`
	Projects  []ws.WingProject `json:"projects,omitempty"`
	PublicKey string           `json:"public_key,omitempty"`
	OrgID     string           `json:"org_id,omitempty"`
}

WingInfo contains summary info about a wing for cluster sync.

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) FindByIdentity

func (r *WingRegistry) FindByIdentity(identity string) *ConnectedWing

func (*WingRegistry) FindForUser

func (r *WingRegistry) FindForUser(userID string) *ConnectedWing

func (*WingRegistry) ListForUser

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

ListForUser returns all wings connected for a given user.

func (*WingRegistry) RegisterAuditRequest added in v0.23.0

func (r *WingRegistry) RegisterAuditRequest(reqID string, ch chan any)

func (*WingRegistry) RegisterDirRequest added in v0.6.3

func (r *WingRegistry) RegisterDirRequest(reqID string, ch chan *ws.DirResults)

func (*WingRegistry) RegisterHistoryRequest added in v0.23.0

func (r *WingRegistry) RegisterHistoryRequest(reqID string, ch chan *ws.SessionsHistoryResults)

func (*WingRegistry) RegisterSessionRequest added in v0.7.32

func (r *WingRegistry) RegisterSessionRequest(reqID string, ch chan *ws.SessionsSync)

func (*WingRegistry) Remove

func (r *WingRegistry) Remove(id string)

func (*WingRegistry) ResolveAuditMessage added in v0.23.0

func (r *WingRegistry) ResolveAuditMessage(reqID string, msg any)

func (*WingRegistry) ResolveDirRequest added in v0.6.3

func (r *WingRegistry) ResolveDirRequest(reqID string, results *ws.DirResults)

func (*WingRegistry) ResolveHistoryRequest added in v0.23.0

func (r *WingRegistry) ResolveHistoryRequest(reqID string, results *ws.SessionsHistoryResults)

func (*WingRegistry) ResolveSessionRequest added in v0.7.32

func (r *WingRegistry) ResolveSessionRequest(reqID string, results *ws.SessionsSync)

func (*WingRegistry) Subscribe added in v0.7.5

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

func (*WingRegistry) Touch

func (r *WingRegistry) Touch(wingID string)

func (*WingRegistry) UnregisterAuditRequest added in v0.23.0

func (r *WingRegistry) UnregisterAuditRequest(reqID string)

func (*WingRegistry) UnregisterDirRequest added in v0.6.3

func (r *WingRegistry) UnregisterDirRequest(reqID string)

func (*WingRegistry) UnregisterHistoryRequest added in v0.23.0

func (r *WingRegistry) UnregisterHistoryRequest(reqID string)

func (*WingRegistry) UnregisterSessionRequest added in v0.7.32

func (r *WingRegistry) UnregisterSessionRequest(reqID string)

func (*WingRegistry) Unsubscribe added in v0.7.5

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

Jump to

Keyboard shortcuts

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