Let a sandbox clone, fetch, and push private repositories without putting a Git token in its environment, credential store, or remote URL. Freestyle’s existing HTTP header transform adds the authorization header at the edge before forwarding the request to your Git provider over verified HTTPS.
The provider decides which repositories the credential can access and whether it can push. The same transform handles reads and writes.
This guide uses a GitHub App installation token. Other Git providers that accept HTTP authentication use the same pattern with their own hostname and credentials.
Prepare The Sandbox And Controller
Use an existing sandbox with Git and ca-certificates installed. If you build
your own base snapshot, include both packages before taking the
snapshot. Package installation needs its own network access; the GitHub rule
below grants access to GitHub only.
Run the TypeScript examples in your trusted controller, outside the sandbox. Install the SDK there:
pnpm add freestyle@latestbun add freestyle@latestnpm install freestyle@latestyarn add freestyle@latest Supply these environment variables to the controller:
FREESTYLE_API_KEY: your Freestyle API key.FREESTYLE_VM_ID: the sandbox that will use Git.GITHUB_INSTALLATION_TOKEN: a GitHub App installation access token.
Keep both API credentials in the controller’s secret store. The sandbox needs neither of them.
Choose The GitHub Permissions
Install your GitHub App on the repositories the sandbox should access, then
generate an installation access token.
Choose the app’s Git access permissions:
Contents: read for clone and fetch, or Contents: read and write for push.
Changes to .github/workflows also need the Workflows permission.
GitHub’s branch protections
and rulesets still apply.
You can narrow the token to selected repositories and permissions when minting
it. A TLS rule matches a hostname, so this rule injects authorization into HTTPS
requests to github.com from the selected VM; it does not select a repository
path. Repository access belongs in the GitHub token’s permissions.
Inject Git Authentication At The Edge
GitHub accepts an installation token as the HTTP Basic password, with
x-access-token as the username. Construct the header in the controller:
import { Buffer } from "node:buffer";
import { Freestyle, type CreateTlsRuleOptions } from "freestyle";
function requiredEnv(name: string): string {
const value = process.env[name]?.trim();
if (!value) throw new Error(`Missing ${name}`);
return value;
}
const freestyle = new Freestyle();
const vmId = requiredEnv("FREESTYLE_VM_ID");
function githubRule(token: string): CreateTlsRuleOptions {
const password = token.trim();
if (!password) throw new Error("A GitHub installation token is required");
const authorization = `Basic ${Buffer.from(
`x-access-token:${password}`,
"utf8",
).toString("base64")}`;
return {
action: "allow",
domain: "github.com",
source: { vmId },
destination: { public: true },
transform: [{ headers: { authorization } }],
};
}
const rule = await freestyle.tls.rules.create(
githubRule(requiredEnv("GITHUB_INSTALLATION_TOKEN")),
);
// Retain the id in your controller so you can rotate or remove this rule.
const ruleId = rule.id;
The protocol defaults to HTTP. Freestyle steers this VM’s HTTPS connections for
github.com through the edge, installs its CA in the guest’s system trust
store, and grants the network path to the edge. No broad public-egress firewall
rule is needed for these Git requests.
The edge replaces any guest-supplied Authorization header and sends the
request to GitHub over a separate HTTPS connection that verifies GitHub’s
certificate. Header values are sealed at rest and read back from the API as
"***".
Clone, Fetch, And Push
Run these commands inside the sandbox, replacing OWNER/REPO with a
repository the token can access:
export GIT_TERMINAL_PROMPT=0
mkdir -p /workspace
git clone https://github.com/OWNER/REPO.git /workspace/repo
git -C /workspace/repo fetch origin
GIT_TERMINAL_PROMPT=0 makes an authentication failure return an error instead
of waiting for interactive credentials. The HTTPS remote URL contains no token.
To push, create a branch and make your changes:
cd /workspace/repo
git switch -c agent/update
Commit the changes with your chosen Git author name and email, then push:
git push -u origin HEAD
GitHub authenticates the push using the injected token and applies its normal authorization rules. The commit’s author name and email are separate from the credential authorizing the push.
Rotate The Token
GitHub App installation tokens expire after one hour. Your controller must mint a replacement before expiry and update the existing TLS rule; Freestyle does not refresh GitHub tokens for you.
Using the same githubRule function, pass the replacement token from your
controller’s GitHub token-minting flow:
async function rotateGitToken(newInstallationToken: string) {
await freestyle.tls.rules.update(ruleId, githubRule(newInstallationToken));
}
An update replaces the full rule, so include its domain, source, destination, and transform again. Do not rebuild it from a redacted API read. Edge decisions are briefly cached, so rotate ahead of expiry rather than at the expiration instant.
Remove The Credential Grant
When the sandbox no longer needs Git access, delete the rule from the controller:
await freestyle.tls.rules.delete(ruleId);
This removes the injected-credential path after cached decisions expire. It does not erase an existing clone or revoke the token at GitHub. Revoke the token at the provider too if the credential itself should stop working. Other TLS and firewall grants continue to determine the VM’s network access.
Connection Details And Troubleshooting
- Use HTTPS remotes.
git@github.com:OWNER/REPO.gitandssh://use SSH, so HTTP header injection cannot authenticate them. Switch an existing remote withgit remote set-url origin https://github.com/OWNER/REPO.git. - Keep certificate verification enabled. Git must trust the system CA
store that Freestyle configures. If a custom image overrides
http.sslCAInfoorGIT_SSL_CAINFO, make sure that trust bundle includes the Freestyle CA. - Check provider permissions on authentication errors. A private repository
may return
404when the token lacks access. Check the installation’s selected repositories, token expiry, Contents permission, and branch rules before putting credentials in the sandbox. - Each hostname needs its own routing decision. Submodules should use HTTPS
and be accessible to the token. A submodule on another host, a redirect to
another host, or Git LFS using a separate endpoint may need additional network
grants and provider-specific authentication. The
github.comrule does not automatically inject its credential on those hosts. - A hostname grant covers all HTTPS paths on that host. Use a Git origin you trust to receive the credential. Keep repository and write permissions scoped at the provider; the sandbox can exercise whatever that credential permits.
For another provider, use its exact HTTPS hostname and the authorization scheme
it documents. With HTTP Basic authentication, encode that provider’s
username:password-or-token in the controller and pass the resulting
Authorization value through the same headers transform.