# Business rules — datafy_powersync

Evidence date: 2026-10-17. Revision: `main` `0aa51f9` (2025-02-25). Full SHA `0aa51f98ff6068260c0a96ccac1d1f47310e769e`.

Rules below are the SQL, flags, and compose settings in this repository. Anything inferred is marked **Inferred**. Domain rules that live only in `flutter_app`, `flutter_webportal`, or `datafy-supabase-functions` are not copied in as rules of this service.

No file here says which compose stack is production.

## Who may connect

| Rule | Statement | Provenance |
|---|---|---|
| BR-001 | Client auth is turned on with `client_auth.supabase: true` in `config/powersync.yaml`, `config.yaml`, every `v2/` service YAML, and `demos/supabase/config/powersync.yaml`. | **Implemented** |
| BR-002 | Accepted JWT audiences are `powersync-dev` and `powersync`. | **Implemented** list on each of those files |
| BR-003 | The comment "Sync all tasks for admin view or similar" does not check a role. Bucket `global` has no `parameters` and no `WHERE` on role. | **Documented** comment. **Implemented** SQL has no admin gate |
| BR-004 | `block_local_jwks` is commented out. The comment says ranges are not blocked by default and that production should enable the block. | **Documented** recommendation. **Implemented** as off, because the key is commented |
| BR-005 | A JWKS URI is not set. `config.yaml` and the `v2/` files contain `#jwks_uri: [TODO]`. | **Implemented** by absence |
| BR-006 | The Node demo identifies the user as whatever UUID is in `localStorage` key `ps_user_id`, and the demo README says the token is not bound to a real user and that all data syncs to all users. | **Implemented** client. **Documented** README. This is the demo, not BR-001 |
| BR-007 | Who may clone `overdrive/datafy_powersync`, and who may open port 8080 or 27017, is not defined in the repo. | **Unknown / Requires Confirmation** |

## What a client receives

The SQL below is the text in `config/sync_rules.yaml`. The same statements are inlined or copied as listed in `features.md`.

| Rule | Statement | Provenance |
|---|---|---|
| BR-008 | Bucket `global` syncs `SELECT * FROM task`, `SELECT * FROM store`, `SELECT * FROM app_user`, and `SELECT * FROM store_user_link`, with no user predicate. | **Implemented** |
| BR-009 | Bucket `user_tasks` takes each `store_code` from `store_user_link` where `user_id = request.user_id()`, then syncs `task` rows with that `store_code`. | **Implemented** |
| BR-010 | Bucket `user_stores` uses the same parameter query and syncs `store` rows with that `store_code`. | **Implemented** |
| BR-011 | `user_tasks` and `user_stores` do not filter `store_user_link.active`, `customer_id`, or a date. | **Implemented** by absence. `flutter_app` documents `active = 1` for its own `user_stores` bucket. That predicate is not in this file |
| BR-012 | `app_user` and `store_user_link` are not repeated in a per-user bucket. They appear only in `global`. | **Implemented** |
| BR-013 | `task` and `store` appear both unfiltered (`global`) and filtered (user buckets). | **Implemented**. **Inferred**: the user buckets do not remove rows already included by `global` |
| BR-014 | `request.user_id()` is the only identity function used. The file header points at `https://docs.powersync.com/usage/sync-rules`. | **Implemented** call. Which JWT claim becomes `user_id` is **Unknown** in this repo (`supabase: true` is the only auth mode selected) |
| BR-015 | Column lists are `SELECT *`. A new column on the source table is not excluded by these rules. | **Implemented** |

## Source database assumptions

| Rule | Statement | Provenance |
|---|---|---|
| BR-016 | The rules assume tables `task`, `store`, `app_user`, and `store_user_link` exist and that `store_user_link` has `user_id` and `store_code`, and that `task` and `store` have `store_code`. | **Inferred** from the SQL. No DDL in this repo confirms the columns |
| BR-017 | No publication for those four tables is created here. The Node demo publication covers `lists` and `todos` only. | **Implemented** by absence, and **Implemented** demo SQL |
| BR-018 | `config/powersync.yaml` allows one replication connection. | **Documented** in that file's comment and **Implemented** as a single list item |
| BR-019 | Every Postgres connection in git sets `sslmode: disable`, including the RDS host under `v2/`. | **Implemented**. The comment in `config/powersync.yaml` says disable is not for public networks |
| BR-020 | Root stack Postgres host, port, database, and user come from the environment. `example.env` uses host `postgres`, port `5432`, database `postgres`, user `postgres`. | **Implemented** template. The password in that file is a local placeholder, not the RDS password |
| BR-021 | `v2/` replication user is `supabase_admin`, database `postgres`, port `5432`, host `datafy-supabase-databasecluster.cx6ggccgkh70.eu-west-1.rds.amazonaws.com`. | **Implemented**. The password is committed and is omitted here |

## Bucket storage

| Rule | Statement | Provenance |
|---|---|---|
| BR-022 | The root stack stores buckets in MongoDB database `powersync_datafy` on replica set `rs0`. | **Implemented** in `docker-compose.yaml` and `config/powersync.yaml` |
| BR-023 | `v2/` stores buckets in Postgres (`storage.type: postgresql`), not Mongo. The Mongo URI in those files is commented and uses database name `powersync_demo`. | **Implemented** |
| BR-024 | `config.yaml` (repo root) stores buckets in Mongo `powersync_datafy` and replicates from `postgresql://postgres:postgres@postgres:5432/postgres`. | **Implemented** file. Not referenced by the current root compose |
| BR-025 | Mongo in the URIs has no username or password. | **Implemented** |
| BR-026 | Host port 27017 is published in both Mongo compose files. | **Implemented** |

## Demo data rules (not the Datafy SQL)

These apply only to `demos/nodejs/init-scripts/setup.sql` and the demo client.

| Rule | Statement | Provenance |
|---|---|---|
| BR-027 | `lists.id` and `todos.id` default to `gen_random_uuid()`. `lists.name` and `todos.description` are `NOT NULL`. `todos.completed` defaults to false. `todos.list_id` is `NOT NULL`. | **Implemented** DDL |
| BR-028 | Seed data is one list `Getting Started` with a fixed id `75f89104-d95a-4f16-8309-5363f1bb377a`, plus two todos. The list `owner_id` is `gen_random_uuid()` at insert time. | **Implemented** |
| BR-029 | The demo README says `setup.sql` runs only on first volume initialisation. | **Documented** |
| BR-030 | The client schema indexes `todos.list_id` and treats `completed` as an integer column. The SQL column is `boolean`. | **Implemented** in `AppSchema.ts` and in `setup.sql`. Whether PowerSync coerces that pair is **Unknown** |
| BR-031 | Writes from the demo client are a batch of `{op, table, id, data}` posted to `/api/data`. | **Implemented** |
| BR-032 | With checkpoint mode `managed` (the compose build arg), the client completes the transaction without fetching a custom checkpoint. | **Implemented** in `DemoConnector.ts` and `ps-nodejs-demo.yaml` |

The Supabase README's rule "create a todo to leave the busy-with-sync state" is **Documented**. The sync file beside that demo does not query a todo table (BR-008).

## Process and lifecycle rules

| Rule | Statement | Provenance |
|---|---|---|
| BR-033 | The selected start command is unified mode. Migrations run automatically unless `disable_auto_migration` is set. That flag is commented out, so the default stands. | **Documented** in comments. **Implemented** command does not disable migrations |
| BR-034 | Demo PowerSync restarts `unless-stopped` and waits until Mongo init has succeeded. The Node demo also waits until `pg-db` is healthy. | **Implemented** in `services/powersync.yaml` and `demos/nodejs/docker-compose.yaml` |
| BR-035 | Root PowerSync has no `restart` policy and does not wait for `mongo-rs-init`. | **Implemented** |
| BR-036 | `services/powersync.yaml` limits Node old space to 1000 (comment: about 80 percent of system memory). The root compose file does not set `NODE_OPTIONS`. | **Implemented** on the shared service only. The "80 percent" figure is a **Documented** comment, not a calculated value |
| BR-037 | Liveness, where configured, is HTTP GET `/probes/liveness` on `PS_PORT`. `old.txt` uses `/health` on port 8080. The root compose file defines neither. | **Implemented** difference |
| BR-038 | Telemetry sharing is not disabled in `config/powersync.yaml`. | **Implemented** `false` on `disable_telemetry_sharing` |
| BR-039 | `v2` PowerSync depends on local Postgres being healthy and does not start the commented Mongo init. | **Implemented** |

## Conflicts between files (not a chosen business rule)

These are disagreements in git. They are not resolved by a comment that names a winner.

| Rule | Statement | Provenance |
|---|---|---|
| BR-040 | Root compose injects `PS_PG_URI`. `services/powersync.yaml` injects `PS_DATA_SOURCE_URI`. `config/powersync.yaml` reads `PS_PG_URI`. `demos/supabase/config/powersync.yaml` reads `PS_DATA_SOURCE_URI`. | **Implemented** |
| BR-041 | Root `PS_PG_URI` omits `$` before `{PG_DATABASE_HOST}`. | **Implemented** |
| BR-042 | Root `powersync` sets `network_mode: "bridge"` while `PS_MONGO_URI` uses hostname `mongo`. | **Implemented**. **Inferred** network split. See `prd.md` FR-006 |
| BR-043 | `v2` bucket URIs use port 5431. The Postgres container listens on 5432 and publishes 5431 on the host. | **Implemented**. **Inferred** mismatch from inside the PowerSync container |
| BR-044 | Node demo tables are `lists` and `todos`. The config directory that service mounts syncs the four Datafy tables. | **Implemented** |
| BR-045 | `Mackup.yml` is a Compose file. Nothing in the repo explains the filename. | **Unknown** intent. **Implemented** contents |

## Security-sensitive facts treated as rules of the tree

| Rule | Statement | Provenance |
|---|---|---|
| BR-046 | A Postgres password for `supabase_admin` on the RDS host is stored in four YAML files under `v2/` and therefore in git history. | **Implemented**. The secret is not repeated in Silicon Ark. Rotation status is **Unknown / Requires Confirmation** |
| BR-047 | `v2/docker-compose.yaml` sets the local bucket-store password to `postgres`. | **Implemented** |
| BR-048 | `example.env` is tracked. `.gitignore` does not exclude `.env`. | **Implemented** |
| BR-049 | Mongo and PowerSync ports are published on the host (`27017`, `${PS_PORT}` / `8080`). No compose file binds them to `127.0.0.1`. | **Implemented** |
