How the GitHub Integration Works in Kaneo: A Complete Technical Breakdown
Kaneo connects projects to GitHub repositories through a GitHub App that uses installation-scoped Octokit clients to validate permissions, store repository metadata as JSON, and expose type-safe REST endpoints for the React frontend.
The GitHub integration in the usekaneo/kaneo repository is implemented as a full-stack feature spanning the API backend, a typed HTTP client library, and React UI components. It enables bidirectional synchronization between Kaneo projects and GitHub repositories by leveraging GitHub App installations, stored configuration objects, and verification workflows that ensure proper permissions before any data exchange occurs.
Architecture Overview
The integration is organized into three distinct layers that handle authentication, data transfer, and user interaction:
-
API Backend: Validates and stores integration data, communicates with the GitHub App to resolve installation IDs, and exposes typed OpenAPI routes via Hono controllers located in
apps/api/src/github-integration/controllers/*. -
Typed Client: The
@kaneo/libspackage generates a type-safe HTTP client that the web UI consumes for all GitHub integration operations, ensuring end-to-end type safety between the frontend and backend. -
Web UI: React components and hooks in
apps/web/src/components/project/github-integration-settings.tsxmanage the connection interface, calling the typed client through small fetcher helpers inapps/web/src/fetchers/github-integration/*.
Creating a GitHub Integration
When a user submits a repository owner and name through the settings UI, the frontend invokes the createGithubIntegration fetcher:
// apps/web/src/fetchers/github-integration/create-github-integration.ts
import { client } from "@kaneo/libs";
export async function createGithubIntegration(
projectId: string,
data: { repositoryOwner: string; repositoryName: string },
) {
const resp = await client["github-integration"].project[":projectId"].$post({
param: { projectId },
json: data,
});
if (!resp.ok) throw new Error(await resp.text());
return resp.json();
}
The request reaches the controller at apps/api/src/github-integration/controllers/create-github-integration.ts, which executes the following validation sequence:
-
GitHub App initialization: Calls
getGithubApp()to ensure the App is configured with environment variables (GITHUB_APP_ID,GITHUB_PRIVATE_KEY,GITHUB_WEBHOOK_SECRET). -
Project validation: Confirms the target project exists in the database.
-
Uniqueness constraint: Queries existing integrations to prevent linking the same repository to multiple projects.
-
Installation ID discovery: Uses
githubApp.octokit.rest.apps.getRepoInstallationto fetch the numeric installation ID for the repository. -
Persistence: Stores the configuration—including owner, name, installationId, and default settings—as a JSON object in
integrationTablewithtype: "github".
Retrieving Integration Data
The frontend polls the current integration state using getGithubIntegration:
// apps/web/src/fetchers/github-integration/get-github-integration.ts
import { client } from "@kaneo/libs";
export async function getGithubIntegration(projectId: string) {
const resp = await client["github-integration"].project[":projectId"].$get({
param: { projectId },
});
if (!resp.ok) throw new Error(await resp.text());
return resp.json();
}
The controller at apps/api/src/github-integration/controllers/get-github-integration.ts reads the stored row, parses the JSON configuration, and returns a normalized shape that merges default values (such as branchPattern) when fields are missing from the stored record.
Verifying GitHub App Installation
Before executing sync operations, Kaneo validates that the GitHub App remains installed and authorized. The verification logic resides in apps/api/src/github-integration/controllers/verify-github-installation.ts:
async function verifyGithubInstallation({ repositoryOwner, repositoryName }) {
const githubApp = getGithubApp();
// ... validation logic ...
}
The verification process performs several checks:
-
Installation status: Detects 404 responses from
apps.getRepoInstallationand returns an installation URL for the user. -
Repository accessibility: Obtains an installation-scoped Octokit client via
getInstallationOctokitand callsrest.repos.getto confirm the repository exists and is accessible. -
Permission validation: Ensures the installation grants write or admin permissions on the
issuesscope, which is the minimum required permission for Kaneo operations. -
User guidance: Returns
settingsUrlandinstallationUrlproperties to direct users to GitHub's configuration pages when permissions are missing.
Core Utilities and Configuration
The apps/api/src/plugins/github/utils/github-app.ts module centralizes GitHub App lifecycle management. It reads private keys from environment variables, initializes the Octokit.App instance, and exposes helper methods:
-
getInstallationOctokit: Returns an authenticated client scoped to a specific installation ID. -
getInstallationIdForRepo: Resolves installation IDs for given repository coordinates.
Default configuration values—such as branchPattern regex and commentTaskLinkOnGitHubIssue flags—are defined in apps/api/src/plugins/github/config.ts. These defaults are merged with stored user configurations during read operations to ensure API stability.
Practical Implementation Examples
Checking Installation Status
Before importing issues, verify the App is properly installed and authorized:
import { client } from "@kaneo/libs";
async function checkInstallation(owner: string, repo: string) {
const { data } = await client["github-integration"]
.verify["verify-github-installation"].$post({
json: { repositoryOwner: owner, repositoryName: repo },
});
if (!data.isInstalled) {
console.warn("App not installed –", data.installationUrl);
} else if (!data.hasRequiredPermissions) {
console.warn("Missing permissions:", data.missingPermissions);
} else {
console.log("All good! Installation ID:", data.installationId);
}
}
Importing GitHub Issues
Once verified, import existing issues into Kaneo tasks:
import { client } from "@kaneo/libs";
async function importIssues(projectId: string, repoOwner: string, repoName: string) {
const { data } = await client["github-integration"]
.import["import-github-issues"].$post({
param: { projectId },
json: { repositoryOwner: repoOwner, repositoryName: repoName },
});
console.log(`${data.importedCount} issues imported`);
}
Summary
-
Kaneo's GitHub integration uses a GitHub App rather than OAuth tokens, enabling granular permission control and organization-level access.
-
The architecture separates concerns across API controllers, a typed Hono client, and React hooks/components for maintainable full-stack operations.
-
Installation IDs are resolved dynamically via
getRepoInstallationand stored in JSON configuration rows within theintegrationTable. -
Permission verification occurs before every sync operation, checking for write/admin access on the issues scope and providing actionable URLs for remediation.
-
Default configurations in
apps/api/src/plugins/github/config.tsensure backward compatibility when reading legacy integration records.
Frequently Asked Questions
What permissions does Kaneo require from the GitHub App?
Kaneo requires write or admin permissions on the issues scope. The verification controller in verify-github-installation.ts explicitly checks for these permissions before allowing import or sync operations, and returns a missingPermissions array if additional access is needed.
How does Kaneo store GitHub integration credentials?
Kaneo does not store personal access tokens. Instead, it stores the installation ID returned by GitHub's App API. The private key and App ID remain in environment variables (GITHUB_PRIVATE_KEY, GITHUB_APP_ID), while the database keeps only the repository metadata and installation reference in a JSON column of the integrationTable.
Can the same GitHub repository connect to multiple Kaneo projects?
No. The create-github-integration.ts controller explicitly checks for existing integrations with the same owner and repository name before creating a new record, enforcing a one-to-one relationship between GitHub repositories and Kaneo projects.
What happens if the GitHub App is uninstalled after initial setup?
The verifyGithubInstallation controller detects 404 errors when fetching the repository installation and returns isInstalled: false along with a fresh installationUrl. The frontend uses this response to prompt users to reinstall the App before continuing with sync operations.
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 →