@synopackageland/cli 0.10.1 → 0.10.3

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 (68) hide show
  1. package/CHANGELOG.md +219 -1669
  2. package/README.md +21 -2
  3. package/assets/login-style/mask.png +0 -0
  4. package/assets/login-style/wallpaper-thumbnail.jpg +0 -0
  5. package/assets/login-style/wallpaper.jpg +0 -0
  6. package/dist/build-spk/generator.js +10 -1
  7. package/dist/build-spk/login-style.d.ts +55 -0
  8. package/dist/build-spk/login-style.js +115 -0
  9. package/dist/cli.js +22 -1
  10. package/dist/error-registry.js +37 -0
  11. package/dist/host/dsm-client.js +6 -9
  12. package/dist/init.js +9 -5
  13. package/dist/runtime/delegate-contract.d.ts +2 -92
  14. package/dist/runtime/delegate-contract.js +2 -24
  15. package/dist/runtime/executor-build.js +10 -0
  16. package/dist/runtime/executor-digests.json +22 -0
  17. package/dist/runtime/executor-inspect.js +3 -0
  18. package/dist/runtime/executor-policy-check.d.ts +14 -0
  19. package/dist/runtime/executor-policy-check.js +70 -0
  20. package/dist/runtime/executor-policy.d.ts +2 -0
  21. package/dist/runtime/executor-policy.js +5 -1
  22. package/dist/runtime/root-programs.d.ts +2 -0
  23. package/dist/runtime/sdk-source.d.ts +4 -0
  24. package/dist/runtime/sdk-source.js +7 -0
  25. package/dist/runtime/sdk-timeouts.d.ts +23 -0
  26. package/dist/runtime/sdk-timeouts.js +44 -0
  27. package/dist/skill.js +1 -0
  28. package/dist/standalone/app-scripts.d.ts +26 -0
  29. package/dist/standalone/app-scripts.js +141 -0
  30. package/dist/standalone/bridge-app.d.ts +2 -0
  31. package/dist/standalone/bridge-app.js +9 -1
  32. package/dist/standalone/bridge-backend.d.ts +1 -1
  33. package/dist/standalone/build.js +5 -2
  34. package/dist/standalone/dsm-window.d.ts +14 -3
  35. package/dist/standalone/dsm-window.js +34 -12
  36. package/dist/standalone/host-adapter.d.ts +1 -1
  37. package/dist/standalone/host-adapter.js +1 -1
  38. package/dist/standalone/host-file-navigation.d.ts +6 -2
  39. package/dist/standalone/host-file-navigation.js +8 -2
  40. package/dist/standalone/inspect-spk.d.ts +9 -0
  41. package/dist/standalone/inspect-spk.js +60 -6
  42. package/dist/standalone/lifecycle.js +3 -1
  43. package/dist/standalone/manifest.d.ts +6 -0
  44. package/dist/standalone/manifest.js +51 -3
  45. package/dist/standalone/portal-window.d.ts +6 -6
  46. package/dist/standalone/portal-window.js +12 -32
  47. package/dist/standalone/request-timeout.d.ts +6 -3
  48. package/dist/standalone/request-timeout.js +10 -4
  49. package/dist/standalone/root-ops.d.ts +3 -2
  50. package/dist/standalone/root-ops.js +9 -3
  51. package/dist/standalone/root-window.d.ts +2 -2
  52. package/dist/standalone/root-window.js +173 -49
  53. package/dist/standalone/sdk-check.js +4 -1
  54. package/dist/standalone/spk.d.ts +3 -0
  55. package/dist/standalone/spk.js +10 -3
  56. package/dist/validate.js +6 -1
  57. package/package.json +2 -3
  58. package/skills/SKILL.md +3 -1
  59. package/skills/reference/create.md +5 -1
  60. package/skills/reference/identity.md +1 -1
  61. package/skills/reference/lifecycle.md +110 -0
  62. package/skills/reference/portal.md +12 -0
  63. package/dist/authorization-sdk.d.ts +0 -6
  64. package/dist/authorization-sdk.js +0 -6
  65. package/dist/standalone/native-authorization-window.d.ts +0 -2
  66. package/dist/standalone/native-authorization-window.js +0 -36
  67. package/dist/standalone/native-root-window.d.ts +0 -29
  68. package/dist/standalone/native-root-window.js +0 -307
package/CHANGELOG.md CHANGED
@@ -1,1674 +1,224 @@
1
1
  # Changelog
2
2
 
3
- ## 0.10.1 (2026-10-13)
4
-
5
- Builds with SDK 0.10.1 and templates 0.10.1: `init` writes SDK v0.10.1 (Go
6
- module, Rust crate tag and `backend.sdk`) and `@synopackageland/sdk` 0.10.1.
7
- The executor digest table adds SDK 0.10.1 and keeps 0.10.0. SDK 0.10.1's
8
- executor changed, so an App rebuilt on it carries a new executor digest and
9
- its pins are approved again; the lifecycle digests equal 0.10.0. The bridge
10
- manifest schema is synced from the SDK (`capabilities.root` also serves
11
- `executor.status` and `executor.control`).
12
-
13
- 繁中摘要:使用 SDK 0.10.1 與 templates 0.10.1;digest 表新增 SDK 0.10.1(保留
14
- 0.10.0)。executor 變更,改用 SDK 0.10.1 重建的 App 要重新核准 executor digest,
15
- lifecycle digest 與 0.10.0 相同。
16
-
17
- - A Python, Node.js or Rust App with root-ops and a Vue UI borrows
18
- `native-go-root-ops/frontend`, as a Go App gets it from its overlay: its
19
- `ExecutorGate.vue` holds the App's pages until an administrator approves
20
- the executor (templates 0.10.1).
21
- - Fix: in a DSM window with root operations (`capabilities.rootOps`, and so a
22
- `portal` App), the App root now fills the window body below the root panels.
23
- It had no height, so an App root asking for `height:100%` collapsed to its
24
- content: a loading overlay covered only the top of the window, and a dialog
25
- centred on the App root was placed above the window. Windows without root
26
- operations are unchanged.
27
-
28
- ## 0.10.0 (2026-10-13)
29
-
30
- Builds with SDK 0.10.0 and templates 0.10.0: `init` writes SDK v0.10.0 (Go
31
- module, Rust crate tag and `backend.sdk`), `@synopackageland/sdk` 0.10.0 and
32
- `@synopackageland/ui` 0.9.4, and the Vue templates use the SDK's `AppShell`.
33
- CLI 0.10 builds only SDK 0.10.x: the executor digest table records SDK
34
- 0.10.0 and no longer lists 0.9.x, so an App on SDK 0.9.x is refused
35
- (`SDK_UNSUPPORTED`) and stays on CLI 0.9.7. SDK 0.10.0's executor changed, so
36
- a rebuilt App carries new executor and lifecycle digests and its pins are
37
- approved again; the policy digest changes only for a policy that uses root
38
- program settings, jobs, `stop` or `state`.
39
-
40
- 繁中摘要:使用 SDK 0.10.0 與 templates 0.10.0(UI 0.9.4),Vue 範本改用 SDK 的
41
- `AppShell`;新增 Python、Node、Rust App(bridge runtime)、root programs 設定、
42
- `ui.window`、`backend.timeout_seconds`(預設 60 秒),CLI 輸出全改英文。CLI 0.10
43
- 只接受 SDK 0.10.x,digest 表移除 0.9.x;executor 變更,重建的 App 要重新核准
44
- executor 與 lifecycle digest。
45
-
46
- - Breaking: every CLI message is English. Error descriptions and suggestions,
47
- `--help` text, and check, inspect, build and deploy output no longer carry
48
- Chinese. Only the multilingual data shipped to DSM end users keeps its
49
- translations: the install/upgrade/uninstall wizard copy, the App UI catalogs
50
- and the INFO `displayname`/`description` translations. A test now fails on
51
- CJK in any other `src/` file.
52
- - Behavior change: every App's backend request deadline is 60 seconds instead
53
- of the SDK's 20. `bridge.json` always carries `timeout_seconds`, so the
54
- runtime's deadline and the `deadline_ms` it sends the App follow it; a Go
55
- CGI gets it as `-X main.requestTimeout` and keeps the SDK's 20 seconds
56
- until its main reads that variable into `api.Timeout`. The DSM window sets
57
- `ctx.requestTimeout` to the deadline and gives up two seconds past it (62
58
- seconds instead of 22), also for a prebuilt binary CGI.
59
- - `backend.timeout_seconds` (1–3600) sets another deadline for go, python,
60
- node and rust backends. A Go main that does not read `requestTimeout` into
61
- `api.Timeout` is refused (`STANDALONE_TIMEOUT_UNSUPPORTED`); a binary
62
- backend refuses it.
63
- - `ui.window: {width, height, minWidth, minHeight}` sets the DSM window's
64
- size; omitted fields keep 960×640, at least 700×420, and a standalone
65
- launch keeps its 320 px minimum width. Sizes outside 320–3840 × 240–2160,
66
- or a minimum larger than the size, are refused
67
- (`STANDALONE_UI_WINDOW_INVALID`).
68
- - The generated DSM window knows a standalone (Portal) launch,
69
- `_S("standalone") === true`: its minimum width drops from 700 to 320 px so
70
- a phone can show it, and `mount` gets `ctx.fullPage` and `ctx.appIcon` (the
71
- package's 64 px window icon) for a full-page layout and brand.
72
- - `init -t native-python | native-node | native-rust` creates the native-go
73
- App with a Python, Node.js (TypeScript) or Rust backend: the Go template's
74
- UI, frontend and root program, the language's capability overlays for
75
- `--with jobs,root-ops,delegate` (jobs also declares `files: session`, which
76
- its example reads; delegate opens `app.status` and `report.save` to
77
- signed-in users; Rust lists the added methods in `backend.methods`), and the
78
- App SDK its tests import in `backend/vendor`. `--shared-folder` stays
79
- native-go only. Capability overlays carry their permission rules.
80
- - Breaking: `backend.kind: rust` (the Rust full SDK) is removed, and
81
- `init -t native-rust` now creates the thin Rust App behind the bridge
82
- runtime instead.
83
- - `backend.runtime: python | node | rust` builds an App in another language
84
- behind the SDK's Go runtime (spec/bridge/v1). `build` compiles
85
- `bridge/cmd/runtime` of the App's SDK release as `ui/cgi/api.cgi` with the
86
- sha256 of `bridge.json` and `permissions.json` bound, and ships the App in
87
- `target/app`: Python with the SDK in `vendor/`, Node with the SDK in
88
- `node_modules/@synopackageland/app` and an `install_dep_packages` on
89
- Package Center's Node.js, Rust cross-compiled with `cargo zigbuild`.
90
- Methods come from `backend.methods` or the App's `--describe`. `inspect`
91
- checks the manifest schema, the digests, the entry, that the App is not
92
- under `ui/`, and the Node dependencies. The `bridge` gate builds all three
93
- for every architecture.
94
- - Bridge Apps may declare `jobs`, `rootOps`, `portal` and an
95
- `until-revoked` `delegate` for `files.moveFromStaging`, plus
96
- `backend.transfers` and `backend.max_upload_bytes`; `bridge.json` carries
97
- them. With jobs or delegate the runtime is also installed as
98
- `target/background/worker` and DSM runs it with `--worker` as the package
99
- account. `inspect` refuses delegate digests that are not the SPK
100
- executor's. The Node SDK is copied as its `package.json` and `dist/`, so
101
- the SDK checkout must be built.
102
- - `root` in dsm-package.yaml configures root programs for the executor's
103
- root programs, root jobs, package stop step and root state directory:
104
- `programs.<name>` gives a params `schema` and `run` or `job` limits,
105
- `kind: binary` takes `root/<arch>/<name>` (an ELF for each architecture,
106
- so the policy digest differs per architecture), `stop` names the program
107
- the executor runs at package stop and upgrade, and `state: true` asks for
108
- the root state directory. Programs without settings keep the bare digest,
109
- so existing policies and their digests do not change. Jobs add
110
- `root.start` and the `root.jobs` operations. A build whose SDK executor
111
- predates these refuses them (`EXECUTOR_ROOT_PROGRAMS_UNSUPPORTED`).
112
- - `preupgrade` stops the executor with `executor/lifecycle stop --upgrade`;
113
- an installed helper without the flag gets a plain stop.
114
-
115
- - The DSM window icons are named after their content,
116
- `images/app_<hash>_<size>.png`, and `ui/config` points at that name. DSM and
117
- the browser cache an App icon by its URL, so after an icon change DSM kept
118
- showing the old one (the blue square in t-0391) until the cache expired; a
119
- new icon now has a new URL, and an unchanged icon keeps its cached one. DSM
120
- 7.4 passes the config's icon path, with `{0}` for the size, to
121
- `SYNO.Core.Synohdpack getHDIcon` for every `webman/3rdparty/` App, so any
122
- file name works.
123
- - `ctx.call(method, params, {signal})` aborts its fetch when the signal does,
124
- so a page the user leaves really stops its request, and rejects
125
- `{code: "request_canceled"}`. It also gives up after the SDK's
126
- `clientTimeout(ctx.requestTimeout)`, two seconds past the backend deadline
127
- (the backend deadline unless the App sets `ctx.requestTimeout` in ms, read
128
- on every call), aborts the fetch and rejects `{code: "request_timeout"}`.
129
- Before, a call waited as long as the browser did. The portal and root
130
- panels use the same call.
131
- - preuninst waits for the App's uninstall hook by looking it up in `/proc`
132
- instead of `kill -0`. DSM's bash logs a failed builtin to syslog even with
133
- `2>/dev/null`, so every uninstall with a hook logged `BASH_ERR ... kill: No
134
- such process` twice (t-0383 B8). The wait, the timeout and the result are
135
- unchanged; a host without `/proc` still uses `kill -0`.
136
- - preuninst stops an uninstall hook past its timeout, and prestart waits for
137
- a stale package user manager to exit, without a `kill` that can fail: the
138
- hook's process group and the manager are looked up in `/proc` first, so
139
- neither logs `BASH_ERR ... kill: No such process` once the process has
140
- exited.
141
- - The Application Portal panel's Italian instructions use the infinitive like
142
- DSM ("Riprovare o usare il Pannello di controllo."), the same text as the
143
- SDK's `i18n/portal` catalogs.
144
- - `ctx.portal.status()` waits for a register or unregister in progress to
145
- end, whatever its result, like the SDK's `createPortalAccess`. Before, a
146
- status read during a slow change could show "The Application Portal entry
147
- was not fully removed".
148
-
149
- ## 0.9.7 (2026-10-13)
150
-
151
- Builds with SDK 0.9.8 and templates 0.9.7: `init` writes SDK v0.9.8 (Go
152
- module and Rust crate tag), `@synopackageland/sdk` 0.9.8 and
153
- `@synopackageland/ui` 0.9.2. SDK 0.9.8 fixes `BackgroundPanel`, whose controls
154
- to choose a file and submit it were hidden on DSM. The executor digest table
155
- records SDK 0.9.8 with the same executor and lifecycle digests as 0.9.7, and
156
- the policy digest does not change, so a rebuilt App keeps the pins it was
157
- approved with.
158
-
159
- 繁中摘要:使用 SDK 0.9.8 與 templates 0.9.7(UI 0.9.2)。SDK 0.9.8 修正
160
- `BackgroundPanel` 在 DSM 上看不到選檔與送出按鈕。digest 表加入 SDK 0.9.8,
161
- executor 與 lifecycle digest 與 0.9.7 相同,policy digest 也不變,重建的 App
162
- 沿用原本核准的 pins。
163
-
164
- ## 0.9.6 (2026-10-12)
165
-
166
- Builds with SDK 0.9.7 and templates 0.9.6: `init` writes SDK v0.9.7 (Go
167
- module and Rust crate tag), `@synopackageland/sdk` 0.9.7 and
168
- `@synopackageland/ui` 0.9.2. The executor digest table records SDK 0.9.7,
169
- whose executor and lifecycle binary changed, so a rebuilt App carries new
170
- executor digests. The policy digest changes only for an App that declares
171
- `uninstall.purge.root`, and such an App asks for approval again after the
172
- upgrade; `bg.jobs.pause` and `bg.jobs.resume` reach the CGI route table, not
173
- the policy. Whether new executor digests alone ask for approval again has not
174
- been checked on a DSM yet.
175
-
176
- 繁中摘要:使用 SDK 0.9.7 與 templates 0.9.6(UI 0.9.2)。executor digest 表加入
177
- SDK 0.9.7;executor 與 lifecycle 都有變更,重建的 App 會帶新的 executor digest。
178
- 只有宣告 `uninstall.purge.root` 的 App 的 policy digest 會變,升級後需要重新核准;
179
- `bg.jobs.pause`、`bg.jobs.resume` 只進 CGI route 表,不影響 policy。只換 executor
180
- digest 時是否需要重新核准,尚未在 DSM 上驗證。新增解除安裝 hook(`uninstall.purge`)。
181
-
182
- - Uninstall hook: `uninstall.purge` in dsm-package.yaml (`exec`, `args`,
183
- `env`, `timeout` 2..120 s, optional `root`) names the App's own command,
184
- and `uninstall.state.items` lists the state it can delete, each with an
185
- `uninstall.state.<key>` text. On a real uninstall preuninst stops the
186
- worker and the daemon, runs the hook as the package account with
187
- `SYNOPKGLAND_DELETE_STATE` and `SYNOPKGLAND_REMOVE_DATA`, then asks the
188
- executor to purge; a failing or timed-out hook is logged and never blocks
189
- the uninstall, and an upgrade skips it. The wizard gains an unchecked
190
- "delete state" box after the item texts. `purge.root` names a rootOps
191
- script the executor runs as root only while its approval stands; the
192
- policy's `purge` member records it, so such an App asks for approval
193
- again. Build refuses a hook outside the package or one that tries to
194
- become root. inspect checks an executor SPK's preuninst and wizard against
195
- the hook recorded in sdk-package.json, and that record's root step against
196
- the policy's `purge`.
197
- - `init` and `add` change only the dsm-package.yaml keys they set
198
- (`capabilities`, `portal`, `os_min_ver`); the template's comments, such as
199
- the commented `uninstall` example, and its formatting stay as written.
200
- - With `SYNOPKGLAND_SDK_DIR`, init also drops the released SDK from the
201
- frontend's package-lock.json, so npm installs the local ts package.
202
-
203
- ## 0.9.5 (2026-10-12)
204
-
205
- Builds with SDK 0.9.6 and templates 0.9.5: `init` writes SDK v0.9.6,
206
- `@synopackageland/sdk` 0.9.6 and `@synopackageland/ui` 0.9.2. The executor
207
- digest table records SDK 0.9.6, whose executor registers the portal entry
208
- and checks a grant's background work from the browser address it recorded,
209
- so a rebuilt App carries new executor digests. The policy digest changes
210
- only for an App whose executor registers its portal entry (a portal and
211
- os_min_ver below 7.2): after the upgrade it asks for approval again. Other
212
- Apps keep their policy digest.
213
-
214
- 繁中摘要:使用 SDK 0.9.6 與 templates 0.9.5(UI 0.9.2)。executor digest 表加入
215
- SDK 0.9.6,重建的 App 會帶新的 executor digest。只有由 executor 註冊入口的
216
- portal App(宣告 portal 且 os_min_ver 低於 7.2)policy digest 會變,升級後需重新
217
- 核准;其他 App 的 policy digest 不變。DSM 7.1 起可用 portal(executor 模式)。
218
-
219
- - The generated DSM window stays inside the desktop: its width is at most the
220
- viewport's and its height at most the viewport's less the DSM taskbar. This
221
- applies to the default 960×640 and to the size DSM restores from
222
- `restoreSizePos`, which may have been saved on a larger screen. Position and
223
- maximized state are restored as before.
224
- - Native root authorization (`createRootAccess` from
225
- `@synopackageland/cli/authorization-sdk`): the first `run` after an `authorize`
226
- runs the task that `authorize` just read back as exactly ours, instead of
227
- reading the TaskScheduler list and task again. Any later run, a repair, and
228
- a run after `remove` still read DSM first; `authorize` still reads the task
229
- back after creating it and checks its identity and script.
230
- - Native root authorization polls the task history every 0.5 s, then at most
231
- once a second (was up to every 2 s), so a finished root task is seen up to
232
- 1 s sooner.
233
- - A portal works from DSM 7.1-42661 on. Below 7.2-64570 (executor mode) the
234
- SPK carries no web-config resource, `etc/portal.*`, alias wizard step or
235
- alias lifecycle; the executor policy carries `portal: {application, alias}`
236
- and the `portal.register` / `portal.unregister` operations, and the CGI is
237
- built with `-X main.portal=executor`. A package name or displayname the
238
- executor would refuse fails with `STANDALONE_PORTAL_ENTRY_INVALID`; below
239
- 7.1 `STANDALONE_PORTAL_MIN_DSM` still refuses the portal, and `add portal`
240
- raises os_min_ver only to 7.1-42661. inspect reports the policy's entry
241
- and flags an INFO os_min_ver below 7.1. Needs templates 0.9.5 and SDK 0.9.6.
242
- - `ctx.portal` in executor mode: `status()` carries `entry`, administrators
243
- get `register()` and `unregister()`, and the plain panel offers "Enable
244
- entry", "Remove entry" or the alias form by entry state. New texts in nine
245
- languages, including `PORTAL_FAILED`. The panel's Korean texts use the
246
- formal -십시오 register.
247
- - The uninstall wizard and the cleanup list of an executor-mode portal App
248
- warn that a stopped or never-approved executor leaves the entry, and give
249
- the root cleanup commands.
250
- - native-layout: `portal: {}` or `{alias}` (DSM 7.1-42661+) writes
251
- `synopkgland_portal_alias` into INFO for the product's own root helper;
252
- inspect checks it against `native-layout.json`.
253
- - DSM's web-config portal registration now needs `os_min_ver` 7.2-64570 or
254
- later instead of 7.3.2-86009. DSM 7.1's package manager marks the web-config
255
- worker Synology-only and refuses it in an unsigned SPK (error 320); synopkg
256
- from DSM 7.2 on drops that mark, the only change to its worker table. The
257
- 7.2 floor comes from that source and is not yet verified on a 7.2 NAS.
258
- inspect flags a web-config portal SPK whose INFO os_min_ver is lower, with
259
- this reason.
260
-
261
- ## 0.9.4 (2026-10-12)
262
-
263
- Builds with SDK 0.9.5 and templates 0.9.4: `init` writes SDK v0.9.5 and
264
- `@synopackageland/sdk` 0.9.5, and the Vue App's `AppRoot` lets Dialogs leave
265
- a DSM window. The executor digest table records SDK 0.9.5, whose executor
266
- equals 0.9.4's; the policy digest stays the same, so an installed App needs
267
- no new approval. AI guidance for DSM-window Dialogs matches the Vue App.
268
-
269
- 繁中摘要:使用 SDK 0.9.5 與 templates 0.9.4。SDK 0.9.5 只改 TypeScript(NAS
270
- picker 改用 UI 0.9.1 元件),executor digest 與 0.9.4 相同,policy digest 不變,
271
- 已安裝的 App 不需重新核准。ui-vue 與 ui-json 指引改為在 DSM 視窗內給 `AppRoot`
272
- `dialog-bounds="viewport"`,並把 `activate` 接到 `ctx.host.activateWindow()`:
273
- Dialog 可拖出 App 視窗,點 Dialog 會把視窗帶到最前;600px 以下為底部 sheet。
274
-
275
- - `ui-vue` and `ui-json`: in a DSM window (`ctx.host.platform().host === 'dsm'`),
276
- give `AppRoot` `dialog-bounds="viewport"` and connect its `activate` event to
277
- `ctx.host.activateWindow()`, so Dialogs can be dragged out of the window and
278
- pressing one brings the window to the front. Below 600px they are bottom
279
- sheets. Standalone web, Portal and previews keep Dialogs inside the App.
280
- - A host-window test pins that the mounted App's `activateWindow` fronts only
281
- its own DSM window.
282
-
283
- ## 0.9.3 (2026-10-12)
284
-
285
- Builds with SDK 0.9.4 and templates 0.9.3: `init` writes SDK v0.9.4 and
286
- `@synopackageland/sdk` 0.9.4. The executor digest table records SDK 0.9.4,
287
- whose executor asks DSM's native `SLIBAppPrivUserHas` for App Privileges
288
- instead of the `SYNO.Core.AppPriv.App` preview. An App rebuilt with SDK 0.9.4
289
- gets the new executor; the policy digest stays the same, so an installed App
290
- needs no new approval.
291
-
292
- 繁中摘要:使用 SDK 0.9.4 與 templates 0.9.3。executor 改以 DSM 原生
293
- `SLIBAppPrivUserHas` 判斷應用程式權限,digest 表加入 0.9.4;policy digest 不變,
294
- 重新建置後已安裝的 App 不需重新核准。限定 IP 的應用程式權限規則改為依瀏覽器位址
295
- 判斷;自行組 `auth` 的呼叫端必須帶 `remote_addr`,否則 executor 回 `APP_DENIED`。
296
-
297
- - Behavior change: an Application Privileges rule limited to IP addresses
298
- applies to the browser's address; with SDK 0.9.3 or earlier the executor
299
- refused every request under such a rule. Work no browser asked for is
300
- checked as coming from 127.0.0.1. The generated CGI sends the address
301
- through `api.DSMSession`; code that builds the executor `auth` itself must
302
- add `remote_addr` or the executor refuses with `APP_DENIED`.
303
- - The fake-DSM executor flow tests host the 0.9.4 executor's embedded bridge
304
- and check the online grant against the browser's address and the offline
305
- write against 127.0.0.1.
306
-
307
- ## 0.9.2 (2026-10-12)
308
-
309
- Builds with SDK 0.9.3 and templates 0.9.2: `init` writes SDK v0.9.3 and
310
- `@synopackageland/sdk` 0.9.3. The executor digest table records SDK 0.9.2 and
311
- 0.9.3 (the same digests; 0.9.3 changes only translations). An App rebuilt
312
- with SDK 0.9.2 or 0.9.3 gets the new executor; the policy digest stays the
313
- same, so an installed App needs no new approval.
314
-
315
- 繁中摘要:`check` 與 `build` 的錯誤訊息每個錯誤碼只出現一次;`build --json` 的 SDK
316
- 下載失敗附上 GOPRIVATE 提示;有 executor 並宣告 `install.sharedFolder` 的 App 通過
317
- `inspect` 的 postinst 檢查。重新安裝後的核准只需輸入一次密碼:共用資料夾讀回等 40 s,
318
- 沒到達 executor 的選擇任務在同一次核准內再跑一次,啟動後的健康檢查等 30 s。
319
-
320
- - `check` and `build` name each error code once. 0.9.1 printed the first code
321
- twice, for example
322
- `[STANDALONE_PORTAL_REQUIRES_EXECUTOR] [STANDALONE_PORTAL_REQUIRES_EXECUTOR] …`.
323
- - A `--json` error without its own hint takes the registry suggestion of its
324
- code. `build --json` now answers `SDK_DOWNLOAD_FAILED` with the GOPRIVATE and
325
- git-credential step instead of the generic hint.
326
- - `inspect` accepts an App with `capabilities.rootOps` or `delegate` that
327
- declares `install.sharedFolder`. Its `postinst` and `postupgrade` start the
328
- executor before the shared-folder script, and 0.9.1 reported them as not
329
- matching.
330
- - Re-approval after a reinstall takes one password submission for both the
331
- executor and an install-selected share. The DSM window now reads the share
332
- selection back for 40 s instead of 20 s, runs a selection task that missed
333
- the restarting executor once more without a new confirmation, and waits
334
- 30 s instead of 10 s for a started executor before withdrawing the tasks an
335
- approval created.
336
-
337
- ## 0.9.1 (2026-10-12)
338
-
339
- Fixes from the 0.9.0 acceptance on DSM (t-0294, t-0290, t-0292).
340
-
341
- 繁中摘要:`build` 重複 `--arch` 時每個架構都建置;SDK 下載失敗回報
342
- `SDK_DOWNLOAD_FAILED`(不再包成 `STANDALONE_API_PERMISSIONS_INVALID`),`check`
343
- 顯示每個錯誤碼自己的建議;`status` 列出 portal;Vue App 只在 SDK 的
344
- ExecutorSettings 核准,plain UI 的 DSM 視窗面板改為有樣式、跟隨 DSM 語言並在面板內
345
- 輸入密碼。更正 0.9.0 的核准說明:policy 不變的升級不需重新核准,只有
346
- install-selected 的 scope 需要管理員確認一次。記錄 SDK 0.9.1 executor digests(只換
347
- executor、policy digest 不變,免重新核准)。App 分兩層:有 executor(rootOps 或
348
- delegate)的 App 解除安裝時自動移除應用程式權限規則,宣告 portal 時再 reload nginx,
349
- 精靈與 cleanup 清單只把它們列為自動清除失敗時的後備;無升權 App 任何階段都不用 root,
350
- 應用程式權限規則仍由管理員手動移除。破壞性變更:portal 只限 executor 層級,無升權
351
- native App 以 `STANDALONE_PORTAL_REQUIRES_EXECUTOR` 拒絕,Compose App 以
352
- `CONTRACT_PORTAL_REQUIRES_EXECUTOR` 拒絕。並附 0.9.0 安裝殘留的清理步驟。
353
-
354
- ### Build and check
355
-
356
- - `build --arch x86_64 --arch aarch64` builds every family named; 0.9.0 built
357
- only the last one. Repeated values and comma lists combine.
358
- - An SDK version Go cannot fetch is reported as `SDK_DOWNLOAD_FAILED` with the
359
- GOPRIVATE and git-credential hint. 0.9.0 wrapped it as
360
- `STANDALONE_API_PERMISSIONS_INVALID`.
361
- - `check` shows the registry suggestion of each error code. It no longer
362
- replaces every `dsm-package.yaml` error's suggestion with "fix the schema 2
363
- fields".
364
- - `STANDALONE_ENTRY_UNSUPPORTED` names the `main.go` of the project's
365
- `backend.main`, not always `cmd/app/main.go`.
366
-
367
- ### Status
368
-
369
- - `status` lists `portal` for an App with `portal: {}`. It reads the App's
370
- `portal.status`: `ready` with the URL when an administrator set an alias,
371
- `needs-admin-approval` when none is set, and `blocked` before install or
372
- when the status cannot be read.
373
-
374
- ### DSM window approval
375
-
376
- - A Vue App approves the executor only in the SDK's `ExecutorSettings`. The
377
- DSM window no longer adds its own approval panel to a Vue App with
378
- `rootOps` or a fixed delegate scope. That panel used unstyled English
379
- buttons, and its password prompt was appended below the App, so approving
380
- from it never reached PasswordConfirm.
381
- - A plain-UI App keeps the window panel. It now uses the panel styles, shows
382
- its text in the DSM language (the nine SDK locales), and asks for the
383
- administrator password inside the panel. The password goes to
384
- `authorize` the same way as in `ExecutorSettings`.
385
- - Breaking: `confirmPassword` without a password fails with
386
- `password_required` and sends nothing; the masked form it appended is gone.
387
-
388
- ### Portal needs the executor tier (breaking)
389
-
390
- - DSM removes a portal App's Login Portal alias at uninstall but does not
391
- reload nginx (DSM 7.4, t-0294), so `/<alias>/` kept opening a DSM page.
392
- Only root can reload it, so a `portal` now needs `capabilities.rootOps` or
393
- `capabilities.delegate`. A no-privilege native App with `portal` fails
394
- `check`/`build` with `STANDALONE_PORTAL_REQUIRES_EXECUTOR`; `add portal`
395
- refuses it unless the same call adds `root-ops` or `delegate`; `inspect`
396
- flags such an SPK with `PORTAL_REQUIRES_EXECUTOR`.
397
- - A Compose App has no executor: `integration.web.portal` now always fails
398
- with `CONTRACT_PORTAL_REQUIRES_EXECUTOR` and the schema no longer describes
399
- its shape. `CONTRACT_PORTAL_INVALID`, `CONTRACT_PORTAL_UNSUPPORTED` and
400
- `CONTRACT_PORTAL_MIN_DSM` are gone, and `inspect` reports a Compose SPK's
401
- `web-config` as an unknown resource.
402
- - Fix: add `root-ops` or `delegate`, or remove the portal and let an
403
- administrator set up a reverse proxy in DSM.
404
-
405
- ### Uninstall
406
-
407
- - An App with the executor (rootOps or delegate) cleans up after DSM itself:
408
- SDK 0.9.1's root purge deletes the App's rules in Control Panel >
409
- Application Privileges and, for a portal App, reloads nginx once DSM has
410
- removed the alias. The uninstall wizard says so in the nine languages, and
411
- `status`/`inspect` list both only as fallbacks: "Only if automatic cleanup
412
- failed after uninstall; see the system log entry synopkgland-executor."
413
- - A no-privilege App never uses root, not even at uninstall. Its wizard keeps
414
- saying that DSM keeps the App's rules in Control Panel > Application
415
- Privileges, that a later install under the same name applies them again,
416
- and that an administrator can remove them; the cleanup list keeps that
417
- item. It never mentions a Login Portal alias, because it cannot declare one.
418
- A test builds plain, files, jobs, identity and `entry: app-privilege` SPKs
419
- and checks they run as the package account, ship no executor or root
420
- tasks, and that their scripts never call synowebapi, nginx or a system
421
- unit.
422
-
423
- ### SDK 0.9.1
424
-
425
- - Depends on `@synopackageland/templates` 0.9.1: `init` scaffolds Apps that
426
- require SDK 0.9.1 (`backend/go.mod` and `@synopackageland/sdk`).
427
- - The CLI records the SDK 0.9.1 executor digests, rebuilt with the
428
- uninstall cleanup (x86_64 `5d75af3c…`, aarch64 `b0e1f817…`, armv7
429
- `17db613a…`; lifecycle digests equal 0.9.0). SDK 0.9.1 only replaces the
430
- executor: the policy format, task scripts and permissions are unchanged,
431
- so the policy digest stays the same and no App needs a new approval. An App moved from SDK
432
- 0.9.0 to 0.9.1 in `backend/go.mod` keeps its approval; the installed
433
- executor re-pins the new executor by itself.
434
-
435
- ### Cleaning up after 0.9.0 installs
436
-
437
- An App built with SDK 0.9.0 could leave root tasks behind at uninstall
438
- (t-0294 bugs 1 and 3). If the App is still installed, rebuild it with SDK
439
- 0.9.1, install that build and then uninstall it: the 0.9.1 purge rebuilds the
440
- task inventory from the approved policy and deletes the tasks. Otherwise an
441
- administrator removes them by hand:
442
-
443
- - Control Panel > Task Scheduler: delete `<package> executor control` and
444
- `<package> executor selection`.
445
- - The boot event, which DSM's web API refuses to delete over HTTP (error
446
- 119): as root, run
447
- `/usr/syno/bin/synowebapi --exec api=SYNO.Core.EventScheduler method=delete version=1 task_name="<package> executor boot"`.
448
- - Control Panel > Application Privileges: remove the App's rules. Control
449
- Panel > Login Portal: if its alias still opens, save an application alias
450
- there to reload nginx, or restart the NAS. The 0.9.1 rebuild-and-uninstall
451
- route above does both for you.
452
-
453
- ### Approval after upgrading (correction to 0.9.0)
454
-
455
- - 0.9.0 said a delegate App rebuilt with it needs one new approval. Measured
456
- on DSM 7.4 (t-0294): a `files.moveFromStaging` App upgraded from 0.8.0 to
457
- 0.9.0 kept the same policy digest. The installed executor re-pinned the new
458
- executor by itself, with no new approval. Only an `install-selected` scope
459
- needed one administrator confirmation (PasswordConfirm) of the shared
460
- folder.
461
- - A new approval is needed only when the executor policy changes, for
462
- example new operations, another scope or changed root scripts.
463
-
464
- ### Release tooling
465
-
466
- - `node scripts/executor-digests.mjs <sdk>/release/vX.Y.Z.json` records a
467
- released SDK's executor and lifecycle digests in
468
- `src/runtime/executor-digests.json`. It checks the three architectures,
469
- go1.26.9 and the CLI minor. A tagged release (`commit` set) never changes
470
- a recorded version; a release not yet tagged (`commit: null`) replaces its
471
- own entry. SDK 0.9.1's digests were recorded this way.
472
- - `test/fixtures/executor-setup/` holds the `executor/setup.json` this CLI
473
- writes for delegate, time-limited delegate with jobs, root-ops and
474
- install-selected Apps, for the SDK executor's contract test.
475
- `UPDATE_EXECUTOR_SETUP_FIXTURES=1 npm test` regenerates them.
476
- - The npm gate runs `scripts/smoke-exports.mjs`, the export check of
477
- `smoke-pack.sh`. In 0.9.0 that check still imported the removed
478
- `authorizationTemplateDirectory`, so `gate.sh docker` always failed.
479
-
480
- ## 0.9.0 (2026-10-11)
481
-
482
- The CLI, the SDK and the project templates are now three repositories, and
483
- the CLI only packages, deploys and checks. An App names its SDK version once,
484
- in `backend/go.mod`. Background saves can create subfolders and choose what
485
- happens on a name conflict, and time-limited delegate jobs gain read, rename,
486
- copy, move and delete.
487
-
488
- 繁中摘要:CLI 移到 github.com/synopackageland/syno-pkg-land-cli,只負責打包、部署與檢查;
489
- 範本改由 `@synopackageland/templates`(精確 pin)提供;SDK 是
490
- `github.com/synopackageland/syno-pkg-land-sdk`(Go)與 `@synopackageland/sdk`(前端,含
491
- `./vue`),版本只寫在 `backend/go.mod`。`build` 依該版本 `go mod download`
492
- executor 並比對 CLI 內建的 sha256 表。`check` 新增 `SDK_REQUIRED`、
493
- `SDK_VERSION_MISMATCH`、`SDK_UNSUPPORTED`。`files.moveFromStaging` 的 `name`
494
- 可帶子資料夾,新增 `conflict: fail|overwrite|rename`;有時限的 delegate 新增
495
- `files.read`、`files.rename`、`files.copy`、`files.move`、`files.delete`。
496
- 管理員只在 executor policy 改變時需要重新核准;0.9.0 的 policy 有改變。
497
-
498
- ### Repositories and the SDK version
499
-
500
- - The CLI is github.com/synopackageland/syno-pkg-land-cli, published as
501
- `@synopackageland/cli`. The SDK is github.com/synopackageland/syno-pkg-land-sdk: the Go
502
- module `github.com/synopackageland/syno-pkg-land-sdk` and the npm package
503
- `@synopackageland/sdk`, which includes the Vue components at
504
- `@synopackageland/sdk/vue`.
505
- - The `require github.com/synopackageland/syno-pkg-land-sdk` line in `backend/go.mod` is the
506
- only SDK version. `dsm-package.yaml` has no SDK field.
507
- - `check` and `build` report `SDK_REQUIRED` when `capabilities.delegate` or
508
- `rootOps` is declared without that line (an `entry` mode without it fails
509
- with `STANDALONE_ENTRY_UNSUPPORTED`), `SDK_UNSUPPORTED` when its minor version is not the CLI's (CLI 0.9.x builds
510
- SDK 0.9.x), and `SDK_VERSION_MISMATCH` when the frontend's
511
- `@synopackageland/sdk` differs from it. A `file:` frontend dependency is
512
- compared through that folder's `package.json` version.
513
- - `build` reads the SDK version from `backend/go.mod`, runs `go mod download`
514
- for the executor of that version, and builds it with go1.26.9. Its sha256
515
- must match the table shipped in this CLI, or the build fails with
516
- `EXECUTOR_DIGEST_MISMATCH`. A version Go cannot fetch fails with
517
- `SDK_DOWNLOAD_FAILED`. While the SDK repositories are private, set
518
- `GOPRIVATE=github.com/synopackageland/*` and give git a credential for
519
- github.com; the error says which of the two is missing. Root task scripts
520
- and `sdk-permissions.json` come from the same SDK version.
521
- - The CGI binds both policy digests at link time:
522
- `<module>/internal/api.expectedPermissionsDigest` for the App and
523
- `github.com/synopackageland/syno-pkg-land-sdk/api.expectedSDKPermissionsDigest` for the
524
- SDK.
525
- - `@synopackageland/cli/authorization-sdk` exports `AUTHORIZATION_RUNTIME`
526
- and `ROOT_RUNTIME`. `authorizationTemplateDirectory` is removed;
527
- `AuthorizationGate` is in `@synopackageland/sdk/vue`.
3
+ ## 0.10.3 (2026-10-14)
4
+
5
+ Builds with SDK 0.10.3 and templates 0.10.3: `init` writes SDK v0.10.3 (Go
6
+ module, Rust crate tag and `backend.sdk`), `@synopackageland/sdk` 0.10.3 and
7
+ `@synopackageland/ui` 0.9.5. **Incompatible:** `check` and `build` need SDK
8
+ 0.10.3 or later. The executor digest table adds SDK 0.10.3 and keeps 0.10.0
9
+ to 0.10.2, so `inspect` still recognizes their SPKs. SDK 0.10.3's executor
10
+ records the package version at every start, so an App rebuilt on it carries
11
+ a new executor digest; when its policy digest does not change, the upgrade
12
+ puts the new executor in place without approval. An App that declares
13
+ `root.start` changes its policy and is approved again. The lifecycle digests
14
+ equal 0.10.2.
15
+
16
+ 繁中摘要:使用 SDK 0.10.3 與 templates 0.10.3(UI 0.9.5);`check`/`build` 需要 SDK
17
+ 0.10.3 以上(不相容)。digest 表新增 SDK 0.10.3(保留 0.10.0~0.10.2)。policy digest
18
+ 不變的 App 升級時自動換 executor;宣告 `root.start` 的 App 需要重新核准。lifecycle
19
+ digest 與 0.10.2 相同。
20
+
21
+ ### Added
22
+
23
+ - `root.start` in dsm-package.yaml names a `root/` program as the executor's start step: the
24
+ executor runs it as root once at the first start after each install or upgrade (`install`,
25
+ or `upgrade` and the previous version), only while the approval is live, for at most 60
26
+ seconds, and a failure never blocks the start. `build` writes it into the policy as `start`;
27
+ `check` refuses a name outside `root/` and a program that also declares `run` or `job`
28
+ (`STANDALONE_ROOT_PROGRAMS_INVALID`); the SDK executor's `check-policy` checks the rest.
29
+ `inspect` lists the root steps. Declaring it changes the policy digest, so the App needs
30
+ approval again; Apps without it keep their policy.
31
+
32
+ 繁中摘要:`root.start` 指定一個 root 程式,executor 在每次安裝或升級後第一次啟動時以 root 執行一次;
33
+ 失敗只記 log、不擋啟動。宣告後 policy digest 改變需重新核准,沒有宣告的 App 不受影響。
34
+
35
+ - Lifecycle scripts: a project's `dsm-scripts/<DSM script name>` (`preinst`, `postinst`,
36
+ `preupgrade`, `postupgrade`, `preuninst`, `postuninst`, `start-stop-status`) is packed as
37
+ `scripts/app/<name>` and called from the generated script of the same name: after the SDK
38
+ steps, and before them for `preupgrade`, `preuninst` and `stop`. It runs as the package
39
+ account with DSM's arguments, environment and exit codes; a failed App `start` stops the SDK
40
+ again. Native and Compose projects. `check` refuses other names (`APP_SCRIPT_UNKNOWN`),
41
+ non-executable files and files without `#!`; `inspect` lists the App scripts. Projects
42
+ without `dsm-scripts/` build unchanged. Guide: `synopkgland skill show lifecycle`.
43
+
44
+ 繁中摘要:專案 `dsm-scripts/` 下以 DSM 原生檔名放的腳本會被打包,並在 CLI 產生的同名腳本中於
45
+ SDK 步驟之後(preupgrade/preuninst/stop 在之前)以套件帳號執行;沒有該資料夾時行為不變。
46
+
47
+ - Login Portal style: a portal App's launcher now carries DSM's `loginStyle` and `loginIcon`,
48
+ so Control Panel > Login Portal > Applications shows a thumbnail instead of a blank box, and
49
+ the alias login page gets a wallpaper, a button colour and the App icon. The CLI ships a
50
+ neutral default (`assets/login-style/`); `login_style` in dsm-package.yaml overrides
51
+ `color`, `wallpaper` with `wallpaper_thumbnail`, and `title`. `check` validates the images
52
+ (`STANDALONE_LOGIN_STYLE_INVALID`, `STANDALONE_LOGIN_STYLE_IMAGE_INVALID`) and prints the
53
+ style; `inspect` prints it and flags a portal SPK without one (`LOGIN_STYLE_MISSING`).
54
+ Rebuilding an existing portal App with this CLI adds the default; no change to the project.
55
+
56
+ 繁中摘要:有 portal 的 App 重新 build 後會自動帶入預設登入樣式,「登入入口 › 應用程式」縮圖不再
57
+ 空白,別名登入頁有桌布、按鈕色與 App icon;可用 `login_style` 換主色、桌布與標題圖。
58
+
59
+ ### Changed
60
+
61
+ - **Incompatible:** the CLI needs SDK 0.10.3 or later. `check` and `build` refuse an App
62
+ whose backend requires SDK 0.10.0, 0.10.1 or 0.10.2 (`SDK_UNSUPPORTED`, naming 0.10.3);
63
+ raise the Go module, Cargo tag or `backend.sdk` and the frontend's
64
+ `@synopackageland/sdk` to 0.10.3. The CLI no longer carries the fallbacks for those
65
+ releases: the pre-`timeouts.json` timeouts, skipping `executor check-policy` for an SDK
66
+ without it, and `EXECUTOR_START_STEP_UNSUPPORTED`.
67
+
68
+ 繁中摘要:CLI 需要 SDK 0.10.3 以上;App 若仍用 SDK 0.10.0~0.10.2,`check`/`build` 會以
69
+ `SDK_UNSUPPORTED` 拒絕。移除為舊版保留的逾時退回值、check-policy 略過與 start 步驟檢查。
70
+
71
+ - `check` and `build` refuse a request deadline that ends before a root program `root.run`
72
+ may run would answer (`STANDALONE_REQUEST_TIMEOUT_SHORT`): `backend.timeout_seconds`
73
+ (60 by default) must be at least the program's `run.timeout_s` (20 by default) + 20, the
74
+ executor's 10 seconds and its client's 10 (SDK `spec/protocol/v1`, Timeouts). The default
75
+ covers programs of up to 40 seconds; a 120-second program needs `timeout_seconds: 140`.
76
+ A program with only `job`, `start` or `stop` is not affected.
77
+ - The request default, the window's wait past the request deadline and the `root.run`
78
+ deadline check read their values from the App's SDK, `spec/protocol/v1/timeouts.json`,
79
+ instead of the CLI's own copies, so they follow the SDK the App builds with. An App
80
+ without the SDK keeps 60 seconds, 2 seconds, and a `run.timeout_s` of 20 + 20. The
81
+ generated values do not change with SDK 0.10.3.
82
+
83
+ 繁中摘要:request 預設逾時、視窗多等的時間與 root.run 逾時檢查改讀 App 所用 SDK 的
84
+ timeouts.json,不再用 CLI 手抄的值;沒有 SDK 的 App 沿用 60 秒、2 秒、20 + 20。
85
+ - The DSM window's approval and `control start` wait for an executor that answers
86
+ `state: "starting"` while it runs its start step, for up to 75 seconds, instead of giving
87
+ up after 30 seconds; past that the result is blocked with code `executor_starting`, and the
88
+ plain-UI panel and the SDK settings say the service is still starting. The plain-UI panel
89
+ says it is waiting for the background service while a request runs.
90
+
91
+ 繁中摘要:核准與 control start 會等待回報「starting」的 executor(最多 75 秒),超過則以
92
+ `executor_starting` 阻擋並說明仍在啟動。
93
+
94
+ - The DSM window's approval asks DSM's schedulers 8 times instead of 15, following the SDK's
95
+ `spec/scheduler/v1`. The boot task is read with one EventScheduler `get` by its name
96
+ (DSM keeps event task names unique) and never listed; Task Scheduler is listed once before
97
+ and once after the writes, and the control task is then read by its listed id. A step reads
98
+ each task once and reuses that read; a delete still reads the task again first. After the
99
+ boot task runs, only `executor.status` is asked: the tasks were read back exactly just
100
+ before, and the executor's pins answer for the rest. `executor.status` is asked every
101
+ 500 ms for the first 4 seconds, then backs off to 2 seconds. `status` (and `check`) gets the
102
+ boot task by name instead of listing EventScheduler. Measured on a DSM 7.4 x86_64 NAS with
103
+ a fresh install: ready came 1.1-4.9 s after the boot task ran instead of 6.3-10.9 s, and
104
+ 10.1-21.8 s after Approve instead of 16.3-26.9 s (DSM's scheduler calls vary by seconds).
105
+
106
+ 繁中摘要:視窗核准對 DSM 排程 API 的呼叫從 15 次降為 8 次:boot 任務改用名稱 get、不再 list
107
+ EventScheduler;Task Scheduler 在寫入前後各 list 一次,之後用 id get;執行 boot 任務後只問
108
+ `executor.status`,前 4 秒每 500 ms 問一次。安全檢查(重複、內容逐字比對、pins)都保留。
109
+
110
+ ### Fixed
111
+
112
+ - The DSM window's `openFileManager` and `revealFile` take a `/volumeN/...` path as the SDK's
113
+ `fileStationPath` does: a DSM volume mount (`/volumeN`, `/volumeUSBN`, `/volumeSATAN`) is
114
+ stripped instead of refused, and the reveal recovery follows the path File Station got.
115
+ Anything but one canonical shared-folder path is still `invalid-input`.
116
+ - Executor approval results carry a structured `code` beside the English `reason`, and an
117
+ `outcome` when the approval rolled back: a refused administrator password is
118
+ `password_rejected` (DSM 3121), other PasswordConfirm refusals `password_required`,
119
+ `session_ended` or `password_confirm_failed`; scheduler, task and executor failures have
120
+ codes of their own (`scheduler_denied`, `task_missing`, `executor_unreachable`, …) and the
121
+ outcomes are `restored`, `withdrawn`, `restore_failed` and `withdraw_failed`. The SDK's
122
+ `ExecutorSettings` reads `code`. The plain-UI window panel now shows the code and outcome in
123
+ the DSM language, followed by the DSM code (`dsm_3121`), instead of the English reason.
124
+ Rebuild the App to pick this up.
125
+
126
+ 繁中摘要:核准結果新增結構化 `code`(例如 `password_rejected`)與回復結果 `outcome`,`reason`
127
+ 仍保留英文細節;純 UI 視窗面板改以 DSM 語系顯示原因,不再直接顯示英文;需重新 build App。
128
+
129
+ - Executor approval: a scheduler write that fails partway no longer leaves the earlier
130
+ approval half deleted. The approval stops at the first failed write, withdraws the tasks
131
+ it created and recreates the earlier boot and control tasks it removed, then offers
132
+ approval again. DSM cannot hold two same-named tasks, so the old task is still deleted
133
+ before its replacement is created. When the rollback itself fails, the window says so, and
134
+ the next approval converges from what is left. A DSM session that ended (`dsm_106`,
135
+ `dsm_107`, `dsm_119`) asks the administrator to sign in to DSM again.
136
+
137
+ 繁中摘要:核准半途寫入失敗時,會撤回這次建立的排程並重建先前核准的舊排程,再提供重新核准;
138
+ 還原失敗時會明白說明,下次核准可自動收斂。DSM session 失效時提示重新登入。
139
+
140
+ - An App opened through its Application Portal alias (`https://nas/<alias>/`) reaches its
141
+ backend again. The generated window posted `ctx.call` and `ctx.files` to the absolute
142
+ `/webman/3rdparty/<package>/cgi/api.cgi`, which DSM answers with 403 on the alias, so every
143
+ call failed with `bad_response` and an approved executor read as unavailable. The path is
144
+ now relative to the page, as the App's own assets are; the DSM desktop resolves it to the
145
+ same URL as before. Rebuild the App to pick this up.
146
+
147
+ 繁中摘要:從應用程式入口別名網址開啟 App 時,`ctx.call`/`ctx.files` 改用相對路徑,不再被
148
+ DSM 以 403 拒絕(原本顯示 `bad_response`、已核准的 executor 顯示無法使用);需重新 build App。
149
+
150
+ - `check` and `build` refusing a `root.start`, `root.stop` or `uninstall.purge` program that
151
+ also declares `run` or `job` (`EXECUTOR_POLICY_REJECTED`) now say what to do: give the step
152
+ another program name in `root.programs`, one without `run` or `job`.
153
+
154
+ 繁中摘要:start/stop/purge 的 program 同時宣告 run/job 而被拒時,提示改用另一個沒有
155
+ run/job 的 program 名稱。
156
+
157
+ - `init` and `build` of a Python App whose SDK has no Python SDK (`python/synopkgland_app`)
158
+ report `STANDALONE_PYTHON_SDK_MISSING` with the path, instead of a bare `ENOENT`.
159
+
160
+ 繁中摘要:找不到 Python SDK 時改回專屬錯誤碼 `STANDALONE_PYTHON_SDK_MISSING`,不再是 `ENOENT`。
161
+
162
+ ### Removed
163
+
164
+ - `@synopackageland/cli/authorization-sdk` is gone: `ROOT_RUNTIME` and
165
+ `AUTHORIZATION_RUNTIME`, the window runtimes of a native product with its own pinned root
166
+ tasks, ship unchanged in `@synopackageland/sdk/native`. A native product imports them from
167
+ the SDK; generated Apps are not affected.
168
+
169
+ 繁中摘要:native 產品的 root/authorization 視窗 runtime 改由 SDK(`@synopackageland/sdk/native`)提供,
170
+ CLI 不再匯出 `authorization-sdk`;一般產生的 App 不受影響。
171
+
172
+ - The CLI's copy of the Application Portal panel text (19 keys in 9 languages) is gone:
173
+ `build` reads it from the App's SDK (`ts/i18n/portal/<locale>.json`, in every SDK 0.10.x),
174
+ the same text the SDK's `PortalSettings` shows. The built window is unchanged; a fix to the
175
+ text reaches an App with its next SDK.
176
+
177
+ 繁中摘要:Application Portal 面板文案改從 App 的 SDK 讀取,CLI 不再保留一份;建出的視窗不變,文案修正隨 SDK 更新。
178
+
179
+ - `dist/runtime/delegate-contract.js` keeps only the operation names the build checks; the
180
+ background-save DTOs, route table and limits, which nothing in the CLI used, and the frozen
181
+ v1 vector now live only in the SDK (`ts/src/delegate/long-running.ts`).
182
+
183
+
184
+ ## 0.10.2 (2026-10-14)
185
+
186
+ First release of the changelog. `@synopackageland/cli` (`synopkgland`)
187
+ builds Synology DSM 7 packages (SPK) from a template, checks them, and
188
+ installs them on a NAS. It builds with SDK 0.10.2 and templates 0.10.2.
189
+
190
+ 繁中摘要:`synopkgland` 從模板建立 DSM 7 套件(SPK),檢查、打包並安裝到
191
+ NAS;搭配 SDK 0.10.2 與 templates 0.10.2。
528
192
 
529
193
  ### Templates
530
194
 
531
- - `init` and `add` read the templates from `@synopackageland/templates`, a
532
- dependency pinned to an exact version. `SYNOPKGLAND_TEMPLATES_DIR` points
533
- them at a templates checkout; without either, `TEMPLATES_MISSING`.
534
- - `init` fills the placeholders in every generated file; the Go module path
535
- of a new backend is the package id. The next steps say
536
- `(cd frontend && npm install)`: the templates ship no lockfile.
537
- - `init` and `add` share one capability overlay table (`root-ops`, `jobs`,
538
- `delegate`).
539
-
540
- ### Local SDK checkout
541
-
542
- - With `SYNOPKGLAND_SDK_DIR`, `init` adds
543
- `replace github.com/synopackageland/syno-pkg-land-sdk => $SYNOPKGLAND_SDK_DIR` to
544
- `backend/go.mod` and sets the frontend dependency to
545
- `file:$SYNOPKGLAND_SDK_DIR/ts`; install it with
546
- `(cd frontend && npm install --install-links)`, which packs the SDK like the
547
- released package. npm does not build a `file:` package, so a Vue project
548
- needs `(cd $SYNOPKGLAND_SDK_DIR/ts && npm ci && npm run build)` first, and
549
- again after SDK changes; `init` refuses a checkout without `ts/dist`, or
550
- without a root `go.mod`, with `SDK_DIR_NOT_READY`.
551
- - `build` uses `$SYNOPKGLAND_SDK_DIR/executor`, skips the digest table and
552
- warns with `EXECUTOR_DIGEST_UNVERIFIED`. Do not
553
- publish such an SPK; the release script refuses to run while
554
- `SYNOPKGLAND_SDK_DIR` or `SYNOPKGLAND_TEMPLATES_DIR` is set.
555
-
556
- ### Delegate: subfolders and conflict policy
557
-
558
- - `files.moveFromStaging` (Go: `delegate.MoveParams.Name`) accepts folders
559
- below the destination, for example `Album/CD1/01.flac`. The executor's move
560
- child creates missing folders as the user, after the final revoke check, so
561
- a canceled move leaves none behind. Existing folders are reused. Limits: 16
562
- folders, 255 bytes per name, 1024 bytes in total. A name without folders
563
- behaves exactly as before.
564
- - New `conflict` field for `files.moveFromStaging` and the time-limited
565
- `files.write`: `fail` (default, unchanged), `overwrite` (atomic replace of a
566
- regular file the user can write) or `rename` (`name (1).ext` up to
567
- `name (99).ext`; the receipt path reports the name used).
568
- - New error codes: `invalid-path`, `path-too-deep`,
569
- `destination-not-directory`, `name-too-long`.
570
-
571
- ### Delegate: file operations for time-limited grants
572
-
573
- - New operations `files.read`, `files.rename`, `files.copy`, `files.move` and
574
- `files.delete`, each declared on its own; a write operation never implies
575
- delete. Go: `job.Files()` returns `delegate.JobFiles` with `Read`, `Rename`,
576
- `CreateFolderAll`, `Copy`, `Move`, `Delete`, `TaskStatus` and `CancelTask`.
577
- - `files.createFolder` takes `parents` (`CreateFolderAll`, like `mkdir -p`).
578
- - Except `files.write` and `files.read`, the executor calls File Station as the
579
- user through one root-side allowlist of methods, and re-checks the paths File
580
- Station reports against the grant. `files.read` copies the file into staging
581
- in a child that drops to the user and never follows symlinks.
582
- - Copy, move and delete run as File Station background tasks followed with the
583
- implied `files.taskStatus` and `files.taskCancel`; revoking the grant stops
584
- them. Delete needs `recursive` for folders and goes to the recycle bin; a
585
- Shared Folder without one needs `permanent`.
586
- - File Station refusals now report SDK codes (`PERMISSION_DENIED`,
587
- `not-found`, `destination-exists`, `NO_SPACE`, `invalid-path`,
588
- `name-too-long`) instead of `DSM_FAILED`. New codes: `not-a-file`,
589
- `recursive-required`, `recycle-bin-disabled`, `task-not-found`,
590
- `too-many-tasks`, `READ_FAILED`.
591
-
592
- ### Approval
593
-
594
- - An administrator approves the executor policy (operations, scope and root
595
- tasks), not the executor binary. When an upgrade keeps the policy and the
596
- new executor matches the `INFO` anchor, it installs without a new approval.
597
- When the policy changes, the App stays at `needs-admin-approval` until an
598
- administrator approves again.
599
- - The 0.9.0 policy changes (new operations and `conflict`), so a delegate App
600
- rebuilt with this CLI needs one new approval after the upgrade.
601
- Moving the SDK to its own repository does not add one. Apps not rebuilt are
602
- not affected.
603
-
604
- ## 0.8.0 (2026-10-11)
605
-
606
- Faster builds, one SPK checker for any package, and a guide for scripts and
607
- interpreted languages. `validate` is merged into `inspect`.
608
-
609
- 繁中摘要:toolkit 改用使用者層級共用快取;新增 `build --jobs` 多架構並行與
610
- `build --verify-reproducible` 可重現檢查(可用依賴指紋略過);`inspect` 對任何
611
- SPK 檢查 DSM 7 格式,synopkgland 建置的 SPK 另查建置合約;native layout 的
612
- aarch64 INFO arch 改寫機型清單、拒絕無執行權限的 script、toolkit 可用 GNU tar
613
- 或 bsdtar 解開;新增腳本/直譯語言打包指南。無圖示的 native layout SPK 現在會
614
- 因 `SPK_ICON_MISSING` 無法通過 inspect。
615
-
616
- ### Shared toolkit cache
617
-
618
- - Native layout toolkit archives now default to one per-user cache shared by
619
- every project and worktree: `$XDG_CACHE_HOME/synopkgland` or
620
- `~/.cache/synopkgland` on Linux, `~/Library/Caches/synopkgland` on macOS.
621
- `SYNOPKGLAND_CACHE_DIR` still overrides it. The project's
622
- `.synopkgland-cache` folder is no longer used by default; delete it or point
623
- `SYNOPKGLAND_CACHE_DIR` at it. The private-folder and `sha256` checks are
624
- unchanged, and concurrent builds may share the cache.
625
-
626
- ### Parallel architecture builds
627
-
628
- - `synopkgland build --jobs <n>` builds up to `n` architectures at the same
629
- time for native-go, binary, native layout and Compose gateway projects. The
630
- default stays `1`. Output files and names are byte-identical to a sequential
631
- build; with `n` above 1 each output line is prefixed with its architecture.
632
- - SPK files now reach the output folder only after every architecture
633
- succeeds, also with `--jobs 1`. A failed architecture is named in the error,
634
- for example `[aarch64]`, and no SPK from that build is written. Before, the
635
- architectures built before the failure stayed in `dist/`.
636
- - Native layout compile hooks get `SYNOPKGLAND_WORK_DIR`, an empty scratch
637
- folder per architecture. `--jobs` above 1 requires `layout.payload` to
638
- contain `{arch}`. Architectures that pin the same toolkit archive download it
639
- once.
640
- - New error code `CLI_JOBS_INVALID`.
641
-
642
- ### Reproducible build check
643
-
644
- - `synopkgland build --verify-reproducible [changed|always]` keeps this
645
- build's SPKs as the first round, builds once more into a temporary folder
646
- with an empty `GOCACHE` after removing the manifest's `reproducible.clean`
647
- paths, and fails with `BUILD_NOT_REPRODUCIBLE` listing the payload and
648
- metadata files that differ. Works for every project type.
649
- - New optional manifest block `reproducible: {inputs, clean}` in
650
- `dsm-package.yaml` (native-go, binary, native layout) and
651
- `synology-app.yaml`. The SHA-256 of the `inputs` files, the CLI version and
652
- native layout toolkit pins form a dependency fingerprint. A passing check
653
- writes it to `reproducible-fingerprint.json`; `changed` (the default) skips
654
- the second build while it is unchanged. New error code
655
- `REPRODUCIBLE_INVALID`.
656
-
657
- ### `inspect` checks any SPK
658
-
659
- - `synopkgland inspect <spk>` now checks any SPK against the DSM 7 package
660
- format, whether `synopkgland` built it or not, without installing it: the
661
- outer archive, `INFO` fields, formats and `checksum`, path and link safety in
662
- `package.tgz`, `ui/config` JSON and its `"texts"` declaration, ELF files
663
- against `arch` (none in `noarch`), lifecycle scripts and their execute bit,
664
- `conf/privilege` and `conf/resource`, icons, `LICENSE`, wizards and
665
- `syno_signature.asc`. Every finding has a stable `SPK_*` code listed in the
666
- errors reference.
667
- - Only an SPK that `synopkgland` built (top-level `sdk-package.json` or
668
- `native-layout.json`, or `synology-app.yaml` in the payload) is also checked
669
- against the build contract. Other SPKs are no longer rejected by it.
670
- - The output has a DSM format section and, for `synopkgland` SPKs, a
671
- `synopkgland` contract section. An error in either exits 1 with
672
- `SPK_INSPECTION_FAILED`; warnings alone exit 0. `--json` keeps every earlier
673
- field and adds `synopkgland` (boolean) and `dsm: {ok, errors, warnings}`.
674
- - There is no `validate` command; the DSM rules it had during 0.8.0
675
- development are part of `inspect`. The project check is `check`.
676
-
677
- ### Native layout fixes
678
-
679
- - `arches: [aarch64]` now writes the same `INFO` `arch` as native-go and
680
- binary (`armada37xx rtd1296 rtd1619 rtd1619b`) instead of `aarch64`, which
681
- the Developer Guide arch table does not list.
682
- - `build` refuses a lifecycle or replace script in `layout.metadata` without
683
- the execute bit with `NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE`; run `chmod +x`.
684
- Before, the SPK was built and DSM could not run the script.
685
- - `native.toolkits` archives extract with GNU tar and with bsdtar (the macOS
686
- default). Other tar programs fail with `NATIVE_TOOLKIT_TAR_UNSUPPORTED`.
687
- Members with absolute or `..` paths are refused.
688
-
689
- ### Scripts and interpreted languages guide
690
-
691
- - New guide: package a shell, Node.js, Python, Bun or Deno program by
692
- compiling it to one executable, bundling the interpreter through
693
- `native.toolkits`, or running it in a Compose container, with measured sizes,
694
- architectures and glibc needs, and a hand-packed `noarch` shell SPK. The CLI
695
- cannot declare `install_dep_packages` for Package Center's Node.js or Python
696
- packages; the guide says so.
697
-
698
- ### 繁體中文摘要
699
-
700
- - `synopkgland build --jobs <n>` 可同時建置多個架構(native-go、binary、native layout、
701
- Compose gateway),預設仍為 1;產出與依序建置逐位元組相同。所有架構成功後才寫入
702
- SPK,失敗時錯誤標明架構且不寫入任何 SPK。native layout hook 新增
703
- `SYNOPKGLAND_WORK_DIR`;`--jobs` 大於 1 時 `layout.payload` 必須含 `{arch}`。
704
- - `synopkgland build --verify-reproducible` 以這次建置為第一輪、從乾淨狀態再建一次比對,
705
- 不同時列出 payload 檔案並以 `BUILD_NOT_REPRODUCIBLE` 失敗。manifest 新增
706
- `reproducible: {inputs, clean}`;依賴指紋未變時略過(記錄在
707
- `reproducible-fingerprint.json`)。
708
- - native layout 的 toolkit 快取預設改為每位使用者一份、所有專案與 worktree 共用
709
- (Linux `$XDG_CACHE_HOME/synopkgland` 或 `~/.cache/synopkgland`,macOS
710
- `~/Library/Caches/synopkgland`);`SYNOPKGLAND_CACHE_DIR` 仍可覆寫。專案內的
711
- `.synopkgland-cache` 不再預設使用。
712
- - `synopkgland inspect <spk>` 改為不安裝、依 DSM 7 套件格式檢查任何 SPK(不限本 CLI 建置),
713
- 涵蓋外層封存、`INFO` 欄位與 `checksum`、`package.tgz` 路徑與連結安全、`ui/config` 的
714
- `"texts"`、ELF 與 `arch`(`noarch` 不得有 ELF)、生命週期指令碼與執行權限、`conf/privilege`
715
- 與 `conf/resource`、圖示、`LICENSE`、精靈與簽章。每個問題有固定 `SPK_*` 代碼,列在錯誤碼參考。
716
- - 只有 `synopkgland` 建置的 SPK(最上層有 `sdk-package.json` 或 `native-layout.json`,或 payload
717
- 有 `synology-app.yaml`)另查建置合約;其他 SPK 不再因合約被拒。任一區段有錯誤時以
718
- `SPK_INSPECTION_FAILED` 結束碼 1 失敗,只有警告時結束碼 0。`--json` 保留原有欄位,新增
719
- `synopkgland` 與 `dsm: {ok, errors, warnings}`。
720
- - 沒有 `validate` 指令;0.8.0 開發期間的 DSM 規則已併入 `inspect`。專案檢查請用 `check`。
721
- - native layout 以 `arches: [aarch64]` 建置時,INFO `arch` 改為與 native-go/binary 相同的
722
- `armada37xx rtd1296 rtd1619 rtd1619b`。`layout.metadata` 的指令碼沒有執行權限時 `build` 以
723
- `NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE` 拒絕,請 `chmod +x`。`native.toolkits` 可用 GNU tar 與
724
- bsdtar 解壓,其他 tar 以 `NATIVE_TOOLKIT_TAR_UNSUPPORTED` 失敗。
725
- - 新增「封裝指令碼與直譯式語言」指南:編譯成單一執行檔、經 `native.toolkits` 附帶直譯器、或以
726
- Compose 容器執行,附實測大小、架構與 glibc 需求,以及手動打包的 `noarch` shell SPK。CLI 無法宣告
727
- `install_dep_packages` 依賴套件中心的 Node.js/Python 套件,指南已明確說明。
728
-
729
- ## 0.7.3 (2026-10-11)
730
-
731
- Rebuild every package built with 0.7.2 that has `i18n.info` translations. In
732
- 0.7.2 its DSM main-menu entry, desktop shortcut and window title showed the raw
733
- key `synopkgland:app_title` in every language.
734
-
735
- ### Translated DSM title fix
736
-
737
- - `ui/config` entries that use the `synopkgland:app_title` and `app_desc`
738
- strings now declare `"texts": "texts"`: the main-menu `app` entry, its window
739
- `lib` entry and the `.url` entry of a `synology-app.yaml` app. DSM loads
740
- `ui/texts/<DSM language>/strings` only for entries that declare `texts`, so
741
- 0.7.2 titles never resolved (seen on DSM 7.4 and 7.1.1). Without `i18n.info`
742
- `ui/config` is unchanged.
743
-
744
- ### Correction to 0.7.2
745
-
746
- - 0.7.2 said `synology-app.yaml` `i18n.info` localizes the DSM window title.
747
- It localizes the DSM main-menu and desktop label only. `launch: dsm-window`
748
- adds a main-menu and desktop entry that DSM always opens in a new browser
749
- tab, as it does for every URL entry; the app has no DSM window and no window
750
- title. This is not caused by DSM's CSRF protection setting. The container app
751
- guide and the `synology-app.yaml` reference now say so.
752
-
753
- ### 繁體中文摘要
754
-
755
- - 修正 0.7.2 的在地化標題:有 `i18n.info` 翻譯時,`ui/config` 中使用 `synopkgland:` 字串的
756
- `app`、`lib` 與 `.url` 項目都加上 `"texts": "texts"`。0.7.2 少了這個欄位,DSM 不會載入
757
- `ui/texts/<語言>/strings`,主選單、桌面捷徑與視窗標題在各語言都顯示 `synopkgland:app_title`。
758
- - 用 0.7.2 建置且含 `i18n.info` 的套件,請以 0.7.3 重新建置並遞增 build 後重新發佈;沒有
759
- `i18n.info` 的套件不受影響。
760
- - 更正 0.7.2:`synology-app.yaml` 的 `i18n.info` 只翻譯 DSM 主選單與桌面標籤,沒有視窗標題。
761
- `launch: dsm-window` 的項目一律由 DSM 在新的瀏覽器分頁開啟(DSM 對所有網址項目都如此),
762
- 與 CSRF 防護設定無關;容器 App 指南與 `synology-app.yaml` 參考已更正。
763
-
764
- ## 0.7.2 (2026-10-11)
765
-
766
- Rebuild packages to pick up the new default icon. Packages without `icon` now
767
- show the SDK default icon instead of a solid blue square.
768
-
769
- ### Package icon
770
-
771
- - `dsm-package.yaml` and `synology-app.yaml` accept `icon`: a square PNG of at
772
- least 256×256 inside the project, or a folder of `icon_<size>.png` (16, 24,
773
- 32, 48, 64, 72, 256; `icon_256.png` required, missing sizes scaled from it).
774
- `build` writes `PACKAGE_ICON.PNG` (64×64), `PACKAGE_ICON_256.PNG` and every
775
- DSM window size `ui/images/app_<size>.png` from it. SVG is not accepted, so
776
- the CLI gains no image dependency. `check` refuses an icon outside the
777
- project, a non-PNG, a non-square or smaller image, and one whose Package
778
- Center icons exceed the package source's 256 KiB limit
779
- (`STANDALONE_ICON_INVALID`, `CONTRACT_ICON_INVALID`).
780
-
781
- - A native layout may set `icon` too; `build` then writes both Package Center
782
- icons and refuses a metadata folder that also ships them. Without `icon` the
783
- layout keeps owning its icons.
784
-
785
- - `check` reports which icon a build uses: `圖示:自訂 <path>`, or `圖示:SDK
786
- 預設圖示` with an info hint about `icon:` (not an error). It warns when an
787
- icon is a single colour. `check --json` reports it in `data.icon`.
788
-
789
- - `inspect` reports `icon` as `default`, `custom` (with the SHA-256 of
790
- `PACKAGE_ICON_256.PNG`) or `none`, and lists a Package Center icon over
791
- 256 KiB, of the wrong size or not a PNG as a finding.
792
-
793
- - The SDK default icon replaces the solid blue square in Package Center, the
794
- DSM main menu and the window. Sources are in `assets/default-icon/`.
795
-
796
- ### Translated DSM title
797
-
798
- - With `i18n.info` translations, the DSM main-menu entry and window title
799
- follow the DSM language. `ui/config` refers to `synopkgland:app_title` and
800
- `app_desc` in `ui/texts/<DSM language>/strings`, written for English and each
801
- translated language; a language without its own text shows the English one.
802
- Without translations `ui/config` keeps the literal English title.
803
-
804
- - `synology-app.yaml` accepts the same `i18n.info` block. It writes INFO
805
- `displayname_<lang>` / `description_<lang>` and, with `launch: dsm-window`,
806
- localizes the DSM window entry. Once a translation exists, the English
807
- `displayname` and `description` must also be one line without `"` or `\`
808
- (`CONTRACT_I18N_INVALID`).
809
-
810
- ### Delegate window
811
-
812
- - A DSM window built for a `scope: install-selected` delegate no longer
813
- contains the dead `if (panels && false)` check. The window leaves out the
814
- approval panel code instead, and other scopes generate `if (panels)`. The
815
- behavior is unchanged: this scope is approved only in the app's
816
- **Administrator setup** panel, because that approval carries the scope the
817
- administrator picks and the window panel has no scope picker. The delegate
818
- guide now says so.
819
-
820
- ### 繁體中文摘要
821
-
822
- - 升級後重新建置套件即可換上新的 SDK 預設圖示(不再是純藍方塊)。executor digest 不變,不需重新核准。
823
- - `dsm-package.yaml`、`synology-app.yaml` 與 native layout 新增 `icon`:專案內至少 256×256 的正方形
824
- PNG,或含 `icon_256.png` 的 `icon_<size>.png` 目錄;不接受 SVG。`check` 顯示使用的圖示並檢查格式與
825
- 256 KiB 上限,`inspect` 回報 `default`/`custom`/`none`。
826
- - 有 `i18n.info` 翻譯時,DSM 主選單與視窗標題會跟著 DSM 語言切換;缺翻譯的語言顯示英文。
827
- `synology-app.yaml` 也支援 `i18n.info`。
828
- - `scope: install-selected` 的 DSM 視窗不再產生 `if (panels && false)` 死碼;此 scope 仍只在 App 的
829
- 「管理員設定」核准。
830
-
831
- ## 0.7.1 (2026-10-10)
832
-
833
- Rebuild packages to pick up template, hook and executor changes. The executor
834
- digest changed. A package built with CLI 0.7.0 needs one administrator approval
835
- after upgrading to a 0.7.1 build: the 0.7.0 executor opened the package
836
- `target` link without following it, so it cannot install the anchored executor
837
- itself. Executor-only upgrades between 0.7.1 or later builds need no approval.
838
-
839
- ### Executor upgrade and daemon timeouts
840
-
841
- - During an INFO-anchored executor-only upgrade the executor log said
842
- `executor paused: package pins require administrator approval` about two
843
- seconds before the new executor was ready. That line now appears only when
844
- the pins differ; an anchored handover logs only `executor upgrade:
845
- INFO-anchored executor installed under the approved policy`. The executor
846
- digest changes.
847
-
848
- - `executor_status_mismatch` in the app window and the app's settings names
849
- its cause: `the approved executor is not answering (<reason>)`, `the running
850
- executor reports other digests than this package pins`, or `the executor
851
- status request failed` with the request's own code, instead of `scheduler
852
- readback or operation failed` for all three. The Data and removal
853
- troubleshooting table explains each.
854
-
855
- - After approval or control start the window asked `executor.status` every
856
- 500 ms for up to 10 s. The wait now doubles from 500 ms to 2 s within the
857
- same 10 s budget: at most 7 requests instead of 20.
858
-
859
- - For live upgrade tests, the repository's `scripts/test/executor-bump.py`
860
- makes an executor-only build+1 SPK and now rewrites the window script's
861
- embedded pins with the INFO anchor and `executor/setup.json`. The earlier
862
- out-of-tree copy left the window pins behind, so the panel reported
863
- `executor_status_mismatch` after every such upgrade while CGI and CLI
864
- status were ready.
865
-
866
- - On kernel 3.10 (DSM 7.1, for example DS416 armv7) an executor-only
867
- upgrade, and approving again while the executor runs, no longer fail
868
- with "pid executable mismatch". That kernel names a running executor
869
- replaced by a rename after the rename source (`runtime/.executor
870
- (deleted)`), and root control accepted only `runtime/executor`. Root
871
- control now identifies the monitor by inode: the current
872
- `runtime/executor`, or an unlinked root-owned inode on the runtime's
873
- device whose last name was `executor` or `.executor*` in the runtime.
874
- The fix runs in the new executor, which the installed one starts: an
875
- upgrade from a package built with the target-link fix below needs no
876
- approval; from 0.7.0 it still needs the one approval described there.
877
- The executor digest changes.
878
-
879
- - When the approved tasks are ready but the executor does not answer
880
- (`executor_status_mismatch`), the app window offers Approve background
881
- access again. With the tasks approved, it restarts the executor without
882
- a password.
883
-
884
- - `executor.status` (root-ops) names approval, not "Start the package", as
885
- the next step when the package's last lifecycle request was start.
886
- Regenerate or copy `backend/internal/app/executor.go`.
887
-
888
- - An executor-only upgrade (same policy, new executor matching the INFO
889
- `synopkgland_executor_sha256` anchor) no longer fails with "executor
890
- upgrade refused: INFO-anchored executor could not be installed". The
891
- installer opened `/var/packages/<pkg>/target/executor` without following
892
- the DSM `target` link, which the executor refuses, so every such upgrade
893
- stayed paused. The fix runs in the executor that is already installed:
894
- an upgrade from a package built with an earlier SDK still needs one
895
- administrator approval. The executor digest changes.
896
-
897
- - `executor.status` (root-ops and delegate projects) no longer answers
898
- `internal` while the executor is paused (package stopped, or pins waiting
899
- for approval). It answers `{ready: false, state: "paused", reason:
900
- "executor_not_listening", next}`, and `synopkgland status` shows the
901
- reason and next step. Regenerate or copy
902
- `backend/internal/app/executor.go` into existing projects.
903
-
904
- - `daemon.timeouts` defaults are now `{start: 30, stop: 30, status: 5,
905
- uninstall: 60}` (were `start: 5, stop: 10, status: 2`). On vt-nas under
906
- IO load a boot start took more than 5 s and a Connect revoke more than
907
- 9 s. The new `timeouts.uninstall` (2–120) bounds `uninstall_args`
908
- separately; it used to get `stop - 1`, so an app had to raise the SIGKILL
909
- wait to give its revoke time. DSM's package unit allows 3600 s for start
910
- and stop. `inspect` still accepts SPKs built before `timeouts.uninstall`.
911
- The manifest reference explains how to pick values from a loaded NAS.
912
-
913
- ### Approval, identity and uninstall fixes
914
-
915
- - `identity: standalone`: "Continue with DSM" and every live revalidation
916
- failed with `session_required` after a DSM web login, because DSM web
917
- sessions enable SynoToken and DSM answers the `id` cookie alone with 119.
918
- The backend now forwards the request's single `X-SYNO-TOKEN` header, which
919
- the DSM window's `ctx.call` already sends, when it checks the DSM session.
920
- Sessions without SynoToken still verify with the cookie alone. The token is
921
- never put in a URL, logged or stored. This also unblocks root-ops approval
922
- in an `identity: standalone` package, which stopped at `executor.control`.
923
- Existing projects need the updated `internal/identity` files.
924
- - When approval created and read back both tasks but the executor then did
925
- not start or report healthy, the window left the tasks behind, and with no
926
- running executor there was no task inventory for uninstall to delete them.
927
- The window now withdraws the tasks that approval created; tasks from an
928
- earlier approval stay.
929
- - After a wrong password or a withdrawn approval, the window panel showed
930
- only "Check status". It and the app's settings now keep the approve action.
931
- - Uninstall no longer logs `task cleanup unconfirmed` for every successful
932
- delete: EventScheduler `delete` v1 and TaskScheduler `delete` v2 answer
933
- `{"success":true}` without data, which now counts as confirmed. The task
934
- definitions are unchanged, so no new approval is needed; the executor
935
- digest changes.
936
- - The native root window (`@synopackageland/cli/authorization-sdk`) gave up
937
- after about 30 s with `root_timeout`, while a loaded NAS ran the bootstrap
938
- task for up to 4.5 minutes. It now keeps waiting while the task is queued or
939
- running, up to 10 minutes, reports `{op, state, elapsedMs}` through
940
- `options.progress`, and the authorization flow accepts the backend's ready
941
- grant after `root_timeout` or `root_task_failed`.
942
- - A DSM certificate that Node does not trust failed with only
943
- `fetch failed`. The CLI now reports `DSM_TLS_UNTRUSTED` with the origin and
944
- certificate error and points to `NODE_EXTRA_CA_CERTS`; certificate checks
945
- stay on.
946
- - Docs: on DSM 7.1 the purge timing lines are only in the journal
947
- (`journalctl -t synopkgland-executor`); `init -t native-go` does not include
948
- `armv7` in `arches`, so add it for an armv7 NAS.
949
-
950
- ### Deploy and status fixes
951
-
952
- - `status` no longer reports `blocked` for every `identity: standalone`
953
- project. It opens a short-lived App session from the administrator's DSM
954
- session, reads `executor.status`, then logs out. That needs an `https://`
955
- host; over `http://` the capability stays `blocked` and the reason says so.
956
- - `deploy` accepts the install wizard values of an `install-selected`
957
- delegate: `--param delegate_share=<Shared Folder>` or
958
- `--param delegate_home=true`. A first install without one now fails with
959
- `DEPLOY_PARAM_REQUIRED` and a usage line before anything is built, instead
960
- of failing inside DSM. Invalid values fail with `DEPLOY_PARAM_TYPE_INVALID`.
961
- - `deploy` and `status` work on a native layout (existing native product).
962
- `deploy` builds the NAS architecture only; an `aarch64` NAS uses a declared
963
- `armv8`.
964
- - The deploy note no longer says every upgrade needs approval: an
965
- executor-only upgrade stays ready; a policy digest change needs approval.
966
- - Uninstall logs per-phase purge timing with the package name to the system
967
- log (`synopkgland-executor`): `task cleanup timing` per task and
968
- `purge timing` for the task and file phases. The executor digest changes.
969
- - `add root-ops` and `add portal` print a notice on stderr when they raise
970
- `os_min_ver`.
971
- - `inspect` on an executor SPK describes the root boot and control tasks
972
- instead of saying the package needs no root privileges.
973
-
974
- ### DSM 7.1: root-ops and delegate approval work
975
-
976
- - DSM 7.1.1 has no `SYNO.Core.TaskScheduler.Root` or
977
- `SYNO.Core.EventScheduler.Root`, TaskScheduler stops at v3 and
978
- PasswordConfirm at v1, so approval failed with "Background file access is
979
- unavailable" and `status`/`deploy` showed `rootOps: unsupported — DSM 103`
980
- or `DSM 104`. DSM 7.1.1 answers a version above the API's maximum with 104
981
- (PasswordConfirm v2, TaskScheduler `get` v4) and a method missing at an
982
- existing version with 103 (TaskScheduler `list` v3). When DSM answers a
983
- scheduler read or the password check with 103 or 104, the app now uses
984
- PasswordConfirm v1, TaskScheduler `list` v2/`get` v3 and creates
985
- the tasks with EventScheduler `create` v1 and TaskScheduler `create` v3.
986
- The password is still confirmed first and both tasks are still read back
987
- exactly before anything runs. Other errors (such as 105, 119, 102 or a
988
- wrong password, 3121) never fall back, and a create is never retried. DSM
989
- 7.1 does not bind task creation to the password check; see the root-ops
990
- guide.
991
- - `status` and `deploy` read the control task the same way.
992
- - Uninstall on DSM 7.1 now removes the boot and control tasks. The executor
993
- looked the control task up with TaskScheduler `get` v4, which DSM 7.1.1
994
- refuses with 104, so it wrote no task inventory and left both tasks behind.
995
- - If approval stops after it created a task (for example, the boot task was
996
- created and the control task was not), the app deletes the tasks it created
997
- in that attempt instead of leaving an enabled root boot task that nothing
998
- owns. Tasks from an earlier approval stay.
999
- - A failed approval shows its reason and DSM error code in the app's settings
1000
- instead of only "Background file access is unavailable", and a refused
1001
- password reads as a password failure, not a scheduler failure.
1002
- - After the app's settings approve, the window's status line no longer shows
1003
- "needs-admin-approval: Task is missing" until a reload; the window panel and
1004
- the settings now follow each other's results.
1005
-
1006
- ### DSM 7.1: jobs import and file downloads
1007
-
1008
- - DSM 7.1.1 sends `SYNO.FileStation.Download` v2 replies without
1009
- `Content-Disposition`, so `bg.jobs.submit` and downloads failed with
1010
- `unavailable`. With no such header, a body of at most 4 KiB that decodes
1011
- as a DSM reply is still refused as that reply; any other body is the file.
1012
-
1013
- ### Docs: build your own armv7 Go code with Go 1.26.9
1014
-
1015
- - On armv7 DSM 7.1.1 (kernel 3.10), a program built with Go 1.26.0 crashed
1016
- at start (`futexwakeup returned -22`, SIGSEGV) and Go 1.26.9 ran. Other
1017
- 1.26 releases were not tested.
1018
-
1019
- ### init: next steps install frontend dependencies
1020
-
1021
- - `init -t native-go` with a Vue frontend now lists `(cd frontend && npm ci)`
1022
- before `synopkgland build`; without it the build fails with
1023
- `STANDALONE_UI_BUILD_FAILED`. `build` still does not install dependencies.
1024
-
1025
- ### Executor, daemon and gateway build with exactly Go 1.26.9
1026
-
1027
- - The CLI builds the executor, lifecycle helper, daemon controller and gateway
1028
- with `GOTOOLCHAIN=go1.26.9`, ignoring your `GOTOOLCHAIN`, `GOFLAGS`,
1029
- `GOEXPERIMENT`, `GOWORK` and `GOAMD64`/`GOARM64`, so every builder produces
1030
- the same executor digest. Go 1.21 or later downloads go1.26.9 when needed;
1031
- offline without it the build fails and names go1.26.9. A binary whose Go
1032
- build info names another release is refused.
1033
- - Template go.mod files now say `toolchain go1.26.9`. The executor digest
1034
- changes; it is anchored in INFO, so an upgrade with an unchanged policy needs
1035
- no new approval.
1036
-
1037
- ### INFO arch: four more DSM platforms
1038
-
1039
- - x86_64 SPKs now also list `broadwellntb`, `coffeelake` and `skylaked`, and
1040
- aarch64 SPKs list `rtd1619` (DSM Developer Guide Appendix A family members).
1041
- INFO `arch` now matches the platforms the package source maps to each family.
1042
-
1043
- ### 繁體中文摘要
1044
-
1045
- - 升級後重新建置套件;executor digest 改變。**從 0.7.0 建的套件升級需管理員再核准一次**(0.7.0 的
1046
- executor 開啟套件 `target` symlink 時未跟隨連結,無法自行安裝錨定的 executor);之後 0.7.1 起的
1047
- executor-only 升級不需核准。
1048
- - executor:kernel 3.10(DSM 7.1,如 DS416 armv7)上 executor-only 升級與執行中再核准不再出現
1049
- `pid executable mismatch`;INFO 錨定升級不再誤報 `executor upgrade refused`,也不再先印
1050
- 「paused: 需管理員核准」;`executor.status` 暫停時回 `paused`/`executor_not_listening` 與下一步。
1051
- - 面板:executor 不回應時可再按一次「Approve background access」免密碼重啟;
1052
- `executor_status_mismatch` 會寫出原因(不回應/digest 不同/status 請求失敗);
1053
- 啟動後的 `executor.status` 等待改為 500 ms→2 s 退避,10 s 預算不變。
1054
- - `daemon.timeouts` 預設改為 `{start: 30, stop: 30, status: 5, uninstall: 60}`,新增 `timeouts.uninstall`。
1055
- - `identity: standalone`:DSM 網頁 session(SynoToken)下「Continue with DSM」與 root-ops 核准修正;
1056
- 核准建立 task 後 executor 未啟動時自動撤回本次建立的 task;卸載不再誤記 `task cleanup unconfirmed`。
1057
- - 原生 root window 等待上限改為 10 分鐘並回報進度;`DSM_TLS_UNTRUSTED` 指向 `NODE_EXTRA_CA_CERTS`。
1058
- - `status`/`deploy`:standalone 專案的 status 改用短期 App session;`deploy` 接受
1059
- install-selected delegate 的精靈參數;支援 native layout;卸載記錄 purge timing。
1060
- - DSM 7.1:無 Root API 時改走 PasswordConfirm v1/TaskScheduler v2–v3/EventScheduler create v1,
1061
- 卸載會移除 boot/control task;`SYNO.FileStation.Download` v2 無 `Content-Disposition` 的下載修正。
1062
- - 建置:executor、lifecycle、daemon、gateway 一律以 Go 1.26.9 建置(armv7 kernel 3.10 上 1.26.0 會當機);
1063
- INFO `arch` 新增 `broadwellntb`、`coffeelake`、`skylaked`、`rtd1619`。
1064
- - 測試工具:`scripts/test/executor-bump.py` 收進 repo,會一併改寫視窗腳本內嵌的 executor pin。
1065
-
1066
- ## 0.7.0 (2026-10-10)
1067
-
1068
- Rebuild packages to pick up template, hook and executor changes. The executor
1069
- digest changed: apps approved under an earlier CLI need one administrator
1070
- approval after upgrading.
1071
-
1072
- ### Delegate: save destinations that last until revoked
1073
-
1074
- - `init --with delegate` / `add delegate` now declare `operations:
1075
- [files.moveFromStaging]`, `destinations: [save]`, `lifetime: until-revoked`,
1076
- `scope: install-selected`. Existing TTL declarations (`files.list` …
1077
- `files.write`, `scope: home`) keep their API and limits.
1078
- - The install wizard pre-selects home or a local share and an administrator
1079
- confirms it once. Choosing a destination in the SDK NasPicker grants it until
1080
- revoked, after fresh DSM session, UID and write checks; administrators run as
1081
- their own UID, never root. Read the choice from `delegate.status`
1082
- `selection`, `sdk.destination(key)` or Go `Service.Destination`.
1083
- - Daemon and export producers save through Go `Service.MoveFromStaging`. The
1084
- generated Background saves page (`createBackgroundSaveFacade`) lists, cancels
1085
- and revokes them and includes a working example (`ExportExample`). Daemon can
1086
- combine with an until-revoked delegate worker.
1087
- - Retrying `MoveFromStaging` with the same params never copies twice. Journal
1088
- errors `canceled`, `jobs_busy` and `jobs_unavailable` are retryable;
1089
- `spool_unsafe` and `journal_corrupt` are terminal.
1090
- - Cancel is idempotent per reference and survives executor crash, package
1091
- restart and logout. Revoke, scope change and disable settle copies in flight
1092
- as `canceled` with that reason (for example `scope-changed`); a scope change
1093
- no longer restarts the executor. Poll `ctx.executor.status()` while a change
1094
- answers `blocked`.
1095
- - Full device lifecycle acceptance (install → setup → reboot → upgrade → revoke
1096
- → uninstall) is still pending.
1097
-
1098
- ### Executor
1099
-
1100
- - Upgrades keep approval: each SPK writes its executor SHA-256 into INFO
1101
- (`synopkgland_executor_sha256`), and a new executor that matches it installs
1102
- without PasswordConfirm as long as the policy is unchanged. Policy changes
1103
- (operations, scope, destinations, root scripts) still need approval. `inspect`
1104
- refuses a missing, duplicated or different anchor.
1105
- - A root supervisor restarts the monitor after a crash or kill (1–60 s
1106
- backoff), so delegate and rootOps recover without a reboot.
1107
- - Every write publishes only through the executor handshake. A copy whose
1108
- executor died stops within 1 MiB and removes its `.part`; a lost publish
1109
- receipt settles `committed` without copying again.
1110
- - A package without an executor can add one in place on upgrade. Upgrade does
1111
- not approve an install-selected scope; confirm it in Settings.
1112
- - Uninstalling a stopped package no longer leaves root tasks and the monitor
1113
- running for ten minutes; the hook waits at most 50 s for the executor to clean
1114
- up.
1115
-
1116
- ### Native layout: per-architecture INFO trust anchors
1117
-
1118
- - `layout.anchors` maps `<key>_sha256` to a payload file. `build` writes that
1119
- architecture's SHA-256 of the file into INFO, so a product root helper can
1120
- trust a new build through the root-owned INFO instead of reapproval. Keys
1121
- match `^[a-z][a-z0-9_]{0,48}_sha256$`, the `synopkgland_` prefix is reserved,
1122
- and at most 16 are allowed. `inspect` refuses a changed, duplicated, missing
1123
- or undeclared anchor.
1124
-
1125
- ### App entry and Application Privileges
1126
-
1127
- - `entry: admin-only` admits administrators only, on every route including the
1128
- SDK routes; ordinary users get `admin_required`. `entry: app-privilege`
1129
- registers the app in Control Panel > Application Privileges and gates every
1130
- route on DSM's decision (`app_privilege_denied`). Omitting `entry` keeps the
1131
- previous behaviour and byte-identical SPKs.
1132
- - With an `entry`, an app route marked `authenticated` or `public` fails with
1133
- `STANDALONE_ENTRY_WIDENED`. `inspect` prints the entry mode and flags
1134
- `ENTRY_LAUNCHER_MISMATCH`.
1135
- - The executor applies the same rule: moves for a user DSM no longer allows are
1136
- refused (`APP_DENIED`).
1137
- - `ctx.user = {name, isAdmin}` lets UIs hide administrator settings; guard them
1138
- on the server with Go `api.RequireAdmin(caller)`.
1139
- - On Linux 3.10 kernels without `memfd_create` (DSM 7 on x86_64 avoton,
1140
- braswell, bromolow, grantley) the administrator and App Privileges bridges run
1141
- from an unlinked file in the package's own tmp instead of failing. That path
1142
- has no memfd seal. Verified on DSM 7.1.1 with an armv7 build only; no x86_64
1143
- Linux 3.10 model has been tested.
1144
-
1145
- ### Application Portal
1146
-
1147
- - `portal: {}` or `portal: {alias}` (native) and `integration.web.portal`
1148
- (Compose DSM-window apps); `synopkgland add portal` adds it. The install
1149
- wizard prefills the alias, upgrades keep it and an administrator activates it.
1150
- - `ctx.portal.mountPanel(element)` shows the link, or the alias form to
1151
- administrators; the Vue and plain starters mount it.
1152
- - A portal needs `os_min_ver` 7.3.2-86009 or later (DSM 7.1.1 rejects it with
1153
- error 320; 7.2 is untested). validate/build fail with
1154
- `STANDALONE_PORTAL_MIN_DSM` / `CONTRACT_PORTAL_MIN_DSM`, and `add portal`
1155
- raises a lower `os_min_ver`.
1156
-
1157
- ### Catalog metadata
1158
-
1159
- - An optional `metadata:` block (contact/support email, homepage, source and
1160
- issues URLs, license, localized long description, changelog, screenshots) is
1161
- validated and written to INFO for the package catalog.
1162
- - `metadata.changelog` may be a per-language map. Package Center shows one line
1163
- in the NAS language, so put the most important change first.
1164
-
1165
- ### Native authorization
1166
-
1167
- - `authorization_required` gains `reason: upgrade_reapproval` when an upgrade
1168
- revoked the old grant; the Gate template explains the next step in nine
1169
- languages. Products that sync the Gate need their helper to write and clear
1170
- the reason-only tombstone, which grants nothing.
1171
-
1172
- ### Lifecycle
1173
-
1174
- - Background packages restart a package user systemd manager that lost its
1175
- runtime socket; a failed worker start names the unit, exit code and socket
1176
- state.
1177
- - Stopping the worker is idempotent, fixing uninstall failure 314 for
1178
- delegate+daemon+jobs.
1179
-
1180
- ### Templates in nine languages
1181
-
1182
- - All template UI text and install, upgrade and uninstall wizard messages come
1183
- from catalogs in the nine DSM languages and follow DSM glossary wording. Sizes
1184
- use DSM's 1024-based KB/MB/GB. Rebuilt Vue and binary SPKs change bytes;
1185
- plain-UI SPKs are unchanged.
1186
- - Vue templates pin public `@synopackageland/ui@0.8.1`; the upload FilePicker
1187
- states `max 16 GB`.
1188
-
1189
- ### armv7 (32-bit ARM) for native-go and binary packages
1190
-
1191
- - `arches` accepts `armv7` (INFO `alpine alpine4k armada38x monaco`, DSM 7.2+);
1192
- the default stays `[x86_64, aarch64]`. It covers the Go CGI (plain or Vue UI),
1193
- `backend.kind: binary`, jobs, daemon, root-ops and delegate. Container
1194
- projects refuse it with `CONTAINER_ARCH_UNSUPPORTED`.
1195
- - The plain native-go CGI was verified on a DS416 (DSM 7.1.1, `alpine4k`).
1196
- armv7 jobs, daemon, executor and lifecycle helper are built and checked but
1197
- not yet verified on a NAS; `armada38x` and `monaco` are untested.
1198
- - `deploy` detects armv7 from an Annapurna Labs CPU and asks for `--arch` on a
1199
- Marvell CPU. x86_64 and aarch64 bridge, executor and lifecycle bytes are
1200
- unchanged by the armv7 work.
1201
-
1202
- ### Packaging
1203
-
1204
- - The npm tarball includes this CHANGELOG.md.
1205
-
1206
- ### 繁體中文摘要
1207
-
1208
- - 升級後重新建置套件;executor digest 改變,舊版 CLI 核准過的 app 需要管理員再核准一次。
1209
- - delegate:預設改為長期存檔目的地(`files.moveFromStaging`、安裝時選 scope、管理員確認一次、
1210
- 在 NasPicker 選目的地即授權至撤銷);Background saves 頁、`MoveFromStaging` 重送不重複複製、
1211
- 取消跨重啟保留。完整裝置生命週期驗收仍待完成。
1212
- - executor:INFO 錨定 executor SHA-256,policy 不變的升級免再核准;root supervisor 異常後自動重啟;
1213
- 卸載已停止的套件不再殘留 root task。
1214
- - 原生 layout:`layout.anchors` 依架構把 payload 檔 SHA-256 寫進 INFO,作為產品 root helper 的信任錨點。
1215
- - `entry: admin-only | app-privilege`(DSM 應用程式權限)、`ctx.user.isAdmin`、`api.RequireAdmin`;
1216
- 未宣告 entry 的 SPK 不變。Linux 3.10 無 memfd_create 時 bridge 改由套件 tmp 執行(無 memfd seal)。
1217
- - Application Portal(`portal:`、`add portal`、`ctx.portal`),需 os_min_ver 7.3.2-86009 以上;
1218
- 目錄 `metadata:` 寫入 INFO,`metadata.changelog` 可依語言分開。
1219
- - 授權契約新增 `reason: upgrade_reapproval`;背景套件 user manager 恢復、卸載 314 修正。
1220
- - 範本與安裝精靈全部九語系並依 DSM 用語;Vue 範本固定 UI 0.8.1。
1221
- - armv7(DSM 7.2+)支援 native-go 與 binary;plain CGI 已在 DS416 實機驗證,jobs/daemon/executor 尚未實機驗證。
1222
- - npm 套件附 CHANGELOG.md。
1223
-
1224
- ## 0.6.1
1225
-
1226
- - Restore native product packaging as a formal schema 2 `backend: {kind: native}`
1227
- route. Keep project-owned multi-binary payload/metadata layout, prepare/compile
1228
- argv hooks, HTTPS/SHA-256 toolkit acquisition and x86_64/aarch64/armv8/armv7.
1229
- `check`, `build` and `inspect --json` preserve package-only lifecycle/systemd
1230
- registration and reject root service accounts, privileged execution prefixes,
1231
- symlinks, setuid files and corrupt toolkit/payload inventories.
1232
- - Restore `@synopackageland/cli/authorization-sdk` as an explicit native-product
1233
- adapter with exact pinned-task identity, fresh administrator approval and
1234
- withdrawal-before-task-removal; do not change generated Go executor semantics.
1235
- Export `./native-layout` and retain direct `./dist/*` imports.
1236
- - Document field migration from schema 1 without changing product bootstrap pins,
1237
- per-build revocation/reapproval or dynamic DSM ABI declarations. Schema 1 remains
1238
- rejected; no compatibility aliases for retired CLI commands. Generated schema 2
1239
- check/build/status/logs and Connect/Gopeed APIs remain unchanged.
1240
- - Align SKILL.md with the shipped delegate/rootOps executor implementation and
1241
- its documented device acceptance boundaries.
1242
- - 正式補回 schema 2 原生產品 layout/hooks/toolkit/三架構及 authorization SDK。
1243
- lifecycle/systemd 維持 package account;root 僅由產品明確核准 digest,
1244
- 不自動授權、不取消逐 build 撤權再授權。新增 Reclaim 欄位 migration 指引。
1245
-
1246
- ## 0.6.0
1247
-
1248
- - Adopt UI 0.8.0 and its renamed `syno-pkg-land-ui` executable (no old alias).
1249
- Pin generated Vue apps and all overlays to the public npm artifact and integrity.
1250
- Skills discover the installed prompt/catalog/describe contract with
1251
- `npx --no syno-pkg-land-ui`; one-off use explicitly selects the npm package.
1252
- Render defaults to `syno-pkg-land-ui-render`. CSS/data selectors use the
1253
- `syno-pkg-land-ui` namespace without old aliases (including select attributes).
1254
- Add a regression audit for executable names and retired CSS/data selectors in
1255
- skills, templates, source, tests, docs and scripts; only released history is exempt.
1256
- 跟進 UI 0.8.0 的 CLI 改名及公開 npm integrity;AI 指引維持版本無關,
1257
- 使用已安裝契約,並防止指令名與 selector 回退。預設截圖目錄改為
1258
- `syno-pkg-land-ui-render`;CSS/data selector 全面使用新 namespace,不保留 alias。
1259
-
1260
- - Add version-independent Vue UI guidance alongside JSON UI guidance. All
1261
- generated projects include AGENTS.md and a CLAUDE.md pointer to discover the
1262
- bundled skills and run local check/build/inspect before authorized NAS work.
1263
- Template design notes use installed UI contracts while dependency pins remain.
1264
- 新增 Vue UI skill;所有 scaffold 提供 AI 入口與本機驗證流程,設計文件依已安裝
1265
- SDK 契約,依賴鎖定不變。
1266
-
1267
- - **Breaking: remove `./authorization-sdk` and the legacy root authorization
1268
- templates and adapters.** Use delegate/root-ops capabilities and the protected
1269
- executor with one-time administrator approval; no compatibility exports remain.
1270
- **不保留相容**:移除 `./authorization-sdk` 與舊 root authorization 模板/adapter,
1271
- 改用 delegate/root-ops 能力及受保護 executor 的一次管理員核准。
1272
-
1273
- - Pin generated Vue apps to public `@synopackageland/ui@0.8.0` with its npm
1274
- integrity; keep bounded app-local modal roots, embedded CSS and unique mount
1275
- ID prefixes, and explicitly retain DSM window layout at wide sizes.
1276
- Add `skill show ui-json` with installed prompt/catalog/schema discovery,
1277
- strict validation and screenshot/layout checks through the published `syno-pkg-land-ui`
1278
- package bin. Update the template
1279
- DOM test dependency and website configuration to remove gate warnings.
1280
- Vue 模板固定公開 UI 0.8.0 與 integrity,維持 DSM 的 app scope、embedded CSS、
1281
- 唯一 mount ID 與 window layout;新增不固定版本的 JSON UI skill,依已安裝
1282
- catalog/schema 產生、嚴格驗證並 render,直接使用套件的 `syno-pkg-land-ui` bin。
1283
-
1284
- - **Delegate (C), P2/v10 (through 26030e74):** `init --with delegate` / `add delegate` imply jobs;
1285
- manifest operations use the five fixed files.* IDs with home/shared scope.
1286
- The worker stages downloads and writes as the consenting user after browser
1287
- close, without overwriting existing files or retaining DSM credentials.
1288
- C/R share one-password boot/control task setup and digest approval; stop pauses,
1289
- start resumes identical pins, changed policy requires reapproval, and uninstall
1290
- requests asynchronous own-task/protected-state purge (normally seconds with a
1291
- healthy monitor), with manual cleanup guidance. Task deletion requires byte-for-byte
1292
- equality with canonical scripts regenerated from package identity and approved pins.
1293
- Purge reapproval recreates tasks and writes `start` before boot; mailbox failure
1294
- blocks boot. Local fake-DSM/real-executor lifecycle tests cover this recovery.
1295
- Package-directory/INFO identity absence immediately pauses the socket/requests,
1296
- retaining the monitor, approved runtime, delegations and tasks. Polling every
1297
- 500 ms must confirm 10 continuous minutes of absence before fallback purge;
1298
- reappearance or unverifiable absence resets the timer, with uncertainty staying paused.
1299
- Recovery rereads even an unchanged mailbox: matching-pin `start` resumes,
1300
- changed pins stay paused/needs-admin-approval, `stop` stays paused, and `purge`
1301
- immediately clears state. Temporary DSM-upgrade gaps preserve protected state;
1302
- preuninst purge remains immediate, with asynchronous cleanup. Boot's absent-package
1303
- retained-pin purge remains immediate. Local tests cover upgrade gaps, recovery,
1304
- timer/reset rules and immediate preuninst purge; device acceptance remains separate.
1305
- Malformed AppPriv responses fail closed, including duplicate keys/target entries,
1306
- null/missing data and nested error keys. Matching token header aliases are allowed;
1307
- conflicting/repeated headers, duplicate id cookies and controls are refused.
1308
- Empty/partial caller auth never falls back to worker revoke. Job admission reserves
1309
- before creating grants outside the journal lock; retry `jobs_busy` with the same key.
1310
- Writes fsync an exclusive hidden temporary then publish a hard link without overwrite;
1311
- cancellation cleans the temporary and leaves no partial final file.
1312
- Add bilingual developer and administrator guides covering typed Files, stable
1313
- errors, AppPriv defaults/denies and DSM-retained directories/accounts.
1314
- Full device lifecycle acceptance remains separate from source/local tests.
1315
- Delegate 隱含 jobs;以本人身分離線寫檔、不覆寫、不保存 DSM 憑證。
1316
- C/R 共用一次密碼設定與 digest 核准;中英文文件說明委託/撤銷、
1317
- typed Files、穩定錯誤碼、paused/purge、手動清理與 DSM 保留界線。
1318
- AppPriv 異常回應 fail-closed;token alias 同值允許、衝突/重複及控制字元拒絕,
1319
- 空/不完整 caller auth 不降級為 worker。`jobs_busy` 沿用相同 key 重試。
1320
- 隱藏暫存檔 fsync 後以 hard link 發布,不覆寫、取消不留半個正式檔。
1321
- 特權清理非同步,monitor 正常時通常數秒完成;刪 task 前完整 script 須逐字相符。
1322
- Purge 後重新確認密碼、重建 task,boot 前 mailbox 重設 `start`;寫入失敗不執行 boot。
1323
- 套件目錄/INFO identity 消失立即關 socket、取消/drain 請求並暫停,保留 monitor、
1324
- 核准 runtime、委託與 task;每 500 ms 輪詢確認連續消失達 10 分鐘才後備 purge。
1325
- 重新出現或無法確認消失皆重設計時;無法確認時維持 paused。
1326
- 恢復時重讀未變 mailbox:`start` 且 pins 相同才恢復,pins 改變維持 paused/
1327
- needs-admin-approval,`stop` 維持暫停,`purge` 立即清理。DSM 升級短暫空窗不誤清;
1328
- preuninst purge 仍立即處理、非同步清理;boot 套件不存在時驗 retained pins 立即 purge 不變。
1329
- 本機測試涵蓋升級空窗、恢復、計時/重設及 preuninst 立即 purge,不代表裝置驗收。
1330
-
1331
- - **Breaking: schema 2 and task-based CLI, with no compatibility layer.**
1332
- Native-go and binary now use dsm-package.yaml schema 2 with backend.kind and
1333
- declared capabilities. Use init → check → build → deploy → status, positional
1334
- inspect <spk> and logs. The former build-spk/validate/info/log commands, native
1335
- layout/native.yaml, native privilege/background/fileStation fields and old
1336
- capability flags are removed; there are no aliases or migration shims.
1337
- Schema 2 uses --with files,jobs,delegate,login,root-ops or add to extend native-go:
1338
- private owner-bound data/jobs (A), live caller-session files/tree/transfers (B),
1339
- standalone identity (I), offline delegation (C), and fixed administrator
1340
- operations (R). C/R share one-time protected executor approval.
1341
- Manifest delegate.operations uses
1342
- files.list/files.info/files.createFolder/files.checkPermission/files.write
1343
- directly. Check/build/inspect/deploy/status support structured JSON results.
1344
- Compose retains its separate contract and uses init -t compose, optionally
1345
- importing with --from. No publish or signing command is added.
1346
- **不保留相容**:native-go/binary 改用 schema 2;CLI 依工作流程改名,
1347
- 移除舊欄位、旗標、指令與 native layout,不提供 alias 或 shim。
1348
- A/B/I/C/R 以能力宣告選用;C/R 共用 executor 一次核准。
1349
-
1350
- - Generate background submit/schedule keys with cryptographic getRandomValues,
1351
- which works on DSM HTTP; do not require secure-context randomUUID.
1352
- 背景工作及排程以 getRandomValues 產生請求鍵,可在 DSM HTTP 使用;
1353
- 不再依賴僅限安全環境的 randomUUID。
1354
-
1355
- - Give interactive File Station uploads their own bounded twenty-minute
1356
- response-header deadline without changing the shared metadata transport.
1357
- 互動 File Station 上傳使用獨立的二十分鐘回應標頭期限,不修改共用查詢 transport;
1358
- 保留目前 session、不覆寫、不重試及寫入結果不確定契約。
1359
-
1360
- - Frame native-Go CGI JSON with exact byte lengths, including Unicode and
1361
- refusal/serialization failures; retain DSM's HTTP-200 JSON error convention.
1362
- native-Go CGI JSON(含 Unicode、拒絕及序列化失敗)提供精確位元組長度,
1363
- 並保留 DSM 的 HTTP 200 JSON 錯誤契約。
1364
-
1365
- - Replace R2 publication with a private GitHub catalog and streamed Release SPKs.
1366
- - Serve live bilingual catalog pages and architecture-specific manual downloads.
1367
- - Remove the two web demo templates; retain native-go, binary and the single compose template.
1368
-
1369
- ## 0.5.3
1370
-
1371
- - Ship the native-go root authorization template with build-pinned plans,
1372
- the canonical Vue gate and nine language catalogs, explicit native approval,
1373
- exact disabled tasks, bounded legacy terminal proof and lifecycle withdrawal.
1374
- - Export `@synopackageland/cli/authorization-sdk` for reviewed native-layout
1375
- adapters to consume the same host runtimes and canonical template resources.
1376
- - Let an explicitly supplied native-layout withdrawal callback finish before
1377
- removing tasks; failed or missing callbacks abort withdrawal.
1378
- - Legacy DSM authorization has no server-enforced one-use create token;
1379
- modern DSM keeps its native one-use PasswordConfirm token contract.
1380
- - Isolate packed Docker runtime smoke in temporary share directories and its
1381
- own Compose project, including owned-volume cleanup. The 0.5.1 release tag
1382
- did not publish because the existing smoke omitted installer share values.
1383
- - Use a pinned long-running imported runtime fixture for readiness smoke;
1384
- the default hello-world demo exits immediately. The 0.5.2 workflow was
1385
- cancelled before publication when CI exposed that fixture race.
1386
- - Retain bounded artifact-test diagnostics up to eight MiB and propagate
1387
- real frontend test failures rather than hitting Node's default buffer limit.
1388
-
1389
- ## 0.5.0
1390
-
1391
- - Add opt-in native layout packaging for existing products: argv-only build hooks,
1392
- checksum-pinned HTTPS toolkit caching, custom package-user metadata/payload
1393
- layouts, explicit ELF linkage assertions, DSM armv8/armv7 and build overrides.
1394
- - Inspect native layouts with complete payload hash/mode and INFO checksum checks;
1395
- generated templates retain their existing privilege and architecture contracts.
1396
-
1397
- - **GitHub CI and tag-based npm releases** (CI-LAND). Add shared full gates, Docker
1398
- runtime/tarball smoke and credential-free release dry runs for both npm packages.
1399
- Version tags require matching metadata, default-branch ancestry, an unpublished
1400
- version and versioned CHANGELOG notes. Push scripts now use the existing GitHub
1401
- origin; `push:all` preserves the separate Community Apps synchronization.
1402
-
1403
- - **Vue templates pin UI 0.5.7 and let the UI text follow the app locale**
1404
- (TEMPLATE-UI-057). Every `native-go --ui vue` project pins
1405
- `@synopackageland/ui@0.5.7` (integrity `sha512-egHKoU4J...`). Each app root now wraps
1406
- its content in `ConfigProvider locale`, set from the DSM language by the template's
1407
- existing `uiLocale()` mapping, so the UI's built-in text follows it in the nine
1408
- built-in locales. The template drops the per-control `FilePicker` locale and the
1409
- `Link` `download-label` that only repeated those defaults; a template test requires
1410
- the provider and rejects those overrides. 0.5.7 also resolves `FormItem`
1411
- `direction="auto"` by the item's own width, stacks `SettingsRow` below 520px and
1412
- keeps `KeyValueRow` values wrapping; the template needed no mitigation for any of
1413
- these. Existing 0.5.6 projects upgrade by regenerating, or by changing the pin and
1414
- re-running `npm ci`.
1415
- - **Vue templates use the UI controls instead of native form elements**
1416
- (TEMPLATE-SELECT). The job form's worker and operation pickers and the
1417
- background schedule interval are now `Select`; the schedule consent is a
1418
- `Checkbox`; the job, schedule, sample-note and folder forms are `Form` +
1419
- `FormItem` + `FormActions` (vertical, controls capped at `md`), and the
1420
- hand-written `.integration-field` label CSS is gone. Labels are wired to
1421
- `Input` and `Textarea` through the FormItem control id. A template test now
1422
- fails when any `.vue` file renders a native `<select>`, `<option>`,
1423
- `<textarea>` or `<label>`, or a `FormItem` outside a vertical `Form`. Three UI
1424
- gaps remain and are not worked around with custom components: `Select` does
1425
- not take the FormItem control id (each Select carries a labelled
1426
- `role="group"` so it keeps an accessible name), the upload stays a native
1427
- `<input type="file">` because `FilePicker` hard-codes English "Choose files" and
1428
- "max" text, and the file download keeps a native `<a>` (no link component).
1429
- Existing projects pick this up only by regenerating; nothing changes in the
1430
- manifest, routes or backend.
1431
- - **Tarball smoke covers the default Vue UI and explicit plain UI**
1432
- (SMOKE-PACK-VUE). Install each generated Vue frontend with its lockfile before
1433
- building; keep both architectures and explicit root fixtures, then binary and
1434
- container packaging. The container smoke now selects `--arch x86_64` for
1435
- `-o app.spk` and its deployment plan, as explicit output requires one arch.
1436
- Docker runtime QA is opt-in with `smoke-pack.sh --docker`
1437
- and is explicitly skipped otherwise. Failed npm UI builds without installed
1438
- dependencies now name the UI directory and suggest `npm ci` (or `npm install`
1439
- without a lockfile) before retrying `build`; builds never install implicitly.
1440
-
1441
- - **Vue templates pin UI 0.5.5 and use PageContent** (TEMPLATE-UI-055). Every
1442
- `native-go --ui vue` project pins `@synopackageland/ui@0.5.5` (integrity
1443
- `sha512-pLQmZqNH...`). The sidebar shell now wraps the page header and the page
1444
- content in the library `PageContent`, so a maximised window keeps both on the
1445
- same left and right edges; the page area scrolls inside it. 0.5.5 also adds
1446
- `CardGrid`, `FormActions`, `RepeatRows` and `fieldWidth`; the template has no
1447
- card lists or custom width CSS, so it needs none of them. Default change to
1448
- know when upgrading: form controls are capped at `md` (320px) unless you pass
1449
- `field-width="full"`. Existing 0.5.4 projects upgrade by re-running `npm ci`
1450
- after the pin change.
1451
-
1452
- - **Vue templates pin UI 0.5.4** (TEMPLATE-UI-054). Every `native-go --ui vue`
1453
- project now pins `@synopackageland/ui@0.5.4` (integrity `sha512-mzuGUO+8...`).
1454
- 0.5.4 is visual only and changes nothing in the template source: under the
1455
- embedded theme, Card, EmptyState, PageHeader and WizardStep titles no longer
1456
- carry a default margin, the expand buttons of SettingsSection, Card and
1457
- ExpandableItem lose their grey box, and dashed separators draw only the bottom
1458
- edge. Existing 0.5.3 projects upgrade by re-running `npm ci` after the pin change.
1459
-
1460
- - **Root tasks retain DSM 7.1 support.** Probe `list v3` and use the legacy
1461
- `create v3`, `list v2`, `get v3`, `run/delete v2`, and `view v1` only on code
1462
- 103. Preserve native password confirmation while distinguishing tokenless
1463
- success from cancellation; DSM 7.1 has no server-side root-creation password
1464
- token. Require a new successful completion record within a bounded deadline,
1465
- exact task identity, and no mutation retries. Root init, validation, and build
1466
- keep a consistent minimum of DSM 7.1-42661.
1467
- - **Vue templates pin UI 0.5.3** (TEMPLATE-UI-053). Every `native-go --ui vue`
1468
- project pins `@synopackageland/ui@0.5.3` in `frontend/package.json` and its
1469
- npmjs lockfile (integrity `sha512-ieKdf0nB...`). 0.4.2 to 0.5.3 is additive for
1470
- the template: new components (Select search and groups, Combobox,
1471
- MultipleSelect, SuperboxSelect, Grid/Row/Col, Space, Affix, Breadcrumb,
1472
- Pagination, SegmentControl, Card, Panel, Fieldset, Form, Notification, and more),
1473
- DataGrid grouping/expansion/reorder, checkable and draggable TreeView, DropdownMenu
1474
- submenus, and `Input`/`Dialog`/`SettingsSection`/`Callout` options with unchanged
1475
- defaults. Visual changes: Tabs and SectionTabs share a sliding marker with a
1476
- 160ms panel fade, the Spinner is a full rotating ring (both honour
1477
- `prefers-reduced-motion`), selected tab labels are blue, and the DataGrid footer
1478
- draws both borders and fills its flex container. The template needed no API
1479
- change; it now uses the library `Checkbox` and `Input` for the consent and limit
1480
- fields instead of native inputs. Existing 0.4.2 projects upgrade by re-running
1481
- `npm ci` after the pin change.
1482
-
1483
- - **Templates never copy a local `node_modules`/`dist`** (TEMPLATE-COPY). A
1484
- template checkout used for development could leak its own install directory or
1485
- build output into a generated project, shadowing the project's `npm ci`.
1486
-
1487
- - **Vue templates pin UI 0.4.2** (TEMPLATE-UI-042). Every `native-go --ui vue`
1488
- project pins `@synopackageland/ui@0.4.2` in `frontend/package.json` and its
1489
- npmjs lockfile. 0.4.1/0.4.2 are visual, backward-compatible control changes with
1490
- no API change: controls share the `--ui-control-*` tokens and
1491
- `--ui-radius-control`, so overriding `--ui-radius-sm`/`--ui-radius-md` no longer
1492
- reshapes them; a disabled grey button keeps its surface, a loading button keeps
1493
- full strength, and the Switch track padding is reset. Existing 0.4.0 projects
1494
- upgrade by re-running `npm ci` after the pin change.
1495
-
1496
- - **Private-copy background schedules (S1-a)**: hourly/daily/weekly owner-scoped
1497
- creation, disable/delete, 8-definition limit, durable atomic trigger checkpoints,
1498
- missed-without-catch-up recovery, revision mismatch disable and explicit admin
1499
- re-enable. The Vue panel covers nine locales. Imported/pinned copies count toward
1500
- quotas; completed results never replay and owner purge also removes schedules.
1501
- Package account only, no stored credentials or offline personal ACL access.
1502
- Evidence: local gates and vt-nas DSM 7.4 x86_64 controlled-due-time tests
1503
- (two runs per owner, stop/start, +1 upgrade, disable and fresh normal1 browser
1504
- readback). This is not an hour-long cadence or reboot guarantee.
1505
-
1506
- - **Minimum DSM 7.1 and multiple architectures** (DSM-COMPAT). Every template now declares
1507
- `os_min_ver: "7.1-42661"` (DSM 7.1; before: `7.2-64570`, `7.2-69057` or `7.2-60000`).
1508
- `init --min-dsm` and `build --min-dsm` take `7.0`, `7.1`, `7.2` or a full
1509
- `7.x-build` to override it. `build --arch` accepts a comma-separated list
1510
- - **Per-runtime minimum DSM and multiple architectures** (DSM-COMPAT, DSM-COMPAT-2).
1511
- `native-go` and `binary` declare `os_min_ver: "7.1-42661"` (DSM 7.1; before:
1512
- `7.2-64570`, `7.2-69057` or `7.2-60000`). The container templates declare
1513
- `7.2.1-69057` (DSM 7.2.1), and `init --min-dsm`/`build --min-dsm` refuse a
1514
- container value below it (`MIN_DSM_CONTAINER_FLOOR`) because Container Manager's
1515
- docker-project worker only exists from DSM 7.2.1; `inspect` reports the same
1516
- gap on an already-built container SPK. `--min-dsm` takes `7.0`, `7.1`, `7.2`,
1517
- `7.2.1` or a full `7.x-build`. `build --arch` accepts a comma-separated list
1518
- (`x86_64,aarch64`); without it every family is built, one SPK per family (binary and
1519
- gateway container Apps used to require `--arch`). `inspect` flags an INFO `arch`
1520
- that mixes families or names an unknown platform, a CGI built for another family than
1521
- INFO declares, and an `os_min_ver` that is not a DSM 7 `<version>-<build>`. The
1522
- x86_64 INFO platform list now includes `kvmcloud`, `epyc7003` and `epyc7003ntb`.
1523
- Only DSM 7.4 x86_64 has been run.
1524
- - **Ordinary-user background jobs (U1)**: administrators may independently enable
1525
- `allowOrdinaryUsers`. Fresh DSM username/UID ownership isolates list/get/cancel/result,
1526
- counts and idempotency keys; unknown UIDs fail closed. Per-owner admission is bounded
1527
- to 2 pending jobs and 1 MiB including reserved results, under the same spool lock.
1528
- Administrators can cancel all jobs and explicitly purge an exact owner with durable
1529
- audit and running-job drain. The Vue panel and all nine locales cover both roles.
1530
- No root or stored user credentials; S1-a adds private-copy schedules below.
1531
-
1532
- - **Container gateway directory groups** (GW-DIRECTORY-GROUPS). Accounts absent
1533
- from `/etc/passwd` now resolve groups through DSM NSS using the fixed `/usr/bin/id`
1534
- as the package account. Qualified `DOMAIN\group` and `group@domain` names retain
1535
- their exact domain and case, including internal spaces; lookup failure denies.
1536
- The SDK caches nothing (TTL 0); DSM NSS caches and already-open streams remain
1537
- limitations. AD resolver allow/deny checks passed in a non-root DSM 7.4 package
1538
- CGI; LDAP, AD-user browser login and group-removal propagation remain unverified.
1539
- Rebuild container SPKs to obtain the change.
1540
-
1541
- - **`synopkgland images pin` resolves and writes container image digests**
1542
- (IMAGE-DIGEST-CLI). It reads every service image in `compose.yaml`, asks the
1543
- registry for the current multi-arch manifest digest over the OCI Distribution
1544
- API (anonymous Bearer token flow for Docker Hub and ghcr; no Docker daemon or
1545
- `buildx`), and rewrites the reference to `tag@sha256:…` in place, keeping
1546
- comments and file layout. `--check` reports unpinned and stale pins without
1547
- writing and exits non-zero. `inspect`'s unpinned-image finding now points at
1548
- the command. Evidence: local unit tests against a fake registry plus one manual
1549
- Docker Hub resolution of `busybox:1.36`; not verified on a real DSM.
1550
- - **Container upgrade wizard no longer breaks on an untouched Run-as control; numeric
1551
- command arguments are rejected for every Compose route** (WIZARD-LIVE-CHECK).
1552
- Measured on vt-nas (DSM 7.4-90069) with the `compose-import` template: an untouched
1553
- upgrade submitted `RUN_AS` pre-filled with the name of the first Shared Folder, `preinst`
1554
- failed, and DSM, which removes the old version before it runs the new `preinst`, left the
1555
- package not installed (data kept). A `dsm_user` now has the same "Change Container Run-as"
1556
- box as a Shared Folder: on an upgrade the submitted user only counts when that box (or
1557
- `deploy --run-as`) is set; otherwise the installed user is kept. Also on vt-nas: the
1558
- Docker Project worker drops YAML quotes, so `command: ["sleep", "3600"]` ended the
1559
- install as broken (`failed to acquire postinst worker`). `synopkgland check` and
1560
- `build` already rejected that on the static route; the dynamic route (install
1561
- parameters, which includes `compose-import`) now rejects it too
1562
- (`COMPOSE_NUMERIC_COMMAND_ARG_FORBIDDEN`).
1563
- - **`install.sharedFolder`** (T-0120): native-go packages can declare a Shared Folder (`select` wizard or `create`
1564
- via DSM `data-share`), stored by name in `var/config.json`; `init --shared-folder select|create` and an `inspect`
1565
- check are included. In `select` mode the administrator grants the package account access afterwards.
1566
- - **Container gateway accepts a query SynoToken; known limit with DSM CSRF protection on**
1567
- (GW-QUERY-TOKEN). On `GET`, `HEAD` and `OPTIONS` with an empty `X-SYNO-TOKEN`, the
1568
- gateway hands a `?SynoToken=` value to `authenticate.cgi` (header wins; still stripped
1569
- upstream); state-changing methods are unchanged. Measured on DSM 7.4 with "Improve
1570
- protection against cross-site request forgery attacks" on: opening a container App
1571
- from the DSM desktop sends no token at all, so the gateway answers `401`
1572
- `session_required`. Known limitation: turn the setting off or use the `native-go`
1573
- template.
1574
- - **Container gateway: readable errors, SSE and WebSocket** (GW-ERRORS-WS). DSM's web
1575
- server replaces the body and headers of any CGI reply with status `403`, `404`,
1576
- `500`, `502`, `503` or `504` by its generic error page (measured on DSM 7.4-90069;
1577
- every other status passes whole). The gateway therefore no longer sends those
1578
- statuses itself: `forbidden` and `token_required` arrive as `401`, a stopped
1579
- container as `521` with `Retry-After`, a timeout as `524`, an invalid reply as
1580
- `520`; every gateway error carries `X-Synopkgland-Error` (the code) and, for a
1581
- stand-in, `X-Synopkgland-Status` (the status it replaces). **Behaviour change:**
1582
- those statuses were `403`/`503`/`504`/`502`/`500` before. A request header
1583
- `X-Synopkgland-Keep-Error-Body: 1` makes the same swap for the container's own
1584
- error replies so an App can read its JSON error bodies. Server-Sent Events pass
1585
- through unbuffered and **WebSocket now works** (it answered `501`): DSM hands both
1586
- directions of an upgraded connection to the CGI, which the gateway uses; the
1587
- handshake must pass the same same-origin check as a state-changing request
1588
- (`cross_site_blocked`), because a browser cannot add `X-SYNO-TOKEN` to a handshake. Other protocol upgrades stay
1589
- `501`. See [Container access control](/docs/guides/container-access/).
1590
- - Fix: **`dsm-groups` with `groups: [users]` let nobody in.** DSM keeps no member
1591
- list for `users` (every local account has it as its primary group), so the gateway,
1592
- which only read member lists from `/etc/group`, answered `403` to everyone, including
1593
- administrators (reproduced on vt-nas). The gateway now also counts the primary gid
1594
- from `/etc/passwd` as membership and fails closed if it cannot read that file.
1595
- Rebuild the SPK to pick it up. Real-account `dsm-groups`/`dsm-admins` checks passed on
1596
- vt-nas (x86_64); the aarch64 gateway is verified in an arm64 Linux VM only, not on an
1597
- aarch64 DSM NAS.
1598
- - **Gateway CSRF check no longer relies on the SynoToken value** (GW-TOKEN). On a NAS
1599
- where DSM's "Improve protection against cross-site request forgery attacks" is off (vt-nas: `enable_syno_token="no"`), DSM
1600
- issues one fixed placeholder token to every session and validates nothing. The container
1601
- gateway now also requires `Sec-Fetch-Site: same-origin` and/or an `Origin` equal to the
1602
- request `Host` for `POST`/`PUT`/`PATCH`/`DELETE` (answer `cross_site_blocked`, sent as `401` because DSM replaces `403` bodies) and a
1603
- well-formed `X-SYNO-TOKEN`; safe methods are unchanged. Browsers that send neither
1604
- header on a state-changing request are refused. Verified on a real DSM 7.4 as an
1605
- administrator and an ordinary user (foreign `Origin`, `null`, `Sec-Fetch-Site`
1606
- cross-site/same-site, missing evidence refused; same-origin passes). The token path with
1607
- the DSM setting on is untested. Rebuild container SPKs to pick it up. See
1608
- [Container access](/docs/guides/container-access/).
1609
- - **Container templates pin their image by digest** (CONTAINER-PULL): `hello-files` and `compose-import` now use
1610
- `image: <tag>@sha256:…`. `build` writes `force_pull: false` into the
1611
- `docker-project` resource when every service image is digest-pinned and keeps
1612
- `force_pull: true` while any image is a bare tag, so an install no longer
1613
- re-resolves a moving tag that other containers share. `inspect` reports each
1614
- service whose image is not digest-pinned. Behaviour for projects with unpinned
1615
- images is unchanged. Not verified on a real DSM.
1616
- - **Breaking: container Apps are DSM-authenticated by default.** A container App
1617
- with an `adminport` is now served through a gateway CGI
1618
- (`/webman/3rdparty/<package>/cgi/gateway.cgi/…`) that runs as the package
1619
- account, checks the DSM session and forwards to the container. Without a valid
1620
- session it answers `401`; unsafe methods need `X-SYNO-TOKEN`; a stopped container
1621
- gives `521` with `Retry-After` (not `503`: DSM would replace its body). New `integration.web.access`: `dsm-users`
1622
- (default), `dsm-admins`, `dsm-groups` (+ `groups`), `public`. No Web Station
1623
- portal is registered unless `public`. The container port must be bound to
1624
- loopback (`127.0.0.1:<adminport>:<port>`): `check` fails with
1625
- `COMPOSE_GATEWAY_PORT_EXPOSED`, and `inspect`/`inspect` report
1626
- `CONTAINER_PORT_EXPOSED` and `CONTAINER_ANONYMOUS_PORTAL`. The gateway is
1627
- compiled Go, so building such an App needs Go and `--arch`, and the SPK is no
1628
- longer `noarch`. Privilege stays `run-as: package` with no root task.
1629
- **Migration:** bind the Compose port to `127.0.0.1`, rebuild with a higher
1630
- `build` and an `--arch`, then upgrade (the old `/<alias>/` portal and bookmarks
1631
- go away); or set `integration.web.access: public` to keep the anonymous
1632
- behaviour. See `website` guide “Container access control”.
1633
- - Add best-effort `revealFile` selection recovery using File Station 1.4.x
1634
- private structures, with feature detection and silent cancellation on absent or
1635
- throwing APIs. One AppLaunch binds its callback receiver; only that receiver's
1636
- model may be selected once. Interaction, navigation away, close, disposal and
1637
- replacement requests cancel observers. Named bounds are 5000 ms total, 100 ms
1638
- checks and 300 ms quiet time, without device latency proof. `requested` still
1639
- does not mean `selected`. Actual effectiveness on DSM 7.4 / File Station 1.4.4
1640
- awaits a rebuilt-SPK device matrix; no device fix is claimed.
1641
-
1642
- - Fix `ctx.host.activateWindow()` to prefer DSM `toFront()`, calling `show()`
1643
- first for hidden/minimized windows; `activate()` remains a compatibility
1644
- fallback. `requestClose()` follows DSM standalone truthiness: falsy values
1645
- (including desktop `undefined`) permit the normal close path, while a truthy
1646
- standalone-main check refuses it. `canClose === false` and host listeners still
1647
- veto. Both methods return `requested`, not completion proof; rebuilt-SPK DSM
1648
- validation remains pending. Source: `7553c24`, `2842634`.
1649
- - Add opt-in package-owned foreground `daemon` to native manifests, with
1650
- generated supervision and lifecycle hooks. Daemon-free packages keep their
1651
- existing lifecycle; no machine-wide service is required.
1652
- - Add per-mount `ctx.host` platform/title adapters, guarded window activation
1653
- and close requests, app/external-URL navigation, resize/reopen subscription
1654
- cleanup, and File Station folder navigation/file reveal. Standalone web uses
1655
- explicit fallbacks; file reveal is not preview. File preview/details/settings
1656
- and host theme/language subscriptions remain unsupported. Final DSM runtime
1657
- behavior and ordinary-user navigation permissions still need NAS validation.
1658
- - Fix SPK inspection to read the build-specific unique window bundle.
1659
-
1660
- ### Known limitations
1661
-
1662
- - **File reveal remains a request:** on DSM 7.4-90069 with File Station
1663
- 1.4.4-2221, one `ctx.host.revealFile()` call was observed opening the correct
1664
- folder without selecting/highlighting the file. A new synthetic fixture retest
1665
- selected the file with both `/home` and `/homes/admin` inputs, once each
1666
- (confidence **high**). The earlier non-selection was not reproduced; its cause
1667
- remains **unknown**. `requested` only means a selection request was issued;
1668
- it does not guarantee selection, preview, existence or ACL access. No runtime
1669
- fix is claimed. Source: V1 device validation and fixture retest, 2026-10-04.
1670
- - Vue templates pin public `@synopackageland/ui@0.4.0`, including app-scoped
1671
- modals and unique mount IDs, with npmjs-only lockfiles and imports. External
1672
- developers do not need an internal registry account.
1673
- - CLI 0.4.0 targets npmjs `latest`; versions 0.1.0–0.3.10 remain available without
1674
- deprecation or unpublishing.
195
+ - `native-go`: a Go backend with a Vue UI (or `--ui plain`).
196
+ - `native-python`, `native-node`, `native-rust`: the same App with a Python,
197
+ Node.js (TypeScript) or Rust backend behind the SDK's Go runtime.
198
+ - `binary`: a static Linux program you build yourself.
199
+ - `compose`: an existing Docker Compose app (`--from compose.yaml`).
200
+ - Existing native products keep their own payload layout with
201
+ `backend: {kind: native}`.
202
+
203
+ ### Capabilities
204
+
205
+ `init --with …` or `add` adds `files`, `jobs`, `login`, `delegate`,
206
+ `root-ops` and `portal`. `delegate` and `root-ops` run through the SDK's
207
+ executor, which an administrator approves once by digest; `check` and
208
+ `build` have the App's executor validate its policy first
209
+ (`EXECUTOR_POLICY_REJECTED`).
210
+
211
+ ### Commands
212
+
213
+ - `init`, `add`: create a project and add capabilities.
214
+ - `check`, `build`: validate the manifest and build the SPK for one or more
215
+ architectures, with the executor checked against the digests shipped in the
216
+ CLI.
217
+ - `inspect`: check any SPK against the DSM 7 package format without
218
+ installing it.
219
+ - `deploy`, `status`, `logs`: install on a NAS and read its state.
220
+ - `dev`, `test`, `images`: run, test and pin images for Compose projects.
221
+ - `skill`: the bundled guidance for AI assistants.
222
+
223
+ `--json` prints one JSON object on stdout; failures carry a stable `code`
224
+ and a `hint`.