api

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Apr 30, 2026 License: MIT Imports: 34 Imported by: 0

Documentation

Overview

Package api provides shared helpers for the Connect-RPC API layer, including error mapping, pagination, and time conversion utilities.

Index

Constants

View Source
const (
	DefaultPageSize = 100
	MaxPageSize     = 1000
)

Variables

View Source
var (
	ErrEntityNotFound       = errors.New("entity not found")
	ErrDeviceNotFound       = errors.New("device not found")
	ErrAreaNotFound         = errors.New("area not found")
	ErrZoneNotFound         = errors.New("zone not found")
	ErrDriverNotFound       = errors.New("driver not found")
	ErrInstanceNotFound     = errors.New("driver instance not found")
	ErrAutomationNotFound   = errors.New("automation not found")
	ErrScriptNotFound       = errors.New("script not found")
	ErrAutomationDisabled   = errors.New("automation disabled")
	ErrRunNotFound          = errors.New("run not found")
	ErrRunAlreadyFinished   = errors.New("run already finished")
	ErrCapabilityUnknown    = errors.New("capability unknown")
	ErrDriverUnavailable    = errors.New("driver unavailable")
	ErrSubscriptionOverflow = errors.New("subscription overflow")
	ErrValidationFailed     = errors.New("validation failed")
	ErrNotImplemented       = errors.New("not implemented")
	ErrPathEscape           = errors.New("path escapes config dir")
)

Functions

func BoundedFanOut

func BoundedFanOut[T any](ctx context.Context, in <-chan T, bufSize int) (<-chan T, <-chan error)

BoundedFanOut reads from in and forwards to an output channel of size bufSize. If the output channel is full, it closes the output channel and sends ErrSubscriptionOverflow on the done channel. When in is closed or ctx is cancelled, done receives nil (or ctx.Err()).

func ClampPageSize

func ClampPageSize(n uint32) uint32

func EncodeCursor

func EncodeCursor(c Cursor) (string, error)

func GoTime

func GoTime(ts *timestamppb.Timestamp) time.Time

func MCPAuthMiddleware

func MCPAuthMiddleware(bearer *authn.Bearer, next http.Handler) http.Handler

MCPAuthMiddleware wraps an http.Handler with bearer-token authentication. It calls the Bearer authenticator; on success it stashes the principal on the context. On failure or missing header it passes through without setting the principal — the MCP handler decides whether to require authentication.

func MCPInterceptor

func MCPInterceptor(m *observability.Metrics) connect.Interceptor

MCPInterceptor emits gohome_mcp_* metrics for MCP-sourced requests.

func NewAuthenticate

func NewAuthenticate(chain auth.Authenticator, bearer *authn.Bearer, tokens *credentials.Tokens) connect.UnaryInterceptorFunc

NewAuthenticate returns the C9 authenticate interceptor. Wraps the supplied authenticator chain, attaches Principal + (if applicable) compiled token scope to the request context.

func NewAuthorize

func NewAuthorize(rt *policy.Runtime, catalog ProcedureCatalog, recorder *audit.Recorder, metrics *observability.Metrics) connect.UnaryInterceptorFunc

NewAuthorize returns the C9 authorize interceptor. When rt is nil the interceptor passes all requests through (daemon bring-up before the policy runtime is loaded). metrics is optional; pass nil to disable metric emission.

func NewWebhookHandler

func NewWebhookHandler(router WebhookRouter, app WebhookAppender, m WebhookMetrics) http.Handler

NewWebhookHandler returns an http.Handler mounted at /webhooks/.

func ProtoTime

func ProtoTime(t time.Time) *timestamppb.Timestamp

func SetStreamConfig

func SetStreamConfig(c StreamConfig)

SetStreamConfig replaces the global stream config. Intended for tests.

func SourceFromContext

func SourceFromContext(ctx context.Context) (string, bool)

SourceFromContext returns the source. Returns ("cli", false) if not set.

func SourceInterceptor

func SourceInterceptor() connect.Interceptor

SourceInterceptor reads x-gohome-source header and puts it on context.

func ToConnect

func ToConnect(ctx context.Context, err error, reason string) error

func WithHTTPRequest

func WithHTTPRequest(ctx context.Context, r *http.Request) context.Context

WithHTTPRequest stores an *http.Request on the context so Connect handlers can read incoming cookies (e.g. for session refresh).

func WithResponseWriter

func WithResponseWriter(ctx context.Context, w http.ResponseWriter) context.Context

WithResponseWriter stores an http.ResponseWriter on the context so Connect handlers can write cookies.

func WithSource

func WithSource(ctx context.Context, source string) context.Context

WithSource attaches the request source to ctx.

Types

type AppendedWebhook

type AppendedWebhook struct {
	Slug     string
	Body     []byte
	Headers  map[string]string
	SourceIP string
}

AppendedWebhook is the parsed and verified inbound webhook payload.

type Area

type Area struct {
	ID          string
	DisplayName string
	ParentID    string
}

type AreaReader

type AreaReader interface {
	ListAreas(ctx context.Context, page PageReq) ([]Area, Cursor, error)
	GetArea(ctx context.Context, id string) (Area, error)
}

type AreaService

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

func NewAreaService

func NewAreaService(be AreaReader) *AreaService

func (*AreaService) Get

type AuthDeps

type AuthDeps struct {
	Identity   *identity.Store
	Password   *credentials.Password
	Tokens     *credentials.Tokens
	Sessions   *sessions.Store
	Enrollment *credentials.Enrollment
	Throttle   *throttle.Throttle
	Audit      *audit.Recorder
	Policy     *policy.Runtime
	Metrics    *observability.Metrics
}

AuthDeps holds the dependencies required by the real AuthService.

type AuthService

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

AuthService implements AuthServiceHandler with real auth logic.

func NewAuthService

func NewAuthService(d AuthDeps) *AuthService

NewAuthService constructs an AuthService with the supplied dependencies.

func (*AuthService) ChangePassword

ChangePassword verifies the old password and sets a new one.

func (*AuthService) CreateToken

CreateToken issues a new bearer token for the authenticated user.

func (*AuthService) CurrentUser

CurrentUser returns the authenticated principal's user record.

func (*AuthService) ExplainAuthorization

ExplainAuthorization evaluates and explains a policy decision for a given user and action.

func (*AuthService) ListUsers

ListUsers returns all users in the identity store.

func (*AuthService) Login

Login authenticates a user with username + password and issues a session cookie.

func (*AuthService) Logout

Logout destroys the current session and clears cookies.

func (*AuthService) MintEnrollmentToken

MintEnrollmentToken issues a one-time enrollment token for a user.

func (*AuthService) RedeemEnrollmentToken

RedeemEnrollmentToken validates and consumes a one-time enrollment token.

func (*AuthService) Refresh

Refresh rotates session cookies using the refresh cookie in the request.

func (*AuthService) RegisterPasskey

RegisterPasskey is not yet implemented.

func (*AuthService) RevokeToken

RevokeToken revokes a token by ID.

func (*AuthService) StartWebAuthnChallenge

StartWebAuthnChallenge is not yet implemented.

type Automation

type Automation struct {
	ID, DisplayName, Mode string
	Enabled               bool
	InFlight              uint32
}

type AutomationControl

type AutomationControl interface {
	List(ctx context.Context, page PageReq) ([]Automation, Cursor, error)
	Get(ctx context.Context, id string) (Automation, error)
	SetEnabled(ctx context.Context, id string, enabled bool, actor string) (Automation, error)
	Trigger(ctx context.Context, id, actor string) (runID string, err error)
	Trace(ctx context.Context, automationID, runID string, fromCursor uint64) (<-chan TraceEvent, func(), error)
}

type AutomationService

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

func NewAutomationService

func NewAutomationService(be AutomationControl) *AutomationService

type CapabilityCaller

type CapabilityCaller interface {
	// Call dispatches the capability invocation through the carport supervisor;
	// blocks until the driver acks or ctx is cancelled. Returns the command's
	// correlation id (the CommandIssued event id) on success.
	Call(ctx context.Context, entityID, capability string, params map[string]any) (correlationID string, err error)
}

type ConfigApplier

type ConfigApplier interface {
	Validate(ctx context.Context, pklBundle []byte) (valid bool, errs []string, diff ConfigDiff, hash string, err error)
	Apply(ctx context.Context, pklBundle []byte, message, expectedHash string, dryRun, strict bool, actor string) (ConfigApplyResult, error)
	Reload(ctx context.Context, actor string) (diff ConfigDiff, correlationID string, err error)
	CurrentArtifact(ctx context.Context) (*configv1.ConfigSnapshot, error)
}

type ConfigApplyResult

type ConfigApplyResult struct {
	Applied       bool
	Diff          ConfigDiff
	CorrelationID string
	BundleHash    string
	Errors        []string
}

type ConfigDiff

type ConfigDiff struct {
	DriverAdded, DriverRemoved, DriverChanged int32
	EntitiesAdded, EntitiesRemoved            int32
	AutomationsChanged                        int32
	Lines                                     []string
}

type ConfigService

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

func NewConfigService

func NewConfigService(be ConfigApplier) *ConfigService

type Cursor

type Cursor struct {
	Position uint64
	Tiebreak string
}

func DecodeCursor

func DecodeCursor(token string) (Cursor, error)

type Device

type Device struct {
	ID               string
	FriendlyName     string
	AreaID           string
	DriverInstanceID string
	EntityIDs        []string
}

type DeviceReader

type DeviceReader interface {
	ListDevices(ctx context.Context, areaID string, page PageReq) ([]Device, Cursor, error)
	GetDevice(ctx context.Context, id string) (Device, error)
}

type DeviceService

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

func NewDeviceService

func NewDeviceService(r DeviceReader, w DeviceWriter) *DeviceService

type DeviceWriter

type DeviceWriter interface {
	RenameDevice(ctx context.Context, id, newName, actor string) (Device, error)
	ReassignDevice(ctx context.Context, id, newAreaID, actor string) (Device, error)
}

DeviceWriter mutates devices and emits the corresponding registry-mutation events (DeviceRenamed, DeviceReassigned). actor is the principal id of the caller; empty string means "system".

type Driver

type Driver struct {
	Name, Version, Description string
	EntityClasses              []string
}

type DriverControl

type DriverControl interface {
	ListDrivers(ctx context.Context, page PageReq) ([]Driver, Cursor, error)
	ListInstances(ctx context.Context, page PageReq) ([]DriverInstance, Cursor, error)
	InstanceHealth(ctx context.Context, instanceID string) (ok bool, detail string, err error)
	RestartInstance(ctx context.Context, instanceID, reason, actor string) error
}

type DriverInstance

type DriverInstance struct {
	ID, DriverName, Status string
	EntityCount            uint32
	LastHandshakeUnixMs    int64
}

type DriverService

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

func NewDriverService

func NewDriverService(be DriverControl) *DriverService

type Entity

type Entity struct {
	ID, Type, DeviceID, AreaID, ZoneID, FriendlyName string
	State                                            *entityv1.Attributes
	Capabilities                                     *entityv1.Attributes
}

type EntityChange

type EntityChange struct {
	EntityID string
	Cursor   uint64
	AtUnixMs int64
	Entity   Entity
}

type EntityFilter

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

func NewEntityFilter

func NewEntityFilter(rt *policy.Runtime, mode commonpb.PolicyMode) *EntityFilter

func (*EntityFilter) AllowsEntity

func (f *EntityFilter) AllowsEntity(ctx context.Context, p auth.Principal, t policy.Target) bool

func (*EntityFilter) Preflight

func (f *EntityFilter) Preflight(ctx context.Context, p auth.Principal, candidates []policy.Target) ([]policy.Target, error)

Preflight evaluates candidates at subscription open. In STRICT mode, returns CodePermissionDenied if any entity is denied. In FILTER mode, returns the narrowed list silently. Nil rt allows all.

type EntityReader

type EntityReader interface {
	ListEntities(ctx context.Context, sel EntitySelector, page PageReq) ([]Entity, Cursor, error)
	GetEntity(ctx context.Context, id string) (Entity, error)
}

type EntitySelector

type EntitySelector struct {
	EntityIDs []string
	DeviceIDs []string
	Areas     []string
	Zones     []string
	Classes   []string
}

type EntityService

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

func NewEntityService

func NewEntityService(r EntityReader, caller CapabilityCaller) *EntityService

func (*EntityService) SetPolicyRuntime

func (s *EntityService) SetPolicyRuntime(rt *policy.Runtime)

SetPolicyRuntime wires the policy runtime after construction.

func (*EntityService) SetStreamSource

func (s *EntityService) SetStreamSource(src EntityStreamSource)

SetStreamSource wires the live subscription source after construction (used by Task 17 to avoid breaking the constructor signature).

type EntityStreamSource

type EntityStreamSource interface {
	// Subscribe returns a channel of EntityChange events filtered by sel,
	// optionally replaying from fromCursor. The returned cancel func MUST be
	// called to release server-side resources.
	Subscribe(ctx context.Context, sel EntitySelector, fromCursor uint64) (<-chan EntityChange, func(), error)
}

EntityStreamSource is implemented in Task 17. Declared here so the field type compiles; concrete impl is added later.

type Event

type Event struct {
	Cursor        uint64
	At            time.Time
	Kind          string
	Entity        string
	Source        string
	CorrelationID string
	CauseID       string
	Payload       *eventv1.Payload
}

type EventAppender

type EventAppender interface {
	Append(ctx context.Context, payload *eventv1.Payload) (uint64, error)
}

EventAppender appends a raw event payload to the event store.

type EventFilter

type EventFilter struct {
	Kinds        []string
	EntityPrefix string
	Sources      []string
	FromCursor   uint64
	ToCursor     uint64
	FromTime     time.Time
	ToTime       time.Time
}

type EventService

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

func NewEventService

func NewEventService(be EventSource) *EventService

func (*EventService) Tail

type EventSource

type EventSource interface {
	Query(ctx context.Context, filter EventFilter, page PageReq) ([]Event, Cursor, error)
	Subscribe(ctx context.Context, filter EventFilter) (<-chan Event, func(), error)
}

type HeartbeatTicker

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

func NewHeartbeatTicker

func NewHeartbeatTicker(ctx context.Context, interval time.Duration) *HeartbeatTicker

func (*HeartbeatTicker) C

func (t *HeartbeatTicker) C() <-chan time.Time

func (*HeartbeatTicker) NotePayloadSent

func (t *HeartbeatTicker) NotePayloadSent()

func (*HeartbeatTicker) Stop

func (t *HeartbeatTicker) Stop()

type MCPCapsProvider

type MCPCapsProvider interface {
	MCPConfig(ctx context.Context) (MCPConfig, error)
}

MCPCapsProvider returns the current MCP capability caps.

type MCPConfig

type MCPConfig struct {
	EvalResultMaxBytes       uint32
	ReadFileMaxBytes         uint32
	EntitySubscriptionBuffer uint32
	TraceSubscriptionBuffer  uint32
	TailDefaultWaitSeconds   uint32
	TailMaxWaitSeconds       uint32
}

type PageReq

type PageReq struct {
	Size   uint32
	Cursor Cursor
}

type ProcedureCatalog

type ProcedureCatalog interface {
	Resolve(procedure string, requestAny any) (auth.Action, auth.Target, bool)
}

ProcedureCatalog resolves a Connect procedure name and request body to the corresponding auth.Action and auth.Target.

type SceneService

type SceneService struct{}

SceneService stub.

func NewSceneService

func NewSceneService() *SceneService

type Script

type Script struct {
	Name, Description string
}

type ScriptRunResult

type ScriptRunResult struct {
	RunID  string
	Result *structpb.Value
}

type ScriptRunner

type ScriptRunner interface {
	List(ctx context.Context, page PageReq) ([]Script, Cursor, error)
	Run(ctx context.Context, name string, args map[string]any, actor string) (ScriptRunResult, error)
	Cancel(ctx context.Context, runID string) error
	Eval(ctx context.Context, expr string, actor string) (result *structpb.Value, stdout string, err error)
	RunTests(ctx context.Context, path string) (<-chan StarlarkTestEvent, func(), error)
}

type ScriptService

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

func NewScriptService

func NewScriptService(be ScriptRunner, events EventAppender, caps MCPCapsProvider) *ScriptService

func (*ScriptService) RunTests

type StarlarkTestEvent

type StarlarkTestEvent struct {
	Name, Outcome, Detail string
	At                    time.Time
}

type StreamConfig

type StreamConfig struct {
	HeartbeatInterval time.Duration
	BufSize           int
}

StreamConfig holds tunables for streaming RPCs.

func DefaultStreamConfig

func DefaultStreamConfig() StreamConfig

DefaultStreamConfig returns the production defaults.

type SubsystemHealth

type SubsystemHealth struct {
	Name   string
	OK     bool
	Detail string
}

type SystemBackend

type SystemBackend interface {
	Version() VersionInfo
	Health(ctx context.Context) (ok bool, summary string, sub []SubsystemHealth)
	MetricsText() (string, error)
	Diagnostics(ctx context.Context) (bundle []byte, configHash string, generatedAt time.Time, err error)
	CreateSnapshot(ctx context.Context, owner, reason string) (cursor uint64, createdAt time.Time, err error)
	ConfigDir(ctx context.Context) (string, error)
	MCPConfig(ctx context.Context) (MCPConfig, error)
	RecordConfigFileEdit(ctx context.Context, p auth.Principal, sessionID, path, sha256Hex string, sizeBytes uint32) (uint64, error)
}

type SystemService

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

func NewSystemService

func NewSystemService(be SystemBackend) *SystemService

type TraceEvent

type TraceEvent struct {
	Cursor       uint64
	At           time.Time
	AutomationID string
	RunID        string
	Kind         string
	Detail       string
	Metadata     map[string]string
}

type VersionInfo

type VersionInfo struct {
	BinaryVersion string
	GitCommit     string
	BuildDate     string
	SchemaVersion string
}

type WebhookAppender

type WebhookAppender interface {
	AppendWebhook(ctx context.Context, w AppendedWebhook) error
}

WebhookAppender persists the inbound webhook as a WebhookReceived event.

type WebhookMetrics

type WebhookMetrics interface {
	Inc(slug, result string)
}

WebhookMetrics is optional; nil is fine in tests.

type WebhookRouter

type WebhookRouter interface {
	SecretFor(slug string) (string, bool)
	MaxBodyBytes() int64
}

WebhookRouter knows which slugs are registered and their HMAC secret.

type Zone

type Zone struct {
	ID          string
	DisplayName string
	AreaIDs     []string
}

type ZoneReader

type ZoneReader interface {
	ListZones(ctx context.Context, page PageReq) ([]Zone, Cursor, error)
	GetZone(ctx context.Context, id string) (Zone, error)
}

type ZoneService

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

func NewZoneService

func NewZoneService(be ZoneReader) *ZoneService

func (*ZoneService) Get

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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