Product · Pour
Coming SoonRuns in your CIPour checks that a copy of your data matches its source, row by row and value by value, and names exactly what doesn't.
Your data stays on your own infrastructure by default (see below for what an opt-in --submit sends). Point it at a source and a target, whether that is an old database and a new one after a migration, a source table and the copy a job produced, a file and its converted version, or this run's output and a known-good version you pinned, and it tells you if they are the same table.
It compares the data itself, so the same table in CSV, Parquet or a database still matches.
Two ways teams use it
A migration you sign off on. A pipeline you stop trusting blindly.
Migrations
- Reads Oracle, SQL Server, Postgres and files (JSON, CSV, Parquet), so a source and a target can be compared straight from the databases involved, not only from exports. The Postgres, Oracle and SQL Server readers are tested against live databases; the JSON, CSV and Parquet readers need no database at all.
- Declare a key and each table is compared by that key instead of by row position, so a target that returns the same rows in a different order still matches, and a missing row is reported by its key. A batch run compares a whole manifest of tables at once, with row counts and exact decimal control totals per table.
- A sixteen-digit amount that is off by one cent is invisible to any tool that reads it as a float, and a plain file checksum fails the moment the format changes even when nothing in the data moved. Pour reads decimals from their exact source text and compares cell by cell, so a one-cent difference on a large amount still fails the build (a real example from the tool's own test fixtures: 1234567890123456.78 versus .79), while a pure format conversion still passes.
pour initgenerates the manifest and a schema for each matched table straight from the two databases (or two directories) being migrated, instead of a person hand-writing one for a hundreds-of-tables migration. Differences you have reviewed and accept can be recorded in an accepted-differences file rather than re-explained on each run, and the sign-off pack turns a finished run into a document: an HTML pack, or a PDF/A-3b filing copy validated against veraPDF, with the approved differences, control totals and an approver field that is never pre-filled.
Everyday pipelines
- Catches a renamed column, a column that quietly stopped being written, a decimal that got rounded a place too far, and confirms a straight format conversion (JSON to Parquet, CSV to JSON) carried the data across unchanged.
- Pin a table's content ID once, and future runs fail the moment that table no longer matches it, useful for a nightly extract that should never silently change shape.
- Coming SoonA hosted dashboard tracks that history for you and can alert a channel or an email address the moment a pinned table changes, instead of you having to notice a red build.
Where your data goes
Nowhere, by default. The check runs on your own runner.
Pour reads your files and your database rows where they already are, inside your own pipeline, and prints an ID. No rows, no cell values and no column names are sent to us or to anyone else unless you pass --submit, which matters when the table holds patient, payment or customer records. A content ID cannot be reversed into the data, though like any hash it can confirm a guess about a very small, predictable table, so treat it as a fingerprint rather than as encryption.
With --submit, the CLI additionally sends the content IDs, the file basenames, the profile name, and CI metadata: repository, commit, ref and run ID, read from whichever CI system detected the run (GitHub Actions, GitLab CI, Jenkins, Azure DevOps, Bitbucket Pipelines, CircleCI or Buildkite). It never sends rows, cell values, column names or directory paths, and never the local diff file, which is the one place cell values are written. The server rejects any other field, so this holds even if the client were changed.
What it will not do
When it cannot be sure, it fails. It never guesses.
Row order, without a key
Two tables with the same rows in a different order read as different tables, unless the schema declares a key. That is a deliberate default for a plain file comparison; a migration should almost always declare a key.
No rounding, no guessing
A decimal with more places than declared, a timestamp finer than its unit or missing its time zone, or a column the schema does not name: each is refused with a non-zero exit, not coerced into a guess.
Sameness, not correctness
A match means the two tables read the same. It does not mean the source table was right to begin with. Pour proves two copies agree; it does not audit whether the original data was correct.
Nested values, such as a list or a struct inside a cell, are supported and compared exactly, including any decimals or timestamps inside them; struct members and map entries have no order of their own, so only the order of a list itself is kept.
Works with any CI
A GitHub Action, built and not yet published. A command line everywhere else.
A GitHub Action wraps the check for GitHub Actions specifically; it is built and tested, and will be available on the Marketplace once Pour ships (see Where it stands, below). Anywhere else, and today, run the same command-line tool as a regular shell step and let its exit code fail the job: it needs only a shell and Node 22, so it runs anywhere those are available. CI-provider metadata is detected for seven systems: GitHub Actions, GitLab CI, Jenkins, Azure DevOps, Bitbucket Pipelines, CircleCI and Buildkite. Only GitHub Actions is exercised in our own CI.
Once signed receipts (below) are live, a pour run --submit receipt will record that provider alongside the repository, commit, ref and run ID. A single verify --submit receipt, which is what the Action produces, does not carry it.
Signed receipts and the dashboard
Coming SoonA record of the check your own team could not have written afterwards.
Auditors already test whether the data a company relies on is complete and accurate. The usual evidence is row counts, totals and screenshots, produced by the same team being audited, often well after the fact. A signed receipt adds the two things a team's own log cannot prove on its own: when the check ran, by a clock the team does not control, and that the record has not been edited since.
Each receipt is signed with both Ed25519 and ML-DSA-65 and can be checked later, entirely offline, against our published public keys. A receipt proves what was recorded and when. It does not prove the data was right, only that these tables had these IDs at that time. If our service is unreachable, --submit prints a warning and your build's own result stands; a missing receipt never fails a build.
The hosted dashboard is the same idea made visible: a history of the receipts your team has submitted, and watches you can set on a table so a pinned or previously-seen content ID that changes unexpectedly emails or messages someone, instead of waiting for a person to read a build log.
Where it stands
The check is built and tested. Distribution is what is left.
Built and tested
- JSON, CSV and Parquet readers, including common compression codecs
- The Postgres, Oracle and SQL Server readers (read-only, tested against live databases)
- Exact decimals, dates, timestamps, times of day and intervals
- Nested lists, structs and maps
- Keyed tables, so row order stops mattering when a key is declared
- Bounded-memory streaming, measured at 10 million rows for CSV and Parquet (peak memory around 1 GB; a 10-million-row Parquet verify took about 17.6 minutes)
- The schema generator (pour schema)
- Batch runs across a manifest of tables, with row counts, control totals and a NOT CHECKED status for anything that could not complete
- The drift report, naming each change by key and column, with a full local export
- An accepted-differences file, for signing off on an explained drift
- pour init, generating a migration manifest and per-table schema from database to database or directory to directory
- The sign-off pack: HTML, and a PDF/A-3b filing copy validated with veraPDF
Built, not yet deployed
- Signed receipts: the endpoint exists but is not live, so --submit currently prints a warning
- The hosted dashboard, history, watches and alerts described above
Not built
- Snowflake, BigQuery, Teradata and Excel readers
None of this is installable yet: the repository is private, there is no npm package, and the GitHub Action is not on the Marketplace. Pour is a paid product, with a 14-day trial and no free tier; sign-up is not live yet and will be available when Pour launches. Until then, early access means a design partnership on a real migration wave, which shapes which database reader and which report format come first.
Get early access →