GitHub repository access
SecondShift reads repositories and syncs their committed .secondshift.yaml
files and skills through a GitHub App or a fine-grained personal access token.
Both are separate from GitHub sign-in.
The App is the better choice for a team. Each step gets its own short-lived token, limited to one repository and to the access that step needs. A token suits a single machine, or an organization where you cannot install the App. See Use a personal access token.
Register the App
Section titled “Register the App”Register an App under your GitHub account or organization in Settings → Developer settings → GitHub Apps → New GitHub App.
- Homepage URL: your public SecondShift HTTPS address.
- Callback URL:
https://secondshift.example.com/github/callback/. - Enable Request user authorization (OAuth) during installation.
- Leave the setup URL empty. Installation uses the authorization callback.
- Disable webhooks for now. The worker polls for changes instead.
- Repository permissions: Contents: Read-only. GitHub includes metadata read access automatically. No organization or account permissions are needed. For draft pull requests, see below. For GitHub tools in jobs, see GitHub tools.
- Choose whether installation is limited to your account or available to others.
Generate a client secret and a private key. Record the App ID, URL slug, and client ID. GitHub’s registration guide explains these options.
Configure SecondShift
Section titled “Configure SecondShift”Set these variables on web and worker only, using private environment files or your deployment’s secret manager:
SECONDSHIFT_GITHUB_APP_ID=123456SECONDSHIFT_GITHUB_APP_SLUG=your-secondshift-appSECONDSHIFT_GITHUB_APP_CLIENT_ID=Iv1.exampleSECONDSHIFT_GITHUB_APP_CLIENT_SECRET=replace-with-client-secretSECONDSHIFT_GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"The private key accepts literal \n escapes or real newlines. Keep it out of Git.
SECONDSHIFT_PUBLIC_URL must match the public origin used in the callback URL.
Existing GitHub login OAuth credentials are independent of these App credentials.
The web process imports repositories through the internal runner API. Newly
generated container role files include its runner URL/token. For existing role
files, copy SECONDSHIFT_RUNNER_SERVICE_URL and
SECONDSHIFT_RUNNER_SERVICE_TOKEN from worker.env into web.env. Keep the
runner API private. Neither web nor worker needs the Docker socket or runner DB.
Imports are synchronous. Set SECONDSHIFT_HTTP_TIMEOUT_SECONDS=300 for web and
180 for runner, and allow at least 300 seconds through any reverse proxy.
Restart web, worker, and runner after updating their settings. Apply migrations
with the usual python manage.py migrate before starting the updated services.
Connect and run
Section titled “Connect and run”- As a workspace owner, open Repositories → Connect GitHub.
- Authorize your GitHub user. If no installation exists, SecondShift sends you to install the App. Select only the repositories it should read. Use Manage GitHub access to install it for another account or change repository access on an existing installation.
- Back in SecondShift, choose a repository and branch using the searchable selectors. Its default branch is selected automatically.
- Import it. The committed manifest and every referenced skill must be valid. Review job capabilities, connect a model provider and any required services, then enable the repository and its scheduled jobs.
- Start a manual run and verify its report, artifacts, and delivery before relying on the schedule.
Only installations returned by GitHub for the authorizing user are linked to the current workspace. A callback’s installation ID is never treated as proof. Available repositories are listed from those installations. Repository import requires a workspace owner. Owners and admins can resync afterward.
Resync fetches the selected branch again. Enabled repositories with automatic
sync turned on are checked every 300 seconds by the worker. Set
SECONDSHIFT_REPOSITORY_SYNC_INTERVAL_SECONDS to change this interval. Set it to 0 to
disable polling. Changed capabilities continue through the existing approval
process. A failed sync preserves the last good revision and appears in repository
validation. It does not silently replace a working job definition.
Check access
Section titled “Check access”On Connections, choose Check access in a GitHub connection’s menu. It checks three things:
- The credential still works. For a token, this also refreshes its expiry date.
- Every added repository is still reachable.
- What each feature can do:
- Sync needs read access to Contents.
- GitHub tools need read access to Pull requests, Actions and Commit statuses.
- Draft pull requests need write access to Contents and Pull requests.
- Issues need write access to Issues.
For the App, SecondShift reads the installation’s permissions. For a token, it makes one read request per permission. A token’s write access cannot be checked without writing, so it is checked when a draft pull request or an issue opens.
Each gap links to the page on GitHub that fixes it. The worker checks each GitHub connection once a day, and right away when a sync fails.
Credentials and limitations
Section titled “Credentials and limitations”For each sync, checkout or check, SecondShift creates one short-lived installation token, limited to the repository and to read-only contents. Tokens stay in trusted source-fetching processes. When a run starts, the worker sends the runner a snapshot of the files at the run’s recorded commit. The snapshot holds no Git history and no credentials. See repository data flow. GitHub’s installation authentication guide describes token permissions and expiry.
Draft pull requests
Section titled “Draft pull requests”Tasks can open draft pull requests when an owner allows it for a repository. For this, the App needs two more repository permissions:
- Contents: Read and write, to push the
secondshift/run-<id>branch. - Pull requests: Read and write, to open the draft.
Do not grant Workflows. Without it, GitHub refuses any push that changes
.github/workflows, so an agent cannot change what CI runs. After you change
the App’s permissions, each installation owner must accept them on GitHub.
Only publishing asks for a write token. It is scoped to one repository and used for one push and one request. Syncs and checkouts stay read-only.
Issues
Section titled “Issues”Jobs that set open_issues can open the issues their agent proposes, when an
owner allows issues for the repository. For this, the App needs one more
repository permission:
- Issues: Read and write, to search open issues and open new ones.
Opening issues asks for a token limited to the run’s repository and to issues. It is used after the run and never reaches the sandbox or the model.
Disconnecting the source connection blocks future syncs and new run checkouts. Removing repository access or uninstalling the App is detected by the next sync, validation, or run checkout. Already running sandboxes can finish their pinned checkout. Reinstall/re-authorize through Connect GitHub to reconnect.
GitHub tools
Section titled “GitHub tools”Jobs read pull requests, commits and Actions runs through the GitHub MCP
connection. On Connections, the GitHub card groups repository access,
GitHub tools and sign-in. Choose Turn on next to GitHub tools. This adds
the github binding, using each repository’s GitHub connection. No token is
stored on it. Each tool call gets
a read-only token for the run’s repository alone, from the App installation or
token the repository was added with. The token never enters the sandbox.
With the App, add these repository permissions:
- Pull requests: Read-only, or read and write if you also open drafts.
- Actions: Read-only, for CI runs and job logs.
- Commit statuses: Read-only.
A tool token asks only for the permissions the installation granted, and only for read access. A permission the App lacks fails just the tools that need it.
GitHub enforces the one-repository limit for private repositories. Any token can read a public repository, so for those SecondShift’s own argument checks keep each job on its repository. Repositories added from a local folder have no GitHub connection to borrow. Connect GitHub tools for them with their own fine-grained token instead.
Installing the App does not turn on GitHub tools. An owner turns them on, and
each job’s request goes through the usual approval. Turn off blocks jobs
that name github and leaves repository access as it is. Check access on a
repository connection shows whether the tools’ permissions are granted.
Use a personal access token
Section titled “Use a personal access token”On Repositories, choose Connect GitHub, then paste a fine-grained personal access token under Personal access token. SecondShift refuses classic tokens, because they reach every repository you can.
The Create a token on GitHub link opens GitHub’s form with the name, a 90-day expiry and the read-only permissions filled in. You only choose the repositories. A second link fills in write access for draft pull requests.
Give the token only the repositories SecondShift should use, and these repository permissions:
- Contents: Read-only, to import and sync.
- Pull requests, Actions and Commit statuses: Read-only, for GitHub tools.
- Contents and Pull requests: Read and write, for draft pull requests. Do not grant Workflows, for the reason given above.
- Issues: Read and write, for jobs that open issues.
GitHub checks the token before it is saved. It is stored encrypted and never returned to the browser or put in a sandbox. Its expiry date shows on Connections, with a warning two weeks before. Choose Replace token on the connection and paste a new token for the same GitHub account. Regenerating the token on GitHub keeps its repositories and permissions. Its repositories in SecondShift keep working.
A token works differently from the App:
- One token does everything it was given. SecondShift cannot narrow it per step, so a token that can write is also the one used to read.
- Pushes, draft pull requests and issues appear as the token’s owner, and GitHub does not let you approve your own pull request.
- API calls count against the owner’s hourly GitHub limit.
- When the owner loses access to a repository, or the token expires, its syncs and runs stop until a new token is added.
This first version uses polling, not webhooks. Each sync fetches only new commits into a mirror kept by SecondShift. A run’s files may be up to 64 MiB compressed, 1 GiB unpacked and 200,000 files. Git LFS objects and submodule contents are not downloaded. Repository renames require a new import so existing capability approvals cannot silently change scope.