How to Set Up GitHub Integration for Bidirectional Task Sync in Kaneo

To set up GitHub integration in Kaneo, configure environment variables for a GitHub App, register the app with webhook URL <KANEO_API_URL>/github-integration/webhook, then use the REST API to link a project to a repository for automatic two-way synchronization of issues, pull requests, and comments.

Kaneo's open-source task management platform supports native GitHub integration for bidirectional task sync, allowing teams to manage work in either tool while keeping both systems consistent. This guide walks through the complete implementation based on the Kaneo source code, from environment configuration to real-time webhook processing.

Architecture Overview

Kaneo follows a controller-router-schema pattern for all integrations. The GitHub module consists of these core components:

Component Location Purpose
API Routes apps/api/src/github-integration/index.ts REST endpoints for CRUD operations and webhooks
Config Schema apps/api/src/plugins/github/config.ts Valibot validation for integration settings
Database Model apps/api/src/database/schema.ts integration table with type = "github"
Controllers apps/api/src/github-integration/controllers/*.ts Business logic for each operation
GitHub App Wrapper apps/api/src/plugins/github/utils/github-app.ts Authenticated Octokit singleton
Webhook Handler apps/api/src/plugins/github/webhook-handler.ts Processes GitHub events and updates tasks
Front-End Hooks apps/web/src/hooks/mutations/github-integration/* React Query integration for the UI

The flow enforces request validation → permission checks → business logic → response at every step, using hono-openapi for type safety and OpenAPI documentation generation.

Step 1: Configure Environment Variables

Create a GitHub App and set these environment variables in your Kaneo API deployment:

GITHUB_APP_ID=your-app-id
GITHUB_APP_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n..."
GITHUB_APP_NAME=Kaneo
GITHUB_WEBHOOK_SECRET=your-webhook-secret

These values are read by apps/api/src/plugins/github/utils/github-app.ts, which exports a getGithubApp() singleton that provides authenticated Octokit instances for API calls.

Step 2: Register Your GitHub App

  1. Navigate to Settings → Developer settings → GitHub Apps → New GitHub App
  2. Set Webhook URL to: https://<your-kaneo-api>/github-integration/webhook
  3. Configure Repository permissions:
    • Issues: Read & Write
    • Pull requests: Read & Write
    • Contents: Read (for branch pattern matching)

The webhook endpoint is handled by handleGithubWebhookRoute in apps/api/src/github-integration/index.ts, which verifies the x-hub-signature-256 header before processing.

Step 3: Create the Integration via API

Link a Kaneo project to a GitHub repository using the REST API:

POST /github-integration/project/:projectId
Content-Type: application/json
Authorization: Bearer <api-key>

{
  "repositoryOwner": "your-org",
  "repositoryName": "your-repo"
}

The create-github-integration.ts controller performs these operations:

  1. Validates the request against githubConfigSchema from apps/api/src/plugins/github/config.ts
  2. Verifies workspace permissions via workspaceAccess.fromProject
  3. Fetches the installation ID via githubApp.octokit.rest.apps.getRepoInstallation
  4. Stores configuration in the integration table with defaultGitHubConfig merged values

Example: React Component for Integration Setup

import { useMutation } from "@tanstack/react-query";
import createGithubIntegration from "@/fetchers/github-integration/create-github-integration";

function GithubIntegrationForm({ projectId }: { projectId: string }) {
  const createMutation = useMutation({
    mutationFn: (payload: { repositoryOwner: string; repositoryName: string }) =>
      createGithubIntegration(projectId, payload),
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const form = e.target as HTMLFormElement;
    const repositoryOwner = (form.elements.namedItem("owner") as HTMLInputElement).value;
    const repositoryName = (form.elements.namedItem("repo") as HTMLInputElement).value;
    createMutation.mutate({ repositoryOwner, repositoryName });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="owner" placeholder="GitHub organization/user" required />
      <input name="repo" placeholder="Repository name" required />
      <button type="submit" disabled={createMutation.isLoading}>
        Connect GitHub
      </button>
    </form>
  );
}

The fetcher at apps/web/src/fetchers/github-integration/create-github-integration.ts wraps the generated OpenAPI client:

export default async function createGithubIntegration(
  projectId: string,
  data: { repositoryOwner: string; repositoryName: string }
) {
  const client = getClient();
  const response = await client["github-integration"].project[projectId].$post({ json: data });
  return response;
}

Step 4: Verify Installation Status

Optionally validate permissions before linking:

POST /github-integration/verify
Content-Type: application/json

{
  "repositoryOwner": "your-org",
  "repositoryName": "your-repo"
}

The verify-github-installation.ts controller checks:

  • Whether the GitHub App is installed on the repository
  • Required permissions are granted
  • Returns detailed error messages for missing scopes

Step 5: Import Existing GitHub Issues

Bulk-import open issues as Kaneo tasks:

POST /github-integration/import-issues
Content-Type: application/json
Authorization: Bearer <api-key>

{
  "projectId": "<project-id>"
}

The import-issues.ts controller:

  • Fetches open issues via octokit.rest.issues.listForRepo
  • Creates corresponding Kaneo tasks with GitHub metadata
  • Stores githubIssueId for ongoing synchronization

Step 6: Enable Real-Time Bidirectional Sync

Once configured, the integration operates automatically:

Kaneo → GitHub: Task status changes, labels, and comments are pushed to GitHub via the Octokit API.

GitHub → Kaneo: Webhook events processed by apps/api/src/plugins/github/webhook-handler.ts update tasks:

GitHub Event Kaneo Action
issues.opened Create new task
issues.edited Update task title/description
issues.labeled Sync labels
issues.closed Set task status to done
pull_request.opened Create task linked to PR
issue_comment.created Add comment to task

The webhook handler verifies signatures using crypto.createHmac('sha256', webhookSecret) before dispatching events.

Configuration Schema Reference

The integration config stored in the database follows this Valibot schema from apps/api/src/plugins/github/config.ts:

export const githubConfigSchema = object({
  repositoryOwner: string(),
  repositoryName: string(),
  installationId: number(),
  branchPattern: optional(string(), "main"),
  syncPullRequests: optional(boolean(), true),
  syncComments: optional(boolean(), true),
});

export type GitHubConfig = Output<typeof githubConfigSchema>;

Summary

  • Environment setup: Configure GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, and GITHUB_WEBHOOK_SECRET for the Octokit wrapper in apps/api/src/plugins/github/utils/github-app.ts
  • API integration: Use POST /github-integration/project/:projectId to link projects, validated by githubConfigSchema and stored in the integration table
  • Bidirectional sync: Webhooks to /github-integration/webhook trigger handleGitHubWebhook for GitHub→Kaneo updates; Kaneo→GitHub updates use authenticated Octokit calls
  • Front-end support: TanStack Query hooks in apps/web/src/hooks/mutations/github-integration/* provide React components with type-safe API access

Frequently Asked Questions

What permissions does the GitHub App need for bidirectional task sync?

The GitHub App requires Issues and Pull requests permissions with Read & Write access, plus Repository contents with Read access. These are validated by verify-github-installation.ts and enforced when creating integrations.

How does Kaneo authenticate with GitHub?

Kaneo uses @octokit/app through a singleton wrapper in apps/api/src/plugins/github/utils/github-app.ts. The wrapper creates installation-specific Octokit instances using the app ID and private key from environment variables, then exchanges these for repository-scoped tokens via GitHub's API.

Can I sync existing GitHub issues after setup?

Yes. Call POST /github-integration/import-issues with the projectId to bulk-import open issues. The import-issues.ts controller fetches all open issues and creates linked Kaneo tasks with githubIssueId stored for ongoing synchronization.

What happens if the webhook signature verification fails?

Requests to /github-integration/webhook with invalid x-hub-signature-256 headers are rejected by handleGithubWebhookRoute before reaching the handler. The route uses crypto.timingSafeEqual to prevent timing attacks during signature comparison.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →