An open-source Experience Management (XM) database service. Hub is a headless API service that unifies customer feedback from multiple sources into a single, normalized data model optimized for analytics and AI-powered insights.
Hub is a specialized Experience Management database that:
- Connects all feedback sources (surveys, reviews, support tickets, social media, etc.)
- Auto-applies taxonomy and mapping with AI-powered context & enrichment
- Provides a unified data model optimized for human and AI analytics
- Sends all events via webhooks (Standard Webhooks specification)
- Headless/API-only design - UI lives in XM Suite (Hub is the backend)
Key Design Principles:
- Performance First: Optimized for high-throughput data ingestion and retrieval
- Data Processing: Focus on query performance, indexing strategies, and analytics capabilities
- Simple Architecture: Barebones implementation with raw
net/httpandpgxfor maintainability - Headless Design: API-only service (no UI)
- Webhook-Only Communication: Events sent via Standard Webhooks (no workflow engine)
- Enterprise Scale: Designed to handle hundreds of millions of records per day
- ✅ RESTful API for feedback record CRUD operations
- ✅ PostgreSQL for data persistence with optimized schema
- ✅ API Key Authentication via environment variable
- ✅ Clean Architecture with repository, service, and handler layers
- ✅ Docker Compose for local development
- ✅ Database Schema initialization
- ✅ OpenAPI spec (
openapi.yaml) and contract tests (Schemathesis) - ✅ Health Check endpoints
- Language: Go 1.25.7
- Database: PostgreSQL 16
- Driver: pgx/v5
- HTTP: Standard library
net/http(barebones approach) - Documentation: OpenAPI spec in repo, contract tests (Schemathesis)
- License: Apache 2.0
.
├── cmd/
│ └── api/ # API server entrypoint
├── internal/
│ ├── api/
│ │ ├── handlers/ # HTTP request handlers
│ │ └── middleware/ # HTTP middleware (auth, CORS, logging)
│ ├── service/ # Business logic layer
│ ├── repository/ # Data access layer
│ ├── models/ # Domain models and DTOs
│ └── config/ # Configuration management
├── pkg/
│ └── database/ # Database utilities and connection pooling
├── migrations/ # SQL migrations (goose)
├── tests/ # Integration tests
└── docs/ # API and product documentation
- Go 1.25.7 or higher — must be installed and available on your
PATH. Verify withgo version. Ifgois not found, see Adding Go to PATH below. - Docker and Docker Compose
- Make (optional, for convenience)
-
Clone the repository and navigate to the project directory
-
Set up the development environment:
make dev-setupThis will:
- Start PostgreSQL container
- Install Go dependencies
- Install development tools (golangci-lint, govulncheck, goose)
- Run database migrations (goose)
- Install git hooks for code quality
- Start the API server:
make runThe server will start on http://localhost:8080
If you prefer not to use Make:
- Start Docker containers:
docker compose up -d- Copy environment variables:
cp .env.example .env- Initialize database schema:
make init-db- Set your API key:
export API_KEY=your-secret-key-hereOr add it to your .env file:
API_KEY=your-secret-key-here
- Start the server:
go run ./cmd/api/main.goGET /health- Health check endpoint
The OpenAPI 3.1 spec lives in openapi.yaml at the repo root and is used by Schemathesis for API contract tests (make schemathesis). Serving the spec (e.g. GET /openapi.json or Swagger UI) is planned; see todo.md § OpenAPI.
POST /v1/feedback-records
Authorization: Bearer <api-key>
Content-Type: application/json
{
"source_type": "survey",
"source_id": "survey-123",
"source_name": "Customer Feedback Survey",
"field_id": "question-1",
"field_label": "How satisfied are you?",
"field_type": "rating",
"value_number": 5,
"metadata": {"campaign": "summer-2025"},
"language": "en",
"user_identifier": "user-abc"
}GET /v1/feedback-records/{id}
Authorization: Bearer <api-key>GET /v1/feedback-records?source_type=survey&limit=50&offset=0
Authorization: Bearer <api-key>Query parameters:
source_type- Filter by source typesource_id- Filter by source IDfield_id- Filter by field IDuser_identifier- Filter by user identifierlimit- Number of results (default: 100, max: 1000)offset- Pagination offset
PATCH /v1/feedback-records/{id}
Authorization: Bearer <api-key>
Content-Type: application/json
{
"value_number": 4,
"metadata": {"updated": true}
}DELETE /v1/feedback-records/{id}
Authorization: Bearer <api-key>make help # Show all available commands
make dev-setup # Set up development environment (docker, deps, tools, schema)
make build # Build all binaries
make run # Run the API server
make tests # Run all tests
make init-db # Run migrations (goose up)
make migrate-status # Show migration status
make migrate-validate # Validate migration files (no DB)
make docker-up # Start Docker containers
make docker-down # Stop Docker containers
make clean # Clean build artifactsmake test-unit # Fast unit tests (no database)
make tests # Integration tests (requires database)
make test-all # Run all testsgo: command not foundormakefails with "go not found" — Go is not on yourPATH. Install Go from go.dev/doc/install, then add it to your PATH using the platform steps in Adding Go to PATH below. Restart the terminal (or re-source your profile) after changing PATH.- Docker/Postgres port already in use (e.g.
5432oraddress already in use) — SetPOSTGRES_PORTin.envto a free port (e.g.5433), and set the same port inDATABASE_URL(e.g.postgres://postgres:postgres@localhost:5433/test_db?sslmode=disable). Then runmake docker-upagain.
After installing Go, your shell must be able to find the go binary. Restart the terminal (or re-source your profile) after editing PATH.
macOS / Linux (bash or zsh)
Add this line to ~/.bashrc or ~/.zshrc (create the file if it doesn’t exist). It adds the Go toolchain (go) and the directory where go install puts binaries (e.g. goose, river) used by the Makefile.
export PATH="$PATH:/usr/local/go/bin:$HOME/go/bin"If you installed Go via the Go version manager (e.g. under $HOME/sdk/go1.21/bin), use that directory instead of /usr/local/go/bin. To check that Go is on your PATH, run which go — it should print the path to the go binary.
The repository includes pre-commit hooks for code quality. To install them:
make install-hooksThe pre-commit hook automatically:
- Validates migration files when
migrations/is changed - Runs
golangci-lint run --fix(format and fixable issues), then the linter (includes gosec for secrets in Go code)
If any check fails, the commit is blocked.
Note: Git hooks are automatically installed when you run make dev-setup.
To bypass hooks temporarily (use sparingly):
git commit --no-verify -m "WIP: work in progress"Schema is managed with goose. Migrations live in migrations/.
- Apply migrations:
make init-db(runsgoose up; requiresDATABASE_URLand goose on PATH — install viamake install-tools). - Check status:
make migrate-status. - Validate files (no DB):
make migrate-validate(use in CI before applying).
In production, run migrations as a separate deploy step before deploying new app code (not on app startup). To add a new migration, create a file in migrations/ with the next version prefix (e.g. 002_add_foo.sql) and add -- +goose up and optionally -- +goose down sections; one logical change per file.
Authentication is done via a single API key set in the API_KEY environment variable. The API key is validated against this environment variable for all protected endpoints.
Set your API key:
export API_KEY=your-secret-key-hereOr add it to your .env file:
API_KEY=your-secret-key-here
All protected endpoints require the Authorization: Bearer <key> header with the API key matching the API_KEY environment variable.
Important: The API_KEY environment variable is required. The server will fail to start if API_KEY is not set.
Copy .env.example to .env and set values as needed. Reference:
| Variable | Required | Default | Description |
|---|---|---|---|
API_KEY |
Yes | — | API key for authenticating requests to protected endpoints. Server will not start without it. |
DATABASE_URL |
No | postgres://postgres:postgres@localhost:5432/test_db?sslmode=disable |
PostgreSQL connection string. Format: postgres://user:password@host:port/database?sslmode=.... When using Docker Compose, the host port in this URL must match POSTGRES_PORT. |
POSTGRES_PORT |
No | 5432 |
Host port used by Docker Compose for the Postgres container (host:container = POSTGRES_PORT:5432). Set this (e.g. 5433) only if 5432 is already in use; set the same port in DATABASE_URL. |
PORT |
No | 8080 |
HTTP server listen port. |
LOG_LEVEL |
No | info |
Log level: debug, info, warn, or error. |
curl -X POST http://localhost:8080/v1/feedback-records \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"source_type": "form",
"field_id": "email",
"field_type": "text",
"value_text": "user@example.com"
}'curl http://localhost:8080/v1/feedback-records \
-H "Authorization: Bearer <your-api-key>"curl -X PATCH http://localhost:8080/v1/feedback-records/{id} \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"metadata": {"verified": true}
}'curl -X DELETE http://localhost:8080/v1/feedback-records/{id} \
-H "Authorization: Bearer <your-api-key>"The application follows a clean architecture pattern:
- Handlers - Handle HTTP requests and responses
- Service - Contain business logic and validation
- Repository - Handle data access and queries
- Models - Define domain entities and DTOs
This separation allows for:
- Easy testing and mocking
- Clear separation of concerns
- Simple maintenance and refactoring
- Barebones HTTP: Using raw
net/httpinstead of frameworks for simplicity and maintainability - Raw SQL: Using
pgxwith raw SQL queries instead of ORMs for performance and control - Headless Design: API-only service - no UI components
- Webhook Communication: Events sent via Standard Webhooks (no workflow engine)
- Performance First: Optimized for high-throughput data processing and analytics
This is an open-source project. Contributions are welcome! Please ensure:
- Code follows Go best practices
- Tests are included for new features
- Documentation is updated
- Changes align with the architecture principles
Apache 2.0
- OpenAPI spec - API contract (used by Schemathesis); serving it from the server is planned (see todo.md).