Skip to main content

Connect Git without making it everybody's hobby

The platform side owns credentials, webhooks, reconciliation, and access policy. Developers should only see a friendly connection ID and a pipeline template.

Pagifier supports:

  • GitHub with a GitHub App or service token;
  • GitLab with a project, group, or service token;
  • self-hosted GitHub and GitLab through baseUrl;
  • other smart-HTTP Git services through polling and URL templates.

GitHub and GitLab get signed webhooks for fast delivery plus polling for recovery. Generic providers use polling because there is no portable webhook or archive API across every Git product.

1. Enable the observer

The Helm chart enables it by default:

values.yaml
gitObserver:
enabled: true
reconcileInterval: 2m
publicURL: https://pagifier.company.com
secretName: pagifier-git-observer-key
secretKey: key
createSecret: true

publicURL lets Pagifier install GitHub and GitLab webhooks during pipeline activation when the provider credential can manage hooks. Leave it empty if hooks are managed elsewhere.

The chart generates a 32-byte encryption key and keeps the Secret across Helm uninstall. Back it up like any other control-plane secret. Losing it does not leak provider credentials, but it does make every stored Git connection unreadable.

For an externally managed key:

kubectl -n pagifier-system create secret generic pagifier-git-observer-key \
--from-literal=key="$PAGIFIER_GIT_SECRET_KEY"

Then set gitObserver.createSecret=false.

2. Prefer a GitHub App in production

A GitHub App gives Pagifier short-lived installation tokens instead of a long-lived personal token. Install the app only on repositories Pagifier may observe. It needs repository content read access and webhook administration if Pagifier will install hooks.

Create the connection with the app details as the secret JSON value:

APP_SECRET="$(jq -cn \
--arg appId "$GITHUB_APP_ID" \
--argjson installationId "$GITHUB_INSTALLATION_ID" \
--rawfile privateKey github-app.pem \
'{appId:$appId,installationId:$installationId,privateKey:$privateKey}')"

jq -n \
--arg name "application repositories" \
--arg secret "$APP_SECRET" \
'{name:$name,provider:"github",authType:"app",secret:$secret}' |
curl --fail-with-body \
-H "Authorization: Bearer $PAGIFIER_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @- \
"$PAGIFIER_URL/v1/git-connections"

For a quick start, use authType:"service_token" and put the GitHub or GitLab token directly in secret. Pagifier encrypts it with AES-256-GCM before persistence; API responses never return it.

Self-hosted installations add their API root:

{
"name": "company gitlab",
"provider": "gitlab",
"baseUrl": "https://gitlab.company.com/api/v4",
"authType": "service_token",
"secret": "..."
}

3. Save the one-time webhook details

Creating a native GitHub or GitLab connection returns:

{
"connection": {"id": "git_abc123", "provider": "github"},
"webhookUrl": "/v1/hooks/git/acme/git_abc123",
"webhookSecret": "shown-once"
}

When automatic hook installation is disabled, combine webhookUrl with the public Pagifier URL and configure a push webhook at the provider. The secret is only returned when Pagifier generated it. Store it in your secret manager before closing the terminal.

Pagifier validates GitHub HMAC signatures and GitLab webhook tokens before accepting an event. Delivery IDs are deduplicated in PostgreSQL.

4. Test before handing it over

curl --fail-with-body \
-H "Authorization: Bearer $PAGIFIER_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"repository":"acme/frontend","ref":"refs/heads/main"}' \
"$PAGIFIER_URL/v1/git-connections/git_abc123/test"

A successful response includes the protected ref's current commit. Now give developers the connection ID—not the provider credential—and point them to Deploy from Git.

5. Give people narrow tokens

The Helm bootstrap tokens remain tenant administrators so you can get started and migrate. Day-to-day users should get expiring, revocable tokens with role bindings:

curl --fail-with-body \
-H "Authorization: Bearer $PAGIFIER_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name":"frontend-prod-deployer",
"expiresIn":"720h",
"bindings":[{
"role":"deployer",
"scopeKind":"environment",
"scopeId":"frontend/prod"
}]
}' \
"$PAGIFIER_URL/v1/tokens"

Built-in roles are admin, maintainer, builder, deployer, approver, and viewer. Bind them at tenant, application, repository, environment, or infrastructure scope. A caller cannot mint a token with permissions the caller does not already hold.

The token secret is returned once. Pagifier stores only its SHA-256 hash; expiry and revocation are checked on every request.

Generic Git services

Generic Git polling reads refs through standard smart HTTP. File and archive download APIs are not standardized, so supply templates:

{
"name": "internal forge",
"provider": "generic",
"baseUrl": "https://git.company.com",
"authType": "service_token",
"secret": "...",
"fileUrlTemplate": "https://git.company.com/{repository}/raw/{ref}/{path}",
"archiveUrlTemplate": "https://git.company.com/{repository}/archive/{ref}.zip"
}

Templates may use {repository}, {ref}, and {path}. If the product is actually GitHub or GitLab under your branding, use the native provider with baseUrl; signatures and API behavior will be better.

Day-two work

  • Watch lastCheckedAt and lastError on GET /v1/git-connections.
  • Alert on repeated reconciliation failures and webhook rejection rates.
  • Rotate a provider credential with PUT /v1/git-connections/{connection}/credential.
  • Back up PostgreSQL and the Git encryption Secret together.
  • Keep control manifests on protected refs. Pagifier follows one exact ref, not a wildcard branch.
  • Review the audit trail for connection creation, rotation, pipeline activation, approvals, token changes, and denied authorization.