Skip to content
Open
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
24 changes: 16 additions & 8 deletions .claude/skills/plotjuggler-plugin/references/parser-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,24 @@ embedded bytes; catalog ingestion validates the complete JSON transactionally.
## Build

```cmake
find_package(plotjuggler_sdk 0.22 REQUIRED COMPONENTS parser_module)
find_package(plotjuggler_sdk 0.24 REQUIRED COMPONENTS parser_module)

pj_add_parser_module(raw_mono_image_parser
SOURCE raw_mono_image_parser.cpp
MANIFEST raw_mono_image_parser.module.json
TARGETS native
TARGETS native wasm # either, or both from the same source
)
```

The helper embeds the manifest, hides every non-ABI symbol, and exports the
complete native `pj_module_*` set. SDK 0.22 accepts only `TARGETS native`;
requesting `TARGETS wasm` stops configuration with “wasm support arrives with
the SDK wasm loader milestone”. The shipped WASI check is structural
conformance testing, not a wasm authoring or execution target.
complete native `pj_module_*` set. `TARGETS wasm` (SDK 0.24+) requires
`PJ_WASI_SDK_ROOT` pointing at wasi-sdk 27: it builds a C++17 WASI reactor
with exceptions disabled, omits the native manifest address/length exports,
embeds the manifest in the `pj_parser_module_manifest` custom section via the
installed `pj-wasm-embed-manifest` tool, and audits the export set post-link.
Wasm reactors import nothing and must declare a linear-memory maximum
(default 256 MiB; override with `PJ_PARSER_MODULE_WASM_MAX_MEMORY_BYTES`) and a
function-table maximum (wasm-ld emits one; the host caps it at 65536 elements).

## Choose a schema-compatibility strategy

Expand Down Expand Up @@ -170,8 +174,12 @@ invalid descriptor.
## Traps

- The kit is header-only and WASI-clean: no threads, filesystem, iostream, host
SDK linkage, or exceptions across its API. In SDK 0.22 the supported build
product is nevertheless native-only.
SDK linkage, or exceptions across its API. The same source builds the native
and the wasm artifact; keep it that way even if you only ship one today.
- Wasm execution is instruction-metered per guest call (an instruction budget,
not a wall-clock deadline) and memory-capped by the declared maximum; a trap
or metering exhaustion is a contract violation the host strikes, not a data
error. Native and wasm instances share one host-side strike/quarantine loop.
- Return `pj::Status` / `pj::Expected<T>`; do not throw. `Blob` uses nothrow
allocation and protobuf matching is bounded, so allocation failure is a
reported data error rather than a process abort or contract strike.
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/linux-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,25 @@ jobs:
echo "PJ_WASI_SDK_ROOT=${install_dir}" >> "${GITHUB_ENV}"
"${install_dir}/bin/clang++" --version

- name: Install Wasmer C API 7.0.1
shell: bash
run: |
set -euo pipefail
version="7.0.1"
archive="${RUNNER_TEMP}/wasmer-${version}.tar.gz"
install_dir="${RUNNER_TEMP}/wasmer-${version}"
url="https://github.com/wasmerio/wasmer/releases/download/v${version}/wasmer-linux-amd64.tar.gz"

curl -fsSL --retry 5 --retry-all-errors "${url}" -o "${archive}"
mkdir -p "${install_dir}"
tar -xzf "${archive}" -C "${install_dir}"
test -f "${install_dir}/include/wasm.h"
test -f "${install_dir}/include/wasmer.h"
test -f "${install_dir}/lib/libwasmer.a"
nm -g --defined-only "${install_dir}/lib/libwasmer.a" > "${RUNNER_TEMP}/wasmer-symbols.txt"
grep -q wasmer_metering_set_remaining_points "${RUNNER_TEMP}/wasmer-symbols.txt"
echo "PJ_WASMER_ROOT=${install_dir}" >> "${GITHUB_ENV}"

- name: Configure ccache
# Compiler-output cache for our own C++ — complementary to the Conan
# cache (which holds prebuilt third-party packages, not our objects).
Expand Down Expand Up @@ -223,6 +242,7 @@ jobs:
-DCMAKE_C_COMPILER_LAUNCHER=ccache
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
-DPJ_WASI_SDK_ROOT=${PJ_WASI_SDK_ROOT}
-DPJ_WASMER_ROOT=${PJ_WASMER_ROOT}
-DPJ_ENABLE_ABI_CHECK=ON

- name: Build
Expand All @@ -233,6 +253,9 @@ jobs:
cmake --build build --target parser_module_wasm_conformance_fixture
ctest --test-dir build -L wasi --output-on-failure

- name: Wasmer parser-module execution
run: ctest --test-dir build -L wasmer --output-on-failure

- name: ABI drift gate
# Mechanically enforces the Release Versioning policy (CLAUDE.md): a non-MAJOR
# change must not break ABI. Diffs the mock_data_source_plugin canary DSO against
Expand Down
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,44 @@
All notable changes to `plotjuggler_sdk` are recorded here. Versioning policy is in
[`CLAUDE.md`](./CLAUDE.md) → "Release Versioning".

## [0.24.0]

### Feature: sandboxed wasm parser modules and authoring preset (MINOR)

Functional parser modules can now be validated, compiled once, and executed as
WASI reactors through the pinned Wasmer 7.0.1 C API:

- The wasm loader admits only reactors with the frozen operational signatures,
`_initialize`, exactly one manifest section, bounded exported memory, bounded
tables, no start function, and the v1 empty import allow-list. The audit is
one shared `pj_base` entry point (`validateParserModuleWasmArtifact`) used by
both the loader and the `pj-wasm-embed-manifest` tool.
- Store-per-instance execution (Singlepass backend when available) copies ABI
blocks through guest allocation, revalidates linear-memory ranges after every
guest call, preserves host payload splice semantics, and classifies traps or
metering exhaustion as contract violations. The wasm wrapper has the native
wrapper's fault contract: it classifies, the host records strikes,
quarantines, and replays through the shared `ParserModuleStrikeTracker`.
- Instruction metering, declared-memory and table caps, and an optional
thread-safe session budget bound calls, artifact size, modules, claims,
instances, and aggregate declared memory. Reservations are opaque ids that
count resources, never manifest identities. Adversarial trap, infinite-loop,
memory-growth, table-cap, and host-driven quarantine fixtures pin the
behavior.
- `ParserModuleStrikeTracker` is now thread-safe, and a contract violation
while a claim is quarantined (a failed create/bind replay) disables it instead
of leaving it quarantined forever.
- The installed `pj-wasm-embed-manifest` frontend embeds or verifies exact
manifest bytes and performs the shared static audit.
- `pj_add_parser_module(... TARGETS wasm)` provides the wasi-sdk 27 C++17
reactor preset, manifest embedding, and post-link audit; `TARGETS native wasm`
emits both artifacts from one author source.

The wasm executor is an in-tree component gated on `PJ_WASMER_ROOT`; installed
packages stay wasmer-free and ship the authoring preset and tool only.
`NativeParserModule::load(path, sink)` is unchanged from 0.22: admission
accounting happens only through the new budget overload.

## [0.23.1]

### Fix: Conan `plugin_host` component links the parser-module host (PATCH)
Expand Down
9 changes: 6 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ not in the PJ4 superproject. This file is the root navigation node for the whole
VoxelGrid, PlotMarkers) and their canonical wire codecs, the C-ABI protocol headers for
DataSource/MessageParser/Toolbox + the C++ SDK base classes / host-view helpers built on them, the
standalone C++17 functional parser-module authoring kit (`pj_base/parser_module/`), the host-side
wasm parser-module manifest custom-section codec, and the test-only static WASI ABI auditor. The
0.22 authoring helper builds native parser modules only; wasm loading/execution is not present.
wasm parser-module manifest custom-section codec + static wasm ABI inspector, and the installed
`pj-wasm-embed-manifest` CLI. `pj_add_parser_module(... TARGETS native wasm)` builds both
artifacts from one source (wasm needs `PJ_WASI_SDK_ROOT`, wasi-sdk 27).
- **pj_plugins** — host-side loaders + RAII handles + plugin **discovery** (directory scan +
embedded-manifest inspection) for four plugin families (DataSource, MessageParser, Dialog, Toolbox),
parser claim admission/resolution and native functional parser-module execution,
parser claim admission/resolution, native functional parser-module execution, the optional
sandboxed wasm parser-module loader/runtime (Wasmer 7.0.1, in-tree only: gated on
`PJ_WASMER_ROOT`, never part of an installed package) with optional session budgets,
config-envelope helpers, and the **dialog C ABI** (`pj_plugins/dialog_protocol/`). The
duplicate-resolution *catalog* (which copy wins by priority/version/compatibility) is host policy
and lives in the app (`pj_runtime`), built on these discovery primitives. Note the split: the DataSource/MessageParser/Toolbox C-ABI
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ if(PJ_INSTALL_SDK)
"${CMAKE_CURRENT_BINARY_DIR}/plotjuggler_sdkConfigVersion.cmake"
cmake/PjPluginManifest.cmake
cmake/PjParserModule.cmake
cmake/parser_module_wasi_no_io_stubs.cpp
DESTINATION ${PJ_PACKAGE_CMAKE_DIR}
)
endif()
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.23.1
0.24.0
Loading
Loading