hermod

package module
v1.18.8 Latest Latest
Warning

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

Go to latest
Published: Oct 15, 2026 License: MIT Imports: 6 Imported by: 0

README

Hermod

Real-time Postgres CDC to 40+ destinations, with a visual pipeline editor. One Go binary, self-hosted, running locally in about a minute.

No JVM, no Kafka cluster required, no per-row cloud bill. ./scripts/dev.sh brings up Postgres, the API, a worker and the UI, and completes first-run setup for you.

Where it fits: managed ELT (Fivetran) and open-source ELT (Airbyte) are batch-scheduled and measure freshness in minutes. Real-time CDC services (Estuary) are sub-second but not self-hostable. Stream processors (Redpanda Connect, NiFi) have no visual pipeline editor or need a JVM. Hermod is the self-hosted overlap: sub-second Postgres CDC, in-pipeline windowing and transforms, and a DAG you can see.

If you need 600 connectors, use Airbyte. If you need durable multi-day workflow orchestration, use Temporal. If you need one binary that tails a Postgres WAL and lands it somewhere else, in order and without a cluster, that is this.

Maturity, up front

Hermod covers a lot of surface, and that surface is not uniformly deep. Connectors are tiered — GA, Beta, Experimental — in Connector maturity tiers. Read that table before picking a connector for production. Anything not listed as GA has not been tested against live infrastructure.

Delivery is at-least-once with sink-side idempotency for duplicate suppression. Where this document says a guarantee is scoped or unfinished, that is the literal state of the code, not modesty.

Initial load is available for PostgreSQL, MySQL and MongoDB, and off by default. Set initial_load: "true" on one of those CDC sources and the rows already in the watched tables are carried across before streaming begins.

PostgreSQL is consistent rather than approximate. The replication slot is created over the replication protocol so that it exports a snapshot, the backfill reads at exactly that snapshot, and streaming starts from the slot's consistent point — so nothing committed before the boundary is missed and nothing is read twice at it.

MySQL and MongoDB are gapless but not snapshot-isolated. Neither offers Postgres's exported-snapshot handshake, so the boundary is pinned by position instead: the binlog coordinates (MySQL) or the cluster time (MongoDB) are taken before the tables are read, and streaming resumes from there. Nothing committed during the backfill is lost, but a row changed while it runs arrives twice — once as it was, once as it became. That is the ordinary at-least-once bargain and sink-side idempotency collapses it; see Delivery guarantee.

Each source keeps its own record of having run, so the backfill is once-only and turning the flag on for a workflow that is already streaming does nothing. For PostgreSQL the record is the replication slot itself — if it exists, changes have already streamed from it and the rows are downstream — so enabling the flag takes effect only once the slot is dropped. MySQL and MongoDB leave nothing server-side that could serve the same purpose, and their stream positions cannot stand in either: a backfill moves no binlog position and produces no resume token, so a table carried across and then never written to would be indistinguishable from one that had never run. They record completion in the source state the engine persists on every ack (initial_load: done), alongside the position.

Off by default deliberately: enabling it for every existing workflow would re-read every source table the first time a position was reset, which is the opposite of what an upgrade should do. The remaining CDC sources still have no initial load — for those, a backfill and a stream have to be sequenced by hand, position first, and the duplicates that ordering produces are what sink-side idempotency is for.


Enterprise Data Platform Features

Hermod is built for mission-critical enterprise data workloads, providing robust features for governance, reliability, and observability:

  • Two-Phase Commit (2PC) across a transactional sink group: A group of sinks commits atomically — either every member applied the batch or none did — and the guarantee survives a crash, because the coordinator's decision is durable before any member is told about it. Recovery uses presumed abort, and a reaper rolls back anything left in doubt past a deadline, which is what keeps an interrupted round from holding PostgreSQL locks indefinitely. Scope, stated plainly: the PostgreSQL (and YugabyteDB) sink and the MySQL (and MariaDB) sink can participate, so a group can keep PostgreSQL and MySQL destinations consistent with each other; MySQL needs 5.7.7+ and MariaDB 10.5.2+. A group containing a sink that cannot take part is refused at construction rather than silently degraded. Kafka does not participate — it previously carried no-op stubs that would have reported a failed rollback as a success. See Distributed transactions, including the operational hazard note, before enabling.
  • SSO & OpenID Connect (OIDC): Support for centralized identity providers like Okta, Auth0, and Azure AD for platform-wide authentication and RBAC.
  • AI automation: Prompt, extract, classify, embed and retrieve with Claude, ChatGPT, Gemini, DeepSeek, Ollama or any OpenAI-compatible model; an AI Agent node whose writes wait for human approval; an MCP server so Claude and other MCP clients can run Hermod workflows; run history with replay. See AI automation.
  • Data lineage & impact: One graph of the tables, topics, files, endpoints and ML models each vhost's workflows read and write, built from their saved configuration. Pick a source to see every workflow and asset downstream of it — what breaks if it changes. Scoped to the vhosts you may see. See Data lineage.
  • Vector Database Sinks: Built-in support for Pinecone, Milvus, and pgvector to power enterprise AI knowledge bases and RAG pipelines.
  • Hermod CLI (hermodctl): A powerful terminal-based tool for workflow linting, secret management, and real-time remote monitoring.
  • Global Schema Registry: Enforce data contracts with built-in JSON Schema, Avro, and Protobuf support. Automatically tracks schema versions and ensures backward compatibility.
  • Workflow Versioning & Rollback: Every change to a workflow is automatically versioned. One-click rollback allows you to quickly revert to a known stable configuration.
  • Distributed State & Coordination: Native support for Redis and Etcd backends for consistent state management across multiple worker instances.
  • Enterprise Secret Management: Securely resolve credentials from HashiCorp Vault, AWS Secrets Manager, and Azure Key Vault using the secret:key prefix.
  • Role-Based Access Control (RBAC): Granular permissions for Administrators, Editors, and Viewers, including VHost-level isolation.
  • Message Tracing & Visual Journey: Visualize the exact path of a single message through the DAG, including latency and data mutations at each step.
  • Pipeline Health Heatmaps: Real-time visualization of throughput and error rates directly on the workflow canvas.
  • WebAssembly (WASM) Transformations: Run custom business logic written in Go, Rust, or C++ at near-native speed within the pipeline.
  • Automated PII Scanning: Built-in mask transformation detects and redacts sensitive data (PII/PHI) using a sophisticated regex-based discovery engine.
  • Field-Level Encryption: encrypt and decrypt transformations seal named fields, so sensitive columns stay unreadable in the sink and are recovered only where a downstream pipeline is configured with the same key. Fourteen algorithms are selectable — AES-128/192/256 in GCM, CBC, CTR and CFB, plus ChaCha20-Poly1305 and XChaCha20-Poly1305 — with AES-256-GCM the default. Keys can be a hashed passphrase (the default), raw/hex/base64 bytes, or derived with PBKDF2 or scrypt, and the payload written as base64, base64url or hex. Each value gets a fresh random nonce, so an encrypted field cannot be used as a join key; in the default envelope format values are written as enc:v1:…/enc:v2:… and already-encrypted values are left alone, making a re-run idempotent. A raw format drops the envelope so a column encrypted by another system can be read and written — see Decrypting data Hermod did not encrypt. Decryption failures fail the message by default (onError can relax this to skip or null). The key is configured on the node, which means it is stored with the workflow definition.
  • Audit Logging: Complete history of administrative changes and system events for security and compliance audits.
  • At-least-once delivery with exactly-once effects at the sink: Messages are acknowledged only after successful delivery, using the Transactional Outbox pattern for SQL sources. Combined with sink-side idempotency keys (see Idempotency and Exactly-Once Effects), a duplicate delivery does not produce a duplicate row. This is not end-to-end exactly-once delivery: the transport is at-least-once and duplicates are suppressed where they land, which is the same guarantee most of this category actually ships.
  • Sequential Control Flow: Explicitly chain sinks and transformations sequentially. Supports "Sinks as Transformers" by returning data from a sink back into the workflow pipeline.
  • Stateful Event Correlation (Join/Zip): Wait for and join messages from multiple sources based on a common key before downstream delivery.
  • Circuit Breaker & Failure Recovery: Protect downstream systems with a built-in Circuit Breaker node. Automatically routes messages to failure branches when error thresholds are exceeded.
  • Interactive Workflow Debugger: Real-time message pausing, stepping, and inspection directly from the UI. Pause messages at any node to inspect payload state and resume processing when ready.
  • Visual Lineage with Data Diffs: Enhanced message tracing with "Before and After" snapshots for every transformation node in the DAG. Visually debug exactly how data is mutated at each step.
  • AIOps & Self-Healing Optimization: AI-driven performance tuning that automatically adjusts concurrency, batch sizes, and retry policies based on real-time throughput and error patterns.
  • Intelligent Data Quality Alerts: Automated detection of data quality drift using the governance.Scorer. Alerts trigger when schema adherence or DQ scores drift from historical averages.
  • Global Mesh Governance (experimental): A cluster registry and an HTTP forwarding sink, letting one Hermod instance route messages to another. Scope check: it is roughly 320 lines, Failover marks a cluster's status and logs rather than redirecting traffic, and there are no health checks, retries or partition handling. Treat it as a foundation, not a control plane.
  • WASM & Blueprint Marketplace: Discover, version, and share custom WebAssembly transformers and Go-based workflow blueprints via an integrated Marketplace.
  • Oracle CDC with LogMiner: Set the Oracle source's change capture method to LogMiner and it reads inserts, updates and deletes from the redo logs, with before and after images, resuming from the last acknowledged SCN after a restart. It needs archive logging and supplemental logging; see Oracle CDC. The default method is still incremental polling, which tracks a monotonically increasing key and so sees inserts only. Query construction for polling goes through sqlutil.BuildIncrementalQuery, which applies each dialect's row limit after the sort (Oracle's ROWNUM is evaluated before ORDER BY, so it is applied to an ordered subquery — getting this wrong silently skips rows).
  • IBM DB2 source (no driver in this build): The DB2 source opens the go_ibm_db driver, which needs CGO and IBM's native client library. Release builds use CGO_ENABLED=0 and include neither, so a DB2 source cannot connect.
  • Advanced Database Mapping & Schema Discovery: Production-ready column mapping for all major databases (Postgres, MySQL, MSSQL, Oracle, Snowflake, MongoDB, etc.). Supports automatic table creation, identity/auto-increment columns, and "Smart Mapping" from both source and sink schemas.
  • Resource-Aware Sharding: Advanced worker sharding using Rendezvous hashing weighted by real-time CPU/Memory metrics. Ensures optimal workload distribution and prevents workflow flapping with built-in hysteresis.
  • AI-Native Transformations: Integrated "Cognitive ETL" nodes for AI Enrichment and AI Mapping, supporting OpenAI and local Ollama models. Includes AI Mapping Suggestions that automatically propose fixes for schema mismatches.
  • Auto-Parallelization & Node-Level Backpressure: The engine automatically detects independent branches in your DAG and executes them in parallel. Built-in node-level backpressure ensures that slow sinks or transformations don't block healthy execution paths.
  • Adaptive Throughput Control: The engine automatically monitors processing latency and throttles ingestion if downstream sinks are under pressure or if worker resources are constrained.
  • Self-Healing "Safe Mode" & Anomaly Detection: Automatically detects processing anomalies (e.g., latency spikes) and can enter a "Safe Mode" during critical failures, diverting traffic to a Dead Letter Sink to preserve data and maintain system stability.
  • Stall Detection & Automatic Recovery: A wedged pipeline is detected and rebuilt without an operator. Two independent watchdogs cover the two ways a pipeline can wedge — work outstanding that never completes, and a CDC stream that stops being served (detected against the source's own wal_sender_timeout keepalive cadence). Recovery is a supervised restart with an exponential backoff, jitter, and a bounded restart budget, so a genuinely broken sink escalates to a human instead of looping. Nothing is lost: un-acknowledged changes replay from the replication slot's confirmed position.
  • Generic Protobuf Source: A universal source that can dynamically load .proto files at runtime, providing type-safe ingestion for any Protobuf-based service without recompilation.
  • Stateful Windowing: Support for Tumbling, Sliding, and Session windows in aggregation nodes, enabling complex real-time analytics and time-series processing directly in the pipeline.
  • Hot-Reloading for Scripts: Transformation logic in WASM or Lua can be updated on-the-fly. The engine detects changes and reloads the logic without requiring a workflow restart.
  • OpenTelemetry (OTLP) Native: Built-in support for exporting internal traces and metrics to standard enterprise observability stacks via the OTLP protocol.
  • Enterprise Connectivity: Native, optimized connectors for high-scale enterprise sinks like Snowflake.

Hermod works by reading data from a Source, buffering it in a high-performance buffer (in-memory RingBuffer or persistent FileBuffer), and then writing it to a Sink. This architecture allows it to handle peak loads and provide a flexible way to connect different databases to various message streams.

[Source] -> [Buffer] -> [Sink]
   ^           |          ^
   |        [Engine]      |
   +-----------+----------+

Install

The current release is v1.2.0. See CHANGELOG.md for what changed, what breaks, and the known gaps.

  • Pin an explicit version for both the image and the chart. It is what makes a deployment reproducible, and it is what the examples below do.
  • Go consumers are still unserved. v1.2.0 falls inside the retracted range [v1.0.0, v1.8.0], exactly as v1.0.0 does, so go get will not select it. See go get does not work, and is not expected to — nothing changed here, the release line simply continues below the range.
Container image
docker pull ghcr.io/gsoultan/hermod:1.2.0
docker run --rm ghcr.io/gsoultan/hermod:1.2.0 -version
# hermod v1.2.0 (Enterprise Edition)

linux/amd64 and linux/arm64 are both published under that one tag.

Helm
helm install hermod oci://ghcr.io/gsoultan/charts/hermod \
  --version 1.2.0 \
  --set existingSecret=hermod-master-key
Binaries

.deb, .rpm, .apk and tarballs for Linux amd64/arm64 are attached to the release.

From source
git clone https://github.com/gsoultan/hermod && cd hermod
go build ./cmd/hermod

Go 1.27 or later is required.

go get does not work, and is not expected to

go get github.com/gsoultan/hermod and go install github.com/gsoultan/hermod/cmd/hermod@… fail at this version. Use the image, the chart, a packaged binary, or a checkout.

Every version the Go module proxy can serve for this path is withdrawn and retracted — the range [v1.0.0, v1.8.0] plus v1.0.0-rc.1 by name. No tombstone tag carries that block any more; it lives in go.mod on main, so every release tag cut from main carries it. The proxy is immutable, so the releases deleted in this reset still answer there — and v1.0.0 in particular is permanently bound to a commit from February whose go.mod declared github.com/user/hermod, a path matching no repository. Re-tagging cannot displace it.

Nothing is lost that previously worked: the module path was a placeholder until 2026-09-02, so no published release was ever importable. Restarting the numbering at 1.0.0 was chosen over a go get path that has never functioned. It starts working once the line passes v1.7.4.


Development Quick Start

One command brings up the whole stack — Postgres, the API + worker, and the UI dev server — and completes Hermod's first-run wizard for you, so all that is left is logging in:

./scripts/dev.sh

Then open http://localhost:5175 and sign in with admin / admin.

Option Effect
./scripts/dev.sh Start everything (PostgreSQL via Apple container)
./scripts/dev.sh --sqlite Use SQLite instead — no container needed
./scripts/dev.sh --reset Wipe the dev database and re-seed from scratch
./scripts/dev.sh --build-ui Refresh the UI bundle the API serves, then start
./scripts/dev.sh --stop Stop a running stack

Work against port 5175. It runs Vite, so edits under ui/src appear in the browser immediately via hot reload — no restart needed. Port 4005 also serves a UI, but that is the pre-built bundle in internal/api/static; it will not show your edits until you run --build-ui. It exists for checking what production actually ships.

Ports are chosen, not assumed. The stack prefers 4005 (API), 50051 (gRPC) and 5175 (UI), and steps up to the next free number for any of them already taken — so a second stack, or an unrelated service, never produces address already in use. The banner prints the numbers it settled on, and ./scripts/dev.sh --print-ports shows them without starting anything. Note that 50051 is the conventional gRPC port and therefore the likeliest to collide, even when both documented ports are free; a failed bind on it takes the whole backend down.

Notes:

  • PostgreSQL is created for you. On first run the script builds a postgres-dev container with Apple's container CLI (macOS 26+), configured with wal_level=logical for CDC, and creates the hermod_metadata, hermod_test_source and hermod_test_sink databases. Nothing to set up by hand.
  • Re-running is safe: an existing container is started rather than rebuilt, its data survives container stop, and an existing admin user is kept rather than recreated.
  • Your real configuration is never touched. The dev stack writes to .dev/ inside the repo via HERMOD_CONFIG_DIR, not ~/.hermod, so it cannot overwrite another Hermod instance's setup.
  • Logs stream to the terminal and are kept in .dev/logs/{backend,ui}.log.
  • Overridable: HERMOD_DEV_ADMIN_USER, HERMOD_DEV_ADMIN_PASS, HERMOD_DEV_API_PORT, HERMOD_DEV_GRPC_PORT, HERMOD_DEV_UI_PORT, HERMOD_DEV_PG_CONTAINER, HERMOD_DEV_PG_PORT. A port set explicitly is used as given — if it is busy the run fails and names the holder, rather than silently moving to another port you did not ask for.
Managing the database container
./scripts/create-postgres.sh              # create it (no-op if it already exists)
./scripts/create-postgres.sh --recreate   # destroy and rebuild from scratch
container exec -it postgres-dev psql -U postgres
container stop postgres-dev               # data is preserved

Overrides: HERMOD_DEV_PG_IMAGE (default postgres:18-alpine), HERMOD_DEV_PG_PASSWORD.

Data lives in the container's own filesystem — it survives stop/start but not container delete. PGDATA is deliberately not bind-mounted, because PostgreSQL's strict ownership requirements do not survive the macOS→Linux filesystem boundary reliably.

Documentation

The README is the overview. Each topic has its own page under docs/:

  • Connectors: Which connectors are production-ready, and the evidence behind each tier.
  • Using Hermod: The command-line client, embedding Hermod as a Go library, running the API and workers, supported platforms, and the admin settings endpoints.
  • Workflows: Control flow, fan-out, versioning and rollback, workflows as MCP tools, blueprints, and the advanced transformation nodes.
  • Reliability and delivery guarantees: What Hermod does to avoid losing data, the delivery guarantee it makes, and sink-side idempotency.
  • Distributed transactions: Transactional sink groups: committing one batch to several sinks atomically.
  • PostgreSQL CDC: Replication slots and publications.
  • API sources and streams: GraphQL, webhook and gRPC sources, synchronous responses, and the real-time SSE stream.
  • Schema registry and data governance: Schema validation and Confluent Schema Registry interop.
  • Security: Authentication, two-factor sign-in, audit logging, security headers and CORS.
  • Encryption: Credential encryption, master key rotation, and the field-level encrypt and decrypt transformations.
  • Observability: Metrics, tracing, alerting, schema evolution, rebalancing, and the health and readiness probes.
  • Deploying and running Hermod: Production settings, Kubernetes, worker coordination, backup and shutdown.
  • Performance tuning: Tuning guide, the Postgres bulk-load fast path, and benchmarks.
  • Developing Hermod: Running the integration and UI tests, and what CI runs.
  • Data lineage and impact: The lineage graph and the downstream impact of changing a source.
  • Oracle CDC with LogMiner: Setting up log-based change capture from Oracle.
  • AI automation: The language-model nodes, the agent, retrieval, the MCP server and the workflow builder.
  • Machine learning: Training models on your data and calling them from workflows, REST and gRPC.
  • Expression functions: The functions you can call in expressions, conditions and templates.
  • Structural nodes: Nodes that reshape a record, parse text inside it, enrich it from a file, or do geometry on it.

Also at the top level: BENCHMARKS.md, RUNBOOK.md, ROADMAP.md and CHANGELOG.md.

Roadmap

For a detailed view of planned features and future development ideas, please refer to the ROADMAP.md file.

Contributing & Documentation

  • Always update the documentation when you add a new feature or change user‑visible behavior: the page under docs/ that covers the topic, and the feature list in README.md if the feature is new.
    • Include a brief description, how to enable/use it, and any relevant config flags, env vars, API endpoints, or UI locations.
    • If the UI is affected, verify the UI builds (cd ui && bun run build) or run the quick verify script on Windows: pwsh -File scripts/quick-verify.ps1.
    • Keep examples up to date. Add to the page that covers the topic; a new page is for a new topic, and gets a line in the Documentation list.

Documentation

Index

Constants

View Source
const (
	StateStoreKey        contextKey = "stateStore"
	RegistryKey          contextKey = "registry"
	WorkflowIDKey        contextKey = "workflow_id"
	NodeIDKey            contextKey = "node_id"
	LastTraceSnapshotKey contextKey = "lastTraceSnapshot"
)
View Source
const MetaOrderingKey = "_hermod_order_key"

MetaOrderingKey names the row a message changes.

It is the unit of ordering: two messages carrying the same key are delivered in the order the source produced them, and messages carrying different keys are free to run in parallel. The engine pins a key to one worker and one sink shard, so the guarantee holds end to end rather than only inside the writer.

It must identify a *row*, not a change. A CDC message's ID is its LSN, which is unique per change — hashing on that scatters every change to one row across every worker, which is the opposite of what is wanted. Sources that know their row identity set this to schema.table plus the primary-key values; the format is opaque, only equality matters.

Empty means "no order to keep" — a queue message, a cron tick, a batch row — and those are spread across workers exactly as before.

Variables

View Source
var (
	ErrNotSupported = errors.New("not supported")

	// ErrPermanent marks a sink failure another attempt cannot fix: the
	// destination refused this message for what it is — too large, addressed
	// to a device that no longer exists — not because it could not be reached.
	//
	// The retry layers stop on it, so the message goes to the dead-letter sink
	// on the first refusal instead of after the whole retry budget, and the
	// circuit breaker does not count it: a refusal about one message says
	// nothing about the sink's health. A sink reports it by wrapping it with
	// %w, or with an error type whose Is method matches it.
	ErrPermanent = errors.New("permanent failure")
)

Functions

func BuildOrderingKey added in v1.8.1

func BuildOrderingKey(schema, table string, keyValues []string) string

BuildOrderingKey composes an ordering key from a table and its key values. It returns "" when there are no key values, because a row that cannot be identified cannot be ordered against itself.

func LookupCacheKeyPrefix added in v1.8.1

func LookupCacheKeyPrefix(sourceID string) string

LookupCacheKeyPrefix is the prefix a db_lookup's cache entry carries for the source it queried. The key continues past it with the table, columns, key value and mode, so the trailing separator is load-bearing: without it, invalidating source "cust" would also drop everything cached from "customers".

It lives here because two packages need to agree on it and neither imports the other -- pkg/comm/transformer/lookup builds the key, and internal/engine/registry drops entries by it when the source is edited.

func MetadataValue added in v1.8.1

func MetadataValue(msg Message, key string) (string, bool)

MetadataValue reads one metadata entry, without copying the map when the message supports it.

Metadata() has to clone — handing out the live map would race with a concurrent SetMetadata — so reading a single key through it copies every other entry only to discard them. Almost every caller wants one key: `_outbox_id`, `_source_node_id`, `traceparent`, the delivery markers. On a real workflow at 32 columns those reads were 11% of everything the pipeline allocated.

A message that cannot do the single-key read still works, at the old cost. This lives here, beside the interface, because three packages had each grown a private copy of it and a fourth was about to.

func OrderingKey added in v1.8.1

func OrderingKey(msg Message) string

OrderingKey returns the message's ordering key, or "" when it has none.

It must not clone the metadata map to read one entry. This runs once per message on the dispatch path, and cloning there cost about a third of the engine's throughput (165k -> 108k msgs/s measured) for a single map lookup.

It used to avoid the clone by indexing MetadataRef's live map with no lock held, on the ground that the caller owns the message — taken off the buffer, not yet handed to a worker. That held for the dispatch path it was written for and not for the caller it later acquired: sinkWriter.pickShard calls this from the per-sink enqueue goroutines runner.go fans out with swg.Go, one per target and all holding the same message, while the sinks' workers write delivery markers and trace lineage back onto it through the locked SetMetadata. An unlocked read against a locked write is a race, and the runtime makes a racing map read fatal.

MetadataValue takes the read lock and still does not clone, so there is no longer a cost to trade against correctness here.

func SourceAllowsDirectQueries added in v1.8.1

func SourceAllowsDirectQueries(sourceType string, config map[string]string) bool

SourceAllowsDirectQueries reports whether a source may be the target of SQL that is not its change stream: a db_lookup's per-message query, a batch_sql's scheduled one. A database already serving logical replication should not also serve those, and where the source is a source node in a workflow as well, batching the same table delivers every row twice.

SQL Server is the documented exception -- its CDC is read back through ordinary queries against change tables, so it carries none of that cost.

func SourceUsesCDC added in v1.8.1

func SourceUsesCDC(config map[string]string) bool

SourceUsesCDC reports whether a source's configuration asks for change data capture.

The flag is opt-out: a configuration with no use_cdc key at all is a CDC source, which is how internal/factory builds one. Reading it any other way -- testing for the key's presence, say -- disagrees with what the engine actually runs, and lets CDC sources through checks written to exclude them.

Types

type BatchSink

type BatchSink interface {
	Sink
	WriteBatch(ctx context.Context, msgs []Message) error
}

BatchSink is an optional interface for sinks that support batch writes.

type Browser

type Browser interface {
	Browse(ctx context.Context, table string, limit int) ([]Message, error)
}

Browser defines an optional interface for browsing multiple records from a table.

type ColumnDiscoverer

type ColumnDiscoverer interface {
	DiscoverColumns(ctx context.Context, table string) ([]ColumnInfo, error)
}

ColumnDiscoverer defines an optional interface for discovering columns of a table.

type ColumnInfo

type ColumnInfo struct {
	Name       string `json:"name"`
	Type       string `json:"type"`
	IsNullable bool   `json:"is_nullable"`
	IsPK       bool   `json:"is_pk"`
	IsIdentity bool   `json:"is_identity"`
	Default    string `json:"default"`
}

type Consumer

type Consumer interface {
	Consume(ctx context.Context, handler Handler) error
	Close() error
}

Consumer defines the interface for receiving messages.

type Discoverer

type Discoverer interface {
	DiscoverDatabases(ctx context.Context) ([]string, error)
	DiscoverTables(ctx context.Context) ([]string, error)
}

Discoverer defines an optional interface for discovering databases and tables.

type Formatter

type Formatter interface {
	Format(msg Message) ([]byte, error)
}

Formatter defines the interface for formatting messages before they are written to a sink.

type Handler

type Handler func(ctx context.Context, msg Message) error

Handler is a function type for processing received messages.

type IdempotencyReporter

type IdempotencyReporter interface {
	// LastWriteIdempotent returns true when the latest write was treated as a
	// duplicate (no-op), and true for conflict when a key collision with a
	// differing payload was detected (when supported by the sink).
	LastWriteIdempotent() (dedup bool, conflict bool)
}

IdempotencyReporter is an optional interface for sinks to report whether the last successful Write/WriteBatch resulted in a deduplicated (skipped) write and/or a payload conflict. Engines can use this to emit standardized metrics.

type LagReporter

type LagReporter interface {
	GetLag(ctx context.Context) (uint64, error)
}

LagReporter defines an optional interface for reporting source lag.

type Loggable

type Loggable interface {
	SetLogger(Logger)
}

Loggable defines an optional interface for things that support structured logging.

type Logger

type Logger interface {
	Debug(msg string, keysAndValues ...any)
	Info(msg string, keysAndValues ...any)
	Warn(msg string, keysAndValues ...any)
	Error(msg string, keysAndValues ...any)
}

Logger defines the interface for logging in Hermod.

type Message

type Message interface {
	ID() string
	Operation() Operation
	Table() string
	Schema() string
	Before() []byte
	After() []byte
	Payload() []byte // Primary data payload
	Metadata() map[string]string
	Data() map[string]any
	MetadataRef() map[string]string
	DataRef() map[string]any
	SetMetadata(key, value string)
	SetData(key string, value any)
	Clone() Message
	ToMap() map[string]any
	ClearPayloads()
	Retain()
	Release()
}

Message represents a generic message structure. Using interface to allow different message implementations.

type NamedBegin added in v1.18.0

type NamedBegin interface {
	BeginNamed(ctx context.Context, txID string) error
}

NamedBegin is an optional interface for a TwoPhaseCommit participant that has to name its transaction when it opens it rather than when it prepares it.

MySQL XA is the case: XA START takes the identifier, and an XA transaction cannot be renamed afterwards. Without this the participant would have to invent a name at Begin and report it back from Prepare, and a crash between that Prepare returning and the coordinator recording the substitute would leave a prepared transaction nothing can name — the window the coordinator-supplied ID in Prepare exists to close.

The coordinator calls BeginNamed instead of Begin when a participant has it, and later calls Prepare with the same txID. An open transaction that has not been prepared is not in doubt — a crash rolls it back — so the name does not need to be durable yet; it is recorded before Prepare, as for every participant.

type Operation

type Operation string

Operation defines the type of CDC operation.

const (
	OpCreate   Operation = "create"
	OpUpdate   Operation = "update"
	OpDelete   Operation = "delete"
	OpSnapshot Operation = "snapshot"
)

type OutboxItem

type OutboxItem struct {
	ID         string            `json:"id"`
	WorkflowID string            `json:"workflow_id"`
	SinkID     string            `json:"sink_id"`
	Payload    []byte            `json:"payload" omitzero:"true"`
	Metadata   map[string]string `json:"metadata" omitzero:"true"`
	CreatedAt  time.Time         `json:"created_at" omitzero:"true"`
	Attempts   int               `json:"attempts"`
	LastError  string            `json:"last_error,omitempty"`
	Status     string            `json:"status"` // pending, processing, failed
}

OutboxItem represents a message persisted for reliable delivery.

type OutboxStorage

type OutboxStorage interface {
	CreateOutboxItem(ctx context.Context, item OutboxItem) error
	ListOutboxItems(ctx context.Context, status string, limit int) ([]OutboxItem, error)
	DeleteOutboxItem(ctx context.Context, id string) error
	UpdateOutboxItem(ctx context.Context, item OutboxItem) error
}

OutboxStorage defines the interface for persisting and retrieving outbox items.

type PendingWorkReporter

type PendingWorkReporter interface {
	// PendingWork reports whether the source has delivered messages that have
	// not been acknowledged yet. known is false when the source cannot tell,
	// so callers fall back to their own signals rather than concluding that
	// nothing is outstanding — a distinction wrappers need, since a decorator
	// implements this interface on behalf of sources that may not.
	PendingWork() (pending bool, known bool)
}

PendingWorkReporter is an optional interface for sources that can say precisely whether they are still owed acknowledgements.

It exists because replication lag does not answer that question. Lag measures the distance between the server's current WAL position and the position this pipeline has confirmed, and the server's position advances on every write anywhere on that instance — including databases and tables this workflow does not follow. An idle workflow attached to a busy server therefore reports lag that grows without limit and never returns to zero, which is indistinguishable from a wedge if lag is all you look at. What distinguishes them is whether the source actually handed over messages that were never acknowledged.

type Producer

type Producer interface {
	Produce(ctx context.Context, msg Message) error
	Close() error
}

Producer defines the interface for sending messages.

type PublicationInfo

type PublicationInfo struct {
	Name      string   `json:"name"`
	AllTables bool     `json:"all_tables"`
	Tables    []string `json:"tables"`
}

PublicationInfo describes an existing publication and the tables it covers.

type ReadyChecker

type ReadyChecker interface {
	IsReady(ctx context.Context) error
}

ReadyChecker is an optional interface for sources that support deep readiness checks.

type ReplicationDiscoverer

type ReplicationDiscoverer interface {
	DiscoverReplicationSlots(ctx context.Context) ([]ReplicationSlotInfo, error)
	DiscoverPublications(ctx context.Context) ([]PublicationInfo, error)
}

ReplicationDiscoverer is an optional interface for CDC sources (e.g. PostgreSQL) that can enumerate existing logical replication slots and publications so the user can choose an existing one or decide to create a new one.

type ReplicationSlotInfo

type ReplicationSlotInfo struct {
	Name     string `json:"name"`
	Plugin   string `json:"plugin"`
	SlotType string `json:"slot_type"`
	Database string `json:"database"`
	Active   bool   `json:"active"`
}

ReplicationSlotInfo describes an existing logical replication slot.

type SQLExecutor

type SQLExecutor interface {
	ExecuteSQL(ctx context.Context, query string) ([]map[string]any, error)
}

SQLExecutor is an interface for sources and sinks that can execute arbitrary SQL queries.

type Sampler

type Sampler interface {
	Sample(ctx context.Context, table string) (Message, error)
}

Sampler defines an optional interface for fetching a sample record from a table.

type Sink

type Sink interface {
	Write(ctx context.Context, msg Message) error
	Ping(ctx context.Context) error
	Close() error
}

type SinkOperationMode

type SinkOperationMode string

SinkOperationMode defines how a sink should treat incoming messages.

const (
	SinkOpModeAuto   SinkOperationMode = "auto"   // Follow message operation
	SinkOpModeInsert SinkOperationMode = "insert" // Always insert
	SinkOpModeUpsert SinkOperationMode = "upsert" // Always upsert/merge
	SinkOpModeUpdate SinkOperationMode = "update" // Always update
	SinkOpModeDelete SinkOperationMode = "delete" // Always delete
)

type Snapshottable

type Snapshottable interface {
	Snapshot(ctx context.Context, tables ...string) error
}

Snapshottable defines an optional interface for sources that support manual snapshots.

type Source

type Source interface {
	Read(ctx context.Context) (Message, error)
	Ack(ctx context.Context, msg Message) error
	Ping(ctx context.Context) error
	Close() error
}

Source defines the interface for reading data from a CDC source.

type StateStore

type StateStore interface {
	Get(ctx context.Context, key string) ([]byte, error)
	Set(ctx context.Context, key string, value []byte) error
	Delete(ctx context.Context, key string) error
}

StateStore defines an interface for persistent key-value storage used by transformers.

type Stateful

type Stateful interface {
	GetState() map[string]string
	SetState(state map[string]string)
}

Stateful defines an optional interface for sources and sinks that have persistent state.

type StreamLivenessReporter

type StreamLivenessReporter interface {
	// LastStreamActivity reports when anything was last received on the
	// stream, keepalives included. A zero time means the stream has not
	// started, which is not a fault.
	LastStreamActivity() time.Time
	// StreamSilenceThreshold is how long silence must last before it is a
	// fault. Zero disables the check — appropriate when the server is
	// configured not to send keepalives at all (wal_sender_timeout = 0).
	StreamSilenceThreshold() time.Duration
}

StreamLivenessReporter is an optional interface for sources that hold a long-lived, server-pushed stream open — a logical replication connection above all.

Such a stream is expected to deliver something at a known cadence even when there are no changes to send: PostgreSQL emits a keepalive on an idle replication connection every wal_sender_timeout/2. That makes silence measurable evidence rather than an absence of evidence, and it is the only signal that distinguishes a source which has stopped delivering from one which simply has nothing to deliver. Every other indicator the engine has — buffer depth, sink queues, processed counts — sees only work the source has already handed over, so a stream wedged upstream of them is invisible.

type StringMap

type StringMap map[string]string

StringMap is a map[string]string that tolerates JSON values of other scalar types when unmarshalling. UI clients sometimes send configuration values as booleans or numbers (e.g. {"use_cdc": true, "batch_size": 100}) rather than strings. Decoding such payloads directly into map[string]string fails with errors like "json: cannot unmarshal bool into Go struct field ... of type string". StringMap coerces those scalar values to their string form so the API stays robust regardless of how the client encodes them.

func (*StringMap) UnmarshalJSON

func (m *StringMap) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a JSON object into a StringMap, coercing scalar values (string, bool, number) to strings. Null becomes an absent/empty map and null values become empty strings. Objects and arrays are preserved as their JSON encoding.

type TraceRecorder

type TraceRecorder interface {
	RecordStep(ctx context.Context, workflowID, messageID string, step TraceStep)
}

TraceRecorder defines the interface for recording message traces.

type TraceStep

type TraceStep struct {
	NodeID    string         `json:"node_id"`
	Timestamp time.Time      `json:"timestamp" omitzero:"true"`
	Duration  time.Duration  `json:"duration" omitzero:"true"`
	Before    map[string]any `json:"before,omitempty" omitzero:"true"`
	After     map[string]any `json:"after,omitempty" omitzero:"true"`
	Error     string         `json:"error,omitempty"`
	Lineage   string         `json:"lineage,omitempty"`
}

TraceStep represents a single step in a message's journey.

type Transactional

type Transactional interface {
	Begin(ctx context.Context) error
	Commit(ctx context.Context) error
	Rollback(ctx context.Context) error
}

Transactional is an optional interface for sources and sinks that support atomic transactions.

type TwoPhaseCommit

type TwoPhaseCommit interface {
	Transactional
	// Prepare votes to commit, under a transaction ID the coordinator supplies.
	//
	// The ID is an argument rather than a return value because of what happens
	// when the process dies mid-flight. A participant that named its own
	// transaction left a window between Prepare returning and the coordinator
	// writing that name down: a crash there leaves a prepared transaction
	// nothing can name, and on PostgreSQL a prepared transaction holds its
	// locks cluster-wide until somebody finds it by hand in
	// pg_prepared_xacts. Supplying the ID lets the coordinator record it
	// *before* the transaction exists, so recovery can always name what it
	// might have created.
	//
	// The returned string is the ID actually in force. It is normally the one
	// passed in; a participant that cannot honour 2PC in its current
	// configuration may return its own sentinel instead — the PostgreSQL sink
	// does this behind a transaction pooler, where PREPARE TRANSACTION is
	// unavailable and it degrades to a local commit.
	Prepare(ctx context.Context, txID string) (string, error)
	CommitPrepared(ctx context.Context, txID string) error
	RollbackPrepared(ctx context.Context, txID string) error
}

TwoPhaseCommit is an optional interface for sinks that support 2PC.

It is driven by pkg/engine/twopc.Coordinator, which a transactional sink group (pkg/comm/sink/txgroup) uses to commit several sinks atomically. The coordinator makes its decision durable before telling any participant, so a crash mid-round is resolved on the next start rather than left in doubt.

Implement this only with genuine prepared-transaction semantics. A no-op implementation is actively harmful: the contract reports failure through the error return, so a Rollback that returns nil without rolling anything back tells a future coordinator the abort succeeded while the writes stand.

type TwoPhaseCommitPreflight

type TwoPhaseCommitPreflight interface {
	// PreflightTwoPhaseCommit returns nil only if a later Prepare would give a
	// genuinely in-doubt, resolvable transaction.
	PreflightTwoPhaseCommit(ctx context.Context) error
}

TwoPhaseCommitPreflight is an optional interface for participants that can check, before any data moves, whether real two-phase commit is actually available to them.

It exists because "implements TwoPhaseCommit" is not the same as "can honour it right now". A PostgreSQL sink behind a transaction pooler cannot use PREPARE TRANSACTION at all — the in-doubt transaction has to be resolved on the backend that created it, which a pooler cannot guarantee — and PostgreSQL ships with max_prepared_transactions = 0, which makes PREPARE fail outright.

Without this check those cases surface as a mid-batch failure at best, and at worst as a participant that quietly commits when asked to prepare: the coordinator then believes it can still roll back, and the transaction silently diverges. A participant that cannot guarantee the contract should say so here and be refused at start-up.

type VHostScoped added in v1.17.0

type VHostScoped interface {
	VHost() string
	SetVHost(vhost string)
}

VHostScoped is implemented by a message that knows which vhost's workflow it is running in. The engine sets it when the message is read; it is not part of the message's data or metadata, so a payload cannot set it. secret() uses it to answer from that vhost's own secrets.

type ValidatingSink

type ValidatingSink interface {
	Sink
	Validate(ctx context.Context, msg Message) error
}

ValidatingSink is an optional interface for sinks that support pre-write validation.

Directories

Path Synopsis
cmd
hermod command
hermod-edge command
hermodctl command
examples
eventstore command
internal
ai
aibudget
Package aibudget enforces a vhost's AI spending policy: the kill switch, the monthly token and cost budget of the vhost, and the monthly caps of single workflows.
Package aibudget enforces a vhost's AI spending policy: the kill switch, the monthly token and cost budget of the vhost, and the monthly caps of single workflows.
aibudget/transport/http
Package http is the REST surface of the vhost AI budgets: reading a vhost's budget and this month's usage, setting the budget and the per-workflow caps, the kill switch, and the two routes a worker uses to have its model calls checked and counted by the control plane.
Package http is the REST surface of the vhost AI budgets: reading a vhost's budget and this month's usage, setting the budget and the per-workflow caps, the kill switch, and the two routes a worker uses to have its model calls checked and counted by the control plane.
api
chat/transport/http
Package http is the chat trigger's transport: the endpoint a chat source receives messages on, from a web page, Slack or Telegram, and answers with the workflow's reply.
Package http is the chat trigger's transport: the endpoint a chat source receives messages on, from a web page, Slack or Telegram, and answers with the workflow's reply.
deadletter/transport/http
Package http is the REST surface of the dead letter page: listing the messages workflows could not deliver, reading one, replaying it (as it was, or edited, from the node that failed it or from the start), discarding it and exporting a selection.
Package http is the REST surface of the dead letter page: listing the messages workflows could not deliver, reading one, replaying it (as it was, or edited, from the node that failed it or from the start), discarding it and exporting a selection.
engine/registry/noderetry
Package noderetry is the per-step retry policy any workflow node can carry.
Package noderetry is the per-step retry policy any workflow node can carry.
engine/registry/nodes
Package nodes is the one import that makes every workflow node type available.
Package nodes is the one import that makes every workflow node type available.
engine/registry/nodes/ai
Package ai holds the workflow node executors that call a language model and route on its answer.
Package ai holds the workflow node executors that call a language model and route on its answer.
engine/registry/nodes/ai/agent
Package agent is the ai_agent node: a bounded tool-use loop in which a language model works towards a goal by calling the tools the node allows.
Package agent is the ai_agent node: a bounded tool-use loop in which a language model works towards a goal by calling the tools the node allows.
engine/registry/nodes/ai/agent/mcptool
Package mcptool lets the ai_agent node use one tool of a remote MCP server over the Streamable HTTP transport.
Package mcptool lets the ai_agent node use one tool of a remote MCP server over the Streamable HTTP transport.
executions
Package executions reads a workflow's run history out of its message traces.
Package executions reads a workflow's run history out of its message traces.
executions/transport/http
Package http serves a workflow's runs: running it once with a payload the user supplies, and (see executions.go) its run history.
Package http serves a workflow's runs: running it once with a payload the user supplies, and (see executions.go) its run history.
lineage
Package lineage derives a data-lineage graph from what is stored: the workflows of a vhost, the sources and sinks their nodes name, and the ML models the vhost registers.
Package lineage derives a data-lineage graph from what is stored: the workflows of a vhost, the sources and sinks their nodes name, and the ML models the vhost registers.
lineage/endpoint
Package endpoint is the lineage API's endpoints: the request and response of each route, validated, and bound to the service.
Package endpoint is the lineage API's endpoints: the request and response of each route, validated, and bound to the service.
lineage/repository
Package repository loads the stored records a lineage graph is derived from: a vhost's workflows, sources and sinks, page by page, and its ML models where the backend keeps them.
Package repository loads the stored records a lineage graph is derived from: a vhost's workflows, sources and sinks, page by page, and its ML models where the backend keeps them.
lineage/service
Package service is the lineage service: it loads one scope's stored records through a Loader, keeps only what the caller may see, and runs the lineage use cases on them.
Package service is the lineage service: it loads one scope's stored records through a Loader, keeps only what the caller may see, and runs the lineage use cases on them.
lineage/transport/http
Package http is the REST surface of data lineage: a vhost's lineage graph, and everything downstream of one of its assets or workflows.
Package http is the REST surface of data lineage: a vhost's lineage graph, and everything downstream of one of its assets or workflows.
mcpserver
Package mcpserver exposes Hermod workflows to Model Context Protocol clients: an MCP client (Claude, ChatGPT, an IDE agent) can list the workflows a user may see, read their status, and run the ones their owner has opted in.
Package mcpserver exposes Hermod workflows to Model Context Protocol clients: an MCP client (Claude, ChatGPT, an IDE agent) can list the workflows a user may see, read their status, and run the ones their owner has opted in.
mcpserver/transport/http
Package http mounts the MCP server on Hermod's HTTP API.
Package http mounts the MCP server on Hermod's HTTP API.
ml
Package ml is Hermod's model registry and inference path: the models a vhost can call, and the one Predict every caller uses — the Predict node, the REST endpoints and the gRPC InferenceService.
Package ml is Hermod's model registry and inference path: the models a vhost can call, and the one Predict every caller uses — the Predict node, the REST endpoints and the gRPC InferenceService.
ml/monitor
Package monitor watches what a vhost's models are asked and answer.
Package monitor watches what a vhost's models are asked and answer.
ml/transport/grpc
Package grpc serves hermod.ml.v1 InferenceService: the gRPC twin of POST /api/ml/serve/{vhost}/{name}.
Package grpc serves hermod.ml.v1 InferenceService: the gRPC twin of POST /api/ml/serve/{vhost}/{name}.
ml/transport/http
Package http is the REST surface of the model registry: managing a vhost's models, calling one from the UI, and the serving endpoint applications call with a model's serving key.
Package http is the REST surface of the model registry: managing a vhost's models, calling one from the UI, and the serving endpoint applications call with a model's serving key.
runtimetune
Package runtimetune applies process-wide runtime tuning (soft memory limit and GC behaviour) so Hermod stays within its lightweight, low-memory target and returns unused memory to the operating system promptly.
Package runtimetune applies process-wide runtime tuning (soft memory limit and GC behaviour) so Hermod stays within its lightweight, low-memory target and returns unused memory to the operating system promptly.
selfheal
Package selfheal keeps the self-correction gate's fixes as proposals that change a workflow only when a person approves them.
Package selfheal keeps the self-correction gate's fixes as proposals that change a workflow only when a person approves them.
selfheal/transport/http
Package http serves the self-healing proposals of a workflow.
Package http serves the self-healing proposals of a workflow.
sse
storage/configsecrets
Package configsecrets decides which connector configuration values are credentials, and encrypts them at rest.
Package configsecrets decides which connector configuration values are credentials, and encrypts them at rest.
storage/mlblobtest
Package mlblobtest is the contract every storage.MLBlobStore keeps, run against each backend by its own tests.
Package mlblobtest is the contract every storage.MLBlobStore keeps, run against each backend by its own tests.
workflow/builder
Package builder drafts a workflow from a plain-language description with a language model.
Package builder drafts a workflow from a plain-language description with a language model.
workflow/builder/transport/http
Package http serves the natural-language workflow builder.
Package http serves the natural-language workflow builder.
workflow/redact
Package redact removes credentials from a workflow before it leaves the instance it runs on.
Package redact removes credentials from a workflow before it leaves the instance it runs on.
ml
bench/gomlp command
Command gomlp measures the built-in engine's neural network (learn's mlp) on a generated tabular dataset: peak memory while it trains, CPU time, wall time, the saved model's size, scoring latency and throughput, and its quality against always answering the most common class.
Command gomlp measures the built-in engine's neural network (learn's mlp) on a generated tabular dataset: peak memory while it trains, CPU time, wall time, the saved model's size, scoring latency and throughput, and its quality against always answering the most common class.
pkg
comm/conformance
Package conformance provides a shared contract suite that every Hermod source and sink must satisfy.
Package conformance provides a shared contract suite that every Hermod source and sink must satisfy.
comm/formatter/schemaregistry
Package schemaregistry formats a Hermod message as a Confluent-framed record, so that a topic Hermod writes can be read by the consumers an existing Kafka estate already runs.
Package schemaregistry formats a Hermod message as a Confluent-framed record, so that a topic Hermod writes can be read by the consumers an existing Kafka estate already runs.
comm/reply
Package reply carries what a workflow did with a message back to a caller that is waiting for it.
Package reply carries what a workflow did with a message back to a caller that is waiting for it.
comm/sink/fcm
Package fcm sends messages to devices, topics and conditions through Firebase Cloud Messaging.
Package fcm sends messages to devices, topics and conditions through Firebase Cloud Messaging.
comm/sink/metis
Package metis drives a BPMN 2.0 workflow engine from a Hermod pipeline.
Package metis drives a BPMN 2.0 workflow engine from a Hermod pipeline.
comm/sink/mldataset
Package mldataset is the Collect Dataset sink: each message a workflow sends it becomes a row of a dataset on Hermod's hermod-ml worker, so the next training of a model on that dataset learns from it.
Package mldataset is the Collect Dataset sink: each message a workflow sends it becomes a row of a dataset on Hermod's hermod-ml worker, so the next training of a model on that dataset learns from it.
comm/sink/panmail
Package panmail sends each message as an email through a panmail gateway.
Package panmail sends each message as an email through a panmail gateway.
comm/sink/txgroup
Package txgroup writes to several sinks inside one distributed transaction.
Package txgroup writes to several sinks inside one distributed transaction.
comm/source/metis
Package metis reads a BPMN 2.0 workflow engine into a Hermod pipeline.
Package metis reads a BPMN 2.0 workflow engine into a Hermod pipeline.
comm/transformer/features
Package features holds the feature-engineering transformers: the steps that turn a record into the numbers a model takes, the same way at training and at inference.
Package features holds the feature-engineering transformers: the steps that turn a record into the numbers a model takes, the same way at training and at inference.
comm/transformer/genai
Package genai holds the workflow nodes that call a large language model: ai_prompt, ai_extract and ai_embed.
Package genai holds the workflow nodes that call a large language model: ai_prompt, ai_extract and ai_embed.
comm/transformer/genai/memory
Package memory gives an AI node a memory of the conversation a message belongs to: the earlier exchanges of the same chat, loaded before the model is called and extended after it answers.
Package memory gives an AI node a memory of the conversation a message belongs to: the earlier exchanges of the same chat, loaded before the model is called and extended after it answers.
comm/transformer/genai/retrieve
Package retrieve is the ai_retrieve (RAG) transformer: it embeds a query with the node's AI connection, asks a vector store for the nearest documents, and writes them with their scores into the message, ready for a following ai_prompt node (or an ai_agent tool).
Package retrieve is the ai_retrieve (RAG) transformer: it embeds a query with the node's AI connection, asks a vector store for the nearest documents, and writes them with their scores into the message, ready for a following ai_prompt node (or an ai_agent tool).
comm/transformer/geo
Package geo holds the geometry transformer: the distance between two points on a record, and whether a point lies in a polygon.
Package geo holds the geometry transformer: the distance between two points on a record, and whether a point lies in a polygon.
comm/transformer/ml
Package ml holds the machine-learning transformers.
Package ml holds the machine-learning transformers.
comm/transformer/structure
Package structure holds the transformers that change the shape of a record rather than its values: flatten and unflatten, parse_field, template_render and field_diff.
Package structure holds the transformers that change the shape of a record rather than its values: flatten and unflatten, parse_field, template_render and field_diff.
engine/telemetry/aimetrics
Package aimetrics turns the record of every language model call an AI node makes into the Prometheus metrics in pkg/engine/telemetry.
Package aimetrics turns the record of every language model call an AI node makes into the Prometheus metrics in pkg/engine/telemetry.
engine/twopc
Package twopc coordinates two-phase commit across sinks.
Package twopc coordinates two-phase commit across sinks.
infra/ackwatermark
Package ackwatermark turns out-of-order acknowledgements into a cursor that is safe to persist.
Package ackwatermark turns out-of-order acknowledgements into a cursor that is safe to persist.
infra/avrodecode
Package avrodecode decodes Avro binary records into map[string]any.
Package avrodecode decodes Avro binary records into map[string]any.
infra/pgxutil
Package pgxutil centralizes pgx connection configuration so that every Postgres connection (source metadata, sink pool, pgvector pool, state store) is built consistently and is safe to use behind a transaction/statement pooling proxy such as PgBouncer.
Package pgxutil centralizes pgx connection configuration so that every Postgres connection (source metadata, sink pool, pgvector pool, state store) is built consistently and is safe to use behind a transaction/statement pooling proxy such as PgBouncer.
infra/schemaregistry
Package schemaregistry speaks the Confluent Schema Registry wire format and REST API.
Package schemaregistry speaks the Confluent Schema Registry wire format and REST API.
infra/sqlident
Package sqlident validates and quotes SQL identifiers.
Package sqlident validates and quotes SQL identifiers.
infra/tracing
Package tracing carries trace context on a message rather than in a Go context.
Package tracing carries trace context on a message rather than in a Go context.
llm
Package llm is Hermod's one interface to large language model providers.
Package llm is Hermod's one interface to large language model providers.
llm/anthropic
Package anthropic is the llm adapter for Claude, through the official Anthropic Go SDK.
Package anthropic is the llm adapter for Claude, through the official Anthropic Go SDK.
llm/connect
Package connect builds a ready-to-use llm.Provider from a connection description: the adapter for its kind, plus retries, a concurrency limit and an optional fallback chain.
Package connect builds a ready-to-use llm.Provider from a connection description: the adapter for its kind, plus retries, a concurrency limit and an optional fallback chain.
llm/gemini
Package gemini is the llm adapter for Google's Gemini API (generativelanguage.googleapis.com, v1beta generateContent).
Package gemini is the llm adapter for Google's Gemini API (generativelanguage.googleapis.com, v1beta generateContent).
llm/openai
Package openai is the llm adapter for the OpenAI Chat Completions wire format.
Package openai is the llm adapter for the OpenAI Chat Completions wire format.
ml/builtin
Package builtin is Hermod's built-in ML engine: datasets, training, versions and predictions in Hermod's own process, in pure Go, with no hermod-ml worker to run.
Package builtin is Hermod's built-in ML engine: datasets, training, versions and predictions in Hermod's own process, in pure Go, with no hermod-ml worker to run.
ml/builtin/learn
Package learn holds the built-in ML engine's algorithms, in pure Go: ridge and logistic regression, a random forest, gradient-boosted trees and a small multilayer perceptron, over the same preprocessing the hermod-ml worker applies (ml/worker/hermod_ml/training.py build_pipeline): a missing number gets the training median, numbers are standardised for the linear model and the network, and text is one-hot encoded.
Package learn holds the built-in ML engine's algorithms, in pure Go: ridge and logistic regression, a random forest, gradient-boosted trees and a small multilayer perceptron, over the same preprocessing the hermod-ml worker applies (ml/worker/hermod_ml/training.py build_pipeline): a missing number gets the training median, numbers are standardised for the linear model and the network, and text is one-hot encoded.
ml/builtin/table
Package table is the built-in ML engine's dataset in memory: typed columns, read from JSON rows, CSV or Excel, with the hermod-ml worker's typing rules (ml/worker/hermod_ml/datasets.py), so the same file gives the same columns on either engine.
Package table is the built-in ML engine's dataset in memory: typed columns, read from JSON rows, CSV or Excel, with the hermod-ml worker's typing rules (ml/worker/hermod_ml/datasets.py), so the same file gives the same columns on either engine.
ml/inference
Package inference calls a model server and turns its answer back into rows.
Package inference calls a model server and turns its answer back into rows.
ml/onnxscore
Package onnxscore evaluates small tabular ONNX models in pure Go: the linear models and tree ensembles scikit-learn and XGBoost export through skl2onnx and onnxmltools, with the preprocessing glue those exports carry.
Package onnxscore evaluates small tabular ONNX models in pure Go: the linear models and tree ensembles scikit-learn and XGBoost export through skl2onnx and onnxmltools, with the preprocessing glue those exports carry.
ml/profile
Package profile describes a dataset for someone deciding what to do with it: per column its kind, how much is missing, how many different values it has, and its numbers, top values or dates with a small chart; the data's problems in plain words, each with how to fix it; and the numeric columns that move together.
Package profile describes a dataset for someone deciding what to do with it: per column its kind, how much is missing, how many different values it has, and its numbers, top values or dates with a small chart; the data's problems in plain words, each with how to fix it; and the numeric columns that move together.
ml/recommend
Package recommend serves the recommendation models the hermod-ml worker trains (ml/worker/hermod_ml/recommend.py) in-process: it parses a version's artifact once and answers "which items next for this person" and "which items are like this one" from it, without a call to the worker.
Package recommend serves the recommendation models the hermod-ml worker trains (ml/worker/hermod_ml/recommend.py) in-process: it parses a version's artifact once and answers "which items next for this person" and "which items are like this one" from it, without a call to the worker.
ml/worker
Package worker is Hermod's client for its hermod-ml worker: the Python process that holds datasets, trains models on them, exports each one to ONNX and serves it over the Open Inference Protocol.
Package worker is Hermod's client for its hermod-ml worker: the Python process that holds datasets, trains models on them, exports each one to ONNX and serves it over the Open Inference Protocol.
scripts
atomic_wipe command
audit_conversion command
Command audit_conversion reports the data_conversion nodes whose behaviour changed when an unresolvable field stopped being a silent no-op.
Command audit_conversion reports the data_conversion nodes whose behaviour changed when an unresolvable field stopped being a silent no-op.
drop_all_slots command
drop_slot command
flow command
insert command
lsn command
pub command
replica command
seed command
setup_test_db command
slots command

Jump to

Keyboard shortcuts

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