How to Adopt Nebuko
Nebuko runs a multi-agent workflow on your issues and pull requests: plan → context → implement → review, with human approval gates, driven entirely from issue comments. This page takes a repository from zero to a first run.
For the underlying concepts first, read architecture.md and workflows.md.
1. What you get
Section titled “1. What you get”Adding Nebuko to a repository gives you:
- Orchestration on your own issues and PRs — start a workflow
(
/nebuko run workflow --name default), or mention an agent directly (@@architect,@@context,@@dev,@@review) from an issue body or comment. - Nebuko branding in the thread — every orchestrator comment is prefixed
with the theme-adaptive Nebuko logo mark (a
<picture>block servingmark-light.svg/mark-dark.svg), exactly as on this project’s own issues. - Branded commit author — commits and PR branches created by the
implementation step are authored as
nebuko <[email protected]>. - AI-attribution signature — agent answers end with a short “generated by an AI agent … on behalf of <user>” line. This line is emitted by the agent prompt (best-effort), not enforced by the engine.
- Coming —
nebuko[bot]actor identity — the App’s own avatar andnebuko[bot]actor name on comments. The App installation itself is live; the actor identity is tracked separately.
Nebuko runs as a GitHub App installation by default: short-lived, per-repository installation tokens minted from the OIDC broker. A fine-grained
GH_PATis only a fallback (section 4).
2. Prerequisites
Section titled “2. Prerequisites”-
A caller repository on GitHub. Nebuko’s reusable workflow is hosted in the public
nebuko/nebukorepository — GitHub requires the called repository to be public for a reusable workflow to be used from another repository. Your own (caller) repository can be public or private. -
A runner. GitHub-hosted
ubuntu-latestby default; you can point the jobs at your own runners instead (section 6). -
The Nebuko App installed on your repository — install it from https://github.com/apps/nebuko/installations/new and grant it only the target repositories. The App requests Contents, Pull requests and Issues read and write — exactly what the engine uses to clone, push branches, open PRs and comment (section 8).
No GitHub token to create:
nebuko initdetects the installation and the workflow mints a short-lived installation token from the OIDC broker. If you cannot install the App, PAT mode is the fallback (section 4). -
An agent config at
.nebuko/agents/agent-config.yamlin your repository — mandatory for now (section 3, step 1). -
A provider API key — one of
OPENROUTER_API_KEY,COMMANDCODE_API_KEY,OPENCODE_GO_API_KEY,MISTRAL_API_KEYorLOCAL_VLLM_API_TOKEN, matching theapi_token_envof the provider in your agent config (section 5).
3. The entrypoint (copy-paste)
Section titled “3. The entrypoint (copy-paste)”Adopting Nebuko is three commits’ worth of work, in this order: the agent config, the workflow, then the secrets that config needs.
Step 0 — or let nebuko init do steps 1–3 (recommended)
Section titled “Step 0 — or let nebuko init do steps 1–3 (recommended)”Install the nebuko binary from the release whose tag you want to pin, then run
the interactive setup:
gh release download -R nebuko/nebuko -p 'nebuko-linux-amd64' -O ~/.local/bin/nebukochmod +x ~/.local/bin/nebukonebuko initnebuko init asks for the target repository (or organization), where the
secrets should live (repository level by default, or organization level), the
LLM provider and the runner labels, then:
- generates
.nebuko/agents/agent-config.yamlfrom the chosen provider’s template at the pinned tag; - generates
.github/workflows/nebuko.yaml, pinned to that tag and passing only the secrets the provider needs; - opens a branch and a pull request with those two files — no secret value is ever committed;
- asks for each secret value with hidden input and stores it with
gh secret setat the level you chose (--org <name>skips the prompt).
Secrets already stored at the chosen level (the provider key, or the PAT
fallback GH_PAT) are not touched: init reports OPENROUTER_API_KEY already setup in acme [ skipping, use --force to update value ] and does not ask for their values. This is what
makes it safe to run init for a second repository against organization-level
secrets. Pass --force to rewrite them.
App detection. Before writing anything, init probes the Nebuko App
installation for the target repository: an installation means App mode — no PAT
prompt — while a 404 (without --pat) makes init stop and print the App
install link (https://github.com/apps/nebuko/installations/new); pass
--pat (alias --assume-pat) to select the PAT fallback, which prompts for
GH_PAT (fallback mode only). In App mode init writes the caller workflow;
the workflow reads the broker endpoint from the optional NEBUKO_BROKER_URL
repository variable and falls back to the published broker when it is unset. See
nebuko-init.md.
It uses your own gh authentication to create the branch, the pull request
and the secrets. init needs the GitHub CLI (gh) on your PATH — it stops with an install hint before doing anything else when it is
missing. Pushing .github/workflows/* needs the workflow
scope — init checks for it up front, before asking for any secret value, and
tells you to run gh auth refresh -s workflow when it is missing. Once the
setup succeeds it prints the next steps: review and merge the pull request, and
comment @@dev Hello Nebuko! on an issue in the target repository.
Release assets: nebuko-linux-amd64, nebuko-macos-arm64, nebuko-macos-amd64,
nebuko-windows-amd64.exe.
Flags for scripted installs: --repo, --org, --provider, --runs-on,
--pin-ref, --template-dir, --non-interactive (secret values from
NEBUKO_SECRET_<NAME>), --force. Re-running init on an initialized
repository asks before overwriting the generated files, and refuses in
--non-interactive mode unless --force is given; secrets that already exist
at the chosen level are skipped (with a note) unless --force is given.
The manual steps below remain the fallback — and the reference for exactly what
init generates.
Step 1 — Create your agent config (mandatory)
Section titled “Step 1 — Create your agent config (mandatory)”Every adopting repository must provide its own
.nebuko/agents/agent-config.yaml. This is mandatory for now: it keeps the
choice of providers, models, prompts and tools on your side rather than
inherited from whatever Nebuko ships. Either write one (section 5,
configuration.md) or copy a shipped profile from
defaults/agents/ and adapt it:
| Profile | Provider | Secret it reads (api_token_env) |
|---|---|---|
agent-config-openrouter.yaml | OpenRouter | OPENROUTER_API_KEY |
agent-config-commandcode.yaml | CommandCode | COMMANDCODE_API_KEY |
agent-config-opencode.yaml | OpenCode Go | OPENCODE_GO_API_KEY |
agent-config-mistral.yaml | Mistral | MISTRAL_API_KEY |
mkdir -p .nebuko/agentscurl -fsSL -o .nebuko/agents/agent-config.yaml \ https://raw.githubusercontent.com/nebuko/nebuko/v0.0.2/defaults/agents/agent-config-openrouter.yamlCommit the file. Note every api_token_env it declares (one per provider):
each one is a secret you must pass in step 2 and create in step 3.
Step 2 — Add the workflow
Section titled “Step 2 — Add the workflow”Create .github/workflows/nebuko.yaml in your repository, point
agent_config_path at your config, and pass the secrets that config reads
(here, the OpenRouter profile):
name: Nebuko
on: issues: types: [opened, edited, assigned, closed] issue_comment: types: [created]
permissions: contents: write pull-requests: write issues: write id-token: write actions: write
jobs: agent: with: agent_config_path: ".nebuko/agents/agent-config.yaml" secrets: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} # = api_token_env in your configOn another provider, replace OPENROUTER_API_KEY with the secret named by your
config’s api_token_env (e.g. COMMANDCODE_API_KEY); list one line per
provider your config uses.
Who can trigger a run
Section titled “Who can trigger a run”How to use: only comments authored by a user with OWNER, MEMBER, or
COLLABORATOR association start a run; anyone else’s comments are ignored at
the caller workflow level. Comments authored by nebuko[bot] pass the caller
gate, but the reusable workflow only runs them when they carry a continuation
marker (<!-- workflow-continue -->, <!-- bot-trigger: or "function_call"),
and never when they carry the terminal marker (anti-loop guard). issues events (issue-body @@nebuko mentions, and
the cleanup that runs when an issue is closed) are gated by the reusable
workflow as before.
How it works: the installed caller workflow’s agent job carries a
job-level if: gate that is evaluated before the reusable workflow is
invoked, so a non-member comment never spins up a caller run. The reusable
workflow’s own caller check (its CALLER_AUTH input) remains a second,
independent layer — defense in depth. The caller workflow’s permissions:
block includes id-token: write, matching the reusable workflow’s
requirement.
If nebuko init installed your caller workflow earlier, re-run
nebuko init to refresh it with the gate (init asks before overwriting).
Step 3 — Add the secrets
Section titled “Step 3 — Add the secrets”Under Settings → Secrets and variables → Actions, create a repository secret
for every provider key your config’s api_token_env entries reference. A
provider whose secret is missing cannot reach its LLM.
No GitHub token is needed in App mode. GH_PAT is a PAT fallback only
(section 4): add it when the App is not installed.
Inputs
Section titled “Inputs”Every input is optional; defaults are shown. Pass them under with:.
| Input | Type | Default | Purpose |
|---|---|---|---|
agent_config_path | string | agent-config.yaml | Path to your agent config — set it to .nebuko/agents/agent-config.yaml (step 1, section 5). A bare file name is looked up in .nebuko/agents/. |
runs_on | string | ubuntu-latest | Runner label used by the jobs (section 6). |
circuit_breaker_threshold | number | 25 | Bot-marker count that trips the circuit breaker (per-issue override: /nebuko update circuit-breaker --value <n>). |
pin_comment | boolean | false | Pin every agent-step comment for verbatim context injection. |
history_comment_count | number | 5 | Most recent comments injected into each agent call. |
python_version | string | 3.13 | Python version installed on the runner. |
enable_uv_cache | boolean | true | Enable the uv dependency cache. |
prompt_body | string | "" | Prompt/body text for programmatic (workflow_call) callers. |
checkout_ref | string | "" | Explicit ref (SHA, tag, or branch) for the workspace checkout. When empty, the checkout follows the workflow’s own commit if the workflow is checking out its own repo (issue #862), else the event default. |
CALLER_AUTH | string | on | When on (default), only OWNER/MEMBER/COLLABORATOR authors may trigger from issues/comments; set to off to allow anyone. |
EXTRA_SECRET_1_NAME … EXTRA_SECRET_9_NAME | string | "" | Env var name materialized from the matching EXTRA_SECRET_n_VALUE secret — use for MCP tokens (section 3.1). |
Secrets
Section titled “Secrets”| Secret | Required | Purpose |
|---|---|---|
GH_PAT | fallback only | Fine-grained PAT for git push, issue/PR API calls and comments — PAT fallback only (section 4). With the App installed (and id-token: write granted) the token is minted by the OIDC broker instead. |
OPENROUTER_API_KEY | optional | Provider key (the api_token_env referenced by defaults/agents/agent-config-openrouter.yaml). |
COMMANDCODE_API_KEY | optional | CommandCode subscription provider. |
OPENCODE_GO_API_KEY | optional | OpenCode Go provider. |
MISTRAL_API_KEY | optional | Mistral provider. |
LOCAL_VLLM_API_TOKEN | optional | Local vLLM provider. |
BRAVE_API_KEY | optional | Brave Search MCP token (the named alias of the slot pattern in section 3.1). |
EXTRA_SECRET_1_VALUE … EXTRA_SECRET_9_VALUE | optional | Values materialized into the env vars named by the EXTRA_SECRET_n_NAME inputs (section 3.1). |
Each provider “optional” above becomes required as soon as your agent
config’s api_token_env references it.
The workflow also reads one repository variable (not a secret):
| Variable | Required | Purpose |
|---|---|---|
NEBUKO_BROKER_URL | App mode | OIDC token broker the workflow exchanges its GitHub OIDC token for a short-lived installation token. nebuko init sets it; see section 8. |
3.1 Wiring extra secrets (MCP tokens)
Section titled “3.1 Wiring extra secrets (MCP tokens)”MCP servers need their own private tokens. You can hand the engine any such
token without editing the reusable workflow: add a repository secret, then point
one of nine generic slots at the env var name the MCP expects. The with: input
carries the name, the secrets: block carries the value:
jobs: agent: with: agent_config_path: ".nebuko/agents/agent-config.yaml" EXTRA_SECRET_1_NAME: "BRAVE_API_KEY" # env var the MCP reads secrets: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} EXTRA_SECRET_1_VALUE: ${{ secrets.BRAVE_API_KEY }}- Slot
npairsEXTRA_SECRET_n_NAMEwithEXTRA_SECRET_n_VALUE; indices run1–9. - Empty slots are ignored; on a duplicate name the lowest populated slot wins.
- Names reserved for the runner (
GITHUB_*,ACTIONS_*,RUNNER_*,PATH,HOME,GITHUB_TOKEN,GH_TOKEN) are dropped, so a slot cannot clobber engine internals. - Precedence: a slot value (written to
$GITHUB_ENV) overrides a same-named job-level alias such as the namedBRAVE_API_KEY; a name the engine step binds explicitly (LOCAL_VLLM_API_TOKEN) shadows$GITHUB_ENV, so mapping a slot onto it has no effect.
Adding an MCP token is therefore a repository secret plus two lines in your
entrypoint — never an edit to nebuko-workflow.yaml. See
ADR-0008 for the rationale.
4. Authentication — GitHub App (default)
Section titled “4. Authentication — GitHub App (default)”Nebuko acts through a GitHub App installation token by default. Nothing long-lived is stored in your repository.
How it works
- Your caller workflow grants
id-token: write(step 2) and requests a GitHub OIDC token for the job. - The workflow posts that OIDC token to the OIDC broker named by the
NEBUKO_BROKER_URLrepository variable (nebuko initsets it). - The broker verifies the OIDC token, confirms the Nebuko App is installed on your repository, and returns a short-lived installation token scoped to that repository.
- The engine uses that installation token to clone, push branches, open/update PRs and post comments.
The installation token expires with the job, so there is nothing to rotate and no credential to leak from your repository. Permissions, key custody and how to suspend or uninstall the App are in section 8.
PAT fallback
Section titled “PAT fallback”PAT mode is selected when the App is not installed on the repository (detection
returns 404) or when you pass --assume-pat to nebuko init. Nebuko then acts
entirely through the GH_PAT you supply: it clones, pushes branches,
opens/updates PRs, and posts comments as the token’s owner. The token is passed
to the reusable workflow as a secret and is never written to the checkout.
What the token must be able to do
- Push branches and read contents in the target repository.
- Read and write issues, comments, and pull requests.
Security notes
- Use a fine-grained token, not a classic one. A fine-grained PAT is limited to a single user or organization and can be narrowed to specific repositories.
- Set an expiration; rotate before it lapses. GitHub removes tokens unused for a year.
- Organization owners can require approval for fine-grained tokens that can access org resources — expect an approval step on org repositories.
- Prefer passing the narrowest token that covers the repositories you point Nebuko at.
Branding — the logo-mark comment prefix, the branded nebuko commit author,
and the prompt-emitted AI-attribution line (section 1) are the same in both
modes. The nebuko[bot] actor identity requires the App and is not available
yet.
5. Provider configuration — ready-to-paste configs
Section titled “5. Provider configuration — ready-to-paste configs”Commit your agent config at .nebuko/agents/agent-config.yaml — mandatory
for now (section 3, step 1) — and set agent_config_path to that path. Other
config artifacts (prompts, skills, workflows) still resolve through the layered
lookup, anchored at the caller’s workspace ($GITHUB_WORKSPACE when set, the
working directory otherwise — not the engine checkout): a repo override under
.nebuko/ first, then the copy shipped in
defaults/. The file is layered providers → llms → agents; the full schema
is in configuration.md. The shipped defaults/prompts/
need no local copies, so a config like the one below is the whole minimal
setup.
OpenRouter
Section titled “OpenRouter”.nebuko/agents/agent-config.yaml:
providers: openrouter: api_url: "https://openrouter.ai/api/v1" api_token_env: "OPENROUTER_API_KEY"
llms: my-model: model_path: "openrouter/deepseek/deepseek-chat-v3-0324" provider: "${providers.openrouter}" default_temperature: 0.7
agents: architect: model: "${llms.my-model}" prompt_file: "defaults/prompts/architect.md" tools: ["think", "finish"]
dev: model: "${llms.my-model}" prompt_file: "defaults/prompts/dev.md" tools: ["terminal", "file_editor", "task_tracker", "think", "finish"]
review: model: "${llms.my-model}" prompt_file: "defaults/prompts/review.md" tools: ["think", "finish"]Wire the matching secret in your entrypoint (section 3):
secrets: OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
prompt_filematters for its file name only: Nebuko resolves each agent’s prompt through the same layered lookup (.nebuko/prompts/override → shippeddefaults/prompts/), so the shipped prompt is used unless you add an override with that name.
Local vLLM
Section titled “Local vLLM”For a self-hosted vLLM endpoint, inline the URL and point api_token_env at
the token secret:
providers: local_vllm: api_url: "http://192.168.1.64:31728/v1" api_token_env: "LOCAL_VLLM_API_TOKEN"
llms: qwen: model_path: "hosted_vllm/qwen-3-6-27b" provider: "${providers.local_vllm}" default_temperature: 0.7
agents: architect: model: "${llms.qwen}" prompt_file: "defaults/prompts/architect.md" tools: ["think", "finish"]Wire the LOCAL_VLLM_API_TOKEN secret in your entrypoint:
secrets: LOCAL_VLLM_API_TOKEN: ${{ secrets.LOCAL_VLLM_API_TOKEN }}api_url may also be the name of an environment variable holding the URL
(resolved at config-load time, mirroring api_token_env) — e.g.
api_url: "LOCAL_VLLM_API_URL". That form only helps when the variable is
actually present on the runner, so inline the URL unless your runner sets it.
CommandCode / subscription providers. See subscription-plans.md for the provider stanza and a working
agent-config-commandcode.yamlexample.
6. Choosing runs_on
Section titled “6. Choosing runs_on”The workflow runs on GitHub-hosted runners by default. Select a runner with the
runs_on input:
with: runs_on: "ubuntu-latest" # default — GitHub-hosted # runs_on: "self-hosted" # your own runner # runs_on: "[self-hosted, linux, x64]" # label combination- GitHub-hosted (
ubuntu-latest) — nothing to install; the workflow sets up Python anduvitself. Billed by GitHub per the repository’s plan. - Self-hosted — use the default
self-hostedlabel or a combination such as[self-hosted, linux, x64](see GitHub’s runner labels). Runners are consumer-managed: you own their availability, patching, and network access to any private vLLM endpoint.
7. Pinning and upgrades
Section titled “7. Pinning and upgrades”The entrypoint above pins the release tag @v0.0.2 (the default version).
Pin a release tag for reproducibility, a full commit SHA for the strongest
guarantee, or @main to track unreleased changes:
# or: .../nebuko-workflow.yaml@<full-commit-sha> # or: .../nebuko-workflow.yaml@main # latest, unreleasedTo upgrade, bump the tag in your entrypoint and re-fetch your agent config profile from the same tag if you started from a shipped one.
GitHub Actions does not follow redirects for reusable workflows: if the called repository or workflow path is ever renamed, callers pinned to the old name fail. Pinning to an immutable ref is what keeps an upgrade deliberate.
8. Trust and security (the GitHub App)
Section titled “8. Trust and security (the GitHub App)”Permissions. The App requests only what the engine uses — the same three
repository permissions as the caller workflow’s permissions: block:
| Permission | Level | Used for |
|---|---|---|
| Contents | read and write | clone the repository, push branches |
| Pull requests | read and write | open and update PRs |
| Issues | read and write | read triggers, post comments, persist WORKFLOW_STATE |
Nothing else: no administration, no packages, no members, no workflows. The
workflow additionally requests id-token: write from GitHub — that is the OIDC
identity token, not a repository permission, and it is what lets the job ask the
broker for an installation token.
Where the key lives. The App’s private key stays with the App owner and
the broker — it is never in your repository, your secrets or your workflow. Your
repository holds only the NEBUKO_BROKER_URL variable (a URL, not a
credential). The installation token the broker returns is short-lived and
expires with the job, so there is nothing to rotate and no long-lived credential
to leak.
Review, suspend, uninstall.
- Organization repositories: Settings → Third-party Access → GitHub Apps → Configure.
- Personal repositories: Settings → Integrations → Applications → Installed GitHub Apps → Configure.
- Change repository access at any time from the same screen — the App only sees the repositories you granted, and removing one revokes its access on the next token request.
- Suspend is a temporary block: the App keeps its installation and settings, but every token request fails until you unsuspend it. Nebuko runs stop working while suspended.
- Uninstall is permanent: the installation and its settings are removed. To
use Nebuko again you reinstall the App and re-run
nebuko init.
If you cannot install an App at all (policy, or a repository the App is not installed on), use the PAT fallback in section 4 and keep that token narrow and expiring.
9. Next steps
Section titled “9. Next steps”- getting-started.md — the first run, in detail.
- configuration.md — the full
agent-config.yamlschema. - features.md — human commands, safety mechanisms, metrics.
- subscription-plans.md — run on a subscription plan.