Thanks for your interest in contributing! This document covers how we work, what we expect from PRs, and how to get started.
- Issues — Check GitHub Issues for open bugs, feature requests, and
good first issuelabels. - Roadmap — The project roadmap lives in GitHub Projects. Pick items from the current milestone.
- AGENTS.md — If you're an AI coding agent (Copilot, Cursor, etc.), read
AGENTS.mdfor repo layout, build instructions, and common task recipes. - Documentation — Architecture and guides live in
docs/:sympozium-design.md— Full architecture and CRD schemaswriting-tools.md— How to add agent toolswriting-skills.md— How to create SkillPack CRDswriting-integration-tests.md— Integration test patterns
See AGENTS.md for the full setup guide. The short version:
# Prerequisites: Go 1.25+, Docker, Kind, kubectl
kind create cluster --name kind
make install # Install CRDs
make docker-build TAG=v0.1.0 # Build all images
# Load images into Kind (see AGENTS.md for the full loop)
kubectl apply -k config/ # Deploy control planeFor day-to-day development you can run the controller and API server as local processes against a remote (or local) cluster. This skips the Docker build → image load → rollout cycle entirely, which is especially helpful on low-bandwidth connections or when iterating quickly on controller logic.
All you need is a running cluster with CRDs installed and a valid kubeconfig.
make dev-allThis starts four services in parallel:
| Service | Address | What it does |
|---|---|---|
| Controller | :9090 (metrics), :9091 (health) |
All CRD reconcilers running locally |
| API server | :8080 |
REST API for the UI |
| Vite dev server | :5173 |
Frontend with hot-reload |
| NATS port-forward | :4222 |
Forwards cluster NATS to localhost |
The in-cluster controller deployment is automatically scaled to zero so there's no conflict. When you Ctrl+C, the in-cluster controller is restored to 1 replica.
Open http://localhost:5173 in your browser. The API token is dev-token (override with SYMPOZIUM_TOKEN).
If you're only working on controller logic and already have make dev running for the UI:
make run-controllerThis builds and runs the controller manager locally, scaling down the in-cluster one. On exit it restores the in-cluster deployment.
If you're only working on the API or frontend and want the in-cluster controller to keep running:
make devTo stop all in-cluster Sympozium deployments while keeping CRDs and their instances intact:
kubectl scale deploy -n sympozium-system --replicas=0 --allThis is useful when you want the local processes to be the only thing running, or when switching between local and cluster-based development.
Every push and PR runs the following checks via GitHub Actions (.github/workflows/build.yaml):
| Check | What it does |
|---|---|
| go vet | Static analysis for common Go mistakes |
| go test -race -short | Unit tests with the race detector enabled |
| Docker build | All 10 component images build successfully |
PRs must pass all checks before merging. On merge to main, images are automatically built and pushed to ghcr.io/sympozium-ai/sympozium/.
Run checks locally before pushing:
make vet # go vet ./...
make test # go test -race ./...
make build # compile all binaries
go build ./... # quick compile checkThe repo ships a pre-commit hook at .githooks/pre-commit that:
- verifies all Go files are
gofmt-formatted, and - if any
api/type files are staged, runsmake generateand fails the commit if CRDs/deepcopy are out of sync.
Enable it once per clone:
make setup-hooksThis catches codegen drift locally — without it, the drift only surfaces in the Codegen & Helm sync CI job after push.
The following CI jobs should be configured as required status checks on the main branch in GitHub branch protection, so PRs can't merge while red:
VetTestBuildCodegen & Helm syncFormat
Sympozium supports linux/amd64 and linux/arm64 (darwin for the CLI).
- Docker images are built with Docker Buildx +
docker/build-push-action@v6with GitHub Actions cache (type=gha). - CLI releases are cross-compiled for
linux/amd64,linux/arm64,darwin/amd64, anddarwin/arm64via the release workflow (.github/workflows/release.yaml). - All Go binaries are built with
CGO_ENABLED=0for static linking.
When adding a new image, ensure its Dockerfile works on both amd64 and arm64. Use multi-stage builds from the existing Dockerfiles as a template.
We use Conventional Commits for all commit messages. This keeps the history readable and enables automated changelog generation.
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
| Type | When to use |
|---|---|
feat |
A new feature (feat: add schedule_task tool) |
fix |
A bug fix (fix: deduplicate fsnotify events in IPC bridge) |
docs |
Documentation only (docs: add Telegram setup instructions) |
chore |
Maintenance, deps, CI (chore: bump controller-gen to v0.17.2) |
refactor |
Code change that neither fixes a bug nor adds a feature |
test |
Adding or updating tests (test: add write-file integration test) |
ci |
CI/CD changes (ci: add multi-arch Docker build) |
Use the component name: agent-runner, controller, ipc-bridge, webhook, tui, slack, telegram, crd, etc.
feat(agent-runner): add fetch_url tool
fix(ipc-bridge): deduplicate fsnotify Create+Write events
docs: add CONTRIBUTING.md
test(integration): add k8s-ops-nodes test
ci: add arm64 to Docker build matrix
chore(deps): bump gorilla/websocket to v1.5.1
Sympozium follows Semantic Versioning (vMAJOR.MINOR.PATCH):
- PATCH (
v0.1.0→v0.1.1) — Bug fixes, docs, minor improvements - MINOR (
v0.1.0→v0.2.0) — New features, new CRD fields (backward-compatible) - MAJOR (
v1.0.0) — Breaking API/CRD changes
- Ensure all changes are committed and pushed to
main. - Create and push a tag:
git tag v0.1.1 git push origin v0.1.1
- The release workflow automatically:
- Builds CLI binaries for all platforms
- Packages install manifests
- Builds and pushes all Docker images tagged with the version
- Creates a GitHub Release with assets
While in v0.x.x, the API is not yet stable and breaking changes may occur in minor versions.
- One concern per PR — Don't mix a feature, a bug fix, and a refactor in one PR.
- Conventional commit title — The PR title becomes the merge commit message.
- Tests required — Add or update unit tests. For new tools or major features, add an integration test in
test/integration/. - CRD changes — If you modify types in
api/v1alpha1/, runmake generateand commit the generated files. - Docs — Update relevant docs in
docs/if behavior changes. - Compile check — Run
go build ./...before pushing.
- Go — Follow standard Go conventions. Run
make fmtandmake vet. - Error handling — Return errors, don't panic. Use
fmt.Errorf("context: %w", err)for wrapping. - Logging — Use the structured logger (
log.Info,log.Error) with key-value pairs, notfmt.Printf. - Naming — CRD types use PascalCase (
SympoziumInstance). Tool names use snake_case (execute_command). NATS topics use dot-separated (agent.run.completed). - IPC protocol — New IPC-based tools must follow the JSON file drop pattern: write to
/ipc/<dir>/, bridge watches with fsnotify, publishes to NATS.
| Pattern | Convention |
|---|---|
| CRD types | api/v1alpha1/<name>_types.go |
| Reconcilers | internal/controller/<name>_controller.go |
| Routers (NATS → K8s) | internal/controller/<name>_router.go |
| Agent tools | All in cmd/agent-runner/tools.go |
| Channel pods | channels/<name>/main.go |
| Dockerfiles | images/<name>/Dockerfile |
| Integration tests | test/integration/test-<name>.sh |
| Sample CRs | config/samples/<name>_sample.yaml |
- Open an issue for questions or discussion
- Check existing docs in
docs/before asking - Look at recent PRs for examples of good contributions