Tidewater has a small number of ideas. Once you know them, the rest of the product is predictable.
Workspaces
A workspace holds everything: pipelines, assets, owners, alert rules and API tokens. Most companies use one workspace per environment, for example production and staging. Workspaces never share data, and each one is stored in one region.
Pipelines
A pipeline is one scheduled job or stream that produces data. An Airflow DAG, a dbt job, a Spark application and a Kafka consumer group are each one pipeline. A pipeline has a name, a source system, an owner, and the assets it writes.
Pipelines are what you count and pay for. A pipeline that is paused does not count.
Runs
A run is one execution of a pipeline. It has a status (started, succeeded or failed), a start time, an end time and optionally the number of rows written. Runs come from integrations or from the API.
A run is identified by a run_key that you choose. Reporting the same key again updates the run, which is why retries are safe.
Assets
An asset is something that holds data and that people depend on: a table, a model, a topic or a dashboard. Assets are connected by lineage, and they are what alerts talk about. When you say “the orders table is late”, analytics.orders is the asset.
Expectations
An expectation is a statement of what normal looks like for a pipeline or an asset. Tidewater supports four kinds.
| Kind | What it checks | Example |
|---|---|---|
| Freshness | The newest data is recent enough | Current by 06:00 on weekdays |
| Volume | The number of rows is in range | At least 900,000 rows a day |
| Schema | Columns have not changed unexpectedly | No dropped or retyped columns |
| Custom SQL | A query you write returns what you expect | No orders with a negative total |
Fixed and learned thresholds
You can state an expectation yourself, or let Tidewater learn it. A learned threshold is built from at least 14 days of history. Tidewater models the normal range for each day of the week and flags values outside it. Learned thresholds suit volume and run duration. Freshness works best with a deadline written by a person, because a deadline is a promise, not a pattern.
Lineage
Lineage is the graph of which assets feed which. Tidewater builds it by reading SQL from your warehouse query logs and your transformation tool, so it works without changes to your code. Edges are recorded at the table level and, on Business and Enterprise plans, at the column level.
Lineage is what turns an alert into a decision. When the orders table is late, the lineage tells you that three models and two dashboards depend on it, and who owns each.
Alerts
An alert opens when an expectation is not met and closes when it is. An alert has a state:
- Open. The problem is current and nobody has said they are looking at it.
- Acknowledged. A person has seen it. Reminders stop, but the alert stays open.
- Resolved. The data has recovered. Tidewater closes the alert and records how long it lasted.
An alert carries its impact: the list of assets that depend on the affected one. Alert rules decide who receives it, using the owner of the asset, an urgency, and optional quiet hours.
Owners and groups
An owner is a team or a person. Owners come from your dbt metadata, orchestrator tags or settings in Tidewater. A group is a named set of pipelines that share an owner, an expectation and an alert rule, for example “finance close”.
Webhooks
Tidewater can send an event to your own HTTPS endpoint when an alert opens, is acknowledged or resolves. Each request is signed, so you can check that it came from Tidewater. The signature is an HMAC of the raw request body with your webhook secret, sent in the Tidewater-Signature header.
Python
import hashlib
import hmac
def is_valid(secret: bytes, body: bytes, signature: str) -> bool:
expected = hmac.new(secret, body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature) TypeScript
import { createHmac, timingSafeEqual } from 'node:crypto';
export function isValid(secret: string, body: string, signature: string): boolean {
const expected = createHmac('sha256', secret).update(body).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && timingSafeEqual(a, b);
} curl
# Compute the expected signature of a saved request body.
openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" body.json How it fits together
Your orchestrator reports runs of pipelines. Each pipeline writes assets. Expectations describe when and how those assets should look. Lineage connects the assets. When an expectation is missed, an alert opens, carries its impact from lineage, and goes to the owner.