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

# Compatibility

> Configure compatibility editions and bucket storage format version in PowerSync's sync config.

To ensure consistency, it is important that the PowerSync Service does not interpret the same source row in different ways after updating to a new version.
At the same time, we want to fix bugs or other inaccuracies that have accumulated during the development of the Service.

## Overview

To make this trade‑off explicit, you choose whether to keep the existing behavior or turn on newer fixes that slightly change how data is processed.

Use the `config` block in your sync config YAML to choose the behavior. There are two ways to turn fixes on:

1. Set an `edition` to enable the full set of fixes for that edition. This is the recommended approach for new projects.
2. Toggle individual options for more fine‑grained control.

For older projects, the previous behavior remains the default. New projects should enable all current fixes.

### Configuration

For new projects, it is recommended to enable all current fixes by setting `edition: <edition>`:

```yaml theme={null}
config:
  edition: 3 # Recommended to set to the latest available edition (see 'Supported fixes' table below)

streams:
  # ...
```

Or, specify options individually:

```yaml theme={null}
config:
  timestamps_iso8601: true
  versioned_bucket_ids: true
  fixed_json_extract: true
  custom_postgres_types: true
```

## Sync Streams Requirement

**New Sync Streams configurations should use `edition: 3`**, which enables the new compiler with an expanded SQL feature set (including `JOIN`, CTEs, multiple queries per stream, `BETWEEN`, `CASE`, and more):

```yaml theme={null}
config:
  edition: 3

streams:
  my_stream:
    query: SELECT * FROM my_table WHERE user_id = auth.user_id()
```

<Note>
  **Upgrading from alpha**: If you have existing Sync Streams using `edition: 2`, upgrade to `edition: 3` to enable the new compiler with an expanded SQL feature set (including `JOIN`, CTEs, multiple queries per stream, `BETWEEN`, `CASE`, and more). See [Supported SQL](/sync/supported-sql) for the full list of supported features.
</Note>

## Storage Version

The PowerSync Service stores replicated bucket data in [bucket storage](/architecture/powersync-service#bucket-storage). That data uses a storage version that can evolve when you deploy new Sync Streams or Sync Rules. This versioning approach avoids large upfront migrations on existing bucket data when the Service introduces bigger storage changes. Each time your sync config is deployed and processed, the bucket data written for that deployment uses a specific storage version.

### Optional `config.storage_version`

You can pin the bucket storage version by setting it under the `config` block:

```yaml theme={null}
config:
  edition: 3
  storage_version: 2 # version 2 (stable) and 3 (unstable) are currently supported

streams:
  todos:
    query: SELECT * FROM todos WHERE owner_id = auth.user_id()
```

### When to Set `storage_version` Explicitly

In most deployments you can omit `storage_version`. The PowerSync Service then uses the latest stable storage version it supports. You should only set this field if you need more control, e.g.:

1. Service downgrade: If you need to run an older Service version that only supports up to a given storage version, deploy sync config with that `storage_version`, wait until reprocessing for that deployment has finished, then downgrade the Service.
2. Experiments: Opt into an odd, unstable storage version in non-production environments.
3. Delaying a storage upgrade: Change other sync config while keeping bucket data on an older stable storage version until you are ready for the newer format.

### Stable and Experimental Versions

The service distinguishes stable and experimental storage versions as follows:

* Even numbers (for example `2`, `4`) denote stable formats. Once a stable version is supported, newer Service releases are expected to keep supporting it until it is officially deprecated.
* Odd numbers (for example `3`) denote unstable formats. The layout may change without notice and support may be removed in a future release. Use odd versions only for testing, not production.

## Supported Fixes

This table lists all fixes currently supported:

| Name                    | Explanation                     | Added in Service version | Fixed in edition |
| ----------------------- | ------------------------------- | ------------------------ | ---------------- |
| `timestamps_iso8601`    | [Link](#timestamps-iso8601)     | 1.15.0                   | 2                |
| `versioned_bucket_ids`  | [Link](#versioned-bucket-ids)   | 1.15.0                   | 2                |
| `fixed_json_extract`    | [Link](#fixed-json-extract)     | 1.15.0                   | 2                |
| `custom_postgres_types` | [Link](#custom-postgres-types). | 1.15.3                   | 2                |

### `timestamps_iso8601`

PowerSync is supposed to encode timestamps according to the ISO-8601 standard.
Without this fix, the service encoded timestamps from MongoDB and Postgres source databases incorrectly.
To ensure time values from Postgres compare lexicographically, they're also padded to six digits of accuracy when encoded.
Since MongoDB only stores values with an accuracy of milliseconds, only three digits of accuracy are used.

For instance, the value `2025-09-22T14:29:30` would be encoded as follows:

* For Postgres: `2025-09-22 14:29:30` without the fix, `2025-09-22T14:29:30.000000` with the fix applied.
* For MongoDB: `2025-09-22 14:29:30.000` without the fix, `2025-09-22T14:29:30.000` with the fix applied.

Note that MySQL has never been affected by this issue, and thus behaves the same regardless of the option used.

#### Configurable sub-second datetime precision

When the `timestamps_iso8601` option is enabled, PowerSync will sync date and time values with a higher
precision depending on the source database.
You can use the `timestamp_max_precision` option to configure the actual precision to use.
For instance, a Postgres timestamp value would sync as `2025-09-22T14:29:30.000000` by default.
If you don't want that level of precision, you can use the following options to make it sync as `2025-09-22T14:29:30.000`:

```yaml sync-config.yaml theme={null}
config:
  edition: 3
  timestamp_max_precision: milliseconds
```

Valid options for `timestamp_max_precision` are `seconds`, `milliseconds`, `microseconds` and `nanoseconds`. When an explicit
value is given, all synced time values will use that precision.
If a source value has a higher precision, it will be truncated (it is not rounded).
If a source value has a lower precision, it will be padded (so setting the option to `microseconds` with a MongoDB source database
will sync values as `2025-09-22T14:29:30.123000`, with the last three sub-second digits always being set to zero).

If no option is given, the default precision depends on the source database:

| Source database | Default precision | Max precision | Notes                                                                                                   |
| --------------- | ----------------- | ------------- | ------------------------------------------------------------------------------------------------------- |
| MongoDB         | Milliseconds      | Milliseconds  |                                                                                                         |
| Postgres        | Microseconds      | Microseconds  |                                                                                                         |
| MySQL           | Milliseconds      | Microseconds  | Defaults to milliseconds, but can be expanded with the option.                                          |
| SQL Server      | Nanoseconds       | Nanoseconds   | SQL Server supports 7 digits of accuracy, the sync service pads values to always use 9 for nanoseconds. |

### `versioned_bucket_ids`

Sync Rules define buckets, which rows to sync are then assigned to. When you run a full defragmentation or
redeploy Sync Rules, the same bucket identifiers are re-used when processing data again.

Because the second iteration uses different checksums for the same bucket ids, clients may sync data
twice before realizing that something is off and starting from scratch.

Applying this fix improves client-side progress estimation and is more efficient, since data would not get
downloaded twice.

For how bucket identifiers are represented in bucket storage at the persistence layer (including automatic use of versioned bucket names with newer storage formats), see [Storage version](#storage-version).

### `fixed_json_extract`

This fixes the `json_extract` functions as well as the `->` and `->>` operators in Sync Rules to behave similar
to recent SQLite versions: We only split on `.` if the path starts with `$.`.

For instance, `'json_extract({"foo.bar": "baz"}', 'foo.bar')` would evaluate to:

1. `baz` with the option enabled.
2. `null` with the option disabled.

### `custom_postgres_types`

If you have custom Postgres types in your backend source database schema, older versions of the PowerSync Service
would not recognize these values and sync them with the textual wire representation used by Postgres.
This is especially noticeable when defining `DOMAIN` types with e.g. a `REAL` inner type: The wrapped
`DOMAIN` type should get synced as a real value as well, but it would actually get synced as a string.

With this fix applied:

* `DOMAIN TYPE`s are synced as their inner type.
* Array types of custom types get parsed correctly, and sync as a JSON array.
* Custom types get parsed and synced as a JSON object containing their members.
* Ranges sync as a JSON object corresponding to the following TypeScript definition:
  ```TypeScript theme={null}
  export type Range<T> =
    | {
        lower: T | null;
        upper: T | null;
        lower_exclusive: boolean;
        upper_exclusive: boolean;
        }
    | 'empty';
  ```
* Multi-ranges sync as an array of ranges.
