This guide covers contributing to Kumoss itself — running the repository’s own test suites, checks, and tooling. It does not cover deploying or operating Kumoss; for that, see Quickstart and Deploy to production.
Prerequisites
Before contributing, read Contributing for the Contributor License Agreement, the code of conduct, and the process for proposing a change.
Repository layout
The repository is a monorepo: the FastAPI core in core/, the IaC and
notifications sidecar services in services/, the React web application
in client/web/, and the sidecar OpenAPI contracts in contracts/. See
Architecture for how they fit together.
Running the stack for development
Copy each env.sample to a .env next to it (core/.env is
required), set the two model strings in config.yaml, and run docker
compose up --build. Quickstart is the step-by-step
guide. --build matters: without it, Compose runs the published
release images (ghcr.io/inditextech/kumoss-*) instead of your
checkout. A docker compose pull later replaces locally built images
with the published ones again.
After a code change, rebuild and recreate the affected service, for
example docker compose up -d --build core. An edit to config.yaml
needs only docker compose restart core, because Compose bind-mounts
that file.
Local Compose overrides
Settings that belong to your machine rather than to the project go in
docker-compose.override.yml, which Compose loads automatically and
git ignores. docker-compose.override.example.yml is the tracked
template — copy it rather than editing it in place:
cp docker-compose.override.example.yml docker-compose.override.yml
Behind a TLS-inspecting proxy
On a corporate network that inspects HTTPS, most outbound container
requests fail while a few domains keep working: web_search returns
engine snippets instead of page content, and docker compose logs core
shows the reads failing before any HTTP request is sent.
Page read for '<url>' failed, falling back to the engine snippet:
PageReadError("could not render '<url>'")
(RuntimeError('[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify
failed: self-signed certificate in certificate chain'))
The proxy terminates TLS and re-signs it with its own root, which the containers do not trust; the domains that still work are the ones the proxy passes through untouched. Confirm it by looking at who signed what you received:
docker compose exec core sh -c \
'echo | openssl s_client -connect example.com:443 \
-servername example.com 2>/dev/null | openssl x509 -noout -issuer'
An issuer naming your company rather than a public CA confirms the
interception. The fix is two-part, both wired in the override template
above, and keeps the certificates on your machine:
-
Build — the core downloads Chromium, so the roots are needed at build time too. The override points the
host-certsbuild context at the host directory holding them. -
Runtime — the override mounts that directory at
KUMOSS_CA_DIR, and each entrypoint merges its certificates into a bundle and points the HTTP clients at it before starting the server.
A hostname mismatch is not a trust problem: the proxy is sinkholing that domain under its filtering policy, and the certificate honestly belongs to the sinkhole. No configuration fixes that one.
Core tests
Python 3.14, uv. pytest and pytest-cov are part of the tooling
dependency group, so sync the project first:
cd core
uv sync --frozen --group tooling
uv run --no-sync pytest tests/
Async tests use unittest.IsolatedAsyncioTestCase (no pytest-asyncio
plugin, no --asyncio-mode flag).
core/tests/conftest.py supplies placeholder values for the LLM
credential, the database URL and the mandatory KUMOSS_IAC_TOKEN that
the configuration module validates at import; suites that talk to
PostgreSQL or Redis need the compose stack (or point
KUMOSS_SQL_DATABASE_URL and KUMOSS_REDIS_URL at your own
instances).
Type checking
basedpyright lives in the tooling dependency group, which the
project install above does not include; install the group first:
cd core
uv pip install --system --group tooling -r pyproject.toml
basedpyright
pyrightconfig.json only includes src/ — tests are not type-checked.
Sidecar tests
Each sidecar service has its own tooling dependency group with
pytest and its test helpers (the exact set differs per service):
cd services/<name>
uv sync --group tooling
uv run pytest
Linting and formatting
pre-commit run --all-files runs Ruff (ruff check and ruff format),
the REUSE check, and gitleaks secret scanning. Install the hooks once
with pre-commit install.
Continuous integration
Pull requests run Repolinter, the REUSE compliance check, the Conventional Commits check, and an offline Markdown link and anchor check, plus a Verify workflow: frontend type check, tests, and production build, and the pytest suite of each sidecar service. The core suite is not run in CI, so run it locally against the compose stack before opening a pull request.
Regenerating sidecar clients
Changing a sidecar’s behavior means updating its spec in
contracts/openapi/, its conformance suite in contracts/conformance/,
and regenerating the core’s client for that service. From core/, once
per service:
uv run --group tooling openapi-python-client generate \
--path ../contracts/openapi/<service>.v1.yaml \
--config ../contracts/openapi-python-client.yaml \
--meta none \
--output-path src/clients/<service> \
--overwrite
Regeneration drops the SPDX headers from the generated files; re-add
them afterwards. Do not hand-edit anything under
core/src/clients/{authz,iac,mapping,notifications}/.
Pull requests
Once your change is ready, Contributing covers the pull-request process and the checks it must pass.