Skip to main content

Manifest Reference

A plugin has two manifest shapes:

FileWho writes itWhen
plugin.tomlYouEdited by hand at the plugin root.
dist/plugin.jsonssg plugins buildGenerated at build time — never edit.

plugin.json is canonical: that's what the SSG daemon and the hub consume. plugin.toml is the ergonomic surface — TOML so author-facing fields can be grouped into tables, and so build-only fields like [build].command don't bleed into the runtime manifest.

plugin.toml

# Required identity
id = "my-plugin"
version = "0.1.0"
name = "My Plugin"
description = "A one-line summary (≤ 160 chars)."
category = "tools"
entry = "index.html"
min_ssg_version = "0.29.0"

# Optional icon — relative to dist/, or a data: URL.
# icon = "logo.svg"

# At least one [[nav]] entry is required.
[[nav]]
label = "My Plugin"
path = "/plugins/my-plugin/"
section = "tools"
order = 100

# Optional build step. Anything that writes to dist/ works.
[build]
command = "pnpm install --silent && pnpm build"

Required fields

FieldTypeConstraints
idstringSlug: [a-z0-9][a-z0-9-]{1,39}. Mounts at /plugins/<id>/. Immutable — choose carefully.
versionstringSemver (0.1.0, 1.0.0-rc.1).
namestringHuman display name.
descriptionstring≤ 160 chars. Shown in the marketplace card.
categorystringOne of tools, observability, productivity, integrations, developer.
entrystringRelative path inside dist/ of the SPA entry HTML. Default index.html.
min_ssg_versionstringSemver. Install refuses on older SSG.
[[nav]]table-arrayAt least one. Each contributes a sidebar entry.

Optional fields

FieldTypeNotes
iconstringPath inside dist/ (logo.svg) or data: URL.
[build].commandstringRun from plugin root before packaging.
FieldTypeNotes
labelstringSidebar text.
pathstringMust start with /plugins/<id>/.
sectionstringtools (default), activity, or settings.
ordernumberLower sorts higher. Default 100.

plugin.json (generated)

ssg plugins build writes this file into dist/plugin.json. It adds three runtime-critical fields the TOML doesn't have:

FieldHow it's filled
artifact_urlBlank locally; ssg plugins publish rewrites it to the GitHub release asset URL.
artifact_sha256SHA-256 of the .tar.gz produced this build.
artifact_signatureEd25519 signature over artifact_sha256, signed with ~/.sigmashake/keys/signing.key.
signing_fingerprintThe corresponding public key (hex SPKI).

Example (truncated):

{
"id": "my-plugin",
"version": "0.1.0",
"name": "My Plugin",
"description": "A one-line summary.",
"entry": "index.html",
"artifact_url": "https://github.com/you/ssg-plugin-my-plugin/releases/download/v0.1.0/my-plugin-0.1.0.tar.gz",
"artifact_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"artifact_signature": "0badcafe…",
"signing_fingerprint": "302a300506032b6570032100…",
"min_ssg_version": "0.29.0",
"nav": [
{"label": "My Plugin", "path": "/plugins/my-plugin/", "section": "tools", "order": 100}
]
}

Tarball layout

The artifact is a gzipped tarball with this layout at root:

./dist/
./dist/<entry> # the file referenced by manifest.entry
./dist/... # everything else under dist/
./plugin.json # (optional) embedded copy — installer falls back to fetching from manifest_url

The installer rejects tarballs without ./dist/<entry> — that's the single mandatory invariant. Everything else inside dist/ is served at /plugins/<id>/<path> verbatim with appropriate MIME detection.

Size cap

Artifacts must be ≤ 50 MB. Larger plugins should host bulk assets off the plugin tarball (CDN, R2) and lazy-load them at runtime.

Forward compatibility

The plugin manifest parser tolerates unknown fields — newer SSG versions can add fields without breaking older plugins, and newer plugins can carry extra metadata without breaking older SSG installs. Don't rely on this for anything load-bearing.

Validation

You can validate a plugin.toml without doing a full build:

ssg plugins build --dry-run # (planned — same validation, no tar)

For now, just run ssg plugins build — it'll fail fast with a clear error if anything is malformed.

See also