Reference Node.js runtime for CommandLayer Commons verbs. This service exposes deterministic verb handlers, signs receipts with Ed25519 via @commandlayer/runtime-core, and verifies receipts with a configured public key or an ENS lookup.
The runtime currently exposes:
GET /— JSON index with service metadata and enabled verb routes.GET /health— health and signer/verifier readiness.GET /healthz— alias for/health.POST /verify— receipt hash/signature verification, with optional ENS lookup and optional schema validation.POST /<verb>/v1.1.0for the verbs enabled byENABLED_VERBS.
The default enabled verbs are:
fetchdescribeformatcleanparsesummarizeconvertexplainanalyzeclassify
The runtime also implements these gated debug routes:
GET /debug/envGET /debug/enskeyGET /debug/validatorsPOST /debug/prewarm
They are available only when both of these are set:
ENABLE_DEBUG=1DEBUG_TOKEN=<token>
Requests must present the token either as Authorization: Bearer <token> or X-Debug-Token: <token>. When debug access is not enabled or the token is missing or wrong, these routes return 404.
There is no /debug/schemafetch route in the current implementation.
Verb routes return a JSON object with a signed receipt and optional unsigned runtime_metadata.
{
"trace_id": "cltrace_...",
"steps": [{ "step": 1, "receipt": { "...": "signed receipt" } }],
"final_receipt": { "...": "same signed receipt" },
"receipt": { "...": "signed receipt" },
"runtime_metadata": {
"trace": { "trace_id": "cltrace_...", "...": "optional" },
"actor": { "...": "optional" },
"delegation_result": { "...": "optional" }
}
}The signed receipt is produced by @commandlayer/runtime-core. The runtime sets proof fields under receipt.metadata.proof, including:
trace_idreceipt_idalgcanonicalsigner_idkidhash_sha256signature_b64
For compatibility, if runtime-core returns metadata.proof.canonical, the runtime also emits metadata.proof.canonical_id in responses.
At boot, the runtime requires signer configuration unless DEV_AUTO_KEYS=1 is set.
The canonical/current environment names shown in .env.example are:
RECEIPT_SIGNING_PRIVATE_KEY_PEM_B64RECEIPT_SIGNING_PUBLIC_KEY_B64RECEIPT_SIGNER_ID
The implementation also accepts several legacy aliases. See docs/CONFIGURATION.md for the exact precedence.
RECEIPT_SIGNING_PUBLIC_KEY_B64 is the current raw-32-byte Ed25519 public key input. RECEIPT_SIGNING_PUBLIC_KEY is not read by the server.
The runtime derives kid from the SHA-256 fingerprint of the active 32-byte public key. It does not read CL_KEY_ID or CL_CANONICAL_ID into production signing behavior.
POST /verify accepts either:
- a bare receipt, or
- the wrapped verb response containing
.receipt.
Supported query flags:
ens=1— resolve the verification public key from ENS instead of using the configured local public key.strict_kid=1— withens=1, requirereceipt.metadata.proof.kidto match the ENScl.sig.kidTXT value when that TXT value is present.refresh=1— refresh ENS cache before verifying.schema=1— validate the receipt against the verb receipt schema.
When schema=1, schema validation uses the receipt verb to compute a v1.1.0 receipt schema URL under SCHEMA_HOST.
When a commons verb request omits execution, the runtime fabricates receipt execution defaults from the live route version: entry: "https://runtime.commandlayer.org/execute", verb: "<verb>", version: "1.1.0", and class: "commons". Commercial/payment-aware behavior belongs in the separate commercial runtime and is intentionally out of scope here.
When VERIFY_SCHEMA_CACHED_ONLY=1 (the default), /verify?schema=1 returns HTTP 202 with validator_not_warmed_yet if the validator for that verb has not been compiled yet. POST /debug/prewarm can queue validator warmup, and GET /debug/validators shows cache state.
If /verify exceeds VERIFY_MAX_MS, the runtime returns HTTP 502 with failure_type: "availability", retryable: true, and the message Verification service did not respond. Receipt may still be valid; retry recommended. Clients should treat that as transient service unavailability, not a cryptographic proof failure.
When ens=1, the runtime resolves TXT records directly on the signer ENS name and reads:
cl.sig.pubby default, configurable viaENS_SIG_PUB_KEYcl.sig.kidby default, configurable viaENS_SIG_KID_KEYcl.sig.canonicalby default, configurable viaENS_SIG_CANONICAL_KEY
The runtime does not read VERIFIER_ENS_NAME or ENS_SIGNER_TEXT_KEY.
cl.sig.pub must contain ed25519:<base64-raw32-public-key>.
npm ciThis repository declares Node >=20.0.0 in package.json.
The fastest documented path is:
cp .env.example .envThen populate the signing values from your own Ed25519 keypair, or run with DEV_AUTO_KEYS=1 for development-only ephemeral keys.
npm startOr use the helper script:
scripts/dev.shscripts/dev.sh generates keys.env with tools/mkkeys.mjs if needed, sources that file, enables debug routes, and starts server.mjs on 127.0.0.1:8099 by default.
curl -s http://127.0.0.1:8080/health | jq .
node scripts/smoke.mjsThis repository's GitHub Actions workflows currently run:
npm cinpm audit --audit-level=highnpm run checknpm test- scheduled and manual
bash scripts/smoke-ens.sh
npm test runs Node unit tests plus tests/smoke.mjs.
The production verification path is server.mjs, which signs receipts and verifies them via @commandlayer/runtime-core.
Repo-local test coverage now includes runtime service tests that exercise the receipt production path and POST /verify behavior directly, alongside the remaining legacy helper coverage under runtime/tests/.
- Configuration reference:
docs/CONFIGURATION.md - Operations notes:
docs/OPERATIONS.md - Security notes:
SECURITY.md