api

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package api provides Chronos's HTTP REST API. It is a thin transport layer over the persistence ports: parse the request, call a port, render the response. Per the cognitive-stack vision Chronos surfaces signals; it does not interpret or render prose. The wire shape is just the structured signal — Title/Summary/Suggestion are not part of this API.

Index

Constants

View Source
const FederationExportVersion = "v1"

FederationExportVersion is the wire-format generation. Bump on shape changes so downstream pull-mirror clients can reject incompatible payloads explicitly.

View Source
const MaxIngestBatchSize = 1000

MaxIngestBatchSize caps a single /v1/ingest/batch payload. Beyond this, callers must chunk — the limit guards memory in the handler path and gives operators a predictable upper bound on transaction duration.

Variables

This section is empty.

Functions

func Chain

func Chain(h http.Handler, mws ...Middleware) http.Handler

Chain composes middlewares into a single handler. The first middleware in the slice is the outermost — i.e. it sees the request first and the response last.

func MarshalIndent added in v0.5.0

func MarshalIndent(v any) ([]byte, error)

MarshalIndent is exported only so the cmd-line helper that pulls federation snapshots can render JSON with the same shape the wire uses. Kept here so the wire and the CLI never drift.

Types

type ConfigValidateRequest added in v0.5.0

type ConfigValidateRequest struct {
	Env map[string]string `json:"env"`
}

ConfigValidateRequest is the wire shape for POST /v1/config/validate. Callers pass the env vars they intend to flip; the handler reports what each detector would do under that config WITHOUT mutating the running server's behaviour.

type ConfigValidateResponse added in v0.5.0

type ConfigValidateResponse struct {
	Detectors []DetectorReport `json:"detectors"`
}

ConfigValidateResponse lists one report per detector, sorted by name for stable diffing across runs.

type DetectorReport added in v0.5.0

type DetectorReport struct {
	Name       string         `json:"name"`
	Enabled    bool           `json:"enabled"`
	Reason     string         `json:"reason,omitempty"`
	Thresholds map[string]any `json:"thresholds,omitempty"`
	Warnings   []string       `json:"warnings,omitempty"`
}

DetectorReport carries the dry-run verdict for one detector.

func BuildConfigReportForExport added in v0.5.0

func BuildConfigReportForExport(cfg *config.Config) []DetectorReport

BuildConfigReportForExport is the exported entry point for callers outside the HTTP layer (e.g. the MCP describe_detector tool) that want the same per-detector verdict the /v1/config/validate endpoint returns. Returns the list directly rather than the wrapping struct so MCP/CLI surfaces can render it however they like.

type EvidenceDTO

type EvidenceDTO struct {
	Series  uuid.UUID          `json:"series"`
	Time    time.Time          `json:"time"`
	Kind    string             `json:"kind"`
	Score   float64            `json:"score"`
	Metrics map[string]float64 `json:"metrics,omitempty"`
}

EvidenceDTO is one piece of supporting evidence.

type ExplanationDTO added in v0.5.0

type ExplanationDTO struct {
	FeatureEvolution   []FeatureSampleDTO `json:"feature_evolution,omitempty"`
	ComparablePeers    int                `json:"comparable_peers,omitempty"`
	BaselineWindowDays int                `json:"baseline_window_days,omitempty"`
	ThresholdUsed      float64            `json:"threshold_used,omitempty"`
	DetectorVersion    string             `json:"detector_version,omitempty"`
}

ExplanationDTO is the wire shape of an Explanation value object. All fields are optional; a Signal without an Explanation omits this object entirely (pointer in SignalDTO).

type FeatureSampleDTO added in v0.5.0

type FeatureSampleDTO struct {
	At    time.Time `json:"at"`
	Value float64   `json:"value"`
}

FeatureSampleDTO is one observation in the feature evolution series.

type FederationExportResponse added in v0.5.0

type FederationExportResponse struct {
	GeneratedAt  time.Time                `json:"generated_at"`
	Source       string                   `json:"source"`  // "chronos"
	Version      string                   `json:"version"` // schema version of this payload
	Patterns     []FederationPatternStats `json:"patterns"`
	TotalSignals int                      `json:"total_signals"`
}

FederationExportResponse is the wire shape for GET /v1/federation/export. Carries aggregated, anonymized pattern statistics: counts per pattern type, average / min / max strength and confidence, mean sample size, plus the generation timestamp.

Nothing in the payload identifies a tenant. There are no scope_ids, no series ids, no signal ids, and no per-row evidence. That is the load-bearing safety property of the federation hook — the export is community-grade statistical insight, not raw data.

type FederationPatternStats added in v0.5.0

type FederationPatternStats struct {
	Pattern          string  `json:"pattern"`
	Count            int     `json:"count"`
	AvgStrength      float64 `json:"avg_strength"`
	MinStrength      float64 `json:"min_strength"`
	MaxStrength      float64 `json:"max_strength"`
	AvgConfidence    float64 `json:"avg_confidence"`
	MinConfidence    float64 `json:"min_confidence"`
	MaxConfidence    float64 `json:"max_confidence"`
	AvgSampleSize    float64 `json:"avg_sample_size,omitempty"` // mean of metrics["n"] when populated
	TentativeCount   int     `json:"tentative_count,omitempty"`
	EstablishedCount int     `json:"established_count,omitempty"`
	StrongCount      int     `json:"strong_count,omitempty"`
}

FederationPatternStats summarises one pattern type's signal population.

type IngestBatchRequest added in v0.5.0

type IngestBatchRequest struct {
	Observations []IngestRequest `json:"observations"`
	// DeferDetection is accepted for API forward-compatibility. Chronos
	// already separates ingest from detection (the compute pipeline
	// runs detection at job time, not on ingest), so the flag is
	// currently a no-op — it is echoed in the response so callers can
	// rely on the field shape without branching.
	DeferDetection bool `json:"defer_detection,omitempty"`
}

IngestBatchRequest is the wire shape for POST /v1/ingest/batch. It accepts an array of observations in one HTTP call so integrators backfilling history don't pay one round-trip per datapoint.

All observations land in a single repository call; on any one observation failing validation the entire batch is rejected (all-or-nothing semantics — partial writes would be hard to roll back and harder to reason about).

type IngestBatchResponse added in v0.5.0

type IngestBatchResponse struct {
	Accepted       int  `json:"accepted"`
	DeferDetection bool `json:"defer_detection"`
}

IngestBatchResponse confirms how many observations were persisted and whether detection was deferred.

type IngestRequest

type IngestRequest struct {
	ID        uuid.UUID         `json:"id,omitempty"`
	EntityID  uuid.UUID         `json:"entity_id"`
	ScopeID   uuid.UUID         `json:"scope_id"`
	Timestamp time.Time         `json:"timestamp"`
	Features  []float64         `json:"features"`
	Labels    []string          `json:"labels,omitempty"`
	Meta      map[string]string `json:"meta,omitempty"`
	// Adapter labels the source for retention policy / count queries. If
	// empty, signals fall back to "http" so streaming clients are
	// distinguishable from pull adapters in the Count column.
	Adapter string `json:"adapter,omitempty"`
}

IngestRequest is the wire shape for POST /v1/ingest. It mirrors the vision's TimeSeriesPoint with multi-feature support.

type Middleware

type Middleware func(http.Handler) http.Handler

Middleware wraps an http.Handler with cross-cutting behaviour. It is the standard "decorator" signature used by net/http composition.

func BearerAuth

func BearerAuth(token string) Middleware

BearerAuth requires every request to carry an "Authorization: Bearer <token>" header matching the configured token. If token is empty, authentication is disabled and the middleware is a no-op — that matches the documented "no auth out of the box" default. The /health route is always public so liveness probes never need credentials.

Token comparison uses subtle.ConstantTimeCompare to avoid leaking the prefix length via timing.

func Logging

func Logging(logger *slog.Logger, metrics *observability.Metrics) Middleware

Logging emits a structured slog event for every HTTP request once the response has been written, and (when metrics is non-nil) records the observation for /metrics scraping. Fields on the slog event: method, path, status, duration, remote.

func Recover

func Recover(logger *slog.Logger) Middleware

Recover converts panics in downstream handlers into 500 responses and logs the stack trace. Without it, a single buggy handler can crash the whole HTTP server because net/http re-throws the panic.

type SSEBroadcaster

type SSEBroadcaster interface {
	// Subscribe registers a stream client. scopes is a server-side
	// allowlist: nil delivers every scope (operator-grade reads),
	// non-nil restricts delivery to the listed scope ids. The handler
	// constructs the allowlist from ?scope_id= (single-element) or
	// ?scope_in= (multi-element) so clients can't widen their own
	// filter post-handshake.
	Subscribe(scopes []uuid.UUID, pattern string) (uuid.UUID, <-chan domain.Signal)
	Unsubscribe(uuid.UUID)
}

SSEBroadcaster is the structural contract the api layer requires of any streaming source. internal/notify.SSE satisfies it without either package importing the other (notify -> api would otherwise cycle through webhook.go's use of ToSignalDTO).

type Server

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

Server holds dependencies for HTTP handlers.

func NewServer

func NewServer(states ports.EntityStateRepository, signals ports.SignalRepository, metrics *observability.Metrics, logger *slog.Logger) *Server

NewServer wires an HTTP server. Pass slog.Default() for the logger if the caller has no preference. The states repository is required to support the streaming /v1/ingest endpoint; pass nil to disable it (the route still mounts but returns 501). metrics may be nil, in which case /metrics returns an empty document and observations in handlers are no-ops.

func (*Server) RegisterRoutes

func (s *Server) RegisterRoutes(mux *http.ServeMux)

RegisterRoutes wires API routes onto a ServeMux. Routes are stable wire contracts; bump /v1 to introduce incompatible changes.

func (*Server) WithSSE

func (s *Server) WithSSE(sse SSEBroadcaster) *Server

WithSSE attaches an SSE broadcaster so the /v1/signals/stream route can register subscribers. Returns the server for chaining. Without this attachment, /v1/signals/stream responds 501 Not Implemented.

type SignalDTO

type SignalDTO struct {
	ID         uuid.UUID          `json:"id"`
	ScopeID    uuid.UUID          `json:"scope_id"`
	Series     uuid.UUID          `json:"series"`
	Pattern    string             `json:"pattern"`
	DetectedAt time.Time          `json:"detected_at"`
	Window     TimeWindowDTO      `json:"window"`
	Strength   float64            `json:"strength"`
	Confidence float64            `json:"confidence"`
	Metrics    map[string]float64 `json:"metrics,omitempty"`
	Evidence   []EvidenceDTO      `json:"evidence,omitempty"`
	// Explanation carries detector-side context that lets downstream
	// consumers narrate WHY the signal fired without re-deriving the
	// data. Omitted when the detector did not surface one.
	Explanation *ExplanationDTO `json:"explanation,omitempty"`
	// ConfidenceClass is the qualitative grade (tentative /
	// established / strong) the detector assigned based on sample
	// size vs MIN_POINTS. Empty when the detector did not classify.
	ConfidenceClass string `json:"confidence_class,omitempty"`
}

SignalDTO is the wire shape for signals returned by the HTTP API. It is decoupled from domain.Signal so internal refactors do not break clients. There is no Title/Summary/Suggestion: per the cognitive- stack vision, Chronos emits signals — not prose. Downstream consumers (Nous) interpret the structured fields.

func ToSignalDTO

func ToSignalDTO(s domain.Signal) SignalDTO

ToSignalDTO renders a domain.Signal into its wire form.

type TimeWindowDTO

type TimeWindowDTO struct {
	Start time.Time `json:"start"`
	End   time.Time `json:"end"`
}

TimeWindowDTO is the analysis window over which the signal was detected.

Directories

Path Synopsis
Package grpc provides Chronos's gRPC transport layer.
Package grpc provides Chronos's gRPC transport layer.

Jump to

Keyboard shortcuts

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