Hub
hivecfm-hub is the Go service that sits next to the Next.jsNext.jsReact framework used by HiveCFM Core. Handles routing, server rendering, and API routes in one bundle. app and does the work that is either CPU-bound, long-running, or concurrency-heavy. Think embeddings, sentiment classification, webhook dispatch, and semantic search. It is a separate process, deployed as its own container, but it shares the same Postgres instance with core.
Why Go?
The web app is already TypeScriptTypeScriptJavaScript with a static type system. Every HiveCFM Node service, the frontend, and the dev hub are written in it. — why a second language?
- Predictable concurrency. Go’s goroutines + channels give us a worker pool model that is hard to match in Node’s event loop. When the Hub processes 500 embedding jobs in parallel, it does so cheaply.
- Low memory per request. A Go binary with a worker pool holds state in kilobytes, not the hundreds of megabytes a Node worker can balloon to under load.
- Fewer surprises under GC pressure. Go’s GC is tuned for short-lived allocations. Embedding batches and HTTP retry loops are a natural fit.
The split is not religious: trivial APIs stay in TypeScriptTypeScriptJavaScript with a static type system. Every HiveCFM Node service, the frontend, and the dev hub are written in it.; anything that looks like a background worker pool lives in the Hub.
Ports
- 8090 — primary HTTP API (semantic search, webhooks, admin).
- 8080 — optional secondary listener for internal-only management endpoints in some deploys; if your deploy does not set
INTERNAL_PORT, 8090 is the only port you need.
Both bind to 127.0.0.1 in Docker ComposeDocker ComposeThe YAML file and CLI that spin up Postgres, Redis, MinIO, MailHog locally with one command.; production puts them behind the same ingress as core.
Layout
hivecfm-hub/
├── cmd/
│ ├── api/ # HTTP + worker entrypoint (main.go)
│ ├── backfill-embeddings/ # One-shot: embed historical rows
│ └── backfill-sentiment/ # One-shot: classify historical rows
├── internal/
│ ├── config/ # Env loading
│ ├── models/ # Domain types
│ ├── observability/ # OTel metrics + tracing
│ ├── repository/ # Postgres access (pgx)
│ ├── service/ # Business logic (SearchService, WebhooksService, ...)
│ └── workers/ # River job workers
├── pkg/
│ └── database/ # Shared Postgres pool helpers
├── migrations/ # goose-managed SQL migrations
├── openapi.yaml # API contract, mirrored into the dev hub
└── compose.yml # Standalone docker-compose for Hub-only devEntry point
hivecfm-hub/cmd/api/main.go is the binary’s main. In one page it:
- Loads config from env (
internal/config). - Sets up
slogstructured logging. - Opens a pgx pool —
database.NewPostgresPool(...)— withpgvectortype registration on connect. - Runs migrations in-process (
runMigrations— both the goose-managed Hub migrations and the River queue’s own tables). - Constructs the
App(wires services, workers, HTTP routes). - Calls
app.Run(ctx)which starts the River worker pool and the HTTP server concurrently. - On SIGINT/SIGTERM,
app.Shutdown(ctx)drains jobs and closes the pool.
A typical Go service shape: no frameworks, no reflection-based DI, explicit wiring.
The River queue, not Kafka. HiveCFM’s async backbone is RiverRiverThe Go background-job queue Hub uses. Jobs are rows in Postgres, so there is no separate broker to run., which stores jobs in Postgres tables. There is no message broker. See Workers.
What the Hub owns
- Async jobs. Feedback embedding, sentiment analysis, webhook dispatch — see Workers.
- Semantic search. The
SearchServiceininternal/service/search_service.goexposes nearest-neighbour lookup with cursor pagination. See Search. - Webhook outbox. Storing, retrying, and signing outgoing webhooks.
- Backfills. One-shot CLIs under
cmd/backfill-*to re-process historical data.
It does not own user-facing pages, auth, billing, or anything transactional. Writes that must be synchronous-from-the-user’s-POV stay in hivecfm-core.
Calling the Hub from core
The web app calls the HubHubThe Go service that owns background processing, integrations, and the admin API. Sibling to Core. over HTTP for synchronous reads (e.g. /search for a UI that wants results immediately). For async work, core enqueues a RiverRiverThe Go background-job queue Hub uses. Jobs are rows in Postgres, so there is no separate broker to run. job by inserting a row into the shared river_job table — the Hub picks it up on its nextNext.jsReact framework used by HiveCFM Core. Handles routing, server rendering, and API routes in one bundle. poll. There is no RPC framework.
Migration story
- Core’s migrations live in
hivecfm-core/packages/database/migrations/(managed by Prisma). - Hub’s migrations live in
hivecfm-hub/migrations/(managed by goose). - Both run against the same Postgres instance, but each owns a disjoint set of tables. Core does not
ALTERHub tables and vice versa.
Read next
- Workers — the River job model and each worker’s purpose.
- Search — how semantic search is implemented end-to-end.
- Database / pgvector and Search — the embedding storage layer.