1# Chapter 10: postjevsql-sidecar, for databases that won't let you in
2
3Most people's Postgres is somebody else's: RDS, Aurora, Cloud SQL, Azure,
4Supabase. None of them will load a custom compiled extension, so
5`CREATE EXTENSION postjevsql` is not an option there. The *sidecar* is the
6way round. You run a small Postgres of your own beside the real one, install
7the extension there, and let it see the real database's tables through
8`postgres_fdw`, Postgres's own way of reading another server's tables as if
9they were local. Your app connects to the sidecar with any ordinary client.
10
11```mermaid
12flowchart LR
13  app["your app, psql, a GUI"] -->|"the ordinary protocol"| side
14  subgraph side["the sidecar (a Postgres you run)"]
15    ext["postjevsql + jev_cache"]
16    ft["foreign tables, one per target table"]
17  end
18  ft -->|"postgres_fdw over verify-full TLS:<br/>only the rows that pass the other filters"| tgt[("the target (managed, untouched)")]
19  ext --> J["Jev"]
20```
21
22The split falls out of a rule postgres_fdw already has: it ships a function
23to the remote only if it is immutable, and every `jev*` function is
24volatile. So the ordinary `WHERE` clauses run on the target, the rows that
25pass come over, and the judging happens on the sidecar. The cache lives on
26the sidecar too, and the target is never written to for bookkeeping.
27
28Setting all of that up (the foreign server, the user mappings, one foreign
29table per target table, kept in step as the target's schema changes) is the
30job of this crate. It is one engine, called by every launcher: the NixOS
31module (`nix/sidecar.nix`), the container image (`nix/sidecar-image.nix`,
32whose entrypoint is this CLI's `serve`), and anyone pointing it at a
33Postgres they already run.
34
35> **Aside: what it costs.** Nothing here is free, and the contract says so.
36> Every row that passes the target's filters crosses the network. A query
37> whose `WHERE` has a `jev` call cannot push its joins, aggregates or `LIMIT`
38> down to the target, so a `count(*) … WHERE jev(…)` reads every candidate
39> row. And postgres_fdw does not do two-phase commit: the target commits
40> first, so a failure between the two loses only the sidecar's cache rows.
41
42> **Try it.** `cargo test -p postjevsql-sidecar` runs the config, secret and
43> sync-plan tests, no database needed. The CLI's own tests, against a real
44> throwaway target and sidecar, are `nix develop -c buck2 test
45> //tests:sidecar_cli`.
46
47## For the people who maintain it
48
49| Path | What |
50| --- | --- |
51| [src/](src/) | The parts with no I/O: the config, the secrets, the sync plan. Chapter 11. |
52| [cli/](cli/) | `postjevsql-sidecar`, the binary that talks to Postgres: `converge`, `sync`, `serve`. Chapter 12. |
53| [BUCK](BUCK) | The library, `:cli`, and `:test`, the library's unit tests. |
54| [Cargo.toml](Cargo.toml) | Generated by `tools/cargo-gen` (chapter 20). |
55
56The design and its invariants are the root `CLAUDE.md`'s *Deployment*
57section.
58
59← Previous: [Chapter 9, postjevsql-core/src/](../postjevsql-core/src/) · Up: [crates](../) · Next: [Chapter 11, postjevsql-sidecar/src/](src/) →