Skip to main content
Version: v0.0.3

Configuration reference

A project is configured by two kinds of YAML file, each checked in full before any command runs:

  • pathways.yaml at the project root, optional, for the whole project;
  • channels/<name>/channel.yaml, one per channel.

Unknown keys are refused. Relative paths are resolved against the file that holds them: the project root for pathways.yaml, the channel's folder for channel.yaml. Secrets (tokens, credential headers, connection URLs, master keys) are never values in the YAML: each is a secret reference, read when a command starts. Any other setting may be written as a value or read from the environment: see Settings from the environment.

JSON Schemas. The package ships a JSON Schema (draft-07) of each file, for editors and other tools: @cosyte/pathways/schema/project.json for pathways.yaml and @cosyte/pathways/schema/channel.json for channel.yaml, at node_modules/@cosyte/pathways/schema/ once installed. Each checks a subset of what pathways start checks: whatever start accepts, the schema accepts, and pathways validate checks the rest. See Check configuration in an editor and in CI.

Value types. A duration is a whole number of milliseconds, or a string such as 500ms, 5s, 2m, 1h or 30d. A memory size is a whole number of megabytes, or a string such as 256MB. A byte size is a whole number of bytes, or a string such as 512B, 64KB or 10MB (1 KB is 1,024 bytes). A secret reference is { env: NAME } (a variable name of letters, digits and underscores), { file: path }, or { secret: { provider, name, field, version } }; see Secrets.

Settings from the environment​

Every setting that holds one value (a port, a host, a duration, a size, a boolean other than plaintext, a path, a URL, a choice such as onStoreFailure or the storage kind, a key id, one event in events, one HTTP source path or method) may be written as { env: NAME }, or { env: NAME, default: value }, in place of the value. So one image, with the same YAML, runs in staging and in production, each with its own environment:

pathways.yaml
encryption:
keys: [{ id: prod, env: PATHWAYS_KEY }]
current: prod
storage:
kind: { env: PATHWAYS_STORAGE, default: sqlite }
postgres: { url: { env: PATHWAYS_DATABASE_URL }, tls: {} }
defaults:
retention: { env: RETENTION, default: 30d }
health:
port: { env: HEALTH_PORT, default: 9090 }
host: { env: HEALTH_HOST, default: 127.0.0.1 }
plaintext: true
  • The variable is read when the configuration is loaded, by every command, and its value is checked by the same rule as a value written in the file: HEALTH_PORT=70000 is refused as port: 70000 is. Text the setting takes is used as it is; otherwise text that spells a number is a number and true or false a boolean, so TIMEOUT=5000 is 5,000 milliseconds and TIMEOUT=5s five seconds.
  • default is used when the variable is unset or empty, and is read as a value written in the file would be. Without one, an unset variable is a problem.
  • A problem names the file, the key and the variable, never the value: pathways.yaml: health.port: Too big: expected number to be <=65535 (read from the environment variable HEALTH_PORT).
  • Only the variables the files name are read. No variable changes a setting the files do not name: there is no PATHWAYS_* prefix scan and no ${VAR} text substitution, so the YAML stays the one place a setting is named.
  • Secrets keep their own meaning of { env: NAME }: a token, a credential header, a connection URL or a master key is a secret reference, read when a command starts and never printed, and it takes no default. plaintext, encryption: none, auth: none, a channel's name and format, and a block (tls, options) are not read from the environment: turning TLS, encryption or authentication off stays in the file, where the audit sees it. Nor are the names in an HTTP source's contextHeaders: which request headers reach a transform stays in the file, where a credential header is refused at load. Nor is a channel's index: block: which ctx.meta keys are stored as searchable tags, and how many bits each keeps, stays in the file, where the audit sees it. Nor is an SFTP block's weakAlgorithms: turning a SHA-1 algorithm back on stays in the file too. Nor is a csv channel's csv: block: how its bodies are read and written belongs with its format.
  • pathways validate --print prints the configuration as start reads it, with every setting the environment gave and the variable it named; see Command reference. The query string of a URL read from the environment is printed as ?(hidden). The audit log's config entries name the variables each file reads, never their values, so a value changed only in the environment is not audited; see Read the audit log.

pathways.yaml​

KeyTypeDefault
encryptionsee Encryptionnone: start refuses
secretssee Secretsenv and file only
storagesee StorageSQLite, .pathways/pathways.db
defaultssee Settingsthe defaults below
hooksa list of hooksnone
healthsee Health endpointno health endpoint
probessee Probe listenerno probe listener
leasesee Engine leasea 15s lease, no standby
nodesee Nodea kept id, a 5s heartbeat
drainsee Drainno delay, a 30s timeout
migrationssee Migrationsno rollback window
configsee Transform configurationnone

Encryption​

One of three forms. pathways start and every command that reads stored messages need it; pathways test does not.

FormMeaning
nonestore messages unencrypted; logged at every start
{ keys: [...], current: id }master keys, each id with a secret reference ({ id, env: NAME }, { id, file: path } or { id, secret: {...} }); current names the one that wraps new values
{ provider: { module, options } }a module whose default export builds a KeyProvider

A key's id is 1 to 64 letters, digits, dots, hyphens and underscores, starting with a letter or digit, and unique in the list. A key's value is 32 bytes in base64. See Encrypt stored messages.

Storage​

Exactly one of these keys, or with kind, any of them, or content alone to keep the default SQLite file:

KeyValue
sqlite{ file: path }
postgresa database server block
mysqla database server block, for MySQL or MariaDB
sqlservera database server block
modulea path to a module whose default export builds a MessageStore; with options, any mapping handed to it
kindsqlite, postgres, mysql, sqlserver or module: the block used

kind, usually read from the environment, lets one file hold a block for each deployment, such as SQLite in development and PostgreSQL in production. The blocks it does not name are checked, not used. kind: sqlite needs no block of its own; any other kind needs its block. Without kind, the one block written is used.

and, beside it:

KeyValueDefaultMeaning
contentkeep or dropAfterDeliverykeepwhat is kept of a message's content; a channel overrides it, and only a channel may set none; see Drop content after delivery

A MessageStore from a module keeps the marker the engine writes before each send: markSending stores the attempt and its start, nextDelivery returns it as sending, and markDelivered, scheduleRetry and failDelivery clear it in the same write as their outcome. That is how a resend after a crash is flagged; see Delivery and errors. For a destination with retry.rotate: true, the engine asks nextDelivery for the oldest delivery due by now ({ dueBy }); a store that ignores the option keeps that destination in order. For a channel with content: dropAfterDelivery, it also implements the optional markDeliveredAndDropContent; on a store without it, pathways start refuses that channel.

Database server​

KeyTypeDefault
urlsecret reference{ env: PATHWAYS_DATABASE_URL }
tlsclient TLSrequired unless plaintext
plaintextbooleanfalse
pool.max1 to 1,00010
pool.idleTimeoutduration30s
connectTimeoutduration10s

See Store messages in a database server.

Settings​

defaults: in pathways.yaml sets these for every channel. A channel overrides the first six, and retry, at the top level of its channel.yaml; an MLLP source overrides onStoreFailure, an MLLP destination ackTimeout, and any destination retry.

KeyTypeDefaultMeaning
timeoutduration5show long one message's transform may run
memorymemory size256MBthe memory cap of a channel's transform worker
onStoreFailureclose, AE or ARclosewhat an MLLP source answers when a message cannot be stored
ackTimeoutduration10show long an MLLP destination waits to connect, then for each ACK
retentionduration, or a block30dhow long a done message is kept after its last activity, or { content, metadata, failed } with archive and audit in the project; see Set message retention
resendFlagbooleantruewrite a marker before each send, so a resend after a crash is flagged; see Delivery and errors
pollIntervalduration1show often the engine looks for new work, and polls a source that sets no poll; project only
retentionIntervalduration1hhow often a running engine runs retention; project only
retrysee Retrybelowhow a failed delivery is retried

Retry​

A retry: block, in defaults:, a channel, or a destination. The narrowest that sets a key wins, key by key. Each key may be read from the environment, none included, as limit: { env: RETRY_LIMIT, default: none }; first is checked against max on the values read. See Delivery and errors.

KeyTypeDefaultMeaning
firstduration, at least 1ms1sthe first retry's wait; no longer than max
maxduration, at least 1ms5mthe longest wait, whatever a Retry-After asks; a held redirect waits this long
factora number from 1 to 1002how much longer each wait is than the one before
jitternone or fullnonefull waits a random time between 0 and the wait
limita whole number of at least 1, or nonenonethe most attempts a delivery gets, the first included, before onExhaust
maxAgeduration of at least 1ms, or nonenonehow long after it was queued a delivery may still be retried, before onExhaust
onExhaustfailedfailedmark the delivery failed, its body kept for a resend, and move on
rotatebooleanfalselet later deliveries go ahead of one waiting for a retry; breaks the destination's order

Hooks​

A hooks: list, in pathways.yaml (every channel's events, and audit entries) or in a channel.yaml (that channel's events). Each entry is one of:

FormMeaning
patha module whose default export builds the hook
{ module: path, options: mapping }the same, with options handed to the factory
{ webhook: { url, headers, tls, plaintext } }the built-in webhook, an HTTP client

and each may add:

KeyTypeDefault
eventsa list of event names, at least oneevery event
timeoutduration above zero5s

The event names are received, receive-failed, transformed, filtered, transform-failed, delivered, delivery-retry, delivery-redirected, delivery-rejected, delivery-exhausted, source-down, source-up and audit. See Add lifecycle hooks.

Health endpoint​

A listener. Its token is read from PATHWAYS_HEALTH_TOKEN when auth is not set. See Check status and health.

Probe listener​

GET /livez and GET /readyz with no token, each answered 200 or 503 and one word, for a load balancer or an orchestrator. Off unless set. See Probe liveness and readiness.

KeyTypeDefault
port1 to 65,535required
hostan address127.0.0.1
plaintexttruerequired

It serves plain HTTP only, with no tls and no auth: it answers one word and takes nothing in, and a probe presents no token. plaintext: true says so, as it does for every endpoint without TLS.

Engine lease​

The lease one engine per database holds, so a second is refused or waits as a standby. See one engine per database.

KeyTypeDefaultMeaning
ttlduration, at least 1s15show long the lease lasts unless renewed; the holder renews it every third of this
standbybooleanfalsestart as a standby, as pathways start --standby does; needs a database server, and is refused with SQLite

Node​

This engine's node: the id it runs under, and how often it writes its heartbeat. See nodes and heartbeats.

KeyTypeDefaultMeaning
idnode idgenerated, kept in .pathways/node-id1 to 64 letters, digits, dots, hyphens and underscores, starting with a letter or a digit; unique among the engines on the database, or a second is refused
heartbeatduration, at least 1s5show often the engine writes its heartbeat; a node whose last heartbeat is three of these old shows stale
pathways.yaml
node:
id: engine-a
heartbeat: 5s

Written as id: { env: HOSTNAME }, each container takes its host name as its node id.

Drain​

How pathways start stops on SIGTERM or SIGINT. See Stop and upgrade a node.

KeyTypeDefaultMeaning
notReadyDelayduration0how long the engine keeps taking messages in after it reports draining (readiness 503), so a load balancer stops sending it new connections first
timeoutduration30show long it then lets the work in flight finish: each MLLP connection's current message and its ACK, the transforms and the sends under way

Each is at most about 24 days. Past timeout, a message not yet stored gets no ACK, so its sender sends it again, and a send under way is cut off and stays pending, sent again at the next start and flagged as a possible duplicate. A second signal stops the engine at once.

Migrations​

How pathways migrate --contract treats contract steps. See schema changes and upgrade Pathways.

KeyTypeDefaultMeaning
rollbackWindowduration0how long after the schema last changed contract steps are refused, so the version before can still be started again; 0 refuses none

Transform configuration​

What every channel's transform reads as ctx.config: a mapping of names to any YAML value (a string, a finite number, a boolean, null, a list or a mapping). A mapping with the key env, { env: NAME } or { env: NAME, default: value }, is read from that environment variable when the engine starts, never when the configuration loads; a variable that is not set, or is empty, takes the default, one value that is not a list or a mapping, and with none stops the start with its name, never a value. A channel's own config replaces this one key by key. See Use the message context.

channel.yaml​

KeyTypeDefault
namelowercase letters, digits and hyphensthe folder's name; must match it
formathl7v2, fhir, x12, ccda, json, text or csvhl7v2
csvhow a csv channel reads and writes; see Read delimited and fixed-width textcomma-delimited, with a header
sourceexactly one sourcerequired
transformpath to the transform modulenone: messages pass through
destinationsa list of at least one destinationrequired
hooksa list of hooksnone
storage{ content: keep }, { content: dropAfterDelivery }, or { content: none, passTimeout }; see Pass messages through without storing themthe project's storage.content; passTimeout 30s
configsee Transform configurationthe project's, key by key
indexa list of ctx.meta keys, each a key or { key, bits }; bits 4 to 32, default 12; see Search messages by metadatanone
timeout, memory, onStoreFailure, ackTimeout, retentionsee Settingsthe project's
resendFlag, retrysee Settingsthe project's

A channel with an MLLP source or destination must have the format hl7v2. A channel with a tcp source or destination and the format hl7v2, said or by default, loads with a warning: tcp sends no HL7 ACK, so HL7 v2 with ACKs goes over MLLP.

Sources​

Exactly one of file, http, mllp, tcp or module. A file or module source may add poll beside it.

Poll schedule​

poll says when the engine polls this source. Without it, a pass runs every pollInterval, the engine-wide setting, with a deadline of 5 minutes.

channels/nightly-claims/channel.yaml
source:
file: { dir: ./in }
poll: { cron: "0 2 * * *", timezone: America/Chicago, deadline: 10m }
destinations:
- file: { dir: ./out }
FormPasses
a duration, such as 30sone at start, then each this long after the last one ended
onStartone when the engine starts, and no other
{ every: 30s }as a duration
{ cron: "0 2 * * *", timezone: UTC }at each time the cron expression names, in timezone; none at start
{ onStart: true }as onStart

A mapping names exactly one of every, cron and onStart, and may add deadline (a duration, 5m by default). cron takes five fields (minute, hour, day of month, month, day of week), parsed by croner; timezone is an IANA name such as America/Chicago, UTC by default, and goes only with cron. Each key of the mapping takes an environment reference. When croner itself cannot be loaded, as after a broken install, a cron is refused with croner, which reads cron expressions, could not be loaded, and the load error's code when it has one, rather than as an expression of the wrong shape.

  • A pass never overlaps itself: a pass that comes due while the last one's poll call has not returned is skipped, logged as poll-overlap, and the next is scheduled as usual.
  • A pass that runs past its deadline is cut off, logged as poll-deadline: the source takes nothing more in that pass (each later message is refused with the code POLL_DEADLINE, before it is stored, so the source keeps it), and the signal the engine handed its poll aborts. Other channels go on meanwhile: each source's pass runs beside the engine loop, never in its way.
  • A pass missed while the engine was down, or while the last pass ran, is not made up: after a restart, an interval or onStart source passes at once and a cron source at its next time.
  • pathways start --once runs one pass of every source, whatever its poll.
  • A paused or stopped channel's source is not polled; a resumed one passes when its next pass is due.
  • With an engine lease, only the engine that holds it polls; a standby polls nothing until it takes the lease over.

File source​

KeyTypeDefaultMeaning
dirpathrequiredthe folder to read; created if missing
minAgeduration1show long a file must sit unchanged before it is read
keepProcessedbooleanfalsemove each stored file into processed/ instead of deleting it
includea list of globs or { regex }every filetake only a file whose path from dir matches one
excludea list of globs or { regex }noneleave a file whose path from dir matches one
recursivebooleanfalseread the subfolders too, never .claimed, processed or errorDir
ignoreDotbooleantrueleave files and folders whose name starts with a dot
minSizebyte size, or 01Bleave a smaller file; by default an empty file is left
maxSizebyte sizeno limitset a larger file aside: into errorDir, or left where it is
maxPerPolla whole number, 1 or moreno limitthe most files claimed in one pass
sortByname or mtimenamethe order files are taken in
stablebooleanfalsetake a file only once two passes in a row find it unchanged
doneFilea suffix, such as .donenonetake a file only once its marker is there, then delete the marker
errorDirpathnonewhere a file set aside goes, under its path from dir
readTriesa whole number, 1 or more3passes in a row a file fails to open before it goes into errorDir
sftpan SFTP blocknoneread dir on an SFTP server; dir and errorDir are its paths
s3an S3 blocknoneread objects in an S3 bucket; dir is the key prefix
azbloban Azure Blob blocknoneread blobs in an Azure Blob Storage container; dir is the prefix

With s3 or azblob, keepProcessed, doneFile, errorDir and readTries are refused: an object store claims by a marker and moves no file. At most one of sftp, s3 and azblob.

A path from dir is written with / on every system. A glob is read as Node.js's path.matchesGlob reads it, and its * never matches a leading dot. A regex is at most 256 characters. Each pattern runs on names whoever writes the files chooses, and can take minutes on a name built for it: a regex with nested repeats, such as ^(a+)+$, and a glob with several * in one segment, such as *_*_*_*_*.hl7, which Node.js turns into as many repeats. So each run of a glob or a regex on a path stops after 50 ms: that file is left where it is this pass, taken by neither include nor exclude, and the engine logs source-read-failed with the code ERR_SCRIPT_EXECUTION_TIMEOUT. The 50 ms are time on the clock, not work done, so a pause of a busy host can stop a harmless run too: the path is tried again on the next pass, and taken once a run finishes. After three timeouts in a row on one path it is set aside, and tried again after 1 pass, then 2, 4 and so on up to 64, each timeout logged again, for as long as the file stays. Write a regex with no repeat inside a repeated group, and a glob with few * in a segment. errorDir is compared with dir's subfolders after symbolic links are resolved. Choose which files a source takes shows each in use.

HTTP source​

A listener, and:

KeyTypeDefault
pathstarting with //
pathsa list of paths, in place of path[path]
methodsa list of POST and PUT[POST]
responseack, or { status, contentType, headers, body }202 with the id
maxBytesbyte size10MB
contextHeadersa list of header namesnone

Its token is read from PATHWAYS_HTTP_TOKEN when auth is not set. response: ack needs the format hl7v2 and takes POST alone. A fixed response has a 2xx status, an optional contentType, headers by the HTTP destination's rules, and a body of at most 4,096 characters. See Send and receive over HTTP for how a path is matched and how each response answers. contextHeaders names the request headers the transform reads in ctx.source.headers; Authorization, Proxy-Authorization and Cookie are refused there, and never passed. Any other header listed is passed as it came, a credential such as an API key header included.

MLLP source​

KeyTypeDefault
port1 to 65,535required unless connect
hostan address127.0.0.1
connect{ host, port }none: the source listens
replyTo{ host, port }none: ACKs go on the connection
connectTimeoutduration10s
tlsMLLP TLSrequired unless plaintext
plaintextbooleanfalse
onStoreFailureclose, AE or ARthe channel's
ackBuilderpath to a modulethe default ACK
maxConnections1 to 100,000100
receiveTimeoutdurationno limit
maxConnectionAgedurationno limit
maxMessageSizesize4MB
keepOpenbooleantrue

A connection over maxConnections is closed at accept. receiveTimeout closes a connection that goes that long without a whole message, a part-sent one included. maxConnectionAge closes a connection older than it after the message it brings and that message's ACK, so the sender connects again, through a load balancer to any node behind it. A message over maxMessageSize closes the connection and is not stored. keepOpen: false closes each connection once its messages are answered. See Bound connections and messages.

connect makes the source dial out to the sender in place of listening, so it cannot be set with port, host, maxConnections, keepOpen or replyTo; its tls is a client's, where tls: {} verifies the sender against Node.js's built-in roots. replyTo sends each ACK on a new connection to that address. connectTimeout bounds each connection the source opens itself, for connect or replyTo, and is refused without one of them. See Dial out to a sender and Send each ACK to another address.

TCP source​

KeyTypeDefault
port1 to 65,535required
hostan address127.0.0.1
tlsserver TLSrequired unless plaintext
plaintextbooleanfalse
framinga TCP framingrequired
maxConnections1 to 100,000100
maxMessageSizebyte size10MB; required with raw
idleTimeoutduration5m
stallTimeoutduration30s
reply{ hex } or { builder: path }none: no reply is sent

A connection over maxConnections is closed at accept, before any byte is read. A frame longer than maxMessageSize, or one that stalls for stallTimeout, closes the connection once the messages before it are stored and answered; the frame is never stored. A sender that closes or resets still has every whole frame it sent stored. A connection that begins no frame for idleTimeout is closed. An empty frame is skipped. reply.hex is the same reply for every message, at most 4,096 bytes in hex; reply.builder is a module whose default export, a TcpReplyBuilder, builds each one from the stored message's id. Either is framed as messages are. See Send and receive over TCP.

TCP framing​

framing names exactly one of:

FramingA message is
{ raw: true }every byte on a connection until the sender closes its side; one per connection
{ delimiter: "0a" }the bytes before the delimiter, 1 to 16 bytes in hex
{ lengthPrefix: { bytes, endian, inclusive } }the bytes after a length of bytes 1, 2 or 4, endian big (default) or little, the length counting the prefix too when inclusive (default false)
{ startEnd: { start: "02", end: "03" } }the bytes between the start and the end bytes, each 1 to 16 bytes in hex

Hex goes in quotes, two digits a byte, such as "0d0a": unquoted, YAML reads 10 as a number.

Module source​

module: path, with options: any mapping handed to its factory.

Destinations​

Each entry is exactly one of file, http, mllp, tcp, smtp or module, and may add a retry block of its own and dryRun, beside that key:

channels/results-out/channel.yaml
source:
file: { dir: ./in }
destinations:
- http: { url: "https://lab.example.test/results" }
retry: { limit: 10, jitter: full }
KeyTypeDefaultMeaning
dryRunbooleanfalsetransform and record each output, mark its delivery dry-run, and never send it: no connector is built, so no TLS file is read and no connection opened, and none of its secrets is read or resolved at start, a provider's (secret:) included, so a missing or expired one does not stop the start. Takes an environment reference. When dryRun is read from an environment variable, setting that variable to false makes the destination send for real, and the audit log does not record that change

A file, smtp or module destination may also set timeout, a duration (30s by default): how long one delivery may take in all before it is cut off and retried. See Every delivery has a deadline.

pathways start --dry-run runs every destination dry. Turning dry run on marks every pending delivery of that destination dry-run, including those queued or waiting for a retry before. See Upgrade Pathways.

Every delivery has a deadline​

Each attempt, connecting, sending and waiting for the receiver to confirm, runs under one deadline. At the deadline the engine stops waiting, aborts the signal it handed the destination, and counts the attempt as failed with the code DELIVERY_TIMEOUT: it is retried by the destination's retry settings, as any other failure.

DestinationDeadline
httpits timeout and one second
mllptwice its ackTimeout (one to connect, one for the ACK) and one second
tcptwice its timeout (one to connect, one for the send and its reply) and one second
fileits timeout, 30s by default
smtpits timeout, 30s by default
moduleits timeout, 30s by default

The second of grace lets an HTTP, MLLP or tcp destination's own timeout, with its own code, come first. A deadline longer than 24 days (the longest one Node timer holds) is kept in full: the engine waits 24 days at a time until it is due, so a timeout: 30d cuts an attempt off at 30 days, never sooner. A receiver may have the message when an attempt is cut off, and a destination that does not honour the signal may still finish its send after the cut, so a message cut off at its deadline may arrive twice. The retry is not flagged as a possible duplicate (no resend on the outgoing message, no HTTP resendHeader). Each output carries a stable id, its message's id and its seq, so a receiver should drop a copy by that pair. See Delivery and errors.

File destination​

file: { dir: path }: the folder to write into, created if missing. With an SFTP block, file: { dir, sftp } writes into dir on the server, an absolute path there.

SFTP block​

A file source's or destination's sftp:. Exactly one of knownHosts and hostKey: the server's key is always checked.

KeyTypeDefault
hosthost name or addressrequired
port1 to 6553522
userthe user to log in asrequired
keya secret: the private keyrequired
passphrasea secret: the key's passphrasenone
knownHostspath to a known_hosts filenone
hostKeythe server's public key, or its SHA256: fingerprintnone
weakAlgorithmsssh-rsa, hmac-sha1, hmac-sha1-etm@openssh.comnone
connectTimeoutduration20s
timeoutduration, for one request30s
keepaliveduration15s

Read and write over SFTP says what each does.

With an S3 block or an Azure Blob block, a file destination writes each output as an object under the prefix dir.

S3 block​

A file source's or destination's s3:. Credentials come from the AWS SDK's default chain, never the YAML.

KeyTypeDefault
bucketan S3 bucket namerequired
regiona region, such as us-east-1the AWS SDK's own chain
endpointan https:// URL, or http:// with plaintextAmazon's
plaintextboolean, only with an http:// endpointfalse
pathStylebooleanfalse
timeoutduration, for one request30s
singleNodeboolean, a source onlyfalse
modecreate or overwrite, a destination onlycreate

Read and write Amazon S3 says what each does.

Azure Blob block​

A file source's or destination's azblob:. The identity is Azure's default chain unless connectionString is set.

KeyTypeDefault
accounta storage account namerequired
containera container namerequired
endpointan https:// URL, or http:// with plaintexthttps://<account>.blob.core.windows.net
connectionStringa secret: a connection stringnone
plaintextboolean: allow an http:// endpointfalse
singleNodeboolean, a source onlyfalse
modecreate or overwrite, a destination onlycreate

Read and write Azure Blob Storage says what each does.

HTTP destination​

KeyTypeDefault
urlan https:// URL, or http:// with plaintextrequired
headersa mapping of names to a string or a secret referencenone
tlsclient TLSNode.js's built-in roots
plaintextboolean, only with an http:// URLfalse
timeoutduration30s
resendHeadera header namenone

The URL may not hold a user name or password. A header whose name holds auth, cookie, token, secret, password, key, signature or credential must be a secret reference. Content-Length, Transfer-Encoding, Connection and Keep-Alive are set by Pathways and may not be given. Content-Type defaults by format.

resendHeader names a header sent only on a possible duplicate: a resend after an attempt was cut off by a stop. Its value is the number of attempts before, such as 1. It may not be one of the headers above, Content-Type, or a name headers sets. See Delivery and errors.

A 2xx is delivered; a 408, 425, 429, 5xx, a network error or no answer within timeout is retried, waiting longer when a 429 or a 503 asks with Retry-After, never more than retry.max (5 minutes by default); a 3xx is held, never followed, and retried every retry.max; a 101 or any other 4xx fails the delivery for good. The destination's retry block sets the waits and any limit. Any other 1xx is an interim answer: the attempt waits for the final answer after it, and is retried when none comes within timeout or the connection closes. See Send and receive over HTTP.

MLLP destination​

KeyTypeDefault
hostan addressrequired
port1 to 65,535required
tlsMLLP TLSrequired unless plaintext; {} uses Node.js's built-in roots
plaintextbooleanfalse
ackTimeoutdurationthe channel's

TCP destination​

KeyTypeDefault
hostan addressrequired
port1 to 65,535required
tlsclient TLSrequired unless plaintext; {} uses Node.js's built-in roots
plaintextbooleanfalse
framinga TCP framingrequired
maxMessageSizebyte size10MB; required with raw and expectReply
timeoutduration10s
expectReplybooleanfalse
replyCheckpath to a modulenone: any reply means delivered; needs expectReply: true

Each output goes as one frame on a connection of its own. Without expectReply, it is delivered once it is written; with it, the destination reads one framed reply, at most maxMessageSize, within timeout. replyCheck is a module whose default export, a TcpReplyCheck, answers delivered, retry or rejected. An output the framing cannot carry fails for good.

SMTP destination​

KeyTypeDefault
hosthost name or addressrequired
port1 to 65535465; 587 with starttls; 25 plaintext
tls{ mode, ca }: mode is implicit or starttlsrequired unless plaintext; {} is implicit
plaintextboolean; cannot go with user and passwordfalse
usera secret, with passwordnone
passworda secret, with usernone
froman addressrequired
toa list of addresses, at least onerequired
cca list of addressesnone
subjecta templatePathways message {{ messageId }}-{{ seq }}
bodya templatea line naming the channel, message and seq
attachboolean: attach the output as a filefalse
onPartialRefusalrejected or delivered: some recipients refusedrejected
maxSizea size such as 10MB, or a number of bytes10MB
connectTimeoutduration, at most the destination's timeout; covers the greeting and a STARTTLS upgrade10s
greetingTimeoutduration, at most the destination's timeout10s
socketTimeoutduration, at most the destination's timeout20s

An address is plain, such as lab@example.test, with no display name. A template's placeholders are {{ messageId }}, {{ seq }}, {{ channel }} and {{ meta.<key> }}. The server's certificate is always verified, with TLS 1.2 or later. Send email over SMTP says what each answer means.

Module destination​

module: path, with options: any mapping handed to its factory.

Shared blocks​

Listener​

What the HTTP source and the health endpoint take:

KeyTypeDefault
port1 to 65,535required
hostan address127.0.0.1
tlsserver TLSrequired unless plaintext
plaintextbooleanfalse
authnone, or { bearer: <secret reference> }mutual TLS when tls.ca is set, otherwise a bearer token from the default variable

TLS​

BlockKeys
server TLScert and key, required; ca to require client certificates it signed (mutual TLS)
client TLSca to trust in place of Node.js's built-in roots; cert and key, together, to present a client certificate

tls and plaintext: true cannot both be set.

An MLLP source's tls is the server block, and an MLLP destination's the client block, each with these keys too:

KeyTypeDefault
minVersionTLSv1.2 or TLSv1.3TLSv1.2
profilebcp195 or atnabcp195, unless ciphers is set
ciphersan OpenSSL cipher listnone; cannot be set with profile
pfxpath to a PKCS#12 filenone; in place of cert and key
passphrasesecret referencenone; goes with pfx

See Choose the TLS version and cipher suites.

Secrets​

A secret reference names where a secret is read when a command starts. It is one of:

FormRead from
{ env: NAME }the environment variable NAME; unset and empty are the same
{ file: path }the file, relative to the YAML file that names it, with one line break at its end dropped
{ secret: { provider, name, field, version } }the provider provider: env, file, or one named under secrets:; field and version optional

field takes one field of a secret that holds a JSON object, or of a store's structured secret. version asks for a version the provider keeps; env and file keep none. { env: NAME } is the same as { secret: { provider: env, name: NAME } }, and { file: path } as the file provider's.

secrets: in pathways.yaml names the other providers, by the name a reference gives:

KeyTypeDefault
modulea module whose default export builds a SecretProvider, required
optionsa mapping, handed to the module's factory{}
timeoutduration: how long resolving one secret may take10s

A provider's name is a letter, then letters, digits, hyphens and underscores, at most 64; env and file are taken. A reference to a provider that is not named is refused at load, and pathways validate checks that each module exists without loading it. At start, each provider a reference names is built, asked once for each secret, then stopped. A secret it cannot answer, answers late or answers expired stops the start, named by its file and key, its name, and the error's name and code; never a value. See Read secrets from files and secret stores.