Documentation
¶
Index ¶
- Constants
- Variables
- func DeriveECKeyStringFromSecret(secret string) (string, error)
- func GenerateECKey() (*ecdsa.PrivateKey, string, error)
- func IssueHandoffJWT(key *ecdsa.PrivateKey, userID, email, orgRole string) (string, error)
- func IssueMCPJWT(key *ecdsa.PrivateKey, userID, issuer, resource, clientID string) (string, time.Time, error)
- func IssueWingJWT(key *ecdsa.PrivateKey, userID, publicKey, wingID string) (string, time.Time, error)
- func MarshalECPublicKey(pub *ecdsa.PublicKey) (string, error)
- func NewLoginProxy(loginAddr string) *httputil.ReverseProxy
- func ParseECKeyFromEnv(envValue string) (*ecdsa.PrivateKey, error)
- func ParseECPublicKey(data string) (*ecdsa.PublicKey, error)
- type BandwidthMeter
- func (b *BandwidthMeter) AddUsage(userID string, bytes int64)
- func (b *BandwidthMeter) DrainCounters() map[string]int64
- func (b *BandwidthMeter) ExceededMonthly(userID string) bool
- func (b *BandwidthMeter) ExceededUsers() []string
- func (b *BandwidthMeter) InvalidateUser(userID string)
- func (b *BandwidthMeter) IsExceeded(userID string) bool
- func (b *BandwidthMeter) MonthlyUsage(userID string) int64
- func (b *BandwidthMeter) SeedFromDB()
- func (b *BandwidthMeter) SetExceeded(userIDs []string)
- func (b *BandwidthMeter) SetTierLookup(fn TierLookup)
- func (b *BandwidthMeter) StartSync(ctx context.Context, interval time.Duration)
- func (b *BandwidthMeter) Wait(ctx context.Context, userID string, n int) error
- func (b *BandwidthMeter) WaitRate(ctx context.Context, userID string, n int) error
- type ConnectedWing
- type DeviceCodeRow
- type Entitlement
- type EntitlementCache
- type EntitlementEntry
- type HandoffClaims
- type MCPClaims
- type MCPClientRegistration
- type NtfyConfig
- type Org
- type OrgInvite
- type OrgMember
- type PTYRoute
- type PTYRoutes
- func (r *PTYRoutes) AddControllerStart(sessionID string, route *PTYRoute) bool
- func (r *PTYRoutes) AddViewer(sessionID, viewerID, wingID string, conn *websocket.Conn) bool
- func (r *PTYRoutes) AuthenticationWing(sessionID, viewerID string, conn *websocket.Conn) (string, bool)
- func (r *PTYRoutes) CanAuthenticate(sessionID, viewerID string, conn *websocket.Conn) bool
- func (r *PTYRoutes) ClearBrowser(conn *websocket.Conn)
- func (r *PTYRoutes) ControllerWing(sessionID string, conn *websocket.Conn) (string, bool)
- func (r *PTYRoutes) Get(sessionID string) *PTYRoute
- func (r *PTYRoutes) IsSpectator(conn *websocket.Conn) bool
- func (r *PTYRoutes) NotifyWingOffline(wingID string)
- func (r *PTYRoutes) Remove(sessionID string)
- func (r *PTYRoutes) RemoveViewer(sessionID, viewerID string)
- func (r *PTYRoutes) Set(sessionID string, route *PTYRoute)
- func (r *PTYRoutes) SetPendingController(sessionID, wingID, userID string, conn *websocket.Conn, ...) bool
- type PasskeyCredential
- type RateLimiter
- type RelayAccess
- type RelayStore
- func (s *RelayStore) AcceptOrgInvite(token, userID, expectedEmail, entitlementID string) (string, string, bool, error)
- func (s *RelayStore) ActivateOrgSubscription(sub *Subscription, orgID string, entitlements []*Entitlement) ([]string, error)
- func (s *RelayStore) ActivateSubscription(sub *Subscription, ent *Entitlement) error
- func (s *RelayStore) AddOrgMember(orgID, userID, role string) error
- func (s *RelayStore) AppendAudit(userID, event string, detail *string) error
- func (s *RelayStore) BackfillProUsers() error
- func (s *RelayStore) CancelOrgSubscription(subID string) ([]string, error)
- func (s *RelayStore) CancelPersonalSubscription(subID, userID string) (string, error)
- func (s *RelayStore) ClaimDeviceCode(code, userID string) error
- func (s *RelayStore) ClearUserEmail(userID string) error
- func (s *RelayStore) Close() error
- func (s *RelayStore) ConsumeMagicLink(token string) (string, error)
- func (s *RelayStore) ConsumeOrgInvite(token string) (string, string, string, error)
- func (s *RelayStore) CountEntitlementsBySub(subID string) (int, error)
- func (s *RelayStore) CountMCPClientRegistrations(now time.Time) (int, error)
- func (s *RelayStore) CountOrgMembers(orgID string) (int, error)
- func (s *RelayStore) CountOrgsOwnedByUser(userID string) (int, error)
- func (s *RelayStore) CreateDeviceCode(code, userCode, deviceID string, expiresAt time.Time) error
- func (s *RelayStore) CreateDeviceCodeWithKey(code, userCode, deviceID, publicKey string, expiresAt time.Time) error
- func (s *RelayStore) CreateDeviceToken(token, userID, deviceID string, expiresAt *time.Time) error
- func (s *RelayStore) CreateEntitlement(ent *Entitlement) error
- func (s *RelayStore) CreateLocalUser() (*User, string, error)
- func (s *RelayStore) CreateMagicLink(id, email, token string, expiresAt time.Time) error
- func (s *RelayStore) CreateOrg(id, name, slug, ownerUserID string) error
- func (s *RelayStore) CreateOrgForOwnerLimited(id, name, slug, ownerUserID string, maxSeats, limit int) error
- func (s *RelayStore) CreateOrgInvite(id, orgID, email, token, invitedBy, role string) error
- func (s *RelayStore) CreateOrgWithSeats(id, name, slug, ownerUserID string, maxSeats int) error
- func (s *RelayStore) CreatePasskeyCredential(id, userID string, credentialID, publicKey []byte, label string) error
- func (s *RelayStore) CreateServiceUser() (*User, string, error)
- func (s *RelayStore) CreateSession(token, userID string, expiresAt time.Time) error
- func (s *RelayStore) CreateSubscription(sub *Subscription) error
- func (s *RelayStore) CreateUser(id string) error
- func (s *RelayStore) CreateUserDev() (*User, error)
- func (s *RelayStore) DB() *sql.DB
- func (s *RelayStore) DeleteEntitlementByUserAndSub(userID, subID string) error
- func (s *RelayStore) DeleteEntitlementsBySub(subID string) ([]string, error)
- func (s *RelayStore) DeleteLabel(targetID, scopeType, scopeID string) error
- func (s *RelayStore) DeleteLabelAuthorized(targetID, scopeType, scopeID, actorUserID string, actorOwnsTarget bool) error
- func (s *RelayStore) DeleteOrg(orgID string) error
- func (s *RelayStore) DeletePasskeyCredential(id, userID string) error
- func (s *RelayStore) DeleteSession(token string) error
- func (s *RelayStore) DeleteToken(token string) error
- func (s *RelayStore) EnsurePersonalSubscription(sub *Subscription, ent *Entitlement) (*Subscription, bool, error)
- func (s *RelayStore) ExchangeClaimedDeviceCode(code, token, userID, deviceID string, expiresAt *time.Time) (bool, error)
- func (s *RelayStore) ExpandOrgSubscription(subID, orgID string, seats int, entitlements []*Entitlement) ([]string, error)
- func (s *RelayStore) GetActiveOrgSubscription(orgID string) (*Subscription, error)
- func (s *RelayStore) GetActivePersonalSubscription(userID string) (*Subscription, error)
- func (s *RelayStore) GetDeviceCode(code string) (*DeviceCodeRow, error)
- func (s *RelayStore) GetDeviceCodeByUserCode(userCode string) (*DeviceCodeRow, error)
- func (s *RelayStore) GetInviteByToken(token string) (*OrgInvite, error)
- func (s *RelayStore) GetMCPClientRegistration(clientID string, now time.Time) (*MCPClientRegistration, error)
- func (s *RelayStore) GetNtfyConfig(userID string) (NtfyConfig, error)
- func (s *RelayStore) GetOrCreateUserByEmail(email string) (*User, error)
- func (s *RelayStore) GetOrgByID(id string) (*Org, error)
- func (s *RelayStore) GetOrgBySlug(slug string) (*Org, error)
- func (s *RelayStore) GetOrgMemberRole(orgID, userID string) string
- func (s *RelayStore) GetRelayConfig(key string) (string, error)
- func (s *RelayStore) GetSession(token string) (*User, error)
- func (s *RelayStore) GetUserByEmail(email string) (*User, error)
- func (s *RelayStore) GetUserByID(id string) (*User, error)
- func (s *RelayStore) GetUserByProvider(provider, providerID string) (*User, error)
- func (s *RelayStore) GrantEntitlement(ent *Entitlement) error
- func (s *RelayStore) HasPersonalSubscription(userID string) bool
- func (s *RelayStore) IsOrgMember(orgID, userID string) bool
- func (s *RelayStore) IsUserPro(userID string) bool
- func (s *RelayStore) ListOrgMembers(orgID string) ([]*OrgMember, error)
- func (s *RelayStore) ListOrgsForUser(userID string) ([]*Org, error)
- func (s *RelayStore) ListPasskeyCredentials(userID string) ([]*PasskeyCredential, error)
- func (s *RelayStore) ListPendingInvites(orgID string) ([]*OrgInvite, error)
- func (s *RelayStore) RemoveOrgMemberAndEntitlement(orgID, userID string) (bool, error)
- func (s *RelayStore) RemoveOrgMemberAuthorized(orgID, actorUserID, targetUserID string) (bool, error)
- func (s *RelayStore) ResolveLabel(targetID, userID, orgID string) string
- func (s *RelayStore) ResolveLabels(targetIDs []string, userID, orgID string) map[string]string
- func (s *RelayStore) ResolveOrg(ref, userID string) (*Org, error)
- func (s *RelayStore) RevokeEntitlement(userID, subID string) (string, error)
- func (s *RelayStore) RevokeOrgInvite(orgID, token string) (bool, error)
- func (s *RelayStore) RevokeOrgInviteAuthorized(orgID, token, actorUserID string) (bool, error)
- func (s *RelayStore) RotateDeviceToken(oldToken, newToken, userID, deviceID string, expiresAt *time.Time) error
- func (s *RelayStore) SaveMCPClientRegistration(reg MCPClientRegistration) error
- func (s *RelayStore) SaveMCPClientRegistrationLimited(reg MCPClientRegistration, now time.Time, limit int) (bool, error)
- func (s *RelayStore) SetLabel(targetID, scopeType, scopeID, label string) error
- func (s *RelayStore) SetLabelAuthorized(targetID, scopeType, scopeID, actorUserID string, actorOwnsTarget bool, ...) error
- func (s *RelayStore) SetNtfyConfig(userID string, cfg NtfyConfig) error
- func (s *RelayStore) SetOrgMaxSeats(orgID string, seats int) error
- func (s *RelayStore) SetRelayConfig(key, value string) error
- func (s *RelayStore) UpdatePasskeySignCount(id string, count int) error
- func (s *RelayStore) UpdateSubscriptionSeats(subID string, seats int) error
- func (s *RelayStore) UpdateSubscriptionStatus(subID, status string) error
- func (s *RelayStore) UpdateUserEmail(userID, email string) error
- func (s *RelayStore) UpdateUserTier(userID, tier string) error
- func (s *RelayStore) UpsertUser(u *User) error
- func (s *RelayStore) ValidateToken(token string) (userID string, deviceID string, err error)
- type Server
- func (s *Server) EnableMCP(runner *egg.ToolRunner, policy *mcp.Policy, nativeTools ...mcp.NativeTool)
- func (s *Server) GetSessionCache() *SessionCache
- func (s *Server) GracefulShutdown(httpSrv *http.Server, timeout time.Duration) error
- func (s *Server) InitJWTKey() error
- func (s *Server) IsEdge() bool
- func (s *Server) IsLogin() bool
- func (s *Server) JWTKey() *ecdsa.PrivateKey
- func (s *Server) JWTPubKey() *ecdsa.PublicKey
- func (s *Server) MCPEnabled() bool
- func (s *Server) MachineID() string
- func (s *Server) PortalNativeMCPTools(embeddedWingID string) []mcp.NativeTool
- func (s *Server) ReloadMCP(runner *egg.ToolRunner, policy *mcp.Policy)
- func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (s *Server) SetJWTKey(key *ecdsa.PrivateKey)
- func (s *Server) SetLocalUser(u *User)
- func (s *Server) SetLoginProxy(p http.Handler)
- func (s *Server) SetSessionCache(sc *SessionCache)
- func (s *Server) StartEdgeSync(ctx context.Context, loginAddr string, interval time.Duration)
- type ServerConfig
- type SessionCache
- func (sc *SessionCache) ActiveUserIDs() []string
- func (sc *SessionCache) StartOrgSync(ctx context.Context, loginAddr string, interval time.Duration)
- func (sc *SessionCache) UpdateUserOrgContext(userID string, orgIDs []string, orgRoles map[string]string)
- func (sc *SessionCache) UpdateUserOrgs(userID string, orgIDs []string)
- func (sc *SessionCache) Validate(token, loginAddr string) *User
- type SessionValidation
- type Subscription
- type TierLookup
- type User
- type WingClaims
- type WingEvent
- type WingLocation
- type WingMap
- func (m *WingMap) All() map[string]WingLocation
- func (m *WingMap) Deregister(wingID string)
- func (m *WingMap) DeregisterConnection(wingID, connectionID string) bool
- func (m *WingMap) EdgeIDs() []string
- func (m *WingMap) IsCurrentEvent(wingID, machineID, connectionID string, revision uint64, exactRevision bool) bool
- func (m *WingMap) Locate(wingID string) (WingLocation, bool)
- func (m *WingMap) ReconcileFull(machineID string, activeWings map[string]bool, snapshotAt time.Time)
- func (m *WingMap) ReconcileSnapshot(machineID string, activeWings map[string]WingLocation, snapshotAt time.Time)
- func (m *WingMap) Register(wingID string, loc WingLocation) bool
- type WingRegistry
- func (r *WingRegistry) Activate(w *ConnectedWing) (*ConnectedWing, []*ConnectedWing, bool)
- func (r *WingRegistry) Add(w *ConnectedWing) *ConnectedWing
- func (r *WingRegistry) All() []*ConnectedWing
- func (r *WingRegistry) BroadcastAll(ctx context.Context, data []byte)
- func (r *WingRegistry) CloseAll()
- func (r *WingRegistry) CountForUser(userID string) int
- func (r *WingRegistry) FindByID(wingID string) *ConnectedWing
- func (r *WingRegistry) ListForUser(userID string) []*ConnectedWing
- func (r *WingRegistry) Remove(id string) *ConnectedWing
- func (r *WingRegistry) RemoveConnection(id string) (*ConnectedWing, bool)
- func (r *WingRegistry) Subscribe(userID string, orgIDs []string, ch chan WingEvent)
- func (r *WingRegistry) Touch(connectionID string)
- func (r *WingRegistry) Unsubscribe(userID string, ch chan WingEvent)
- func (r *WingRegistry) UpdateConfig(id string, locked bool, allowedCount int, directMCP bool, hostedRelay string) *ConnectedWing
- func (r *WingRegistry) UpdateUserOrgs(userID string, orgIDs []string) bool
Constants ¶
const ( RelayPolicyLegacy = "legacy" RelayPolicyDirectFree = "direct-free" )
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 ¶
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") ErrOrgOwnerRemoval = errors.New("organization owner cannot be removed") ErrOrgSeatsNotIncreased = errors.New("organization seats were not increased") )
Functions ¶
func DeriveECKeyStringFromSecret ¶ added in v0.140.0
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
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.
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 ¶
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
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 Entitlement ¶ added in v0.23.0
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".
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
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
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
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
ClearBrowser nils the BrowserConn and removes spectator entries for this connection.
func (*PTYRoutes) ControllerWing ¶ added in v0.144.0
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) IsSpectator ¶ added in v0.137.0
IsSpectator returns true if conn is a spectator on any session.
func (*PTYRoutes) NotifyWingOffline ¶ added in v0.49.0
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) RemoveViewer ¶ added in v0.137.0
RemoveViewer removes a spectator connection from a session route.
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 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 (*RelayStore) ConsumeMagicLink ¶
func (s *RelayStore) ConsumeMagicLink(token string) (string, error)
func (*RelayStore) ConsumeOrgInvite ¶ added in v0.23.0
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 (*RelayStore) CreateMagicLink ¶
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
GracefulShutdown sends relay.restart to all connected WebSockets, then shuts down the HTTP server.
func (*Server) InitJWTKey ¶ added in v0.108.0
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
IsEdge returns true if this node is an edge relay (no SQLite).
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
JWTPubKey returns the ES256 public key for JWT verification.
func (*Server) MCPEnabled ¶ added in v0.140.0
MCPEnabled reports whether the MCP surface is configured.
func (*Server) MachineID ¶ added in v0.23.0
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) 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 (*Server) SetLoginProxy ¶ added in v0.23.0
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.
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
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 TierLookup ¶ added in v0.23.0
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 (*WingMap) DeregisterConnection ¶ added in v0.145.0
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
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 (r *WingRegistry) Add(w *ConnectedWing) *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) 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).