Catalogue upload
How a catalogue reaches a hosting service: the publish command and the upload envelope.
Boundary
Receivers, hosted or self-hosted, need only the published @mokly/mokly
package and its protocol documents and fixtures; Mokly Cloud has no private
protocol. mokly build is local-only. mokly publish builds into its own
private directory outside the repository, then runs the exchange so a receiver
stores only content it lacks. The mokly-upload.json envelope, the
ownership marker, and the Plan response each
carry their own version of the format inventory.
Earlier receivers reject this format with 426 under the
namespace compatibility contract.
A receiver reads Changes only from the catalogue index that the Plan archive
carries, under the
Changes count, so a summary such
as a pull request check agrees with the viewer’s Changes list.
CLI
npx mokly publish --endpoint https://api.mokly.ai/v1/projects/<projectId>/publications --token TOKEN
npx mokly publish --config tools/mokly.config.json --base main
npx mokly publish --no-changes --repository git.example.com/team/project
npx mokly publish --upload-concurrency 4Install @mokly/mokly first, or use npx --package @mokly/mokly mokly publish.
Explicit --endpoint and --token override MOKLY_ENDPOINT and MOKLY_TOKEN.
Both are required; empty values fail before the build. An endpoint URL must
not contain a fragment. The endpoint identifies the
receiver’s Plan route and must satisfy the exchange request URL boundary. For
Mokly Cloud it is
https://api.mokly.ai/v1/projects/<projectId>/publications. HTTPS is
recommended for remote services. The
exchange contract defines the
accepted URL, exact Plan target and receiver-supplied Blob and Complete URLs.
Tokens use the bearer-token grammar [A-Za-z0-9._~+/-]+=*. Tokens never
appear in CLI output, including errors and diagnostic stacks. Response bodies,
response headers and transport exception text are not printed. The exchange
contract owns redirect, retry, timeout, expiry, accounting and cancellation
behavior. --upload-concurrency <n> bounds parallel Blob requests to an
integer from 1 through 32 (default 8); another value, or the option on another
command, fails as cli-invalid. The composite action forwards no concurrency
input.
Value options also accept --name=value. Use --token=-TOKEN for a credential
beginning with -, or set MOKLY_TOKEN. Split only at the first =, preserving
token padding and URL queries. Empty assigned values fail; boolean options such
as --no-changes reject assignments. A separate value beginning with -
is ambiguous and fails as a missing value; use the assigned form instead.
publish has no --out option: it builds into an output directory inside
its own private directory outside the repository. The Mokly publish build line of the
terminal errors contract names that output, under the
static build contract and its ownership rules. --config
uses normal discovery. Comparisons are included by default: --base overrides
review.base (default origin/main). --no-changes skips base lookup and
comparison generation, including removed entries and comparison assets and
controls. It rejects an explicit --base. Both modes build and validate the
catalogue. Build warnings fail the command before output installation or upload
only with --strict. Without it, warnings print and publication continues.
Strict mode is opt-in so recoverable authoring warnings do not block publication.
After a successful publication or an already-published response, delete exactly the private directory created by this publication, including its site and reservation directory. Do not follow symbolic links. A failed or cancelled upload retains the whole private directory. If removal fails, warn with the private directory path and keep publication successful. The receiver has already accepted it. Removing only the snapshot would leave the reservation directory behind.
Publish requires a Git checkout with a commit even without comparisons, to
identify the uploaded revision. With comparisons enabled, the base side renders
from the pinned merge base’s sources under the
comparison contract. --no-changes requires no
base history. Publish requires a clean
checkout. headSha names the commit whose clean checkout produced the catalogue.
Dependencies and other ignored build products come from the local environment.
The CLI enforces this rule; a receiver cannot verify it. A receiver keeps the
first publication it completes for a headSha and configPath.
Publish checks HEAD and repository status before the build, then checks both again
before Plan. Either status check fails before any receiver request. Modified, staged, deleted, renamed, type-changed,
unmerged, untracked non-ignored paths and submodule changes anywhere in the
repository count, regardless of user Git settings. Git-ignored files and
Mokly-owned caches and temporary paths never count. Uncommitted changes fail
with git-uncommitted under the
terminal error contract.
Repository And Revision Identity
--repository <host>/<owner>/<name> overrides remote detection. Otherwise use
origin, or the sole remote if origin is absent; multiple other remotes are
ambiguous and fail. HTTPS, SSH URLs and scp-style Git remotes are supported.
Local paths are not repository identities. Remove one trailing .git suffix.
Host is lowercase DNS or IPv4 without a port. Owner may contain slash-separated
groups, such as GitLab subgroups; repository name is one segment. Segments
contain only ASCII letters, digits, ., _, and -, and cannot be . or
... Credentials in remote URLs are discarded and never printed.
Branch is GITHUB_HEAD_REF in GitHub Actions pull-request runs, otherwise
GITHUB_REF_NAME for branch runs (GITHUB_REF_TYPE=branch), otherwise Git’s
symbolic branch. Detached checkouts fall back to HEAD; headSha is always
the actual checked-out commit, never a substituted pull-request SHA.
pullRequest is the positive number in
GITHUB_REF=refs/pull/<number>/merge or /head, otherwise null. Read Actions
context only when GITHUB_ACTIONS=true. For pull-request-head semantics, check
out the head explicitly as in the action guide.
Upload Manifest
mokly-upload.json is UTF-8 JSON at the output root with exactly these fields:
interface MoklyUploadV3 {
schemaVersion: 3;
moklyVersion: string;
repository: { host: string; owner: string; name: string };
branch: string;
headSha: string;
baseRef: string | null;
baseSha: string | null;
pullRequest: number | null;
configPath: string;
exportedAt: string;
}Readers reject missing/extra upload-manifest fields and duplicate JSON keys.
moklyVersionis the installed package’s exact SemVer, including prerelease or build metadata, at most 255 UTF-8 bytes.schemaVersionversions this envelope independently of the catalogue index v9.repositoryobeys the identity grammar above; it is an assertion to authorize, not proof of repository ownership.hostis at most 253 bytes; owner and name are each at most 255 bytes.branchis nonempty, at most 255 UTF-8 bytes, with no Unicode category Cc character.headShaand non-nullbaseShaare full lowercase hexadecimal Git object ids, exactly 40 or 64 characters. Resolve HEAD before the build and reject a changed HEAD before Plan.- With a ready comparison generation,
baseRefis the effective ref. It is nonempty, at most 255 UTF-8 bytes, and has no category Cc character.baseShais the pinned merge base, never the current tip ofbaseRef.baseShaequals the index’sbaseCommit, andbaseRefequals the index’sbase, the effective ref of the pinned comparison. With disabled or unavailable Changes,baseRefandbaseShaare both null and no comparison generation is present. pullRequestis null or a positive JavaScript-safe integer.configPathis the slash-separated path from the Git repository root to the resolved config and follows the ownership marker’s portable-path grammar. The config is not included. Metadata uses the Git root even when a current-only catalogue is scoped to a subdirectory.exportedAtis UTC ISO 8601, exactlyYYYY-MM-DDTHH:mm:ss.sssZ, recorded while finalizing the build. It is client-reported time, not authorization.
The manifest is written before ownership finalization. Publish declares it as
publication metadata, so it is excluded from the deployment identity while
remaining an owned file whose exact bytes are hashed in the ownership marker.
Changing only publication metadata therefore changes the marker without
changing deploymentId, the index, the deployment file, or any detail. The Plan
archive and every Blob request use the same finalized byte snapshot as the
installed output, never a later walk of mutable output.
Output
The exchange accounting and output contract defines uploaded and unchanged counts, per-round progress, viewer URL handling, and cancellation classification. The terminal compatibility contract owns the exact summary, cancellation, and transport-failure lines, and the terminal output contract owns plain and rich rendering. Failed publication leaves the complete built directory in place; it is not rolled back.
GitHub Action
The public composite action lives at .github/actions/publish in this repo;
see its usage guide. Consumers pin
the action revision and supply an exact released @mokly/mokly version that
supports publish. It installs that package in runner temporary storage and
executes its CLI directly, independent of the consumer’s local CLI version.
Endpoint, token, config and base are forwarded through environment values and
quoted arguments. Consumer dependencies and Git history must already be
installed. The action exposes no upload-concurrency input.