Every setting, field, command and state.
The guide walks through an integration in order. This page is for looking things up. It describes version 0.1.5, the one on npm.
The contract file
conseqa.contracts.json sits at the top of your project and describes one package of contracts. conseqa contracts build reads it.
| Field | Takes | If left out | What it does |
|---|---|---|---|
packageName | text | Required | A name for this set of contracts. Shown beside each build in the dashboard. |
packageVersion | version, such as 1.4.0 | Required | The version of the set as a whole. |
runtimeTarget | text | node22 | The runtime the bundles are built for. |
sourceCommit | text | unknown | The commit the build came from, recorded in the manifest. |
clientEntry | file | Required | The file that exports each contract's application half. |
runnerEntry | file | Required | The file that exports each contract's runner half. |
regressions | JSON file | none | Cases for conseqa contracts test. Data only; an executable file is refused. |
outputDirectory | folder | dist/conseqa | Where the build is written. It must be a folder of its own. |
contracts | list, at least one | Required | The contracts, described below. |
One contract
| Field | Takes | If left out | What it does |
|---|---|---|---|
name | text, 3 characters or more | Required | The contract's name, such as refund.processed. |
version | whole number from 1 | Required | Raise it whenever a published contract changes. A published name and version never changes its contents. |
operation | text | Required | The operation it protects, such as refund.create. Issues and coverage group by it. |
intentSchema | JSON Schema file | Required | The shape of what the application meant to do. |
bindingSchema | JSON Schema file | Required | The shape of what identifies the thing that was made. |
observationSchema | JSON Schema file | Required | The shape of what the runner reads back. |
clientExport | identifier | Required | The name the application half is exported under in clientEntry. |
runnerExport | identifier | Required | The name the runner half is exported under in runnerEntry. |
schedule | object | Required | When to look, described below. |
supportsDiscovery | true or false | false | Whether the runner half can find a record by the reference sent with the call, when the reply was lost. |
evidenceAllowlist | list of JSON Pointers | empty | The observed fields that may leave the machine and be shown in the dashboard. Everything else stays local. |
redaction | object | nothing dropped or hashed | Under drop, fields that never leave the machine. Under hash, fields that leave only as a pseudonym made with the redaction key. |
expectations | object | the runner half decides the verdict | The verdict as data: rules under satisfied must all hold for Verified, and rules under violated mean Failed. Needs version 0.1.5 or later. |
schedule
| Field | Takes | If left out | What it does |
|---|---|---|---|
initialDelayMs | milliseconds | 0 | How long to wait after the call returns before the first look. |
baseIntervalMs | milliseconds | Required | The pause before the second look. Later pauses grow from it. |
maxIntervalMs | milliseconds | Required | The longest pause between looks. |
deadlineMs | milliseconds | Required | How long the outcome has to appear. An action still pending after it ends as Overdue. |
expectations
A rule has an id, a pointer to a field of the observation, an operator and usually an expected value. The expected value is fixed, or taken from what the application intended with { "$intent": "/amountMinor" }, or from the binding with { "$binding": "/refundId" }. Every rule under satisfied must hold for Verified. When they do not all hold and every rule under violated does, the outcome is Failed. Otherwise the runner looks again until the deadline.
| Operator | What it does |
|---|---|
equals | The field is there and equals the expected value. |
not_equals | The field is missing, or differs from the expected value. |
exists | The field is there. With expected set to false: it is missing. |
in | The field equals one of a list of expected values. |
gte | The field is a number at or above the expected number. |
lte | The field is a number at or below the expected number. |
Contract code
A contract has two halves. The application half runs inside your program, around the call. The runner half runs inside the runner and only reads.
The application half
deriveProviderCorrelationKeyreturns the reference that is sent with the call, so the record can be found by it later.bindResulttakes what the call returned and returns what identifies the record, in the shape of the binding schema, ornullwhen the reply does not say.classifyExceptiontakes an error the call threw and says what it proves, with acertaintyand whether it isretryable.
| certainty | What it does |
|---|---|
rejected | The provider refused the call. Nothing was done, and nothing is checked. |
not_executed | The call never left your application. Nothing was done. |
ambiguous | The error does not say whether the call was carried out. The runner then looks for the record, if the contract supports discovery. |
The runner half: discover
Called only when the application got no usable reply. It is given the intent and the reference, and returns an object whose kind is one of these.
| kind | What it does |
|---|---|
found | Exactly one record carries the reference. Return its binding. |
not_found | No record carries it yet. The runner looks again until the deadline. |
ambiguous | More than one record could be it. The outcome ends as Unknown, never as a guess. |
error | The read failed. Say whether it is worth trying again. |
The runner half: verify
Called on the schedule once the record is known. It is given the intent and the binding, and returns an object whose kind is one of these.
| kind | What it does |
|---|---|
observed | The record was read. Return it as observation. With expectations declared they decide the verdict; without them, return satisfied, violated or pending yourself. |
inconclusive | The record could not be read. Give the reason and whether to try again. |
The SDK
loadClientContractPackage({ directory }) loads a built package and checks it against its manifest. new Conseqa(options) takes these options.
| Option | Takes | If left out | What it does |
|---|---|---|---|
contractRegistry | from loadClientContractPackage | Required | The built contract package your application loaded. Required. |
runnerUrl | address | http://127.0.0.1:4319 | Where the runner listens. |
sidecarToken | secret | none | The runner's CONSEQA_SIDECAR_TOKEN. |
runner | transport | HTTP to runnerUrl | Your own way of reaching the runner. Cannot be combined with sidecarToken. |
spool | durable spool | none | Lets a required action go ahead while the runner is down, by writing the intent to an encrypted folder the runner shares. Without it a runner outage stops required actions. |
runtimeHeartbeatIntervalMs | milliseconds | 30000 | How often the application tells the runner which contract build it runs. 0 switches that off. |
instanceId | UUID | a new one | An opaque identifier of this application instance. Not a host name or a person. |
onTelemetrySignal | function | none | Called with each protection signal, such as a gap, so you can count them yourself. |
clock | function | the system clock | For tests. |
idFactory | function | random UUIDs | For tests. |
sdkVersion | text | the SDK's own | For tests and wrappers. |
conseqa.protect(contract, request)
| Field | Takes | If left out | What it does |
|---|---|---|---|
actionKey | text, up to 512 characters | Required | One stable key for each business operation, such as the ticket or order it belongs to. A key that was already used stops the call with DuplicateActionError. |
protectionMode | required or best_effort | Required | With required, the call is not made unless the intent was recorded first. With best_effort, the call goes ahead even if the intent cannot be recorded, and then shows as an unprotected gap. |
intent | JSON object | Required | What the call is meant to do, in the shape of the contract's intent schema. Rules can compare what was observed with it. |
execute | function | Required | The call you already make. It is given providerCorrelationKey, the reference to send with the call. What it returns or throws is what protect returns or throws. |
deadlineAt | date | the contract's deadlineMs after the intent is recorded | A deadline for this one action. |
conseqa.observe({ operation, execute }) runs a call without protecting it and marks it in your traces as one that could have been protected, so that coverage counts it. conseqa.close() stops the SDK's background reporting when your program ends.
Errors
| Error | What it does |
|---|---|
ProtectionUnavailableError | A required action could not be recorded first, so the call was not made. Nothing was sent to your provider. |
DuplicateActionError | The action key was already used, so the call was not made. Carries the identifier of the earlier action. |
ActionKeyConflictError | With a spool: the action key is already held for a different intent. Carries the identifier of the earlier action. |
RunnerUnavailableError | The runner did not answer, or answered with a server error. |
RunnerRejectedError | The runner refused the request. Carries the status and what it replied. |
ContractNotValidatedError | With a spool: the runner has not confirmed the contract build the application loaded, so an intent for it cannot be held while the runner is down. |
UnregisteredContractError | The contract passed to protect did not come from the loaded contract package. |
ContractPackageIntegrityError | The built contract package does not match its manifest. Build it again. |
SpoolQuotaExceededError | The spool is full, so the intent could not be written to it. |
Commands
The command-line tool is conseqa, from the package @conseqa/cli. Inside a project that has it installed, run it as npx conseqa.
conseqa init- Writes a starter contract into a project. Leaves existing files alone.
--directoryThe project folder. Defaults to the current one.
conseqa contracts build- Builds the contract package and prints its build hash.
--directoryThe project folder.
conseqa contracts install- Installs the built package into a runner.
--runnerThe runner's address. Required.--tokenThe runner's admin token. Defaults to CONSEQA_RUNNER_ADMIN_TOKEN.--allow-remote-runnerPermits a runner that is not on this machine. It must use HTTPS.--directoryThe project folder.
conseqa contracts publish- Sends the package's manifest and schemas to the hosted registry. The executable bundles stay with you.
--cloudThe hosted service's address. Required.--api-keyAn environment key. Defaults to CONSEQA_ENVIRONMENT_KEY.--directoryThe project folder.
conseqa contracts test- Judges the package's recorded cases without calling anything. Takes an optional contract name. Exits with an error if any case does not pass.
--directoryThe project folder.--jsonWrites the results to a JSON file.--junitWrites the results as JUnit XML.--uploadSends the result names and verdicts to the hosted service. Nothing is sent without it.--api-keyAn environment key, for --upload.
conseqa doctor- Checks that a runner is healthy and lists the contract builds it has loaded.
--runnerThe runner's address. Defaults to http://127.0.0.1:4319.--tokenThe sidecar token. Defaults to CONSEQA_SIDECAR_TOKEN.--expect-hashFails unless this build is loaded.--allow-insecure-private-runnerPermits plain HTTP to a runner on a private network you trust.
Runner settings
The runner is configured with environment variables. In the installation bundle they come from its .env file through Compose.
| Variable | Takes | If left out | What it does |
|---|---|---|---|
CONSEQA_HOST | address | 127.0.0.1 | The address the runner listens on. Anything other than a loopback address needs CONSEQA_SIDECAR_TOKEN. |
CONSEQA_PORT | port | 4319 | The port it listens on. |
CONSEQA_SIDECAR_TOKEN | secret | none | The bearer token your application presents to the runner. Required when the runner is not loopback-only or when it reports to the hosted service. |
CONSEQA_ADMIN_TOKEN | secret | none | The bearer token that installs contracts. Without it no contract can be installed. Keep it different from the sidecar token. |
CONSEQA_ALLOW_REMOTE_ADMIN | true or unset | unset | Lets a contract be installed from an address that is not loopback. The container sets it, because requests reach it across the Docker bridge. |
CONSEQA_CLOUD_URL | HTTPS address | none | The hosted service to report to. Set together with CONSEQA_CLOUD_API_KEY, or leave both out to keep everything on the machine. |
CONSEQA_CLOUD_API_KEY | secret | none | The environment key from Settings in your workspace. |
CONSEQA_REDACTION_KEY | 32-byte key, hex or base64 | the spool key | Turns a field listed under redaction.hash into a pseudonym before it leaves the machine. One of this and the spool key is required when reporting to the hosted service. |
CONSEQA_DATA_DIR | folder | .conseqa in the working folder | Where the runner keeps its records. |
CONSEQA_DATABASE_PATH | file | runner.sqlite in the data folder | The SQLite file that holds intents, events and what is waiting to be sent. |
CONSEQA_CONTRACTS_DIR | folder | contracts in the data folder | Where installed contract builds are kept, read-only once installed. |
CONSEQA_SPOOL_DIR | folder | spool in the data folder | The encrypted folder your application writes to when the runner is down, if a spool is configured. |
CONSEQA_SPOOL_KEY | 32-byte key, hex or base64 | no spool | Switches the spool on. The application and the runner must hold the same key and share the same folder. |
CONSEQA_SPOOL_KEY_ID | text | none | A stable name for the spool key, so a rotated key can be told from the old one. |
CONSEQA_SPOOL_QUOTA_BYTES | whole number | 104857600 (100 MiB) | The most the spool may hold. |
CONSEQA_PROBE_CONCURRENCY | whole number | 4 | How many outcomes are checked at the same time. |
CONSEQA_VERIFIER_TIMEOUT_MS | milliseconds | 5000 | How long one read of your system may take before it counts as inconclusive. |
CONSEQA_HEARTBEAT_INTERVAL_MS | milliseconds | 30000 | How often the runner reports its state to the hosted service. |
CONSEQA_RUNTIME_INSTANCE_TTL_MS | milliseconds, at least 60000 | 90000 | How long an application instance counts as running after it last reported to the runner. |
CONSEQA_COLLECTOR_HEALTH_URL | HTTP address | none | The health address of your OpenTelemetry Collector. When set, the runner reports whether the Collector answers. |
The runner's own traces follow the standard OpenTelemetry variables: OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT says where to send them, and OTEL_SDK_DISABLED=true switches them off.
States
Every action has two states. One says what happened to the call. The other says what the runner concluded about its outcome, and is the one the dashboard shows as the verdict.
The call
| State | What it means |
|---|---|
REGISTERED | The intent is recorded. The call has not started. |
DISPATCHING | The call was allowed to start and has not come back. |
RETURNED_SUCCESS | The call returned without an error. |
RETURNED_REJECTED | The provider refused the call, so nothing was done and nothing is checked. |
EXECUTION_AMBIGUOUS | The call ended in an error that does not say whether it was carried out, such as a lost reply. |
CONFIRMED_EXECUTED | After an ambiguous ending, the runner found the record by its reference: the call was carried out. |
ABANDONED_BEFORE_DISPATCH | The intent was recorded and the call was never started. |
The outcome
| State | In the dashboard | What it means |
|---|---|---|
DORMANT | Not evaluated | Nothing to check yet. |
AWAITING_BINDING | Awaiting binding | The call returned, and what identifies its result is not known yet. |
DISCOVERING | Discovering | The runner is looking for the record by the reference sent with the call. |
PENDING | Pending | The record was read and has not reached the expected state. The runner looks again. |
SATISFIED | Verified | Every rule that must hold, holds. |
VIOLATED | Failed | The record is in a state the contract calls a failure. |
TIMED_OUT | Overdue | The record never reached the expected state before the deadline. |
INCONCLUSIVE | Unknown | The runner could not establish what happened: the read kept failing, or a lost reply could not be matched to exactly one record. |
NOT_APPLICABLE | Not applicable | There is nothing to check, because the call was refused or never started. |
CANCELLED | Checking was cancelled. |
Limits
| What | What it does |
|---|---|
Action key | Up to 512 characters. |
Operation name | Up to 256 characters. |
One contract publication | Up to 1 MB of manifest and schemas. |
Trace retention | 14 days in a production environment and 3 days in any other. Settings shows the value for each environment. |
Monthly use in early access | 10,000 protected actions and 1,000,000 trace spans per workspace. Reaching a limit shows a notice; nothing is blocked. |
Alert delivery | Up to ten attempts with growing pauses. Your endpoint has five seconds to answer. |
Something missing or unclear here? Tell Devraj Jhala. Send a description, never a password, token, connection string or raw customer record.