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:
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
lastCheckedAtandlastErroronGET /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.