http

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package http is the R5 review collection endpoint: the HTTP arm of the §2A surface→transport map. It builds a plain net/http handler (Mount) that the R3 service runtime stitches onto its HTTP/SSE edge via internal/service/http.(*Server).RegisterMount(mount.Prefix(), mount) — this package never touches the R3 host, middleware chain, or lifecycle (spec D5.5: route ownership lives here, hosting lives in R3). No EventBus access is needed in v1 (r3 §D4.3).

Route trees, rooted at the mount prefix (default /api/reviews):

GET    {prefix}/runs/{iteration}/labels            list labels   (labels:read)
POST   {prefix}/runs/{iteration}/labels            submit label  (labels:write, audited)
PATCH  {prefix}/runs/{iteration}/labels/{label_id} edit label    (labels:write, audited)
GET    {prefix}/audit                              audit view    (audit:read, admin)
GET    {prefix}/users                              list users    (users:manage, admin)
POST   {prefix}/users                              create user   (users:manage, audited)
PATCH  {prefix}/users/{email}                      change role   (users:manage, audited)
DELETE {prefix}/users/{email}                      delete user   (users:manage, audited)

net/http mounts do not strip prefixes, so the full route paths (prefix included) are registered on the internal mux at construction time; R3 simply forwards requests whose path lives under the prefix.

Auth is bearer-token via the pluggable internal/review/auth Authenticator (missing/invalid token → 401, insufficient role → 403 — spec R8). Every mutating call is recorded in the internal/review/audit chained log by the audit middleware, which is FAIL-CLOSED: each mutating request's [read → mutate → audit] section runs under a per-target lock (in-process mutex + agentslock file lock, so concurrent requests and CLI processes cannot interleave read-modify-write cycles and drop each other's writes), with the target file's pre-image captured up front — if the audit append fails, the mutation is rolled back to the pre-image and the client gets a 500 carrying the X-Request-Id. A persisted-but-unaudited mutation can only survive in the doubly-degraded case where the rollback write also fails (reported loudly with the request id; audit.Append's at-least-once note applies to that residual only — reconcile by request id, never blind-retry).

The package name collides with the standard library, so net/http is imported under the alias nethttp (matching internal/service/http).

Index

Constants

View Source
const DefaultPrefix = "/api/reviews"

DefaultPrefix is the mount point R3 registers this handler under (see internal/service/http server docs: "R5 its review queue at /api/reviews").

View Source
const HeaderRequestID = "X-Request-Id"

HeaderRequestID is the request/response header carrying the per-request id used as the audit idempotency key. A client-supplied value is honored (so a reconciling retry can reuse it); otherwise the audit middleware generates one. It is echoed on every mutating response, success or failure.

Variables

View Source
var (
	// ErrInvalidPrefix is returned when the mount prefix is not a rooted,
	// non-root path.
	ErrInvalidPrefix = errors.New("review/http: mount prefix must be a rooted path below /")
	// ErrNilDependency is returned when a required dependency is missing.
	ErrNilDependency = errors.New("review/http: missing dependency")
)

Errors returned by New.

Functions

This section is empty.

Types

type AuditLog

type AuditLog interface {
	Append(e audit.Event) (audit.Record, error)
	Records() ([]audit.Record, error)
}

AuditLog is the audit surface the mount consumes: chained append for the audit middleware, record read-back for the admin audit view. *audit.Log satisfies it.

type Deps

type Deps struct {
	// Auth resolves bearer tokens to identities (401/403 enforcement).
	Auth auth.Authenticator
	// Labels persists label submissions and edits.
	Labels LabelStore
	// Users backs the admin user-management routes.
	Users UserStore
	// Audit records every mutating call (spec R6).
	Audit AuditLog
}

Deps carries the collaborators a Mount is built from. All fields are required.

type FileUserStore

type FileUserStore struct {
	Path string
}

FileUserStore is the production UserStore over the users file at Path.

func (FileUserStore) FilePath

func (s FileUserStore) FilePath() string

func (FileUserStore) Load

func (s FileUserStore) Load() (*auth.UsersFile, error)

func (FileUserStore) Save

func (s FileUserStore) Save(uf *auth.UsersFile) error

type LabelStore

type LabelStore interface {
	List(iteration int) ([]labels.Label, error)
	Get(iteration int, labelID string) (labels.Label, error)
	Add(iteration int, in labels.AddInput) (labels.Label, error)
	Edit(iteration int, labelID string, in labels.EditInput) (labels.Label, error)
	// SidecarPath is the file a mutation on the iteration will modify — the
	// mutation guard locks it and captures its pre-image before the handler
	// runs. An empty string disables the guard (stores with no file target).
	SidecarPath(iteration int) string
}

LabelStore is the label persistence surface the handlers consume. The production implementation is SidecarLabelStore (iter-N.labels.yaml sidecars via internal/review/labels); tests inject failures through this seam.

type Mount

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

Mount is the review collection endpoint as a plain http.Handler. Construct it with New; R3 registers it via RegisterMount(m.Prefix(), m). It is safe for concurrent use — all mutable state lives in the backing stores, which serialize their own writes.

func New

func New(prefix string, deps Deps) (*Mount, error)

New builds a Mount whose routes are rooted at prefix (typically DefaultPrefix). The prefix must be rooted and below "/"; a trailing slash is normalized away, mirroring the R3 side's RegisterMount normalization so the registered mux patterns and the mount registration always agree.

func (*Mount) Prefix

func (m *Mount) Prefix() string

Prefix returns the normalized mount prefix, i.e. the exact value R3 passes to RegisterMount.

func (*Mount) ServeHTTP

func (m *Mount) ServeHTTP(w nethttp.ResponseWriter, r *nethttp.Request)

ServeHTTP dispatches to the mount's internal mux. Patterns carry the full prefixed paths, so no prefix stripping happens (or is needed) on the R3 side.

type SidecarLabelStore

type SidecarLabelStore struct {
	// Dir is the iteration-log directory holding iter-N.labels.yaml sidecars.
	Dir string
}

SidecarLabelStore is the production LabelStore, delegating to the internal/review/labels sidecar CRUD rooted at the iteration-log directory.

func (SidecarLabelStore) Add

func (s SidecarLabelStore) Add(iteration int, in labels.AddInput) (labels.Label, error)

func (SidecarLabelStore) Edit

func (s SidecarLabelStore) Edit(iteration int, labelID string, in labels.EditInput) (labels.Label, error)

func (SidecarLabelStore) Get

func (s SidecarLabelStore) Get(iteration int, labelID string) (labels.Label, error)

func (SidecarLabelStore) List

func (s SidecarLabelStore) List(iteration int) ([]labels.Label, error)

func (SidecarLabelStore) SidecarPath

func (s SidecarLabelStore) SidecarPath(iteration int) string

type UserStore

type UserStore interface {
	Load() (*auth.UsersFile, error)
	Save(uf *auth.UsersFile) error
	// FilePath is the users file a mutation will modify — the mutation guard
	// locks it and captures its pre-image before the handler runs. An empty
	// string disables the guard (stores with no file target).
	FilePath() string
}

UserStore is the users-file surface the admin handlers consume. The production implementation is FileUserStore over the local users file (~/.config/da/review/users.yaml — auth.DefaultUsersPath).

Jump to

Keyboard shortcuts

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