Skip to main content
For operators. This guide is for the person who installs and configures the Oxy server. End users who want to import a repository just click Import from GitHub in the Workspaces UI — they don’t need to read this.

Overview

In multi-workspace mode, Oxy uses a GitHub App (not a personal OAuth app) to:
  • Import repositories as workspaces (“Import from GitHub”)
  • Clone repositories on the server when a workspace is created
  • Read and push commits through the built-in IDE Git panel
The same GitHub App handles both user authentication and repository access. This guide walks through creating the app, configuring permissions, and setting the required environment variables.
GitHub App integration is only needed for multi-workspace mode (the default oxy serve). If you run oxy serve --local, the workspace files already exist on disk and no GitHub integration is required.

Step 1: Create the GitHub App

  1. Go to GitHub → Settings → Developer settings → GitHub Apps → New GitHub App (For an organization app, go to Organization Settings → Developer settings)
  2. Fill in the app details:
    FieldValue
    GitHub App namee.g. my-company-oxy
    Homepage URLYour Oxy instance URL (e.g. https://app.example.com)
    Callback URLhttps://your-domain.com/github/callback
    WebhookUncheck “Active” — Oxy does not use GitHub webhooks
    Where can this GitHub App be installed?”Any account” (or “Only on this account” for org-only)
  3. Click Create GitHub App.

Step 2: Configure Permissions

After creating the app, go to Permissions & Events and set:

Repository permissions

PermissionLevelPurpose
ContentsRead & WriteClone repositories, read and push commits
MetadataRead-onlyRequired by GitHub for all apps

Organization permissions

PermissionLevelPurpose
MembersRead-onlyRequired — allows regular org members (not just admins) to see this installation during the OAuth connect flow
Without Organization → Members: Read, only GitHub org owners and admins will be able to connect their account. Regular org members will see “GitHub App not installed” even after the app has been installed on the org. Enable this permission and re-install the app if this happens.
Leave all other permissions at “No access” and save.

Step 3: Generate a Private Key

  1. Scroll to the Private keys section on the app settings page.
  2. Click Generate a private key.
  3. GitHub downloads a .pem file — keep this safe.
The private key content is the value for GITHUB_APP_PRIVATE_KEY. It should start with -----BEGIN RSA PRIVATE KEY-----.

Step 4: Note Your App Credentials

From the app settings page, collect these values:
ValueWhere to find itEnvironment variable
App IDTop of the app settings pageGITHUB_APP_ID
App slugURL: github.com/apps/{slug}GITHUB_APP_SLUG
Client ID”OAuth App” sectionGITHUB_CLIENT_ID
Client secretGenerate one in “OAuth App” sectionGITHUB_CLIENT_SECRET

Step 5: Set Environment Variables

Set the following on your Oxy server:
# GitHub App credentials
GITHUB_APP_ID=123456
GITHUB_APP_SLUG=my-company-oxy
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
...your key content...
-----END RSA PRIVATE KEY-----"

# GitHub OAuth credentials (same App — used for user login)
GITHUB_CLIENT_ID=Iv23liqo62PgXXXXXX
GITHUB_CLIENT_SECRET=abc123...

# HMAC secret for signing OAuth state and selection tokens
# Generate once and store permanently — changing this invalidates all in-flight OAuth flows
GITHUB_STATE_SECRET=$(openssl rand -hex 32)
GITHUB_STATE_SECRET must stay the same across server restarts and deploys. If it changes, users in the middle of an OAuth flow will see “Invalid state parameter” and have to start over. Generate it once, store it in your secrets manager, and never rotate it unless you intentionally want to invalidate active sessions.
The GITHUB_APP_PRIVATE_KEY value must include the full PEM block with literal newlines, not \n escape sequences. In a .env file, wrap the value in double quotes and paste the key with actual line breaks.

Formatting the private key in .env

# .env
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0Z3VS5JJcds3xHn/ygWep4Xh3bzbFa/DJdTpPOBdLQmBFHDm
...rest of key...
-----END RSA PRIVATE KEY-----"

Formatting the private key in Kubernetes secrets

# Encode the .pem file contents as base64
cat your-app.private-key.pem | base64 -w 0
Store the base64 value in your Kubernetes secret and reference it with envFrom or externalSecrets.

Step 6: Install the App

Before users can import repositories, the GitHub App must be installed on the target GitHub organization or user account:
  1. Go to https://github.com/apps/{your-app-slug}
  2. Click Install and select the organization/account
  3. Choose “All repositories” or specific repositories
  4. Click Install
After installation, users can connect their account by clicking Sign in with GitHub in the Workspaces UI.

Step 7: Verify Callback URL Routing

Oxy derives the OAuth callback URL automatically from the Origin header of the browser request — no hardcoding is needed. However, ensure:
  1. The callback URL in GitHub matches the domain your users access. For example, if users go to https://app.example.com, the callback must be https://app.example.com/github/callback.
  2. Your reverse proxy forwards the Origin header to the Oxy backend. Most proxies (nginx, Caddy, ALB) do this by default.
  3. OXY_API_URL is set if your backend is at a different URL than the frontend:
    OXY_API_URL=https://app.example.com/api
    

How the Connect Flow Works for Users

When a user clicks Import from GitHub in the Workspaces UI, Oxy walks them through connecting their GitHub account and selecting a repository.

Step 1 — Connect a GitHub account

Oxy opens a GitHub OAuth popup. The user authorizes the app, and GitHub redirects back with a short-lived code that Oxy exchanges for an access token. This token is used to discover which GitHub App installations are visible to that user. There are three possible outcomes:
OutcomeWhat the user sees
One installation foundOxy auto-connects it — the user skips straight to repository selection
Multiple installations foundA picker lists the available GitHub accounts/organizations; the user chooses one
No installation foundA message tells the user to ask their admin to install the GitHub App first

Step 2 — Select a repository

Once an installation (GitHub account or organization) is connected, the user sees the repositories that the GitHub App has access to. They pick one and Oxy clones it as a new workspace.

Connecting a different account

If a user wants to import from a different GitHub account or organization, they can click Use a different GitHub account in the connect dialog. This opens a fresh OAuth popup and lets them authorize a different account — the newly authorized account replaces the previous one for future imports.
The GitHub App must be installed on any account or organization before its repositories appear in the picker. See Step 6: Install the App above, or ask the org owner to install it.

Security: How Installation Selection Is Protected

When multiple installations are shown, Oxy issues a short-lived selection token — an HMAC-signed value encoding the eligible installation IDs, bound to the user’s ID and valid for 10 minutes. When the user picks one:
  1. The client sends { installation_id, selection_token } back to the server
  2. The server verifies the signature, confirms the user ID matches, checks the token is not expired, and validates that the chosen ID is in the token’s list
  3. Only then is the namespace created
This ensures a user can only connect an installation they were explicitly shown — installation IDs from other tenants cannot be guessed or injected.

Full Environment Variable Reference

# ── Required for GitHub workspace import ─────────────────────────────
GITHUB_APP_ID=123456                    # Numeric app ID from app settings page
GITHUB_APP_SLUG=my-company-oxy          # App slug (from the app's URL on github.com)
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----"          # Full PEM block from the downloaded .pem file
GITHUB_CLIENT_ID=Iv23liqo62PgXXXXXX    # OAuth Client ID from app settings
GITHUB_CLIENT_SECRET=abc123...          # OAuth Client Secret from app settings
GITHUB_STATE_SECRET=random-64-char-hex  # Random secret for HMAC signing (state + selection tokens)

Troubleshooting

“GitHub App not installed” — but the app IS installed The most common cause: the Organization → Members: Read permission is not enabled on the GitHub App. GET /user/installations only returns installations the OAuth user can see. Without Members: Read, only org owners and admins are included; regular members get an empty list, which Oxy reports as not_installed. Fix:
  1. Go to your GitHub App settings → Permissions & Events → Organization permissions
  2. Set Members to Read-only and save
  3. GitHub will ask you to re-approve the updated permissions — click Accept
  4. Try connecting again
“GitHub callback redirects to localhost” The redirect_uri is derived from the Origin header. Check that your proxy is forwarding the Origin header and that the correct callback URL is registered in the GitHub App settings. “GitHub App ID not configured in environment” GITHUB_APP_ID is not set or not being loaded. Verify your .env file is loaded at server startup or that the environment variable is injected by your deployment platform. “Invalid state parameter” The HMAC verification on the OAuth state failed. This usually means GITHUB_STATE_SECRET changed between when the user started the OAuth flow and when GitHub redirected back. Ensure the secret is consistent across server restarts (don’t regenerate it on every deploy). “Invalid or expired selection token” The user took more than 10 minutes to pick an installation, or the server restarted and GITHUB_STATE_SECRET changed. The user should click Sign in with GitHub again to get a fresh token. Users see “Sign in with GitHub” but nothing happens Check that GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET are set correctly. Also verify the callback URL in the GitHub App settings matches your deployment domain exactly. “Use a different GitHub account” connects the wrong account The connect popup opens in a new browser window. If the user is already signed into GitHub as a different account in that window, they may need to sign out of GitHub first before authorizing. After a successful authorization, Oxy always uses the most recently connected account.