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
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
name: plugin-nerdctl
name: plugin-containerd
description: The out-of-tree charly ENGINE plugin serving engine:containerd (verb:containerd
/ command:containerd) — every engine op delegates to container.InvokeEngineOp. `containerd`
is the engine word; `nerdctl` is only the CLI binary it is driven through. Use when working
with the engine:containerd provider, the plugin-nerdctl candy, or the engine provider-class
with the engine:containerd provider, the plugin-containerd candy, or the engine provider-class
placement model.
---

# plugin-nerdctl
# plugin-containerd

The out-of-tree charly ENGINE plugin serving `engine:containerd` (plus the
`verb:containerd` / `command:containerd` convenience words).
Expand Down Expand Up @@ -55,7 +55,25 @@ mode-split workload users, `address`, `namespace`, `privilege_prefix`,
`substrates`, `runtime_handlers`) is served by the spec module's engine body —
one table, no copy here.

## The read-only check verb (`verb:containerd`)

`containerd: <method>` is a READ-ONLY check verb (W3.9). Its typed input is
`#ContainerdInput` with `method` as the PRIMARY field, so `containerd: info`
desugars to `{method: "info"}`. It drives the venue's own tooling over the host
executor reverse channel; each method catches a defect nothing else does:

| method | measures | visible skip (names the mechanism) |
|---|---|---|
| `info` | daemon id + mode from `nerdctl info` **SecurityOptions** (never `Plugins`) | — |
| `runtimes` | the declared CRI handlers (`crictl info`) | `crictl` not installed |
| `cdi_devices` | devices in the **client's** CDI spec dirs (`/etc/cdi`, `/var/run/cdi`) | — (an absent dir is an empty, passing list; a malformed spec FAILS) |
| `criu_preflight` | `criu check --unprivileged --feature seccomp_suspend`: rc **and** the supported line; `--extra` is never used | `criu` not installed |
| `krun_guest` | one throwaway probe guest (`--rm`): kernel, arch, CPU features | no probe `image:`, no `/dev/kvm`, handler not declared |
| `image` | an image present in a **named** namespace | — |

A skip is never a silent pass: an assertion that cannot run is not green.

## Use when

Working with the `engine:containerd` provider, the plugin-nerdctl candy, or the
engine provider-class placement model.
Working with the `engine:containerd` provider, the `containerd:` check verb, the
plugin-containerd candy, or the engine provider-class placement model.
43 changes: 25 additions & 18 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
# AGENTS.md — plugin-nerdctl
# AGENTS.md — plugin-containerd

Standalone out-of-tree plugin repo serving the `engine:containerd` engine backend
(`engine:containerd` + `verb:containerd` + `command:containerd`). `containerd` is
the ENGINE WORD; `nerdctl` is only the CLI binary it is driven through (the
CUE-owned `#EngineBinaries` map), never an engine word. The plugin is a Go
module at `candy/plugin-nerdctl/` (module path
`github.com/opencharly/plugin-nerdctl/candy/plugin-nerdctl`); the root
module at `candy/plugin-containerd/` (module path
`github.com/opencharly/plugin-containerd/candy/plugin-containerd`); the root
`charly.yml` declares `discover: box` + `discover: candy` so the repo is a
project and its candy is scanned.

Canonical files:

- `candy/plugin-nerdctl/charly.yml` — the `plugin-nerdctl:` candy entity
(`plugin:` block, `plan:` checks) and the embedded `plugin-nerdctl-skill:`
- `candy/plugin-containerd/charly.yml` — the `plugin-containerd:` candy entity
(`plugin:` block, `plan:` checks) and the embedded `plugin-containerd-skill:`
skill entity.
- `candy/plugin-nerdctl/` — the Go source: `plugin.go` (provider + meta + the
containerd start-plan policy), `schema/plugin.cue`, `cmd/serve/main.go`.
- `.agents/skills/plugin-nerdctl/SKILL.md` — the skill body, the one canonical
base the corpus projects `/charly-internals:plugin-nerdctl` from. The candy's
`plugin-nerdctl-skill:` entity is the owner registration paired with that body.
- `candy/plugin-containerd/` — the Go source: `plugin.go` (provider + meta + the
containerd start-plan policy), `verb.go` (the READ-ONLY `containerd:` check
verbs), `schema/plugin.cue`, `params/` (generated from the schema by
`params/gen.sh`; never hand-edit `cue_types_gen.go`), `cmd/serve/main.go`.
- `.agents/skills/plugin-containerd/SKILL.md` — the skill body, the one canonical
base the corpus projects `/charly-internals:plugin-containerd` from. The candy's
`plugin-containerd-skill:` entity is the owner registration paired with that body.
- `README.md` — user overview only; never agent guidance.

The repo carries only the `tag-on-merge` caller (`.github/workflows/`); the
org-wide required workflow is what gates the merge.

## Load these skills first (R0)

- `/charly-internals:plugin-nerdctl` — the `engine:containerd` provider reference
(projected from this repo's `.agents/skills/plugin-nerdctl/SKILL.md`). Load
- `/charly-internals:plugin-containerd` — the `engine:containerd` provider reference
(projected from this repo's `.agents/skills/plugin-containerd/SKILL.md`). Load
before changing an engine op.
- `/charly-internals:plugin` — the plugin authoring reference: the `plugin:`
block, the unified Provider model, the per-plugin CUE-schema contract,
Expand All @@ -38,9 +40,14 @@ org-wide required workflow is what gates the merge.

## Build / validate / test

- `go build ./...` in `candy/plugin-nerdctl/` — compile the plugin module.
- `go test ./...` in `candy/plugin-nerdctl/` — the plugin's Go tests, including
the out-of-process go-plugin round trip (skipped with `-short`).
- `go build ./...` in `candy/plugin-containerd/` — compile the plugin module.
- `go test ./...` in `candy/plugin-containerd/` — the plugin's Go tests, including
the out-of-process go-plugin round trip (skipped with `-short`). The live
containerd check-verb arm is opt-in: `CHARLY_CONTAINERD_LIVE=1 go test ./...
-run TestVerbLiveLocal` (and `CHARLY_CONTAINERD_LIVE_IMAGE=<ref>` for the
`image`/`krun_guest` methods); without it the arm skips visibly.
- `go generate ./params` in `candy/plugin-containerd/` — regenerate
`params/cue_types_gen.go` from `schema/plugin.cue`; a second run is a no-op.
- `charly box validate` at the repo root — the structural check (the candy +
`plugin:` block, CUE schema).
- The merge gate is the **org-wide** `charly/pr-validator` (required check
Expand All @@ -49,7 +56,7 @@ org-wide required workflow is what gates the merge.

## Modify this repo

- Edit the `plugin-nerdctl:` candy entity, the Go source, and `schema/plugin.cue`
- Edit the `plugin-containerd:` candy entity, the Go source, and `schema/plugin.cue`
**together** — the schema is the served declaration surface.
- Keep the delegation to `container.InvokeEngineOp("containerd", …)` in the spec
module: the engine class has ONE implementation served from either placement.
Expand All @@ -61,9 +68,9 @@ org-wide required workflow is what gates the merge.
- Keep `nerdctl` out of the engine vocabulary. It is the CLI binary named by
`spec.EngineBinaries["containerd"]`; a second engine spelling is a cutover
regression.
- Keep `.agents/skills/plugin-nerdctl/SKILL.md` in step with any provider-surface
- Keep `.agents/skills/plugin-containerd/SKILL.md` in step with any provider-surface
change: it is the skill BODY, the one canonical base the corpus reader resolves.
The candy's `plugin-nerdctl-skill:` entity is the owner registration the reader
The candy's `plugin-containerd-skill:` entity is the owner registration the reader
pairs with that body (name/family/owner only — it carries no body); update its
metadata only when the name/family/owner changes.

Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# plugin-nerdctl
# plugin-containerd

The out-of-tree charly ENGINE plugin serving `engine:containerd` — the containerd
engine backend for OpenCharly pods, plus the `verb:containerd` /
Expand All @@ -20,7 +20,7 @@ class has one implementation and two placements.
| Capability | Surface |
|---|---|
| `engine:containerd` | the engine provider-class op envelope: `describe` / `binary` / `gpu_args` / `start_plan` / `unit_emit` / `network_ensure` |
| `verb:containerd` | the `containerd:` convenience verb |
| `verb:containerd` | the read-only `containerd:` check verb: `info` / `runtimes` / `cdi_devices` / `criu_preflight` / `krun_guest` / `image` |
| `command:containerd` | the `charly containerd` convenience command |

The start plan always names an OCI runtime handler (nerdctl's unqualified default
Expand All @@ -42,7 +42,7 @@ containerd/buildkit/CNI stack) to deploy a pod with `engine: containerd`.
Compose the plugin candy alongside the layer candy in a box's `candy:` list:

```yaml
- '@github.com/opencharly/plugin-nerdctl/candy/plugin-nerdctl:<tag>'
- '@github.com/opencharly/plugin-containerd/candy/plugin-containerd:<tag>'
- '@github.com/opencharly/layer-nerdctl/candy/layer-nerdctl:<tag>'
```

Expand All @@ -54,18 +54,18 @@ engine: containerd

## Layout

- `candy/plugin-nerdctl/` — the plugin module: `plugin.go` (provider + meta +
the containerd start-plan policy), `schema/plugin.cue`, and
`cmd/serve/main.go`.
- `.agents/skills/plugin-nerdctl/SKILL.md` — the skill body, the one canonical
base the corpus projects `/charly-internals:plugin-nerdctl` from. The candy's
`plugin-nerdctl-skill:` entity is the owner registration paired with that body
- `candy/plugin-containerd/` — the plugin module: `plugin.go` (provider + meta +
the containerd start-plan policy), `verb.go` (the read-only `containerd:` check
verbs), `schema/plugin.cue`, `params/` (generated), and `cmd/serve/main.go`.
- `.agents/skills/plugin-containerd/SKILL.md` — the skill body, the one canonical
base the corpus projects `/charly-internals:plugin-containerd` from. The candy's
`plugin-containerd-skill:` entity is the owner registration paired with that body
(name/family/owner only); it carries no body.
- `charly.yml` — the root project manifest (`discover: box` + `discover: candy`).

## Related

- Owning skill: `/charly-internals:plugin-nerdctl` — the engine:containerd
- Owning skill: `/charly-internals:plugin-containerd` — the engine:containerd
provider reference (projected from this repo's skill body).
- `/charly-internals:plugin` — the plugin/provider model and placement.
- [`opencharly/charly`](https://github.com/opencharly/charly) — the charly CLI.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# plugin-nerdctl — the out-of-tree charly ENGINE plugin serving `engine:containerd`.
# plugin-containerd — the out-of-tree charly ENGINE plugin serving `engine:containerd`.
#
# A standalone Go module served over go-plugin gRPC via the charly plugin SDK; the
# charly loader host-builds ./cmd/serve and connects it OUT-OF-PROCESS. It answers
Expand All @@ -9,7 +9,7 @@
# `containerd` is the ENGINE WORD. `nerdctl` is only the CLI binary that drives it
# (#EngineBinaries maps containerd -> nerdctl); the retired engine spelling is gone
# (R5, no alias).
plugin-nerdctl:
plugin-containerd:
candy:
description: |
The out-of-tree charly ENGINE plugin serving engine:containerd (plus the
Expand All @@ -27,7 +27,15 @@ plugin-nerdctl:
renderer and its envelope carries no scope field: a rootful unit is a user
unit whose ExecStart is the (prefixed, --runtime-bearing) start-plan argv
the caller supplies, and this provider does not re-apply the policy at the
unit boundary. A standalone Go module
unit boundary. The verb:containerd word additionally serves the READ-ONLY
check verbs (W3.9), each able to FAIL on a defect nothing else catches:
info (daemon id + mode from nerdctl info SecurityOptions — Plugins is never
a mode source), runtimes (the declared CRI handlers via crictl info),
cdi_devices (the CLIENT's CDI spec dirs), criu_preflight (rc AND the
seccomp_suspend line; --extra is never used), krun_guest (one throwaway
probe guest: kernel + CPU features) and image (present in a named
namespace). A capability the venue lacks is a visible skip naming the
mechanism, never a silent pass. A standalone Go module
(go.mod + cmd/serve) host-built and connected over go-plugin gRPC. Composes
with the layer-nerdctl candy (the packages + rootless
containerd/buildkit/CNI stack) to deploy a pod with engine: containerd.
Expand All @@ -36,7 +44,9 @@ plugin-nerdctl:
- engine:containerd
- verb:containerd
- command:containerd
source: github.com/opencharly/plugin-nerdctl/candy/plugin-nerdctl
source: github.com/opencharly/plugin-containerd/candy/plugin-containerd
primary:
containerd: method
plan:
- check: the out-of-tree engine:containerd plugin ships a buildable Go module (go.mod + cmd/serve) the host can build + serve out-of-process
id: containerd-module-present
Expand All @@ -48,13 +58,18 @@ plugin-nerdctl:
context:
- build
command: grep -q 'engine:containerd' charly.yml
- check: the served schema declares the typed verb:containerd input and its full method set
id: containerd-check-verbs-declared
context:
- build
command: grep -q '#ContainerdInput' schema/plugin.cue && grep -q 'criu_preflight' schema/plugin.cue && grep -q 'krun_guest' schema/plugin.cue

# ---- HARNESS SKILL ----
# Owner registration for the skill body at .agents/skills/plugin-nerdctl/SKILL.md,
# Owner registration for the skill body at .agents/skills/plugin-containerd/SKILL.md,
# the one canonical base the corpus reader resolves. This entity names the owner
# (name/family/owner) and carries no body; the body is the file at the repo root.
plugin-nerdctl-skill:
plugin-containerd-skill:
skill:
name: plugin-nerdctl
name: plugin-containerd
family: internals
owner: plugin-nerdctl
owner: plugin-containerd
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
package main

import (
nerdctl "github.com/opencharly/plugin-nerdctl/candy/plugin-nerdctl"
containerd "github.com/opencharly/plugin-containerd/candy/plugin-containerd"
"github.com/opencharly/sdk"
)

func main() { sdk.Serve(nerdctl.NewProvider(), nerdctl.NewMeta()) }
func main() { sdk.Serve(containerd.NewProvider(), containerd.NewMeta()) }
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package nerdctl
package containerd

// containerd_engine_test.go — the engine-word cutover contract.
//
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/opencharly/plugin-nerdctl/candy/plugin-nerdctl
module github.com/opencharly/plugin-containerd/candy/plugin-containerd

go 1.26.8

Expand All @@ -7,6 +7,7 @@ require (
github.com/hashicorp/go-plugin v1.8.0
github.com/opencharly/sdk v0.2026294.1520
github.com/opencharly/spec v0.2026295.1243
go.yaml.in/yaml/v3 v3.0.5
)

require (
Expand All @@ -26,7 +27,6 @@ require (
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20260217160748-a481f6a22f94 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/sys v0.48.0 // indirect
golang.org/x/text v0.42.0 // indirect
Expand Down
File renamed without changes.
73 changes: 73 additions & 0 deletions candy/plugin-containerd/params/cue_types_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions candy/plugin-containerd/params/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package params

//go:generate sh ./gen.sh
30 changes: 30 additions & 0 deletions candy/plugin-containerd/params/gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/sh
# gen.sh — regenerate params/cue_types_gen.go from the ONE source,
# ../schema/plugin.cue.
#
# The served schema MUST stay package-less (the host splices it onto its base as
# `base ++ plugin`; a package clause in the appended body would be a syntax
# error). `cue exp gengotypes` can only generate from a NAMED package, so this
# script stages a copy with the params package clause, generates from it, and the
# generator writes cue_types_gen.go into this directory. A second run is a
# byte-identical no-op.
set -eu

here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

{
echo "package params"
echo
cat "$here/../schema/plugin.cue"
} >"$tmp/plugin.cue"

cd "$here"
cue exp gengotypes "$tmp/plugin.cue"

[ -f cue_types_gen.go ] || {
echo "gen.sh: cue emitted no cue_types_gen.go" >&2
exit 1
}
gofmt -w cue_types_gen.go
Loading