Skip to main content
A signal is a telemetry envelope your app sends so FireWeave can see how a release is going. The spec defines four kinds only: health, error, metric, and outcome. OpenFeature and the wire still say flagKey when a signal names a control point. Capture uses type: "signal". These are not the in-app Log / Alert / Block severities.

Why it exists

Evaluation says what value was chosen. Signals say what happened next — a component is degraded, an error you already classified, a numeric observation, or a user-visible result. Messages are secret-redacted. Attributes pass an allowlist so arbitrary PII does not go on the wire.

When to use it

Do not invent extra kinds. Do not map console Block onto recordError.

How it relates

Whether signals always reach fw-server is NEEDS VERIFICATION (compatibility known gap: Go/Java sink vs Node/Python in-process record). Check capabilities.get().runtime.features and do not claim universal delivery.

Shared envelope

Required by spec/signal.schema.json: kind + name (1–256 chars). Optional correlation: targetingKey, rolloutId, changeId, stampId, flagKey, variant. Optional status, errorKind, message, value, unit (language-specific), attributes.
  • Calls are opt-in per invocation — nothing is emitted until you call a record* method.
  • Empty name is rejected (InvalidContext on Node).
  • Extension lifecycle gate: degrade before READY / after shutdown; never throw (Go returns error).
  • Node/Python keep an in-process list (getRecorded() / signals.recorded) useful for tests.

Health

Purpose: “Is this component ok?” Typical fields: name, status (spec examples: ok, degraded).

Error

Purpose: A classified failure. errorKind uses the SDK’s 15-kind taxonomy (for example Timeout). message is redacted.

Metric

Purpose: A named observation. Spec value is number, boolean, or string. Python’s helper types value as float. Optional unit.
There is no SDK type named “adoption signal.” Use recordMetric or recordOutcome.

Outcome

Purpose: Record that a result happened. This is a signal kind, not its own product object and not a separate docs page. signals.recordOutcome / record_outcome / RecordOutcome takes a name + status and optional rollout correlation (rolloutId, changeId on Python). Node releases.complete() / fail() also records { kind: 'outcome', name: 'release', status: 'completed' | 'failed' }. Completing a release is still a different call from recording an application outcome (for example checkout completed).
Whether console Completed / Rolled back consume these signals is NEEDS VERIFICATION. Node also exposes generic signals.record({ kind, name, ... }). Java has signals().record(Signal).

Attribute allowlist

Unknown attribute keys are dropped, not rejected. String values are secret-redacted. Node default (DEFAULT_SIGNAL_ATTRIBUTE_ALLOWLIST, on unless you override telemetry.attributeAllowlist) and Python _SIGNAL_ATTRIBUTE_ALLOWLIST: name, kind, status, value, unit, rolloutId, changeId, stampId, errorKind, message, flagKey, variant, environment, service Go’s emission allowlist is slightly different: flagKey, variant, value, rolloutId, changeId, stampId, stampIds, status, name, kind, errorKind, message, metricValue. Java signals use a fixed canonical field set; FireweaveConfig.telemetryAttributeAllowlist can filter further. Do not send arbitrary PII as signal attributes and expect it to survive.

Batching and failure

  • Signals are recorded when you call record*. Remote adapters capture via POST /v1/capture when a sink is attached.
  • Node calls adapter.recordSignal when present. Python calls deliver_signal and swallows sink exceptions.
  • Telemetry loss must not break evaluate or login paths.
  • After shutdown, calls degrade with AlreadyClosed.
Last modified on August 18, 2026