inboxes

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 14 Imported by: 0

README

Inboxes

An inbox is a saved filter over the desktop app's Up next queue. People define inboxes and the app lists them under Inbox in the sidebar, each showing the queue entries that match. Matching lives in the app; this package only stores the definitions in the daemon and serves them at /api/inboxes, so they follow the person across app restarts. Per-inbox notifications are a later step.

An inbox

Field Values
id assigned by the daemon (random hex), stable across saves
name 1 to 40 characters after trimming, unique ignoring case
color blue, green, red, amber, purple, grey
what any, review, merge, agent, mine
owners GitHub owner logins (organization or user), at most 50; empty means any
risk any, 3, 4 (strings)
repositories owner/name, at most 50; empty means every repository that matches
query a filter query the app parses and matches, trimmed, one line of at most 500 characters; empty (or missing) for none. The app reads what, owners, risk, repositories and no_bots as part of it and saves them folded in, leaving those fields at their plain values
no_bots leave out pull requests by bot authors (author_bot in discovery)
badge show a count badge in the sidebar

At most 20 inboxes. The array order is the sidebar order. Owners and repositories are compared and de-duplicated ignoring case; the first spelling given is kept.

Storage

The state/inboxes document in the shared private app.sqlite application database:

{"revision": 3, "inboxes": [{"id": "...", "name": "Platform", "color": "blue", "what": "any", "owners": ["baum-ai"], "risk": "any", "repositories": ["baum-ai/baum"], "no_bots": false, "badge": true}]}

A missing document reads as revision 0 with no inboxes. A document that does not parse is never overwritten: GET and PUT answer 500 inboxes_unreadable. Fix or remove the document to continue. Writes compare the revision and replace the document in one immediate SQLite transaction, including across independent store instances.

API

  • GET /api/inboxes answers {"available": true, "revision": n, "inboxes": [...]}.
  • PUT /api/inboxes takes {"revision": n, "inboxes": [...]} and replaces the whole list, answering the saved list in the shape of GET. revision is the one the client read; any other answers 409 stale_revision with the current list. Each save increments the revision. An entry without an id gets one. An id in the daemon's shape (32 lower-case hex characters) is kept even when the stored list no longer holds it, so an inbox deleted and brought back by Undo keeps its links; any other id is refused. Other methods answer 405.

Errors are {"code": "...", "message": "..."} with these codes. A refused save changes nothing.

Status Code Meaning
400 invalid_request the body is not one JSON object of the expected shape, has no revision, or is over 256 KB
400 invalid_inbox a field is out of range: an id the daemon did not assign, name length, color, what, risk, owner or repository shape, too many owners or repositories, or a query that is too long or has more than one line
400 too_many_inboxes more than 20 inboxes
400 duplicate_name two names equal ignoring case
400 duplicate_id the same id twice
409 stale_revision the revision is not the stored one; the body carries the current list
500 inboxes_unreadable the stored file cannot be parsed; it is left untouched

Documentation

Index

Constants

View Source
const (
	MaxInboxes      = 20
	MaxNameLength   = 40
	MaxOwners       = 50
	MaxRepositories = 50
	MaxQueryLength  = 500
)

Limits and the allowed values of the enumerated fields.

View Source
const (
	CodeInvalidRequest = "invalid_request"
	CodeInvalidInbox   = "invalid_inbox"
	CodeTooMany        = "too_many_inboxes"
	CodeDuplicateName  = "duplicate_name"
	CodeDuplicateID    = "duplicate_id"
	CodeStale          = "stale_revision"
	CodeUnreadable     = "inboxes_unreadable"
)

Error codes of the API.

Variables

This section is empty.

Functions

func ValidQuery

func ValidQuery(query string) bool

ValidQuery reports whether a filter query is one line of valid UTF-8 text of at most MaxQueryLength characters. The daemon never parses it: the app does.

Types

type API

type API struct {
	Store *Store
	// Changed is called after every successful save.
	Changed func()
}

API serves /api/inboxes. It has no authentication; the daemon mounts it behind its authenticated host.

func (*API) Handler

func (h *API) Handler() http.Handler

Handler routes /api/inboxes.

type Inbox

type Inbox struct {
	ID           string   `json:"id"`
	Name         string   `json:"name"`
	Color        string   `json:"color"`
	What         string   `json:"what"`
	Owners       []string `json:"owners"`
	Risk         string   `json:"risk"`
	Repositories []string `json:"repositories"`
	Query        string   `json:"query"`
	NoBots       bool     `json:"no_bots"`
	Badge        bool     `json:"badge"`
}

Inbox is a saved filter over the app's Up next queue. The daemon stores it and never evaluates it.

type InvalidError

type InvalidError struct {
	Code    string
	Message string
}

InvalidError is a refused request: a stable code and a message for a person.

func (*InvalidError) Error

func (e *InvalidError) Error() string

type StaleError

type StaleError struct{ Current State }

StaleError is a replace based on an older revision than the stored one.

func (*StaleError) Error

func (*StaleError) Error() string

type State

type State struct {
	Revision int     `json:"revision"`
	Inboxes  []Inbox `json:"inboxes"`
}

State is the stored list and the revision it is at.

type Store

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

Store keeps inbox definitions in the application database.

func Open

func Open(path string) *Store

Open returns a store backed by the application database at path.

func (*Store) Read

func (s *Store) Read() (State, error)

Read returns stored state; a missing document is revision 0 with no inboxes.

func (*Store) Replace

func (s *Store) Replace(revision int, inboxes []Inbox) (State, error)

Replace saves the whole list, based on the revision the caller read. Entries without an id get one; an id in the daemon's shape is kept, stored or not.

type UnreadableError

type UnreadableError struct{ Err error }

UnreadableError reports stored state that cannot be read. It is never overwritten, so what it holds is not lost silently.

func (*UnreadableError) Error

func (e *UnreadableError) Error() string

func (*UnreadableError) Unwrap

func (e *UnreadableError) Unwrap() error

Jump to

Keyboard shortcuts

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