Git Security — converting private repositories
Converting a private repository through gitmd2pdf requires credentials, because our server has to fetch it from your git host the same way your laptop would. This page walks through the recommended workflow, the alternatives, and what gitmd2pdf does with the credentials you supply.
Short answer: put your username in the repository URL, then enter your Personal Access Token (or whatever your host calls it — see the provider table) in the dedicated field that appears below. Use a short-lived, scoped, read-only token — not your account password. Revoke the token after a one-off conversion.
TL;DR by provider
| Host | What to create | Where to find it | Scope |
|---|---|---|---|
| GitHub | Personal Access Token (PAT) — fine-grained recommended | Settings → Developer settings → Personal access tokens | Contents: Read on the target repo only |
| GitLab | Project Access Token or Deploy Token | Settings → Access Tokens / Repository → Deploy tokens | read_repository |
| Bitbucket | Repository Access Token or App Password | Repository settings → Access tokens / Personal settings → App passwords | Repository: Read only |
| Azure DevOps | Personal Access Token (PAT) | User Settings → Personal access tokens | Code: Read on the project |
| Self-hosted | Whatever your server's equivalent is | Your admin / docs | Read-only on the single repo |
In every case: scope the token to one repository, not your whole account, set an expiry of weeks rather than "no expiry", and revoke the token immediately if the conversion was a one-off. PATs are designed to be cheap to create + revoke — that's the whole point of using them rather than your password.
How to convert a private repository
Step 1 — Create a Personal Access Token
In your git host's settings panel (links above), create a token with read-only access to the repository you want to convert. Keep the token visible — you'll paste it in step 3.
Step 2 — Paste the repository URL with your username
In the Dashboard's Repository tab, paste a URL of the form:
https://YOUR-USERNAME@github.com/your-org/your-repo.git
https://YOUR-USERNAME@gitlab.com/your-namespace/your-project.git
https://YOUR-USERNAME@bitbucket.org/your-workspace/your-repo.git
Substitute your actual git-host login for YOUR-USERNAME. Do NOT include the token here — leave the part after @ as just the host + path.
When you paste a URL of this shape, the form recognises that the host needs authentication and reveals a Personal Access Token field below the URL.
Step 3 — Enter the token in the dedicated field
Type (or paste) your PAT into the Personal Access Token field. Click Convert to PDF.
That's it. The token is forwarded to the converter through a subprocess environment variable (never command-line argv, never written to disk), and the working directory is deleted as soon as the PDF is generated.
Why is the username in the URL and not a separate form field?
HTTPS git authentication needs two values: a username and a credential. They both ride in the URL like https://USER:CRED@host/.... The dashboard splits them so the token gets entered in a separate password-type input (so browsers don't autocomplete it, sync it across devices, or leak it via referrer headers), but keeps the username in the URL itself for one important reason: single source of truth. An earlier version of the form had a separate username input — pre-filled from the URL, but editable. The result was confusing UX: two places to enter a username, with the field silently overriding the URL prefix on the wire. Removed in change-248. The URL prefix is now the canonical username source.
If your token type needs a magic-string username (oauth2, x-token-auth, etc. — see the table below), edit the URL prefix to use that string. For example: https://myers@gitlab.com/org/repo.git becomes https://oauth2@gitlab.com/org/repo.git for a GitLab Project Access Token.
What username should I put in the URL?
For most token types the username is just your account login — but several token types require a literal magic-string username instead. If your token type isn't your standard account login, edit the USER portion of the URL before submitting.
| Token type | Username to put in the URL prefix |
|---|---|
| GitHub Personal Access Token (classic + fine-grained) | Your GitHub login (or any non-empty string — GitHub ignores it for PAT auth) |
| GitHub App installation token | x-access-token |
GitHub Actions GITHUB_TOKEN (inside a workflow) |
x-access-token |
| GitLab Personal Access Token | Your GitLab login (or oauth2) |
| GitLab Project Access Token | oauth2 |
| GitLab Deploy Token | The deploy-token's name (the name, not its scope) |
GitLab CI_JOB_TOKEN (inside a pipeline) |
gitlab-ci-token |
| Bitbucket Repository Access Token | x-token-auth |
| Bitbucket App Password | Your Bitbucket login |
| Azure DevOps PAT | Empty or any non-empty string |
| Self-hosted (Gitea, Forgejo, Codeberg, etc.) | Whatever your host's docs say — usually your login |
If your conversion fails with a 403 Forbidden or fatal: Authentication failed error and you're confident the token is valid, the URL-prefix username is the most likely culprit — try the magic-string equivalent for your token type before regenerating the token.
Alternative: put the token directly in the URL
If you'd rather embed the token in the URL — for example, you have automation that already builds URLs of this shape — that also works. The format is:
https://YOUR-USERNAME:YOUR-TOKEN@github.com/your-org/your-repo.git
https://oauth2:YOUR-TOKEN@gitlab.com/your-namespace/your-project.git
https://x-token-auth:YOUR-TOKEN@bitbucket.org/your-workspace/your-repo.git
Paste the whole URL into the Repository tab and convert. Everything after https:// up to the @ is the credential portion — gitmd2pdf never stores it, and the redaction step (described below) strips it from the PDF and from any log output.
Why the username-in-URL + PAT-in-field flow is preferred: the URL itself can leak through browser history, bookmark sync, referrer headers, and shared error reports. Anything you put in the URL field is at risk through those channels. The dedicated PAT field has none of those leak vectors — it's a password-type input that browsers don't autofill or sync.
Can I use my account password instead of a PAT?
Technically yes — most git hosts still accept your account password where they accept a token (use it as the :password half of the URL or in the PAT field). Don't. Reasons:
- Your account password is a single point of failure for your entire account; a PAT scoped to one repo with read-only access is not.
- PATs can be revoked individually without changing your password or signing out of any device.
- PATs can be set to expire automatically (1 day for a one-off conversion is reasonable).
- Most hosts (GitHub since 2021, Bitbucket since 2022) have actually deprecated password authentication for git operations over HTTPS — even if it works for you today, it may stop working at any time.
If you're going to type a credential into a form, type a PAT.
What gitmd2pdf does on your behalf
- We fetch a single shallow snapshot of the repository (latest commit only, no history) on a short-lived worker. The fetch runs over TLS to your git host and has no access to anything else in your account.
- The working directory is created fresh per conversion and deleted as soon as the PDF is generated.
- Before writing the title page or any log that references the URL, we redact credentials: anything matching
user:pass@,?token=,access_token=,api_key=,password=, orsecret=is replaced with****. Successful conversions never expose your PAT in the PDF or in our operational logs. - Credentials are never persisted server-side. There is no stored-token table; the token exists only for the duration of the single subprocess that fetches your repository, and that subprocess's environment is wiped when it exits.
What you should avoid
Putting credentials in places that leak:
- Sharing a URL that embeds a PAT. Anyone you send it to can use it until you revoke. If you must share, share the username-only URL and pass the token through a separate secure channel (or make the recipient generate their own).
- Pasting a PAT into a chat / issue / email when reporting an error. Always redact tokens before forwarding error messages — even if gitmd2pdf already redacts them in our output, the URL you typed in is your responsibility.
- Using a long-lived "no expiry" token for a one-off conversion. Set a short expiry; revoke when done.
If a PAT is exposed by any channel, revoke it immediately at your provider and issue a new one. That's the whole point of using short-lived scoped tokens — revocation is cheap.
API users — send the token in the request body
If you're calling the Premium API rather than using the Dashboard, send the token in the repoCredentials body field instead of embedding it in the URL:
curl -X POST https://yourapp.com/api/v1/convert \
-H "Authorization: Bearer YOUR_API_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "repo",
"repoUrl": "https://github.com/your-org/your-repo.git",
"repoCredentials": {
"username": "your-github-login",
"token": "ghp_..."
}
}'
Both username and token are required when repoCredentials is present (HTTPS git auth needs both halves). The token is forwarded to the converter via subprocess env (NOT process argv), so it never appears in ps output, access logs, or MongoDB.
Coming Soon: stored credentials
A future Premium feature will let you save scoped tokens server-side — encrypted at rest with a KMS-managed key, audit-logged, and referenced by ID in the convert request — so you don't have to paste the token on every conversion. Today's flow (single-use per request, no storage) is the safe default until that lands.
SSH URLs
The web UI accepts only http:// and https:// URLs today. The Premium API v1 additionally accepts git@host:path and ssh://… URLs for customers on Premium integrations. SSH auth against a private repo requires keys that are pre-provisioned on the converter host — if you need that pattern for an enterprise or self-hosted deployment, contact us and we'll walk through the options.
When in doubt
- Public repo? Don't use a token at all — plain
https://github.com/org/repo.gitconverts fine. - One-off conversion? Create a fine-grained PAT scoped to that repo with an expiry of 1 day, convert, then revoke it.
- Automated integration? Use the Premium API with OAuth2 bearer credentials rather than stuffing PATs into URLs; tokens live in the Authorization header and never appear in URLs or logs.
- Unsure whether gitmd2pdf kept your token? It didn't — no credentials touch long-term storage — but rotating the PAT after a conversion is free insurance.
Related: How do I convert a private GitHub or GitLab repository? in the FAQ covers the same workflow at a lower level of detail.