Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ docs/reference/generated/*.md text eol=lf
# hashed). Pin them to LF so Windows checkouts don't convert them to CRLF and
# break the byte-identical key match (off-vs-on testbench, #611/#498).
rust/eval/testbench/** text eol=lf

# Registry snapshots are generated by `gen_registry` with LF and compared
# byte-for-byte in CI and lib tests (GH #726). Same rationale as the
# generated docs above.
rust/data/addon_registry.json text eol=lf
rust/data/grammar_registry.json text eol=lf

3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,9 @@ jobs:
- name: Check generated reference docs are current
working-directory: rust
run: cargo run --example gen_docs --features dev-tools -- --check
- name: Check bundled registry snapshots are canonical
working-directory: rust
run: cargo run --example gen_registry --features dev-tools -- --check
- name: Check committed testbench recording is current
working-directory: rust
run: cargo run --example gen_testbench_recording --features dev-tools -- --check
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
## [3.9.2] — 2026-07-06

### Added
- **Unified distribution, Phase 2 (GH #724/#726): self-service addon
publishing + hosted installs.** New `lean-ctx addon publish --namespace
<ns>` wraps the authoring `lean-ctx-addon.toml` verbatim into a signed
`kind=addon` context package and uploads it through the existing ctxpkg
publish path — after local gates that mirror the hosted listing bar
(schema, runnable `[mcp]` endpoint, audit verdict: blocking findings
refuse, `review` publishes disclosed; `--check` runs everything without
network I/O). `lean-ctx addon add <ns>/<name>[@version]` installs hosted
packs: index-verified download, integrity hashes, **mandatory** ed25519
signature, kind ↔ payload coherence — then the embedded manifest walks the
unchanged consent → preflight → health-probe pipeline; `addon update`
re-resolves from the install source. The context registry refuses to
import `kind=addon` packs (wrong trust chain, use `addon add`). The
bundled registries are now generated snapshots: `gen_registry` validates,
sorts and canonicalizes `rust/data/{addon,grammar}_registry.json`;
CI + preflight fail on drift (deterministic, timestamp-free, #498).
- **Unified distribution, Phase 1 (GH #724/#725): managed addon binaries +
the `kind` package taxonomy.** `.ctxpkg` manifests gain an optional `kind`
field (`context` | `skills` | `addon` | `grammar`; default `context`,
Expand Down
44 changes: 39 additions & 5 deletions docs/guides/addons.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ instead of being encapsulated by it.
lean-ctx addon list # installed addons + the registry
lean-ctx addon search markdown # search the registry (empty = list all)
lean-ctx addon info <name> # details + the MCP wiring it would add
lean-ctx addon add <name> # install (asks for confirmation)
lean-ctx addon add <name> # install from the curated registry
lean-ctx addon add acme/tool # install a hosted pack from ctxpkg.com
lean-ctx addon remove <name> # uninstall
```

Expand All @@ -88,6 +89,14 @@ scripts. Installing an addon enables the MCP gateway (`gateway.enabled = true`);
its tools become reachable via `ctx_tools` (find/call) — restart your MCP client
to pick them up.

A `<namespace>/<name>` target resolves against the hosted ctxpkg registry
(GH #726): lean-ctx downloads the signed `kind=addon` pack, verifies the
artifact hash against the registry index, the pack's integrity hashes, its
**mandatory** ed25519 signature and the kind ↔ payload coherence — then the
embedded manifest walks the exact same consent → preflight → health-probe
pipeline as a local or curated install. `@version` pins a release;
`addon update` re-resolves from wherever the addon was installed.

### Install on add — artifacts, ephemeral runners & the `[install]` block

There are three ways `add` makes a tool runnable, all pinned and disclosed —
Expand Down Expand Up @@ -270,9 +279,31 @@ sha256 = "…the digest…" # the gateway refuses a mismatch, fail-clo
A pinned binary is one of the requirements for the verified/paid tier (see the
audit gate below).

### 4. Get listed in the registry
### 4. Publish it

Two distribution channels, one trust chain:

**Self-service — `addon publish` (GH #726).** Ship without waiting for a
review cycle: your `lean-ctx-addon.toml` is wrapped verbatim into a signed
`kind=addon` context package and uploaded to the hosted ctxpkg registry.

```bash
lean-ctx addon publish --namespace <your-handle> --check # every gate, no upload
CTXPKG_TOKEN=ctxp_… lean-ctx addon publish --namespace <your-handle>
```

`publish` refuses locally what the registry would refuse remotely — schema
errors, a missing runnable `[mcp]` endpoint, an empty description, and every
blocking audit finding (shell-exec wiring, non-HTTPS endpoints, malware
heuristics, under-declared capabilities). A `review`-level audit publishes
with the findings disclosed. After that, anyone installs it with:

```bash
lean-ctx addon add <your-handle>/my-addon
```

Open a merge request adding your manifest as an entry to
**Curated default catalog.** For the addons every lean-ctx binary should know
about offline, open a merge request adding your manifest as an entry to
`rust/data/addon_registry.json`:

```json
Expand All @@ -296,11 +327,14 @@ Open a merge request adding your manifest as an entry to
}
```

Before opening the merge request, validate the registry locally — the same bar
CI enforces:
Before opening the merge request, validate and canonicalize the registry
locally — the same bar CI enforces. The registry files are **generated
snapshots**: `gen_registry` sorts entries by name and writes one canonical
form, and CI fails on any byte drift, so hand-edits can't diverge:

```bash
lean-ctx addon registry validate rust/data/addon_registry.json
cargo run --example gen_registry --features dev-tools # canonicalize in place
```

Once merged, everyone can run `lean-ctx addon add my-addon`, and your addon
Expand Down
47 changes: 33 additions & 14 deletions docs/specs/unified-distribution-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,20 @@ content embeds the addon manifest plus artifact references:
},
"content": {
"addon": {
"manifest_toml": "<verbatim lean-ctx-addon.toml>",
"artifacts": {
"aarch64-apple-darwin": { "url": "…", "sha256": "…", "byte_size": 4200000 },
"x86_64-unknown-linux-gnu": { "url": "…", "sha256": "…", "byte_size": 4600000 },
"x86_64-pc-windows-msvc": { "url": "…", "sha256": "…", "byte_size": 4800000 }
}
"manifest_toml": "<verbatim lean-ctx-addon.toml>"
}
}
}
```

Since Phase 1 the per-platform artifact references live **inside the TOML**
(`[artifacts.<target-triple>]` tables, GH #725) — the pack payload embeds the
TOML verbatim and adds nothing beside it, so there is no second copy that
could drift. Kind ↔ payload coherence is enforced on both ends
(`validate_kind_coherence`): a `kind=addon` pack must embed a parseable,
valid, runnable addon manifest whose name/version match the pack's; any
other kind must not carry an `addon` payload.

The artifact shape is **`GrammarAsset` generalised** (`filename`/`url`/`sha256`
— same fields, same semantics, one struct shared by both kinds after Phase 1).
Artifacts may be hosted anywhere (GitHub Releases is the expected default —
Expand Down Expand Up @@ -264,16 +267,31 @@ Each phase closes a construction site and is releasable alone.
full determinism suite green.
- Closes: "where does the addon binary come from" — permanently.

### Phase 2 — Publish flow + registry consolidation (target: next release after Phase 1)
### Phase 2 — Publish flow + registry consolidation (client side: v3.9.2)

- `lean-ctx addon publish`: builds the `kind=addon` pack from
`lean-ctx-addon.toml` + CI artifact URLs/hashes, signs with publisher key,
uploads via existing `remote.rs` publish path; verified-publisher +
audit gate (`core/addons/audit.rs`) enforced server-side before listing.
- ctxpkg registry API: `kind` filter on resolve/search/catalog.
- `addon_registry.json` + `grammar_registry.json` become CI-generated
snapshots (deterministic generator + drift check); hand-editing ends.
- leanctx.com/addons renders the `kind=addon` catalog view from ctxpkg.com.
`lean-ctx-addon.toml` (artifact URLs/hashes live in its `[artifacts]`
tables), gates locally (schema + audit + runnable endpoint), signs with the
publisher key, uploads via the existing `remote.rs` publish path; `--check`
runs every gate without network I/O. The audit gate
(`core/addons/audit.rs`) is additionally enforced server-side before
listing.
- `lean-ctx addon add <ns>/<name>[@version]` resolves the hosted pack:
index → hash-verified download → full verification (integrity +
**mandatory** signature + kind coherence) → embedded TOML enters the
normal consent/preflight/probe pipeline. `addon update` re-resolves from
the source it was installed from. The context registry refuses to import
`kind=addon` packs (they must go through the addon trust chain).
- `addon_registry.json` + `grammar_registry.json` are generated snapshots
(`gen_registry`, deterministic + timestamp-free) with a drift check in CI
and preflight; hand-editing ends.
- Server side, registry API (shipped with this phase): publish-time `kind`
validation (unknown kinds 400, non-context kinds require schema v2,
structural kind ↔ `content.addon` coherence), `kind` persisted per package
and exposed in every catalog/search/publisher/package response, and an
optional `?kind=` filter on `index.json`, `search` and the publisher feed
(unknown values 400). Remaining, tracked in GH #726: leanctx.com/addons
renders the `kind=addon` catalog view from ctxpkg.com.
- Closes: double-registry maintenance; "two marketplaces" story.

### Phase 3 — `kind=skills` + dependency resolution (target: after Phase 2)
Expand Down Expand Up @@ -331,3 +349,4 @@ Each phase closes a construction site and is releasable alone.
- Epic + phase issues: GitHub `yvgude/lean-ctx` (see epic issue for links).
- GitLab mirror (scoped labels, `status::…`): pending token renewal
(`glab auth login --hostname gitlab.pounce.ch`), then mirror per parity rule.

6 changes: 5 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ name = "gen_docs"
path = "src/bin/gen_docs.rs"
required-features = ["dev-tools"]

[[example]]
name = "gen_registry"
path = "src/bin/gen_registry.rs"
required-features = ["dev-tools"]

[[example]]
name = "gen_rules"
path = "src/bin/gen_rules.rs"
Expand Down Expand Up @@ -472,4 +477,3 @@ harness = false
[[bench]]
name = "efficiency"
harness = false

Loading
Loading