---
description: Connect an existing GitHub JSON file and understand Studio's metadata-based change detection and conflict handling.
---

# GitHub Publishing

An administrator can connect one existing JSON file on a GitHub branch.
Studio commits validated Preview Data to that file; the repository's hosting
integration handles the public-site build.

## Connect a Target

For a new site, deploy
[Studio Starter](https://github.com/zeropress-app/zeropress-starter-studio) and
open its `zeropress-preview-data.json` file on GitHub. Otherwise, select the
existing Preview Data file used by your build.

In **Site Settings → Publishing**:

1. Paste the file URL, such as
   `https://github.com/example/my-site/blob/main/zeropress-preview-data.json`.
2. Verify the owner, repository, branch, and file path. Use direct input if a
   branch containing `/` makes the URL ambiguous.
3. Select **Create GitHub token**. The link preselects the owner and
   **Contents: Read and write**. Select only the target repository; an
   organization may require approval of the token.
4. Enter the token, check the connection, enable publishing, and save.

The target must be a branch and an existing regular `.json` file on
`github.com`. Tags, commit IDs, directories, symbolic links, and submodules
are not publishing targets. Studio does not create a missing file.

Connection checks verify that the branch and file metadata can be read. They
do not inspect the file contents or guarantee write permission. Branch rules
must allow the direct commit. Saving settings with publishing switched off
keeps the connection disabled.

## Credential Storage

The token is encrypted in Studio D1 using a publishing-specific key derived
from `STUDIO_AUTH_SECRET`. It is not returned by settings reads or included in
Preview Data or logs. Leave the input blank to keep a saved token, enter a new
value to replace it, or disable publishing before deleting it.

Keep `STUDIO_AUTH_SECRET` stable. After replacing it, enter the GitHub token
again. Settings saves use a revision check so one session cannot silently
replace changes made by another.

## How Unchanged Data Is Recognized

Studio uses two hashes for different purposes:

| Value | Meaning |
| --- | --- |
| Data hash | SHA-256 of Preview Data with only the top-level `generated_at` omitted and object keys recursively sorted |
| Git Blob SHA | Git's SHA-1 object ID for the exact UTF-8 bytes uploaded, including the Git blob header |

The data hash preserves array order and all other values, including content
timestamps, generator information, and comment tokens. Regenerating identical
content at a later time therefore does not by itself require another commit.
The uploaded JSON uses the export's normal key order, two-space indentation,
and a final newline; comparison normalization does not rewrite the document.

Each Studio commit has these trailers:

```text
chore(publish): update site data

ZeroPress-Publish: 1
ZeroPress-Path: zeropress-preview-data.json
ZeroPress-Data-Hash: sha256:<data digest>
ZeroPress-Blob-SHA: <Git blob object ID>
```

For comparison, Studio reads the branch HEAD, follows the file's path through
Git trees, and reads the latest commit affecting that path at that HEAD.
Unrelated commits elsewhere in the repository do not change that file's
publishing metadata. These reads do not download the remote file body.

A publish is skipped only when:

1. The file's latest commit has valid, unambiguous Studio trailers for its path.
2. The recorded Blob SHA matches the file's current Blob SHA.
3. The newly prepared data hash matches the recorded data hash.

An external edit, missing metadata, or invalid metadata removes that basis for
comparison. The next confirmed publish establishes a new Studio baseline,
even if an external tool happened to write equivalent JSON. Studio does not
fall back to downloading and comparing the file body.

## Rechecks and Concurrent Changes

Publishing uses the connection revision and prepared data reviewed on the
screen. Studio rechecks the content and remote file before the write, then
sends the existing file SHA with the GitHub Contents API update.

If the file changes concurrently, Studio reports a conflict instead of forcing
an overwrite. A removed target stops publishing rather than becoming a file
creation request. Review the remote state before confirming a new publish.

## Results and Recovery

| Result | Meaning and next action |
| --- | --- |
| Updated on GitHub | GitHub confirmed the commit; check the hosting provider for deployment |
| No changes | The prepared data already matches valid Studio metadata; no commit is needed |
| Authentication or permission error | Review the token, repository access, and branch rules |
| Conflict or changed preparation | Review the latest content and target before trying again |
| Rate limit or temporary service failure | Retry after the reported restriction or outage clears |
| Confirmation required | A write response was lost and subsequent metadata reads could not confirm the result; inspect the file's latest GitHub commit |

After a lost response, Studio makes a limited metadata check for the intended
commit. It does not automatically resend an uncertain write. A confirmed
commit remains successful even if a later status refresh fails.

The [GitHub Contents API](https://docs.github.com/en/rest/repos/contents#create-or-update-file-contents)
documents file updates and their SHA requirement.
