githubapp

package
v0.99.1 Latest Latest
Warning

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

Go to latest
Published: Sep 6, 2026 License: MIT Imports: 12 Imported by: 0

README

Workbench GitHub App control-plane contract

github.com/sneat-dev/wb/api/githubapp owns the typed HTTP contract for the Workbench dashboard at https://sneat.work/bench. The service is mounted by the existing Sneat Go Cloud Run executable at https://wb-github-app.sneat.dev. It is not a separate service.

The host supplies three narrow ports:

  1. ReadModel, which records a repository's explicit public opt-in (including its README-linked free-eligibility declaration) before it returns anonymous data. Private subjects require an authenticated member and are rendered as 404 for every other caller.
  2. DeliveryStore, backed by durable storage, which atomically claims GitHub delivery IDs and persists coalesced wakeups.
  3. AuthoritativeReader, which refreshes GitHub App state before a webhook can enqueue work. Cached data is never enough to authorize an action.

The API provides the dashboard summary, repository/organization/user stats, time series usable as tables or graphs, leaderboards, and latest merges with pull request, issue, merge commit, release, and Workbench receipt URLs.

GET /v0/workbench/events is the default server-to-browser transport. It is resumable SSE: after (or Last-Event-ID) replays durable events with strictly monotonic IDs before the browser receives the live subscription. The source and service filter private events before serialization. Event types are queue, job.phase, job.progress, ci, cleanup, sync, and daemon.generation. WebSocket is reserved for later bidirectional controls such as cancellation and reprioritization.

The sequenced EventSource is also the terminal-monitoring source: filter by repo, task, operation, session, severity, after, and RFC 3339 since. wb monitor --format=jsonl consumes this same sequence; wb log tail can be an alias, but immutable Work Logs are never used as a mutable event queue.

Documentation

Overview

Package githubapp defines the Workbench GitHub App control-plane API.

Index

Constants

View Source
const (
	// ControlPlaneOrigin is the dedicated machine API origin.
	ControlPlaneOrigin = "https://wb-github-app.sneat.dev"
	// ControlPlaneHost is the host-only form used by the Cloud Run adapter.
	ControlPlaneHost = "wb-github-app.sneat.dev"
	// UIOrigin is the browser origin permitted to call the control-plane API.
	UIOrigin = "https://sneat.work"
	// APIPrefix is mounted by the host application.
	APIPrefix = "/v0/workbench"
)

Variables

View Source
var (
	ErrPrivateData = errors.New("private Workbench data requires membership")
	ErrNoReadModel = errors.New("workbench read model is not configured")
	ErrNoWebhook   = errors.New("workbench webhook processor is not configured")
)

Functions

func NewHandler

func NewHandler(options HandlerOptions) http.Handler

NewHandler returns the Workbench GitHub App API under APIPrefix. It permits the Sneat Workbench browser origin only; GitHub webhooks have no CORS need.

Types

type Access

type Access[T any] struct {
	Visibility Visibility
	Value      T
}

Access wraps a read-model result with its disclosure class.

type AuthoritativeReader

type AuthoritativeReader interface {
	Refresh(context.Context, WebhookDelivery) error
}

AuthoritativeReader refreshes the GitHub App's authoritative view before an action is queued. A cache hit alone must never satisfy this call.

type Dashboard

type Dashboard struct {
	GeneratedAt time.Time `json:"generated_at"`
	Summary     Summary   `json:"summary"`
	Links       []Link    `json:"links,omitempty"`
}

Dashboard is the top-level dashboard response.

type DeliveryStore

type DeliveryStore interface {
	HasDelivery(context.Context, string) (bool, error)
	CommitDeliveryAndWakeup(context.Context, string, Wakeup) (bool, error)
}

DeliveryStore must be backed by durable storage. HasDelivery is a cheap preflight that avoids repeating an authoritative GitHub read for a redelivery. CommitDeliveryAndWakeup atomically records the delivery ID and persists a wakeup coalesced by its scope key. It returns false when a concurrent request committed the same delivery first.

type Event

type Event struct {
	ID         uint64          `json:"id"`
	Type       EventType       `json:"type"`
	Visibility Visibility      `json:"visibility"`
	At         time.Time       `json:"at"`
	Repository string          `json:"repository,omitempty"`
	Task       string          `json:"task,omitempty"`
	Operation  string          `json:"operation,omitempty"`
	Session    string          `json:"session,omitempty"`
	Severity   string          `json:"severity,omitempty"`
	Payload    json.RawMessage `json:"payload"`
}

Event is an SSE record. ID is a durable monotonic cursor, not a timestamp. Payload is only serialized after the viewer passes its visibility check.

type EventFilter

type EventFilter struct {
	After      uint64
	Since      time.Time
	Repository string
	Task       string
	Operation  string
	Session    string
	Severity   string
}

EventFilter selects one resumable daemon/direct-WB event sequence. Work Logs remain immutable per-task evidence; this filter is transient monitoring only.

type EventSource

type EventSource interface {
	Replay(context.Context, EventFilter) ([]Event, error)
	Subscribe(context.Context, EventFilter) (<-chan Event, error)
}

EventSource durably replays events strictly after a cursor, then exposes a live subscription. Implementations must retain enough history for reconnects and issue globally monotonic IDs across daemon generations.

type EventType

type EventType string

EventType classifies a live Workbench daemon update. WebSocket is reserved for later bidirectional controls such as cancel and reprioritize.

const (
	EventQueue            EventType = "queue"
	EventJobPhase         EventType = "job.phase"
	EventJobProgress      EventType = "job.progress"
	EventCI               EventType = "ci"
	EventCleanup          EventType = "cleanup"
	EventSync             EventType = "sync"
	EventDaemonGeneration EventType = "daemon.generation"
)

type HandlerOptions

type HandlerOptions struct {
	Service        Service
	ViewerResolver ViewerResolver
	AllowedOrigin  string
}

HandlerOptions supplies the narrow host bindings for the public API.

type LatestMerge

type LatestMerge struct {
	Repository     string    `json:"repository"`
	PullRequest    int       `json:"pull_request"`
	MergedAt       time.Time `json:"merged_at"`
	PullRequestURL string    `json:"pull_request_url,omitempty"`
	IssueURL       string    `json:"issue_url,omitempty"`
	MergeCommitSHA string    `json:"merge_commit_sha,omitempty"`
	MergeCommitURL string    `json:"merge_commit_url,omitempty"`
	ReleaseURL     string    `json:"release_url,omitempty"`
	ReceiptURL     string    `json:"receipt_url,omitempty"`
}

LatestMerge gives the dashboard every navigable artifact around a merge.

type Leaderboard

type Leaderboard struct {
	Metric  string             `json:"metric"`
	Entries []LeaderboardEntry `json:"entries"`
}

Leaderboard groups ranked values for a requested metric.

type LeaderboardEntry

type LeaderboardEntry struct {
	Rank        int    `json:"rank"`
	SubjectID   string `json:"subject_id"`
	DisplayName string `json:"display_name"`
	Value       int64  `json:"value"`
}

LeaderboardEntry is intentionally small so public leaderboards do not leak private repository names or private activity counts.

type Link struct {
	Kind string `json:"kind"`
	Href string `json:"href"`
}

Link is a canonical GitHub, release, or Workbench receipt reference.

type PublicEligibility

type PublicEligibility struct {
	Repository string    `json:"repository"`
	READMEURL  string    `json:"readme_url"`
	GrantedAt  time.Time `json:"granted_at"`
}

PublicEligibility is the auditable opt-in record required before a repository can appear in unauthenticated results. READMEURL may point to the repository's free-eligibility declaration; it is not inferred from a public GitHub repository alone.

type ReadModel

ReadModel owns persistence and GitHub data projection. It must return only subjects whose public opt-in is recorded, unless the supplied viewer is an authenticated member of the private subject.

type Scope

type Scope string

Scope identifies the GitHub subject represented by a statistic.

const (
	ScopeRepository   Scope = "repository"
	ScopeOrganization Scope = "organization"
	ScopeUser         Scope = "user"
)

type Series

type Series struct {
	Scope  Scope         `json:"scope"`
	ID     string        `json:"id"`
	Metric string        `json:"metric"`
	Points []SeriesPoint `json:"points"`
}

Series is a named time-series for graph and table consumers.

type SeriesPoint

type SeriesPoint struct {
	At    time.Time `json:"at"`
	Value int64     `json:"value"`
}

SeriesPoint can render either a graph point or a table row.

type Service

type Service struct {
	ReadModel           ReadModel
	Deliveries          DeliveryStore
	AuthoritativeReader AuthoritativeReader
	WebhookSecret       []byte
	Events              EventSource
}

Service applies disclosure policy around a Workbench read model and processes signed GitHub App webhook deliveries.

func (Service) Dashboard

func (service Service) Dashboard(ctx context.Context, viewer Viewer) (Dashboard, error)

func (Service) EventStream

func (service Service) EventStream(ctx context.Context, viewer Viewer, filter EventFilter) ([]Event, <-chan Event, error)

EventStream replays visible durable events after cursor and returns the filtered live channel. It validates monotonic order so a bad source cannot cause a browser to skip or regress a reconnect cursor.

func (Service) LatestMerges

func (service Service) LatestMerges(ctx context.Context, viewer Viewer, limit int) ([]LatestMerge, error)

func (Service) Leaderboard

func (service Service) Leaderboard(ctx context.Context, viewer Viewer, metric string) (Leaderboard, error)

func (Service) ProcessWebhook

func (service Service) ProcessWebhook(ctx context.Context, delivery WebhookDelivery, signature string) (bool, error)

ProcessWebhook verifies a signed delivery, cheap-checks its durable receipt, refreshes GitHub authoritatively, then atomically records the delivery and persists one coalesced wakeup. Failed refreshes leave no delivery receipt, so GitHub retries remain safe; a race is resolved by the atomic final commit.

func (Service) Series

func (service Service) Series(ctx context.Context, viewer Viewer, scope Scope, id, metric string) (Series, error)

func (Service) Stats

func (service Service) Stats(ctx context.Context, viewer Viewer, scope Scope, id string) (Stat, error)

type Stat

type Stat struct {
	Scope       Scope     `json:"scope"`
	ID          string    `json:"id"`
	DisplayName string    `json:"display_name"`
	Summary     Summary   `json:"summary"`
	UpdatedAt   time.Time `json:"updated_at"`
	Links       []Link    `json:"links,omitempty"`
}

Stat is one scoped repository, organization, or user result.

type Summary

type Summary struct {
	Repositories int `json:"repositories"`
	OpenPulls    int `json:"open_pulls"`
	MergedPulls  int `json:"merged_pulls"`
	OpenIssues   int `json:"open_issues"`
	Releases     int `json:"releases"`
}

Summary is the compact dashboard card set.

type Viewer

type Viewer struct {
	Authenticated bool
	Member        bool
	UserID        string
}

Viewer is resolved by the host before a private record is rendered.

type ViewerResolver

type ViewerResolver interface {
	Viewer(*http.Request) (Viewer, error)
}

ViewerResolver binds the host authentication and membership system to the Workbench domain. The WB domain never accepts identity headers directly.

type Visibility

type Visibility string

Visibility describes whether a response contains an explicitly opted-in public subject or a member-only private subject.

const (
	VisibilityPublic  Visibility = "public"
	VisibilityPrivate Visibility = "private"
)

type Wakeup

type Wakeup struct {
	Key        string
	Repository string
	Event      string
}

Wakeup is a durable, coalescible unit of refresh work.

type WebhookDelivery

type WebhookDelivery struct {
	ID         string
	Event      string
	Repository string
	Payload    []byte
}

WebhookDelivery is the verified, minimally parsed GitHub webhook envelope.

Jump to

Keyboard shortcuts

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