> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

# Observe

You can verify a flow at three levels, each one step closer to production:

1. **Simulate**: run an event through the built flow offline, with destinations mocked, see [bundled mode](https://www.walkeros.io/docs/getting-started/modes/bundled.md#see-your-event).
2. **Preview**: run the flow where your app runs and watch each event in your console, see [integrated mode](https://www.walkeros.io/docs/getting-started/modes/integrated.md#see-your-event).
3. **Observe**: attach a running flow to an Observe session and watch its live records arrive in a feed, from any browser or server deployment.

This page covers the third rung: connecting a flow to an observer.

## What you commit vs what arrives later[​](#what-you-commit-vs-what-arrives-later "Direct link to What you commit vs what arrives later")

Your code and config carry only public connect values: the observer `url` and the project `binding`. Neither is a secret, both are safe to commit.

The per-session credential arrives out-of-band: as a URL parameter in the browser, or as environment variables on the server. **No credentials ever live in committed artifacts**, not in `flow.json`, not in a built bundle, not in your application code.

## Web, integrated mode[​](#web-integrated-mode "Direct link to Web, integrated mode")

Add the public connect pair to `startFlow`:

```
import { startFlow } from '@walkeros/collector';
import { sourceBrowser } from '@walkeros/web-source-browser';

const { elb } = await startFlow({
  sources: {
    browser: { code: sourceBrowser, config: { settings: { pageview: true } } },
  },
  observe: {
    url: 'https://observer.example.com', // public, safe to commit
    binding: 'pb_x', // public project binding, safe to commit
  },
});
```

Then open your dev URL with a session credential appended:

```
https://localhost:3000/?elbObserve=obsw_pb_x.ses_9.sec_1
```

On load, the runtime checks that the credential's binding matches the one in your config, persists the credential in the browser, removes the parameter from the address bar, and starts posting flow records to the observer. Later page loads reuse the stored credential, so one activation covers a whole browsing session.

Without a credential, the same page does zero observation work: nothing is parsed, stored, or sent. When the Observe session ends, the observer rejects the next post and the runtime detaches and clears the stored credential on its own.

## Web, bundled mode[​](#web-bundled-mode "Direct link to Web, bundled mode")

The same connect pair goes into `flow.json` under `config.observe`:

```
{
  "version": 4,
  "flows": {
    "default": {
      "config": {
        "platform": "web",
        "observe": {
          "url": "https://observer.example.com",
          "binding": "pb_x"
        },
        "bundle": {
          "packages": {
            "@walkeros/collector": {},
            "@walkeros/web-source-browser": {}
          }
        }
      },
      "sources": {
        "browser": {
          "package": "@walkeros/web-source-browser",
          "config": { "settings": { "pageview": true } }
        }
      },
      "collector": { "run": true }
    }
  }
}
```

Build once:

```
walkeros bundle flow.json -o ./dist/walker.js
```

The bundle carries only the public pair. `url` and `binding` must both be set: a partial pair warns at build time and wires nothing. Activation works exactly as in integrated mode, via the `?elbObserve=` parameter on any page that loads the bundle.

## Server[​](#server "Direct link to Server")

Server deployments read their connect config from the environment:

```
import { startFlow } from '@walkeros/collector';
import { observeFromEnv } from '@walkeros/core';

const { elb } = await startFlow({
  // undefined unless all three variables are set; undefined means zero observation work
  observe: observeFromEnv(process.env),
  // ...your sources and destinations
});
```

Set the environment on the deployment, never in code or config files:

```
WALKEROS_OBSERVER_URL=https://observer.example.com
WALKEROS_DEPLOYMENT_ID=ses_1a2b3c
WALKEROS_INGEST_TOKEN=... # secret, inject via deployment env only
WALKEROS_OBSERVE_LEVEL=standard # optional: off | standard | trace
```

`observeFromEnv` returns a config only when the full trio (`URL`, `DEPLOYMENT_ID`, `INGEST_TOKEN`) is present; otherwise it returns `undefined` and the deployment does no observation work. A set but unrecognized `WALKEROS_OBSERVE_LEVEL` (a typo like `offf`) also disables observation entirely rather than silently observing at `standard`. The ingest token is the one secret in this setup, which is why it lives in the deployment environment and nowhere else.

## Connect values vs telemetry knobs[​](#connect-values-vs-telemetry-knobs "Direct link to Connect values vs telemetry knobs")

The `observe` block carries two kinds of fields, and they answer different questions:

| Fields            | Question                                                                | Who sets them                         |
| ----------------- | ----------------------------------------------------------------------- | ------------------------------------- |
| `url`, `binding`  | Where to attach, and which project may attach                           | You, committed once                   |
| `level`, `sample` | How verbose managed telemetry is, and what fraction of records it emits | The managing platform, per deployment |

The web connect pair is `url` and `binding`, nothing else. `level` (`off` | `standard` | `trace`) and `sample` (a fraction from 0 to 1) are managed-telemetry knobs: leave them unset in your own config and let the managing platform decide what gets captured. It sets them per runtime, not per session: an Observe session's `--level` steers the container arm it provisions, while the web arm captures at the level baked into the bundle it loads. On the server, `WALKEROS_OBSERVE_LEVEL` is the same knob in environment form.

## Bring your own observer[​](#bring-your-own-observer "Direct link to Bring your own observer")

Observation is an open channel, not a hosted-only feature. Any function can receive the same flow records via `observers`:

```
const { elb } = await startFlow({
  observers: [
    (state) => console.log('flow state', state.stepId, state.phase),
  ],
});
```

Observers are advisory by contract: they run inside the collector's emit loop, and a throwing observer never breaks event processing. Use this to stream records to your own endpoint, a log pipeline, or a test assertion.

## Starting an Observe session[​](#starting-an-observe-session "Direct link to Starting an Observe session")

☁️walkerOS Cloud

Observe sessions with a live feed and minted credentials are part of the hosted app; `walkeros observe start <flowId>` mints one from the terminal and prints what each attached arm needs, the session expiry, and the received-record count. A preview arm prints the web activation link (the `?elbObserve=` credential already appended), a container arm prints the server environment trio, and which of the two attach follows the settings being observed. The session itself lives server-side and idles out when nothing keeps it alive. Self-hosting? The `observers` escape hatch above streams the same records to any function or endpoint you control. [What Cloud adds](https://app.walkeros.io)

## Next steps[​](#next-steps "Direct link to Next steps")

* [Deploy](https://www.walkeros.io/docs/getting-started/deploy.md): take the flow to production first
* [Bundled mode](https://www.walkeros.io/docs/getting-started/modes/bundled.md): the `flow.json` workflow
* [Collector reference](https://www.walkeros.io/docs/collector.md): full `startFlow()` API documentation
