1# Chapter 12: postjevsql-sidecar/cli, the one engine
2
3This is the binary, `postjevsql-sidecar`, and the only part of the sidecar
4that talks to Postgres. It takes the config from chapter 11 and one of three
5commands:
6
7```text
8postjevsql-sidecar [--sidecar <conninfo>] <config.toml> converge
9postjevsql-sidecar [--sidecar <conninfo>] <config.toml> sync [--apply]
10postjevsql-sidecar <config.toml> serve [-- <postgres options>…]
11```
12
13- **`converge`** creates `postgres_fdw` and `postjevsql` on the sidecar,
14  declares the foreign server with exactly the configured options (so an
15  option someone added by hand is dropped), and declares the user mappings:
16  a password from its secret, or PG18's `use_scram_passthrough` when none
17  is given.
18- **`sync`** reads the target's catalog and prints the plan from
19  `sync::plan`. **`sync --apply`** applies it in one transaction, unless a
20  change touches something a sidecar view, `BEGIN ATOMIC` function or grant
21  depends on (pg_depend), in which case it names each dependent and changes
22  nothing.
23- **`serve`** is the container image's entrypoint: initialise `$PGDATA` if
24  it is empty, start Postgres, wait for it, `converge`, `sync --apply`, stop
25  it with `pg_ctl stop`, and then exec `postgres` in its own place, so the
26  server is PID 1 and gets the container runtime's signals directly. A
27  refusal stops the server and exits non-zero with the reason.
28
29`--sidecar` is the conninfo of the sidecar itself; by default
30`host=/run/postgresql user=postgres dbname=postgres`. Every secret is
31resolved before anything connects, so a config that names a secret twice is
32refused first.
33
34```mermaid
35sequenceDiagram
36  participant CLI as postjevsql-sidecar sync
37  participant S as Sidecar Postgres
38  participant T as Target (through the foreign server)
39  CLI->>S: BEGIN, then CREATE SCHEMA postjevsql_sync_0
40  CLI->>S: as the first mapping's user, IMPORT FOREIGN SCHEMA public FROM SERVER target INTO postjevsql_sync_0
41  S->>T: read the catalog
42  T-->>S: the target's tables, as postgres_fdw imports them
43  CLI->>S: read the scratch tables and the existing foreign tables
44  CLI->>S: ROLLBACK
45  CLI->>CLI: sync::plan, then (with --apply) check pg_depend and apply in one transaction
46```
47
48> **Aside.** Notice that the CLI never connects to the target. It reads the
49> target's catalog *through the sidecar's own foreign server*, importing it
50> into a scratch schema inside a transaction that is rolled back. So there is
51> no second connection to configure, no second set of credentials, and the
52> column types it compares are exactly the ones postgres_fdw would import.
53> One cost, stated: a function with a string body is not tracked by
54> pg_depend, so a change it depends on is not found.
55
56> **Try it.** `nix develop -c buck2 test //tests:sidecar_cli` starts a
57> throwaway target and a throwaway sidecar and checks that `converge`
58> declares exactly the configured server, that `sync` changes nothing, that
59> `sync --apply` changes only what differs, and that a change a view depends
60> on is refused with nothing applied.
61
62## For the people who maintain it
63
64| File | What |
65| --- | --- |
66| [main.rs](main.rs) | The three commands, the secret resolution before connecting, the catalog read through `IMPORT FOREIGN SCHEMA`, the pg_depend check, and `serve`'s start, converge, stop and exec. Built as `//crates/postjevsql-sidecar:cli`, packaged by `nix/sidecar-cli.nix`. |
67
68← Previous: [Chapter 11, postjevsql-sidecar/src/](../src/) · Up: [postjevsql-sidecar](../) · Next: [Chapter 13, tests/](../../../tests/) →