@supacloud/lite 0.33.2 → 0.35.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,28 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.35.0](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.34.0...supacloud-lite-v0.35.0) (2026-10-26)
4
+
5
+
6
+ ### Features
7
+
8
+ * **lite:** add PostgREST count strategies and rollback preference ([#2253](https://github.com/vibeunion/supacloud/issues/2253)) ([b87ef01](https://github.com/vibeunion/supacloud/commit/b87ef01f320cb1ed5b94cd16ea0412cf798fe5c9))
9
+ * **lite:** add S3-compatible storage endpoint ([#2252](https://github.com/vibeunion/supacloud/issues/2252)) ([37a37b3](https://github.com/vibeunion/supacloud/commit/37a37b3e193cd328a5ef5e852d927b594ad1856f))
10
+
11
+ ## [0.34.0](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.33.2...supacloud-lite-v0.34.0) (2026-10-26)
12
+
13
+
14
+ ### Features
15
+
16
+ * **lite:** add authorized realtime HTTP broadcast ([#2250](https://github.com/vibeunion/supacloud/issues/2250)) ([405747c](https://github.com/vibeunion/supacloud/commit/405747cce8f055c371e8f76c38a862aaf480f94c))
17
+ * **lite:** add production readiness doctor checks ([#2249](https://github.com/vibeunion/supacloud/issues/2249)) ([d9f09f2](https://github.com/vibeunion/supacloud/commit/d9f09f2ff797898af9a68d2c261b9d66e0eea001))
18
+ * **lite:** add restricted same-origin external auth proxy ([#2247](https://github.com/vibeunion/supacloud/issues/2247)) ([10e420e](https://github.com/vibeunion/supacloud/commit/10e420e700f206a8f190e6d51622c980d867fc68))
19
+ * **lite:** add storage status and metrics endpoints ([#2251](https://github.com/vibeunion/supacloud/issues/2251)) ([183b323](https://github.com/vibeunion/supacloud/commit/183b32397006a2b80d9961e7ae26d5779654b064))
20
+
21
+
22
+ ### Documentation
23
+
24
+ * **lite:** add single-node production deployment assets ([#2248](https://github.com/vibeunion/supacloud/issues/2248)) ([1e36554](https://github.com/vibeunion/supacloud/commit/1e36554cbbdbfecbb1e2a4974af560e8fa62124a))
25
+
3
26
  ## [0.33.2](https://github.com/vibeunion/supacloud/compare/supacloud-lite-v0.33.1...supacloud-lite-v0.33.2) (2026-10-25)
4
27
 
5
28
 
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
 
@@ -238,6 +253,9 @@ supacloud-lite auth create-user --project-dir ./my-project \
238
253
  - `SUPACLOUD_LITE_STORAGE_DIR`
239
254
  - `SUPACLOUD_LITE_STORAGE_BACKEND`:`fs`(默认)、`memory` 或 `s3`
240
255
  - `SUPACLOUD_LITE_S3_PREFIX`:S3 对象 key 前缀,可被 `--s3-prefix` 覆盖
256
+ - `SUPACLOUD_LITE_S3_ENDPOINT_ACCESS_KEY_ID` / `SUPACLOUD_LITE_S3_ENDPOINT_SECRET_ACCESS_KEY`:启用入站 `/storage/v1/s3` 的 SigV4 凭据,必须成对设置
257
+ - `SUPACLOUD_LITE_S3_ENDPOINT_REGION`:入站 S3 期望的 SigV4 region,默认 `us-east-1`
258
+ - `SUPACLOUD_LITE_S3_ENDPOINT_BUCKETS`:逗号分隔的入站 S3 bucket allowlist;为空表示项目内全部 bucket
241
259
  - `SUPACLOUD_LITE_POSTGRES_MIRROR`:native 引擎下载 PostgreSQL 发布包时使用的 HTTPS 镜像前缀,例如 `https://ghproxy.net/`
242
260
  - `SUPACLOUD_LITE_ENGINE`:`pglite`(默认)或 `native`
243
261
  - `SUPACLOUD_LITE_REPLICATION_PROFILE`:仅支持显式值 `powersync`,且仅用于 native 引擎
@@ -351,6 +369,26 @@ S3 模式的快照只包含数据库中的 Storage 元数据和密钥,不复
351
369
 
352
370
  内存数据库没有可持久化的数据,因此 `snapshot` 和 `upgrade` 会拒绝 `--memory`。
353
371
 
372
+ ### S3 兼容入口
373
+
374
+ 设置 `SUPACLOUD_LITE_S3_ENDPOINT_ACCESS_KEY_ID` 和 `SUPACLOUD_LITE_S3_ENDPOINT_SECRET_ACCESS_KEY` 后,Lite 会在 `/storage/v1/s3` 挂载 SigV4 + XML 的 S3 协议入口。AWS SDK 使用 path-style endpoint:
375
+
376
+ ```ts
377
+ const s3 = new S3Client({
378
+ endpoint: 'https://example.com/storage/v1/s3',
379
+ region: 'us-east-1',
380
+ forcePathStyle: true,
381
+ credentials: { accessKeyId, secretAccessKey },
382
+ })
383
+ ```
384
+
385
+ 支持 `ListBuckets`、Bucket 创建/HEAD/删除、`PutObject`、`GetObject`/Range、`HeadObject`、`DeleteObject`、`DeleteObjects`、`ListObjectsV2`、`CopyObject`、Presigned GET,以及最多 10000 个 part 的 multipart 上传。Multipart part 会先写入 StorageDriver 的临时 key,完成时按顺序合并;因此它适合单机 Lite,不是大规模并行对象组装服务。
386
+
387
+ S3 对象和 multipart 完成后的总大小上限为 64 MiB,同时遵守 bucket 的大小、MIME 类型限制及项目默认大小限制。Multipart 完成请求必须明确列出升序且不重复的 part;XML 请求体上限为 2 MiB。
388
+ 项目最多保留 128 个活跃 multipart 上传、10000 个临时 part 和 256 MiB 临时数据。上传 24 小时后过期,启动和后续 multipart 请求会清理过期数据;删除 bucket 时也会清理其临时分片。
389
+
390
+ 第一版明确限制:只支持 path-style,不支持 virtual-hosted-style、S3 ACL/Bucket Policy、Versioning、Object Lock、Replication、S3 Select 和 `STREAMING-*` SigV4 payload。S3 凭据是服务级凭据,默认绕过 RLS;需要隔离时用 `SUPACLOUD_LITE_S3_ENDPOINT_BUCKETS` 限定 bucket。S3 写入仍使用与普通 Storage API 相同的 bucket/object 元数据和 StorageDriver。
391
+
354
392
  ### 兼容范围
355
393
 
356
394
  | 能力 | V1 状态 | 说明 |
@@ -374,6 +412,21 @@ S3 模式的快照只包含数据库中的 Storage 元数据和密钥,不复
374
412
 
375
413
  RLS 表的 Realtime DELETE 无法在行删除后安全重放 SELECT policy,因此 V1 只向 `service_role` 订阅者发送这类 DELETE 事件。普通用户仍可收到通过逐行 RLS 校验的 INSERT/UPDATE 事件。
376
414
 
415
+ PostgREST 的边缘语义补充:表和 view 的 `Prefer: count=planned` 和 `count=estimated` 都在当前角色与 JWT claims 的只读事务中读取最外层 `count(*)` 输入节点的 `Plan Rows`(该计数聚合节点本身恒为 1),保留过滤条件、RLS、inner embed、view 和查询本身的 GROUP BY 聚合。Lite 不直接读取全表 `pg_class.reltuples`;`estimated` 是保守的 planner 兼容实现,不实现 PostgREST 按阈值切换精确计数的行为,估算值仍可能不准确。两者都只用于 GET/HEAD,结果仍写入 `Content-Range`。`Prefer: tx=rollback` 可包裹成功的 POST/PATCH/DELETE,返回与实际提交相同的状态和 representation,但事务最终回滚且不触发 CDC/webhook。其他 `Prefer` 值(`return`、`resolution`、`missing`)行为不变。
416
+
417
+ Realtime 也支持服务端 HTTP 广播:
418
+
419
+ ```http
420
+ POST /realtime/v1/api/broadcast
421
+ apikey: <project-anon-key>
422
+ authorization: Bearer <access-token>
423
+ content-type: application/json
424
+
425
+ {"messages":[{"topic":"room:42","event":"notice","payload":{"text":"hello"},"private":false}]}
426
+ ```
427
+
428
+ 请求沿用 Realtime 的 `messages` 数组格式,单次最多 100 条,每条 `event` 最多 256 字节、`topic` 最多 256 字节、`payload` 最多 1 MiB。`private: true` 会按 `realtime.messages` 的 INSERT policy 校验调用者;整个批次先完成校验再发送,任一主题无写权限时返回 `403`,不会部分广播。成功返回 `202 {"message":"Broadcast sent"}`。该端点只做实时投递,不持久化消息;需要离线补偿时仍应使用数据库表或 outbox。
429
+
377
430
  ### Capability doctor 与 PowerSync profile
378
431
 
379
432
  `doctor` 输出 Lite 自己的稳定能力契约,不复制完整 Management API:
@@ -508,6 +561,10 @@ Lite 的缓存调用是进程内数据库操作,不具备标准版跨进程 HT
508
561
 
509
562
  V1 不在一个进程内复用多个 PGlite 项目。需要多个项目时,为每个项目配置独立的工作目录、端口和 `.supacloud-lite` 状态目录,并用进程管理器分别启动。这样可保持数据库、JWT、Storage 和 Realtime 的故障域隔离。
510
563
 
564
+ ### 生产部署
565
+
566
+ `deploy/` 提供单机生产部署模板:systemd 用户/目录/状态目录约定、Caddy TLS 反代、外部 SupAuth 同源代理、快照维护窗口、S3 对象单独备份和回滚步骤。Lite 不自带 TLS、HA 或控制面;必须保持单进程单状态目录,并由 Caddy/Nginx 终止 TLS。生产前至少应完成容量测试、恢复演练和未认证请求失败关闭验证。
567
+
511
568
  ### 资源边界
512
569
 
513
570
  PGlite 是 WebAssembly PostgreSQL,不是 SQLite。它换来了 PostgreSQL SQL、角色、RLS 和 Supabase 迁移兼容性,但内存占用通常高于 PocketBase/SQLite。生产部署前应按真实 schema、并发和 Realtime 负载做容量测试。
@@ -531,6 +588,8 @@ await running.close()
531
588
 
532
589
  图片变换目前提供 Bun.Image 的兼容子集:`width`、`height`、`resize=fill|contain`,以及 `format=origin|jpeg|png|webp` 和 JPEG/WebP `quality`。`resize=cover` 需要裁剪能力,当前 Bun 运行时没有对应 API,因此会明确返回不支持错误,不会错误地把图片拉伸成 `cover`。
533
590
 
591
+ Storage 还提供两个运维端点:`GET /storage/v1/status` 无需项目 API key,返回服务名称、版本和健康状态;`GET /storage/v1/metrics` 需要 `service_role`,以 Prometheus 文本格式输出 bucket 数、对象元数据行数和声明对象总字节数。指标只统计 Lite 自己的 `storage` schema,不代表远端 S3 bucket 的真实用量或后台清理延迟。
592
+
534
593
  也可以使用 `createLiteBackend()` 直接创建内存或自定义目录的嵌入式后端,并把它的 `fetch` 传给自定义宿主。
535
594
 
536
595
  ### 来源与许可
@@ -662,7 +721,7 @@ supacloud-lite snapshot create [-o backup.tar.gz]
662
721
  supacloud-lite snapshot restore <backup.tar.gz> [--force]
663
722
  supacloud-lite upgrade [-o pre-upgrade.tar.gz]
664
723
  supacloud-lite inspect
665
- supacloud-lite doctor [--json]
724
+ supacloud-lite doctor [--json] [--production]
666
725
  supacloud-lite version
667
726
  ```
668
727
 
@@ -707,6 +766,9 @@ Environment variables:
707
766
  - `SUPACLOUD_LITE_STORAGE_DIR`
708
767
  - `SUPACLOUD_LITE_STORAGE_BACKEND`: `fs` (default), `memory`, or `s3`
709
768
  - `SUPACLOUD_LITE_S3_PREFIX`: S3 object key prefix, can be overridden by `--s3-prefix`
769
+ - `SUPACLOUD_LITE_S3_ENDPOINT_ACCESS_KEY_ID` / `SUPACLOUD_LITE_S3_ENDPOINT_SECRET_ACCESS_KEY`: SigV4 credentials enabling the inbound `/storage/v1/s3` endpoint; set both together
770
+ - `SUPACLOUD_LITE_S3_ENDPOINT_REGION`: SigV4 region expected by the inbound S3 endpoint, default `us-east-1`
771
+ - `SUPACLOUD_LITE_S3_ENDPOINT_BUCKETS`: comma-separated bucket allowlist for the inbound endpoint; empty means every project bucket
710
772
  - `SUPACLOUD_LITE_POSTGRES_MIRROR`: HTTPS prefix used to proxy native-engine PostgreSQL release downloads, for example `https://ghproxy.net/`
711
773
  - `SUPACLOUD_LITE_ENGINE`: `pglite` (default) or `native`
712
774
  - `SUPACLOUD_LITE_REPLICATION_PROFILE`: explicit `powersync` opt-in for the native engine only
@@ -853,10 +915,54 @@ GraphQL fails startup when missing; `doctor --json` reports actual or unverified
853
915
  capability. External SupAuth can reuse the shared Elysia verifier through
854
916
  `createSupAuthLiteIdentity` and an explicit application-owned local user mapping.
855
917
 
918
+ When the browser application and the identity service have different origins and
919
+ the identity service does not allow the application origin via CORS, Lite can
920
+ serve a restricted same-origin OAuth proxy:
921
+
922
+ ```sh
923
+ supacloud-lite start \
924
+ --external-auth-proxy-issuer https://auth.example.com/auth/v1 \
925
+ --external-auth-proxy-redirect-origin https://app.example.com
926
+ ```
927
+
928
+ The proxy accepts only `/auth/v1/oauth/token` (POST),
929
+ `/auth/v1/oauth/userinfo` (GET) and `/auth/v1/logout` (GET/POST). It never
930
+ forwards cookies, `x-forwarded-*`, or arbitrary upstream URLs. Authorization
931
+ remains a top-level navigation to the identity service; the application points
932
+ token/userinfo at the Lite same-origin address. Embedded hosts can call
933
+ `createExternalAuthProxy()` directly.
934
+
935
+ `doctor --production` adds single-node production policy checks: strict runtime,
936
+ non-memory storage, external identity proxying, public URLs, database/storage
937
+ path separation, state-directory location and snapshot freshness. Blockers exit
938
+ non-zero; warnings are operational obligations such as backing up S3 objects
939
+ outside the Lite snapshot, capacity-testing PGlite, and terminating TLS at the
940
+ proxy.
941
+
856
942
  See repository document `docs/lite-runtime-compatibility.md` for configuration
857
943
  and acceptance boundaries. Run `bun run benchmark` (or add `--native`) in the
858
944
  source package for a synthetic baseline, not a production performance claim.
859
945
 
946
+ ### S3-compatible endpoint
947
+
948
+ When `SUPACLOUD_LITE_S3_ENDPOINT_ACCESS_KEY_ID` and `SUPACLOUD_LITE_S3_ENDPOINT_SECRET_ACCESS_KEY` are set, Lite mounts a SigV4 + XML S3 endpoint at `/storage/v1/s3`. Configure AWS SDKs with a path-style endpoint:
949
+
950
+ ```ts
951
+ const s3 = new S3Client({
952
+ endpoint: 'https://example.com/storage/v1/s3',
953
+ region: 'us-east-1',
954
+ forcePathStyle: true,
955
+ credentials: { accessKeyId, secretAccessKey },
956
+ })
957
+ ```
958
+
959
+ It supports `ListBuckets`, bucket create/HEAD/delete, `PutObject`, `GetObject`/Range, `HeadObject`, `DeleteObject`, `DeleteObjects`, `ListObjectsV2`, `CopyObject`, presigned GET, and multipart uploads with up to 10000 parts. Multipart parts are staged in the StorageDriver and concatenated on completion; this is suitable for single-node Lite, not a distributed object-assembly service.
960
+
961
+ S3 objects, including completed multipart uploads, are limited to 64 MiB and also honor bucket size/MIME restrictions and the project default size limit. Completion requests must explicitly list unique parts in ascending order. XML request bodies are limited to 2 MiB.
962
+ Each project allows at most 128 active uploads, 10000 staged parts and 256 MiB of staged bytes. Uploads expire after 24 hours and are reclaimed at startup and on subsequent multipart requests. Bucket deletion also removes its staged multipart data.
963
+
964
+ The first version deliberately excludes virtual-hosted-style addressing, S3 ACL/Bucket Policy, Versioning, Object Lock, Replication, S3 Select, and `STREAMING-*` SigV4 payloads. S3 credentials are service-level and bypass RLS by default; use `SUPACLOUD_LITE_S3_ENDPOINT_BUCKETS` to restrict buckets. S3 writes use the same bucket/object metadata and StorageDriver as the normal Storage API.
965
+
860
966
  ### Compatibility Scope
861
967
 
862
968
  | Capability | V1 Status | Notes |
@@ -880,6 +986,21 @@ source package for a synthetic baseline, not a production performance claim.
880
986
 
881
987
  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
988
 
989
+ PostgREST edge semantics: for tables and views, `Prefer: count=planned` and `count=estimated` both read the `Plan Rows` of the outermost `count(*)` input inside the current role and JWT-claims read-only transaction (that counting aggregate itself always produces one row). This preserves filters, RLS, inner embeds, views, and the query's own GROUP BY aggregates. Lite does not read relation-wide `pg_class.reltuples` directly; `estimated` is a conservative planner-based compatibility implementation, not PostgREST's threshold-based switch to exact counting, and estimates may still be inaccurate. Both are only evaluated for GET/HEAD and still populate `Content-Range`. `Prefer: tx=rollback` wraps a successful POST/PATCH/DELETE, returns the same status and representation as a commit, then rolls the transaction back without firing CDC/webhooks. Other `Prefer` values (`return`, `resolution`, `missing`) keep their existing behavior.
990
+
991
+ Realtime also exposes server-side HTTP broadcast:
992
+
993
+ ```http
994
+ POST /realtime/v1/api/broadcast
995
+ apikey: <project-anon-key>
996
+ authorization: Bearer <access-token>
997
+ content-type: application/json
998
+
999
+ {"messages":[{"topic":"room:42","event":"notice","payload":{"text":"hello"},"private":false}]}
1000
+ ```
1001
+
1002
+ 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.
1003
+
883
1004
  ### Capability doctor and PowerSync profile
884
1005
 
885
1006
  `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 +1119,16 @@ Lite's cache calls are in-process database operations and do not have the timeou
998
1119
 
999
1120
  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
1121
 
1122
+ ### Production Deployment
1123
+
1124
+ The `deploy/` directory documents a single-node production contract: systemd
1125
+ service account/state directories, Caddy TLS termination, external SupAuth
1126
+ same-origin proxying, snapshot maintenance windows, separate S3 object backup,
1127
+ and rollback. Lite does not bundle TLS, HA, or a control plane. Keep one process
1128
+ per state directory and terminate TLS at Caddy/Nginx. Before production, run
1129
+ capacity tests, a restore drill, and an unauthenticated-request fail-closed
1130
+ check.
1131
+
1001
1132
  ### Resource Boundaries
1002
1133
 
1003
1134
  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 +1152,8 @@ await running.close()
1021
1152
 
1022
1153
  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
1154
 
1155
+ 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.
1156
+
1024
1157
  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
1158
 
1026
1159
  ### 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