Applications and Projects
An application is the deployable unit in Kinotic. Projects are the building blocks that make up an application. This page explains how to configure both.
Applications
Every application has:
- Name -- A human-readable name (e.g.,
Inventory App). - Id -- Derived from the name at creation: the slugified name, made of lowercase letters, digits, and single interior dashes (e.g.,
inventory-app). The id forms the final label of the application's zone, and joined to the organization id with--it forms the application's API host label,<organizationId>--<applicationId>. Each of its UIs' sites is<applicationId>--<uiName>under the organization's own subdomain of the sites domain,<applicationId>--<uiName>.<organizationId>.kinotic.io; that first label is held to 37 characters, which leaves a tenant's name room in front of it on a tenant's site (<tenantId>--<applicationId>--<uiName>, within DNS's 63), and the API host label to DNS's 63, so a name that leaves no room for a UI name, or whose API host label would exceed the limit under the organization's id, is rejected. - Description -- A human-readable summary of what the application does.
- Primary UI -- The name of the UI whose site the application's browser flows return to, chosen on the application's Settings page: OAuth consent opens its
/oauth/consentpage, and a sign-in an identity provider fails returns to its/loginpage with anerrorparameter. Until one is chosen, the application's OAuth authorization endpoint answers400. It must name a UI one of the application's projects has published; it stays set if that UI's deployment is later removed.
Applications are created and managed through the Kinotic CLI and the Kinotic OS dashboard.
Projects
A project is a directory with its own package.json and a .config/kinotic.config.ts configuration file. The CLI uses that config to understand how the project fits into the broader application.
The .config/kinotic.config.ts File
Every project carries a .config/kinotic.config.ts, created when Kinotic OS provisions
the project repository. It is a TypeScript module exporting a KinoticProjectConfig, so
your editor type-checks it:
import type { KinoticProjectConfig } from '@kinotic-ai/management-api'
const config: KinoticProjectConfig = {
organizationId: "my-org",
applicationId: "my-app",
projectId: "my-app-main",
entitiesPaths: [
{
path: "packages/domain/model",
repositoryPath: "packages/domain/repositories",
mirrorFolderStructure: true
}
],
fileExtensionForImports: ".js",
validate: false
}
export default config
The CLI reads the first kinotic.config.* file it finds in .config, so a .js or .json config
is loaded the same way.
Fields
| Field | Description |
|---|---|
organizationId | Required. The id of the Kinotic organization this project belongs to. |
applicationId | Required. The application id this project belongs to. Must match the application id registered with Kinotic OS: lowercase letters, digits, and interior dashes. |
projectId | Required. The id of this project in Kinotic OS. Provisioned repositories carry the id of the project that created them; kinotic sync addresses this project and fails when it does not exist. |
name | Optional project name. When omitted, the name from the project's package.json is used. |
description | Optional project description. When omitted, the description from the project's package.json is used. |
entitiesPaths | Required. An array whose entries are either a plain path string or an EntitiesPathConfig object with path (the directory containing entity definitions), repositoryPath (where the CLI writes generated repository classes), and mirrorFolderStructure (whether to replicate the entity directory structure in the output, default true). The CLI scans these paths when you run kinotic sync. |
servicesPaths | The directories the CLI scans for classes decorated with @Publish, the services the project serves. kinotic generate and kinotic sync write their definitions to ServiceDefinitions.ts under generatedPath, which the project imports so each service registers in the platform's service directory as it comes online. |
generatedPath | The default output path for generated files, used for entitiesPaths entries that are plain strings and for the ServiceDefinitions module of servicesPaths. Ignored for entitiesPaths entries that use EntitiesPathConfig. |
fileExtensionForImports | The extension the CLI writes on import paths in generated code. Defaults to .js. |
validate | When true, generated repository classes validate data before sending it to the server. |
Project Dependencies
Projects within the same application can depend on each other. For example, a microservice project might import entity types defined in a persistence project. These dependencies are managed through standard package.json -- you add the dependency just like any other Bun package.
{
"dependencies": {
"@my-org/data": "workspace:*"
}
}
When the application is deployed, the platform resolves these internal dependencies and ensures all projects are available to each other through the Service Directory.
Deployable packages
When a push deploys a project (see Push to Deploy), the platform finds the packages it deploys by where they sit in the workspace:
- Microservices live directly under
packages/microservices, one package each. A microservice runs in a VM of its own from itspackage.jsonmain, orsrc/main.tswhen it declares none. - UIs live directly under
packages/ui, one package each, and declare abuildscript that writesdist/index.html. A package underpackages/uiwithout abuildscript is a library and is left alone.
A package's identity is the unscoped part of its package.json name (@acme/admin is
admin), which must be lowercase letters, digits, and interior dashes and unique among the
packages of its kind. The directory name never matters.
A UI's name also names its site, <applicationId>--<uiName>.<organizationId> under the sites
domain, so it must not contain --, and it belongs to one project of the application: a project publishing a
UI whose name another project of the application already published fails its deployment
until that UI's deployment is removed.
The UI build contract
Every UI is built during the deployment with bun run build, and the build is handed its
application's API host, <organizationId>--<applicationId> under the platform's application
domain, as three variables. Vite exposes VITE_* variables to the page on its
own, so a Vite project reads them with no configuration; a UI with another build tool must
pass them through itself:
| Variable | Value | What the UI does with it |
|---|---|---|
VITE_KINOTIC_HOST | e.g. acme--orders.apps-api.kinotic.io | The host the UI connects to Kinotic on from a browser |
VITE_KINOTIC_PORT | e.g. 443 | Its port |
VITE_KINOTIC_USE_SSL | true or false | Whether to connect over TLS |
await Kinotic.connect({
server: {
host: import.meta.env.VITE_KINOTIC_HOST,
port: parseInt(import.meta.env.VITE_KINOTIC_PORT),
useSSL: import.meta.env.VITE_KINOTIC_USE_SSL === 'true',
},
})
A build that does not write dist/index.html fails the deployment naming the UI.
dist is published as it is, from the site's root: files under assets/ carry a content
hash in their name and are cached for a year, everything else is never cached. Each site
also publishes the commit it serves as version.json next to its index.html, never
cached, as { "commitSha": "<commit>" }. A publish replaces the previous commit's files;
a tab left open on the previous commit loads the new commit when it next reloads.
Developing a UI locally
A UI served by its Vite dev server reaches a local app server through the dev server itself: point the three variables at the dev server, and have it proxy the API to the app server under your application's API host, so the page and its API share an origin and a session cookie:
# .env.development
VITE_KINOTIC_HOST=localhost
VITE_KINOTIC_PORT=5173
VITE_KINOTIC_USE_SSL=false
// vite.config.ts
const APP_API_HOST = 'acme--orders.localhost:58505' // <organizationId>--<applicationId> under kinotic.domain.appApiBaseUrl
export default defineConfig({
server: {
port: 5173,
proxy: {
// REST under /api and the STOMP WebSocket at /api/v1
'/api': { target: 'http://localhost:58505', headers: { host: APP_API_HOST }, ws: true },
},
},
})
The app server's development profile admits a page on any http://localhost port as a UI of
every application; elsewhere an application's users sign in only from its published sites.
Typical Setup
Most applications start with a single project created through Kinotic OS, which provisions a GitHub repository scaffolded as a Bun workspace mono repo: packages/domain (entity model and generated repositories), packages/microservices, packages/ui, and a .config/kinotic.config.ts wired to the owning organization and application. Clone the provisioned repository and start building.
As the application grows, you can add additional projects for microservices, batch jobs, or frontends -- each with its own .config/kinotic.config.ts pointing to the same application identifier.