npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

spell-runtime

v1.2.5

Published

Minimal CLI runtime for SpellBundle v1

Downloads

34

Readme

Spell Runtime v1

Minimal CLI runtime for SpellBundle v1.

Setup

  • Node.js >= 20
  • pnpm (recommended)
  • npm (supported)
pnpm install
pnpm run build
pnpm test

Local dev:

pnpm run dev -- --help

Install as CLI

Global install:

npm i -g spell-runtime
spell --help

Run with npx:

npx --yes --package spell-runtime spell --help

Local package smoke checks:

npm run smoke:link
npm run smoke:npx

Commands

  • spell install <source>
  • spell list
  • spell inspect <id> [--version x.y.z]
  • spell cast <id> [--version x.y.z] [-p key=value ...] [--input input.json] [--dry-run] [--yes] [--allow-billing] [--allow-unsigned] [--require-signature] [--verbose] [--profile <name>]
  • spell license add <name> <entitlement-token>
  • spell license list
  • spell license remove <name>
  • spell sign keygen <publisher> [--key-id default] [--out-dir .spell-keys]
  • spell sign bundle <local-path> --private-key <file> [--key-id default] [--publisher <name>]
  • spell trust add <publisher> <public-key> [--key-id default]
  • spell trust list
  • spell trust remove <publisher>
  • spell log <execution-id>

Install Sources

spell install <source> accepts:

  • local bundle paths (existing behavior)
  • pinned git URLs with an explicit ref suffix:
    • https://...#<ref>
    • ssh://...#<ref>
    • git@...#<ref>

Git sources must include #<ref>. If omitted, install fails with:

  • git source requires explicit ref (#<ref>)

When a git source is provided, runtime clones the repository, checks out the requested ref, resolves the checked-out commit SHA (git rev-parse HEAD), and installs from that checkout.

Limitations:

  • git must be installed and available on PATH.
  • clone/auth/network behavior is delegated to your local git configuration.
  • spell.yaml must exist at the cloned repository root (subdirectory installs are not supported).

Storage Layout

  • Spells: ~/.spell/spells/<id_key>/<version>/
  • ID index: ~/.spell/spells/<id_key>/spell.id.txt
  • Install provenance: ~/.spell/spells/<id_key>/<version>/source.json
  • Logs: ~/.spell/logs/<timestamp>_<id>_<version>.json
  • Billing entitlement records: ~/.spell/licenses/*.json

source.json captures install provenance:

  • type: local or git
  • source: original install input
  • ref: requested git ref (git installs only)
  • commit: resolved git commit SHA (git installs only)
  • installed_at: install timestamp (ISO-8601)

id_key is fixed as base64url(utf8(id)).

  • id is the logical identifier (display, package identity).
  • id_key is only for safe filesystem storage.

Consistency rule:

  • install checks spell.yaml id against spell.id.txt when spell.id.txt already exists.
  • mismatch is treated as an error.

Cast Preflight

cast performs these checks before execution:

  • bundle resolution by id (and optional version)
  • input assembly (--input + -p overrides)
  • JSON Schema validation by Ajv
  • signature verification (default on; bypass only with --allow-unsigned)
  • runtime policy guard (~/.spell/policy.json)
  • platform guard
  • risk guard (high/critical requires --yes)
  • billing guard (billing.enabled requires --allow-billing)
  • billing entitlement guard (billing.enabled + --allow-billing requires a matching valid entitlement from spell license add ...)
  • connector token guard (CONNECTOR_<NAME>_TOKEN)
  • execution summary output

If --dry-run is set, command exits after summary and validation.

Policy file format (~/.spell/policy.json):

{
  "version": "v1",
  "default": "allow",
  "publishers": { "allow": ["samples"], "deny": ["blocked"] },
  "max_risk": "high",
  "runtime": { "allow_execution": ["host", "docker"] }
}

Notes:

  • missing policy file => allow by default
  • invalid policy file => invalid policy: ...
  • policy rejection => policy denied: <reason>

Runtime Safety Limits (v2 isolation)

cast enforces these runtime limits (used by direct CLI casts and API-triggered casts because the API invokes spell cast):

  • SPELL_RUNTIME_INPUT_MAX_BYTES (default 65536): max bytes for merged cast input (--input + -p overrides).
  • SPELL_RUNTIME_STEP_TIMEOUT_MS (default 60000): max runtime per shell step. On timeout, the runtime kills the step process and fails with the step name + timeout ms.
  • SPELL_RUNTIME_EXECUTION_TIMEOUT_MS (default disabled): max total cast runtime across host/docker paths when set to an integer > 0.

Runtime Model

v1 supports:

  • host: steps run in order, shell/http supported.
  • docker: steps run in a linux container via "runner-in-image".

Docker mode (v1) details:

  • runtime.execution=docker requires runtime.docker_image.
  • the image must provide spell-runner on PATH (this repo publishes it as a second npm bin).
  • the bundle is mounted read-only at /spell; the runner copies it into a writable temp workdir before executing steps.
  • hardened docker run defaults:
    • --network none
    • --cap-drop ALL
    • --security-opt no-new-privileges
    • --read-only
    • --user 65532:65532
    • --pids-limit 256
    • --tmpfs /tmp:rw,noexec,nosuid,size=64m
  • hardening env overrides (all optional):
    • SPELL_DOCKER_NETWORK (none|bridge|host, default none)
    • SPELL_DOCKER_USER (default 65532:65532; set empty to disable --user)
    • SPELL_DOCKER_READ_ONLY (1 default; set 0 to disable --read-only)
    • SPELL_DOCKER_PIDS_LIMIT (256 default; set 0 to disable --pids-limit)
    • SPELL_DOCKER_MEMORY (default empty; when set adds --memory)
    • SPELL_DOCKER_CPUS (default empty; when set adds --cpus)
  • environment variables passed from host -> container are restricted to connector tokens (CONNECTOR_<NAME>_TOKEN) plus SPELL_RUNTIME_STEP_TIMEOUT_MS.

Windows Policy

  • host mode does not assume bash/sh.
  • shell step expects executable files (.js/.exe/.cmd/.ps1 etc).
  • process spawn uses shell=false.
  • for strict cross-OS reproducibility, docker mode is the long-term recommended path.

Effects Vocabulary (Recommended)

Use these effect.type words where possible:

  • create
  • update
  • delete
  • deploy
  • notify

v1 Limitations (Intentionally Not Implemented)

  • name search or ambiguous resolution (id only)
  • registry/marketplace integration
  • real billing execution (Stripe)
  • DAG/parallel/rollback/self-healing
  • advanced templating language (only {{INPUT.*}} and {{ENV.*}})
  • docker env passthrough beyond connector tokens and SPELL_RUNTIME_STEP_TIMEOUT_MS

Signature (Sign + Verify)

spell cast requires signature verification by default. To bypass this for unsigned bundle workflows, use:

spell cast <id> --allow-unsigned ...

--require-signature remains accepted for backward compatibility.

Signing flow:

spell sign keygen samples --key-id default --out-dir .spell-keys
spell trust add samples <public_key_base64url> --key-id default
spell sign bundle ./examples/spells/call-webhook --private-key .spell-keys/samples__default.private.pem --key-id default

Trust store:

  • spell trust add <publisher> <public-key>
  • spell trust list
  • spell trust remove <publisher>

Notes:

  • publisher is derived from the spell id prefix before the first / (example: samples/call-webhook -> samples).
  • public key format is ed25519 spki DER encoded as base64url.

Entitlement Tokens (Billing)

spell license add <name> <token> now validates and stores signed entitlement tokens.

Token format:

  • ent1.<payloadBase64url>.<signatureBase64url>

Payload JSON required fields:

  • version ("v1")
  • issuer (string)
  • key_id (string)
  • mode ("upfront" | "on_success" | "subscription")
  • currency (string)
  • max_amount (number)
  • not_before (ISO string)
  • expires_at (ISO string)

Verification rules:

  • signature algorithm is ed25519
  • signed message is the raw payload segment bytes (the exact payload base64url segment string bytes)
  • trust source is the publisher trust store (spell trust add ...) keyed by issuer + key_id
  • token must be within not_before <= now <= expires_at

Billing-enabled cast requires a matching currently-valid entitlement:

  • entitlement mode === manifest.billing.mode
  • entitlement currency equals manifest.billing.currency (case-insensitive)
  • entitlement max_amount >= manifest.billing.max_amount

spell license list prints entitlement summary columns (issuer, mode, currency, max_amount, expires_at) and never prints raw tokens.

Example Flow

spell install ./fixtures/spells/hello-host
spell list
spell inspect fixtures/hello-host
spell cast fixtures/hello-host --dry-run -p name=world
spell cast fixtures/hello-host -p name=world
spell log <execution-id>

OSS Release (pnpm + GitHub Actions)

Release automation is defined in:

  • .github/workflows/release.yml

Prerequisites:

  • npm account with 2FA enabled
  • GitHub repository secret NPM_TOKEN (publish-capable npm token)

Local release checks:

pnpm install
pnpm run typecheck
pnpm run lint
pnpm run build
pnpm test
pnpm run pack:check

Tag-based release flow:

npm version patch
git push --follow-tags

Pushing tag vX.Y.Z triggers GitHub Actions release and runs npm publish. The workflow verifies that the git tag version matches package.json.

Real-Use Sample Spells

These are product-facing examples (separate from test fixtures):

  • /Users/koichinishizuka/spell-runtime/examples/spells/call-webhook
  • /Users/koichinishizuka/spell-runtime/examples/spells/repo-ops
  • /Users/koichinishizuka/spell-runtime/examples/spells/publish-site

Quick try:

spell install ./examples/spells/call-webhook
spell inspect samples/call-webhook
spell cast samples/call-webhook --dry-run -p event=deploy -p source=manual -p payload='{"service":"web"}'

UI Connection Spec

  • Decision-complete button integration spec:
    • /Users/koichinishizuka/spell-runtime/docs/ui-connection-spec-v1.md
  • Sample button registry:
    • /Users/koichinishizuka/spell-runtime/examples/button-registry.v1.json
  • Button registry schema:
    • /Users/koichinishizuka/spell-runtime/examples/button-registry.v1.schema.json
  • Registry optional policy:
    • require_signature:
      • true: Execution API enforces signature (--require-signature)
      • false/omitted: Execution API opts into unsigned path (--allow-unsigned)

Runtime Decision Log

  • /Users/koichinishizuka/spell-runtime/docs/runtime-decisions-v1.md

Repository Policies

  • /Users/koichinishizuka/spell-runtime/CONTRIBUTING.md
  • /Users/koichinishizuka/spell-runtime/CODE_OF_CONDUCT.md
  • /Users/koichinishizuka/spell-runtime/SECURITY.md

Execution API (Async)

Start API server:

npm run api:dev

By default it listens on :8787 and reads:

  • button registry: ./examples/button-registry.v1.json
  • limits:
    • request body: 64KB
    • execution timeout: 60s
    • in-flight executions: 4
  • execution index persistence: ~/.spell/logs/index.json
  • routes:
    • GET / (minimal Receipts UI)
    • GET /ui/app.js (UI client script)
    • GET /api/buttons
    • GET /api/spell-executions (status, button_id, tenant_id, limit query supported)
    • POST /api/spell-executions
    • GET /api/spell-executions/:execution_id

Optional environment variables:

  • SPELL_API_PORT
  • SPELL_BUTTON_REGISTRY_PATH
  • SPELL_API_AUTH_KEYS (comma-separated role=token or tenant:role=token entries; when set, /api/* requires auth and derives actor_role + tenant_id from token)
  • SPELL_API_AUTH_TOKENS (legacy: comma-separated tokens; when set, /api/* requires auth but does not bind role)
  • SPELL_API_BODY_LIMIT_BYTES
  • SPELL_API_EXECUTION_TIMEOUT_MS
  • SPELL_API_RATE_LIMIT_WINDOW_MS
  • SPELL_API_RATE_LIMIT_MAX_REQUESTS
  • SPELL_API_MAX_CONCURRENT_EXECUTIONS
  • SPELL_API_LOG_RETENTION_DAYS (default 14, 0 disables age-based pruning)
  • SPELL_API_LOG_MAX_FILES (default 500, 0 disables count-based pruning)

Security note:

  • execution logs redact secret-like keys (token, authorization, apiKey, etc.)
  • environment-derived secret values are masked in persisted logs
  • when auth is enabled, pass Authorization: Bearer <token> (or x-api-key) for /api routes
  • with SPELL_API_AUTH_KEYS, non-admin list requests are restricted to their own tenant and cross-tenant tenant_id filters return 403 (TENANT_FORBIDDEN)
  • do not set both SPELL_API_AUTH_KEYS and SPELL_API_AUTH_TOKENS at the same time