Documentation
¶
Overview ¶
Package githubapp defines the Workbench GitHub App control-plane API.
Index ¶
- Constants
- Variables
- func NewHandler(options HandlerOptions) http.Handler
- type Access
- type AuthoritativeReader
- type Dashboard
- type DeliveryStore
- type Event
- type EventFilter
- type EventSource
- type EventType
- type HandlerOptions
- type LatestMerge
- type Leaderboard
- type LeaderboardEntry
- type Link
- type PublicEligibility
- type ReadModel
- type Scope
- type Series
- type SeriesPoint
- type Service
- func (service Service) Dashboard(ctx context.Context, viewer Viewer) (Dashboard, error)
- func (service Service) EventStream(ctx context.Context, viewer Viewer, filter EventFilter) ([]Event, <-chan Event, error)
- func (service Service) LatestMerges(ctx context.Context, viewer Viewer, limit int) ([]LatestMerge, error)
- func (service Service) Leaderboard(ctx context.Context, viewer Viewer, metric string) (Leaderboard, error)
- func (service Service) ProcessWebhook(ctx context.Context, delivery WebhookDelivery, signature string) (bool, error)
- func (service Service) Series(ctx context.Context, viewer Viewer, scope Scope, id, metric string) (Series, error)
- func (service Service) Stats(ctx context.Context, viewer Viewer, scope Scope, id string) (Stat, error)
- type Stat
- type Summary
- type Viewer
- type ViewerResolver
- type Visibility
- type Wakeup
- type WebhookDelivery
Constants ¶
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 ¶
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.
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 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 ¶
type ReadModel interface {
Dashboard(context.Context, Viewer) (Access[Dashboard], error)
Stats(context.Context, Viewer, Scope, string) (Access[Stat], error)
Series(context.Context, Viewer, Scope, string, string) (Access[Series], error)
Leaderboard(context.Context, Viewer, string) (Access[Leaderboard], error)
LatestMerges(context.Context, Viewer, int) (Access[[]LatestMerge], error)
}
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 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 ¶
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) 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) Leaderboard ¶
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.
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 ViewerResolver ¶
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" )