State-contract deploy pipeline
State contracts are the deployable logic of a Zeq machine —
JSON that fires on the clock and writes an audited trail. zeq-deploy.mjs puts the
three steps you'd run by hand into one pipeline you can drop in CI, so a contract
that breaks the rules never reaches production.
It is zero-dependency (Node ≥18 built-ins), served at /zeq-deploy.mjs on every
node — read it before you run it.
curl -fsSL https://zeqsdk.com/zeq-deploy.mjs -o zeq-deploy.mjs
node zeq-deploy.mjs lint my-contract.json
The four steps
node zeq-deploy.mjs lint <contract.json>
node zeq-deploy.mjs simulate <contract.json> --to STATE [--from STATE] [--input k=v ...] --origin URL --key KEY
node zeq-deploy.mjs deploy <contract.json> --slug MACHINE --origin URL --key KEY
node zeq-deploy.mjs pipeline <contract.json> --slug MACHINE --origin URL --key KEY
| Step | What it does | Auth | Exit |
|---|---|---|---|
lint | Structural validation of the contract JSON, locally, no network: object shape, a name/version, states with exactly one initial, every transition's from/to resolving to a real state and carrying an operator, observers, audit_clock, a numeric zeqond_tick_rate. Catches malformed contracts before they leave your repo. | none | 0/1 |
simulate | Dry-runs a transition through the node (POST /api/contracts/simulate) — no write, no token minted: "would this transition fire?". | machine key | 0/1 |
deploy | Creates the contract on your machine (POST /api/chain/<slug>/contracts/custom). The node re-validates it against the live operator registry and the compliance gate — a contract that breaks the rules is refused with 400. That gate is what your build rides. | machine key | 0/1 |
pipeline | lint → simulate (initial → first target) → deploy, stopping at the first failure. The single CI command. | machine key | 0/1 |
Credential: a machine API key (Bearer zsm_…) bound to the target machine — mint
one in the workbench or with zeq keys. lint needs no key. Pass the key via
--key, ZEQ_KEY, and never commit it.
In GitHub Actions
A self-hosted composite action ships in the repo:
# .github/workflows/zeq-contract.yml
name: Zeq contract deploy
on:
push:
paths: ["contracts/**.json"]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hulyasmath/zeq-framework/.github/actions/zeq-deploy@main
with:
contract: contracts/threshold-monitor.json
slug: ${{ vars.ZEQ_MACHINE }}
key: ${{ secrets.ZEQ_MACHINE_KEY }} # Bearer zsm_…, from repo secrets
mode: pipeline
Want a PR check that only lints (no key, no deploy)? Set mode: lint and drop the
key/slug — it runs on every PR and fails on a malformed contract.
Why the compliance gate is the point
The deploy endpoint doesn't trust your JSON: it re-parses the definition against the
live ContractDefinitionSchema, checks every operator exists in the node's
registry, and runs the compliance validation before it writes a row. So "it
deployed" means "the node, not your laptop, agreed this contract is well-formed and
references real physics." Your pipeline inherits that guarantee for free — the same
reason the reproducibility gate inherits the node's
conformance check.
See next
- State contracts — what you're deploying, and the templates to start from
- Contract IDE — author and preview contracts interactively
- Reproducibility CI — the sibling gate for physics, not contracts
Source: served per-node at /zeq-deploy.mjs (canonical copy in apps/zeq-dev/public/zeq-deploy.mjs); the Action at .github/actions/zeq-deploy/.