> ## Documentation Index
> Fetch the complete documentation index at: https://docs.synq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Getting started

> Install the CLI, write your first suite, and locate a real difference in under ten minutes

By the end of this page you will have run a reconciliation, seen it report a mismatch, and narrowed that mismatch down to the two rows responsible — all from your own machine.

The walkthrough uses a self-contained dataset so you can follow it with no warehouse and no credentials. [Point it at your own databases](#point-it-at-your-own-databases) at the end is a two-line change.

## Install the CLI

<Tabs>
  <Tab title="Docker">
    ```bash theme={null}
    docker pull europe-docker.pkg.dev/synq-cicd-public/synq-public/synq-recon:latest
    ```

    Run it with your working directory mounted, so it can read your suite file:

    ```bash theme={null}
    docker run --rm -v "$PWD:/work" -w /work \
      europe-docker.pkg.dev/synq-cicd-public/synq-public/synq-recon:latest \
      check-config suite.yaml
    ```

    <Note>
      The image runs DuckDB, which is what the walkthrough below uses so it needs no warehouse. If a `type: duckdb` connection fails with a driver error, your image predates DuckDB support — pull `:latest`.
    </Note>
  </Tab>

  <Tab title="Build from source">
    If you have access to the monorepo:

    ```bash theme={null}
    cd synq-recon
    go build -o synq-recon ./cmd/synq-recon
    ```

    CGO must be enabled (it is by default) for DuckDB connections.
  </Tab>
</Tabs>

<Note>
  A public download channel for the binaries is on its way. Until it lands, the Docker image is the artifact available to everyone — [get in touch](/support/support) if neither path works for you.
</Note>

Check that it runs:

```bash theme={null}
synq-recon --help
```

## Sign in

Authentication is only needed once you want to save suites to your workspace or run them on our backend. A purely local comparison needs no credentials at all.

```bash theme={null}
synq-recon auth login
synq-recon auth whoami
```

`auth whoami` prints the workspace your credential resolves to. Get into the habit of reading that line — it is the only thing that decides which workspace a command writes to.

## Describe what to compare

A **suite** is a YAML file holding the connections and the comparisons. Save this as `quickstart.yaml`:

```yaml quickstart.yaml theme={null}
# yaml-language-server: $schema=https://schemas.synq.io/synq-recon/v1/config.schema.json

name: quickstart
title: Quickstart

connections:
  local:
    duckdb:
      database: ":memory:"

# Stands in for two real systems: an application database and the warehouse
# copy of it. The copy is missing one order and has the wrong amount on another.
setup:
  local:
    - |
      CREATE TABLE app_orders AS
      SELECT i AS order_id, 'CUST-' || (i % 7) AS customer_id, i * 10.0 AS amount
      FROM generate_series(1, 1000) AS t(i)
    - |
      CREATE TABLE warehouse_orders AS
      SELECT order_id, customer_id,
             CASE WHEN order_id = 617 THEN amount + 5 ELSE amount END AS amount
      FROM app_orders
      WHERE order_id <> 384

reconciliations:
  orders:
    title: Orders (application vs warehouse)
    source:
      connection: local
      table: app_orders
    target:
      connection: local
      table: warehouse_orders
    key_columns: [order_id]
    mode: row_checksum
    bisection:
      factor: 10
      threshold: 10
```

<Tip>
  That first line points your editor at the published JSON schema, which gives you completion and inline validation for every field. Keep it at the top of every suite you write.
</Tip>

The `bisection` block is tuned small here so the drill-down has something to do on a thousand rows. On real tables the defaults are the right starting point.

## Validate before you run

```bash theme={null}
synq-recon check-config quickstart.yaml
```

```
Configuration Summary
=====================

Connections (1):
  - local (duckdb)

Reconciliations (1):
  - orders
    Source: local
    Target: local
    Key Column(s): order_id
    Mode: row_checksum
    Bisection: factor=10, threshold=10

Configuration check passed!
```

This costs nothing and touches no database. Add `--db` to also connect, run every query through the database's planner, and report table sizes, indexes and a suggested key column:

```bash theme={null}
synq-recon check-config quickstart.yaml --db
```

<Note>
  `--db` does not run the suite's `setup:` SQL, so for this walkthrough it will report that `app_orders` does not exist. That is expected — the tables are created by `setup` when the comparison runs.
</Note>

## Compare

```bash theme={null}
synq-recon run-check quickstart.yaml --no-report
```

```
Coalesce Quality Reconciliation - Quick Check
============================================

Reconciliation: Orders (application vs warehouse) (orders)
Mode: row_checksum
Duration: 6ms

             Source              Target
Row Count    1,000               999

Status: MISMATCH
  - Source has 1 more rows than target

Hint: Run 'synq-recon run-drill <config> --include orders' to locate differences.
```

Two queries — one per side — and you know the datasets disagree. If they had agreed, this would be the entire run.

<Note>
  The command exits `1`. For a comparison, a non-zero exit means "differences found", which is a result rather than a failure. [Exit codes](/reconciliation/running-locally#exit-codes) has the full list.
</Note>

`--no-report` keeps the run entirely local. Without it, a run reports its results to your workspace whenever it can find a credential, so they show up in the app alongside backend runs.

## Locate the difference

```bash theme={null}
synq-recon run quickstart.yaml --auto-drill --no-report
```

```
Depth 0: Segment [*, *)
  Source: 1,000 rows, checksum: -2052783606565609
  Target: 999 rows, checksum: -2210053578859533
  Status: MISMATCH -> drilling down (20/1,000 rows in mismatched segments)
  Depth 1: Segment [301, 401)
    Source: 100 rows, checksum: -167029730887454
    Target: 99 rows, checksum: -295596598382435
    Status: MISMATCH -> drilling down (10/100 rows in mismatched segments)
    Depth 2: Segment [381, 391)
      Source: 10 rows, checksum: 330886401684630
      Target: 9 rows, checksum: 202319534189649
      Status: MISMATCH (leaf: threshold_reached)
  Depth 1: Segment [601, 701)
    Source: 100 rows, checksum: -683539572079355
    Target: 100 rows, checksum: -712242676878298
    Status: MISMATCH -> drilling down (10/100 rows in mismatched segments)
    Depth 2: Segment [611, 621)
      Source: 10 rows, checksum: -381158226855223
      Target: 10 rows, checksum: -409861331654166
      Status: MISMATCH (leaf: threshold_reached)

Summary
-------
Segments processed: 33
Mismatch leaves: 2
Max depth reached: 2
Duration: 26ms
```

Read that from the top. The whole dataset disagrees, so it is split into ten segments; eight of them match on the first comparison and are never looked at again. The two that differ get split again, and the run stops with two ten-row windows to inspect.

Notice the shape of each finding. The segment around order 384 has **one fewer row** on the target — a missing row. The segment around order 617 has the **same count but a different checksum** — a value changed. You have not read a single row value yet.

<Tip>
  Thirty-three queries to find two bad rows in a thousand. The saving grows with the table: the number of comparisons scales with the *logarithm* of the row count, not the row count. That is the whole point of the drill-down.
</Tip>

## Get the rows

Every mismatch leaf comes with ready-to-run SQL for both sides:

```bash theme={null}
synq-recon run-drill quickstart.yaml --no-report -o json 2>/dev/null \
  | jq -r '.investigation_queries[] | .source_query, .target_query, "--"'
```

Run those against the two databases and you are looking at the rows themselves. This is the point at which row values enter the picture — on your terms, in your SQL client, for ten rows rather than a million.

<Warning>
  Redirect logs with `2>/dev/null`, never `2>&1`. Logs go to stderr and results to stdout; merging them makes `-o json` unparseable.
</Warning>

## Point it at your own databases

Swap the `connections:` block for the real thing, drop the `setup:` block, and name your tables. Nothing else changes:

```yaml suite.yaml theme={null}
connections:
  app:
    postgres:
      host: db.internal
      port: 5432
      database: payments
      username: ${PG_USER}
      password: ${PG_PASSWORD}
  warehouse:
    snowflake:
      account: myorg.us-east-1
      warehouse: COMPUTE_WH
      role: ANALYST
      username: ${SF_USER}
      password: ${SF_PASSWORD}
      databases: [ANALYTICS]

reconciliations:
  orders:
    title: Orders (application vs warehouse)
    source:
      connection: app
      table: public.orders
    target:
      connection: warehouse
      table: ANALYTICS.PUBLIC.ORDERS
    key_columns: [order_id]
    mode: row_checksum
```

Then run the same four commands. Two things to do before you trust the result:

1. **Keep the credentials out of the file.** Move the `connections:` block to a git-ignored `.connections.yaml` — the CLI picks it up automatically. See [keep credentials out of the suite](/reconciliation/authoring-suites#keep-credentials-out-of-the-suite).
2. **Run `check-config --db`.** It confirms every connection and query, and warns if your key column is not indexed — which is the difference between a drill that takes seconds and one that takes minutes.

## Next steps

<CardGroup cols={2}>
  <Card title="Authoring suites" icon="file-code" href="/reconciliation/authoring-suites">
    Datasets, column selection, key columns, and the validation loop in full.
  </Card>

  <Card title="Time windows and cutoffs" icon="clock" href="/reconciliation/time-windows-and-cutoffs">
    Comparing two live systems? Read this before you trust a mismatch.
  </Card>

  <Card title="Workspace suites" icon="cloud-arrow-up" href="/reconciliation/workspace-suites">
    Save the suite, run it on our backend, put it on a schedule.
  </Card>

  <Card title="CLI reference" icon="terminal" href="/reconciliation/cli">
    Every command, argument and flag, generated from the CLI itself.
  </Card>
</CardGroup>
