How to Manage Kaneo Configurations: A Complete Guide to Environment Variables and API-First Settings
Kaneo configurations are managed through environment variables that are read at runtime, exposed via a public /config API endpoint, and consumed reactively by the frontend through TanStack Query hooks.
Kaneo is an open-source project management platform built with a modern stack. Understanding how to manage Kaneo configurations is essential for self-hosting, customizing integrations, and extending the platform. This guide walks through the complete configuration lifecycle—from environment variables to React components—based on the actual source code in the usekaneo/kaneo repository.
How Kaneo's Configuration System Works
Kaneo uses a thin, API-first configuration layer rather than static config files. This design choice enables runtime flexibility and seamless frontend reactivity without rebuilds.
The Three-Layer Architecture
- Environment layer –
process.envvariables serve as the single source of truth - API layer –
GET /configendpoint exposes typed settings as JSON - Client layer – TanStack Query hooks provide cached, reactive access
Each layer is intentionally simple. The getSettings() helper in apps/api/src/utils/get-settings.ts performs the only transformation: parsing environment strings into typed values.
Reading Configuration Settings on the Server
All server-side configuration flows through one function.
The getSettings() Helper
In apps/api/src/utils/get-settings.ts, the getSettings() function maps environment variables to a typed object:
function getSettings() {
return {
isDemoMode: process.env.DEMO_MODE === "true",
hasSmtp: isSmtpConfigured(),
hasGithubSignIn: !!process.env.GITHUB_CLIENT_ID,
hasGoogleSignIn: !!process.env.GOOGLE_CLIENT_ID,
// ... additional flags
};
}
Key characteristics:
- Returns a plain JavaScript object with boolean and string properties
- Performs no caching—reads fresh
process.envvalues on every call - Delegates complex logic (like SMTP validation) to specialized utilities
SMTP-Specific Configuration
Email settings receive special handling in packages/email/src/smtp-config.ts. The getSmtpTransportOptions() function constructs a complete Nodemailer configuration object, while isSmtpConfigured() performs a quick check for required variables.
Exposing Configurations via the API
The GET /config endpoint in apps/api/src/config/index.ts is the bridge between environment variables and the frontend.
The Config Route Implementation
import { configSchema } from "@/schemas";
import { getSettings } from "@/utils/get-settings";
app.get("/config", (c) => {
const settings = getSettings();
return c.json(settings);
});
OpenAPI integration – The response shape is governed by configSchema in apps/api/src/schemas.ts, defined with Valibot validators:
export const configSchema = v.object({
isDemoMode: v.boolean(),
hasSmtp: v.boolean(),
hasGithubSignIn: v.boolean(),
hasGoogleSignIn: v.boolean(),
// ... additional fields
});
Thanks to hono-openapi decorators, this schema automatically generates documentation and runtime validation without manual synchronization.
Consuming Kaneo Configurations in the Frontend
The frontend accesses configurations through a typed API client and reactive hooks.
Fetching Configurations with the API Client
The base URL resolution happens in packages/libs/src/api-url.ts via resolveApiBaseUrl(), which handles both development and production environments. The fetcher in apps/web/src/fetchers/config/get-config.ts wraps the Hono client:
import { client } from "@kaneo/libs";
export async function getConfig() {
const response = await client.config.$get();
if (!response.ok) {
throw new Error("Failed to fetch config");
}
return response.json();
}
Using the useGetConfig React Hook
For reactive access, useGetConfig in apps/web/src/hooks/queries/config/use-get-config.ts wraps the fetcher in TanStack Query:
import useGetConfig from "@/hooks/queries/config/use-get-config";
export default function ConfigDisplay() {
const { data: cfg, isLoading, error } = useGetConfig();
if (isLoading) return <p>Loading…</p>;
if (error) return <p>Error loading config</p>;
return (
<ul>
<li>Demo mode: {cfg?.isDemoMode ? "Yes" : "No"}</li>
<li>SMTP configured: {cfg?.hasSmtp ? "Yes" : "No"}</li>
<li>GitHub sign‑in: {cfg?.hasGithubSignIn ? "Enabled" : "Disabled"}</li>
</ul>
);
}
This pattern provides automatic caching, background refetching, and error handling without additional boilerplate.
Adding a New Kaneo Configuration Flag
Extending the configuration system requires coordinated changes across three files.
Step 1: Add the Environment Variable
Document the new variable in .env.example:
# Enable real-time chat features
ENABLE_CHAT="true"
Step 2: Expose in getSettings()
Update apps/api/src/utils/get-settings.ts:
function getSettings() {
return {
// …existing flags
enableChat: process.env.ENABLE_CHAT === "true",
};
}
Step 3: Extend the OpenAPI Schema
Update apps/api/src/schemas.ts:
export const configSchema = v.object({
// …existing fields
enableChat: v.boolean(),
});
The frontend automatically receives the new property through getConfig() and useGetConfig() without additional changes.
Integration-Specific Configuration Patterns
Kaneo uses consistent patterns for third-party integrations.
Authentication Providers
Each OAuth provider exposes a boolean flag indicating configuration status:
hasGithubSignIn– checks forGITHUB_CLIENT_IDhasGoogleSignIn– checks forGOOGLE_CLIENT_IDhasDiscordSignIn– checks forDISCORD_CLIENT_ID
These flags enable conditional UI rendering (show/hide sign-in buttons) without exposing sensitive credentials.
Email Configuration
SMTP settings are the most complex configuration area. The packages/email/src/smtp-config.ts module provides:
isSmtpConfigured()– quick boolean check for UI purposesgetSmtpTransportOptions()– full Nodemailer configuration for sending
This separation allows the API to report email capability without constructing transport objects unnecessarily.
Summary
- Single source of truth – All Kaneo configurations originate from environment variables read via
process.env - Server entry point – The
getSettings()function inapps/api/src/utils/get-settings.tsparses and types all environment variables - Public API – The
GET /configendpoint inapps/api/src/config/index.tsexposes settings with automatic OpenAPI documentation - Frontend access – Use
useGetConfig()for reactive state orgetConfig()for imperative fetching - Extensibility – Adding flags requires updating three files: environment documentation,
getSettings(), andconfigSchema
Frequently Asked Questions
Where does Kaneo store its configuration files?
Kaneo does not use static configuration files. All settings are read from environment variables at runtime through process.env, making it ideal for containerized deployments and secret management systems.
How do I add a custom configuration option to Kaneo?
Add your variable to .env.example, parse it in apps/api/src/utils/get-settings.ts, and extend configSchema in apps/api/src/schemas.ts. The frontend will automatically receive the new value through existing hooks.
Why does Kaneo expose configurations through an API endpoint instead of baking them into the build?
The API-first approach enables runtime configuration changes without recompiling the frontend. This is critical for Docker deployments where the same image runs across different environments (staging, production) with different settings.
How can I check if email notifications are configured in Kaneo?
Query the public config endpoint or use useGetConfig() in a React component. The hasSmtp boolean indicates whether SMTP credentials are present, while packages/email/src/smtp-config.ts contains the detailed transport configuration used when sending actual emails.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →