relay

package
v0.7.1 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2026 License: MIT Imports: 34 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateOrLoadSecret

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

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

func IssueWingJWT

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

IssueWingJWT creates a signed JWT for a wing connection.

func SeedDefaultSkills

func SeedDefaultSkills(store *RelayStore) error

func SeedSpacesFromIndex

func SeedSpacesFromIndex(store *RelayStore, idx *embedding.SpaceIndex, embedderName string) (int, error)

SeedSpacesFromIndex upserts anchors from a SpaceIndex with real embeddings. It writes centroids to both the legacy embedding_512 column and the new anchor_embeddings table.

func StartSidebarRefresh

func StartSidebarRefresh(store *RelayStore, interval time.Duration)

StartSidebarRefresh launches a background goroutine that recomputes the sidebar cache every interval. Does an initial compute synchronously.

Types

type AnchorEmbedding

type AnchorEmbedding struct {
	AnchorID    string
	Slug        string
	Centroid512 []byte
}

AnchorEmbedding holds an anchor's centroid for a specific embedder.

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

type ChatRegistry

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

ChatRegistry tracks active chat sessions.

func NewChatRegistry

func NewChatRegistry() *ChatRegistry

func (*ChatRegistry) Add

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

func (*ChatRegistry) CountForUser

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

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

func (*ChatRegistry) Get

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

func (*ChatRegistry) ListForUser

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

ListForUser returns all chat sessions for a given user.

func (*ChatRegistry) Remove

func (r *ChatRegistry) Remove(id string)

type ChatSession

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

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

type ConnectedWing

type ConnectedWing struct {
	ID         string
	UserID     string
	MachineID  string
	PublicKey  string
	Agents     []string
	Skills     []string
	Labels     []string
	Identities []string
	Projects   []ws.WingProject
	Conn       *websocket.Conn
	LastSeen   time.Time
}

ConnectedWing represents a wing connected via WebSocket.

type DeviceCodeRow

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

type ExportAnchor

type ExportAnchor struct {
	Slug       string  `json:"slug"`
	Similarity float64 `json:"similarity"`
}

type ExportComment

type ExportComment struct {
	Link      string  `json:"link"`
	UserID    string  `json:"user_id"`
	ParentID  *string `json:"parent_id,omitempty"`
	Content   string  `json:"content"`
	IsBot     bool    `json:"is_bot"`
	CreatedAt string  `json:"created_at"`
}

ExportComment holds a comment keyed by link for sync.

type ExportPost

type ExportPost struct {
	Text         string         `json:"text"`
	Title        *string        `json:"title,omitempty"`
	Link         *string        `json:"link,omitempty"`
	Mass         int            `json:"mass"`
	PublishedAt  *string        `json:"published_at,omitempty"`
	Embedding512 []byte         `json:"embedding_512"`
	EmbedderName string         `json:"embedder_name"`
	Anchors      []ExportAnchor `json:"anchors"`
}

ExportPost holds a post with its embedding and anchor assignments for sync.

type ExportVote

type ExportVote struct {
	Link      string `json:"link"`
	UserID    string `json:"user_id"`
	CreatedAt string `json:"created_at"`
}

ExportVote holds a vote keyed by link for sync.

type PTYRegistry

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

PTYRegistry tracks active PTY sessions.

func NewPTYRegistry

func NewPTYRegistry() *PTYRegistry

func (*PTYRegistry) Add

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

func (*PTYRegistry) CountForUser

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

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

func (*PTYRegistry) Get

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

func (*PTYRegistry) ListForUser

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

ListForUser returns all PTY sessions for a given user.

func (*PTYRegistry) Remove

func (r *PTYRegistry) Remove(id string)

type PTYSession

type PTYSession struct {
	ID          string
	WingID      string
	UserID      string
	Agent       string
	CWD         string // working directory for this session
	Status      string // "active", "detached", "exited"
	BrowserConn *websocket.Conn
	// contains filtered or unexported fields
}

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

type PostAnchor

type PostAnchor struct {
	PostID     string
	AnchorID   string
	Similarity float64
}

type PostParams

type PostParams struct {
	UserID      string
	Text        string
	Title       string
	Link        string
	Mass        int
	PublishedAt *time.Time
}

type RelayStore

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

func OpenRelay

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

func (*RelayStore) AnchorConnectivity

func (s *RelayStore) AnchorConnectivity() (map[string]map[string]int, error)

AnchorConnectivity returns a connectivity matrix: slug -> {slug -> shared_post_count}. Two anchors are connected when they share posts via post_anchors. Only considers posts from the last 30 days.

func (*RelayStore) AnchorMasses

func (s *RelayStore) AnchorMasses() (map[string]float64, error)

AnchorMasses returns slug -> sum(decayed_mass) for all anchors with posts.

func (*RelayStore) AnchorSlugsForPosts

func (s *RelayStore) AnchorSlugsForPosts(postIDs []string) (map[string][]string, error)

AnchorSlugsForPosts returns a map of postID -> []slug for the given post IDs.

func (*RelayStore) AppendAudit

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

func (*RelayStore) AssignPostAnchors

func (s *RelayStore) AssignPostAnchors(postID string, assignments []PostAnchor) error

func (*RelayStore) CheckRateLimit

func (s *RelayStore) CheckRateLimit(userID, action string, isPro bool) (bool, error)

func (*RelayStore) ClaimDeviceCode

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

func (*RelayStore) Close

func (s *RelayStore) Close() error

func (*RelayStore) CommentCountsForPosts

func (s *RelayStore) CommentCountsForPosts(postIDs []string) (map[string]int, error)

CommentCountsForPosts returns a map of postID -> comment count.

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

func (*RelayStore) CountPostsByAnchor

func (s *RelayStore) CountPostsByAnchor(anchorID string) (int, error)

func (*RelayStore) CreateComment

func (s *RelayStore) CreateComment(c *SocialComment) error

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 (s *RelayStore) CreateMagicLink(id, email, token string, expiresAt time.Time) error

func (*RelayStore) CreateSession

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

func (*RelayStore) CreateSkill

func (s *RelayStore) CreateSkill(name, description, category, agent, tags, content, sha256, publisher, sourceURL string, weight int) error

func (*RelayStore) CreateSocialEmbedding

func (s *RelayStore) CreateSocialEmbedding(e *SocialEmbedding) error

func (*RelayStore) CreateSocialUserDev

func (s *RelayStore) CreateSocialUserDev() (*SocialUser, error)

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

func (*RelayStore) CreateUser

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

func (*RelayStore) DB

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

DB returns the underlying database connection.

func (*RelayStore) DecayMasses

func (s *RelayStore) DecayMasses() error

func (*RelayStore) DeleteSession

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

func (*RelayStore) DeleteSkill

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

func (*RelayStore) DeleteToken

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

func (*RelayStore) DequeueTask

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

DequeueTask deletes a task from the queue after dispatch.

func (*RelayStore) EnqueueTask

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

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

func (*RelayStore) ExportPosts

func (s *RelayStore) ExportPosts() ([]ExportPost, error)

ExportPosts returns all visible posts with their embeddings and anchor assignments.

func (*RelayStore) ExportVotesAndComments

func (s *RelayStore) ExportVotesAndComments(since time.Time) ([]ExportVote, []ExportComment, error)

ExportVotesAndComments returns votes and comments since the given time, keyed by post link.

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

func (s *RelayStore) GetOrCreateSocialUserByEmail(email string) (*SocialUser, error)

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) (*SocialUser, error)

func (*RelayStore) GetSkill

func (s *RelayStore) GetSkill(name string) (*SkillRow, error)

func (*RelayStore) GetSocialEmbedding

func (s *RelayStore) GetSocialEmbedding(id string) (*SocialEmbedding, error)
func (s *RelayStore) GetSocialEmbeddingByLink(link string) (*SocialEmbedding, error)

func (*RelayStore) GetSocialEmbeddingBySlug

func (s *RelayStore) GetSocialEmbeddingBySlug(slug string) (*SocialEmbedding, error)

func (*RelayStore) GetSocialUser

func (s *RelayStore) GetSocialUser(id string) (*SocialUser, error)

func (*RelayStore) GetSocialUserByProvider

func (s *RelayStore) GetSocialUserByProvider(provider, providerID string) (*SocialUser, error)

func (*RelayStore) GetUpvoteCount

func (s *RelayStore) GetUpvoteCount(postID string) (int, error)

func (*RelayStore) ListAllPosts

func (s *RelayStore) ListAllPosts(sort string, limit int) ([]*SocialEmbedding, error)

ListAllPosts returns all visible posts ordered by the given sort. sort: "hot" (default, live decay), "new", "week", "month", "year".

func (*RelayStore) ListAnchors

func (s *RelayStore) ListAnchors() ([]*SocialEmbedding, error)

func (*RelayStore) ListAnchorsForEmbedder

func (s *RelayStore) ListAnchorsForEmbedder(embedderName string) ([]AnchorEmbedding, error)

ListAnchorsForEmbedder returns visible anchors with centroids for the given embedder.

func (*RelayStore) ListCommentsByPost

func (s *RelayStore) ListCommentsByPost(postID string) ([]*SocialComment, error)

func (*RelayStore) ListPendingTasks

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

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

func (*RelayStore) ListPostsByAnchor

func (s *RelayStore) ListPostsByAnchor(anchorID, sort string, limit int) ([]*SocialEmbedding, error)

func (*RelayStore) ListSkills

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

func (*RelayStore) RefreshUpvotes24h

func (s *RelayStore) RefreshUpvotes24h() error

func (*RelayStore) SearchSkills

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

func (*RelayStore) SetRelayConfig

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

SetRelayConfig writes a value to the relay_config table.

func (*RelayStore) SumDecayedMassByAnchor

func (s *RelayStore) SumDecayedMassByAnchor(anchorID string) (float64, error)

func (*RelayStore) ToggleUpvote

func (s *RelayStore) ToggleUpvote(userID, postID string) (bool, error)

ToggleUpvote inserts if not exists, deletes if exists. Returns true if now upvoted.

func (*RelayStore) TopPostsByAnchor

func (s *RelayStore) TopPostsByAnchor() (map[string]topPost, error)

TopPostsByAnchor returns a map of anchorID -> top post (title + text + link) by highest decayed_mass.

func (*RelayStore) UpdateAnchorCentroid

func (s *RelayStore) UpdateAnchorCentroid(anchorID string, centroid512 []byte) error

func (*RelayStore) UpdateAnchorEffective

func (s *RelayStore) UpdateAnchorEffective(anchorID string, effective512 []byte) error

func (*RelayStore) UpsertAnchorEmbedding

func (s *RelayStore) UpsertAnchorEmbedding(anchorID, embedderName string, centroid512 []byte) error

UpsertAnchorEmbedding inserts or updates a per-embedder centroid for an anchor.

func (*RelayStore) UpsertSocialUser

func (s *RelayStore) UpsertSocialUser(u *SocialUser) error

func (*RelayStore) Upvote

func (s *RelayStore) Upvote(userID, postID string) error

func (*RelayStore) UserUpvotesForPosts

func (s *RelayStore) UserUpvotesForPosts(userID string, postIDs []string) (map[string]bool, error)

UserUpvotesForPosts returns the set of post IDs the user has upvoted from the given list.

func (*RelayStore) ValidateToken

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

type Server

type Server struct {
	Store          *RelayStore
	Embedder       embedding.Embedder
	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
	Wings          *WingRegistry
	PTY            *PTYRegistry
	Chat           *ChatRegistry
	Bandwidth      *BandwidthMeter
	// contains filtered or unexported fields
}

func NewServer

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

func (*Server) ServeHTTP

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

func (*Server) SubmitTask

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

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

type ServerConfig

type ServerConfig struct {
	BaseURL            string
	AppHost            string // e.g. "app.wingthing.ai" — serve SPA at root
	WSHost             string // e.g. "ws.wingthing.ai" — WebSocket only
	JWTSecret          string // base64-encoded; overrides DB-stored secret
	GitHubClientID     string
	GitHubClientSecret string
	SMTPHost           string
	SMTPPort           string
	SMTPUser           string
	SMTPPass           string
	SMTPFrom           string
}

type SkillRow

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

type SocialComment

type SocialComment struct {
	ID        string
	PostID    string
	UserID    string
	ParentID  *string
	Content   string
	IsBot     bool
	CreatedAt time.Time
}

type SocialEmbedding

type SocialEmbedding struct {
	ID           string
	UserID       string
	Link         *string
	Text         string
	Title        *string
	Centerpoint  *string
	Slug         *string
	Embedding    []byte // raw float32 blob
	Embedding512 []byte
	Centroid512  []byte
	Effective512 []byte
	Kind         string // "post", "subscription", "anchor", "antispam"
	Visible      bool
	Mass         int
	Upvotes24h   int
	DecayedMass  float64
	Swallowed    bool
	CreatedAt    time.Time
	UpdatedAt    time.Time
	PublishedAt  *time.Time
}

func CreatePost

func CreatePost(store *RelayStore, emb embedding.Embedder, p PostParams) (*SocialEmbedding, error)

CreatePost embeds text, assigns to anchors by cosine similarity, and stores the post. URL dedup: if Link is non-empty and already exists, returns the existing post (no duplicate). It uses per-embedder centroids from anchor_embeddings when available, falling back to the legacy embedding_512 column on social_embeddings.

type SocialUser

type SocialUser struct {
	ID          string
	Provider    string
	ProviderID  string
	DisplayName string
	AvatarURL   *string
	IsPro       bool
	CreatedAt   time.Time
}

type WingClaims

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

WingClaims are the JWT claims for a wing connection.

func ValidateWingJWT

func ValidateWingJWT(secret []byte, tokenString string) (*WingClaims, error)

ValidateWingJWT verifies a JWT and returns the claims.

type WingRegistry

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

WingRegistry tracks all connected wings.

func NewWingRegistry

func NewWingRegistry() *WingRegistry

func (*WingRegistry) Add

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

func (*WingRegistry) CountForUser

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

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

func (*WingRegistry) FindByID

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

func (*WingRegistry) FindByIdentity

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

func (*WingRegistry) FindForUser

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

func (*WingRegistry) ListForUser

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

ListForUser returns all wings connected for a given user.

func (*WingRegistry) RegisterDirRequest added in v0.6.3

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

func (*WingRegistry) Remove

func (r *WingRegistry) Remove(id string)

func (*WingRegistry) ResolveDirRequest added in v0.6.3

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

func (*WingRegistry) Touch

func (r *WingRegistry) Touch(wingID string)

func (*WingRegistry) UnregisterDirRequest added in v0.6.3

func (r *WingRegistry) UnregisterDirRequest(reqID string)

Jump to

Keyboard shortcuts

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