> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tinfoil.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Private images

> How attestation works with private source code, and how to configure authentication for private container registries.

## Overview

Tinfoil's own stack is fully open source, which is how anyone can audit what runs inside our enclaves. But your application doesn't need to be.

When an enclave boots, the hardware fingerprints everything inside it and signs that fingerprint with the chip manufacturer's key. On every connection, the client can verify that the code actually running is exactly the code that was committed to. If you're open source, anyone can inspect that code themselves. If you're not, you can choose to have a third-party auditor review it and certify that a given image hash meets their requirements. The fingerprint is public and permanently committed to, even if the source code is not available.

Attestation is performed against the pinned SHA256 digest in your configuration, so verification works the same way whether your image is public or private. If your image isn't public, you can have a third-party auditor review it and certify that a given hash meets their requirements; the fingerprint stays public and permanently committed to either way.

A Tinfoil Container deployment has two artifacts, and they follow different visibility rules:

* **The config repo must be public.** Tinfoil measures `tinfoil-config.yml` at the tag you deploy and publishes the measurement to the Sigstore transparency log. Clients verify against that log, so the repo's contents need to be inspectable.
* **The Docker image can be private.** Your config pins the image by SHA256 digest, and the enclave's attestation commits to that exact digest. Whether the image bytes are publicly pullable doesn't affect verifiability — the digest already pins them.

The rest of this page covers authentication for private image registries.

## Registry authentication

<Info>
  Registry authentication is an enterprise feature. [Contact us](mailto:contact@tinfoil.sh) to enable private registry support for your organization.
</Info>

If your Docker images are stored in a private registry, you need to add registry credentials so Tinfoil can pull them during deployment. Supported registries are GitHub Container Registry (`ghcr.io`), Google Artifact Registry (`*-docker.pkg.dev`) / Container Registry (`gcr.io`), and Docker Hub (`docker.io`).

## Adding credentials

1. Go to the **Registry Credentials** tab in the Containers section of the dashboard
2. Click **Configure** for your registry type
3. Enter your authentication credentials
4. Click **Save**

Credentials are stored encrypted and scoped to your organization.
Once configured, any container in your org can pull images from that registry.

## GitHub Container Registry (ghcr.io)

Tinfoil needs a **personal access token** with read access to your packages.

### Creating a classic PAT

Classic PATs are the most reliable option for ghcr.io — they work with all packages, including those not linked to a specific repository. See [GitHub's guide to creating a classic PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) for full details.

1. Go to [github.com/settings/tokens](https://github.com/settings/tokens)
2. Click **Generate new token** > **Generate new token (classic)**
3. Give it a descriptive name (e.g. "Tinfoil registry read-only")
4. Set an **expiration** — 90 days is a reasonable default
5. Under scopes, check only **`read:packages`**
6. Click **Generate token** and copy it immediately

<Info>
  If your GitHub organization uses **SAML SSO**, you must authorize the token for your org after creating it. Go to [github.com/settings/tokens](https://github.com/settings/tokens), find the token, click **Configure SSO**, then click **Authorize** next to your organization name. Without this step, pulls from your org's packages will fail with a 403. See [GitHub's SSO authorization guide](https://docs.github.com/en/enterprise-cloud@latest/authentication/authenticating-with-saml-single-sign-on/authorizing-a-personal-access-token-for-use-with-saml-single-sign-on).
</Info>

### What to enter in the dashboard

* **Username**: Your GitHub username
* **Token**: The personal access token you just created

<Info>
  GitHub's fine-grained personal access tokens **do not support GitHub Packages** (including ghcr.io). You must use a classic PAT for pulling container images.
</Info>

## Google Artifact Registry / Container Registry

Google Container Registry (`gcr.io`) was shut down in 2025. Projects that migrated to Artifact Registry can still use `gcr.io` URLs, which now route to Artifact Registry under the hood. Tinfoil supports both `gcr.io` and `*-docker.pkg.dev` image URLs.

Tinfoil needs a **service account JSON key** with read access to your container images. See [Google's service account creation guide](https://cloud.google.com/iam/docs/service-accounts-create) and [Artifact Registry authentication docs](https://cloud.google.com/artifact-registry/docs/docker/authentication) for full details.

### Creating a service account

1. Open the [Google Cloud Console](https://console.cloud.google.com)
2. Select your project
3. Go to **IAM & Admin** > **Service Accounts**
4. Click **Create Service Account**
5. Name it (e.g. "tinfoil-registry-reader") and click **Create and Continue**
6. Grant the role **Artifact Registry Reader** (`roles/artifactregistry.reader`)
   — for project-wide access, grant it at the project level
   — for a single repo, grant it on the specific Artifact Registry repository instead
7. Click **Done**

### Creating and downloading the JSON key

See [Google's guide to creating service account keys](https://cloud.google.com/iam/docs/keys-create-delete#creating) for full details.

1. In the Service Accounts list, click on the service account you just created
2. Go to the **Keys** tab
3. Click **Add Key** > **Create new key**
4. Select **JSON** and click **Create**
5. A `.json` file will download — store it securely. This file can only be downloaded once; if you lose it, you must create a new key.

### What to enter in the dashboard

* **Service Account Key**: Paste the **entire contents** of the downloaded JSON key file

<Info>
  Service account JSON keys **do not expire by default**. However, your organization may enforce key expiration via the `constraints/iam.serviceAccountKeyExpiryHours` organization policy. If this policy is set, keys will stop working after the configured duration and you'll need to create a new one.
</Info>

## Docker Hub

Tinfoil needs a **personal access token** with read permissions. See [Docker's access token documentation](https://docs.docker.com/security/for-developers/access-tokens/) for full details.

### Creating an access token

1. Log in to [Docker Home](https://app.docker.com)
2. Click your profile avatar > **Account settings**
3. Go to **Personal access tokens** in the left sidebar
4. Click **Generate new token**
5. Enter a description (e.g. "Tinfoil registry read")
6. Set the permission to **Read**
7. Set an **expiration date**
8. Click **Generate** and copy the token immediately

### What to enter in the dashboard

* **Username**: Your Docker Hub username
* **Token**: The access token you just created

<Info>
  Docker Hub access tokens support an **expiration date** set at creation time. The expiration cannot be changed after creation — you must create a new token if you need a different expiration. If your account has **two-factor authentication** enabled, PATs are required for CLI access (passwords will not work).
</Info>

## Troubleshooting

### "Requires authentication" error during deployment

This means the container image is in a private registry but no credentials are configured for that registry type.

**Fix**: Go to the **Registry Credentials** tab and configure credentials for the registry shown in the error message. The dashboard includes a direct link to the tab in the error.

### "Expired or been revoked" error during deployment

This means credentials are configured but the registry rejected them. The token may have expired, been revoked, or lost access.

**Fix**: Go to the **Registry Credentials** tab and update your credentials with a fresh token. After updating, the expired credentials banner will clear automatically.

Common causes:

* **GitHub PAT expired** — classic PATs can be set with an expiration date. Create a new one and update it in the dashboard.
* **GitHub PAT not authorized for SSO** — if your org enabled SAML SSO after you created the token, you need to re-authorize it.
* **Google service account key disabled or deleted** — check the service account in Google Cloud Console. Create a new key if needed.
* **Google IAM permissions changed** — verify the service account still has `roles/artifactregistry.reader`.
* **Docker Hub token expired or revoked** — Docker Hub tokens have mandatory expiration dates. Create a new access token in Docker Hub settings.

### Expired credentials banner

When Tinfoil detects that your registry credentials have been rejected during a deployment attempt, an amber banner appears on the Active tab. This banner persists until you update the credentials in the Registry Credentials tab.

## Using the CLI

The [Tinfoil CLI](/containers/cli) sets the same per-registry credentials, which is convenient when rotating tokens from a script:

```bash theme={"dark"}
tinfoil registry list

# GitHub Container Registry: classic PAT with read:packages
tinfoil registry set ghcr --username my-gh-user --token ghp_xxx

# Google Artifact Registry / Container Registry: service-account JSON
tinfoil registry set gcr --key-file ./gcp-sa.json

# Docker Hub: PAT with read permission
tinfoil registry set dockerhub --username my-docker-user --token dckr_xxx

# Remove credentials for a registry
tinfoil registry delete ghcr
```

Pass `--key-file -` to read the GCR key from stdin instead of disk. Once credentials are stored, any container in the org can pull images from that registry.
