DOCS · GUIDES
Connect a repository
The three ways in — a local path, a GitHub App installation, or a GitLab token — and what each one lets specd do.
On this page
specd never copies your repository. It records which one, and reads it when it needs to. What differs between the three connection modes is only how it reads and writes.
Choosing a mode
| Mode | Reads | Writes | Use it when |
|---|---|---|---|
| Local path | From disk | A local branch you push yourself | Evaluating specd, or working on a repository that must not leave the machine. |
| GitHub App | Repository-scoped API token | Branch + pull request | GitHub-hosted work. This is the recommended production path. |
| GitLab access token | GitLab API | Branch + merge request | gitlab.com or self-managed GitLab. |
Local path
The simplest mode, and the one the seeded fixture repository uses. Register the current directory from the CLI:
specd connect .Code stays on your machine. In place of a merge webhook you get an "I merged it" button in the app, so adoption and delivery are still explicit events rather than assumptions.
Where origin points at github.com or gitlab.com and that host's CLI (gh, glab) is installed and signed in on the same machine, setup and build branches are pushed and opened as real pull requests through your account. The CLI is checked before anything is pushed, so a repository specd cannot open a review on is never published to. Set SPECD_LOCAL_OPEN_PR=0 to keep everything local; the branch is committed either way.
For a self-managed GitLab or GitHub Enterprise, that is not enough: specd refuses to guess what software a host runs from its URL, and the host's CLI is often not on a corporate machine. So the local step asks which host to open reviews on — and for GitLab, that is usually all it needs.
The instance URL is derived from your repository's origin, so it is normally left blank. Set it only for an instance served from a subpath (https://host/gitlab), plain http, or a non-standard API port — and give the instance root, never a group or project page. Where the two readings are genuinely ambiguous, specd asks the instance rather than guessing.
GitHub — as an App, not a personal token
specd connects to GitHub as a GitHub App. That is a deliberate choice over a personal access token, and it is worth understanding what it buys:
- Repository-scoped, not account-scoped
- The installation covers the repositories you selected. A PAT carries whatever its owner can reach.
- Tokens that expire within the hour
- Installation tokens are minted per operation and are short-lived, so a leaked token has a small blast radius.
- Three permissions, not a checklist
contents:writeto push a branch,pull_requests:writeto open the pull request,metadata:readbecause GitHub requires it. Nothing else is requested.- It is not tied to a person
- A PAT stops working when its owner leaves. An App installation belongs to the organisation.
With the API running, registering the App is one click:
open http://localhost:4000/api/github/app/registerThe full walkthrough, including the by-hand path and local webhook delivery, is in GitHub.
GitLab
gitlab.com and self-managed, connected with a personal or group access token — GitLab has nothing App-shaped to install. Same adapter interface, same branch-and-merge-request write path, and the same fail-closed webhook rule using the mechanism GitLab actually offers (token echo, compared in constant time). See GitLab.
What connecting does not do
- It does not clone your repository into specd's storage. There is no copy to leak.
- It does not grant push access to a default branch. Agents open pull requests; they never push to
main. - It does not start anything. Grounding is the next station, and you trigger it.
Merging is adopting
Once a repository is connected, merges are the events specd listens for. The setup branch merged means adoption recorded and knowledge/ indexed; a spec/… branch merged means the spec is delivered and re-indexed; anything touching knowledge/ on the default branch triggers a re-index. Closing a pull request without merging changes nothing, on purpose.