Content pinning and the GitHub App
Reads can be pinned to a numbered release, so a build takes content as a version rather than as whatever the API returns. myna.lock records the pin, and connecting a repository makes publishing open a pull request that moves it.
Pinned reads
Pass a release to the client and every read answers with what that release served.
import { createMyna } from "@myna-sh/sdk";
const myna = createMyna({ project: "my-site", release: 42 });
await myna.entries.list("posts"); // as release 42 served it
await myna.entries.list("posts", { release: 41 }); // an older one
await myna.entries.list("posts", { release: null }); // live
Over HTTP the parameter is at, and it takes a release number, release-42, or an ISO 8601 instant, which resolves to the newest release published at or before it. Filters, q, ordering, and keyset pagination apply to the pinned set and are still evaluated by the database. include resolves embedded entries at the same release.
An entry is addressed by the slug it carried at that release, so a URL built from a pinned read resolves at that release after a rename. Pinned responses carry Cache-Control: public, max-age=31536000, immutable.
Collection visibility and editorial order are read live: entries.rank carries no revision, and visibility is an access decision rather than content. See reading content.
myna.lock
myna pull # pin to the newest release
myna pull --release 42 # pin to a specific one
myna pull --check # non-zero exit when the pin is behind
The file names a project, a release, and the date it published. Commit it, and the build reads at it:
import { readLock } from "@myna-sh/sdk/lock";
const lock = readLock();
const myna = createMyna({
project: "my-site",
...(lock ? { release: lock.release } : {}),
});
@myna-sh/sdk/lock is a Node-only entry point. No lockfile means live reads, so a repository that has not adopted it is unaffected.
Pull requests
Install the Myna GitHub App, then open Settings → GitHub on a project and pick a repository. Publishing opens a pull request that moves myna.lock, with the field-level diff of every release in the span as its description. The repository's own checks run against the pinned content.
The app asks for Contents and Pull requests, read and write, on the repositories you select. Myna stores the installation id; the token that writes the pull request is requested per operation and expires within the hour.
One branch per project and release, so a redelivered event or a retry finds the branch and opens nothing.
myna pull --open-pr
myna pull --open-pr
myna pull --open-pr --run 'pnpm build:content'
Opens the same pull request from CI, using git and the GitHub CLI. Use it when the repository is not on GitHub, or when the build generates something from content — --run regenerates that artifact at the new pin and commits it alongside, which the app cannot do because it does not run your build.
Releases
myna releases diff 42
Field by field, for a release that already shipped.
Upgrading
Nothing to change. Pinning is opt-in and unpinned reads behave as before. GET /v1/meta advertises content.at and github.app.
Affects sdk · cli · api · dashboard
