# Run Flowscript locally

> Install the standalone package, declare your own typed sources, evaluate JSONL files or producer pipes, and save and resume a local run.

[Canonical documentation](https://flowscript.ftw.trade/docs/local)

Flowscript runs independently of ftw.trade. Declare your own sources, supply
observations and inspect the program's results on your machine. The local CLI,
JavaScript package and native Rust library use the same compiler and evaluator;
ftw.trade supplies one application host for them.

| Use | Entry point | Your application supplies |
|---|---|---|
| Local files or a producer pipe | The packaged `flowscript` CLI | A host JSON file and JSONL observations |
| A JavaScript or TypeScript service | `@flowscript/core` | A host object, retained run, observations and output consumers |
| A native service | `flowscript-core` | A `Host`, retained `live::Run` and application bindings |

The examples below use an arbitrary `prices` namespace. There are no injected
market names, FTW sources, accounts or credentials.

## Build and install the local package

There is currently no public package download or npm registry release. With
authorized access to the source, copy or check out `flowscript/` alone. Building
needs Node.js 22.18 or newer, npm, Rust and its `wasm32-unknown-unknown` target.
No other repository directory, application server or root `Justfile` is needed.

From `flowscript/npm`:

```sh
npm ci
npm test
npm pack
```

`npm test` builds the JavaScript and WebAssembly and tests the package. At the
current version, `npm pack` creates `flowscript-core-0.1.0.tgz`. The filename
follows `package.json`; packing does not publish it or change the repository's
license.

In a separate project directory, install that tarball. Replace the source path
with your own checkout or copy:

```sh
npm init -y
npm install /path/to/source/flowscript/npm/flowscript-core-0.1.0.tgz
./node_modules/.bin/flowscript --help
```

The tarball contains compiled JavaScript, the CLI and `flowscript.wasm`. Running
the installed package needs Node.js; it does not need Rust, TypeScript, the
source checkout or a connection to ftw.trade.

## Declare sources and a program

Save these three files in your project. Their values illustrate the input
contract; they are not a market dataset or strategy performance evidence.

`host.json`:

```json
{
  "sources": [
    { "name": "prices.mid", "type": "number" },
    { "name": "prices.status", "type": { "record": { "venue": "text", "asOf": "number" } } }
  ],
  "capabilities": [],
  "documentation": {
    "sources": {
      "prices.mid": "Observed midpoint in the producer's declared price units.",
      "prices.status": "Venue identity and original observation time in seconds."
    }
  }
}
```

`strategy.flow`:

```flowscript
flowscript "0.3";
uses { prices: "^1" }
const mid = prices.mid();
const status = prices.status();
const average = isNa(mid[2]) ? na : sma(mid, 3);
metric("mid", mid);
metric("average", average);
metric("asOf", status.asOf);
metric("venue", status.venue);
```

`observations.jsonl` (one complete JSON object per line):

```jsonl
{"time":1700000000,"values":{"prices.mid":100,"prices.status":{"venue":"demo","asOf":1700000000}}}
{"time":1700000001,"values":{"prices.mid":102,"prices.status":{"venue":"demo","asOf":1700000001}}}
{"time":1700000002,"values":{"prices.mid":104,"prices.status":{"venue":"demo","asOf":1700000002}}}
```

Check and run:

```sh
./node_modules/.bin/flowscript check strategy.flow --host host.json
./node_modules/.bin/flowscript run strategy.flow --host host.json --input observations.jsonl
```

The first JSONL output record has `type: "program"`, the artifact hash,
language version, declared reads, requirements, warnings and
`actionMode: "unsent-intents"`. Each following `type: "step"` record includes
the input line, time, bar, `metrics`, `texts` and `decisions`. The third step's
`metrics.average` is `102`; the program explicitly leaves earlier averages
unknown until three observations are present. Unknown numeric metrics encode
as JSON `null`. Each step's `texts.venue` is `"demo"`.

Errors go to stderr and exit with status 1; invalid command usage exits with
status 2. Nothing substitutes fabricated observations or restarts implicitly.

The host file is the same `Host` JSON accepted by `compile(source, host)`.
Source declarations provide types, key shapes and intervals; they do not fetch
data. There are no default sources or capability manifests. Declare each read
and action the program uses. The [embedding guide](https://flowscript.ftw.trade/docs/embedding) covers the
API; [libraries and tools](https://flowscript.ftw.trade/docs/extending) covers imports, typed external model
outputs, custom actions and integrations.

## Connect your own producer

Your producer can read a file, subscribe to a venue, collect sensor values or
run an external model in its own process. It writes the same JSONL frames to
stdout; application diagnostics belong on stderr. Pipe it into the evaluator:

```sh
node producer.mjs | ./node_modules/.bin/flowscript run strategy.flow --host host.json --input -
```

`producer.mjs` is your adapter, not a built-in subscription service. The CLI
does not connect to a data provider, poll a URL or infer symbol names. Choose
the producer's authentication, ordering, reconnect policy and freshness checks
explicitly. For an existing observation file, the equivalent pipe is:

```sh
cat observations.jsonl | ./node_modules/.bin/flowscript run strategy.flow --host host.json --input -
```

For a live producer, supervise both processes or enable your shell's `pipefail`
so a collection failure remains visible. EOF means the input stream ended; it
does not certify provider health. A producer must report unavailable data
explicitly or stop with an error, rather than quietly dropping failed reads.

Each frame has exactly `time` and `values`. Time is a nonnegative safe integer
in seconds and must not decrease; equal timestamps allow several observations
within one second. Preserve finer source time and sequence in declared fields
when you need them. `values` maps source names to their declared observations.
An omitted source holds its previous observation; explicit `null` makes it
unknown. A clock-only frame such as `{"time":1700000003,"values":{}}` still
steps the evaluator, including any deadlines. Build bars or sampling policies
in your adapter rather than assuming the CLI aggregates the feed.
Blank lines and extra frame fields are refused. End each appended file row
with a newline; appending inside an already consumed final row is an error on
resume. The CLI bounds source text to 1,000,000 UTF-8 bytes, the host document
to 4 MiB, an input line to 1 MiB and its checkpoint document to 32 MiB.

## Save and resume a file

For a file input, save the retained evaluator and its consumed input cursor:

```sh
./node_modules/.bin/flowscript run strategy.flow --host host.json --input observations.jsonl --checkpoint state.json
```

Append further complete observations to that same file. Resume explicitly:

```sh
./node_modules/.bin/flowscript run strategy.flow --host host.json --input observations.jsonl --checkpoint state.json --resume
```

Resume verifies the program and host identity and the exact consumed file
prefix before it evaluates the suffix. A changed prefix, incompatible program
or host, corrupt or missing checkpoint, or conflicting checkpoint ownership is
an error. Starting a new run over an existing checkpoint is refused. The CLI
does not silently discard state and replay from the beginning.

The runner holds a sibling `state.json.lock` while it owns the checkpoint and
removes it on normal exit. A killed process can leave that lock behind.
Confirm that no writer remains before removing a retained lock and resuming.

Checkpoint replacement atomically saves the evaluator snapshot, line cursor
and prefix hash together after each successful step and before its output.
It is available only with file input, not stdin.
Stdout and checkpoint storage do not form one transaction: a crash can leave
emitted output and the saved cursor out of step. Consumers that require durable
exactly-once effects need an application-owned transactional input/output
journal and reconciliation; redirecting stdout is not an outbox.

## Add execution in your application

A program can declare custom action capabilities and produce decisions. The
CLI prints these as unsent intents; it does not place orders, sign, send
transactions or report simulated fills. A manifest alone does not implement
an executor. Use the JavaScript or Rust embedding API to authorize and consume
decisions and report authoritative outcomes. Keep keys, limits, durable
submission identity and settlement reconciliation in that host.
The local runner refuses `async`/`await` actions and any behavior its runtime
reports as unmodelled. Embed Flowscript with a request and settlement
coordinator when a venue completes an action later.

The [outputs guide](https://flowscript.ftw.trade/docs/outputs) demonstrates application consumers. The
[performance guide](https://flowscript.ftw.trade/docs/performance) explains retained execution, queue and
allocation budgets and why evaluator timing alone is not order latency.
