Assay
Universal API Execution Engine — Lightweight Lua runtime for Kubernetes. Verification, scripting, and web services.
What is Assay?
Assay is a single ~9 MB binary that replaces 50-250 MB Python/Node/kubectl containers in Kubernetes Jobs. It provides a full-featured Lua runtime with built-in HTTP client/server, database access, WebSocket, JWT signing, templates, and 29 embedded Kubernetes-native and AI agent libraries.
One binary, auto-detected behavior:
Scripts that call http.serve() become web services. Scripts that call http.get() and exit are
jobs. Same binary, same builtins.
Why Assay?
Container image size comparison (compressed pull):
+------------------------------------------------------------------+
| Docker image size comparison (compressed pull) |
| |
| Assay Full ## 6 MB |
| Python alpine ########## 17 MB |
| bitnami/kubectl #################### 35 MB |
| Python slim ########################## 43 MB |
| Node.js alpine ################################## 57 MB |
| alpine/k8s ######################################## 60 MB |
| Deno ############################################ 75 |
| Node.js slim ############################################### |
| Bun ############################################### |
| postman/newman ############################################### |
+------------------------------------------------------------------+
| Runtime | Compressed | On-disk | vs Assay | Sandbox | K8s-native |
|---|---|---|---|---|---|
| Assay | 6 MB | 13 MB | 1x | Yes | Yes |
| Python alpine | 17 MB | 50 MB | 3x | No | No |
| bitnami/kubectl | 35 MB | 90 MB | 6x | No | Partial |
| Python slim | 43 MB | 130 MB | 9x | No | No |
| Node.js alpine | 57 MB | 180 MB | 12x | No | No |
| alpine/k8s | 60 MB | 150 MB | 10x | No | Partial |
Installation
Pre-built Binary (fastest)
Download from GitHub Releases:
# Linux (x86_64, static — runs on any distro, no dependencies)
# macOS (Apple Silicon)
Docker
Cargo
From Source
Usage
Two Modes
Assay auto-detects behavior by file extension:
1. YAML Check Mode (Orchestration)
Run structured verification checks with retry, backoff, and JSON output:
# checks.yaml
timeout: 120s
retries: 3
backoff: 5s
parallel: false
checks:
- name: grafana-healthy
type: http
url: http://grafana.monitoring:80/api/health
expect:
status: 200
json: ".database == \"ok\""
- name: prometheus-targets
type: prometheus
url: http://prometheus.monitoring:9090
query: "count(up)"
expect:
min: 1
- name: custom-check
type: script
file: verify.lua
2. Lua Script Mode (Direct Execution)
Run Lua scripts with all builtins available:
#!/usr/bin/assay
-- HTTP health check with JWT auth
local token = crypto.
local resp = http.
assert.
log.
Shebang Support
Assay supports shebang for executable Lua scripts:
#!/usr/bin/assay
log.
v0.5.0: Universal API Execution Engine
Assay v0.5.0 adds intelligent module discovery and LLM-friendly context injection.
New Subcommands
assay context <query> — Find modules by keyword
Searches all available modules and returns prompt-ready Markdown with method signatures:
Output:
# Assay Module Context
## Matching Modules
### assay.grafana
Grafana monitoring and dashboards. Health, datasources, annotations, alerts, folders.
Methods:
c:health() -> {database, version, commit} | Check Grafana health
c:datasources() -> [{id, name, type, url}] | List all datasources
c:dashboard(uid) -> {dashboard, meta} | Get dashboard by UID
...
Paste the output directly into an LLM prompt to get accurate, hallucination-free Lua code.
assay modules — List all available modules
Lists all 40+ built-in modules with source and description:
MODULE SOURCE DESCRIPTION
--------------------------------------------------------------------------------
assay.grafana builtin Grafana monitoring and dashboards...
assay.k8s builtin Kubernetes API client. 30+ resource types...
http builtin HTTP client and server: get, post, put, patch, delete, serve
...
assay exec -e '<lua>' — Run Lua inline
assay run <file> — Explicit run (same as passing file directly)
v0.5.1: MCP Comparison & Website
Keyword-enriched search across all 40 modules. Static website at assay.rs with MCP comparison (42 servers), AI agent integration guides, and llms.txt for LLM context traversal.
Filesystem Module Loading
Place custom .lua modules in ./modules/ (project-local) or ~/.assay/modules/ (global):
./modules/
myapi.lua # require("assay.myapi") in scripts
~/.assay/modules/
company.lua # require("assay.company") in scripts
Set ASSAY_MODULES_PATH to override the global directory.
Built-in API Reference
All builtins are available to .lua scripts. YAML check mode uses a sandboxed subset (http, json,
yaml, assert, log, env, sleep, time, base64).
HTTP Client
| Function | Description |
|---|---|
http.get(url, opts?) |
GET request, returns {status, body, ...} |
http.post(url, body, opts?) |
POST request (auto-JSON if table) |
http.put(url, body, opts?) |
PUT request |
http.patch(url, body, opts?) |
PATCH request |
http.delete(url, opts?) |
DELETE request |
http.serve(port, routes) |
Start HTTP server (blocking) |
opts.headers = {["X-Key"] = "value"} |
Custom headers |
routes = {GET = {["/path"] = handler}} |
Route table for server |
return {sse = function(send) ... end} |
SSE streaming response (server) |
Serialization
| Function | Description |
|---|---|
json.parse(str) |
Parse JSON string to Lua table |
json.encode(table) |
Encode Lua table to JSON string |
yaml.parse(str) |
Parse YAML string to Lua table |
yaml.encode(table) |
Encode Lua table to YAML string |
toml.parse(str) |
Parse TOML string to Lua table |
toml.encode(table) |
Encode Lua table to TOML string |
base64.encode(str) |
Base64 encode |
base64.decode(str) |
Base64 decode |
Filesystem
| Function | Description |
|---|---|
fs.read(path) |
Read file to string |
fs.write(path, s) |
Write string to file |
Cryptography
| Function | Description |
|---|---|
crypto.jwt_sign(claims, key, alg, opts?) |
Sign JWT (RS256/384/512), opts: {kid="..."} |
crypto.hash(str, alg) |
Hash string (sha256, sha384, sha512, etc.) |
crypto.hmac(key, data, alg?, raw?) |
HMAC (sha256 default, raw=true for binary) |
crypto.random(len) |
Secure random string (hex) |
Regular Expressions
| Function | Description |
|---|---|
regex.match(pattern, str) |
Test if pattern matches |
regex.find(pattern, str) |
Find first match |
regex.find_all(pattern, str) |
Find all matches |
regex.replace(pattern, str, r) |
Replace matches |
Database (SQL)
| Function | Description |
|---|---|
db.connect(url) |
Connect to database (Postgres/MySQL/SQLite) |
db.query(conn, sql, params?) |
Execute query, return rows |
db.execute(conn, sql, params?) |
Execute statement, return affected count |
db.close(conn) |
Close connection |
Supported URLs:
postgres://user:pass@host:5432/dbnamemysql://user:pass@host:3306/dbnamesqlite:///path/to/file.db
WebSocket
| Function | Description |
|---|---|
ws.connect(url) |
Connect to WebSocket server |
ws.send(conn, msg) |
Send message |
ws.recv(conn) |
Receive message (blocking) |
ws.close(conn) |
Close connection |
Templates (Jinja2-compatible)
| Function | Description |
|---|---|
template.render(path, vars) |
Render template file |
template.render_string(tmpl, v) |
Render template string |
Async
| Function | Description |
|---|---|
async.spawn(fn) |
Spawn async task, returns handle |
async.spawn_interval(fn, ms) |
Spawn recurring task, returns handle |
handle:await() |
Wait for task completion |
handle:cancel() |
Cancel recurring task |
Assertions
| Function | Description |
|---|---|
assert.eq(a, b, msg?) |
Assert equal |
assert.ne(a, b, msg?) |
Assert not equal |
assert.gt(a, b, msg?) |
Assert greater than |
assert.lt(a, b, msg?) |
Assert less than |
assert.contains(str, sub, msg?) |
Assert substring |
assert.not_nil(val, msg?) |
Assert not nil |
assert.matches(str, pat, msg?) |
Assert regex match |
Logging
| Function | Description |
|---|---|
log.info(msg) |
Info log |
log.warn(msg) |
Warning log |
log.error(msg) |
Error log |
Utilities
| Function | Description |
|---|---|
env.get(key) |
Get environment variable |
sleep(secs) |
Sleep for seconds |
time() |
Unix timestamp (seconds) |
Stdlib Modules
Assay embeds 29 Lua modules for Kubernetes-native and AI agent operations. Use
require("assay.<module>") — or run assay modules to see all 46+ available modules including Rust
builtins:
| Module | Description |
|---|---|
assay.prometheus |
Query metrics, alerts, targets, rules, label values, series |
assay.alertmanager |
Manage alerts, silences, receivers, config |
assay.loki |
Push logs, query, labels, series |
assay.grafana |
Health checks, dashboards, datasources |
assay.k8s |
30+ resource types, CRDs, readiness checks |
assay.argocd |
Apps, sync, health, projects, repositories |
assay.kargo |
Stages, freight, promotions, verification |
assay.flux |
GitRepositories, Kustomizations, HelmReleases |
assay.traefik |
Routers, services, middlewares, entrypoints |
assay.vault |
KV secrets, policies, auth, transit, PKI |
assay.openbao |
Alias for vault (OpenBao API-compatible) |
assay.certmanager |
Certificates, issuers, ACME challenges |
assay.eso |
ExternalSecrets, SecretStores, ClusterSecretStores |
assay.dex |
OIDC discovery, JWKS, health |
assay.crossplane |
Providers, XRDs, compositions, managed resources |
assay.velero |
Backups, restores, schedules, storage locations |
assay.temporal |
Workflows, task queues, schedules |
assay.harbor |
Projects, repositories, artifacts, vulnerability scanning |
assay.healthcheck |
HTTP checks, JSON path, body matching, latency, multi-check |
assay.s3 |
S3-compatible storage (AWS, iDrive e2, R2, MinIO) — Sig V4 |
assay.unleash |
Feature flags: projects, environments, features, strategies, API tokens |
assay.postgres |
PostgreSQL helpers. User/database management, grants, Vault integration |
assay.zitadel |
Zitadel OIDC identity management. Projects, apps, IdPs, users, login policies |
assay.openclaw |
OpenClaw AI agent platform — invoke tools, state, diff, approve, LLM tasks |
assay.github |
GitHub REST API — PRs, issues, actions, repos, GraphQL |
assay.gmail |
Gmail REST API with OAuth2 — search, read, reply, send, labels |
assay.gcal |
Google Calendar REST API with OAuth2 — events CRUD, calendar list |
assay.oauth2 |
Google OAuth2 token management — file-based credentials, auto-refresh |
assay.email_triage |
Email classification — deterministic rules + optional LLM-assisted triage |
Example:
local prom = require
local result = prom.
log.
Examples
HTTP Health Check
#!/usr/bin/assay
local resp = http.
assert.
local data = json.
assert.
log.
JWT Authentication to API
#!/usr/bin/assay
-- Read RSA private key from file
local key = fs.
-- Sign JWT with RS256
local token = crypto.
-- Call API with JWT
local resp = http.
assert.
local users = json.
log.
Database Query
#!/usr/bin/assay
local pg = db.
-- Parameterized query (safe from SQL injection)
local rows = db.
for _, row in ipairs
db.
Web Server
#!/usr/bin/assay
-- Simple API server
http.
Server-Sent Events (SSE)
#!/usr/bin/assay
-- Stream events to clients in real-time
http.
The send callback accepts a table with optional fields: event, data, id, retry. Headers
Content-Type: text/event-stream, Cache-Control: no-cache, and Connection: keep-alive are set
automatically. The stream closes when the function returns.
Prometheus Verification
#!/usr/bin/assay
local prom = require
-- Check Prometheus is up
local targets = prom.
local up_count = 0
for _, target in ipairs
assert.
log.
-- Query metrics
local result = prom.
log.
YAML Check Mode
YAML check mode provides structured orchestration with retry, backoff, and parallel execution:
# Global config
timeout: 120s # Max time for all checks
retries: 3 # Retry failed checks
backoff: 5s # Wait between retries
parallel: false # Run checks sequentially (true = parallel)
checks:
# HTTP check with JSON path assertion
- name: api-health
type: http
url: https://api.example.com/health
expect:
status: 200
json: ".status == \"healthy\""
# Prometheus query check
- name: high-cpu
type: prometheus
url: http://prometheus:9090
query: "avg(rate(cpu_usage[5m]))"
expect:
max: 0.8 # Alert if CPU > 80%
# Custom Lua script check
- name: database-check
type: script
file: verify-db.lua
env:
DB_URL: postgres://user:pass@postgres:5432/mydb
Check types:
type: http— HTTP request with status/body/JSON assertionstype: prometheus— PromQL query with min/max assertionstype: script— Custom Lua script (sandboxed builtins)
Output is structured JSON:
Exit code: 0 if all checks pass, 1 if any fail.
Development
Build
Test
Lint
Format
Run Examples
Self-contained scripts (no external services needed):
Kubernetes examples (require services running in-cluster):
OpenClaw Integration
Assay v0.6.0 integrates with OpenClaw as an agent tool. This enables AI agents to execute deterministic Lua workflows with human approval gates.
Tool Mode
|
OpenClaw Extension
Install the @developerinlondon/assay-openclaw-extension package (GitHub Packages) to register
Assay as an OpenClaw tool:
# One-time: configure npm to use GitHub Packages for @developerinlondon scope
# Install the extension
See openclaw-extension/README.md for full configuration details.
Architecture
+------------------------------------------------------------------+
| Assay v0.6.0 (~9 MB static MUSL binary, Alpine container) |
| |
| CLI subcommands: |
| assay <file.yaml> (.yaml -> check orchestration) |
| assay <file.lua> (.lua -> run script) |
| assay run <file> (explicit run, any extension) |
| assay run --mode tool <f> (tool mode for OpenClaw agents) |
| assay resume --token <t> (resume paused approval gates) |
| assay exec -e '<lua>' (inline Lua evaluation) |
| assay modules (list all 46+ modules) |
| assay context <query> (LLM-ready module context) |
| |
| Shebang support: |
| #!/usr/bin/assay (works like #!/usr/bin/python3) |
| |
| Rust Core: |
| Config parser (YAML) -> Runner (retry/backoff/timeout) |
| -> Structured JSON output -> Exit code (0/1) |
| |
| Lua Runtime (mlua + Lua 5.5): |
| - 64 MB memory limit per VM |
| - Fresh VM per check (YAML mode) |
| - Single VM per script (Lua mode) |
| - Async support via tokio LocalSet |
| |
| Rust Builtins (all available to .lua scripts): |
| http.{get,post,put,patch,delete,serve} |
| ws.{connect,send,recv,close} |
| json.{parse,encode} yaml.{parse,encode} toml.{parse,encode} |
| fs.{read,write} base64.{encode,decode} |
| crypto.{jwt_sign,hash,random} regex.{match,find,replace} |
| db.{connect,query,execute,close} (postgres, mysql, sqlite) |
| template.{render,render_string} |
| assert.{eq,gt,lt,contains,not_nil,matches} |
| log.{info,warn,error} env.get sleep time |
| async.{spawn,spawn_interval} |
| |
| Lua Stdlib (29 embedded .lua files via include_dir!): |
| Monitoring: prometheus, alertmanager, loki, grafana |
| K8s/GitOps: k8s, argocd, kargo, flux, traefik |
| Security: vault, openbao, certmanager, eso, dex |
| Infra: crossplane, velero, temporal, harbor |
| Data: postgres, s3 |
| Identity: zitadel |
| Utilities: healthcheck, unleash |
| AI/Agent: openclaw, github, gmail, gcal, oauth2, |
| email_triage |
+------------------------------------------------------------------+
Use Cases
- ArgoCD/Kargo Hooks: PostSync verification, PreSync validation, health checks
- Kubernetes Jobs: Database migrations, API configuration, secret rotation
- Lightweight Web Services: Webhook receivers, API proxies, mock servers, dashboards
- Platform Automation: Operational tasks, cross-service connectivity checks, report generation
- Verification: E2E tests, smoke tests, integration tests
Why Lua 5.5?
Assay uses Lua 5.5 (released Dec 2025) over LuaJIT for:
- Global declarations: Catches accidental globals (reduces bugs)
- Named vararg tables: Cleaner function signatures
- Incremental major GC: Smoother latency for long-running servers
- Native int64: Better for timestamps, IDs
- MUSL static linking: No assembler issues
Our scripts are I/O bound (HTTP calls, database queries). LuaJIT's 5-10x CPU speedup provides negligible benefit (<1% of total job time).
License
MIT
Contributing
Contributions welcome! Please open an issue or PR on GitHub.
Links
- Repository: https://github.com/developerinlondon/assay
- Crates.io: https://crates.io/crates/assay-lua
- Docker: https://github.com/developerinlondon/assay/pkgs/container/assay
- Issues: https://github.com/developerinlondon/assay/issues
- Website: https://assay.rs
- MCP Comparison: https://assay.rs/mcp-comparison.html — Assay vs 42 MCP servers
- Agent Guides: https://assay.rs/agent-guides.html — Claude Code, Cursor, Windsurf, Cline, OpenCode
- Module Reference: https://assay.rs/modules.html — All 40+ modules with method signatures
- LLM Context: https://assay.rs/llms.txt — Jeremy Howard spec for LLM agent traversal