Reference · SDK / runner 0.1.5

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.

FieldTakesIf left outWhat it does
packageNametextRequiredA name for this set of contracts. Shown beside each build in the dashboard.
packageVersionversion, such as 1.4.0RequiredThe version of the set as a whole.
runtimeTargettextnode22The runtime the bundles are built for.
sourceCommittextunknownThe commit the build came from, recorded in the manifest.
clientEntryfileRequiredThe file that exports each contract's application half.
runnerEntryfileRequiredThe file that exports each contract's runner half.
regressionsJSON filenoneCases for conseqa contracts test. Data only; an executable file is refused.
outputDirectoryfolderdist/conseqaWhere the build is written. It must be a folder of its own.
contractslist, at least oneRequiredThe contracts, described below.

One contract

FieldTakesIf left outWhat it does
nametext, 3 characters or moreRequiredThe contract's name, such as refund.processed.
versionwhole number from 1RequiredRaise it whenever a published contract changes. A published name and version never changes its contents.
operationtextRequiredThe operation it protects, such as refund.create. Issues and coverage group by it.
intentSchemaJSON Schema fileRequiredThe shape of what the application meant to do.
bindingSchemaJSON Schema fileRequiredThe shape of what identifies the thing that was made.
observationSchemaJSON Schema fileRequiredThe shape of what the runner reads back.
clientExportidentifierRequiredThe name the application half is exported under in clientEntry.
runnerExportidentifierRequiredThe name the runner half is exported under in runnerEntry.
scheduleobjectRequiredWhen to look, described below.
supportsDiscoverytrue or falsefalseWhether the runner half can find a record by the reference sent with the call, when the reply was lost.
evidenceAllowlistlist of JSON PointersemptyThe observed fields that may leave the machine and be shown in the dashboard. Everything else stays local.
redactionobjectnothing dropped or hashedUnder drop, fields that never leave the machine. Under hash, fields that leave only as a pseudonym made with the redaction key.
expectationsobjectthe runner half decides the verdictThe 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

FieldTakesIf left outWhat it does
initialDelayMsmilliseconds0How long to wait after the call returns before the first look.
baseIntervalMsmillisecondsRequiredThe pause before the second look. Later pauses grow from it.
maxIntervalMsmillisecondsRequiredThe longest pause between looks.
deadlineMsmillisecondsRequiredHow 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.

OperatorWhat it does
equalsThe field is there and equals the expected value.
not_equalsThe field is missing, or differs from the expected value.
existsThe field is there. With expected set to false: it is missing.
inThe field equals one of a list of expected values.
gteThe field is a number at or above the expected number.
lteThe 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

  • deriveProviderCorrelationKey returns the reference that is sent with the call, so the record can be found by it later.
  • bindResult takes what the call returned and returns what identifies the record, in the shape of the binding schema, or null when the reply does not say.
  • classifyException takes an error the call threw and says what it proves, with a certainty and whether it is retryable.
certaintyWhat it does
rejectedThe provider refused the call. Nothing was done, and nothing is checked.
not_executedThe call never left your application. Nothing was done.
ambiguousThe 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.

kindWhat it does
foundExactly one record carries the reference. Return its binding.
not_foundNo record carries it yet. The runner looks again until the deadline.
ambiguousMore than one record could be it. The outcome ends as Unknown, never as a guess.
errorThe 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.

kindWhat it does
observedThe record was read. Return it as observation. With expectations declared they decide the verdict; without them, return satisfied, violated or pending yourself.
inconclusiveThe 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.

OptionTakesIf left outWhat it does
contractRegistryfrom loadClientContractPackageRequiredThe built contract package your application loaded. Required.
runnerUrladdresshttp://127.0.0.1:4319Where the runner listens.
sidecarTokensecretnoneThe runner's CONSEQA_SIDECAR_TOKEN.
runnertransportHTTP to runnerUrlYour own way of reaching the runner. Cannot be combined with sidecarToken.
spooldurable spoolnoneLets 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.
runtimeHeartbeatIntervalMsmilliseconds30000How often the application tells the runner which contract build it runs. 0 switches that off.
instanceIdUUIDa new oneAn opaque identifier of this application instance. Not a host name or a person.
onTelemetrySignalfunctionnoneCalled with each protection signal, such as a gap, so you can count them yourself.
clockfunctionthe system clockFor tests.
idFactoryfunctionrandom UUIDsFor tests.
sdkVersiontextthe SDK's ownFor tests and wrappers.

conseqa.protect(contract, request)

FieldTakesIf left outWhat it does
actionKeytext, up to 512 charactersRequiredOne 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.
protectionModerequired or best_effortRequiredWith 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.
intentJSON objectRequiredWhat the call is meant to do, in the shape of the contract's intent schema. Rules can compare what was observed with it.
executefunctionRequiredThe 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.
deadlineAtdatethe contract's deadlineMs after the intent is recordedA 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

ErrorWhat it does
ProtectionUnavailableErrorA required action could not be recorded first, so the call was not made. Nothing was sent to your provider.
DuplicateActionErrorThe action key was already used, so the call was not made. Carries the identifier of the earlier action.
ActionKeyConflictErrorWith a spool: the action key is already held for a different intent. Carries the identifier of the earlier action.
RunnerUnavailableErrorThe runner did not answer, or answered with a server error.
RunnerRejectedErrorThe runner refused the request. Carries the status and what it replied.
ContractNotValidatedErrorWith 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.
UnregisteredContractErrorThe contract passed to protect did not come from the loaded contract package.
ContractPackageIntegrityErrorThe built contract package does not match its manifest. Build it again.
SpoolQuotaExceededErrorThe 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.
  • --directory The project folder. Defaults to the current one.
conseqa contracts build
Builds the contract package and prints its build hash.
  • --directory The project folder.
conseqa contracts install
Installs the built package into a runner.
  • --runner The runner's address. Required.
  • --token The runner's admin token. Defaults to CONSEQA_RUNNER_ADMIN_TOKEN.
  • --allow-remote-runner Permits a runner that is not on this machine. It must use HTTPS.
  • --directory The project folder.
conseqa contracts publish
Sends the package's manifest and schemas to the hosted registry. The executable bundles stay with you.
  • --cloud The hosted service's address. Required.
  • --api-key An environment key. Defaults to CONSEQA_ENVIRONMENT_KEY.
  • --directory The 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.
  • --directory The project folder.
  • --json Writes the results to a JSON file.
  • --junit Writes the results as JUnit XML.
  • --upload Sends the result names and verdicts to the hosted service. Nothing is sent without it.
  • --api-key An environment key, for --upload.
conseqa doctor
Checks that a runner is healthy and lists the contract builds it has loaded.
  • --runner The runner's address. Defaults to http://127.0.0.1:4319.
  • --token The sidecar token. Defaults to CONSEQA_SIDECAR_TOKEN.
  • --expect-hash Fails unless this build is loaded.
  • --allow-insecure-private-runner Permits 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.

VariableTakesIf left outWhat it does
CONSEQA_HOSTaddress127.0.0.1The address the runner listens on. Anything other than a loopback address needs CONSEQA_SIDECAR_TOKEN.
CONSEQA_PORTport4319The port it listens on.
CONSEQA_SIDECAR_TOKENsecretnoneThe 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_TOKENsecretnoneThe bearer token that installs contracts. Without it no contract can be installed. Keep it different from the sidecar token.
CONSEQA_ALLOW_REMOTE_ADMINtrue or unsetunsetLets 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_URLHTTPS addressnoneThe 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_KEYsecretnoneThe environment key from Settings in your workspace.
CONSEQA_REDACTION_KEY32-byte key, hex or base64the spool keyTurns 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_DIRfolder.conseqa in the working folderWhere the runner keeps its records.
CONSEQA_DATABASE_PATHfilerunner.sqlite in the data folderThe SQLite file that holds intents, events and what is waiting to be sent.
CONSEQA_CONTRACTS_DIRfoldercontracts in the data folderWhere installed contract builds are kept, read-only once installed.
CONSEQA_SPOOL_DIRfolderspool in the data folderThe encrypted folder your application writes to when the runner is down, if a spool is configured.
CONSEQA_SPOOL_KEY32-byte key, hex or base64no spoolSwitches the spool on. The application and the runner must hold the same key and share the same folder.
CONSEQA_SPOOL_KEY_IDtextnoneA stable name for the spool key, so a rotated key can be told from the old one.
CONSEQA_SPOOL_QUOTA_BYTESwhole number104857600 (100 MiB)The most the spool may hold.
CONSEQA_PROBE_CONCURRENCYwhole number4How many outcomes are checked at the same time.
CONSEQA_VERIFIER_TIMEOUT_MSmilliseconds5000How long one read of your system may take before it counts as inconclusive.
CONSEQA_HEARTBEAT_INTERVAL_MSmilliseconds30000How often the runner reports its state to the hosted service.
CONSEQA_RUNTIME_INSTANCE_TTL_MSmilliseconds, at least 6000090000How long an application instance counts as running after it last reported to the runner.
CONSEQA_COLLECTOR_HEALTH_URLHTTP addressnoneThe 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

StateWhat it means
REGISTEREDThe intent is recorded. The call has not started.
DISPATCHINGThe call was allowed to start and has not come back.
RETURNED_SUCCESSThe call returned without an error.
RETURNED_REJECTEDThe provider refused the call, so nothing was done and nothing is checked.
EXECUTION_AMBIGUOUSThe call ended in an error that does not say whether it was carried out, such as a lost reply.
CONFIRMED_EXECUTEDAfter an ambiguous ending, the runner found the record by its reference: the call was carried out.
ABANDONED_BEFORE_DISPATCHThe intent was recorded and the call was never started.

The outcome

StateIn the dashboardWhat it means
DORMANTNot evaluatedNothing to check yet.
AWAITING_BINDINGAwaiting bindingThe call returned, and what identifies its result is not known yet.
DISCOVERINGDiscoveringThe runner is looking for the record by the reference sent with the call.
PENDINGPendingThe record was read and has not reached the expected state. The runner looks again.
SATISFIEDVerifiedEvery rule that must hold, holds.
VIOLATEDFailedThe record is in a state the contract calls a failure.
TIMED_OUTOverdueThe record never reached the expected state before the deadline.
INCONCLUSIVEUnknownThe runner could not establish what happened: the read kept failing, or a lost reply could not be matched to exactly one record.
NOT_APPLICABLENot applicableThere is nothing to check, because the call was refused or never started.
CANCELLEDChecking was cancelled.

Limits

WhatWhat it does
Action keyUp to 512 characters.
Operation nameUp to 256 characters.
One contract publicationUp to 1 MB of manifest and schemas.
Trace retention14 days in a production environment and 3 days in any other. Settings shows the value for each environment.
Monthly use in early access10,000 protected actions and 1,000,000 trace spans per workspace. Reaching a limit shows a notice; nothing is blocked.
Alert deliveryUp 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.

Back to the guide