@spree/docs 0.1.257 → 0.1.259

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -195,6 +195,17 @@ spree api-key create --name "Admin" --type secret --scopes read_all # Admin
195
195
  spree api-key revoke <id> # Revoke a key
196
196
  ```
197
197
 
198
+ ### `spree encryption init`
199
+
200
+ Generate [Active Record encryption](../deployment/environment_variables.md#active-record-encryption) keys — Spree encrypts webhook signing secrets, payment gateway customer IDs and OAuth tokens with them. New projects already have keys; use this for projects created before they were generated at setup.
201
+
202
+ ```bash
203
+ spree encryption init # Add ACTIVE_RECORD_ENCRYPTION_* keys to the project's .env
204
+ spree encryption init --print # Print a fresh set without writing anything (e.g. for your hosting provider)
205
+ ```
206
+
207
+ It never overwrites keys `.env` already sets, and there is no `--force`: changing the keys makes data encrypted with them unreadable. Afterwards, recreate the containers so they load the new `.env` (`spree update`, or `spree dev` for an ejected project — `spree restart` keeps the old environment), and back the keys up in your secret manager.
208
+
198
209
  ### `spree api`
199
210
 
200
211
  Call the Admin API with generic `get`/`post`/`patch`/`delete` commands — zero-config inside a project (a read-only key is minted on first use), env vars or profiles for remote stores. See [Admin API from the CLI](admin-api.md) for the full guide.
@@ -637,7 +637,7 @@ Maps a Spree customer to their provider-specific customer profile. This enables
637
637
  | `payment_method_id` | The gateway this customer belongs to | `1` |
638
638
  | `customer_id` | The Spree customer | `42` |
639
639
 
640
- A customer has at most one record per payment method, and `profile_id` is encrypted with Active Record Encryption where it is configured.
640
+ A customer has at most one record per payment method, and `profile_id` is encrypted with [Active Record encryption](../deployment/environment_variables.md#active-record-encryption) where it is configured.
641
641
 
642
642
  > **NOTE:** This one is internal — the API does not expose it. It is listed here because
643
643
  > gateway integrations rely on it; nothing a storefront does will read it.
@@ -63,7 +63,7 @@ my-store/
63
63
  │ ├── seller-dashboard/ # Seller panel, for marketplaces (React)
64
64
  │ └── storefront/ # Next.js storefront (unless --no-storefront)
65
65
  │ └── .env.local # API URL + publishable key
66
- ├── .env # SECRET_KEY_BASE, SPREE_PORT, SPREE_VERSION_TAG, SPREE_SAMPLE_DATA
66
+ ├── .env # SECRET_KEY_BASE, encryption keys, SPREE_PORT, SPREE_VERSION_TAG, SPREE_SAMPLE_DATA
67
67
  ├── .spree/ # CLI credentials — gitignored, created on first run
68
68
  ├── docker-compose.yml # Spree backend (prebuilt image) + Postgres + Meilisearch
69
69
  ├── docker-compose.dev.yml # Alternative: build from local server/
@@ -49,6 +49,11 @@ services:
49
49
  environment:
50
50
  DATABASE_URL: postgres://spree:YOUR_PASSWORD@your-db.abc123.eu-west-1.rds.amazonaws.com:5432/spree_production
51
51
  SECRET_KEY_BASE: generate-with-openssl-rand-hex-64
52
+ # Active Record encryption — generate with `spree encryption init --print`
53
+ # or `bin/rails db:encryption:init`; never change them once data is encrypted
54
+ ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: ...
55
+ ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: ...
56
+ ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: ...
52
57
  RAILS_HOST: store.example.com
53
58
  # Uploads to S3 (recommended) — see /developer/deployment/assets
54
59
  # AWS_REGION: eu-west-1
@@ -222,6 +222,9 @@ Create the following secrets in AWS Secrets Manager:
222
222
  |---|---|---|
223
223
  | `spree/database-url` | `DATABASE_URL` | PostgreSQL connection URL |
224
224
  | `spree/secret-key-base` | `SECRET_KEY_BASE` | Generate with `bin/rails secret` |
225
+ | `spree/ar-encryption-primary-key` | `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY` | [Active Record encryption](environment_variables.md#active-record-encryption) keys — generate all three with `bin/rails db:encryption:init` (or `spree encryption init --print`). Never change them once data is encrypted |
226
+ | `spree/ar-encryption-deterministic-key` | `ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY` | Generated together with the primary key |
227
+ | `spree/ar-encryption-key-derivation-salt` | `ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT` | Generated together with the primary key |
225
228
 
226
229
  Optional secrets for email delivery, file storage, and error tracking:
227
230
 
@@ -292,6 +295,18 @@ One task definition runs the whole app — the web server also runs background j
292
295
  {
293
296
  "name": "SECRET_KEY_BASE",
294
297
  "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/secret-key-base"
298
+ },
299
+ {
300
+ "name": "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY",
301
+ "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/ar-encryption-primary-key"
302
+ },
303
+ {
304
+ "name": "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY",
305
+ "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/ar-encryption-deterministic-key"
306
+ },
307
+ {
308
+ "name": "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT",
309
+ "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/ar-encryption-key-derivation-salt"
295
310
  }
296
311
  ],
297
312
  "logConfiguration": {
@@ -351,6 +366,18 @@ Switches to [split mode](quickstart.md#web-and-worker): background jobs move int
351
366
  {
352
367
  "name": "SECRET_KEY_BASE",
353
368
  "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/secret-key-base"
369
+ },
370
+ {
371
+ "name": "ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY",
372
+ "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/ar-encryption-primary-key"
373
+ },
374
+ {
375
+ "name": "ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY",
376
+ "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/ar-encryption-deterministic-key"
377
+ },
378
+ {
379
+ "name": "ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT",
380
+ "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/ar-encryption-key-derivation-salt"
354
381
  }
355
382
  ],
356
383
  "logConfiguration": {
@@ -54,6 +54,11 @@ services:
54
54
  environment:
55
55
  DATABASE_URL: postgres://postgres@postgres:5432/spree_production
56
56
  SECRET_KEY_BASE: change-me-to-a-real-secret
57
+ # Active Record encryption — generate with `spree encryption init --print`
58
+ # or `bin/rails db:encryption:init`; never change them once data is encrypted
59
+ ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: change-me
60
+ ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: change-me
61
+ ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: change-me
57
62
  RAILS_FORCE_SSL: "false"
58
63
  RAILS_ASSUME_SSL: "false"
59
64
  # Public host used in generated URLs (images/attachments in API
@@ -93,6 +98,10 @@ Background jobs run inside the web container by default. When job load deserves
93
98
  environment:
94
99
  DATABASE_URL: postgres://postgres@postgres:5432/spree_production
95
100
  SECRET_KEY_BASE: change-me-to-a-real-secret
101
+ # the same encryption keys as web
102
+ ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: change-me
103
+ ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: change-me
104
+ ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: change-me
96
105
  RAILS_HOST: localhost:3000
97
106
  command: bin/jobs
98
107
  ```
@@ -103,6 +112,7 @@ Background jobs run inside the web container by default. When job load deserves
103
112
  | --- | --- | --- |
104
113
  | `DATABASE_URL` | PostgreSQL connection URL | `postgres://user:pass@host:5432/spree` |
105
114
  | `SECRET_KEY_BASE` | Secret key for session encryption | Generate with `bin/rails secret` |
115
+ | `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY`, `ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY`, `ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT` | Keys Spree encrypts secrets at rest with — see [Active Record encryption](environment_variables.md#active-record-encryption). Never change them once data is encrypted | Generate with `spree encryption init --print` or `bin/rails db:encryption:init` |
106
116
  | `RAILS_HOST` | Public host used in generated URLs — image/attachment URLs in API responses, email links | `store.example.com` |
107
117
  | `SOLID_QUEUE_IN_PUMA` | Run background jobs inside the web container (default `true`); set `false` when running a dedicated worker | `true` |
108
118
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Environment Variables
3
- description: Reference for Spree deployment environment variables — database, web server, background jobs, SMTP, file storage, and application settings.
3
+ description: Reference for Spree deployment environment variables — database, secrets and encryption keys, web server, background jobs, SMTP, file storage, and application settings.
4
4
  ---
5
5
 
6
6
  Spree uses environment variables for all deployment configuration. No secrets or credentials are stored in the codebase.
@@ -14,7 +14,58 @@ These two variables are all a production deployment strictly needs:
14
14
  | `DATABASE_URL` | Database connection URL — see [Database Configuration](database.md) | `postgres://user:pass@localhost:5432/spree` |
15
15
  | `SECRET_KEY_BASE` | Secret used to encrypt sessions and cookies. Generate with `openssl rand -hex 64` | `2fad5c0b79d25e4765d3018d8c740f8c3a665f0e5c...` |
16
16
 
17
- You'll almost always want to set [`RAILS_HOST`](#urls-and-hosts) too — without it, generated URLs point at `localhost`.
17
+ You'll almost always want to set [`RAILS_HOST`](#urls-and-hosts) too — without it, generated URLs point at `localhost`, and the three [Active Record encryption](#active-record-encryption) keys — without them, secrets Spree stores are kept in plain text.
18
+
19
+ ## Active Record encryption
20
+
21
+ Spree encrypts sensitive values at rest with [Active Record encryption](https://guides.rubyonrails.org/active_record_encryption.html): webhook endpoint signing secrets, payment gateway customer IDs, and the OAuth tokens stored on user identities. It does so only when encryption keys are configured — without them, these values are stored in plain text and a production app logs a warning at boot.
22
+
23
+ | Variable | Description |
24
+ | --- | --- |
25
+ | `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY` | Key for (non-deterministic) encrypted attributes |
26
+ | `ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY` | Key for deterministic encrypted attributes — the ones Spree looks records up by, such as webhook signing secrets and gateway customer IDs |
27
+ | `ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT` | Salt for deriving the encryption keys |
28
+
29
+ Set all three. Each is a random string — generate a set with any of:
30
+
31
+
32
+ ```bash Spree CLI
33
+ spree encryption init # writes a set to your project's .env (never overwrites existing keys)
34
+ spree encryption init --print # prints a fresh set, e.g. for your hosting provider
35
+ ```
36
+
37
+ ```bash Without Spree CLI
38
+ bin/rails db:encryption:init
39
+ ```
40
+
41
+
42
+ - **New projects** — `create-spree-app` generates the keys into `.env`, and the [Render Blueprint](render.md) generates them for production.
43
+ - **Production** — use a separate set of keys from development and store it in your secret manager as a backup.
44
+ - **Rails credentials** — instead of env vars, you can put the values printed by `bin/rails db:encryption:init` under `active_record_encryption` in your encrypted credentials. When both are present, the env vars win.
45
+
46
+ > **WARNING:** Never change or lose the keys once data is encrypted — records encrypted with them become unreadable. Rails supports [rotating the primary key](https://guides.rubyonrails.org/active_record_encryption.html#rotating-keys) by adding the new key and keeping the old one, but it doesn't support rotating the deterministic key — and webhook endpoint secrets and gateway customer IDs are encrypted deterministically. Keep `ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY` and `ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT` unchanged unless you plan a migration that re-encrypts those records.
47
+
48
+ ### Enabling encryption on an existing installation
49
+
50
+ Records written before the keys were set are stored in plain text. OAuth tokens on user identities stay readable and are encrypted on their next write. Webhook endpoint secrets and gateway customer IDs are not readable in plain text once encryption is on, so enable encryption with plaintext support first, then encrypt the existing rows:
51
+
52
+ 1. Allow reading and looking up plaintext rows in `config/application.rb`:
53
+
54
+ ```ruby
55
+ config.active_record.encryption.support_unencrypted_data = true
56
+ config.active_record.encryption.extend_queries = true
57
+ ```
58
+
59
+ 2. Set the three keys ([see above](#active-record-encryption)) and deploy.
60
+ 3. Encrypt the existing rows from a Rails console (`spree console` or `bin/rails console`):
61
+
62
+ ```ruby
63
+ [Spree::WebhookEndpoint, Spree::GatewayCustomer, Spree::UserIdentity].each { |model| model.find_each(&:encrypt) }
64
+ ```
65
+
66
+ 4. Remove the two settings from step 1 and deploy again.
67
+
68
+ Apps created from an older `spree-starter` also need `config/application.rb` to read the env vars — see the [5.6 to 6.0 upgrade guide](../upgrades/5.6-to-6.0.md#enable-active-record-encryption).
18
69
 
19
70
  ## URLs and Hosts
20
71
 
@@ -37,7 +37,7 @@ The `/jobs` dashboard uses HTTP Basic auth: user `jobs`, with a password the Blu
37
37
 
38
38
  ### Environment Variables
39
39
 
40
- Render sets `DATABASE_URL` and `SECRET_KEY_BASE` automatically from the Blueprint. For additional configuration (SMTP, file storage, Sentry, etc.), see [Environment Variables](environment_variables.md).
40
+ Render sets `DATABASE_URL`, `SECRET_KEY_BASE` and the three [Active Record encryption](environment_variables.md#active-record-encryption) keys automatically from the Blueprint. Copy the encryption keys from the **Environment** tab into your secret manager as a backup — never change them once data is encrypted. For additional configuration (SMTP, file storage, Sentry, etc.), see [Environment Variables](environment_variables.md).
41
41
 
42
42
  Generated URLs (images and attachments in API responses, email links) automatically use Render's `RENDER_EXTERNAL_HOSTNAME` (`<your-app-name>.onrender.com`). When you attach a custom domain, set [`RAILS_HOST`](environment_variables.md#urls-and-hosts) to it — it takes precedence.
43
43
 
@@ -141,6 +141,8 @@ end
141
141
 
142
142
  `find_or_create_user_from_oauth` returns the **user**, not the identity. It creates the `Spree::UserIdentity` row on first login (mapping `provider + uid → user`) and reuses it on subsequent logins — so repeat sign-ins land on the same Spree customer.
143
143
 
144
+ > **NOTE:** Any `access_token` and `refresh_token` you pass in `tokens` are stored on the identity and encrypted at rest with Active Record encryption. Configure the encryption keys in production (`bin/rails db:encryption:init`, then store the output in your encrypted credentials). Without them, Spree stores the tokens in plain text.
145
+
144
146
  ## Step 2: Register the Strategy
145
147
 
146
148
  Add the strategy to a registry in `config/initializers/spree.rb`. **This is the only line that decides which API the provider serves** — `store_authentication_strategies` for customer login, `admin_authentication_strategies` for staff login. Register with both to allow the same provider on either surface. The key you choose here is what clients send as `provider` in the login payload.
@@ -182,7 +184,7 @@ The strategy is instantiated with the surface's user class automatically — `Sp
182
184
 
183
185
  > **TIP:** Restrict a surface to SSO by removing the built-in email/password strategy after adding yours: `Spree.admin_authentication_strategies.remove(:email)` locks staff to your provider; the store side stays on email/password.
184
186
 
185
- > **WARNING:** `Spree::UserIdentity` validates that `provider` is a registered strategy key. Registration must happen during boot — before the first login attempt.
187
+ > **WARNING:** `Spree::UserIdentity` validates that `provider` is a key registered in the store, admin or seller strategy registry. Registration must happen during boot — before the first login attempt.
186
188
 
187
189
  ## Step 3: Call the Exchange Endpoint
188
190
 
@@ -63,6 +63,7 @@ Spree API is built on **Ruby on Rails** which provides strong security defaults
63
63
  We recommend:
64
64
 
65
65
  - Keeping Spree and all dependencies up to date
66
+ - Configuring [Active Record encryption](../deployment/environment_variables.md#active-record-encryption) keys in production — without them, webhook signing secrets, payment gateway customer IDs and OAuth tokens are stored in plain text
66
67
  - Following the [Rails Security Guide](https://guides.rubyonrails.org/security.html)
67
68
  - Using [bundler-audit](https://github.com/rubysec/bundler-audit) to scan for known vulnerabilities in dependencies
68
69
  - Running [brakeman](https://github.com/presidentbeef/brakeman) for static security analysis
@@ -178,6 +178,41 @@ bundle exec rake spree:upgrade:migrate_country_state_codes
178
178
 
179
179
  Countries and states are no longer database records in 6.0. Addresses, delivery zone members, market countries, stock locations and stores now name them by ISO code (`country_code`, `state_code`), and this task fills those columns from the old `country_id` / `state_id` references. The old `country_iso` / `state_abbr` names still work on addresses for one release.
180
180
 
181
+ ## Enable Active Record encryption
182
+
183
+ Spree encrypts webhook endpoint signing secrets, payment gateway customer IDs and the OAuth tokens on user identities with [Active Record encryption](https://guides.rubyonrails.org/active_record_encryption.html) — but only when encryption keys are configured. Without keys these values are stored in plain text. New 6.0 projects get keys at setup; upgraded applications need to add them.
184
+
185
+ **1. Make the app read the keys.** Projects created from `spree-starter` read them from the `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY`, `ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY` and `ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT` env vars, falling back to the `active_record_encryption` entry in Rails credentials. If your `config/application.rb` predates this, add inside the `Application` class:
186
+
187
+ ```ruby config/application.rb
188
+ %i[primary_key deterministic_key key_derivation_salt].each do |key|
189
+ value = ENV["ACTIVE_RECORD_ENCRYPTION_#{key.upcase}"].presence ||
190
+ credentials.dig(:active_record_encryption, key).presence
191
+ config.active_record.encryption[key] = value if value
192
+ end
193
+ ```
194
+
195
+ Setting the keys in `config.active_record.encryption` (rather than only in credentials) matters: Spree checks that configuration when it decides whether to encrypt webhook secrets and gateway customer IDs.
196
+
197
+ **2. Generate keys for development and production** — a separate set for each environment:
198
+
199
+
200
+ ```bash Spree CLI (Docker)
201
+ spree encryption init # adds a set to your project's .env (never overwrites existing keys)
202
+ spree encryption init --print # prints a set for your production host
203
+ ```
204
+
205
+ ```bash Without Spree CLI
206
+ bin/rails db:encryption:init # prints a set; put it in env vars or encrypted credentials
207
+ ```
208
+
209
+
210
+ Recreate the containers so they pick up the new `.env` (`spree update`, or `spree dev` for an ejected project — `spree restart` keeps the old environment). Set the keys on your production host **before** deploying, and store them in your secret manager.
211
+
212
+ > **WARNING:** Never change or lose the keys once data is encrypted — the encrypted records become unreadable.
213
+
214
+ **3. Encrypt existing rows.** Rows written before the keys were set are plain text. OAuth tokens on user identities stay readable and are encrypted on their next write. Webhook endpoint secrets and gateway customer IDs can't be read in plain text once encryption is on — follow [Enabling encryption on an existing installation](../deployment/environment_variables.md#enabling-encryption-on-an-existing-installation): turn on `support_unencrypted_data` and `extend_queries`, deploy with the keys, run `find_each(&:encrypt)` over `Spree::WebhookEndpoint`, `Spree::GatewayCustomer` and `Spree::UserIdentity`, then turn the two settings off again.
215
+
181
216
  ## The Cart/Order split
182
217
 
183
218
  The single biggest change. What used to be one `Spree::Order` living through checkout and beyond is now two models:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spree/docs",
3
- "version": "0.1.257",
3
+ "version": "0.1.259",
4
4
  "description": "Spree Commerce developer documentation for AI agents and local reference",
5
5
  "type": "module",
6
6
  "license": "CC-BY-4.0",