@synopackageland/cli 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (578) hide show
  1. package/CHANGELOG.md +613 -0
  2. package/README.md +81 -89
  3. package/dist/add.js +39 -16
  4. package/dist/build-spk/delegate-scope.d.ts +6 -0
  5. package/dist/build-spk/delegate-scope.js +34 -0
  6. package/dist/build-spk/dsm-arch.d.ts +7 -1
  7. package/dist/build-spk/dsm-arch.js +24 -28
  8. package/dist/build-spk/dsm-platforms.json +39 -0
  9. package/dist/build-spk/dsm-version.d.ts +11 -0
  10. package/dist/build-spk/dsm-version.js +17 -0
  11. package/dist/build-spk/gateway.d.ts +1 -1
  12. package/dist/build-spk/gateway.js +2 -2
  13. package/dist/build-spk/generator.js +30 -5
  14. package/dist/build-spk/notices.js +4 -1
  15. package/dist/build-spk/resource-worker.js +3 -0
  16. package/dist/build-spk/wizard-messages.d.ts +66 -0
  17. package/dist/build-spk/wizard-messages.js +75 -9
  18. package/dist/cli.js +15 -7
  19. package/dist/contract/schema-validate.js +8 -0
  20. package/dist/contract/validate.js +30 -0
  21. package/dist/deploy/deploy.d.ts +1 -1
  22. package/dist/deploy/deploy.js +8 -0
  23. package/dist/deploy/plan.js +1 -1
  24. package/dist/deploy/spk-version.js +1 -1
  25. package/dist/error-registry.js +43 -15
  26. package/dist/host/dsm-client.d.ts +8 -4
  27. package/dist/host/dsm-client.js +10 -4
  28. package/dist/host/types.d.ts +2 -1
  29. package/dist/info.js +18 -2
  30. package/dist/init.d.ts +3 -0
  31. package/dist/init.js +49 -18
  32. package/dist/package-metadata.d.ts +21 -0
  33. package/dist/package-metadata.js +101 -0
  34. package/dist/portal/alias.d.ts +25 -0
  35. package/dist/portal/alias.js +48 -0
  36. package/dist/portal/package.d.ts +33 -0
  37. package/dist/portal/package.js +116 -0
  38. package/dist/runtime/delegate-contract.d.ts +79 -0
  39. package/dist/runtime/delegate-contract.js +27 -0
  40. package/dist/runtime/executor-build.d.ts +1 -1
  41. package/dist/runtime/executor-build.js +5 -4
  42. package/dist/runtime/executor-inspect.d.ts +3 -0
  43. package/dist/runtime/executor-inspect.js +11 -5
  44. package/dist/runtime/executor-policy.d.ts +12 -0
  45. package/dist/runtime/executor-policy.js +43 -3
  46. package/dist/runtime/executor-scope-task.d.ts +4 -0
  47. package/dist/runtime/executor-scope-task.js +21 -0
  48. package/dist/runtime/executor-tasks.d.ts +2 -1
  49. package/dist/runtime/executor-tasks.js +4 -4
  50. package/dist/skill.js +1 -0
  51. package/dist/standalone/api-permissions.d.ts +10 -0
  52. package/dist/standalone/api-permissions.js +19 -2
  53. package/dist/standalone/background.d.ts +16 -0
  54. package/dist/standalone/background.js +69 -2
  55. package/dist/standalone/binary.d.ts +1 -1
  56. package/dist/standalone/binary.js +22 -8
  57. package/dist/standalone/build.d.ts +1 -1
  58. package/dist/standalone/build.js +38 -8
  59. package/dist/standalone/daemon.d.ts +1 -1
  60. package/dist/standalone/daemon.js +2 -1
  61. package/dist/standalone/dsm-window.d.ts +3 -1
  62. package/dist/standalone/dsm-window.js +72 -15
  63. package/dist/standalone/inspect-spk.d.ts +8 -0
  64. package/dist/standalone/inspect-spk.js +105 -26
  65. package/dist/standalone/lifecycle.js +86 -8
  66. package/dist/standalone/manifest.d.ts +13 -0
  67. package/dist/standalone/manifest.js +57 -8
  68. package/dist/standalone/native-authorization-window.d.ts +1 -1
  69. package/dist/standalone/native-authorization-window.js +2 -0
  70. package/dist/standalone/native-layout.d.ts +3 -0
  71. package/dist/standalone/native-layout.js +37 -5
  72. package/dist/standalone/portal-window.d.ts +21 -0
  73. package/dist/standalone/portal-window.js +155 -0
  74. package/dist/standalone/portal.d.ts +4 -0
  75. package/dist/standalone/portal.js +4 -0
  76. package/dist/standalone/resource.d.ts +5 -3
  77. package/dist/standalone/resource.js +6 -3
  78. package/dist/standalone/root-window.js +62 -15
  79. package/dist/standalone/spk.d.ts +1 -1
  80. package/dist/standalone/spk.js +25 -7
  81. package/dist/templates/_daemon/main_test.go +61 -8
  82. package/dist/templates/_executor/README.md +127 -16
  83. package/dist/templates/_executor/anchor.go +146 -0
  84. package/dist/templates/_executor/anchor_test.go +126 -0
  85. package/dist/templates/_executor/apppriv_review_test.go +1 -1
  86. package/dist/templates/_executor/cancel_contract_test.go +236 -0
  87. package/dist/templates/_executor/copy.go +64 -0
  88. package/dist/templates/_executor/copy_test.go +88 -0
  89. package/dist/templates/_executor/dsm.go +23 -10
  90. package/dist/templates/_executor/dsm_test.go +95 -1
  91. package/dist/templates/_executor/executor.go +260 -49
  92. package/dist/templates/_executor/executor_test.go +36 -6
  93. package/dist/templates/_executor/identity.go +27 -1
  94. package/dist/templates/_executor/identity_kernel_linux_test.go +60 -0
  95. package/dist/templates/_executor/identity_linux.go +82 -0
  96. package/dist/templates/_executor/identity_security_test.go +43 -0
  97. package/dist/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  98. package/dist/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  99. package/dist/templates/_executor/internal/taskscript/boot.sh +9 -9
  100. package/dist/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  101. package/dist/templates/_executor/internal/taskscript/control.sh +15 -10
  102. package/dist/templates/_executor/internal/taskscript/tasks.go +5 -3
  103. package/dist/templates/_executor/lifecycle_test.go +11 -0
  104. package/dist/templates/_executor/link_linux.go +28 -0
  105. package/dist/templates/_executor/long_running_test.go +530 -0
  106. package/dist/templates/_executor/main.go +72 -20
  107. package/dist/templates/_executor/move_linux.go +490 -0
  108. package/dist/templates/_executor/move_linux_test.go +460 -0
  109. package/dist/templates/_executor/move_validation.go +46 -0
  110. package/dist/templates/_executor/move_validation_test.go +187 -0
  111. package/dist/templates/_executor/operations.go +926 -0
  112. package/dist/templates/_executor/platform_linux.go +24 -173
  113. package/dist/templates/_executor/platform_linux_test.go +9 -65
  114. package/dist/templates/_executor/platform_other.go +30 -4
  115. package/dist/templates/_executor/process.go +3 -0
  116. package/dist/templates/_executor/records.go +242 -0
  117. package/dist/templates/_executor/scope_cancel_contract_test.go +91 -0
  118. package/dist/templates/_executor/scope_client.go +37 -0
  119. package/dist/templates/_executor/scope_contract.go +12 -0
  120. package/dist/templates/_executor/selected_scope.go +146 -0
  121. package/dist/templates/_executor/selected_scope_test.go +186 -0
  122. package/dist/templates/_executor/status_disabled_test.go +33 -0
  123. package/dist/templates/_executor/store.go +4 -3
  124. package/dist/templates/_executor/supervise.go +109 -0
  125. package/dist/templates/_executor/supervise_test.go +130 -0
  126. package/dist/templates/_executor/task_race_review_test.go +1 -1
  127. package/dist/templates/_executor/tasks.go +7 -8
  128. package/dist/templates/_executor/tasks_review_test.go +1 -1
  129. package/dist/templates/_executor/tasks_test.go +4 -4
  130. package/dist/templates/_executor/watch.go +8 -0
  131. package/dist/templates/_executor/watch_test.go +23 -2
  132. package/dist/templates/_executor/write_review_linux_test.go +4 -12
  133. package/dist/templates/_shared/AGENTS.md +129 -0
  134. package/dist/templates/binary/dsm-package.yaml +24 -0
  135. package/dist/templates/binary/i18n/messages/de.json +4 -0
  136. package/dist/templates/binary/i18n/messages/en.json +4 -0
  137. package/dist/templates/binary/i18n/messages/es.json +4 -0
  138. package/dist/templates/binary/i18n/messages/fr.json +4 -0
  139. package/dist/templates/binary/i18n/messages/it.json +4 -0
  140. package/dist/templates/binary/i18n/messages/ja.json +4 -0
  141. package/dist/templates/binary/i18n/messages/ko.json +4 -0
  142. package/dist/templates/binary/i18n/messages/zh-CN.json +4 -0
  143. package/dist/templates/binary/i18n/messages/zh-TW.json +4 -0
  144. package/dist/templates/binary/ui/app.js +19 -4
  145. package/dist/templates/native-authorization/AuthorizationGate.vue +5 -3
  146. package/dist/templates/native-authorization/i18n/de.json +1 -0
  147. package/dist/templates/native-authorization/i18n/en.json +1 -0
  148. package/dist/templates/native-authorization/i18n/es.json +2 -1
  149. package/dist/templates/native-authorization/i18n/fr.json +1 -0
  150. package/dist/templates/native-authorization/i18n/it.json +11 -10
  151. package/dist/templates/native-authorization/i18n/ja.json +1 -0
  152. package/dist/templates/native-authorization/i18n/ko.json +10 -9
  153. package/dist/templates/native-authorization/i18n/zh-CN.json +6 -5
  154. package/dist/templates/native-authorization/i18n/zh-TW.json +6 -5
  155. package/dist/templates/native-go/README.md +43 -10
  156. package/dist/templates/native-go/backend/cmd/app/main.go +27 -7
  157. package/dist/templates/native-go/backend/internal/api/entry_test.go +47 -0
  158. package/dist/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  159. package/dist/templates/native-go/backend/internal/api/registry.go +11 -0
  160. package/dist/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  161. package/dist/templates/native-go/backend/internal/api/server.go +18 -5
  162. package/dist/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  163. package/dist/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  164. package/dist/templates/native-go/backend/internal/app/portal.go +30 -0
  165. package/dist/templates/native-go/backend/internal/app/portal_test.go +55 -0
  166. package/dist/templates/native-go/backend/internal/app/routes.go +8 -2
  167. package/dist/templates/native-go/backend/internal/background/runner.go +1 -1
  168. package/dist/templates/native-go/backend/internal/background/store.go +16 -14
  169. package/dist/templates/native-go/backend/internal/dsm/admin.go +8 -2
  170. package/dist/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  171. package/dist/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  172. package/dist/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  173. package/dist/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  174. package/dist/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  175. package/dist/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  176. package/dist/templates/native-go/backend/internal/dsm/entry.go +177 -0
  177. package/dist/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  178. package/dist/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  179. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  180. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  181. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  182. package/dist/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  183. package/dist/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  184. package/dist/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  185. package/dist/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  186. package/dist/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  187. package/dist/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  188. package/dist/templates/native-go/backend/internal/filestation/service.go +1 -1
  189. package/dist/templates/native-go/backend/internal/portal/portal.go +138 -0
  190. package/dist/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  191. package/dist/templates/native-go/dsm-package.yaml +20 -1
  192. package/dist/templates/native-go/i18n/glossary.json +2 -1
  193. package/dist/templates/native-go/i18n/messages/de.json +1 -0
  194. package/dist/templates/native-go/i18n/messages/en.json +1 -0
  195. package/dist/templates/native-go/i18n/messages/es.json +1 -0
  196. package/dist/templates/native-go/i18n/messages/fr.json +1 -0
  197. package/dist/templates/native-go/i18n/messages/it.json +1 -0
  198. package/dist/templates/native-go/i18n/messages/ja.json +1 -0
  199. package/dist/templates/native-go/i18n/messages/ko.json +1 -0
  200. package/dist/templates/native-go/i18n/messages/zh-CN.json +1 -0
  201. package/dist/templates/native-go/i18n/messages/zh-TW.json +1 -0
  202. package/dist/templates/native-go/ui/app.css +10 -0
  203. package/dist/templates/native-go/ui/app.js +11 -1
  204. package/dist/templates/native-go-delegate/DELEGATE.md +126 -5
  205. package/dist/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  206. package/dist/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  207. package/dist/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  208. package/dist/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  209. package/dist/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  210. package/dist/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  211. package/dist/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  212. package/dist/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  213. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  214. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  215. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  216. package/dist/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  217. package/dist/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  218. package/dist/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  219. package/dist/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  220. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  221. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  222. package/dist/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  223. package/dist/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  224. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  225. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  226. package/dist/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  227. package/dist/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  228. package/dist/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  229. package/dist/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  230. package/dist/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  231. package/dist/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  232. package/dist/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  233. package/dist/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  234. package/dist/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  235. package/dist/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  236. package/dist/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  237. package/dist/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  238. package/dist/templates/native-go-delegate/test.sh +1 -0
  239. package/dist/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  240. package/dist/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  241. package/dist/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  242. package/dist/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  243. package/dist/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  244. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  245. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  246. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  247. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  248. package/dist/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  249. package/dist/templates/native-go-vue/README.md +4 -0
  250. package/dist/templates/native-go-vue/frontend/package-lock.json +4 -4
  251. package/dist/templates/native-go-vue/frontend/package.json +1 -1
  252. package/dist/templates/native-go-vue/frontend/src/App.vue +18 -13
  253. package/dist/templates/native-go-vue/frontend/src/context.ts +31 -0
  254. package/dist/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  255. package/dist/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  256. package/dist/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  257. package/dist/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  258. package/dist/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  259. package/dist/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  260. package/dist/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  261. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  262. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  263. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  264. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  265. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  266. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  267. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  268. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  269. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  270. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  271. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  272. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  273. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  274. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  275. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  276. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  277. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  278. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  279. package/dist/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  280. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  281. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  282. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  283. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  284. package/dist/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  285. package/dist/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  286. package/dist/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  287. package/dist/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  288. package/dist/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  289. package/dist/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  290. package/dist/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  291. package/dist/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  292. package/dist/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  293. package/dist/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  294. package/dist/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  295. package/dist/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  296. package/dist/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  297. package/dist/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  298. package/dist/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  299. package/dist/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  300. package/dist/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  301. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  302. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  303. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  304. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  305. package/dist/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  306. package/dist/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  307. package/dist/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  308. package/dist/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  309. package/dist/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  310. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  311. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  312. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  313. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  314. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  315. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  316. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  317. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  318. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  319. package/dist/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  320. package/dist/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  321. package/dist/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  322. package/dist/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  323. package/dist/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  324. package/dist/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
  325. package/package.json +5 -1
  326. package/schema/package-metadata.schema.json +101 -0
  327. package/schema/synology-app.schema.json +112 -0
  328. package/skills/SKILL.md +106 -4
  329. package/skills/reference/create.md +101 -2
  330. package/skills/reference/deploy.md +2 -2
  331. package/skills/reference/files.md +113 -2
  332. package/skills/reference/identity.md +70 -1
  333. package/skills/reference/portal.md +17 -0
  334. package/skills/reference/ui-vue.md +12 -0
  335. package/templates/_daemon/main_test.go +61 -8
  336. package/templates/_executor/README.md +127 -16
  337. package/templates/_executor/anchor.go +146 -0
  338. package/templates/_executor/anchor_test.go +126 -0
  339. package/templates/_executor/apppriv_review_test.go +1 -1
  340. package/templates/_executor/cancel_contract_test.go +236 -0
  341. package/templates/_executor/copy.go +64 -0
  342. package/templates/_executor/copy_test.go +88 -0
  343. package/templates/_executor/dsm.go +23 -10
  344. package/templates/_executor/dsm_test.go +95 -1
  345. package/templates/_executor/executor.go +260 -49
  346. package/templates/_executor/executor_test.go +36 -6
  347. package/templates/_executor/identity.go +27 -1
  348. package/templates/_executor/identity_kernel_linux_test.go +60 -0
  349. package/templates/_executor/identity_linux.go +82 -0
  350. package/templates/_executor/identity_security_test.go +43 -0
  351. package/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  352. package/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  353. package/templates/_executor/internal/taskscript/boot.sh +9 -9
  354. package/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  355. package/templates/_executor/internal/taskscript/control.sh +15 -10
  356. package/templates/_executor/internal/taskscript/tasks.go +5 -3
  357. package/templates/_executor/lifecycle_test.go +11 -0
  358. package/templates/_executor/link_linux.go +28 -0
  359. package/templates/_executor/long_running_test.go +530 -0
  360. package/templates/_executor/main.go +72 -20
  361. package/templates/_executor/move_linux.go +490 -0
  362. package/templates/_executor/move_linux_test.go +460 -0
  363. package/templates/_executor/move_validation.go +46 -0
  364. package/templates/_executor/move_validation_test.go +187 -0
  365. package/templates/_executor/operations.go +926 -0
  366. package/templates/_executor/platform_linux.go +24 -173
  367. package/templates/_executor/platform_linux_test.go +9 -65
  368. package/templates/_executor/platform_other.go +30 -4
  369. package/templates/_executor/process.go +3 -0
  370. package/templates/_executor/records.go +242 -0
  371. package/templates/_executor/scope_cancel_contract_test.go +91 -0
  372. package/templates/_executor/scope_client.go +37 -0
  373. package/templates/_executor/scope_contract.go +12 -0
  374. package/templates/_executor/selected_scope.go +146 -0
  375. package/templates/_executor/selected_scope_test.go +186 -0
  376. package/templates/_executor/status_disabled_test.go +33 -0
  377. package/templates/_executor/store.go +4 -3
  378. package/templates/_executor/supervise.go +109 -0
  379. package/templates/_executor/supervise_test.go +130 -0
  380. package/templates/_executor/task_race_review_test.go +1 -1
  381. package/templates/_executor/tasks.go +7 -8
  382. package/templates/_executor/tasks_review_test.go +1 -1
  383. package/templates/_executor/tasks_test.go +4 -4
  384. package/templates/_executor/watch.go +8 -0
  385. package/templates/_executor/watch_test.go +23 -2
  386. package/templates/_executor/write_review_linux_test.go +4 -12
  387. package/templates/_shared/AGENTS.md +129 -0
  388. package/templates/binary/dsm-package.yaml +24 -0
  389. package/templates/binary/i18n/messages/de.json +4 -0
  390. package/templates/binary/i18n/messages/en.json +4 -0
  391. package/templates/binary/i18n/messages/es.json +4 -0
  392. package/templates/binary/i18n/messages/fr.json +4 -0
  393. package/templates/binary/i18n/messages/it.json +4 -0
  394. package/templates/binary/i18n/messages/ja.json +4 -0
  395. package/templates/binary/i18n/messages/ko.json +4 -0
  396. package/templates/binary/i18n/messages/zh-CN.json +4 -0
  397. package/templates/binary/i18n/messages/zh-TW.json +4 -0
  398. package/templates/binary/ui/app.js +19 -4
  399. package/templates/native-authorization/AuthorizationGate.vue +5 -3
  400. package/templates/native-authorization/i18n/de.json +1 -0
  401. package/templates/native-authorization/i18n/en.json +1 -0
  402. package/templates/native-authorization/i18n/es.json +2 -1
  403. package/templates/native-authorization/i18n/fr.json +1 -0
  404. package/templates/native-authorization/i18n/it.json +11 -10
  405. package/templates/native-authorization/i18n/ja.json +1 -0
  406. package/templates/native-authorization/i18n/ko.json +10 -9
  407. package/templates/native-authorization/i18n/zh-CN.json +6 -5
  408. package/templates/native-authorization/i18n/zh-TW.json +6 -5
  409. package/templates/native-go/README.md +43 -10
  410. package/templates/native-go/backend/cmd/app/main.go +27 -7
  411. package/templates/native-go/backend/internal/api/entry_test.go +47 -0
  412. package/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  413. package/templates/native-go/backend/internal/api/registry.go +11 -0
  414. package/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  415. package/templates/native-go/backend/internal/api/server.go +18 -5
  416. package/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  417. package/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  418. package/templates/native-go/backend/internal/app/portal.go +30 -0
  419. package/templates/native-go/backend/internal/app/portal_test.go +55 -0
  420. package/templates/native-go/backend/internal/app/routes.go +8 -2
  421. package/templates/native-go/backend/internal/background/runner.go +1 -1
  422. package/templates/native-go/backend/internal/background/store.go +16 -14
  423. package/templates/native-go/backend/internal/dsm/admin.go +8 -2
  424. package/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  425. package/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  426. package/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  427. package/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  428. package/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  429. package/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  430. package/templates/native-go/backend/internal/dsm/entry.go +177 -0
  431. package/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  432. package/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  433. package/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  434. package/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  435. package/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  436. package/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  437. package/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  438. package/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  439. package/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  440. package/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  441. package/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  442. package/templates/native-go/backend/internal/filestation/service.go +1 -1
  443. package/templates/native-go/backend/internal/portal/portal.go +138 -0
  444. package/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  445. package/templates/native-go/dsm-package.yaml +20 -1
  446. package/templates/native-go/i18n/glossary.json +2 -1
  447. package/templates/native-go/i18n/messages/de.json +1 -0
  448. package/templates/native-go/i18n/messages/en.json +1 -0
  449. package/templates/native-go/i18n/messages/es.json +1 -0
  450. package/templates/native-go/i18n/messages/fr.json +1 -0
  451. package/templates/native-go/i18n/messages/it.json +1 -0
  452. package/templates/native-go/i18n/messages/ja.json +1 -0
  453. package/templates/native-go/i18n/messages/ko.json +1 -0
  454. package/templates/native-go/i18n/messages/zh-CN.json +1 -0
  455. package/templates/native-go/i18n/messages/zh-TW.json +1 -0
  456. package/templates/native-go/ui/app.css +10 -0
  457. package/templates/native-go/ui/app.js +11 -1
  458. package/templates/native-go-delegate/DELEGATE.md +126 -5
  459. package/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  460. package/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  461. package/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  462. package/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  463. package/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  464. package/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  465. package/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  466. package/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  467. package/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  468. package/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  469. package/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  470. package/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  471. package/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  472. package/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  473. package/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  474. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  475. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  476. package/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  477. package/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  478. package/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  479. package/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  480. package/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  481. package/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  482. package/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  483. package/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  484. package/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  485. package/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  486. package/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  487. package/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  488. package/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  489. package/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  490. package/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  491. package/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  492. package/templates/native-go-delegate/test.sh +1 -0
  493. package/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  494. package/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  495. package/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  496. package/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  497. package/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  498. package/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  499. package/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  500. package/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  501. package/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  502. package/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  503. package/templates/native-go-vue/README.md +4 -0
  504. package/templates/native-go-vue/frontend/package-lock.json +4 -4
  505. package/templates/native-go-vue/frontend/package.json +1 -1
  506. package/templates/native-go-vue/frontend/src/App.vue +18 -13
  507. package/templates/native-go-vue/frontend/src/context.ts +31 -0
  508. package/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  509. package/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  510. package/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  511. package/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  512. package/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  513. package/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  514. package/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  515. package/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  516. package/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  517. package/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  518. package/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  519. package/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  520. package/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  521. package/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  522. package/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  523. package/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  524. package/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  525. package/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  526. package/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  527. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  528. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  529. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  530. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  531. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  532. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  533. package/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  534. package/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  535. package/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  536. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  537. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  538. package/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  539. package/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  540. package/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  541. package/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  542. package/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  543. package/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  544. package/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  545. package/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  546. package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  547. package/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  548. package/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  549. package/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  550. package/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  551. package/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  552. package/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  553. package/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  554. package/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  555. package/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  556. package/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  557. package/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  558. package/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  559. package/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  560. package/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  561. package/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  562. package/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  563. package/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  564. package/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  565. package/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  566. package/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  567. package/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  568. package/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  569. package/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  570. package/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  571. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  572. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  573. package/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  574. package/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  575. package/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  576. package/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  577. package/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  578. package/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
@@ -1,13 +1,124 @@
1
1
  # 檔案與背景工作
2
2
 
3
- Native-go 的 B 能力使用 `init --with files` 或 `add files`,對應 schema 2 `capabilities.files: session`。File tree 與共用 NasPicker 以 TreeView/DataGrid 顯示本人可見內容;選取路徑不建立授權。FilePicker 選瀏覽器本機檔案。上傳/下載使用 streaming transport,不把內容放進 JSON。每次以呼叫者有效 DSM session 由 File Station 判權,不需管理員進行能力設定。
3
+ Native-go 的 B 能力使用 `init --with files` 或 `add files`,對應 schema 2 `capabilities.files: session`。File tree 與共用 NasPicker 以 TreeView/DataGrid 顯示本人可見內容;一般選取不建立授權;delegate destination 選取例外,確認即授權。FilePicker 選瀏覽器本機檔案。上傳/下載使用 streaming transport,不把內容放進 JSON。每次以呼叫者有效 DSM session 由 File Station 判權,不需管理員進行能力設定。
4
4
 
5
5
  只有 App session 沒有有效 DSM cookie 時回 `dsm_session_required`,須在同 hostname 登入 DSM。不能以套件帳號或 root 作後備存取。
6
6
 
7
7
  A 能力 `--with jobs` 使用套件帳號處理已匯入的私有副本,依可信 username/UID 綁定;一般使用者不需 admin 設定即可提交、查詢與取消自己的工作。關閉瀏覽器後繼續,不保存 cookie/SID/密碼。匯入需要本人在線 session;私有副本不是離線讀取原檔案的權限。Jobs 與 daemon 互斥。
8
8
 
9
- C 能力 delegate 為 P2 整合中,目前 CLI 拒絕。規劃 admin 確認一次密碼設定 executor;使用者委託/撤銷,綁定 job/operations/scope/期限。DSM runner 與降權寫檔依本人權限執行;scope 限制使用範圍,不是權限邊界。每次重驗帳號與 AppPriv,停用或拒絕即阻擋。不宣稱完整生命週期已驗證。
9
+
10
+ ## Permanent destination integration
11
+
12
+ `init --with delegate` / `add delegate` default to `operations: [files.moveFromStaging]`,
13
+ `destinations: [save]`, `lifetime: until-revoked`, `scope: install-selected`. Existing legacy
14
+ TTL declarations retain their 1–3600 second service / 30-day wire ceiling and
15
+ 64 MiB / 60-second URL example. Do not apply those limits to the new move API.
16
+ Delegate can coexist with daemon; ordinary standalone jobs cannot. Preserve
17
+ App business route floors. Binary/compose remain unsupported.
18
+
19
+ Use File Station `/home[/...]`, never `/homes/<user>`. The SDK destination picker
20
+ selection itself grants access after fresh DSM session/UID/write verification,
21
+ without a second consent dialog. Ordinary and administrator subjects use their
22
+ own nonzero UID/current groups, never root. An online App can create an in-scope
23
+ grant using a valid session; there is no trusted external consent or notification.
24
+
25
+ Use the default `createBackgroundSaveFacade(ctx)` / CapabilityPanels management
26
+ page. `delegate.status` exposes verified `disabled` and manifest mode; missing
27
+ protected evidence must not imply enabled. `delegate.operations.list` accepts
28
+ only `{}`, uses the backend move mirrors, and returns the caller's username+UID
29
+ operations only, including for admins. Mirrors are not completion receipts:
30
+ query original `delegate.operations.get` and show Saved only for committed with
31
+ a receipt. Cancel by the same reference; revoke by grant ID. Never resubmit
32
+ uncertain/interrupted work. Revoke, scope change and disable store their reason (`grant-revoked`,
33
+ `scope-changed`, `integration-disabled`) the same way: a copy in flight answers
34
+ `CANCEL_REQUESTED` with it and settles `canceled` with it across a restart. A
35
+ copy the executor stops without a cancel ends `interrupted`/`EXECUTOR_PAUSED`
36
+ (`interrupted`/`interrupted` after a crash), never `WRITE_FAILED`. A lost publish receipt (or a restart after publish
37
+ intent) settles to committed only when the recorded part inode is the final name;
38
+ otherwise it stays uncertain. Nothing is copied twice. Admin disable is protected and revokes grants;
39
+ enable/mailbox start does not resurrect them. Keep IDs, never session credentials.
40
+
41
+ App-owned daemon completion and scheduled CSV export call configured Go
42
+ `Service.MoveFromStaging` with `delegation_id`, stable `job_id`, private relative
43
+ `staging`, `name`, `size`. No owner/UID selector. Bounds: 16 GiB/file, queued16,
44
+ active1/package, 24-hour attempt, 5-second revalidation ticks (one check at a time, 30-second complete-check deadline; proven timeouts retain private copying, publication still revalidates), 10 MiB operation records,
45
+ 50 MiB audit. Scope is a usage restriction, not hard subtree confinement.
46
+ Local production-executor fixtures use fake DSM/peer/bulk writer; do not claim
47
+ NAS ACL, actual writer UID, logout/reboot or full lifecycle acceptance from them.
48
+ Gopeed product wiring remains a separate task.
49
+
50
+ Admission recovery (per job): on `EXECUTOR_UNAVAILABLE`, `EXECUTOR_TIMEOUT`,
51
+ `EXECUTOR_PROTOCOL`, `canceled`, `EXECUTOR_PAUSED`, `queue-full`,
52
+ `STORAGE_ERROR`, `ADMISSION_PENDING`, `jobs_busy` or `jobs_unavailable` (the
53
+ package journal was busy or failed transiently; a caller deadline or cancel
54
+ spent waiting for it is `canceled`), call `MoveFromStaging` again later with
55
+ the same exact params. It queries the original reference first and re-sends
56
+ admission only when the executor does not know it and the pinned source is
57
+ unchanged; executor tuple deduplication keeps one operation, so a lost reply or a
58
+ racing caller never copies twice. At most 10 possibly-delivered sends, 2 s
59
+ backoff doubling to 5 min; the worker sweep does the same. Terminal, do not
60
+ retry: `APP_DENIED`, `DELEGATION_DENIED`, `operation-conflict`, `disabled`,
61
+ `SCOPE_DENIED` and other executor refusals, `STAGING_CHANGED`,
62
+ `ADMISSION_EXHAUSTED`, `spool_unsafe` and `journal_corrupt` (repair the package
63
+ spool first), and `operation-not-found` for an accepted operation.
64
+ Never change job/grant IDs, delete mirrors or call the socket directly to retry.
65
+
66
+ Cancellation (per job): `CancelOperation` / `delegate.operations.cancel` with the
67
+ original ref is idempotent; repeat it with the same ref after any retryable
68
+ error or a lost reply. A reply `queued`→`canceled` is final at once. A copy in
69
+ flight answers `copying` with code `CANCEL_REQUESTED`: the cancel is accepted and
70
+ durable, not finished; poll `OperationStatus` on the same ref until terminal
71
+ (repeating the cancel is harmless). `canceled` (`OPERATION_CANCELED`) guarantees the writer
72
+ stopped, its part file is removed, nothing was published and staging is kept; it
73
+ stays canceled across executor crash, package stop/start and logout. `committed`
74
+ means publication won the race: the file stays (cancel never removes it) and only
75
+ the app record may be dropped. `uncertain` means a publish attempt failed before
76
+ the cancel; keep querying. A cancel of a ref the executor has not admitted yet
77
+ (reserved/sent mirror) returns `canceled` and leaves a tombstone, so a late or
78
+ recovered admission of that ref returns `canceled` and never copies; a producer
79
+ cancel is also written to the mirror first, so `MoveFromStaging` and the worker
80
+ sweep forward the cancel instead of admitting it.
81
+
10
82
 
11
83
  容器使用固定 DSM run-as 使用者與掛載,不代表瀏覽器本人 ACL。容器路徑依 Compose/映像的 upstream target,保留 target、ro/rw,只替換 host source;管理員在安裝時選擇 Share(共用資料夾)。不要對既有資料 chown。App Data 使用 package var;共用資料夾與 named volume 分別備份。`posix_all_shares` 僅為部署時快照,不會自動加入新資料夾。
12
84
 
13
85
  `install.sharedFolder` 使用套件帳號:select 模式安裝後可能須管理員手動授予權限,create 由 DSM data-share 建立;都不是逐使用者 session access。移除不刪共用資料夾。Delete App Data 不是完整清理證明,須讀回殘留。
86
+
87
+
88
+ ### Install-selected scope and the destination API
89
+
90
+ - The DSM install wizard dynamically lists local shares (`SYNO.Core.Share list`
91
+ via `api_store`) and offers home. It saves only a package-owned pre-selection
92
+ in `var/delegate-install.json`; never trust that file for authority.
93
+ - The existing administrator PasswordConfirm form displays this pre-selection
94
+ at first enable. `ctx.executor.authorize(password, selectedScope)` confirms it
95
+ through a pinned, exact-readback one-time root task. Settings changes use the
96
+ same form/password path; no App CGI route may set protected scope directly.
97
+ A change on a running, pin-verified executor never restarts it; copies in
98
+ flight end `canceled`/`scope-changed`. A `blocked` reply with `requested:
99
+ {scope, generation}` may still land: poll `ctx.executor.status()` until
100
+ `selection` shows that scope above that generation; resubmitting is safe.
101
+ - The executor trusts `/usr/local/etc/synopkgland/<pkg>/delegations/selected-scope.record`
102
+ only. Its scope/generation are runtime protected state; shipped policy pins
103
+ lock the selection mode and operations. Changes revoke old grants and cancel
104
+ pending writes. Change-away-and-back never revives an old grant.
105
+ - Follow `selection` from verified `delegate.status` / `sdk.destination(key)`.
106
+ If the draft differs, display the difference and follow the protected value.
107
+ Home and fixed `shared:<name>` manifests stay compatible.
108
+ - Frontend: `const sdk = createBackgroundSaveFacade(ctx); const current = await
109
+ sdk.destination('save')` returns typed `{key, selection, destination, status}`.
110
+ `selection` contains `{scope, root, generation}`; `destination` is this caller's
111
+ grant or null. Handle active/missing/reselection-required/needs-scope-confirmation/disabled.
112
+ - Go: `Service.Destination(ctx, caller, request, key)` verifies the fresh DSM
113
+ session and username+UID. Capture its active grant ID into the App job. The
114
+ offline producer uses existing `Service.MoveFromStaging`; no user/UID selector,
115
+ cookie, token, or browser path cache. Executor revalidates at use/publication.
116
+ - The generated Background saves page includes a working **Save example file**
117
+ (`sdk.exportExample({key, job_id})`, `delegate.example.export`). Go
118
+ `ExportExample` creates a private JSON file, calls `MoveFromStaging` once, and
119
+ repeats the original request on repeated IDs. Review `example.go` to
120
+ adapt a real producer; show Saved only from a protected committed receipt.
121
+ - Wizard support is measured on vt-nas DSM 7.4 via a session-only SSH local
122
+ forward. This is not proof of other DSM releases or full writer/reboot behavior.
123
+
124
+ The pinned scope-selection task also binds the protected generation observed before confirmation. The executor compares it under the publish lock and refuses stale tasks, so a retained task cannot replay an older selection after another confirmation.
@@ -2,10 +2,79 @@
2
2
 
3
3
  Native-go validates the caller's DSM session per package. Business route permissions come from backend/internal/api/permissions.json; SDK-owned route policy is protected separately. Capabilities describe execution/resources and do not grant business route access. Never trust a request username as owner.
4
4
 
5
+ Entry: omit `entry` for legacy access; `entry: admin-only` admits administrators only and is the floor of every route: SDK routes a capability adds (files, jobs, portal, delegate, rootOps) refuse ordinary users with `admin_required`, the launcher is hidden, and an app route marked `authenticated` or `public` in permissions.json fails validate/build with `STANDALONE_ENTRY_WIDENED` (only the public `identity: standalone` login routes stay public; they admit no one by themselves). The SPK records the mode in `sdk-package.json`; `inspect` prints it and flags `ENTRY_LAUNCHER_MISMATCH`; `entry: app-privilege` also admits accounts DSM Application Privileges allows for `SYNO.SDS.<package>.Instance` (native-go only, needs an `authenticated` route). DSM evaluates user/group/everyone rules, allow/deny precedence and IP ranges; one CGI check covers every route, failures refuse with `app_privilege_denied`, and decisions are cached at most 30 seconds. DSM's install default allows administrators only, so declaring or upgrading never opens the app to everyone. The delegate executor applies the same entry rule to every protected and producer move: once DSM no longer allows a user, new moves under that user's still-valid grant are refused (`APP_DENIED`, grant invalidated); files already saved stay. Guard global settings in shared routes with Go `api.RequireAdmin(caller)` (`admin_required`); the frontend `ctx.user?.isAdmin` only hides controls.
6
+
5
7
  I capability: init --with login or add login enables Go-native standalone identity and Vue login. Same-host DSM cookies can be reused across ports after backend verification. Missing cookies use an HTTPS DSM account/password/OTP fallback; passwords and DSM credentials are not persisted. Plain UI is unsupported. App sessions last at most 15 minutes; sensitive operations revalidate live DSM cookies and local UID. Password-only sessions permit only basic app.status/greeting.get reads. Missing revalidation returns session_revalidation_required; files return dsm_session_required. Sign in to DSM on the same hostname. Web Station portal/proxy acceptance remains pending.
6
8
 
7
9
  B files needs the current user's DSM session. A jobs processes owner-bound private copies as the package account without administrator setup. Neither App identity nor login alone grants offline File Station access.
8
10
 
9
- C delegate is P2 integration in progress and rejected by the current CLI. Planned grants bind verified subject/uid, job, operations, scope and expiry. The executor rechecks account enablement and AppPriv and dispatches with the user's own permissions. User/admin revocation is required. R exposes only fixed admin operations; current approval is per operation. P2 shares one-time password-confirmed, digest-locked executor setup and reboot boot task between C/R. Do not invent final API names or claim complete cleanup before integration acceptance.
11
+
12
+ ## Permanent destination integration
13
+
14
+ `init --with delegate` / `add delegate` default to `operations: [files.moveFromStaging]`,
15
+ `destinations: [save]`, `lifetime: until-revoked`, `scope: home`. Existing legacy
16
+ TTL declarations retain their 1–3600 second service / 30-day wire ceiling and
17
+ 64 MiB / 60-second URL example. Do not apply those limits to the new move API.
18
+ Delegate can coexist with daemon; ordinary standalone jobs cannot. Preserve
19
+ App business route floors. Binary/compose remain unsupported.
20
+
21
+ Use File Station `/home[/...]`, never `/homes/<user>`. The SDK destination picker
22
+ selection itself grants access after fresh DSM session/UID/write verification,
23
+ without a second consent dialog. Ordinary and administrator subjects use their
24
+ own nonzero UID/current groups, never root. An online App can create an in-scope
25
+ grant using a valid session; there is no trusted external consent or notification.
26
+
27
+ Use the default `createBackgroundSaveFacade(ctx)` / CapabilityPanels management
28
+ page. `delegate.status` exposes verified `disabled` and manifest mode; missing
29
+ protected evidence must not imply enabled. `delegate.operations.list` accepts
30
+ only `{}`, uses the backend move mirrors, and returns the caller's username+UID
31
+ operations only, including for admins. Mirrors are not completion receipts:
32
+ query original `delegate.operations.get` and show Saved only for committed with
33
+ a receipt. Cancel by the same reference; revoke by grant ID. Never resubmit
34
+ uncertain/interrupted work. Admin disable is protected and revokes grants;
35
+ enable/mailbox start does not resurrect them. Keep IDs, never session credentials.
36
+
37
+ App-owned daemon completion and scheduled CSV export call configured Go
38
+ `Service.MoveFromStaging` with `delegation_id`, stable `job_id`, private relative
39
+ `staging`, `name`, `size`. No owner/UID selector. Bounds: 16 GiB/file, queued16,
40
+ active1/package, 24-hour attempt, 5-second revalidation ticks (one check at a time, 30-second complete-check deadline; proven timeouts retain private copying, publication still revalidates), 10 MiB operation records,
41
+ 50 MiB audit. Scope is a usage restriction, not hard subtree confinement.
42
+ Local production-executor fixtures use fake DSM/peer/bulk writer; do not claim
43
+ NAS ACL, actual writer UID, logout/reboot or full lifecycle acceptance from them.
44
+ Gopeed product wiring remains a separate task.
45
+
46
+ Admission recovery (per job): on `EXECUTOR_UNAVAILABLE`, `EXECUTOR_TIMEOUT`,
47
+ `EXECUTOR_PROTOCOL`, `canceled`, `EXECUTOR_PAUSED`, `queue-full`,
48
+ `STORAGE_ERROR`, `ADMISSION_PENDING`, `jobs_busy` or `jobs_unavailable` (the
49
+ package journal was busy or failed transiently; a caller deadline or cancel
50
+ spent waiting for it is `canceled`), call `MoveFromStaging` again later with
51
+ the same exact params. It queries the original reference first and re-sends
52
+ admission only when the executor does not know it and the pinned source is
53
+ unchanged; executor tuple deduplication keeps one operation, so a lost reply or a
54
+ racing caller never copies twice. At most 10 possibly-delivered sends, 2 s
55
+ backoff doubling to 5 min; the worker sweep does the same. Terminal, do not
56
+ retry: `APP_DENIED`, `DELEGATION_DENIED`, `operation-conflict`, `disabled`,
57
+ `SCOPE_DENIED` and other executor refusals, `STAGING_CHANGED`,
58
+ `ADMISSION_EXHAUSTED`, `spool_unsafe` and `journal_corrupt` (repair the package
59
+ spool first), and `operation-not-found` for an accepted operation.
60
+ Never change job/grant IDs, delete mirrors or call the socket directly to retry.
61
+
62
+ Cancellation (per job): `CancelOperation` / `delegate.operations.cancel` with the
63
+ original ref is idempotent; repeat it with the same ref after any retryable
64
+ error or a lost reply. A reply `queued`→`canceled` is final at once. A copy in
65
+ flight answers `copying` with code `CANCEL_REQUESTED`: the cancel is accepted and
66
+ durable, not finished; poll `OperationStatus` on the same ref until terminal
67
+ (repeating the cancel is harmless). `canceled` (`OPERATION_CANCELED`) guarantees the writer
68
+ stopped, its part file is removed, nothing was published and staging is kept; it
69
+ stays canceled across executor crash, package stop/start and logout. `committed`
70
+ means publication won the race: the file stays (cancel never removes it) and only
71
+ the app record may be dropped. `uncertain` means a publish attempt failed before
72
+ the cancel; keep querying. A cancel of a ref the executor has not admitted yet
73
+ (reserved/sent mirror) returns `canceled` and leaves a tombstone, so a late or
74
+ recovered admission of that ref returns `canceled` and never copies; a producer
75
+ cancel is also written to the mirror first, so `MoveFromStaging` and the worker
76
+ sweep forward the cancel instead of admitting it.
77
+
78
+ RootOps/delegate share PasswordConfirm-approved digest-pinned setup with exact task readback and health. Pins changes require reapproval. The separate native authorization-sdk adapter must not mix its tasks with generated executor tasks.
10
79
 
11
80
  Compose uses its DSM gateway plus upstream authentication. A fixed service UID/GID identifies the service, not the browser user. Do not infer general SSO or per-user file authority from a service mount.
@@ -0,0 +1,17 @@
1
+ # DSM Application Portal
2
+
3
+ Declare: native-go/binary `portal: {}` or `portal: {alias: my-app}` in dsm-package.yaml (`synopkgland add portal`); Compose `integration.web.portal: {}` only for a DSM-window app behind the DSM auth gateway. The alias defaults to the package name in lowercase. Aliases are 1–64 lowercase letters, digits or `-`, not starting or ending with `-`, and not a DSM path or Synology App alias (`webman`, `file`, …). The native-layout `backend: {kind: native}` owns its conf/scripts: the SDK does not register a portal for it.
4
+
5
+ Build: the SPK carries `etc/portal.json` (every field DSM's app-portal worker needs) and `etc/portal.mustache`, `conf/resource` registers them, and the install wizard adds a prefilled alias step. preinst validates the answer; postinst stores it as the package's own draft and default alias. Nothing is active until an administrator applies it. `inspect <spk> --json` reports `portal: {appId, alias}` and refuses any other portal content.
6
+
7
+ Activate: DSM owns the saved setting. Administrators use the app's portal panel (`ctx.portal.apply(alias)` / `disable()`, DSM `SYNO.Core.AppPortal set` with their own session; DSM refuses non-administrators with 105) or Control Panel > Login Portal > Applications (`ctx.portal.openControlPanel()`). DSM errors map to `portal_alias_invalid`, `portal_alias_reserved`, `portal_alias_in_use`, `admin_required`, `portal_unavailable`.
8
+
9
+ Read: ordinary users call the Go backend's `portal.status` (`authenticated`), which reads `/var/packages/<package>/etc/AppPortal.json` read-only, bounded (64 KiB, no symlinks, one JSON object, matching app id). Anything else is `unavailable` and the UI shows "ask an administrator". A binary CGI must implement the same `{state, alias}` answer itself or ordinary users never see the link.
10
+
11
+ DSM floor: os_min_ver must be 7.3.2-86009 or later. DSM 7.1.1-42962 refuses an unsigned SPK whose conf/resource carries web-config app-portal (Package Center error 320, "non-synology-signed package should not use internal worker"); 7.3.2-86009 and 7.4 install it; 7.2 and 7.3.0/7.3.1 are untested (unknown). validate/build fail with `STANDALONE_PORTAL_MIN_DSM` / `CONTRACT_PORTAL_MIN_DSM`, `--min-dsm` cannot go below it, `add portal` raises a lower os_min_ver, and inspect flags a lower INFO os_min_ver. Fix: raise os_min_ver or remove the portal.
12
+
13
+ Capability: beyond that floor, no version gate. The panel needs SYNO.API.Info to list `SYNO.Core.AppPortal` (v2) and the app's registration to exist; otherwise administrators get only the Control Panel shortcut.
14
+
15
+ UI: plain JS calls `ctx.portal && ctx.portal.mountPanel(element)` and disposes it on unmount; Vue renders `portal/PortalSettings.vue`. `ctx.portal.messages` carries the SDK text in all nine languages.
16
+
17
+ Never: write `AppPortal.json`, `@appconf/<package>` or `/usr/syno/etc/www/app.d`; add a backend route that changes the alias; relay QuickConnect responses or build QuickConnect links; reboot or change other packages' portal entries while testing. DSM keeps the alias across upgrades and removes the entry on uninstall.
@@ -69,3 +69,15 @@ handlers; it does not replace the Vue build, tests or actual-app screenshots.
69
69
  Finally run the project's `check --json`, `build --json` and
70
70
  `inspect <spk> --json` workflow from AGENTS.md. Fix errors, warnings and layout
71
71
  issues before handing over the app.
72
+
73
+
74
+ ## Delegate scope Settings
75
+
76
+ Default delegate uses `install-selected`. Keep ExecutorSettings and BackgroundSavePanel
77
+ in the existing Settings surface. Show the installer draft only as a pre-selection;
78
+ follow protected `selection` from `delegate.status` / `sdk.destination(key)`. Use
79
+ `ctx.executor.authorize(password, scope)` for both first enable and scope changes,
80
+ with the same PasswordConfirm form and no extra dialog. Mismatch must be explicit.
81
+ Restrict the existing NasPicker to `selection.root`; do not grant via cached paths.
82
+ The default facade includes typed `destination(key)` and `exportExample({key,job_id})`;
83
+ use original operation references and display Saved only for protected receipts.
@@ -41,9 +41,15 @@ func fixture(t *testing.T, mode string) (string, string, string) {
41
41
  if err != nil {
42
42
  t.Fatal(err)
43
43
  }
44
- if err := os.WriteFile(filepath.Join(dest, "daemon/payload/app"), raw, 0755); err != nil {
44
+ app := filepath.Join(dest, "daemon/payload/app")
45
+ if err := os.WriteFile(app, raw, 0755); err != nil {
45
46
  t.Fatal(err)
46
47
  }
48
+ // macOS verifies a newly written executable on its first exec, which takes
49
+ // seconds on a loaded host; pay it here, not inside a one-second budget.
50
+ if raw, err := exec.Command(app, "cleanup").CombinedOutput(); err != nil {
51
+ t.Fatalf("payload warm-up: %v %s", err, raw)
52
+ }
47
53
  cfg := config{Package: "DaemonFixture", Exec: "app", Args: []string{"${SYNOPKG_PKGVAR}/a space", "$(touch BAD)'", "${SYNOPKG_PKGDEST}"}, Env: map[string]string{"APP_MODE": mode}, Timeouts: deadlines{Start: 2, Stop: 2, Status: 1}}
48
54
  if os.Geteuid() == 0 {
49
55
  cfg.Package = "nobody"
@@ -91,22 +97,69 @@ func runControl(control, action, dest, data string) (int, string) {
91
97
  return -1, err.Error()
92
98
  }
93
99
 
100
+ // Readiness only proves the child stayed alive for a second, not that it reached
101
+ // main: on a loaded host a stop can kill it before it writes its line. Spawn
102
+ // identity therefore comes from the inventory, and log content is awaited.
103
+ func recordedChild(t *testing.T, data string) int {
104
+ t.Helper()
105
+ raw, err := os.ReadFile(filepath.Join(data, "daemon/daemon.pid"))
106
+ if err != nil {
107
+ t.Fatal(err)
108
+ }
109
+ var r record
110
+ if err := json.Unmarshal(raw, &r); err != nil || r.Child < 1 {
111
+ t.Fatalf("daemon inventory: %v %s", err, raw)
112
+ }
113
+ return r.Child
114
+ }
115
+
116
+ func awaitChildLines(t *testing.T, data string, want int) {
117
+ t.Helper()
118
+ path := filepath.Join(data, "daemon/daemon.log")
119
+ for deadline := time.Now().Add(10 * time.Second); ; time.Sleep(20 * time.Millisecond) {
120
+ raw, _ := os.ReadFile(path)
121
+ if got := strings.Count(string(raw), `"uid":`); got >= want || time.Now().After(deadline) {
122
+ if got != want {
123
+ t.Fatalf("child lines=%d want=%d: %s", got, want, raw)
124
+ }
125
+ return
126
+ }
127
+ }
128
+ }
129
+
94
130
  func TestDaemonLifecycle(t *testing.T) {
95
131
  control, dest, data := fixture(t, "normal")
96
- for _, step := range []struct {
97
- action string
98
- want int
99
- }{{"status", 3}, {"stop", 0}, {"start", 0}, {"status", 0}, {"start", 0}, {"restart", 0}, {"stop", 0}, {"stop", 0}, {"status", 3}, {"unknown", 1}} {
100
- if rc, out := runControl(control, step.action, dest, data); rc != step.want {
101
- t.Fatalf("%s rc=%d want=%d: %s", step.action, rc, step.want, out)
132
+ step := func(action string, want int) {
133
+ t.Helper()
134
+ if rc, out := runControl(control, action, dest, data); rc != want {
135
+ t.Fatalf("%s rc=%d want=%d: %s", action, rc, want, out)
102
136
  }
103
137
  }
138
+ step("status", 3)
139
+ step("stop", 0)
140
+ step("start", 0)
141
+ awaitChildLines(t, data, 1)
142
+ first := recordedChild(t, data)
143
+ step("status", 0)
144
+ step("start", 0)
145
+ if child := recordedChild(t, data); child != first {
146
+ t.Fatalf("duplicate start spawned another child: %d replaced %d", child, first)
147
+ }
148
+ step("restart", 0)
149
+ if child := recordedChild(t, data); child == first {
150
+ t.Fatalf("restart kept child %d", child)
151
+ }
152
+ awaitChildLines(t, data, 2)
153
+ step("stop", 0)
154
+ step("stop", 0)
155
+ step("status", 3)
156
+ step("unknown", 1)
104
157
  raw, err := os.ReadFile(filepath.Join(data, "daemon/daemon.log"))
105
158
  if err != nil {
106
159
  t.Fatal(err)
107
160
  }
108
161
  if strings.Count(string(raw), `"uid":`) != 2 {
109
- t.Fatalf("duplicate start spawned another child: %s", raw)
162
+ t.Fatalf("unexpected child spawn: %s", raw)
110
163
  }
111
164
  if strings.Contains(string(raw), "must-not-leak") {
112
165
  t.Fatalf("ambient environment leaked: %s", raw)
@@ -7,9 +7,16 @@ including newline at most 65536 bytes). Socket access additionally requires exac
7
7
  Linux SO_PEERCRED UID equality, even for another member of the package group.
8
8
  Non-Linux builds support portable tests and refuse privileged service operations.
9
9
 
10
+ Fixed writer children verify `no_new_privs` with `PR_GET_NO_NEW_PRIVS` on their
11
+ locked Linux thread. DSM kernel 4.4 supports this flag but omits its proc status
12
+ field; a failed syscall or a value other than one still refuses the write.
13
+ Saved IDs and zero capabilities remain mandatory. The parent passes the pinned
14
+ package-owned private source as a read-only FD; destination creation and publish
15
+ remain under the subject UID, and source cleanup runs under the package UID.
16
+
10
17
  ## Protected inventory and policy
11
18
 
12
- Setup must copy the digest-pinned executor, policy and root scripts into:
19
+ Setup must copy the INFO-anchored executor, policy-pinned policy and root scripts into:
13
20
 
14
21
  ```
15
22
  /usr/local/etc/synopkgland/<package>/ root:root 0700
@@ -74,7 +81,7 @@ operations and policy-excluded operations are rejected. Replies follow §2.4.
74
81
 
75
82
  | Operation | Parameters | Result |
76
83
  | --- | --- | --- |
77
- | status | none | executor_digest, policy_digest, version, ready |
84
+ | status | none | executor_digest, policy_digest, version, ready, disabled |
78
85
  | delegate.create | job_id, ops[], scope, path, ttl_seconds | delegation_id, subject {username, uid}, expires_at (RFC3339) |
79
86
  | delegate.list | none | delegations[] with public metadata, including revoked/expired records |
80
87
  | delegate.revoke | delegation_id; job_id required without cookie | revoked |
@@ -86,7 +93,7 @@ operations and policy-excluded operations are rejected. Replies follow §2.4.
86
93
  Create/list require a cookie and SynoToken. Revoke accepts the subject/admin session pair or a matching
87
94
  package job ID. Root operations require the session pair independently verified as an
88
95
  active administrators member. Delegated operations recheck the account, UID,
89
- expired=`normal`, non-admin membership, App and File Station privileges, scope,
96
+ expired=`normal`, App and File Station privileges, scope,
90
97
  job, operation, expiry and revocation. UIDs below 1024 are excluded as system
91
98
  accounts. TTL must be 1–2592000 seconds. No cookie/SynoToken/SID/password is persisted.
92
99
 
@@ -94,22 +101,26 @@ Paths use File Station spellings `/home[/...]` or `/<share>[/...]`. A delegation
94
101
  binds the requested directory's canonical path. Symlinks resolving outside that
95
102
  directory are denied. `files.write` opens package-owned, singly linked regular
96
103
  staging files with no symlink components; its fresh child checks setgroups,
97
- setresgid and setresuid in order, then all real/effective IDs and groups. The child
104
+ setresgid and setresuid in order, then real/effective/saved/filesystem IDs and groups, empty capability sets and
105
+ `no_new_privs`. Admin users retain their own nonzero UID and current groups. The child
98
106
  opens the destination after dropping identity, verifies its pinned directory and
99
107
  scope, and copies into an exclusive hidden `.synopkgland-<job_id>-<random>.part`
100
108
  file. After fsync it publishes the complete file with a hard link, then removes
101
109
  the temporary name. Existing files and symlinks are never overwritten. The
102
110
  executor cleans the exact temporary name as the subject user after a canceled or
103
111
  killed child, using a retained directory descriptor; startup performs no scan.
104
- Successful writes delete staging. DSM ACL inheritance can
112
+ Successful writes request exact source cleanup in a separate package-UID child;
113
+ cleanup failure retains the source and never downgrades a committed receipt. DSM ACL inheritance can
105
114
  produce a mode different from the requested 0600 (observed 0711 on vt-nas).
106
115
 
107
116
  File Station CheckPermission uses v3 with a fixed non-overwriting probe filename
108
117
  and size zero; it creates no file. Missing/failed API data is denied. DSM output
109
118
  and root script output are capped at 1 MiB; commands have 20-second deadlines,
110
119
  connection read/write deadlines are 15/5 seconds and request execution is bounded
111
- by 30 seconds. At most 16 connections are admitted. Execution/revocation are
112
- serialized. Linux command timeouts kill the entire command process group.
120
+ by 30 seconds. At most 16 connections are admitted. Legacy runner APIs use short
121
+ admission leases; revoke/disable persist the fence then drain accepted legacy
122
+ calls outside the commit lock before acknowledging. Bulk copy holds no commit
123
+ lock. Linux command timeouts kill the entire command process group.
113
124
 
114
125
  File Station APIs resolve path strings inside DSM after executor scope validation;
115
126
  there is no atomic DSM API for binding that subsequent lookup to our directory
@@ -122,13 +133,20 @@ items, not assertions of complete race freedom.
122
133
 
123
134
  ```
124
135
  executor serve --package com.example.downloader
136
+ executor supervise --package com.example.downloader
125
137
  executor control start --package com.example.downloader
126
138
  executor control stop --package com.example.downloader
127
139
  executor control purge --package com.example.downloader
128
140
  ```
129
141
 
130
142
  Root control start launches asynchronously; the caller reads socket status to
131
- establish readiness. A held flock pidfile refuses duplicate serve. Root's pinned
143
+ establish readiness. It starts `executor supervise`, a root supervisor that runs
144
+ `serve` as its child and restarts it after an abnormal end (signal or non-zero
145
+ exit) with a 1 s backoff doubling to 60 s, reset after a minute of healthy run.
146
+ A restarted `serve --revived` honors a `stop` already in the mailbox. Exit 0
147
+ (root replacement, shutdown, purge), exit 3 (another monitor holds the pidfile)
148
+ or a removed runtime end supervision. A held flock pidfile refuses duplicate
149
+ serve. Root's pinned
132
150
  boot/control start can validate and terminate a previous monitor before launching
133
151
  its replacement; package-account start only writes the mailbox and cannot replace
134
152
  runtime. Stop pauses the listener while the monitor continues. Purge removes this
@@ -136,7 +154,8 @@ package's protected state and deletes only its verified recorded scheduler tasks
136
154
 
137
155
  Run `go test -race ./...` and `bash scripts/ci/gate.sh native-go`. The gate adds
138
156
  Linux amd64/arm64 vet, static binary builds and test compilation. Linux-specific
139
- syscall tests also run on vt-nas from a cross-compiled test executable. The
157
+ syscall tests require a disposable Linux root fixture. This revision was tested
158
+ on a Linux VM; DSM ACL, logout and reboot behavior remain unverified. The
140
159
  `testdata/live_probe.py` harness expects isolated disposable accounts and a
141
160
  manually copied runtime, reads passwords only from stdin, and logs no credentials.
142
161
  The live harness uses DSM's registered `SYNO.Desktop` AppPriv ID as a stand-in for
@@ -148,12 +167,17 @@ an installed App; installed-package AppPriv integration belongs to setup testing
148
167
  Package-account hooks atomically write `stop` before upgrades/stops, `start` on
149
168
  install/upgrade completion and package start, and `purge` before uninstall.
150
169
  `stop` cancels/drains requests and removes the listener without ending the root
151
- monitor. `start` resumes only if both package executor and policy hashes equal
152
- the approved runtime pins; otherwise the service stays paused for administrator
153
- approval. Only root's pinned boot/control path can replace a monitor or runtime.
170
+ monitor. `start` resumes if both package executor and policy hashes equal the approved
171
+ runtime pins. If only the executor changed, the policy hash is unchanged and the
172
+ executor hash equals the single `synopkgland_executor_sha256` key in the
173
+ root-owned `/var/packages/<package>/INFO`, the root monitor copies it into
174
+ `runtime/`, verifies the copy and runs root `control start`, which replaces the
175
+ monitor without reapproval. Any other change stays paused for administrator
176
+ approval. Only root's pinned boot/control path or this anchored install can
177
+ replace a monitor or runtime.
154
178
 
155
179
  At start, scheduler readback must exactly match the canonical lifecycle scripts
156
- regenerated from the package identity and approved executor/policy pins. Package
180
+ regenerated from the package identity and approved policy pin. Package
157
181
  setup metadata supplies hints only. The verified names, pins and script hashes
158
182
  are stored in root:root 0600 `runtime/tasks.json`. Purge regenerates the scripts
159
183
  and revalidates each current definition before EventScheduler v1
@@ -188,9 +212,10 @@ and `purge` clears state immediately. A root-approved new monitor ignores the
188
212
  stop file already present at startup; that exception ends after an absence.
189
213
 
190
214
  The boot task checks pending purge/package disappearance before accessing source.
191
- It verifies retained pins and protected copies again before offline purge. Normal
192
- boot installs verified copies and calls root control start to replace a previous
193
- monitor. Scripts stay within 1 KiB with no percent characters, including the
215
+ It verifies the retained policy pin before offline purge from the root-only
216
+ runtime. Normal boot verifies the copies against the policy pin and the INFO
217
+ executor anchor, installs them and calls root control start to replace a
218
+ previous monitor. Scripts stay within 1 KiB with no percent characters, including the
194
219
  longest package name. Partial/tampered retained state requires administrator
195
220
  review. All nine uninstall wizard locales name the two tasks and disclose the
196
221
  retained DSM storage/infrastructure. `inspect`/`status` cleanup guidance is
@@ -202,3 +227,89 @@ a directory descriptor and verified with fstat, so boot umask 0077 cannot reduce
202
227
  validated and never repaired/adopted. The package directory must already have
203
228
  root/package-group ownership. The socket is set to root/package-group 0660 and
204
229
  verified using Linux O_PATH + fstat on its filesystem inode.
230
+
231
+ ## Explicit long-running v1 extension
232
+
233
+ The pinned policy opts in with top-level `destinations: ["save"]` and
234
+ `lifetime: "until-revoked"`, together with `files.moveFromStaging` in the
235
+ operation allowlist. Destination keys match `^[a-z][a-z0-9_-]{0,63}$`; unknown
236
+ keys/lifetimes fail closed. Legacy policies/TTL grants never become permanent.
237
+ The existing v1 envelope, peer UID check, protected runtime and session verifier
238
+ remain unchanged. `root.run` is independent and is never a writer fallback.
239
+
240
+ | Operation | Parameters | Result |
241
+ | --- | --- | --- |
242
+ | delegate.create (permanent) | scope, ops:[files.moveFromStaging], path, destination, lifetime:until-revoked | delegation_id, subject, path, destination, lifetime; zero expiry |
243
+ | files.moveFromStaging | delegation_id, job_id, staging, name, size | job_id, state |
244
+ | files.operationStatus | original delegation_id, job_id | job_id, state, optional code/receipt |
245
+ | files.cancelOperation | original delegation_id, job_id | current operation state |
246
+ | delegate.disable / delegate.enable | none; live administrator session | disabled |
247
+ | delegate.audit | none; live owner/admin session | latest bounded events |
248
+
249
+ Permanent create needs the current user's cookie and SynoToken and a successful
250
+ File Station write check. The executor derives username/UID and exact directory
251
+ identity itself. Permanent revoke is owner-bound even for administrators; admins
252
+ can disable the entire package. Auth-less operation submission/status/cancel is
253
+ for the trusted package producer holding the grant reference and original job ID.
254
+ If credentials are supplied, both must verify and the owner must match; failed
255
+ caller auth never falls back to offline authority. A compromised package backend
256
+ can use all valid grants it holds, as documented in the approved trust model.
257
+
258
+ Grants survive service restart in the protected store. The disabled flag also
259
+ survives; mailbox start/boot never clears it. Disable persists first and revokes
260
+ all legacy/permanent grants. Enable checks the live admin session and completes
261
+ revocation before clearing the flag; it never restores old grants. Repeated enable
262
+ while already enabled is a no-op.
263
+
264
+ Operations are separate `operation-<sha256(job_id)>.record` files, root:root 0600.
265
+ They bind immutable grant/job/staging/name/size and move through queued, copying,
266
+ committed, canceled, interrupted or uncertain. Queued resumes without credentials;
267
+ copying becomes interrupted on restart, or uncertain after durable publish intent.
268
+ Neither state is replayed. Changed input for the same job is `operation-conflict`.
269
+ Committed status alone includes `receipt:{path,size}`. Cleanup failures retain
270
+ committed and add `CLEANUP_PENDING`. No same-name/hash guessing establishes receipt.
271
+
272
+ Limits: 16 GiB per file, actual size+1 read, 16 queued, one active copy per package,
273
+ 24-hour attempt, 10 MiB operation metadata. Full journals reject new admissions;
274
+ terminal records are retained to prevent replay. Bulk input bypasses job spools
275
+ and the old 64 MiB wrapper limit. Long copies revalidate account/AppPriv/ACL/path
276
+ with five-second polling and bounded checks; publish revalidates again. A failed
277
+ identity/ACL/path check invalidates the grant and requires a new selection.
278
+
279
+ The fixed child drops identity before opening the destination and creating any
280
+ inode. Every component uses FD-relative O_NOFOLLOW; ancestors use O_PATH so only
281
+ search rights are required. Source is package-owned, regular and singly linked;
282
+ destination is the exact granted device/inode, never a caller-supplied path.
283
+ Part metadata is durable before copying. Publish links the open part inode with
284
+ no replacement, refusing files/symlinks/FIFOs at the final name. Revoke, disable
285
+ and publish share the commit lock. Lost/malformed publish replies drain the child
286
+ before releasing that lock and produce uncertain; a confirmed no-side-effect
287
+ refusal such as `destination-exists` remains interrupted. The lock covers bounded
288
+ publish handshake/fsync, never the bulk copy. During the copy the child watches
289
+ its control pipe: if `serve` dies the pipe closes, so the child stops within one
290
+ 1 MiB chunk and unlinks its part (no `PR_SET_PDEATHSIG`, which the identity drop
291
+ clears; works on kernel 3.10). Kernel-blocked filesystem I/O can
292
+ still delay draining; it cannot be declared safely complete by a timeout.
293
+ Legacy `files.write` uses this same child and publish handshake; the executor
294
+ has no other writer, so no child can publish after `serve` is gone.
295
+
296
+ Destination/part cleanup runs as the owner; source cleanup runs as package UID.
297
+ Restart cleans only journaled exact part identity as the current eligible owner;
298
+ changed/missing/inaccessible directories are retained for review, never scanned
299
+ or deleted as root. Subject-controlled directory moves after the final check
300
+ can still complete in the pinned directory object. DSM ACL changes and publication
301
+ are not atomic, and silent UID/inode reuse is not universally detected. A subject
302
+ who owns the destination can mutate its own output; this is not a content sandbox.
303
+
304
+ Structured audit lives in protected `audit.jsonl`/`audit.previous`, at most 50 MiB
305
+ combined, separate from authority. Events include package, username/UID, operation,
306
+ job, location and result; no session credentials. Owner queries are filtered,
307
+ admins can view this package's events. Replies contain at most 256 events/1 MiB.
308
+ Admission events are labeled `admitting` because a subsequent durable write can
309
+ fail. The operation journal, not an admission audit event, establishes completion.
310
+
311
+ The existing status operation reads `Store.Disabled`; protected-store errors set
312
+ `ready:false` rather than assuming enabled. Backend `delegate.status` exposes the
313
+ verified boolean and `disabled` capability state. Backend `delegate.operations.list`
314
+ is strictly empty-params, freshly owner/UID-filtered move-mirror metadata; it is
315
+ not an executor operation. Live completion remains `files.operationStatus`.