Kinotic OS

Contributing

How to contribute to the Kinotic OS project.

Overview

We welcome contributions to Kinotic OS. This guide covers the repository structure, build process, and contribution workflow.

Repository Structure

DirectoryDescription
kinotic-core/Java/Kotlin backend (Spring Boot) — RPC gateway, service registry, authentication
kinotic-management-api/Domain model and management API services — application, project, and cluster management
kinotic-js/TypeScript SDK workspace (Bun) — @kinotic-ai/core, @kinotic-ai/persistence, and related packages
kinotic-frontend/Vue.js UI workspace (pnpm) — apps/portal (the Kinotic OS dashboard), apps/system (the platform-operator console), packages/common (shared UI code)
website/Documentation site (Docus/Nuxt)
deployment/Helm charts, Docker Compose, Terraform configurations

Building

Java/Kotlin Backend

./gradlew build

TypeScript SDK

cd kinotic-js
bun install
bun run build

Website

cd website
bun install
bun run dev

Testing

Java/Kotlin

./gradlew test

TypeScript (Vitest)

cd kinotic-js
bun test

Publishing UIs against Azure

The development profile publishes UIs to a local Azurite and marks every site ready without Front Door. To exercise the real path — a storage account per organization, sites served through Front Door — point the server at a subscription of your own.

Create what the server cannot create itself, once:

az login
cd deployment/terraform/azure/dev
terraform init
terraform apply -target=azurerm_cdn_frontdoor_profile.sites   # the profile's identity first: its principal id is unknown until it exists
terraform apply   # environment = "local" in terraform.tfvars; pick a name of your own
terraform output -raw application_local_yml > ../../../../kinotic-server/src/main/resources/application-local.yml

That is a resource group, a Front Door Standard profile with an endpoint and an identity that reads every organization's storage, the apps-<environment>.<zone> sites domain in the platform's DNS zone, and a service principal for the server with the roles it needs: Contributor and Storage Blob Data Contributor on the group, DNS Zone Contributor on the zone, and Contributor on the email service, so it sends mail too. The written file is the git-ignored local profile: it turns both provisioners on and names those resources. Turning the storage provisioner on is also what moves publishing off the development profile's Azurite, whose connection string the layered profile still carries: with the provisioner on, each organization is reached at the account it was provisioned in. The environment is yours alone: a site hostname can be bound to one Front Door profile in all of Azure, so two developers sharing a sites domain would collide.

The principal's AZURE_CLIENT_ID, AZURE_CLIENT_SECRET and AZURE_TENANT_ID are written to .env.local at the repository root, which git ignores, as a commented block after a blank line. When the file exists its other lines are kept and the block a previous apply wrote is replaced, or appended if absent. Run the server with that file in its environment; DefaultAzureCredential takes those variables before anything else, so the server provisions as the principal whatever az login is signed in as. A role assignment takes a minute or two to become visible; an organization provisioned before that fails with AuthorizationFailed, and Provision again finishes it once the roles have propagated.

Run the server with both profiles:

SPRING_PROFILES_ACTIVE=development,local

The local profile overrides the development one, which already disables private endpoints: a developer machine is outside any platform VNet, so the server and the publish workload reach each storage account over its public endpoint, and the workload's egress allowlist names that host.

Before involving the server, run the provisioning against your subscription as a test. It does what the provisioning job and a deployment do, for a fixed organization kinotic-azure-it and a site azure-it.apps-<environment>.<zone>, and reads back from Azure what each step created, so a step Azure rejects fails naming the call:

./gradlew :kinotic-system-api:test --tests '*AzureProvisioningIntegrationTest*'

The module's test task puts .env.local in the test's environment, and the test reads your application-local.yml; without either it skips. What it creates is left in place, so a second run is quick, and terraform destroy removes the account with the resource group; the site's records under apps-<environment> in the zone are removed by hand.

Then sign up an organization, or open an existing one in the system console and choose Provision again: the provision-organization-<id> job creates its storage account and prepares its Front Door origin group, and the organization's overview shows the outcome. Deploy a project that contains a UI; the deployment publishes it and provisions its site, served at https://<label>.apps-<environment>.<zone> once Front Door has validated the domain, issued its certificate and serves the UI's version.json, which takes a few minutes and shows as the deployment turning from provisioning to ready.

terraform destroy removes the resource group with every storage account and Front Door resource the server created in it. The CNAME and validation TXT records of sites live in the platform's DNS zone, outside the group; removing a deployment removes them, so remove your deployments first, or delete the records under apps-<environment> by hand.

Submitting Changes

  1. Fork the repository and create a feature branch from develop
  2. Make your changes with clear, descriptive commit messages
  3. Ensure all tests pass before submitting
  4. Submit a pull request against the develop branch
Copyright © 2026