Chalk starts at the feature. Signals starts at the event.
Both serve values fast enough to sit in a request path. They differ on what they expect to find already in place, on when the value is computed, and on who is meant to be holding the keyboard.
Both fast in a request path, different assumptions about what feeds them.
| Dimension | Signals | Chalk |
|---|---|---|
| What you bring | An SDK in your app. Behavioral events are collected and schema-validated on the way in. | Data sources to resolve from. Collection and event schema are out of scope. |
| Where behavior comes from | Built in. Web, mobile and server SDKs feed the same governed schema. | Wherever you already put it. If there is no behavioral stream, that is a project first. |
| Primary consumer | Application surfaces and agents first, models second. | Models and the engineers who build features for them. |
| Who defines an attribute | A product manager in the console, or an engineer in code through CI/CD. | An engineer, in Python, in the platform's own resolver model. |
| Entity keys | Any entity: user, session, account, basket, listing, store. | Supported, but the entity has to exist in the sources you resolve from. |
| Attribute update latency | <1s from the event to the attribute being current. | Depends on the source being resolved and how it is cached. |
| Serving latency | 6ms p50, 10ms p95 in-region. One call returns a whole service. | Designed for request-path latency, varying with the resolvers in the path. |
| Real-time triggers | Conditions evaluated continuously, delivered to your app or to Braze, Kafka, webhooks and Pub/Sub. | Not the focus. Acting on a condition is a system you build beside it. |
| Deployment | Managed SaaS or a private deployment in your own cloud, identical architecture. | Managed, or a private deployment in your own cloud account. The two are level here. |
Based on published vendor documentation at the time of writing · confirm against the current product before quoting it
The behavioral events do not exist yet, or exist and nobody trusts them.
The consumer is a product surface or an agent, not only a model.
A product manager needs to define an attribute without opening an engineering ticket.
You want triggers on live conditions in the same system that computes the attributes.
The event schema needs governing at collection rather than assumed correct downstream.
Your data sources are already in place, and resolving across them is the hard part.
The people defining features are engineers who want to express them in Python.
Your inputs come mostly from operational databases and third-party APIs rather than behavior.
The ML feature lifecycle, rather than the application surface, is what you are buying for.
You do not need collection, schema governance or triggers in the same product.
Signals can be the behavioral source Chalk resolves against.
Signals for behavior, Chalk for everything else
Let Signals own the live behavioral half, and keep resolving your operational and third-party sources where they already are.
Signals for the application path only
Models keep reading the feature platform. The storefront, the agent and the service desk read Signals at request time.
Signals instead, for now
Teams whose inputs are overwhelmingly behavioral start on Signals and add a feature platform later if the model estate demands it.
p50 in-region read, one call per service
Event in to attribute current
Shared by live serving and the training set
user · session · basket · listing · store
The questions your architect will ask.
Do these two actually compete?
They overlap on serving values fast enough for a request path. They diverge on collection, on governance of the event schema, and on whether a non-engineer can define an attribute.
We already have our behavioral data somewhere. Does the collection advantage matter?
Only if you trust it. The question to ask is whether a new attribute needs a data engineering ticket today, and how long that ticket takes.
Can we run both?
Yes. The clean split is behavior in Signals, operational and third-party sources resolved where they already live.
What happens when serving is unreachable?
The SDK reports it and marks values stale, and your application decides whether to fall back or degrade the surface. Worth putting the same question to Chalk, because a resolver that reaches a live source at request time has more places to fail.
Test it against your own behavior.
Define one attribute, read it from your product, and see how much of the work was collection rather than computation.