Contributing
Overview
We welcome contributions to Kinotic OS. This guide covers the repository structure, build process, and contribution workflow.
Repository Structure
| Directory | Description |
|---|---|
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
- Fork the repository and create a feature branch from
develop - Make your changes with clear, descriptive commit messages
- Ensure all tests pass before submitting
- Submit a pull request against the
developbranch