Help us shape the future of Go! Take the 2026 Go Developer Survey

jobbroker

package
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package jobbroker is the stack's async job broker: it accepts paid requests the x402-verifier has already verified + settled, replays them against the offer's real upstream with no client-facing deadline, and serves free status pages plus gated results. One broker per stack, multi-tenant across offers; jobs are runtime data in SQLite on a PVC — deliberately NOT CRDs (etcd write amplification, 1.5MiB caps on result bodies, TTL churn) and NOT k8s Jobs (the work is an HTTP replay against a running Service, not a batch pod).

Index

Constants

View Source
const (
	HeaderUpstreamURL  = "X-Obol-Upstream-Url"
	HeaderOffer        = "X-Obol-Offer"
	HeaderPayTo        = "X-Obol-Pay-To"
	HeaderJobTTL       = "X-Obol-Job-Ttl"
	HeaderVisibility   = "X-Obol-Result-Visibility"
	HeaderPublicPrefix = "X-Obol-Public-Prefix"
	HeaderUpstreamAuth = "X-Obol-Upstream-Auth"

	// Set by the verifier from verified credentials.
	HeaderPaymentPayer   = "X-Payment-Payer"
	HeaderVerifiedWallet = "X-Verified-Wallet"
)

Contract headers between the x402-verifier and the broker. The verifier sets all of them (client-supplied copies never survive the gate), so the broker can stay stateless about offers: everything it needs rides the request.

View Source
const (
	StatePending  = "pending"
	StateRunning  = "running"
	StateComplete = "complete"
	StateFailed   = "failed"
)

Job states.

View Source
const HeaderBrokerSig = "X-Obol-Broker-Sig"

HeaderBrokerSig carries the verifier's HMAC over the contract headers, so the broker can reject forged submits even from a pod the NetworkPolicy allows (defense in depth with the F1 NetworkPolicy). Mirrored in internal/x402 (see authgate.go) — the two packages don't share code.

Variables

This section is empty.

Functions

func NewID

func NewID() string

NewID returns a 128-bit random hex id. Unguessability is load-bearing: in public-visibility mode the id IS the result capability.

Types

type Job

type Job struct {
	ID        string
	Token     string // capability credential returned in the 202 body
	Offer     string // "<namespace>/<name>"
	Payer     string // wallet that paid (lowercase 0x…), may be empty
	PayTo     string // offer's payTo wallet (sellers list their jobs with it)
	State     string
	CreatedAt time.Time
	StartedAt time.Time
	DoneAt    time.Time
	ExpiresAt time.Time

	Method      string
	Path        string // path relative to the offer root, e.g. /submit
	ContentType string
	Body        []byte

	UpstreamURL string // in-cluster base URL to replay against
	CallbackURL string // optional completion webhook
	Visibility  string // payer | public

	ResultStatus      int
	ResultContentType string
	Result            []byte
	Error             string
}

Job is one accepted async request and (eventually) its stored result.

type ListSummary

type ListSummary struct {
	ID        string    `json:"jobId"`
	State     string    `json:"state"`
	Path      string    `json:"path"`
	CreatedAt time.Time `json:"createdAt"`
}

ListSummary is the /jobs listing row (no bodies).

type Server

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

Server is the broker's HTTP surface.

func NewServer

func NewServer(store *Store) *Server

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler returns the broker mux. The verifier strips the offer prefix before proxying, so paths arrive offer-relative:

/jobs                → list (wallet-scoped)
/jobs/<id>           → status (free; JSON or HTML by Accept)
/jobs/<id>/result    → stored result (visibility-gated)
anything else        → submit (a paid request the verifier settled)

func (*Server) RunSweeper

func (s *Server) RunSweeper(interval time.Duration, stop <-chan struct{})

RunSweeper deletes expired jobs every interval until stop is closed.

type Store

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

Store persists jobs. All methods are safe for concurrent use (database/sql pools; SQLite serializes writers).

func OpenStore

func OpenStore(path string) (*Store, error)

OpenStore opens (creating if needed) the job database at path. Use ":memory:" for tests.

func (*Store) Close

func (s *Store) Close() error

func (*Store) Create

func (s *Store) Create(j *Job) error

func (*Store) Finish

func (s *Store) Finish(id string, status int, contentType string, body []byte, errMsg string, at time.Time) error

Finish records the upstream outcome. status<400 → complete, else failed (money already settled at acceptance — the status page says so plainly).

func (*Store) Get

func (s *Store) Get(id string) (*Job, error)

func (*Store) ListForWallet

func (s *Store) ListForWallet(offer, wallet string, limit int) ([]ListSummary, error)

ListForWallet returns jobs the wallet may see under one offer: all of the offer's jobs when the wallet is the offer's payTo (the seller), else only jobs the wallet paid for.

func (*Store) MarkRunning

func (s *Store) MarkRunning(id string, at time.Time) error

func (*Store) SweepExpired

func (s *Store) SweepExpired(now time.Time) (int64, error)

SweepExpired deletes jobs past their TTL. Returns rows removed.

Jump to

Keyboard shortcuts

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