clio

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

Documentation

Overview

Package clio integrates a clio event store as a server-registered Atlas connector: a BPMN clio "write-events" connector task appends an event to a configured clio instance through the job path (ADR-0036), mirroring how the dmn package delegates a decision to temis (ADR-0014). The integration inherits the job protocol's durability and non-blocking properties (ADR-0007):

  • A connector task creates a job carrying the reserved compiler.ClioWriteJobType. The processor never performs the outbound call itself, so it stays allocation-free (invariant I1) and free of any HTTP dependency.
  • The in-process Handler — a job worker — pulls those jobs, appends the event to clio off the processor goroutine and after fsync (invariant I2, never inside applyToState / I4), and completes the job, which drives the token onward.
  • The clio endpoint and credentials live in a server-side Registry keyed by connector name, so a model refers to a connector by name only and never carries a URL or secret (ADR-0036).

Delivery is at-least-once (a crash between "clio accepted" and "job completed" replays the write); every event carries the job key as an idempotency key so clio de-duplicates a replayed write rather than doubling the event.

Index

Constants

View Source
const DefaultEventSource = "atlas"

DefaultEventSource is the CloudEvents `source` an outbound write carries when a task does not set one. clio rejects a write with an empty source, so this keeps a model that only names a subject and type working.

Variables

This section is empty.

Functions

func Handler

func Handler(store *state.Store, lookup ProcessLookup, reg *Registry) job.Handler

Handler builds a job handler that performs a clio "write-events" connector task. Register it with a job.Runner for the reserved ClioWriteJobType index; the runner then pulls activatable clio jobs, and for each the handler resolves the connector task's connector/subject/event-type from the compiled process, resolves the connector's client from reg, and appends an event carrying the instance's variables as its body — keyed by the job key so an at-least-once retry de-duplicates (ADR-0036). Returning an error leaves the job pending, exactly as for any worker; the runner completes it only on success.

func MintKey

func MintKey(ctx context.Context, endpoint, adminToken string, req KeyRequest) (string, error)

MintKey creates a new clio API key and returns its full, once-shown secret (clio's "kid.secret"). It is a standalone call — not a Client method — because it authenticates with a clio **admin** token, distinct from a connector's read token, and exists only to provision a connector's credential (ADR-0092) without an operator copy-pasting one. The caller must never persist adminToken; only the returned scoped key is stored (sealed in the vault). Delivery is not idempotent, so a caller should mint once per provisioning action.

func QueryHandler

func QueryHandler(store *state.Store, lookup ProcessLookup, reg *Registry) job.OutputHandler

QueryHandler builds a job handler for a clio "query" connector task: it reads projected state (get_state) or runs a stored query (run_query) on the task's connector and writes the result back into the task's result variable. Register it with a job.Runner for the reserved ClioQueryJobType index via HandleWithOutput, like the REST worker (ADR-0036/0067): when the task carries a query the handler runs it, otherwise it reads get_state for the task's subject (with the optional reduce spec). Returning an error leaves the job pending (retry, then an incident), exactly as for the write handler.

func ReadHandler

func ReadHandler(store *state.Store, lookup ProcessLookup, reg *Registry) job.OutputHandler

ReadHandler builds a job handler for a clio "read" connector task: it reads the task's subject events (up to the task's limit) from the connector and writes them back into the task's result variable as a JSON array. Register it for the reserved ClioReadJobType index via HandleWithOutput (ADR-0036).

Types

type Client

type Client interface {
	WriteEvent(ctx context.Context, e Event) error
	GetState(ctx context.Context, subject, reduceSpec string) (map[string]any, error)
	Query(ctx context.Context, subject, where string) (any, error)
	ReadEvents(ctx context.Context, req ReadEventsRequest) ([]InboundEvent, error)
}

Client talks to one clio instance. It is an interface so the worker and the inbound bridge are testable without a live clio and so a connector name binds to exactly one endpoint. WriteEvent appends a domain event; GetState reads a projection; Query runs a stored query; ReadEvents reads a subject's events.

type Connector

type Connector struct {
	Endpoint string
	Token    string
}

Connector is the server-side configuration of one clio connector: the base endpoint of the clio instance and an optional bearer token for it.

type Event

type Event struct {
	Source         string // CloudEvents source (clio requires it); defaults to DefaultEventSource when empty
	Subject        string
	Type           string
	Data           map[string]any
	IdempotencyKey string
}

Event is one event a connector task appends to clio. IdempotencyKey is deterministic (the job key), so an at-least-once retry is de-duplicated by clio rather than appended twice.

type HTTPClient

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

HTTPClient talks to a real clio instance over its HTTP API (clio v1). Each method targets clio's documented route: write-events, read-events, run-query (all POST with a JSON body) and state (GET with the subject in the path). clio subjects are absolute ("/orders/42"); a leading slash is added if a task omits it. Reads stream NDJSON, one event JSON per line, oldest first.

func NewHTTPClient

func NewHTTPClient(conn Connector) *HTTPClient

NewHTTPClient builds a clio HTTP client for a configured connector.

func (*HTTPClient) GetState

func (c *HTTPClient) GetState(ctx context.Context, subject, reduceSpec string) (map[string]any, error)

GetState reads a subject's folded state and returns its state object. clio's state route is GET {Endpoint}/api/v1/state/<subject> with the subject in the path; the effective reduce spec is chosen server-side by registered prefix (ADR-0041), so reduceSpec is accepted for interface symmetry but not sent. The response is a state envelope; we return its `state` object.

func (*HTTPClient) Query

func (c *HTTPClient) Query(ctx context.Context, subject, where string) (any, error)

Query runs a clio filter query over a subject and returns the matching events. Wire format: POST {Endpoint}/api/v1/run-query with {subject, where}, where `where` is clio's CEL predicate ("" = every event in the scope). The response streams NDJSON (one event per line); we collect the events into a slice so the result canonicalizes into a process variable like any other JSON array.

func (*HTTPClient) ReadEvents

func (c *HTTPClient) ReadEvents(ctx context.Context, r ReadEventsRequest) ([]InboundEvent, error)

ReadEvents reads a subject's events oldest-first. Wire format: POST {Endpoint}/api/v1/read-events with a JSON body {subject, recursive, lowerBound, types, limit}, returning NDJSON (one event JSON per line). clio's lowerBound is inclusive but AfterID is the last-consumed id, so the first line equal to AfterID is dropped, making AfterID an exclusive cursor. `recursive` includes the subject's whole subtree — the setting that lets a watch on /employees catch an event written to /employees/E-123456.

func (*HTTPClient) WriteEvent

func (c *HTTPClient) WriteEvent(ctx context.Context, e Event) error

type InboundEvent

type InboundEvent struct {
	ID        string         `json:"id"`
	Subject   string         `json:"subject"`
	Type      string         `json:"type"`
	Data      map[string]any `json:"data"`
	Partition int            `json:"partition,omitempty"`
}

InboundEvent is one event read back from clio (a read/query result, or an event the inbound bridge consumes, ADR-0075). ID is clio's event id — a per-partition monotonic sequence rendered as a decimal string (clio's `strconv.FormatUint`), which is both the resume cursor and, parsed as a uint64, the inbound bridge's dedup sequence. clio events carry no separate `seq` field. Partition is a server-derived view attribute set only on reads (0 for a single-partition clio, the recommended production configuration).

type KeyRequest

type KeyRequest struct {
	Name      string
	Scopes    []string
	ExpiresAt string
}

KeyRequest describes a clio API key to mint (clio's POST /api/v1/keys). Scopes are clio scope strings — e.g. "read:/employees/*" grants read on the /employees subtree (the recursive grant a subtree watch needs), "read:/employees" only the exact subject. ExpiresAt is an optional RFC3339 timestamp ("" = no expiry).

type ProcessLookup

type ProcessLookup func(defKey uint64) *compiler.CompiledProcess

ProcessLookup resolves a process-definition key to its compiled process. The worker uses it to find the connector, subject, and event type a clio job belongs to, so one handler serves every deployed process.

type ReadEventsRequest

type ReadEventsRequest struct {
	Subject   string
	AfterID   string
	Recursive bool
	Types     []string
	Limit     int
}

ReadEventsRequest selects the events a read returns: a Subject, an optional exclusive AfterID cursor ("" reads from the start), whether to include the subject's subtree, an optional type filter, and a Limit (0 = the connector's default).

type Registry

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

Registry resolves a connector name to the Client for its clio instance. Connectors are registered at the server from configuration (endpoint plus credentials), so a model refers to a connector by name only (ADR-0036). A Registry is read-only once populated and safe for concurrent use by workers.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates an empty connector registry.

func (*Registry) Client

func (r *Registry) Client(name string) (Client, bool)

Client returns the client bound to name, or nil and false if none is registered.

func (*Registry) Register

func (r *Registry) Register(name string, c Client)

Register binds a connector name to its client. Registering the same name again replaces the earlier binding (last write wins), so reconfiguration is simple. Populate the registry before the processes that use it start running.

func (*Registry) Replace

func (r *Registry) Replace(clients map[string]Client)

Replace swaps the whole set of registered connectors at once, so a server can rebuild the registry from managed configuration after a change (ADR-0041). The caller must serialize Replace with the workers that read the registry — the Atlas server does both on its run-loop goroutine — so no lock is needed. A nil map clears the registry.

Jump to

Keyboard shortcuts

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