# patchhub.json: downloads

`patchhub.json` at the root of a project tells PatchHub what people can **download**.

Nothing is stored ahead of time. When someone downloads, PatchHub applies the patches they picked to the version they picked, then builds it in a sandbox.

No `patchhub.json` yet? PatchHub's AI suggests one as a patch when a project first gets code. The maintainer can also ask for one in the project's Settings tab.

## Example

```json
{
  "version": 1,
  "setup": "ph-install go",
  "artifacts": [
    {
      "id": "app",
      "name": "Tasks",
      "kind": "download",
      "targets": [
        {"id": "macos-arm64", "name": "macOS (Apple Silicon)", "build": "GOOS=darwin GOARCH=arm64 go build -o dist/tasks .", "output": "dist/tasks"},
        {"id": "windows-x64", "name": "Windows (64-bit)", "build": "GOOS=windows GOARCH=amd64 go build -o dist/tasks.exe .", "output": "dist/tasks.exe"},
        {"id": "linux-x64", "name": "Linux (x64)", "build": "GOOS=linux GOARCH=amd64 go build -o dist/tasks .", "output": "dist/tasks"}
      ]
    },
    {"id": "manual", "name": "Manual", "kind": "download", "output": "MANUAL.pdf"}
  ]
}
```

## Fields

| Field | Where | Meaning |
|---|---|---|
| `version` | top level | **Required.** The file format version: `1`. |
| `image` | top level, artifact or target | Optional Docker image to build in, e.g. `golang:1.27` or `node:22-slim`. |
| `setup` | top level, artifact or target | Shell command run before the build. All levels run, in that order. |
| `artifacts` | top level | What the project offers. |
| `id` | artifact, target | Lowercase letters, digits, dots and dashes. |
| `name` | artifact, target | Shown to people. |
| `kind` | artifact | `download` (the default, and the only kind for now). |
| `targets` | download artifact | One entry per system or format. Leave it out for a single file. |
| `build` | artifact or target | Shell command, run at the project root. Optional for files that are already in the code. |
| `output` | artifact or target | The file to download. A folder ending in `/` is zipped. |

Download ids are `artifact/target` (e.g. `app/macos-arm64`). A project with a single artifact uses the bare target id (e.g. `macos-arm64`). `source` (the code as a .zip) always exists.

## The build sandbox

- Linux x86-64, with Node, Bun, Python, git, curl and jq. There is no C compiler.
- No internet, except package registries: `proxy.golang.org`, `sum.golang.org`, `go.dev`, `dl.google.com`, `registry.npmjs.org`, `registry.yarnpkg.com`, `pypi.org`, `files.pythonhosted.org`, `index.crates.io`, `static.crates.io`, `static.rust-lang.org`.
- `ph-install go` (or `ph-install go@1.27`) installs Go from go.dev and verifies the official checksum. It's a convenience, not a requirement: `setup` can be any shell command.
- Each step can take up to about 5 minutes. Downloads can be up to 500 MB.
- Build commands come from the **chosen version's** `patchhub.json`, never from a patch, so a patch can't change how things are built.

## Custom images

Set `image` to build inside a Docker container instead of the default sandbox:

```json
{"version": 1, "image": "golang:1.27-alpine", "artifacts": [
  {"id": "app", "name": "Tasks", "targets": [
    {"id": "linux-x64", "name": "Linux", "build": "GOOS=linux CGO_ENABLED=0 go build -o dist/tasks .", "output": "dist/tasks"}]}]}
```

- The project is mounted at `/work` (the working directory). `setup` and `build` run there with `sh -c`.
- Images come from Docker Hub or GitHub's registry (`ghcr.io/...`). Pin a tag or digest so builds are repeatable.
- Without `image`, builds run directly in the sandbox, and `ph-install` is available.

## Problems

The project's Settings tab lists problems with its `patchhub.json`, e.g. a missing `version` or an `output` outside the project. Entries with problems are skipped; the rest still work.
