Documentation menu

Providers

GitHub

Uses the Ahena CLI (beta). Install it with npm install -g @ahena/cli, or prefix commands with npx @ahena/cli. See Installation. The dashboard covers projects, connections, Doctor, the Stack Graph, plans and approvals without it.

Overview

Ahena checks the GitHub repository your app is built and deployed from: its visibility, default branch protection, deployment environments, GitHub Actions variables and secret names, webhooks and Pages. It creates environments, sets Actions variables, adds required status checks and reviews through its own ruleset, and creates a webhook, each after you approve the plan. Secret values never go through Ahena: Doctor confirms each declared secret name exists and gives you the gh secret set command for any that don't.

GitHub isn't called by your app at runtime, so there's nothing to generate: Ahena has no ahena generate github. Ahena only talks to GitHub's management API (api.github.com), never to your repository's contents.

Package: @ahena/provider-github · Category: repository · Pinned API version: 2026-03-10 (sent as X-GitHub-Api-Version) Capabilities: repository-inspection, environments, actions-variables, actions-secrets, branch-protection, webhooks, pages

Maturity: verified against a fake of the API; not yet verified against the real service.

Connection

Field Secret Notes
GITHUB_TOKEN yes A fine-grained personal access token (github_pat_…) limited to this one repository (recommended). A classic token (ghp_…) also works, but it reaches every repository you can; Doctor warns about it in production.
GITHUB_OWNER no The user or organization, e.g. leo-labs. Offered from the repositories the token can see.
GITHUB_REPO no The repository name, e.g. leo. Offered from the repositories the token can see.
GITHUB_TOKEN=github_pat_… ahena connect github -e production --set GITHUB_OWNER=leo-labs --set GITHUB_REPO=leo

GitHub App installation tokens aren't supported: they expire after an hour, and refreshing them would mean giving Ahena the app's private key.

Permissions

Create the fine-grained token with Repository access: Only select repositories (just this one) and these repository permissions (Read and write includes Read):

Permission Access Needed for
Metadata Read-only Always included: the repository, rulesets on the default branch
Actions Read-only Listing environments
Administration Read and write Creating environments and the Ahena ruleset; reading classic branch protection and whether Actions is enabled (Read-only is enough if you don't let Ahena change environments or protection)
Environments Read and write Environment variables and environment secret names (Read-only if you don't declare environment variables)
Variables Read and write Repository variables (Read-only if you don't declare any)
Secrets Read-only Repository secret names (GitHub never returns values)
Webhooks Read and write Creating the declared webhook (Read-only if you don't declare one)
Pages Read-only Reporting Pages status

Ahena never needs Contents, Pull requests, Issues or Workflows permissions, and never reads or writes code.

How Ahena checks permissions (permissionCheck: "checked"). Fine-grained tokens don't list their permissions anywhere. When you connect, Ahena asks with reads that can't change anything (a GET per area: environments, Actions settings, variables, secrets, webhooks, Pages, and environment variables inside an existing environment). GitHub answers 2xx when the token has the read permission and 403 ("Resource not accessible by personal access token") when it doesn't, so each read is reported as granted or missing. Write permissions can't be probed without writing, so they're reported as …:write:unknown (or missing when even the read is refused); a missing write shows up as a 403 naming the permission (from GitHub's X-Accepted-GitHub-Permissions header) when you apply. For a classic token Ahena reads the OAuth scopes GitHub reports (X-OAuth-Scopes): repo grants everything above.

Capabilities

Capability Access Verification Changes What Ahena does
repository-inspection read-only Visibility, default branch, archived, Actions enabled.
environments writable read-back CONFIRMATION_REQUIRED Creates the declared GitHub environment; reports its protection rules and deployment branch policy.
actions-variables writable read-back CONFIRMATION_REQUIRED Sets repository and environment variables to the declared values.
actions-secrets manual existence only Checks declared secret names exist (with when they were updated); gives the gh secret set command for missing ones.
branch-protection writable read-back CONFIRMATION_REQUIRED Adds required status checks and approving reviews on the default branch through the ruleset Ahena: default branch.
webhooks writable read-back CONFIRMATION_REQUIRED Creates the declared webhook (JSON, TLS verified) with a new signing secret.
pages read-only Reports Pages status and whether HTTPS is enforced.

Refused outright: changing repository visibility; deleting, archiving, renaming or transferring the repository; deleting environments, variables, rulesets or webhooks; changing environment protection rules; removing or lowering required checks or reviews, or disabling a ruleset; editing classic branch protection or rulesets Ahena didn't create; changing existing webhooks or creating one that skips TLS verification; Actions variables whose names look like secrets; reading secret values (GitHub never returns them).

Network: only api.github.com (enforced). The webhook route is probed through Ahena's credential-free probe. ahena lock records visibility, default branch, environments, variable values, secret names (never values), default-branch protection and webhooks for drift.

Configure

repository: {
  provider: "github",
  owner: "leo-labs",
  repo: "leo",
  variables: { NODE_VERSION: "22" },        // repository-level Actions variables
  secrets: ["TURBO_TOKEN"],                  // repository-level secret NAMES that must exist
  environments: {                            // keyed by Ahena environment
    production: {
      name: "production",                    // the GitHub environment (defaults to the key)
      variables: { API_URL: "https://api.leo.app" },
      secrets: ["DEPLOY_TOKEN"],
    },
    staging: { variables: { API_URL: "https://api.staging.leo.app" } },
  },
  protection: { requiredChecks: ["ci"], requiredReviews: 1 },        // default branch
  webhook: { production: { url: "https://leo.app/api/webhooks/github", events: ["push", "pull_request"] } },
},

Each Ahena environment's GitHub connection manages the GitHub environment declared for it, plus the repository-level settings (variables, protection, webhook). Repository-level settings are the same in every environment, so applying them from a second environment changes nothing. webhook may be a single value or keyed by environment. Declaring environments.production makes Doctor require default-branch protection.

Change Classification Notes
Create a GitHub environment CONFIRMATION_REQUIRED Created with no protection rules; add reviewers or deployment branches in GitHub (Doctor reminds you for production). Never sent for an environment that exists.
Set an Actions variable (repository or environment, production included) CONFIRMATION_REQUIRED The diff shows old and new values. Variables not in ahena.config.ts are left alone.
Create the ruleset Ahena: default branch CONFIRMATION_REQUIRED Targets the default branch; adds required_status_checks and pull_request rules. Created only when existing protection (any ruleset, any level, or classic protection) doesn't already require them.
Extend that ruleset CONFIRMATION_REQUIRED Adds missing checks and raises the review count; keeps every existing rule and setting, and sets it back to active if it was disabled (the diff says so). Refused at apply if the ruleset changed after the plan.
Create the webhook CONFIRMATION_REQUIRED Idempotent by URL: planned only when no webhook has that URL, and re-checked right before creating. A random signing secret is stored encrypted as GITHUB_WEBHOOK_SECRET.
Set an Actions secret MANUAL gh secret set <NAME> --repo <owner>/<repo> [--env <environment>] (prompts for the value).

Ahena never proposes removals: asking for fewer checks or reviews than GitHub already requires proposes nothing. Archived repositories are refused (GitHub rejects changes to them).

Why secrets are manual: GitHub only accepts secrets encrypted with libsodium sealed boxes (X25519 + XSalsa20-Poly1305), which WebCrypto doesn't provide, and plans never carry secret values. So Ahena doesn't send secret values to GitHub; it checks names and you set values with the GitHub CLI.

Limitations

  • Secret values can't be read back: GitHub never returns them, so Ahena verifies existence (and the last-updated date in ahena inspect), never content.
  • Ahena doesn't set secret values (see above) or Dependabot/Codespaces secrets.
  • Environment protection rules (required reviewers, wait timers, deployment branch policies) are reported, not changed.
  • Rulesets and branch protection on private repositories need GitHub Pro, Team or Enterprise; on GitHub Free, GitHub refuses the ruleset (Ahena shows GitHub's message).
  • Classic branch protection is read (with Administration read) but never written.
  • Organization-level variables, secrets and rulesets are counted where GitHub reports them on the repository (rulesets on the default branch) but never managed.
  • Webhook events of an existing webhook aren't changed; Doctor lists missing events.
  • GitHub App installation tokens aren't supported.
  • Nothing is deleted: removing something from ahena.config.ts leaves it in GitHub.

Manual steps

  1. Set secret values: gh secret set <NAME> --repo <owner>/<repo> (add --env <environment> for environment secrets). Doctor prints the exact commands.
  2. Protect the production environment in GitHub (Settings › Environments › production: Deployment branches "Protected branches only" and/or Required reviewers).
  3. Organization repositories: an owner may need to approve the fine-grained token (Organization settings › Personal access tokens › Pending requests).
  4. Regenerate the token before it expires (Doctor warns 14 days ahead) and reconnect.

Doctor checks

Id Severity Meaning
github.token PASS / FAIL The token authenticates (401 → reconnect).
github.token.expiration PASS / INFO / WARNING / FAIL From GitHub-Authentication-Token-Expiration: WARNING within 14 days, FAIL within 3; INFO when GitHub reports no expiry.
github.token.kind INFO / WARNING A classic token (WARNING in production): replace with a fine-grained one.
github.repository PASS / FAIL The repository is reachable (GitHub answers 404 for repositories the token can't see).
github.repository.match FAIL The connection is for a different repository than ahena.config.ts names.
github.repository.archived WARNING Archived: nothing can be applied.
github.actions PASS / WARNING GitHub Actions is enabled for the repository.
github.protection PASS / FAIL Production declared: the default branch has some protection (rulesets at any level, or classic).
github.protection.checks PASS / FAIL Declared required checks and review count are in force.
github.environment PASS / FAIL The declared GitHub environment exists (with its protection rules as evidence).
github.environment.protection WARNING The production environment has no protection rules or branch policy.
github.variables / github.environment.variables PASS / WARNING / FAIL Declared variables exist (FAIL) and match (WARNING).
github.secrets / github.environment.secrets PASS / FAIL Declared secret names exist; MANUAL gh secret set steps otherwise.
github.secrets.values INFO Not verified: secret values (GitHub never returns them).
github.webhook PASS / WARNING / FAIL Webhook to the declared URL exists, is active, and has the declared events.
github.webhook.endpoint PASS / WARNING / FAIL Unsigned-request probe to your route.
github.secret.github_webhook_secret PASS / FAIL The webhook signing secret is stored in this environment (checked by name).
github.pages INFO / WARNING Pages status; WARNING when a custom domain doesn't enforce HTTPS.
github.config FAIL repository in ahena.config.ts isn't valid.

A check the token can't read is reported as WARNING "Not verified: … (the token can't read it)" with the permission to add, never as passing.

Set up step by step

  1. Create the token. On GitHub: your avatar › Settings › Developer settings › Personal access tokens › Fine-grained tokens › Generate new token (https://github.com/settings/personal-access-tokens/new). Choose the resource owner (the organization that owns the repository), an expiration, and Only select repositories → your repository.

  2. Grant the minimum permissions from the table above: Metadata (read), Actions (read), Administration, Environments, Variables, Webhooks (read and write, or read-only for what you don't let Ahena change), Secrets (read), Pages (read). Generate the token and copy it.

  3. Connect:

    GITHUB_TOKEN=github_pat_… ahena connect github -e production --set GITHUB_OWNER=… --set GITHUB_REPO=…
  4. Verify the connection: ahena inspect github -e production shows the repository, environments, variables, secret names, protection and webhooks. The connection's granted scopes list each permission as granted or :unknown.

  5. Run Doctor: ahena doctor -e production.

  6. Plan: ahena plan -e production (all providers), or ahena configure github -e production for GitHub alone. Review each action; every change is CONFIRMATION_REQUIRED.

  7. Apply: ahena apply -e production. Changes that matter are approved in the dashboard. If a webhook was created, its signing secret is stored as GITHUB_WEBHOOK_SECRET (ahena env reveal production GITHUB_WEBHOOK_SECRET to see it).

  8. Verify: Ahena re-plans after applying; an empty plan means GitHub shows the desired state. Then set any missing secrets with the gh secret set commands Doctor prints, and run ahena doctor -e production again. ahena lock records the state for ahena drift.

  9. Troubleshooting:

    Symptom Cause Fix
    GitHub rejected the token Expired, revoked or mistyped Regenerate it and reconnect.
    The token can't see owner/repo (404) Repository not selected for the token, wrong name, or the organization hasn't approved the token Edit the token's repository access; ask an org owner to approve it.
    403 … needs variables=write The token lacks that permission Edit the token and grant it (the permission named in the message).
    Upgrade to GitHub Pro or make this repository public Rulesets on a private repository on GitHub Free Upgrade the plan, or protect the branch another way.
    the ruleset … changed after this plan was made Someone edited Ahena: default branch Run ahena plan again.
    rate limiting this token GitHub's rate limit Wait until the time shown and retry.
    github.secrets FAIL after setting a secret Set at the wrong level Repository secrets: no --env; environment secrets: --env <name>.
  10. Disconnect and revoke: ahena disconnect github -e production removes the stored token. Then delete the token at https://github.com/settings/personal-access-tokens (classic tokens: https://github.com/settings/tokens).

Disconnect behavior

Removes the connection and the token Ahena stored; Ahena can't revoke a personal access token itself. Delete it at https://github.com/settings/personal-access-tokens. Environments, variables, secrets, the Ahena ruleset and webhooks stay in GitHub.