# Site source

A site's files live in git. You clone the repository, edit it, and push.

Git is the source of truth for what a site is made of. It is git rather
than an upload API for one reason: the build sandbox needs credentials
that reach one site and nothing else, and a git credential is per
repository by nature.

## Two repositories

A site has two repositories, and the split is what lets a post go live
alone.

| Repository | Holds | Address |
|---|---|---|
| source | the site's code and design | `https://api.heyclave.party/git/<site id>.git` |
| content | the site's posts | `https://api.heyclave.party/git/<site id>.content.git` |

The build mounts the content repository at `src/content/`, so the two can
never hold one file. Every surface takes one tree: a path under
`src/content/` routes itself to the content repository.

A batch that crosses the two repositories is refused. Two commits in two
repositories are not one change.

The content repository takes an allowlist of formats. "Not code" is what
it has to promise, and a denylist promises only "not the code we thought
of".

## Files you may not change

Clave maintains some files and freezes them: `package.json`, the lockfiles
and `.npmrc`. A push that changes one is refused, by the API and by the
`pre-receive` hook.

The reason is the build. A site that edits its own dependency list builds
against a tree that the file does not describe.

`astro.config.mjs` is **not** frozen. A site that wants another
integration adds it itself.

## Where a file may go

What a site may hold is gated by where the file goes, not by one list of
formats.

- **`src/assets`** takes what the build's toolchain can read. A file the
  toolchain cannot decode is a build failure that blames the site's own
  source.
- **`public/`** is copied out as it is, and nothing opens it. It takes a
  second, shorter list — today `.pdf` alone.

`public/` must never take a format that runs script. That directory is
served from the site's own hostname, and an upload link that writes into
it travels over chat.

## Three ways a file arrives

1. **A push**, for anything in the tree.
2. **The API**, with `PUT /v1/sites/{id}/assets/{path}` for a file the
   agent already holds, `POST /v1/sites/{id}/photos` for a stock photo,
   and `POST /v1/sites/{id}/uploads` for a link the owner uses.
3. **An upload link**, which the owner opens in a browser and drops a file
   on. The link is bound to one site, it expires, it has a slot count, and
   each file has a byte cap.

One daily allowance covers all three. Read [Site source](/api/source).

## Drafts of posts

A post marked `draft: true` never reaches the live site. The filter is in
the platform, not in the site's own code: a filter in a page is code an
agent may remove, and a draft post on the live site is the one mistake
this feature must not make.

---

## Where this page sits

This is one page of the Clave documentation.
Read it as a web page at https://docs.heyclave.party/concepts/source.
Every page has a `.md` address, and https://docs.heyclave.party/llms.txt lists them all.

- Previous: [The model](https://docs.heyclave.party/concepts/model.md)
- Next: [Publishing](https://docs.heyclave.party/concepts/publishing.md)
- Index: [Overview](https://docs.heyclave.party/index.md)
