@supacloud/lite 0.33.2 → 0.34.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.34.0](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.33.2...supacloud-lite-v0.34.0) (2026-10-26)
4
+
5
+
6
+ ### Features
7
+
8
+ * **lite:** add authorized realtime HTTP broadcast ([#2250](https://github.com/vibeunion/supacloud/issues/2250)) ([405747c](https://github.com/vibeunion/supacloud/commit/405747cce8f055c371e8f76c38a862aaf480f94c))
9
+ * **lite:** add production readiness doctor checks ([#2249](https://github.com/vibeunion/supacloud/issues/2249)) ([d9f09f2](https://github.com/vibeunion/supacloud/commit/d9f09f2ff797898af9a68d2c261b9d66e0eea001))
10
+ * **lite:** add restricted same-origin external auth proxy ([#2247](https://github.com/vibeunion/supacloud/issues/2247)) ([10e420e](https://github.com/vibeunion/supacloud/commit/10e420e700f206a8f190e6d51622c980d867fc68))
11
+ * **lite:** add storage status and metrics endpoints ([#2251](https://github.com/vibeunion/supacloud/issues/2251)) ([183b323](https://github.com/vibeunion/supacloud/commit/183b32397006a2b80d9961e7ae26d5779654b064))
12
+
13
+
14
+ ### Documentation
15
+
16
+ * **lite:** add single-node production deployment assets ([#2248](https://github.com/vibeunion/supacloud/issues/2248)) ([1e36554](https://github.com/vibeunion/supacloud/commit/1e36554cbbdbfecbb1e2a4974af560e8fa62124a))
17
+
3
18
  ## [0.33.2](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.33.1...supacloud-lite-v0.33.2) (2026-10-25)
4
19
 
5
20
 
package/README.md CHANGED
@@ -166,8 +166,23 @@ Lite 支持平铺 SQL 与 `时间戳目录/migration.sql`,执行前检查重
166
166
 
167
167
  `/graphql/v1` 使用真正的 `pg_graphql` 扩展和调用者的数据库角色/RLS,不模拟扩展。默认 PGlite 和标准 native 下载包均不包含该扩展;native 可通过 `--postgres-dir` 选择已经安装匹配扩展的 PostgreSQL。配置 `[lite.graphql] enabled = true` 会在运行时启动前验证扩展,`doctor --json` 区分已支持、缺失与尚未验证。
168
168
 
169
+ `doctor --production` 在能力报告之外检查单机生产阻断项和运维义务:strict 运行时、非内存存储、外部身份同源代理、公开地址、数据库/存储目录隔离、状态目录位置和快照新鲜度。阻断项使命令以非零退出码结束;警告项表示必须由部署方承担的运维责任(例如 S3 对象不在快照内、PGlite 容量验证、TLS 必须由反代终止)。
170
+
169
171
  外部 SupAuth 可通过 `createSupAuthLiteIdentity` 复用 `@supacloud/elysia` 的验证和业务侧本地用户映射;本地 Auth 仍是默认模式。外部模式不会把本地令牌降级当成 SupAuth,也不会根据请求头授予权限。
170
172
 
173
+ 如果浏览器应用与身份服务不同源,且身份服务没有为业务域名开放 CORS,可在 Lite 前面启用受限的同源 OAuth 反代:
174
+
175
+ ```sh
176
+ supacloud-lite start \
177
+ --external-auth-proxy-issuer https://auth.example.com/auth/v1 \
178
+ --external-auth-proxy-redirect-origin https://app.example.com
179
+ ```
180
+
181
+ 反代只接受 `/auth/v1/oauth/token`(POST)、`/auth/v1/oauth/userinfo`(GET)和
182
+ `/auth/v1/logout`(GET/POST),不会转发 cookie、`x-forwarded-*` 或任意上游地址。
183
+ 授权请求仍由浏览器顶层跳转到身份服务;应用端把 token/userinfo 指向 Lite 同源地址。
184
+ 嵌入宿主也可以直接调用 `createExternalAuthProxy()`。
185
+
171
186
  完整配置、边界和回归方法见仓库文档 `docs/lite-runtime-compatibility.md`。性能基线可在源码目录运行 `bun run benchmark`,native 模式加 `--native`。
172
187
 
173
188
  ### CLI
@@ -187,7 +202,7 @@ supacloud-lite snapshot create [-o backup.tar.gz]
187
202
  supacloud-lite snapshot restore <backup.tar.gz> [--force]
188
203
  supacloud-lite upgrade [-o pre-upgrade.tar.gz]
189
204
  supacloud-lite inspect
190
- supacloud-lite doctor [--json]
205
+ supacloud-lite doctor [--json] [--production]
191
206
  supacloud-lite version
192
207
  ```
193
208
 
@@ -374,6 +389,19 @@ S3 模式的快照只包含数据库中的 Storage 元数据和密钥,不复
374
389
 
375
390
  RLS 表的 Realtime DELETE 无法在行删除后安全重放 SELECT policy,因此 V1 只向 `service_role` 订阅者发送这类 DELETE 事件。普通用户仍可收到通过逐行 RLS 校验的 INSERT/UPDATE 事件。
376
391
 
392
+ Realtime 也支持服务端 HTTP 广播:
393
+
394
+ ```http
395
+ POST /realtime/v1/api/broadcast
396
+ apikey: <project-anon-key>
397
+ authorization: Bearer <access-token>
398
+ content-type: application/json
399
+
400
+ {"messages":[{"topic":"room:42","event":"notice","payload":{"text":"hello"},"private":false}]}
401
+ ```
402
+
403
+ 请求沿用 Realtime 的 `messages` 数组格式,单次最多 100 条,每条 `event` 最多 256 字节、`topic` 最多 256 字节、`payload` 最多 1 MiB。`private: true` 会按 `realtime.messages` 的 INSERT policy 校验调用者;整个批次先完成校验再发送,任一主题无写权限时返回 `403`,不会部分广播。成功返回 `202 {"message":"Broadcast sent"}`。该端点只做实时投递,不持久化消息;需要离线补偿时仍应使用数据库表或 outbox。
404
+
377
405
  ### Capability doctor 与 PowerSync profile
378
406
 
379
407
  `doctor` 输出 Lite 自己的稳定能力契约,不复制完整 Management API:
@@ -508,6 +536,10 @@ Lite 的缓存调用是进程内数据库操作,不具备标准版跨进程 HT
508
536
 
509
537
  V1 不在一个进程内复用多个 PGlite 项目。需要多个项目时,为每个项目配置独立的工作目录、端口和 `.supacloud-lite` 状态目录,并用进程管理器分别启动。这样可保持数据库、JWT、Storage 和 Realtime 的故障域隔离。
510
538
 
539
+ ### 生产部署
540
+
541
+ `deploy/` 提供单机生产部署模板:systemd 用户/目录/状态目录约定、Caddy TLS 反代、外部 SupAuth 同源代理、快照维护窗口、S3 对象单独备份和回滚步骤。Lite 不自带 TLS、HA 或控制面;必须保持单进程单状态目录,并由 Caddy/Nginx 终止 TLS。生产前至少应完成容量测试、恢复演练和未认证请求失败关闭验证。
542
+
511
543
  ### 资源边界
512
544
 
513
545
  PGlite 是 WebAssembly PostgreSQL,不是 SQLite。它换来了 PostgreSQL SQL、角色、RLS 和 Supabase 迁移兼容性,但内存占用通常高于 PocketBase/SQLite。生产部署前应按真实 schema、并发和 Realtime 负载做容量测试。
@@ -531,6 +563,8 @@ await running.close()
531
563
 
532
564
  图片变换目前提供 Bun.Image 的兼容子集:`width`、`height`、`resize=fill|contain`,以及 `format=origin|jpeg|png|webp` 和 JPEG/WebP `quality`。`resize=cover` 需要裁剪能力,当前 Bun 运行时没有对应 API,因此会明确返回不支持错误,不会错误地把图片拉伸成 `cover`。
533
565
 
566
+ Storage 还提供两个运维端点:`GET /storage/v1/status` 无需项目 API key,返回服务名称、版本和健康状态;`GET /storage/v1/metrics` 需要 `service_role`,以 Prometheus 文本格式输出 bucket 数、对象元数据行数和声明对象总字节数。指标只统计 Lite 自己的 `storage` schema,不代表远端 S3 bucket 的真实用量或后台清理延迟。
567
+
534
568
  也可以使用 `createLiteBackend()` 直接创建内存或自定义目录的嵌入式后端,并把它的 `fetch` 传给自定义宿主。
535
569
 
536
570
  ### 来源与许可
@@ -662,7 +696,7 @@ supacloud-lite snapshot create [-o backup.tar.gz]
662
696
  supacloud-lite snapshot restore <backup.tar.gz> [--force]
663
697
  supacloud-lite upgrade [-o pre-upgrade.tar.gz]
664
698
  supacloud-lite inspect
665
- supacloud-lite doctor [--json]
699
+ supacloud-lite doctor [--json] [--production]
666
700
  supacloud-lite version
667
701
  ```
668
702
 
@@ -853,6 +887,30 @@ GraphQL fails startup when missing; `doctor --json` reports actual or unverified
853
887
  capability. External SupAuth can reuse the shared Elysia verifier through
854
888
  `createSupAuthLiteIdentity` and an explicit application-owned local user mapping.
855
889
 
890
+ When the browser application and the identity service have different origins and
891
+ the identity service does not allow the application origin via CORS, Lite can
892
+ serve a restricted same-origin OAuth proxy:
893
+
894
+ ```sh
895
+ supacloud-lite start \
896
+ --external-auth-proxy-issuer https://auth.example.com/auth/v1 \
897
+ --external-auth-proxy-redirect-origin https://app.example.com
898
+ ```
899
+
900
+ The proxy accepts only `/auth/v1/oauth/token` (POST),
901
+ `/auth/v1/oauth/userinfo` (GET) and `/auth/v1/logout` (GET/POST). It never
902
+ forwards cookies, `x-forwarded-*`, or arbitrary upstream URLs. Authorization
903
+ remains a top-level navigation to the identity service; the application points
904
+ token/userinfo at the Lite same-origin address. Embedded hosts can call
905
+ `createExternalAuthProxy()` directly.
906
+
907
+ `doctor --production` adds single-node production policy checks: strict runtime,
908
+ non-memory storage, external identity proxying, public URLs, database/storage
909
+ path separation, state-directory location and snapshot freshness. Blockers exit
910
+ non-zero; warnings are operational obligations such as backing up S3 objects
911
+ outside the Lite snapshot, capacity-testing PGlite, and terminating TLS at the
912
+ proxy.
913
+
856
914
  See repository document `docs/lite-runtime-compatibility.md` for configuration
857
915
  and acceptance boundaries. Run `bun run benchmark` (or add `--native`) in the
858
916
  source package for a synthetic baseline, not a production performance claim.
@@ -880,6 +938,19 @@ source package for a synthetic baseline, not a production performance claim.
880
938
 
881
939
  Realtime DELETE on RLS tables cannot safely replay SELECT policies after a row is deleted, so V1 only sends such DELETE events to `service_role` subscribers. Regular users can still receive INSERT/UPDATE events that pass row-by-row RLS validation.
882
940
 
941
+ Realtime also exposes server-side HTTP broadcast:
942
+
943
+ ```http
944
+ POST /realtime/v1/api/broadcast
945
+ apikey: <project-anon-key>
946
+ authorization: Bearer <access-token>
947
+ content-type: application/json
948
+
949
+ {"messages":[{"topic":"room:42","event":"notice","payload":{"text":"hello"},"private":false}]}
950
+ ```
951
+
952
+ It uses Realtime's `messages` envelope: at most 100 messages per request, each `event` and `topic` at most 256 bytes, and each `payload` at most 1 MiB. `private: true` authorizes the caller through the `realtime.messages` INSERT policy. The whole batch is authorized before any delivery, so one denied topic returns `403` without partially broadcasting. Success returns `202 {"message":"Broadcast sent"}`. This endpoint delivers live events only; use a database table or outbox when messages must survive disconnects.
953
+
883
954
  ### Capability doctor and PowerSync profile
884
955
 
885
956
  `supacloud-lite doctor --json` reports a stable Lite capability contract without copying the full Management API. PGlite explicitly reports Logical Replication and PowerSync source support as `unsupported`; it never emulates WAL, publications, or slots. Multi-device offline deployments should use central SupaCloud + PowerSync, while a single field workstation can use Lite as a local project server or upload through an application-owned outbox.
@@ -998,6 +1069,16 @@ Lite's cache calls are in-process database operations and do not have the timeou
998
1069
 
999
1070
  V1 does not multiplex multiple PGlite projects within a single process. When multiple projects are needed, configure an independent working directory, port, and `.supacloud-lite` state directory for each project, and start them separately with a process manager. This keeps the database, JWT, Storage, and Realtime failure domains isolated.
1000
1071
 
1072
+ ### Production Deployment
1073
+
1074
+ The `deploy/` directory documents a single-node production contract: systemd
1075
+ service account/state directories, Caddy TLS termination, external SupAuth
1076
+ same-origin proxying, snapshot maintenance windows, separate S3 object backup,
1077
+ and rollback. Lite does not bundle TLS, HA, or a control plane. Keep one process
1078
+ per state directory and terminate TLS at Caddy/Nginx. Before production, run
1079
+ capacity tests, a restore drill, and an unauthenticated-request fail-closed
1080
+ check.
1081
+
1001
1082
  ### Resource Boundaries
1002
1083
 
1003
1084
  PGlite is WebAssembly PostgreSQL, not SQLite. It trades off for PostgreSQL SQL, roles, RLS, and Supabase migration compatibility, but its memory footprint is typically higher than PocketBase/SQLite. Before production deployment, you should perform capacity testing against the real schema, concurrency, and Realtime load.
@@ -1021,6 +1102,8 @@ await running.close()
1021
1102
 
1022
1103
  Image transforms currently provide a compatible subset of Bun.Image: `width`, `height`, `resize=fill|contain`, as well as `format=origin|jpeg|png|webp` and JPEG/WebP `quality`. `resize=cover` requires cropping capability, which the current Bun runtime does not have a corresponding API for, so it explicitly returns an unsupported error instead of incorrectly stretching the image to `cover`.
1023
1104
 
1105
+ Storage also exposes two operational endpoints: `GET /storage/v1/status` needs no project API key and returns the service name, version, and health; `GET /storage/v1/metrics` requires `service_role` and returns Prometheus text with bucket count, object metadata rows, and declared object bytes. These metrics cover Lite's `storage` schema only; they do not claim the real usage of a remote S3 bucket or background cleanup latency.
1106
+
1024
1107
  You can also use `createLiteBackend()` to directly create an embedded backend with an in-memory or custom directory, and pass its `fetch` to a custom host.
1025
1108
 
1026
1109
  ### Provenance and License
@@ -0,0 +1,110 @@
1
+ # SupaCloud Lite production deployment
2
+
3
+ This directory contains a minimal single-project deployment contract. It is not
4
+ a multi-tenant control plane. Run one Lite process per state directory and put
5
+ TLS plus a reverse proxy in front of it.
6
+
7
+ ## Layout
8
+
9
+ ```text
10
+ /opt/supacloud-lite/project reviewed migrations, seed and functions (read-only at runtime)
11
+ /var/lib/supacloud-lite database, storage and snapshots (persistent)
12
+ /etc/supacloud-lite/env secrets and runtime configuration (root:root 0600)
13
+ /usr/local/bin/supacloud-lite installed CLI / standalone binary
14
+ ```
15
+
16
+ The systemd unit sets `SUPACLOUD_LITE_STATE_DIR=/var/lib/supacloud-lite`, so the
17
+ project checkout does not contain mutable data.
18
+
19
+ ## Install
20
+
21
+ 1. Install the pinned Lite release and verify its version.
22
+ 2. Create a service account and directories:
23
+
24
+ ```sh
25
+ sudo useradd --system --home /var/lib/supacloud-lite --shell /usr/sbin/nologin supacloud-lite
26
+ sudo install -d -o supacloud-lite -g supacloud-lite -m 0750 /var/lib/supacloud-lite
27
+ sudo install -d -o root -g root -m 0755 /opt/supacloud-lite/project /etc/supacloud-lite
28
+ ```
29
+
30
+ 3. Put the reviewed project files under `/opt/supacloud-lite/project`.
31
+ 4. Create `/etc/supacloud-lite/env` from the project's deployment secret store.
32
+ Keep it `root:root` and `0600`. Supported values include `S3_*` credentials,
33
+ `SUPACLOUD_LITE_S3_PREFIX`, `SUPACLOUD_LITE_STORAGE_BACKEND=s3`, and the
34
+ trusted external identity module setting.
35
+ 5. Run migrations and create the first local account **before** starting the
36
+ service:
37
+
38
+ ```sh
39
+ sudo -u supacloud-lite env SUPACLOUD_LITE_STATE_DIR=/var/lib/supacloud-lite \
40
+ supacloud-lite migrate --project-dir /opt/supacloud-lite/project
41
+ sudo -u supacloud-lite env SUPACLOUD_LITE_STATE_DIR=/var/lib/supacloud-lite \
42
+ supacloud-lite auth create-user --project-dir /opt/supacloud-lite/project \
43
+ --email operator@example.com --password-stdin --email-confirm < /secure/password.txt
44
+ ```
45
+
46
+ 6. Install the unit and reload systemd:
47
+
48
+ ```sh
49
+ sudo install -m 0644 deploy/systemd/supacloud-lite.service /etc/systemd/system/
50
+ sudo systemctl daemon-reload
51
+ sudo systemctl enable --now supacloud-lite
52
+ curl -fsS http://127.0.0.1:54321/health
53
+ ```
54
+
55
+ 7. Install the Caddy site from `deploy/caddy/Caddyfile`, replace the hostname,
56
+ and verify HTTPS plus an authenticated API request.
57
+
58
+ ## External SupAuth
59
+
60
+ If the browser application and identity service are on different origins and
61
+ the identity service does not allow the application origin via CORS, use the
62
+ restricted same-origin proxy documented in the package README:
63
+
64
+ ```sh
65
+ supacloud-lite start \
66
+ --external-auth-proxy-issuer https://auth.example.com/auth/v1 \
67
+ --external-auth-proxy-redirect-origin https://app.example.com
68
+ ```
69
+
70
+ The proxy handles only OAuth token, userinfo and logout. Authorization remains a
71
+ top-level navigation. Membership and application permissions stay in the
72
+ application-owned data and RLS model; Lite does not grant them from token claims.
73
+
74
+ ## Backup, upgrade and restore
75
+
76
+ Lite snapshots require the process to be stopped. Use a maintenance window:
77
+
78
+ ```sh
79
+ sudo systemctl stop supacloud-lite
80
+ sudo -u supacloud-lite env SUPACLOUD_LITE_STATE_DIR=/var/lib/supacloud-lite \
81
+ supacloud-lite snapshot create \
82
+ --project-dir /opt/supacloud-lite/project \
83
+ -o /var/lib/supacloud-lite/backups/pre-upgrade.tar.gz
84
+ sudo systemctl start supacloud-lite
85
+ ```
86
+
87
+ For an upgrade, stop the service, install the pinned candidate, run
88
+ `supacloud-lite upgrade`, start the service, and verify `/health` and a real
89
+ authenticated read. If the upgrade fails, restore the pre-upgrade snapshot with
90
+ `snapshot restore --force` and restart the previous binary.
91
+
92
+ When using S3, snapshots contain database metadata and secrets but **not remote
93
+ objects**. Back up the bucket/prefix separately and restore it before starting
94
+ the service. `db reset` is intentionally rejected in S3 mode.
95
+
96
+ ## Production checks
97
+
98
+ Before exposing the service:
99
+
100
+ - keep the process bound to loopback and terminate TLS at the proxy;
101
+ - use `--runtime-mode strict` for functions;
102
+ - set an explicit snapshot retention period and test a restore drill;
103
+ - use S3/MinIO or a durable mounted volume for storage, not an ephemeral disk;
104
+ - use native PostgreSQL when the project needs extensions such as `pg_graphql`;
105
+ - keep one process per state directory; Lite is not a multi-writer cluster;
106
+ - verify unauthenticated API calls fail closed and authenticated reads succeed.
107
+
108
+ `supacloud-lite doctor --json` reports the selected engine, extension inventory,
109
+ identity mode and replication readiness. It is a capability report, not a
110
+ production security certification.
@@ -0,0 +1,15 @@
1
+ # Replace app.example.com with the real public hostname.
2
+ # Lite must remain bound to 127.0.0.1:54321 behind this proxy.
3
+ https://app.example.com {
4
+ encode zstd gzip
5
+
6
+ # Caddy terminates TLS and forwards WebSocket/Realtime upgrades by default.
7
+ reverse_proxy 127.0.0.1:54321
8
+
9
+ header {
10
+ Strict-Transport-Security "max-age=31536000; includeSubDomains"
11
+ X-Content-Type-Options "nosniff"
12
+ Referrer-Policy "strict-origin-when-cross-origin"
13
+ -Server
14
+ }
15
+ }
@@ -0,0 +1,32 @@
1
+ [Unit]
2
+ Description=SupaCloud Lite (single-project backend)
3
+ Documentation=https://github.com/vibeunion/supacloud/tree/main/packages/supacloud-lite
4
+ After=network-online.target
5
+ Wants=network-online.target
6
+
7
+ [Service]
8
+ Type=simple
9
+ User=supacloud-lite
10
+ Group=supacloud-lite
11
+ WorkingDirectory=/opt/supacloud-lite/project
12
+ EnvironmentFile=/etc/supacloud-lite/env
13
+ Environment=SUPACLOUD_LITE_STATE_DIR=/var/lib/supacloud-lite
14
+ Environment=SUPACLOUD_LITE_HOST=127.0.0.1
15
+ Environment=SUPACLOUD_LITE_PORT=54321
16
+ Environment=SUPACLOUD_LITE_RUNTIME_MODE=strict
17
+ ExecStart=/usr/local/bin/supacloud-lite start --project-dir /opt/supacloud-lite/project
18
+ Restart=on-failure
19
+ RestartSec=3
20
+ # Lite has no bundled TLS termination. Keep it on loopback and terminate TLS in
21
+ # Caddy/Nginx; bind a non-loopback address only when another trusted proxy or
22
+ # network boundary protects it.
23
+ UMask=0077
24
+ NoNewPrivileges=true
25
+ PrivateTmp=true
26
+ ProtectHome=true
27
+ ProtectSystem=strict
28
+ StateDirectory=supacloud-lite
29
+ ReadWritePaths=/var/lib/supacloud-lite
30
+
31
+ [Install]
32
+ WantedBy=multi-user.target