# Serving the GRIP artifacts

Handoff for the foxora.dev site work. Source tree: `foxora-runtime/spec/grip/`.

## The one hard requirement

The schema `$id` values are **absolute and baked into the files**:

```
https://grip.foxora.dev/schema/1.0-draft.2/common.schema.json
https://grip.foxora.dev/schema/1.0-draft.2/mandate.schema.json
https://grip.foxora.dev/schema/1.0-draft.2/grant.schema.json
https://grip.foxora.dev/schema/1.0-draft.2/receipt.schema.json
https://grip.foxora.dev/schema/1.0-draft.2/stop.schema.json
https://grip.foxora.dev/schema/1.0-draft.2/settlement.schema.json
https://grip.foxora.dev/schema/1.0-draft.2/context.schema.json
```

**These URLs must resolve to those exact files.** Other people's validators
fetch `$id` and `$ref` by URL; a 404 makes the schemas unusable outside this
repo. `common.schema.json` is referenced by relative `$ref` from the other six,
so all seven must sit in the same directory.

## Layout

The deploy took the site root of `grip.foxora.dev`, so the paths are one level
shallower than the tree here — the artifacts sit at the root and the spec's own
documents sit under `/spec/`:

```
/                          → the GRIP sheet (index.html)
/schema/1.0-draft.2/       → the 7 schemas, at the URLs their $ids name
/registry/                 → the 5 registry JSON files
/vectors/                  → 7 vector dirs, 80 signed objects
/tools/                    → gripcore.py, grip_verify.py, make_vectors.py
/conformance/              → run.py
/spec/                     → README.md, this file, LICENSE, CHECKSUMS.txt
/paper/                    → the paper, the ALM companion, CITATION.bib
```

Version the path, never overwrite it. When `1.0` is released, publish
`schema/1.0/` alongside `schema/1.0-draft.2/` and leave the draft in place
forever — an `$id` that changes meaning is worse than one that 404s.

## Headers

| Path | Content-Type |
| --- | --- |
| `*.schema.json` | `application/schema+json` |
| other `*.json` | `application/json` |
| `*.py` | `text/plain; charset=utf-8` (serve as source, do not download-prompt) |

Enable CORS (`Access-Control-Allow-Origin: *`) on everything under the site root.
Validators fetch these cross-origin; without it they fail in browsers.

Cache: `max-age=31536000, immutable` on the versioned schema path — it never
changes. Short cache on the site root itself.

## /.well-known/grip

GRIP-0 conformance requires a trust bundle at `/.well-known/grip`
(§4.7, Appendix B). Not needed to publish the spec, but if foxora.dev ever
claims a conformance level, that path has to serve a real bundle. See
`vectors/refund-happy-path/trust-bundle.json` for the shape.

## Published — 2026-08-24

These paths are live at the `grip.foxora.dev` site root, served from
`foxora-web/sites/grip/`, and the served bytes match this tree. The versioned
schema path resolves every baked `$id`.

The name is now effectively fixed: `$id`s are permanent identifiers, and anyone
may implement against them from here on. The **Grip Security trademark question
was still recorded open** when this went live (gate (1) in the paper header) —
it is no longer a pre-publication gate, it is a rename cost, and a rename means
publishing a new `$id` namespace and leaving `1.0-draft.2/` served forever.

## Verifying the deploy

Fetch every artifact and compare it against this tree, then run the suite on
what was served rather than on what was uploaded — a rewritten line ending
changes a digest, and a changed digest breaks every signature downstream of it.

```
curl -sI https://grip.foxora.dev/schema/1.0-draft.2/mandate.schema.json
curl -s  https://grip.foxora.dev/registry/denial-reasons.json | head -5
```

**Verified 2026-08-24.** All 111 served artifacts are byte-identical to
`spec/grip/`, and `conformance/run.py`, run against the downloaded copies,
returns 7/7 — the chains verify from the public URLs, not just from disk.
