relay

package
v0.149.5 Latest Latest
Warning

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

Go to latest
Published: Oct 12, 2026 License: MIT Imports: 55 Imported by: 0

Documentation

Index

Constants

View Source
const (
	RelayPolicyLegacy     = "legacy"
	RelayPolicyDirectFree = "direct-free"
)
View Source
const (
	SustainedRate = 375_000 // 3 Mbit/s — all tiers

)

Tier rate/cap constants. Hosted-payload cohorts share the same sustained rate (3 Mbit/s) — this is a reasonable ceiling for compressed terminal PTY, not a paid differentiator. Legacy/migration free relay access retains its 1 GiB monthly cap; pro/team is unlimited. Direct-only coordination never consumes or consults that cap.

Variables

View Source
var (
	ErrActivePersonalSubscription = errors.New("user already has an active personal subscription")
	ErrActiveOrgSubscription      = errors.New("organization has an active subscription")
	ErrDeviceUserCodeExists       = errors.New("device user code already exists")
	ErrOrgInviteExists            = errors.New("organization invite already exists")
	ErrOrgLimitReached            = errors.New("organization ownership limit reached")
	ErrOrgMutationUnauthorized    = errors.New("organization mutation is not authorized")
	ErrOrgOwnerRemoval            = errors.New("organization owner cannot be removed")
	ErrOrgSeatsNotIncreased       = errors.New("organization seats were not increased")
)

Functions

func DeriveECKeyStringFromSecret added in v0.140.0

func DeriveECKeyStringFromSecret(secret string) (string, error)

DeriveECKeyStringFromSecret deterministically derives a P-256 signing key from an existing high-entropy deployment secret. Rejection sampling avoids modulo bias, while the context string domain-separates this key from any other use of the same secret.

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 IssueMCPJWT added in v0.140.0

func IssueMCPJWT(key *ecdsa.PrivateKey, userID, issuer, resource, clientID string) (string, time.Time, error)

IssueMCPJWT mints a short-lived bearer token for exactly one MCP resource and client.

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.

func (*BandwidthMeter) WaitRate added in v0.145.0

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

WaitRate applies the shared sustained-rate boundary without consuming or consulting the hosted-payload monthly quota. The direct tier uses this for its tightly bounded discovery, passkey, and WebRTC signaling responses: an account that previously exhausted relay bytes must still be able to establish a direct connection.

type ConnectedWing

type ConnectedWing struct {
	ID             string
	UserID         string
	WingID         string
	PublicKey      string
	OrgID          string // org ID this wing serves
	Locked         bool
	AllowedCount   int
	PurposeBinding bool
	DirectMCP      bool
	HostedRelay    string
	// Revision is local to one connection generation. It starts at one and
	// increases whenever policy/capability state changes, allowing split-node
	// snapshots and real-time events to be ordered without comparing host clocks.
	Revision    uint64
	Conn        *websocket.Conn
	ConnectedAt time.Time
	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, internalSecret ...string) *EntitlementCache

func (*EntitlementCache) GetEnrollment added in v0.145.0

func (c *EntitlementCache) GetEnrollment(userID string) (bool, bool)

GetEnrollment returns the authoritative private-roost enrollment decision. N-1 login nodes did not publish such a decision, so callers opting into the new enrollment boundary must fail closed until the login node is upgraded.

func (*EntitlementCache) GetRelayAccess added in v0.145.0

func (c *EntitlementCache) GetRelayAccess(userID string) RelayAccess

GetRelayAccess returns the login node's cached hosted relay decision. A missing entry fails closed while directory and signaling remain available.

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"`
	RelayAllowed bool   `json:"relay_allowed"`
	RelayReason  string `json:"relay_reason"`
	Enrolled     bool   `json:"enrolled"`
}

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"`
	TokenUse string `json:"token_use"`
}

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

type MCPClaims added in v0.140.0

type MCPClaims struct {
	jwt.RegisteredClaims
	TokenUse string `json:"token_use"`
	ClientID string `json:"client_id"`
}

MCPClaims are deliberately distinct from wing connection credentials. Audience binds the token to one MCP resource, while TokenUse prevents another JWT class from crossing surfaces.

func ValidateMCPJWT added in v0.140.0

func ValidateMCPJWT(pubKey *ecdsa.PublicKey, tokenString, issuer, resource string) (*MCPClaims, error)

ValidateMCPJWT accepts only an ES256 MCP token issued for this authorization server and audience. General wing, handoff, and database tokens are not valid MCP credentials.

type MCPClientRegistration added in v0.140.0

type MCPClientRegistration struct {
	ClientID     string
	ClientName   string
	RedirectURIs []string
	ExpiresAt    time.Time
}

MCPClientRegistration is durable Dynamic Client Registration state. Claude Code keeps client IDs across MCP logout/remove operations, so registrations must outlive a roost restart.

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 {
	ControllerID        string
	PendingControllerID string
	BrowserConn         *websocket.Conn            // authorized controller (can send input)
	PendingController   *websocket.Conn            // attach awaiting wing authorization
	PendingUserID       string                     // promoted with PendingController
	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
	Provisional         bool                       // route recreated by attach, not yet confirmed by wing
	CreatedAt           time.Time                  // set on provisional creation, for cap/expiry sweeps
	// 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) AddControllerStart added in v0.145.0

func (r *PTYRoutes) AddControllerStart(sessionID string, route *PTYRoute) bool

AddControllerStart reserves a bounded provisional route for a new session. It becomes durable only after the wing confirms pty.started. This prevents a silent or disconnected wing from letting authenticated start attempts pin unbounded relay state.

func (*PTYRoutes) AddViewer added in v0.137.0

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

AddViewer adds a spectator connection to a session route, creating the minimal route when the relay restarted after the egg was created.

func (*PTYRoutes) AuthenticationWing added in v0.144.0

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

AuthenticationWing returns the route's wing only when conn owns the attach attempt identified by viewerID. Controller attempts use an empty viewer ID.

func (*PTYRoutes) CanAuthenticate added in v0.144.0

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

CanAuthenticate reports whether conn owns this route's controller attach or the exact spectator viewer ID carried by a passkey response.

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) ControllerWing added in v0.144.0

func (r *PTYRoutes) ControllerWing(sessionID string, conn *websocket.Conn) (string, bool)

ControllerWing returns the route's wing only when conn is the controller the wing has already authorized. Pending reattach attempts and spectators cannot drive session input or lifecycle messages.

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. Snapshot route ownership before I/O so one slow browser cannot hold the global routing lock and stall every session on the relay.

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)

func (*PTYRoutes) SetPendingController added in v0.144.0

func (r *PTYRoutes) SetPendingController(sessionID, wingID, userID string, conn *websocket.Conn, controllerIDs ...string) bool

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 RelayAccess added in v0.145.0

type RelayAccess struct {
	Allowed bool   `json:"allowed"`
	Reason  string `json:"reason"`
}

type RelayStore

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

func OpenRelay

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

func (*RelayStore) AcceptOrgInvite added in v0.145.0

func (s *RelayStore) AcceptOrgInvite(token, userID, expectedEmail, entitlementID string) (string, string, bool, error)

AcceptOrgInvite consumes an invite, installs the membership, and grants an available seat from the org's active subscription in one transaction. A full org or failed entitlement grant must not burn the invite or leave a partially installed membership.

func (*RelayStore) ActivateOrgSubscription added in v0.145.0

func (s *RelayStore) ActivateOrgSubscription(sub *Subscription, orgID string, entitlements []*Entitlement) ([]string, error)

ActivateOrgSubscription creates a team subscription, updates the seat limit, and grants the initial member entitlements as one transaction.

func (*RelayStore) ActivateSubscription added in v0.145.0

func (s *RelayStore) ActivateSubscription(sub *Subscription, ent *Entitlement) error

ActivateSubscription installs a subscription, its first entitlement, and the denormalized user tier as one transaction. Callers must not expose a successful upgrade if only part of that state reached disk.

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) CancelOrgSubscription added in v0.145.0

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

CancelOrgSubscription cancels the subscription, removes its grants, and recomputes compatibility tiers atomically. It returns users whose cached entitlement state should be invalidated.

func (*RelayStore) CancelPersonalSubscription added in v0.145.0

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

CancelPersonalSubscription cancels one user's active personal subscription, removes its grant, and recomputes the compatibility tier atomically. The user constraint prevents a stale or forged subscription ID from canceling another account's subscription.

func (*RelayStore) ClaimDeviceCode

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

func (*RelayStore) ClearUserEmail added in v0.145.0

func (s *RelayStore) ClearUserEmail(userID string) error

ClearUserEmail removes a provider address that can no longer be verified. This is authorization state when a private roost has an enrollment list, so callers must not leave a previously allowed address in place after a denied provider callback.

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) CountMCPClientRegistrations added in v0.140.0

func (s *RelayStore) CountMCPClientRegistrations(now time.Time) (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. Repeated and concurrent calls return the same token.

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 one-seat org and adds the owner as an owner member.

func (*RelayStore) CreateOrgForOwnerLimited added in v0.145.0

func (s *RelayStore) CreateOrgForOwnerLimited(id, name, slug, ownerUserID string, maxSeats, limit int) error

CreateOrgForOwnerLimited creates an organization only if the owner remains below limit. The count and inserts share one immediate transaction so concurrent requests cannot exceed the per-owner product limit.

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) CreateOrgWithSeats added in v0.145.0

func (s *RelayStore) CreateOrgWithSeats(id, name, slug, ownerUserID string, maxSeats int) error

CreateOrgWithSeats creates an org and its owner membership atomically.

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) DeleteLabelAuthorized added in v0.145.0

func (s *RelayStore) DeleteLabelAuthorized(targetID, scopeType, scopeID, actorUserID string, actorOwnsTarget bool) error

DeleteLabelAuthorized applies the same commit-time authorization as SetLabelAuthorized before deleting the scoped label.

func (*RelayStore) DeleteOrg added in v0.32.5

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

DeleteOrg removes an org and all its members, invites, and scoped labels. The active-subscription check lives in the same immediate transaction as the deletion so callers cannot race an upgrade between a preflight check and the destructive write.

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) EnsurePersonalSubscription added in v0.145.0

func (s *RelayStore) EnsurePersonalSubscription(sub *Subscription, ent *Entitlement) (*Subscription, bool, error)

EnsurePersonalSubscription atomically creates a personal subscription and entitlement, or repairs the entitlement on the user's existing active subscription. It returns the effective subscription and whether it was newly created.

func (*RelayStore) ExchangeClaimedDeviceCode added in v0.145.0

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

ExchangeClaimedDeviceCode stores a token and consumes its approved device code in one immediate transaction. A device code is a one-time credential: concurrent or repeated polls must never mint multiple usable bearer tokens. If token creation fails, the transaction restores the device code so the client can safely retry.

func (*RelayStore) ExpandOrgSubscription added in v0.145.0

func (s *RelayStore) ExpandOrgSubscription(subID, orgID string, seats int, entitlements []*Entitlement) ([]string, error)

ExpandOrgSubscription changes the subscription and org seat limits together with any newly available member grants.

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) GetMCPClientRegistration added in v0.140.0

func (s *RelayStore) GetMCPClientRegistration(clientID string, now time.Time) (*MCPClientRegistration, error)

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) GrantEntitlement added in v0.145.0

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

GrantEntitlement atomically installs an entitlement and reflects it in the user's compatibility tier field.

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) RemoveOrgMemberAndEntitlement added in v0.145.0

func (s *RelayStore) RemoveOrgMemberAndEntitlement(orgID, userID string) (bool, error)

RemoveOrgMemberAndEntitlement removes a membership and any grant from the org's active subscription in one transaction. It returns whether a grant was removed so callers can invalidate cached entitlement state.

func (*RelayStore) RemoveOrgMemberAuthorized added in v0.145.0

func (s *RelayStore) RemoveOrgMemberAuthorized(orgID, actorUserID, targetUserID string) (bool, error)

RemoveOrgMemberAuthorized rechecks the actor's current organization role in the same immediate transaction as the removal. This prevents a cached or preflight admin role from surviving a concurrent demotion.

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) RevokeEntitlement added in v0.145.0

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

RevokeEntitlement atomically removes one grant and recalculates the user's compatibility tier from their remaining active entitlements.

func (*RelayStore) RevokeOrgInvite added in v0.32.4

func (s *RelayStore) RevokeOrgInvite(orgID, token string) (bool, error)

RevokeOrgInvite deletes a pending invite only when it belongs to orgID. It returns false when the token is absent, already claimed, or belongs to a different org so callers cannot authorize one tenant and mutate another.

func (*RelayStore) RevokeOrgInviteAuthorized added in v0.145.0

func (s *RelayStore) RevokeOrgInviteAuthorized(orgID, token, actorUserID string) (bool, error)

RevokeOrgInviteAuthorized rechecks the actor's current role in the same transaction as the tenant-scoped delete.

func (*RelayStore) RotateDeviceToken added in v0.145.0

func (s *RelayStore) RotateDeviceToken(oldToken, newToken, userID, deviceID string, expiresAt *time.Time) error

RotateDeviceToken replaces an existing token without creating a window in which neither the old nor new credential exists.

func (*RelayStore) SaveMCPClientRegistration added in v0.140.0

func (s *RelayStore) SaveMCPClientRegistration(reg MCPClientRegistration) error

func (*RelayStore) SaveMCPClientRegistrationLimited added in v0.145.0

func (s *RelayStore) SaveMCPClientRegistrationLimited(reg MCPClientRegistration, now time.Time, limit int) (bool, error)

SaveMCPClientRegistrationLimited prunes expired registrations, checks the durable capacity, and installs the new public client in one transaction. Keeping these operations together prevents concurrent anonymous DCR calls from racing past the resource bound.

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) SetLabelAuthorized added in v0.145.0

func (s *RelayStore) SetLabelAuthorized(targetID, scopeType, scopeID, actorUserID string, actorOwnsTarget bool, label string) error

SetLabelAuthorized applies a label only if the actor still owns the target or, for an organization-scoped target, is still an organization manager. Authorization and the write share one immediate transaction.

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, enrolled authenticated users can access the embedded service wing

	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) EnableMCP added in v0.140.0

func (s *Server) EnableMCP(runner *egg.ToolRunner, policy *mcp.Policy, nativeTools ...mcp.NativeTool)

EnableMCP wires owner-scoped native controls and optional role-scoped executable tools onto POST /mcp with OAuth bearer authentication.

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) MCPEnabled added in v0.140.0

func (s *Server) MCPEnabled() bool

MCPEnabled reports whether the MCP surface is configured.

func (*Server) MachineID added in v0.23.0

func (s *Server) MachineID() string

MachineID returns this node's unique machine identifier.

func (*Server) PortalNativeMCPTools added in v0.145.0

func (s *Server) PortalNativeMCPTools(embeddedWingID string) []mcp.NativeTool

PortalNativeMCPTools adapts the portal-owned portion of the shared control contract. embeddedWingID names the one wing whose runtime is hosted by this roost process; connected external wings remain inventory-only until routed control is implemented.

func (*Server) ReloadMCP added in v0.140.0

func (s *Server) ReloadMCP(runner *egg.ToolRunner, policy *mcp.Policy)

ReloadMCP atomically replaces the authorization policy and tool runner. In-flight requests finish on the previous immutable snapshot; new requests see both replacements.

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
	InternalSecret       string // optional secret for /internal/*; distinct from JWT signing material
	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)
	RelayPolicy          string // "legacy" or "direct-free"
	RelayMigrationBefore time.Time
	// RoostAllowedEmails is an optional, exact email allowlist for private OAuth
	// gateways and all-in-one roosts.
	// Empty preserves the historical behavior where completing OAuth enrolls a
	// user. Local mode does not consult this list.
	RoostAllowedEmails []string
}

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(internalSecret ...string) *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) UpdateUserOrgContext added in v0.145.0

func (sc *SessionCache) UpdateUserOrgContext(userID string, orgIDs []string, orgRoles map[string]string)

UpdateUserOrgContext replaces the cached memberships and exact roles for a user after an authoritative single-user refresh. Unlike UpdateUserOrgs, which consumes the legacy bulk endpoint and must retain known roles for backwards compatibility, this method applies demotions immediately.

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"`
	Email        string            `json:"email,omitempty"`
	Tier         string            `json:"tier"`
	RelayAllowed bool              `json:"relay_allowed"`
	RelayReason  string            `json:"relay_reason"`
	OrgIDs       []string          `json:"org_ids"`
	OrgRoles     map[string]string `json:"org_roles,omitempty"`
}

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
	OrgRoles    map[string]string // transient: org ID to authenticated role 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"`
	TokenUse  string `json:"token_use"`
}

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"`
	PurposeBinding *bool   `json:"purpose_binding,omitempty"`
	DirectMCP      *bool   `json:"direct_mcp,omitempty"`
	HostedRelay    *string `json:"hosted_relay,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
	ConnectionID   string
	UserID         string
	OrgID          string
	PublicKey      string
	Locked         bool
	AllowedCount   int
	PurposeBinding bool
	DirectMCP      bool
	HostedRelay    string
	GenerationAt   time.Time
	Revision       uint64
	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) DeregisterConnection added in v0.145.0

func (m *WingMap) DeregisterConnection(wingID, connectionID string) bool

DeregisterConnection removes a wing only when the event belongs to the currently published socket generation. Empty connection IDs preserve the legacy unconditional behavior during rolling upgrades.

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) IsCurrentEvent added in v0.145.0

func (m *WingMap) IsCurrentEvent(wingID, machineID, connectionID string, revision uint64, exactRevision bool) bool

IsCurrentEvent checks that a current-node lifecycle event still belongs to the directory generation installed by its preceding registration. Legacy events omit connectionID and remain accepted during rolling upgrades.

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) ReconcileSnapshot added in v0.145.0

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

ReconcileSnapshot applies one edge's authoritative snapshot without letting it steal a wing that has since registered on another machine or overwrite a same-machine connection/config generation established after the snapshot began. Current edges provide GenerationAt and Revision from their own clock and registry; RegisteredAt remains only as an N-1 compatibility fallback.

func (*WingMap) Register added in v0.44.9

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

Register publishes one real-time wing generation. Current nodes include a connection ID, edge-local generation time, and per-connection revision. Use those fields to keep a delayed request from an older socket or config update from rolling the login-node directory backward. N-1 requests omit the connection ID and retain their last-arrival behavior during rolling upgrades.

type WingRegistry

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

WingRegistry tracks all connected wings.

func NewWingRegistry

func NewWingRegistry() *WingRegistry

func (*WingRegistry) Activate added in v0.145.0

func (r *WingRegistry) Activate(w *ConnectedWing) (*ConnectedWing, []*ConnectedWing, bool)

Activate publishes a connection and removes older overlapping sockets for the same stable wing ID. Reconnects can briefly overlap; only the newest generation may publish policy or runtime events.

func (*WingRegistry) Add

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) RemoveConnection added in v0.145.0

func (r *WingRegistry) RemoveConnection(id string) (*ConnectedWing, bool)

RemoveConnection reports whether removing this socket took the stable wing offline, as opposed to completing an old side of a reconnect overlap.

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(connectionID 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, directMCP bool, hostedRelay string) *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