ontoref-code/examples/catalog-extension/02-sidecar
2026-07-10 01:44:59 +01:00
..
catalog chore: clean up for constellation migration 2026-07-10 01:44:59 +01:00
sidecar chore: clean up for constellation migration 2026-07-10 01:44:59 +01:00
config-snippet.ncl chore: clean up for constellation migration 2026-07-10 01:44:59 +01:00
README.md chore: clean up for constellation migration 2026-07-10 01:44:59 +01:00

Path B — kind = 'Sidecar (ADR-030 mechanism #6)

Phase A — implementable today. The main ontoref daemon proxies dispatch over HTTP to a per-project sidecar process. Each sidecar can ship its own deploy cadence and signing key while sharing a Rust library with sibling instances.

Canonical use case: shared-domain (provisioning) + Level-3 instances (libre-daoshi, libre-wuji, libre-forge) — see reflection/qa.ncl::ontoref-catalog-discovery-shared-domain.

Files

File Purpose
catalog/send_email_notification.ncl Catalog declaration with kind = 'Sidecar
sidecar/Cargo.toml Standalone Cargo manifest (own workspace)
sidecar/src/main.rs Minimal axum server on :7902 — validates inputs, mock-dispatches email, returns DispatchResponse-shaped JSON
config-snippet.ncl ops.sidecar_url = "http://127.0.0.1:7902" snippet for .ontoref/config.ncl

How to run

  1. Build and start the sidecar (standalone — not part of ontoref's workspace):

    cd examples/catalog-extension/02-sidecar/sidecar
    cargo run
    

    You should see:

    INFO addr=127.0.0.1:7902 example-mail-sidecar listening
    
  2. Test the sidecar directly (bypass the main daemon proxy):

    curl -s -X POST http://127.0.0.1:7902/ops/send_email_notification \
      -H 'Content-Type: application/json' \
      -d '{"inputs":{"recipient":"jpl@jesusperez.pro","subject":"hi","body":"hello","template":"plain"}}' \
      | jq .
    

    Expected response (message_id varies):

    {
      "status": "queued",
      "message_id": "msg-19a3c8...-jpl",
      "payload": {
        "recipient": "jpl@jesusperez.pro",
        "subject":   "hi",
        "template":  "plain",
        "bytes":     5
      }
    }
    
  3. To dispatch via the main daemon (once the Phase A proxy handler lands in crates/ontoref-daemon/src/api.rs):

    curl -X POST 'http://127.0.0.1:7891/ops/send_email_notification?project=<your-project>' \
      -H "Authorization: Bearer $ONTOREF_TOKEN" \
      -d '{"recipient":"jpl@jesusperez.pro","subject":"hi","body":"hello","template":"plain"}'
    

    The daemon looks up sidecar_url for the project, proxies the request to http://127.0.0.1:7902/ops/send_email_notification, signs the response envelope as the witness, returns the DispatchResponse to the caller.

Key properties

  • evaluate_ondaod is mandatory in constraints.pre — ADR-034 Hard constraint. The host runtime invokes ondaod-discipline before proxying to the sidecar; the sidecar itself cannot satisfy it.
  • Witness honestypayload_kind = "sidecar_response_envelope". The daemon's signature attests it faithfully forwarded the sidecar's reported effect; it does NOT claim to attest the sidecar's internal computation (HTTP boundary is opaque).
  • Per-instance signing key — each instance's sidecar can be configured with its own actor id; the cryptographic blast radius stays per-instance.
  • No wasmtime dependency — Sidecar runtime is HTTP + axum + tokio in the sidecar process; the main daemon adds only a ~80-LOC reqwest-based proxy handler. Honours protocol-not-runtime axiom (invariant=true).

When NOT to use

  • The op is part of the ontoref piloto self-host → use Path A 'Rust (zero IPC overhead, full type safety).
  • You need cross-instance op dispatch with witness chaining → ADR-030 T3 trigger, leans toward Phase B WASM.
  • You need to author the op in a non-Rust language → Path B works (sidecar can be any language as long as it speaks the HTTP contract) but consider whether toolchain divergence justifies a Phase B spike per ADR-030 T4.

References