@synopackageland/cli 0.3.9 → 0.4.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.
Files changed (286) hide show
  1. package/README.md +20 -9
  2. package/dist/bin.js +1 -2
  3. package/dist/build-spk/generator.d.ts +0 -3
  4. package/dist/build-spk/generator.js +135 -287
  5. package/dist/build-spk/native-lifecycle.js +0 -3
  6. package/dist/build-spk/native-payload.js +0 -1
  7. package/dist/cli.js +171 -151
  8. package/dist/compose/analyze.d.ts +1 -2
  9. package/dist/compose/analyze.js +5 -48
  10. package/dist/contract/unsupported-types.d.ts +10 -0
  11. package/dist/contract/unsupported-types.js +49 -0
  12. package/dist/contract/validate.js +25 -2
  13. package/dist/contract/web-launch.js +0 -2
  14. package/dist/deploy/deploy.d.ts +1 -4
  15. package/dist/deploy/deploy.js +57 -156
  16. package/dist/deploy/homes-values.js +1 -1
  17. package/dist/deploy/plan.d.ts +18 -0
  18. package/dist/deploy/plan.js +54 -0
  19. package/dist/deploy/port-preflight.d.ts +21 -0
  20. package/dist/deploy/port-preflight.js +42 -0
  21. package/dist/deploy/spk-version.d.ts +2 -14
  22. package/dist/deploy/spk-version.js +25 -111
  23. package/dist/dev.d.ts +1 -3
  24. package/dist/dev.js +14 -28
  25. package/dist/error-registry.js +810 -857
  26. package/dist/host/dsm-client.js +23 -263
  27. package/dist/host/dsm-port-profile.d.ts +47 -0
  28. package/dist/host/dsm-port-profile.js +206 -0
  29. package/dist/host/port-info-lookup.d.ts +9 -0
  30. package/dist/host/port-info-lookup.js +87 -0
  31. package/dist/host/port-info.d.ts +33 -0
  32. package/dist/host/port-info.js +66 -0
  33. package/dist/host/types.d.ts +6 -78
  34. package/dist/index.d.ts +0 -9
  35. package/dist/index.js +0 -6
  36. package/dist/info.d.ts +0 -1
  37. package/dist/info.js +3 -10
  38. package/dist/init.d.ts +11 -1
  39. package/dist/init.js +112 -4
  40. package/dist/log.js +2 -3
  41. package/dist/native/validate.js +0 -1
  42. package/dist/standalone/binary.d.ts +9 -0
  43. package/dist/standalone/binary.js +67 -0
  44. package/dist/standalone/build.d.ts +25 -0
  45. package/dist/standalone/build.js +108 -0
  46. package/dist/standalone/dsm-window.d.ts +26 -0
  47. package/dist/standalone/dsm-window.js +251 -0
  48. package/dist/standalone/helper.d.ts +31 -0
  49. package/dist/standalone/helper.js +104 -0
  50. package/dist/standalone/i18n.d.ts +28 -0
  51. package/dist/standalone/i18n.js +161 -0
  52. package/dist/standalone/inspect-spk.d.ts +21 -0
  53. package/dist/standalone/inspect-spk.js +160 -0
  54. package/dist/standalone/manifest.d.ts +71 -0
  55. package/dist/standalone/manifest.js +276 -0
  56. package/dist/standalone/root-messages.d.ts +7 -0
  57. package/dist/standalone/root-messages.js +250 -0
  58. package/dist/standalone/root-ops.d.ts +42 -0
  59. package/dist/standalone/root-ops.js +101 -0
  60. package/dist/standalone/root-window.d.ts +35 -0
  61. package/dist/standalone/root-window.js +290 -0
  62. package/dist/standalone/spk.d.ts +22 -0
  63. package/dist/standalone/spk.js +205 -0
  64. package/dist/templates/binary/synology-app.yaml +7 -0
  65. package/dist/templates/hello-files/compose.yaml +0 -1
  66. package/dist/templates/hello-web/compose.yaml +13 -10
  67. package/dist/templates/hello-web/html/app.js +1 -23
  68. package/dist/templates/hello-web/html/index.html +1 -3
  69. package/dist/templates/hello-web-page/compose.yaml +13 -10
  70. package/dist/templates/hello-web-page/html/app.js +1 -23
  71. package/dist/templates/hello-web-page/html/index.html +1 -3
  72. package/dist/templates/native-go/README.md +268 -0
  73. package/dist/templates/native-go/backend/cmd/app/main.go +49 -0
  74. package/dist/templates/native-go/backend/cmd/helper/main.go +38 -0
  75. package/dist/templates/native-go/backend/go.mod +3 -0
  76. package/dist/templates/native-go/backend/internal/api/errors.go +33 -0
  77. package/dist/templates/native-go/backend/internal/api/registry.go +94 -0
  78. package/dist/templates/native-go/backend/internal/api/registry_test.go +329 -0
  79. package/dist/templates/native-go/backend/internal/api/server.go +155 -0
  80. package/dist/templates/native-go/backend/internal/app/root_none.go +9 -0
  81. package/dist/templates/native-go/backend/internal/app/routes.go +127 -0
  82. package/dist/templates/native-go/backend/internal/app/routes_test.go +167 -0
  83. package/dist/templates/native-go/backend/internal/dsm/auth.go +118 -0
  84. package/dist/templates/native-go/backend/internal/dsm/auth_test.go +139 -0
  85. package/dist/templates/native-go/backend/internal/helper/control.go +219 -0
  86. package/dist/templates/native-go/backend/internal/helper/control_test.go +242 -0
  87. package/dist/templates/native-go/backend/internal/helper/peer_linux.go +44 -0
  88. package/dist/templates/native-go/backend/internal/helper/peer_other.go +7 -0
  89. package/dist/templates/native-go/backend/internal/helper/process_linux.go +167 -0
  90. package/dist/templates/native-go/backend/internal/helper/process_other.go +5 -0
  91. package/dist/templates/native-go/backend/internal/helper/server_linux.go +83 -0
  92. package/dist/templates/native-go/backend/internal/helper/status.go +271 -0
  93. package/dist/templates/native-go/backend/internal/helper/status_test.go +205 -0
  94. package/dist/templates/native-go/backend/internal/helper/supervision.go +84 -0
  95. package/dist/templates/native-go/backend/internal/helper/supervision_test.go +124 -0
  96. package/dist/templates/native-go/backend/internal/helper/supervisor_linux.go +192 -0
  97. package/dist/templates/native-go/backend/internal/helper/supervisor_linux_test.go +302 -0
  98. package/dist/templates/native-go/dsm-package.yaml +58 -0
  99. package/dist/templates/native-go/gitignore +1 -0
  100. package/dist/templates/native-go/i18n/glossary.json +662 -0
  101. package/dist/templates/native-go/i18n/messages/de.json +28 -0
  102. package/dist/templates/native-go/i18n/messages/en.json +28 -0
  103. package/dist/templates/native-go/i18n/messages/es.json +28 -0
  104. package/dist/templates/native-go/i18n/messages/fr.json +28 -0
  105. package/dist/templates/native-go/i18n/messages/it.json +28 -0
  106. package/dist/templates/native-go/i18n/messages/ja.json +28 -0
  107. package/dist/templates/native-go/i18n/messages/ko.json +28 -0
  108. package/dist/templates/native-go/i18n/messages/zh-CN.json +28 -0
  109. package/dist/templates/native-go/i18n/messages/zh-TW.json +28 -0
  110. package/dist/templates/native-go/ui/app.css +35 -0
  111. package/dist/templates/native-go/ui/app.js +94 -0
  112. package/dist/templates/native-go-better-auth/backend/internal/app/identity.go +208 -0
  113. package/dist/templates/native-go-better-auth/backend/internal/app/identity_test.go +113 -0
  114. package/dist/templates/native-go-better-auth/node/app.mjs +72 -0
  115. package/dist/templates/native-go-better-auth/node/build.mjs +2 -0
  116. package/dist/templates/native-go-better-auth/node/package-lock.json +862 -0
  117. package/dist/templates/native-go-better-auth/node/package.json +1 -0
  118. package/dist/templates/native-go-better-auth/node/test/auth.test.mjs +31 -0
  119. package/dist/templates/native-go-integration/backend/internal/app/identity_none.go +6 -0
  120. package/dist/templates/native-go-integration/backend/internal/app/personal.go +203 -0
  121. package/dist/templates/native-go-integration/backend/internal/app/personal_test.go +119 -0
  122. package/dist/templates/native-go-root/backend/internal/app/root.go +107 -0
  123. package/dist/templates/native-go-root/backend/internal/app/root_test.go +124 -0
  124. package/dist/templates/native-go-root/root/whoami.sh +6 -0
  125. package/dist/templates/native-go-vue/README.md +110 -0
  126. package/dist/templates/native-go-vue/frontend/DESIGN.md +78 -0
  127. package/dist/templates/native-go-vue/frontend/package-lock.json +2507 -0
  128. package/dist/templates/native-go-vue/frontend/package.json +6 -0
  129. package/dist/templates/native-go-vue/frontend/src/App.vue +132 -0
  130. package/dist/templates/native-go-vue/frontend/src/context.ts +17 -0
  131. package/dist/templates/native-go-vue/frontend/src/main.ts +26 -0
  132. package/dist/templates/native-go-vue/frontend/src/style.css +14 -0
  133. package/dist/templates/native-go-vue/frontend/test/apps.test.ts +148 -0
  134. package/dist/templates/native-go-vue/frontend/tsconfig.json +1 -0
  135. package/dist/templates/native-go-vue/frontend/vite.config.ts +8 -0
  136. package/dist/templates/native-go-vue/gitignore +2 -0
  137. package/dist/types.d.ts +1 -1
  138. package/dist/validate.js +11 -7
  139. package/package.json +3 -7
  140. package/schema/synology-app.schema.json +36 -37
  141. package/skills/SKILL.md +4 -48
  142. package/skills/reference/create.md +11 -83
  143. package/skills/reference/deploy.md +7 -78
  144. package/skills/reference/diagnose.md +4 -101
  145. package/skills/reference/files.md +5 -41
  146. package/skills/reference/identity.md +3 -29
  147. package/skills/reference/wrap.md +0 -2
  148. package/templates/binary/synology-app.yaml +7 -0
  149. package/templates/hello-files/compose.yaml +0 -1
  150. package/templates/hello-web/compose.yaml +13 -10
  151. package/templates/hello-web/html/app.js +1 -23
  152. package/templates/hello-web/html/index.html +1 -3
  153. package/templates/hello-web-page/compose.yaml +13 -10
  154. package/templates/hello-web-page/html/app.js +1 -23
  155. package/templates/hello-web-page/html/index.html +1 -3
  156. package/templates/native-go/README.md +268 -0
  157. package/templates/native-go/backend/cmd/app/main.go +49 -0
  158. package/templates/native-go/backend/cmd/helper/main.go +38 -0
  159. package/templates/native-go/backend/go.mod +3 -0
  160. package/templates/native-go/backend/internal/api/errors.go +33 -0
  161. package/templates/native-go/backend/internal/api/registry.go +94 -0
  162. package/templates/native-go/backend/internal/api/registry_test.go +329 -0
  163. package/templates/native-go/backend/internal/api/server.go +155 -0
  164. package/templates/native-go/backend/internal/app/root_none.go +9 -0
  165. package/templates/native-go/backend/internal/app/routes.go +127 -0
  166. package/templates/native-go/backend/internal/app/routes_test.go +167 -0
  167. package/templates/native-go/backend/internal/dsm/auth.go +118 -0
  168. package/templates/native-go/backend/internal/dsm/auth_test.go +139 -0
  169. package/templates/native-go/backend/internal/helper/control.go +219 -0
  170. package/templates/native-go/backend/internal/helper/control_test.go +242 -0
  171. package/templates/native-go/backend/internal/helper/peer_linux.go +44 -0
  172. package/templates/native-go/backend/internal/helper/peer_other.go +7 -0
  173. package/templates/native-go/backend/internal/helper/process_linux.go +167 -0
  174. package/templates/native-go/backend/internal/helper/process_other.go +5 -0
  175. package/templates/native-go/backend/internal/helper/server_linux.go +83 -0
  176. package/templates/native-go/backend/internal/helper/status.go +271 -0
  177. package/templates/native-go/backend/internal/helper/status_test.go +205 -0
  178. package/templates/native-go/backend/internal/helper/supervision.go +84 -0
  179. package/templates/native-go/backend/internal/helper/supervision_test.go +124 -0
  180. package/templates/native-go/backend/internal/helper/supervisor_linux.go +192 -0
  181. package/templates/native-go/backend/internal/helper/supervisor_linux_test.go +302 -0
  182. package/templates/native-go/dsm-package.yaml +58 -0
  183. package/templates/native-go/gitignore +1 -0
  184. package/templates/native-go/i18n/glossary.json +662 -0
  185. package/templates/native-go/i18n/messages/de.json +28 -0
  186. package/templates/native-go/i18n/messages/en.json +28 -0
  187. package/templates/native-go/i18n/messages/es.json +28 -0
  188. package/templates/native-go/i18n/messages/fr.json +28 -0
  189. package/templates/native-go/i18n/messages/it.json +28 -0
  190. package/templates/native-go/i18n/messages/ja.json +28 -0
  191. package/templates/native-go/i18n/messages/ko.json +28 -0
  192. package/templates/native-go/i18n/messages/zh-CN.json +28 -0
  193. package/templates/native-go/i18n/messages/zh-TW.json +28 -0
  194. package/templates/native-go/ui/app.css +35 -0
  195. package/templates/native-go/ui/app.js +94 -0
  196. package/templates/native-go-better-auth/backend/internal/app/identity.go +208 -0
  197. package/templates/native-go-better-auth/backend/internal/app/identity_test.go +113 -0
  198. package/templates/native-go-better-auth/node/app.mjs +72 -0
  199. package/templates/native-go-better-auth/node/build.mjs +2 -0
  200. package/templates/native-go-better-auth/node/package-lock.json +862 -0
  201. package/templates/native-go-better-auth/node/package.json +1 -0
  202. package/templates/native-go-better-auth/node/test/auth.test.mjs +31 -0
  203. package/templates/native-go-integration/backend/internal/app/identity_none.go +6 -0
  204. package/templates/native-go-integration/backend/internal/app/personal.go +203 -0
  205. package/templates/native-go-integration/backend/internal/app/personal_test.go +119 -0
  206. package/templates/native-go-root/backend/internal/app/root.go +107 -0
  207. package/templates/native-go-root/backend/internal/app/root_test.go +124 -0
  208. package/templates/native-go-root/root/whoami.sh +6 -0
  209. package/templates/native-go-vue/README.md +110 -0
  210. package/templates/native-go-vue/frontend/DESIGN.md +78 -0
  211. package/templates/native-go-vue/frontend/package-lock.json +2507 -0
  212. package/templates/native-go-vue/frontend/package.json +6 -0
  213. package/templates/native-go-vue/frontend/src/App.vue +132 -0
  214. package/templates/native-go-vue/frontend/src/context.ts +17 -0
  215. package/templates/native-go-vue/frontend/src/main.ts +26 -0
  216. package/templates/native-go-vue/frontend/src/style.css +14 -0
  217. package/templates/native-go-vue/frontend/test/apps.test.ts +148 -0
  218. package/templates/native-go-vue/frontend/tsconfig.json +1 -0
  219. package/templates/native-go-vue/frontend/vite.config.ts +8 -0
  220. package/templates/native-go-vue/gitignore +2 -0
  221. package/dist/attestation/app-package.d.ts +0 -86
  222. package/dist/attestation/app-package.js +0 -206
  223. package/dist/attestation/install-on-host.d.ts +0 -14
  224. package/dist/attestation/install-on-host.js +0 -52
  225. package/dist/attestation/keyring.d.ts +0 -21
  226. package/dist/attestation/keyring.js +0 -168
  227. package/dist/attestation/platform-service.d.ts +0 -70
  228. package/dist/attestation/platform-service.js +0 -250
  229. package/dist/build-spk/broker-glue.d.ts +0 -16
  230. package/dist/build-spk/broker-glue.js +0 -40
  231. package/dist/deploy/catalog-publish.d.ts +0 -3
  232. package/dist/deploy/catalog-publish.js +0 -7
  233. package/dist/doctor.d.ts +0 -22
  234. package/dist/doctor.js +0 -721
  235. package/dist/error-collector.d.ts +0 -2
  236. package/dist/error-collector.js +0 -46
  237. package/dist/host/broker-paths.d.ts +0 -1
  238. package/dist/host/broker-paths.js +0 -1
  239. package/dist/host/broker-upgrade-preflight.d.ts +0 -61
  240. package/dist/host/broker-upgrade-preflight.js +0 -156
  241. package/dist/host/digests.d.ts +0 -5
  242. package/dist/host/digests.js +0 -63
  243. package/dist/host/fake-nas.d.ts +0 -41
  244. package/dist/host/fake-nas.js +0 -174
  245. package/dist/mock-broker/bootstrap.d.ts +0 -2
  246. package/dist/mock-broker/bootstrap.js +0 -8
  247. package/dist/mock-broker/files.d.ts +0 -60
  248. package/dist/mock-broker/files.js +0 -408
  249. package/dist/mock-broker/oidc.d.ts +0 -31
  250. package/dist/mock-broker/oidc.js +0 -161
  251. package/dist/mock-broker/path.d.ts +0 -9
  252. package/dist/mock-broker/path.js +0 -50
  253. package/dist/mock-broker/server.d.ts +0 -19
  254. package/dist/mock-broker/server.js +0 -350
  255. package/dist/publish/build-ledger.d.ts +0 -22
  256. package/dist/publish/build-ledger.js +0 -26
  257. package/dist/publish/community-path.d.ts +0 -6
  258. package/dist/publish/community-path.js +0 -41
  259. package/dist/publish/curated-release.d.ts +0 -78
  260. package/dist/publish/curated-release.js +0 -883
  261. package/dist/publish/file-build-ledger.d.ts +0 -18
  262. package/dist/publish/file-build-ledger.js +0 -268
  263. package/dist/publish/github-pr.d.ts +0 -25
  264. package/dist/publish/github-pr.js +0 -85
  265. package/dist/publish/github-repo.d.ts +0 -8
  266. package/dist/publish/github-repo.js +0 -21
  267. package/dist/publish/platform-service-release.d.ts +0 -60
  268. package/dist/publish/platform-service-release.js +0 -333
  269. package/dist/publish/preflight.d.ts +0 -24
  270. package/dist/publish/preflight.js +0 -138
  271. package/dist/publish/publish.d.ts +0 -27
  272. package/dist/publish/publish.js +0 -136
  273. package/dist/publish/sync-definition.d.ts +0 -4
  274. package/dist/publish/sync-definition.js +0 -48
  275. package/dist/release-bin.d.ts +0 -2
  276. package/dist/release-bin.js +0 -23
  277. package/dist/templates/hello-web/html/sdk.js +0 -68
  278. package/dist/templates/hello-web-page/html/sdk.js +0 -68
  279. package/dist/templates/native/synology-app.yaml +0 -7
  280. package/dist/test-host.d.ts +0 -15
  281. package/dist/test-host.js +0 -37
  282. package/templates/hello-web/html/sdk.js +0 -68
  283. package/templates/hello-web-page/html/sdk.js +0 -68
  284. package/templates/native/synology-app.yaml +0 -7
  285. /package/dist/templates/{native → binary}/native.yaml +0 -0
  286. /package/templates/{native → binary}/native.yaml +0 -0
@@ -1,93 +1,21 @@
1
1
  # 建立(Create)
2
2
 
3
- 從一句話或空白目錄產出可跑專案。**最少問題**:只問 schema/模板無法推斷的意圖。
3
+ 先選擇 `native-go`(Go CGI + DSM 視窗)、`binary`(預建服務程式)或 `compose-import`(上游容器)。`hello-web` 與 `hello-web-page` 分別示範 DSM 視窗及新分頁入口;`hello-files` 示範 Share 與固定 run-as 使用者。
4
4
 
5
- ## 選模板(`synopkgland init -t`)
6
-
7
- | 模板 | 何時 | 預設整合 |
8
- | --- | --- | --- |
9
- | `hello-web` | 第一個「DSM 帳號打開」的 Web App(DSM 視窗) | 身分;`launch: dsm-window`;**不**開廣域 `files.*` |
10
- | `hello-web-page` | Web App 以 Package Center Open 開新分頁 | 身分;`launch: new-page` |
11
- | `hello-files` | 讀寫管理員選的 Shared Folder | 身分 + 必填 `install` shared_folder |
12
- | `compose-import` | 已有 `compose.yaml` | 保留映像/upstream 的 container target;只把 host source 換成 `${PARAM}`;Share 安裝時選 |
13
-
14
- ```bash
15
- synopkgland init -t hello-web [目錄]
16
- ```
17
-
18
- 產物:`synology-app.yaml`、`compose.yaml`、README。需要程式碼時在同一專案加最小 Web App;v1 官方範例用 TypeScript + `@synopackageland/dev-sdk`。
19
-
20
- ## Published port 分配
21
-
22
- `hello-web` 與 `hello-web-page` 模板的預設 `adminport` 是 **18333**。這個預設值在 2026-08-24 由 8080 調整而來,因為 8080 在真實 NAS 上常被既有容器使用;它只降低碰撞機率,不能保證該 port 一定可用。
23
-
24
- `adminport` 是 NAS 上的 published host port,必須和 `compose.yaml` 的 host-side published port 對應。若同一台 NAS 上有多個測試或正式 instance,每個 package/instance 都要分配獨立的 published port;**同一台 NAS 上不要重用同一個 port**。可挑選尚未被既有服務使用的高位 port,例如 19xxx 範圍(`19160` 只是示例),並記錄每個 instance 的分配。
25
-
26
- 例如把模板 instance 改用 19160 時,`synology-app.yaml` 與 Compose 的 host-side port 要一起改,容器內 target 仍依原本 Compose/映像設定:
27
-
28
- ```yaml
29
- # synology-app.yaml
30
- adminport: 19160
31
-
32
- # compose.yaml
33
- ports:
34
- - "19160:18333"
5
+ ```sh
6
+ synopkgland init -t native-go --package MyApp my-app
7
+ synopkgland init --binary service
8
+ synopkgland init -t compose-import upstream-app
35
9
  ```
36
10
 
37
- 不要只改其中一處:`adminport` 沒有對應的 published port 會在 `validate` 被擋下;host port 已被同一台 NAS 的其他服務占用,則可能直到 deploy 才以 DSM 的 `broken` 呈現。
38
-
39
- 包現成 Compose 時,先用 `inspect` 看原本的 volume mapping。容器路徑跟 Compose/映像的 upstream target 走,不要為了套平台範例改成 `/data`;只把 host source 換成 `${PARAM}`。原有的 upstream named volume 要保留在頂層 `volumes:`。需要既有 Share 時宣告 `install.<PARAM>.type: shared_folder`,管理員在安裝時選這台 NAS 的 Share。
40
-
41
- ### `posix_all_shares`
42
-
43
- 當 App 必須在安裝當下掛載這台 NAS 目前列出的每一個共用資料夾時,宣告 `install.<NAME>.type: posix_all_shares`,並把 `${NAME}` 作為完整 Compose volume source,例如 `${NAME}:/shares:ro`。Package Center wizard 會以 `SYNO.Core.Share.list` 的 multi-select 讓管理員再次確認;這是最大警告:容器可讀(或依 Compose 模式讀寫)所有列出的 Share,安裝後新建的共用資料夾不會自動加入,由管理員決定是否安裝。不要在契約或 Compose 寫 `/volume*`、domain、實體 path;`CLI deploy` 未給 `--param NAME=...` 時會用管理員 session 重新列出當下的 Share,preinst 只解析清單,不掃描檔案系統。
44
-
45
- ## 路線判準:零設定(Static Zero-Config)vs Dynamic vs Broker
46
-
47
- Syno Package Land 在打包 Compose App 時,依契約與 Compose 內容自動分類為不同路線:
48
-
49
- | 路線 | 條件與特徵 | 適用環境 |
50
- | --- | --- | --- |
51
- | **零設定(Static Zero-Config)** | 靜態、低權限、無 `${...}` 插值(除專案相對路徑 `./html` bind 外)、單一 published port 對應 `adminport`。由 DSM 原生 `docker-project` 與 `webservice` resource worker 直接接管。 | Personal 與 **Curated 上架均支援** |
52
- | **Dynamic** | 宣告了 string/boolean 的 `install` 參數,在 Compose 內以 `${NAME}` 引用;preinst carrier 以 0600 寫入 `$SYNOPKG_PKGINST_TEMP_DIR/compose/.env` 供 DSM worker 載入。 | Personal 支援(Curated 暫限已支援子集) |
53
- | **Broker-installer(拒收於 Curated)** | 包含 `${SYNOPKG_PKGVAR}` 等平台 bind、需要 root 啟動的映像(例如官方 nginx)、未宣告參數的插值等。 | Personal 會警告;**Curated 上架直接拒收(`CURATED_BROKER_ROUTE_FORBIDDEN`)** |
54
-
55
- ### Curated 拒絕 Broker-installer 路線與零設定寫法
56
-
57
- Rootless Broker 不支援 Container Manager 操作,因此 Curated 上架不接受 Broker-installer 路線:
58
- - 官方 `nginx:alpine` 這類需要 root 權限啟動的 image 在 DSM Container Manager 注入 package UID 後會直接 `ExitCode 1` 起不來。
59
- - `${SYNOPKG_PKGVAR}` 等平台 bind 應改用 named volume,或改用 D1 dynamic route 的 string/boolean 參數。
60
- - Web 服務推薦使用可由任意 UID 執行的映像檔與專案相對路徑 bind(模板寫法):
61
-
62
- ```yaml
63
- # 推薦的零設定 compose.yaml 寫法(參考 hello-web 模板)
64
- services:
65
- web:
66
- image: busybox:1.36
67
- command: ["sh","-c","httpd -f -p 18333 -h /usr/share/nginx/html"]
68
- ports:
69
- - "18333:18333"
70
- volumes:
71
- - "./html:/usr/share/nginx/html:ro"
72
- ```
73
-
74
- (來源:docs/impl/STATUS.md A31、packages/app-cli/templates/hello-web/compose.yaml)
75
-
76
- ### Compose 變數未定義與執行身分限制
77
-
78
- - **未定義變數會導致 DSM Worker 靜默失敗**:Compose 內引用的環境變數若未在契約 `install` 宣告或未提供預設值,DSM `docker-project` worker 會直接失敗,套件呈現 `broken 150` 且 `worker_msg` 為空。(來源:docs/impl/dynamic-compose-d3-runbook.md「DSM 事實」第 4 條、docs/impl/STATUS.md 關鍵教訓 11)
79
- - **容器以 Package UID 執行**:DSM worker 會注入 `user: <package-uid>:<package-gid>`。以 root 擁有的 Docker named volume 會因為無寫入權限而報錯;容器如需寫入目錄,請寫入 `/tmp` 或由映像建立 package UID 可寫的目錄。(來源:docs/impl/STATUS.md 關鍵教訓 19)
11
+ 容器沿用上游登入。包現成 Compose 時保留 upstream target,只把 host source 換成 `${PARAM}`;管理員在安裝時選 Share。不要硬編碼 NAS 路徑,不要以固定 UID/GID 冒充逐使用者 ACL。
80
12
 
81
- ## 只問這些(缺才問)
13
+ ## Published port
82
14
 
83
- 1. **Package Identity**(reverse-DNS,例如 `com.example.myapp`)— 將來上架同一條身分。
84
- 2. **顯示名稱**(`displayname`)。
85
- 3. **要不要碰 NAS 檔案** — 若要,預設走 Shared Folder(`hello-files`),不要默默加 `files.read`/`files.write`/`files.delete`(見 `files.md`)。
86
- 4. **已有 Compose 嗎** — 有則 `compose-import` + `inspect`。
15
+ hello-web 預設 published port 是 18333。每個 package/instance 都要分配獨立的 port;同一台 NAS 上不要重用。可挑選未使用的 19xxx 高位 port,例如 19160;同時修改 contract adminport 與 Compose published port,保留容器內 target。
87
16
 
88
- 不要問:SPK INFO、wizard、DDNS、port forwarding、Community PR。
17
+ ## 建置與檢查
89
18
 
90
- ## 下一步
19
+ 執行 validate、build-spk、inspect --spk,再看 deploy --dry-run。Go CGI 每包驗證 DSM session,root 模式只提供固定管理員核准工作。容器必須使用已建置映像;不支援的特權 host 存取、secret/config 與 build stanza 會拒絕。
91
20
 
92
- - 本機:`synopkgland dev` → `validate` → `test`
93
- - 要上 NAS:讀 `deploy.md`
21
+ 安裝前讀 deploy.md。需要資料或登入時讀 files.md、identity.md;不加入尚未實作的 bridge。
@@ -1,81 +1,10 @@
1
- # 部署(Deploy)
1
+ # Build and deploy
2
2
 
3
- Inner Loop:thin SPK、NAS Administrator DSM 授權、Local Attestation、經 Package Center 安裝。Personal deploy 會把 Broker 簽發的 local attestation 內嵌到套件供 preinst 使用。**不要 SSH**,不要把 Compose 貼進 Container Manager 手工裝。
3
+ 1. Run `synopkgland validate` and `synopkgland build-spk --arch x86_64` (use aarch64 for that target).
4
+ 2. Inspect the resulting file with `synopkgland inspect --spk <file>`.
5
+ 3. Review `synopkgland deploy --dry-run --arch x86_64`, which does not connect to a NAS.
6
+ 4. Only when installation is authorized, run `synopkgland deploy --host <profile>`.
4
7
 
5
- ## 前置
8
+ Deployment uploads directly to DSM. It does not publish artifacts. Container install answers use --share, --run-as and --param; omitted upgrade answers are retained by package preinst, which also rejects missing newly required values.
6
9
 
7
- ```bash
8
- synopkgland validate
9
- synopkgland test
10
- ```
11
-
12
- NAS 需已安裝 **SynoPkgLandService**(Broker)與 Container Manager。CLI 憑證:`synoagentcli` profile 或 `DSM_URL`/`DSM_PASSWORD`(**勿寫進 Git 或 chat**)。
13
-
14
- ## 正式 vs 開發 Instance(UF-2/D3/D4)
15
-
16
- | 情況 | 做法 |
17
- | --- | --- |
18
- | 第一次裝這個 `package` | `synopkgland deploy --host <nas>` |
19
- | 已裝**正式** Instance,要邊改邊玩、不影響使用者 | **`synopkgland deploy --host <nas> --dev`**(`{package}.dev`) |
20
- | NAS 上該 `package` 已是 **Curated**(App Package Attestation,或已裝版本 build ≥ 10000) | 預設 **`deploy` 會拒絕**(`CURATED_DEPLOY_REFUSED`)→ 建議 `--dev`;**不要**預設「取代商店版」 |
21
- | 必須用本機版覆蓋 Curated 正式版 | 僅在 NAS Administrator **明示危險確認**後:`--replace-curated`(印警告 → DSM stop → 正常卸載保留 App Data → Personal 安裝) |
22
-
23
- **規則**:若正式 Instance 已存在,skill **必須建議 `.dev`**,不得默默對同一 Package Identity 執行 `deploy`。
24
-
25
- (來源:docs/impl/STATUS.md A33)
26
-
27
- ## 安裝參數(`--param`)
28
-
29
- Dynamic Compose 路線支援以 `--param NAME=value` 提供安裝時參數(可重複傳入):
30
-
31
- - 只接受 `synology-app.yaml` 的 `install` 區塊宣告的 `string` 或 `boolean` 參數。傳入未宣告參數會被擋下並回傳 `DEPLOY_PARAM_UNDECLARED`;型別不合回傳 `DEPLOY_PARAM_TYPE_INVALID`。
32
- - `string` 參數值必須是非空字串,禁止控制字元、`${` 插值或超過 256 字元。
33
- - `boolean` 參數值只接受字串 `"true"` 或 `"false"`(對應 DSM multiselect 的 canonical text 值)。
34
-
35
- (來源:docs/impl/STATUS.md A1、packages/app-cli/src/deploy/param-values.ts)
36
-
37
- ## 版本規則(Personal vs Curated)
38
-
39
- 為避免 Personal 開發版本號搶先 Curated 商店版本,系統採用五位 offset 劃分命名空間:
40
-
41
- - **Personal 命名空間**:`<base>-0001` .. `<base>-9999`(四位)。
42
- - 版本 base 取自 `synology-app.yaml` 的 `version`(去掉自帶的 `-NNNN`)。
43
- - 若目標 NAS 或 feed 上的最新版本與當前 base 相同,則在此 base 下 build 單調遞增(例如 `1.0.0-0001` → `1.0.0-0002`)。
44
- - 當契約 base 升級時(例如 `1.0.0` 升至 `1.1.0`),新版本從 `<base>-0001` 開始(例如 `1.1.0-0001`)。
45
- - 候選版本若不高於目標 NAS 已裝版本(降版),會 fail closed 拋出 `DEPLOY_VERSION_NOT_ASCENDING`。
46
- - Personal build 超過 `9999` 上限時,拋出 `DEPLOY_PERSONAL_BUILD_EXHAUSTED`。
47
- - **Curated 命名空間**:`<base>-<10000 + ledger Build>`(五位,例如 `1.0.0-10010`,對應 CI ledger Build 10)。
48
- - Curated 版本永遠大於任何 Personal 版本(`1.0.0-10001` > `1.0.0-9999`)。
49
-
50
- (來源:docs/adr/0067-curated-versions-live-above-the-personal-build-range.md、docs/impl/STATUS.md A28、A30)
51
-
52
- ## 已裝 Curated 處理與 `--replace-curated`
53
-
54
- 當 NAS 上該 package 已持有 Curated Attestation 或已安裝版本 build ≥ 10000 時:
55
-
56
- - 預設 `deploy` 會立即拒絕並回傳 `CURATED_DEPLOY_REFUSED`。
57
- - `--dev` 使用獨立的 `{package}.dev` identity,不受正式版 Curated 影響,是邊改邊玩的建議路線。
58
- - 若管理者明示要用本機版覆蓋 Curated 正式版,使用 `--replace-curated`:
59
- 1. CLI 印出警告文案:`警告:--replace-curated 會移除 Curated 安裝,但會保留 App Data。`
60
- 2. 透過 DSM API 停止套件並執行正常卸載(保留 package `var` 中的 App Data)。
61
- 3. 重新向 Broker 申請 Local Attestation 並依 Personal 規則從 `<base>-0001` 開始安裝。
62
-
63
- (來源:docs/impl/STATUS.md A33)
64
-
65
- ## 命令
66
-
67
- ```bash
68
- synopkgland deploy --host <nas> # 正式 package
69
- synopkgland deploy --host <nas> --dev # 開發 package(.dev)
70
- synopkgland deploy --host <nas> --replace-curated # 危險:Curated 降級,需人確認
71
-
72
- synopkgland info --host <nas> [--dev]
73
- synopkgland log --host <nas> [--dev]
74
- synopkgland test --host <nas> [--dev]
75
- ```
76
-
77
- 成功後:DSM 按 Open;`info` 的 Open URL。已登入 DSM 不應再要平台密碼。
78
-
79
- ## 未宣稱通過
80
-
81
- D3/D4/D12 實機與 15 分鐘黃金路徑仍可能失敗;失敗走 `diagnose.md`,不要宣稱產品已全綠。
10
+ For native-go root, approve fixed operations in DSM after installation. Updates that change the task text require fresh approval. Remove tasks before uninstall. Never delete installed packages or data as part of a code cleanup.
@@ -1,104 +1,7 @@
1
- # 排查(Diagnose)
1
+ # Diagnose
2
2
 
3
- 遇到問題時,**先跑 `synopkgland doctor`,依穩定錯誤碼行動**。
3
+ Start with validate and inspect. For DSM broken / 150 inspect Package Center status, including broken_by=install_corruption, and whether a required port 被占用. `info` reads package state; `log` reads package status, not app stdout streaming.
4
4
 
5
- > **注意**:`doctor` 載入設定與專案上下文從 cwd 往上找。請在 repo 或專案目錄執行;若在其他目錄執行,必須加上 `--project <dir>`(來源:docs/impl/STATUS.md 關鍵教訓 15)。
5
+ The independent container path uses resource-worker lifecycle. Unsupported Compose settings fail before build; there is no alternate installer. COMPOSE_NUMERIC_COMMAND_ARG_FORBIDDEN means DSM may strip quoting from 純數字 command 元素. Use a single shell command when appropriate, for example `command: ["sh","-c","httpd -f -p 18333 -h /srv"]`.
6
6
 
7
- ```bash
8
- # 診斷本機專案(契約、Compose/Native 設定、版本漂移)
9
- synopkgland doctor
10
-
11
- # 診斷目標 NAS 主機(連線、Broker 狀態、Container Manager、Attestation 信任根)
12
- synopkgland doctor --host <nas>
13
-
14
- # 診斷特定 App 套件(Project Binding 狀態、上次安裝/就緒失敗原因、入口探測)
15
- synopkgland doctor --host <nas> --package <id>
16
- ```
17
-
18
- 權威是 `synopkgland doctor` 診斷檢查、`synopkgland validate` 輸出、`deploy`/Broker 回傳與 `info`/`log` 的 readiness。所有 fail 項均帶有穩定錯誤碼與具體的下一步建議。不要猜 Compose。
19
-
20
- ## deploy 後 `broken`/statusCode 150
21
-
22
- 若 deploy 回報 `CATALOG_INSTALL_FAILED`,而 DSM 顯示 `[CATALOG_INSTALL_FAILED] ... (Package Center status broken). Check synopkg postinst / webservice acquire.`,先跑:
23
-
24
- ```bash
25
- synopkgland doctor --host <nas> --package <id>
26
- ```
27
-
28
- 這類失敗可能只有 `broken_by=install_corruption`/`statusCode=150`,DSM 的錯誤訊息通常看不出真正原因。先依 `doctor` 的建議排查以下常見原因:
29
-
30
- | 常見原因 | 修法與說明 |
31
- | --- | --- |
32
- | 同一台 NAS 上已有其他服務或 instance,published host port 已被占用 | 為每個 package/instance 分配不重複的 host port,例如改用 19160;同步把 `synology-app.yaml` 的 `adminport` 改成 19160,並把 `compose.yaml` 的 host-side mapping 改成例如 `"19160:18333"`。容器內 target 依原本 Compose/映像設定保留。 |
33
- | Compose `command`/`entrypoint` 陣列含純數字字串元素,例如 `"18333"` | 若走 resource-worker 路徑,`validate` 會報 `COMPOSE_NUMERIC_COMMAND_ARG_FORBIDDEN`。把整個命令包成單一字串,例如 `command: ["sh","-c","httpd -f -p 18333 -h /srv"]`,或把數字和 flag 併寫成 `-p18333`。 |
34
-
35
- 第一項是 published port 碰撞;第二項是 DSM 的 resource-worker 在 YAML round-trip 時掉引號,將純數字元素變成整數,導致 postinst acquire 失敗,可能同樣呈現 `broken`/150,且 `worker_msg` 為空。`COMPOSE_NUMERIC_COMMAND_ARG_FORBIDDEN` **只適用於 Compose 走 resource-worker 路徑**;純數字元素在標準 Compose 合法,Broker fallback 不受此限制,這是 DSM worker 的缺陷而不是 Compose 規格不允許。
36
-
37
- ### Broken 150 深入排查順序與日誌位置(純觀察)
38
-
39
- 當 DSM 與 Package Center 日誌資訊不足時,可依序觀察以下系統位置與現象,排查殘留孤兒狀態(**注意:此處僅供現象與位置觀察診斷,禁止進行未經確認的破壞性 root 操作**):
40
-
41
- 1. **`/var/log/synoscgi.log` 日誌**:
42
- DSM resource worker 的底層真正錯誤會記錄在 `/var/log/synoscgi.log`,而 `synopkg.log` 往往只有簡略的 `(fail)`,且 `worker_msg` 幾乎永遠為空。(來源:docs/impl/STATUS.md 關鍵教訓 12)
43
- 2. **`.sc` port profile 孤兒**:
44
- 檢查 `/usr/local/etc/services.d/docker_<container>.sc` 是否存在。DSM worker 在 rollback 或 normal uninstall 時可能殘留 `.sc` 檔案(內含 `dst.ports` 宣告)。若後續有不同容器名但使用相同 host port 的安裝,會在 `profile.cpp:118 Fail to create port section` 失敗並呈現 broken 150。(來源:docs/impl/STATUS.md A29、關鍵教訓 13)
45
- 3. **Container Manager project 殘留記錄**:
46
- 觀察 `/volume1/@appconf/ContainerManager/projects/<id>.config.json`。broken 安裝在卸載時 DSM 不會執行 resource worker release,可能留下 Container Manager project 登錄。(來源:docs/impl/STATUS.md 關鍵教訓 17)
47
- 4. **Web Station portal 與 service 殘留**:
48
- Web Station 設定(如 `Portal.json`、`Shortcut.json`、`/usr/syno/etc/www/nginx_config.json` 中的 `configs` 項,或 `/usr/local/etc/nginx/conf.d/alias.synopkgland-<alias>.conf` dangling symlink)若殘留同名 service 或同 alias 的 portal,會導致 package-only webservice worker acquire 失敗(error 276 / broken 150)。(來源:docs/impl/STATUS.md A27、關鍵教訓 17、18)
49
-
50
- 修正後依序執行 `synopkgland validate`,再用 `synopkgland deploy --host <nas>` 重試;若仍失敗,保留 `doctor`、`info` 與 `log` 的診斷輸出供後續判斷。
51
-
52
- ## validate/analyzer(修契約或 Compose)
53
-
54
- | 錯誤碼 | 下一步 |
55
- | --- | --- |
56
- | `CONTRACT_SCHEMA_INVALID`/`CONTRACT_MISSING_FIELD` | 對照 schema 與模板;補必填欄位,刪除非法鍵 |
57
- | `CONTRACT_SCHEMA_UNSUPPORTED` | `schema` 必須為整數 `1` |
58
- | `CONTRACT_INVALID_STRING`/`CONTRACT_ADMINPORT_INVALID` | 修正字串或 `adminport` 型別 |
59
- | `INSTALL_STRING_*`/`INSTALL_PARAMETER_UNREFERENCED` | 修正 install 預設值;在 compose 加 `${參數名}` |
60
- | `COMPOSE_MISSING` | 建立 `compose.yaml`(容器 App)或 `native.yaml`(原生 binary App) |
61
- | `PROJECT_RUNTIME_EXCLUSIVE` | `compose.yaml` 與 `native.yaml` 互斥,刪掉其中一個 |
62
- | `NATIVE_SCHEMA_INVALID` | `native.yaml` 的 `schema` 只接受 `1` |
63
- | `NATIVE_TARGETS_REQUIRED` / `NATIVE_TARGETS_EMPTY` | 在 `targets` 底下至少宣告一個 `x86_64` 或 `aarch64` |
64
- | `NATIVE_ARCH_UNKNOWN` | `targets` 的 key 只接受 `x86_64`、`aarch64` |
65
- | `NATIVE_TARGET_SOURCE_EXCLUSIVE` | 每個 target 只能有 `path:` 或 `url:` 其中一個 |
66
- | `NATIVE_PATH_FORBIDDEN_IN_LISTING` | Curated 上架只接受 `url:` + `sha256:`;本機開發才可以用 `path:` |
67
- | `NATIVE_SHA256_REQUIRED_IN_LISTING` | 補上 `sha256:`,CI 要能自己重抓比對 |
68
- | `NATIVE_SHA256_INVALID` | `sha256` 必須是 64 個 hex 字元 |
69
- | `NATIVE_STRIP_COMPONENTS_INVALID` | `strip_components` 必須是 >= 0 的整數 |
70
- | `NATIVE_EXEC_REQUIRED` / `NATIVE_EXEC_INVALID_PATH` | `service.exec` 必填,且必須是相對 `SYNOPKG_PKGDEST` 的路徑,不可為絕對路徑或含 `..` |
71
- | `NATIVE_PKGVAR_REQUIRED` | `service.args` 或 `service.env` 至少要用一次 `${SYNOPKG_PKGVAR}`,否則升級時資料會被連同 `target/` 一起換掉 |
72
- | `NATIVE_SERVICE_USER_FORBIDDEN` | 拿掉 `service.user`:DSM 自己建 package user,`SYNOPKG_PKGVAR` 已經是對的 owner |
73
- | `NATIVE_SERVICE_SHELL_FIELD_FORBIDDEN` | 拿掉 `command:` / `pre_start:` 這類 shell 欄位;`args` 是陣列,逐項傳給 exec,不經 shell |
74
- | `NATIVE_ARGS_INVALID` / `NATIVE_ENV_INVALID` | `args` 必須是字串陣列,`env` 必須是 string→string |
75
- | `COMPOSE_PRIVILEGED_FORBIDDEN` 等安全碼 | 移除 privileged、host 網路、socket 掛載等 |
76
- | `COMPOSE_SERVICE_NOT_FOUND` | 修正 `adminport` 對應的 service |
77
- | `CONTRACT_ADMINPORT_UNMAPPED` | 在 compose 發布與 `adminport` 相同的 host port |
78
-
79
- 流程:改檔 → `synopkgland validate` 直到通過。
80
-
81
- ## deploy
82
-
83
- | 碼/訊息 | 下一步 |
84
- | --- | --- |
85
- | `VALIDATION_FAILED` | 先 `validate` |
86
- | `CURATED_DEPLOY_REFUSED` | 用 `--dev` 並行開發,或經管理員確認後 `--replace-curated` |
87
- | `INNER_LOOP_API_MISSING` | 升級 NAS 上 SynoPkgLandService(>=0.1.0-0016);確認 CGI 非 HTML 404 |
88
- | `ATTESTATION_FAILED` | 讀 Broker 訊息;常見 digest/身分衝突,必要時 `info --host` 看 attestation |
89
- | `BUILD_SPK_FAILED` | 修契約/compose 後重試 |
90
- | `CATALOG_PUBLISH_FAILED` | Package Source/網路;`info` 看是否已裝;勿手動拼 SPK 上架 |
91
- | `CATALOG_INSTALL_FAILED`/DSM `broken`/150 | 先跑 `doctor --host <nas> --package <id>`;檢查 published port 碰撞與 `COMPOSE_NUMERIC_COMMAND_ARG_FORBIDDEN` |
92
-
93
- ## info/log/test --host
94
-
95
- | 觀察 | 下一步 |
96
- | --- | --- |
97
- | `NOT_INSTALLED`/log 說先 deploy | `deploy`(或 `--dev`) |
98
- | Readiness 非 RUNNING | `log --host` 看容器;Container Manager 深連結(勿假設使用者懂 Project) |
99
- | `test --host` Open smoke 失敗 | `info` 的 `openUrl`、registration port;identity 任務見 broker 日誌 |
100
- | Attestation 為 Curated 但要改本機 | 不要預設覆蓋;`--dev` 或顯式 `--replace-curated` |
101
-
102
- ## 支援 bundle
103
-
104
- `synopkgland log --host` 結尾 diagnostics 已遮罩 secret;可複製給人類或 agent,**不要再貼 DSM 密碼**。
7
+ Never work around a failed permission check by running the package as root. Native CGI authentication and root-operation administrator approval are separate checks.
@@ -1,47 +1,11 @@
1
1
  # 檔案(Files)
2
2
 
3
- ## 預設(私人 App)
3
+ 管理員在安裝時選 Share,並指定 Container Run-as 的 DSM 使用者。容器路徑依 Compose/映像的 upstream target;只替換 host source,保留 target 與 ro/rw。固定 UID/GID 與 supplementary groups 決定整個服務的檔案存取,並非目前瀏覽器使用者的 DSM ACL。
4
4
 
5
- Personal App **預設較窄**:
5
+ App Data 使用 package var;外部 Share 與 named volume 各有自己的備份及生命週期。不要更改上游 target 以配合範例,不要對現有資料 chown。選擇另一個 run-as 使用者不會自動轉移舊檔案所有權。
6
6
 
7
- 1. Compose 管理的 App Data(`${SYNOPKG_PKGVAR}` 等平台路徑)。
8
- 2. 管理員在安裝時用 Package Center 原生 Share 選擇器,從**這台 NAS**挑 **Shared Folder Mount**(`install.<NAME>.type: shared_folder`)。有 Share 時還要選一個 DSM 使用者當 **Container Run-as**(`install.<NAME>.type: dsm_user`)。容器路徑跟 Compose/映像的 upstream target 走;包現成映像時尤其不要改成平台範例的 `/data`,也不要把 upstream 的 `user: "0:0"` 留著去寫 Share。
7
+ `posix_all_shares` 僅支援 CLI deploy 的管理員 Share 快照;安裝後新增的 Share 不會自動掛入。一般情況用明確 shared_folder。
9
8
 
10
- ### App Data、App Secret 位置與備份提醒
9
+ App 自行保存敏感設定;此工具不提供自動 secret 產生/注入,也沒有廣域 files.* 能力。逐登入者的 NAS 檔案 bridge 尚未實作,須另行設計可信任身份交接及使用者授權。
11
10
 
12
- 在 Synology NAS 上,資料與生命週期分為三類:
13
-
14
- - **App Data**:位於 SPK 原生 package `var`(`/var/packages/<package>/var` → `/volume?/@appdata/<package>`),保存 App 自身資料庫與設定檔。Normal uninstall 預設保留,只有在 Package Center 卸載時明確勾選「Delete App Data」才會刪除。
15
- - **App Secret**:位於 package `home` 的受限子目錄 `/var/packages/<package>/home/secrets`(權限 0700/0600),由 Compose secret mount 掛載。Normal uninstall 預設保留,重裝同一個 Package Identity 時優先重用同名 secret。
16
- - **Shared Folder Mount**:由 NAS Administrator 在安裝時挑選的 DSM 共用資料夾(`/volume?/<ShareName>`)。寫入權限由 Container Run-as 使用者決定;平台 lifecycle 絕不刪除或修改其內容。
17
-
18
- > **Named Volume 警告**:若第三方 Compose 使用 Docker named volume(如 `volumes: [ "my-data:/data" ]`),其生命週期完全由 Container Manager 管理,**不在** Syno Package Land 平台的保留、備份或清理承諾範圍內。需要持久化時請改用 package `var` 或 Shared Folder Mount。
19
-
20
- ### 包現成 Compose
21
-
22
- - volume mapping 右側的 container target 與 `ro`/`rw` 是映像/upstream 的契約,保留原值;只把左側 host source 換成 `${NAME}`,並在 App Contract 宣告對應的 `shared_folder` 槽。
23
- - 原有 named volume 的 mount 與頂層 `volumes:` 宣告都保留。需要兩個既有 Share 就宣告兩個 `shared_folder` 槽;管理員在安裝時各選一個這台 NAS 的 Share。
24
-
25
- 需要列出/讀寫這台 NAS 上登入者能看到的各個 Share 時,宣告 `files.*` 並走 File Gateway,不要掃 `/volume*`。
26
-
27
- `hello-web` **不**宣告廣域檔案 scope。`hello-files` 只宣告 Shared Folder。
28
-
29
- ## 廣域 `files.read`/`files.write`/`files.delete`
30
-
31
- 僅在使用者**明確要求**且理解風險時才加入;必須:
32
-
33
- 1. 在 App Contract 明示(`integration`/權限模型依模板與 `validate`)。
34
- 2. 用**人話**向 NAS Administrator 說明範圍(可讀寫哪些邏輯路徑、與 Shared Folder 的差異)。
35
- 3. 取得同意後才 `deploy`;**不得**由模板或 skill 默默打開。
36
-
37
- 安裝 wizard 會顯示同意文案;agent 在對話中也要用同樣語氣確認一次。
38
-
39
- ## 開發
40
-
41
- ```bash
42
- synopkgland dev --files-root <本機目錄> # 可選:對應假檔案樹
43
- ```
44
-
45
- ## 下一步
46
-
47
- 契約定案後 → `validate` → `deploy.md`
11
+ 使用 validate 與 inspect 檢查路徑,再依 deploy.md 安裝。不要因檔案存取失敗改用 root。
@@ -1,31 +1,5 @@
1
- # 身分(Identity)
1
+ # Identity boundaries
2
2
 
3
- Personal App 用 **broker-managed** 身分:已登入 DSM 的使用者 Open 時不再輸入另一組平台密碼。
3
+ native-go includes per-package CGI DSM session validation. Preserve it when extending the app, and make app authorization explicit. Do not treat login alone as file permission or root authorization.
4
4
 
5
- ## 開發
6
-
7
- - `synopkgland dev` 啟動 mock Broker;`requireLogin` 等 API 與 NAS 同契約,但 User Context **不能**在 NAS 上使用。
8
- - 範例模板:`hello-web` 已含最小 Browser/Server SDK 整合。
9
-
10
- ## 部署後驗證
11
-
12
- ```bash
13
- synopkgland info --host <nas> [--dev]
14
- synopkgland test --host <nas> [--dev]
15
- ```
16
-
17
- `info` 會顯示 attestation 種類、Open URL、readiness。
18
-
19
- ## Package Identity 與 Instance
20
-
21
- - **正式**:`synology-app.yaml` 的 `package`(例如 `com.example.hello-web`)。
22
- - **開發用**:`{package}.dev`(`synopkgland deploy --dev`);displayname 加「(開發中)」;不得進 Community Repository。
23
-
24
- 若 NAS 上**已有正式 Instance**(Local 或 Curated),邊改邊玩應走 `.dev`,見 `deploy.md`。
25
-
26
- ## 禁止
27
-
28
- - 不要把 DSM 密碼、session cookie、signing key 寫進 repo 或 chat。
29
- - 不要為了繞過登入而在 Compose 硬編碼長期 token。
30
- - 不要把 App 註冊成 Synology SSO Server 的 application;wrap 的 OIDC 只接 Broker facade(`oidc_compat`),見規格 identity §2.3。
31
- - 不要以為 OIDC 登入成功=DSM 檔案 ACL。
5
+ OSS defaults to upstream authentication. A fixed service UID/GID does not identify the browser user. Bare DSM-to-app session handoff and a trusted user-aware file bridge are separate future designs, not implemented features. Optional standards-based login requires an actual provider and app interoperability validation. Do not create a mandatory shared service or invent a completed bridge.
@@ -13,9 +13,7 @@ synopkgland validate # 契約 + Compose 安全與交叉引用
13
13
 
14
14
  必填:`schema`、`package`、`version`、`displayname`、`description`、`maintainer`、`os_min_ver`。
15
15
 
16
- 常見可選:`adminprotocol`、`adminport`、`adminurl`、`integration.web.launch`(`dsm-window` 預設|`new-page`)、`install`(參數型別僅 `string`|`boolean`|`secret`|`shared_folder`|`dsm_user`)、`integration`、`permissions`。有 `shared_folder` 時必須有恰好一個 `dsm_user`,Compose 用 `${NAME_UID}:${NAME_GID}`,不要寫死 `0:0`。wrap 若只能 OIDC:`integration.identity.oidc_compat.redirect_path` 填 App 自己的 callback path,Compose 引用 `${SYNOPKGLAND_OIDC_ISSUER}`/`${SYNOPKGLAND_OIDC_CLIENT_ID}`,secret 讀檔 `SYNOPKGLAND_OIDC_CLIENT_SECRET`。**不要**在契約寫 issuer/domain/env 改名表。
17
16
 
18
- **禁止發明** schema 沒有的 top-level 鍵或 `install` 型別。`integration` 內容以模板與 `validate` 為準,不要自創 Broker DSL。
19
17
 
20
18
  ## Compose 規則(analyzer 會擋)
21
19
 
@@ -0,0 +1,7 @@
1
+ schema: 1
2
+ package: com.example.binary-app
3
+ version: 0.1.0
4
+ displayname: Binary App
5
+ description: A minimal Syno Package Land app that runs one prebuilt binary.
6
+ maintainer: Syno Package Land
7
+ os_min_ver: "7.2-60000"
@@ -3,7 +3,6 @@ services:
3
3
  image: alpine:3.20
4
4
  command: ["sleep", "infinity"]
5
5
  volumes:
6
- # Dynamic Shared Folder plus writable /state is Broker-managed and is
7
6
  # intentionally outside the zero-config docker-project resource-worker path.
8
7
  - "${MEDIA_SHARE}/data:/data:ro"
9
8
  - "${SYNOPKG_PKGVAR}/state:/state"
@@ -1,16 +1,19 @@
1
1
  services:
2
2
  web:
3
3
  image: busybox:1.36
4
- # 兩個平台限制,改動前先看 docs/impl/STATUS.md 的 2026-08-24 段:
5
- # 1) command/entrypoint 陣列不得有純數字元素(例如 "18333")。DSM 的
6
- # docker-project worker 會在 YAML round-trip 時掉引號、把它變成整數,
7
- # postinst acquire 直接失敗(error 276 → 套件 broken/150,且 worker_msg
8
- # 為空,開發者看不到原因)。所以 port 要包在單一字串裡。
9
- # `synopkgland validate` 已會擋這個寫法。
10
- # 2) 預設不用 8080:那個 port 在真實 NAS 上很常被既有容器占用,而 port
11
- # 衝突同樣以那個看不出原因的 broken/150 呈現。
12
- # 官方 nginx image 也不能用:resource worker 注入任意 package UID/GID,
13
- # nginx entrypoint 啟動階段需要 root,會在該身分下直接退出。
4
+ # DSM worker constraints to preserve when changing this service:
5
+ # 1) command/entrypoint arrays must not contain bare numeric elements
6
+ # (such as "18333"). DSM's docker-project worker drops the quotes on a
7
+ # YAML round-trip and turns them into integers, so postinst acquire
8
+ # fails (error 276 -> package broken/150, with an empty worker_msg that
9
+ # tells the developer nothing). Keep the port inside one string.
10
+ # `synopkgland validate` already rejects the bare form.
11
+ # 2) The default is not 8080: that port is often taken by an existing
12
+ # container on a real NAS, and the conflict shows up as the same
13
+ # unexplained broken/150.
14
+ # The official nginx image cannot be used either: the resource worker
15
+ # injects an arbitrary package UID/GID, and the nginx entrypoint needs
16
+ # root during startup, so it exits immediately under that identity.
14
17
  command: ["sh","-c","httpd -f -p 18333 -h /usr/share/nginx/html"]
15
18
  ports:
16
19
  - "18333:18333"
@@ -1,23 +1 @@
1
- import { createSynologyClient } from "./sdk.js";
2
-
3
- const status = document.getElementById("status");
4
- const userEl = document.getElementById("user");
5
-
6
- const client = createSynologyClient();
7
-
8
- try {
9
- await client.auth.requireLogin();
10
- const user = await client.auth.getCurrentUser();
11
- if (status) {
12
- status.textContent = "Signed in with DSM";
13
- }
14
- if (userEl) {
15
- userEl.textContent = user.username;
16
- }
17
- } catch (error) {
18
- const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
19
- const message = error instanceof Error ? error.message : String(error);
20
- if (status) {
21
- status.textContent = code === "UNAUTHENTICATED" ? "DSM login required" : message;
22
- }
23
- }
1
+ document.getElementById("status").textContent = "Your app is running.";
@@ -7,9 +7,7 @@
7
7
  </head>
8
8
  <body>
9
9
  <h1>Hello Web</h1>
10
- <p id="status">Opening with your DSM session…</p>
11
- <p id="user"></p>
12
- <script src="./bootstrap.js"></script>
10
+ <p id="status">Loading…</p>
13
11
  <script type="module" src="./app.js"></script>
14
12
  </body>
15
13
  </html>
@@ -1,16 +1,19 @@
1
1
  services:
2
2
  web:
3
3
  image: busybox:1.36
4
- # 兩個平台限制,改動前先看 docs/impl/STATUS.md 的 2026-08-24 段:
5
- # 1) command/entrypoint 陣列不得有純數字元素(例如 "18333")。DSM 的
6
- # docker-project worker 會在 YAML round-trip 時掉引號、把它變成整數,
7
- # postinst acquire 直接失敗(error 276 → 套件 broken/150,且 worker_msg
8
- # 為空,開發者看不到原因)。所以 port 要包在單一字串裡。
9
- # `synopkgland validate` 已會擋這個寫法。
10
- # 2) 預設不用 8080:那個 port 在真實 NAS 上很常被既有容器占用,而 port
11
- # 衝突同樣以那個看不出原因的 broken/150 呈現。
12
- # 官方 nginx image 也不能用:resource worker 注入任意 package UID/GID,
13
- # nginx entrypoint 啟動階段需要 root,會在該身分下直接退出。
4
+ # DSM worker constraints to preserve when changing this service:
5
+ # 1) command/entrypoint arrays must not contain bare numeric elements
6
+ # (such as "18333"). DSM's docker-project worker drops the quotes on a
7
+ # YAML round-trip and turns them into integers, so postinst acquire
8
+ # fails (error 276 -> package broken/150, with an empty worker_msg that
9
+ # tells the developer nothing). Keep the port inside one string.
10
+ # `synopkgland validate` already rejects the bare form.
11
+ # 2) The default is not 8080: that port is often taken by an existing
12
+ # container on a real NAS, and the conflict shows up as the same
13
+ # unexplained broken/150.
14
+ # The official nginx image cannot be used either: the resource worker
15
+ # injects an arbitrary package UID/GID, and the nginx entrypoint needs
16
+ # root during startup, so it exits immediately under that identity.
14
17
  command: ["sh","-c","httpd -f -p 18333 -h /usr/share/nginx/html"]
15
18
  ports:
16
19
  - "18333:18333"
@@ -1,23 +1 @@
1
- import { createSynologyClient } from "./sdk.js";
2
-
3
- const status = document.getElementById("status");
4
- const userEl = document.getElementById("user");
5
-
6
- const client = createSynologyClient();
7
-
8
- try {
9
- await client.auth.requireLogin();
10
- const user = await client.auth.getCurrentUser();
11
- if (status) {
12
- status.textContent = "Signed in with DSM";
13
- }
14
- if (userEl) {
15
- userEl.textContent = user.username;
16
- }
17
- } catch (error) {
18
- const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
19
- const message = error instanceof Error ? error.message : String(error);
20
- if (status) {
21
- status.textContent = code === "UNAUTHENTICATED" ? "DSM login required" : message;
22
- }
23
- }
1
+ document.getElementById("status").textContent = "Your app is running.";
@@ -7,9 +7,7 @@
7
7
  </head>
8
8
  <body>
9
9
  <h1>Hello Web Page</h1>
10
- <p id="status">Opening with your DSM session…</p>
11
- <p id="user"></p>
12
- <script src="./bootstrap.js"></script>
10
+ <p id="status">Loading…</p>
13
11
  <script type="module" src="./app.js"></script>
14
12
  </body>
15
13
  </html>