GitHub connections
A GitHub connection binds a Novo organization to a GitHub App installation. The customer chooses the GitHub account and repositories on GitHub. Novo then mints a short-lived token for each operation, narrowed to the repositories and permissions that operation needs.
Customers never paste a personal access token into Novo. GitHub credentials stay in Novo's hosted worker and never enter a workspace, tool result, stream event, or customer execution environment.
SDK methods
| SDK | HTTP |
|---|---|
| novo.githubConnections.authorize({...}) | POST /v1/github-connections/authorize |
| novo.githubConnections.getAuthorization(id) | GET /v1/github-connections/authorize/:id |
| novo.githubConnections.list({...}) | GET /v1/github-connections |
| novo.githubConnections.get(id) | GET /v1/github-connections/:id |
| novo.githubConnections.update(id, {...}) | PATCH /v1/github-connections/:id |
| novo.githubConnections.delete(id) | DELETE /v1/github-connections/:id |
| novo.githubConnections.listRepositories(id) | GET /v1/github-connections/:id/repositories |
| novo.githubConnections.sync(id) | POST /v1/github-connections/:id/sync |
| novo.githubConnections.test(id) | POST /v1/github-connections/:id/test |
Connect GitHub
Start authorization on your server, then send the operator to the returned GitHub URL:
const authorization = await novo.githubConnections.authorize({
name: 'acme-github',
returnUrl: 'https://console.acme.dev/settings/integrations',
});
if (!authorization.url) throw new Error('GitHub install URL was not returned');
// Redirect the operator to authorization.url.
The URL can be forwarded to an organization owner when the initiating user cannot install GitHub Apps. It expires after 30 minutes and can be used once. The callback binds the installation only after GitHub OAuth proves that the consenting user can access it.
Poll by connection id when your product needs to follow the flow outside Novo's console:
const status = await novo.githubConnections.getAuthorization(authorization.id);
switch (status.status) {
case 'completed':
console.log(status.connection.access.repositories);
break;
case 'action_required':
console.log('A GitHub organization owner must approve the installation.');
break;
case 'pending':
console.log('Waiting for GitHub.');
break;
}
getAuthorization never returns the install URL again. Call authorize({ connectionId }) to repair a revoked installation, renew a lost or expired URL, or capture a new user grant while preserving the connection id and existing agent bindings:
const repair = await novo.githubConnections.authorize({
connectionId: authorization.id,
returnUrl: 'https://console.acme.dev/settings/integrations',
});
Connection resource
type GithubConnection = {
id: string;
object: 'github_connection';
name: string;
status: 'pending' | 'action_required' | 'ready' | 'revoked' | 'error';
statusReason: string | null;
account: { login: string; type: 'User' | 'Organization' } | null;
access: {
repositories: string[];
owners: string[];
account: boolean;
};
supportsRepositoryTools: boolean;
supportsAccountTools: boolean;
lastSyncedAt: string | null;
metadata: Record<string, string>;
configVersion: number;
createdAt: string;
updatedAt: string;
};
access.repositories is the subset this connection grants to agents. access.owners bounds repository create and fork destinations. access.account opts into tools that act as the installing person, including gists and repository create or fork.
The initial callback grants the repositories selected on GitHub and adds the installation account to owners. Account tools start off until you explicitly enable them.
Manage repository and account access
Read the installation's live repository selection before expanding the connection grant:
const page = await novo.githubConnections.listRepositories(connection.id);
const selectedOnGitHub = page.data.map((item) => item.repository);
const updated = await novo.githubConnections.update(connection.id, {
access: {
repositories: selectedOnGitHub.filter((repo) => repo.startsWith('acme/')),
owners: ['acme'],
account: true,
},
});
Novo rejects repositories that the installation does not currently select. Repositories added on GitHub appear in listRepositories as grantable and require an explicit update before agents can use them.
sync(id) reconciles installation state, suspension, uninstallation, and repository removals. Sync removes grants that GitHub removed and heals a healthy installation back to ready. It does not automatically grant newly selected repositories.
const synced = await novo.githubConnections.sync(connection.id);
const probe = await novo.githubConnections.test(connection.id);
if (!probe.ok) {
console.log(probe.status, probe.repositories, probe.accountGrant);
}
test(id) performs side-effect-free checks. It verifies the installation, probes every granted repository with a read-only installation token, and refreshes the user grant when account access is enabled.
Grant a connection to an agent
GitHub is a software-development capability, so it always requires a managed workspace with filesystem, shell, and network access. The API tools execute in Novo's hosted worker while the workspace provides a durable working tree for repository inspection, edits, tests, and commits.
const agent = await novo.agents.create({
context: 'Fix issues in acme/app and open reviewed pull requests.',
band: 'core',
workspace: { type: 'managed', filesystem: true, shell: true },
capabilities: {
github: {
connectionId: connection.id,
repositories: ['acme/app'],
git: {
branchPrefix: 'novo/',
baseBranches: ['main'],
},
approval: 'always',
},
},
});
repositories can narrow the connection grant for one agent. It can never add access. git.branchPrefix limits published branch names. git.baseBranches limits pull-request targets.
Filter the family by public tool name when an agent needs a smaller surface:
capabilities: {
github: {
connectionId: connection.id,
allowedTools: [
'github_list_issues',
'github_get_issue',
'github_comment_issue',
],
approval: 'always',
gatedTools: ['github_comment_issue'],
},
}
allowedTools and blockedTools are mutually exclusive and accept names from the 50-tool catalog below. Writes request approval by default. approval accepts once, always, or never. gatedTools narrows an explicit once or always mode to named write tools.
Managed tool family
The App-backed family contains 50 tools. Availability depends on the connection grant, agent filter, account access, and GitHub permissions. Every GitHub-enabled agent has the managed workspace needed for the workspace-git group.
- Repositories and code:
github_get_repository,github_list_branches,github_get_file_content,github_create_branch,github_create_or_update_file,github_create_repository,github_fork_repository,github_list_commits,github_get_commit,github_get_blame,github_search_code,github_search_repositories. - Issues and labels:
github_list_issues,github_get_issue,github_create_issue,github_comment_issue,github_close_issue,github_list_labels,github_add_labels,github_remove_label. - Pull requests:
github_list_pull_requests,github_get_pull_request,github_list_pull_request_files,github_list_pull_request_reviews,github_create_pull_request,github_comment_pull_request,github_get_pull_request_checks,github_get_pull_request_review_threads,github_create_pull_request_review,github_resolve_review_thread,github_merge_pull_request. - Workspace git:
github_checkout,github_checkout_pull_request,github_status,github_push_branch,github_upsert_pull_request. - Actions:
github_list_workflows,github_list_workflow_runs,github_get_workflow_run,github_list_workflow_jobs,github_trigger_workflow,github_cancel_workflow_run,github_rerun_workflow_run. - Gists:
github_list_gists,github_get_gist,github_list_gist_comments,github_create_gist,github_update_gist,github_delete_gist,github_comment_gist.
github_search_repositories reaches beyond granted repositories and appears only when allowedTools names it explicitly. Search results do not grant repository access.
Gist tools and repository create or fork act as the person who installed the App. They appear only when access.account is enabled and the user grant is healthy. Other tools act as the App installation.
Workspace git boundary
Workspace git uses a plain https://github.com/owner/repo.git remote. Novo applies an authorization header to the sandbox network policy only for the duration of github_checkout, pull-request checkout fetches, and github_push_branch. The header is restored in finally; an unproven restore taints and terminates the sandbox.
The credential is absent from shell environment, argv, files, Git config, remote URLs, tool output, and telemetry. Direct remote Git commands have no credential. Local git status, git diff, git add, git commit, and test commands work normally between managed operations.
Push publishes one branch owned by the current thread. Branch-prefix, base-branch, tag, deletion, multi-ref, and ownership checks fail closed before remote mutation. Reviews and merges pin the inspected pull-request head SHA. Merge also checks the owned last-pushed SHA and any configured status/check policy.
Write replay and unknown outcomes
Novo records every GitHub write in the durable external-action journal before dispatch. A completed claim replays its saved result. A definite failure replays its stable error. A run that resumes after dispatch without a durable settlement returns github_action_outcome_unknown.
Treat that result as a reconciliation request. Read the issue, pull request, branch, file, workflow run, or gist that would prove whether the action happened. Retry only after evidence shows it did not happen, using a new tool call. Novo does not silently repeat an ambiguous write.
Lifecycle
pending: the authorize URL is live or the callback has not completed.action_required: GitHub is waiting for an organization owner to approve the installation.ready: the installation is healthy and can materialize its granted tools.revoked: the installation was removed, suspended, or the user grant was revoked. Sync or repair provides the next action.error: Novo could not establish a runnable binding. Repair creates a fresh signed authorization.
Deleting a connection fails with 409 resource_in_use while active agents reference it. The error includes details.inUseByAgentIds. Deleting the Novo resource does not uninstall the GitHub App from the GitHub account.
Trust boundary
Issue bodies, pull-request text, reviews, comments, workflow metadata, search hits, and gist content are untrusted GitHub-authored data. Novo fences those results before the model reads them. Repository file and commit content remains bounded to the granted repository set.
Repository instructions can load automatically when the effective grant is one repository: AGENTS.md, agents.md, CLAUDE.md, or claude.md. A broad multi-repository connection skips automatic instruction loading unless the agent narrows capabilities.github.repositories.