@synopackageland/cli 0.14.1 → 0.15.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 (114) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +26 -16
  3. package/dist/build-spk/generator.d.ts +3 -2
  4. package/dist/build-spk/generator.js +6 -846
  5. package/dist/build-spk/resource-worker.d.ts +2 -14
  6. package/dist/build-spk/resource-worker.js +3 -166
  7. package/dist/cli.js +208 -56
  8. package/dist/compose/analyze.d.ts +3 -61
  9. package/dist/compose/analyze.js +35 -776
  10. package/dist/compose/env.d.ts +5 -3
  11. package/dist/compose/env.js +17 -25
  12. package/dist/compose/runner.js +23 -6
  13. package/dist/contract/web-launch.d.ts +1 -12
  14. package/dist/contract/web-launch.js +10 -74
  15. package/dist/ctl/cli.d.ts +36 -0
  16. package/dist/ctl/cli.js +218 -0
  17. package/dist/ctl/descriptor.d.ts +81 -0
  18. package/dist/ctl/descriptor.js +93 -0
  19. package/dist/ctl/dsm-auth.d.ts +10 -0
  20. package/dist/ctl/dsm-auth.js +30 -0
  21. package/dist/ctl/errors.d.ts +17 -0
  22. package/dist/ctl/errors.js +32 -0
  23. package/dist/ctl/flows.d.ts +11 -0
  24. package/dist/ctl/flows.js +52 -0
  25. package/dist/ctl/input.d.ts +20 -0
  26. package/dist/ctl/input.js +128 -0
  27. package/dist/ctl/json.d.ts +2 -0
  28. package/dist/ctl/json.js +38 -0
  29. package/dist/ctl/redaction.d.ts +15 -0
  30. package/dist/ctl/redaction.js +198 -0
  31. package/dist/ctl/service.d.ts +38 -0
  32. package/dist/ctl/service.js +156 -0
  33. package/dist/ctl/store.d.ts +51 -0
  34. package/dist/ctl/store.js +93 -0
  35. package/dist/ctl/transfers.d.ts +14 -0
  36. package/dist/ctl/transfers.js +74 -0
  37. package/dist/ctl/transport.d.ts +11 -0
  38. package/dist/ctl/transport.js +79 -0
  39. package/dist/deploy/deploy.d.ts +4 -3
  40. package/dist/deploy/deploy.js +5 -210
  41. package/dist/deploy/plan.js +26 -7
  42. package/dist/dev.js +5 -4
  43. package/dist/error-registry.js +1020 -834
  44. package/dist/info.js +120 -28
  45. package/dist/init.js +188 -64
  46. package/dist/inspect.js +21 -6
  47. package/dist/runtime/executor-digests.json +1 -12
  48. package/dist/skill.js +51 -9
  49. package/dist/standalone/api-description-inspect.d.ts +4 -0
  50. package/dist/standalone/api-description-inspect.js +12 -0
  51. package/dist/standalone/api-description.d.ts +7 -0
  52. package/dist/standalone/api-description.js +73 -0
  53. package/dist/standalone/build.d.ts +3 -0
  54. package/dist/standalone/build.js +140 -33
  55. package/dist/standalone/compose-errors.d.ts +3 -0
  56. package/dist/standalone/compose-errors.js +243 -0
  57. package/dist/standalone/compose.d.ts +97 -10
  58. package/dist/standalone/compose.js +306 -75
  59. package/dist/standalone/daemon.js +111 -29
  60. package/dist/standalone/dsm-window.js +39 -8
  61. package/dist/standalone/identity-gateway.d.ts +9 -2
  62. package/dist/standalone/identity-gateway.js +282 -66
  63. package/dist/standalone/inspect-spk.d.ts +12 -0
  64. package/dist/standalone/inspect-spk.js +666 -136
  65. package/dist/standalone/lifecycle.js +124 -42
  66. package/dist/standalone/manifest.d.ts +8 -1
  67. package/dist/standalone/manifest.js +303 -86
  68. package/dist/standalone/resource.js +6 -2
  69. package/dist/standalone/spk.d.ts +4 -1
  70. package/dist/standalone/spk.js +138 -39
  71. package/dist/template-paths.d.ts +0 -2
  72. package/dist/template-paths.js +0 -5
  73. package/dist/test-command.d.ts +1 -1
  74. package/dist/test-command.js +1 -1
  75. package/dist/validate.d.ts +3 -3
  76. package/dist/validate.js +32 -140
  77. package/package.json +5 -3
  78. package/schema/synopkgland-api-v1.schema.json +423 -0
  79. package/skills/SKILL.md +3 -1
  80. package/skills/reference/compose.md +349 -0
  81. package/skills/reference/create.md +8 -17
  82. package/skills/reference/ctl.md +41 -0
  83. package/skills/reference/deploy.md +1 -1
  84. package/skills/reference/files.md +4 -0
  85. package/skills/reference/identity-gateway.md +8 -144
  86. package/skills/reference/wrap.md +10 -39
  87. package/assets/go/daemon/fixture_linux_test.go +0 -15
  88. package/assets/go/daemon/fixture_other_test.go +0 -7
  89. package/assets/go/daemon/go.mod +0 -5
  90. package/assets/go/daemon/main.go +0 -636
  91. package/assets/go/daemon/main_test.go +0 -315
  92. package/assets/go/daemon/network.go +0 -107
  93. package/assets/go/daemon/network_test.go +0 -86
  94. package/assets/go/daemon/process_linux.go +0 -55
  95. package/assets/go/daemon/process_linux_test.go +0 -161
  96. package/assets/go/daemon/process_other.go +0 -14
  97. package/assets/go/daemon/testdata/child.go +0 -44
  98. package/assets/go/gateway/auth.go +0 -101
  99. package/assets/go/gateway/auth_test.go +0 -216
  100. package/assets/go/gateway/cgi.go +0 -133
  101. package/assets/go/gateway/cgi_test.go +0 -156
  102. package/assets/go/gateway/csrf.go +0 -73
  103. package/assets/go/gateway/csrf_test.go +0 -155
  104. package/assets/go/gateway/directory.go +0 -108
  105. package/assets/go/gateway/directory_test.go +0 -202
  106. package/assets/go/gateway/gateway.go +0 -412
  107. package/assets/go/gateway/gateway_test.go +0 -676
  108. package/assets/go/gateway/go.mod +0 -5
  109. package/assets/go/gateway/groups.go +0 -66
  110. package/assets/go/gateway/main.go +0 -45
  111. package/dist/build-spk/gateway.d.ts +0 -31
  112. package/dist/build-spk/gateway.js +0 -60
  113. package/dist/compose/privilege.d.ts +0 -14
  114. package/dist/compose/privilege.js +0 -102
@@ -1,840 +1,1026 @@
1
+ import { COMPOSE_ERRORS } from "./standalone/compose-errors.js";
1
2
  export const ERROR_REGISTRY = {
2
- 'LAUNCHER_PORTAL_MISMATCH': { code: 'LAUNCHER_PORTAL_MISMATCH', description: 'The launcher has Portal fields without a matching explicit Portal declaration.', suggestion: 'Remove defaultAlias and login fields, or explicitly enable portal and rebuild.' },
3
- 'LAUNCHER_TYPE_MISMATCH': { code: 'LAUNCHER_TYPE_MISMATCH', description: 'The built launcher or INFO entry differs from the declared launch mode.', suggestion: 'Rebuild with synopkgland build; native new-page uses standalone, and schema-1 web uses url at the trusted entry; schema-2 SDK wrappers use standalone.' },
4
- 'STANDALONE_UI_LAUNCH_INVALID': { code: 'STANDALONE_UI_LAUNCH_INVALID', description: 'The native ui.launch value is invalid.', suggestion: 'Set ui.launch to dsm-window (UI SDK) or new-page (other frontends). Portal is a separate opt-in.' },
5
- "NATIVE_LAYOUT_INVALID": {
6
- code: "NATIVE_LAYOUT_INVALID",
7
- description: "The native layout's manifest, hooks, toolkit or artifacts violate the package-only contract.",
8
- suggestion: "Use schema 2 with backend.kind: native; check paths, ELF linkage and checksum pins."
3
+ ...COMPOSE_ERRORS,
4
+ CTL_TRUST_REQUIRED: { "code": "CTL_TRUST_REQUIRED", "description": "App description access requires approval.", "suggestion": "Run synopkgland ctl list with explicit host and approve the candidate batch." },
5
+ CTL_UNSUPPORTED_BACKEND: { "code": "CTL_UNSUPPORTED_BACKEND", "description": "Automatic descriptions require a native SDK backend.", "suggestion": "Disable ctl for binary/Compose wrappers or implement the public contract separately." },
6
+ CTL_DESCRIBE_REQUIRED: { "code": "CTL_DESCRIBE_REQUIRED", "description": "The Go backend has no build-only registry exporter.", "suggestion": "Update the native template and implement --describe using api.DescribeRoutes." },
7
+ CTL_DESCRIBE_FAILED: { "code": "CTL_DESCRIBE_FAILED", "description": "The backend registry export failed.", "suggestion": "Check the backend --describe output and build diagnostics." },
8
+ CTL_COMMAND_UNREGISTERED: { "code": "CTL_COMMAND_UNREGISTERED", "description": "A command references an unregistered backend method.", "suggestion": "Correct ctl.commands bindings or register the backend method." },
9
+ CTL_DESCRIPTOR_INVALID: { "code": "CTL_DESCRIPTOR_INVALID", "description": "The generated API description violates the public contract.", "suggestion": "Correct ctl.commands and rebuild the private synopkgland-api.json." },
10
+ CTL_DECLARATION_INVALID: { "code": "CTL_DECLARATION_INVALID", "description": "The ctl declaration is invalid.", "suggestion": "Check ctl.enabled, aliases and explicit commands in dsm-package.yaml." },
11
+ ...{
12
+ LAUNCHER_PORTAL_MISMATCH: {
13
+ code: "LAUNCHER_PORTAL_MISMATCH",
14
+ description: "The launcher has Portal fields without a matching explicit Portal declaration.",
15
+ suggestion: "Remove defaultAlias and login fields, or explicitly enable portal and rebuild.",
16
+ },
17
+ LAUNCHER_TYPE_MISMATCH: {
18
+ code: "LAUNCHER_TYPE_MISMATCH",
19
+ description: "The built launcher or INFO entry differs from the declared launch mode.",
20
+ suggestion: "Rebuild with synopkgland build; native new-page uses standalone, and schema-1 web uses url at the trusted entry; schema-2 SDK wrappers use standalone.",
21
+ },
22
+ STANDALONE_UI_LAUNCH_INVALID: {
23
+ code: "STANDALONE_UI_LAUNCH_INVALID",
24
+ description: "The native ui.launch value is invalid.",
25
+ suggestion: "Set ui.launch to dsm-window (UI SDK) or new-page (other frontends). Portal is a separate opt-in.",
26
+ },
27
+ NATIVE_LAYOUT_INVALID: {
28
+ code: "NATIVE_LAYOUT_INVALID",
29
+ description: "The native layout's manifest, hooks, toolkit or artifacts violate the package-only contract.",
30
+ suggestion: "Use schema 2 with backend.kind: native; check paths, ELF linkage and checksum pins.",
31
+ },
32
+ CONTRACT_SCHEMA_UNSUPPORTED: {
33
+ code: "CONTRACT_SCHEMA_UNSUPPORTED",
34
+ description: "The schema version in synology-app.yaml is not supported.",
35
+ suggestion: "Set the schema field in synology-app.yaml to 1.",
36
+ },
37
+ CONTRACT_MISSING_FIELD: {
38
+ code: "CONTRACT_MISSING_FIELD",
39
+ description: "synology-app.yaml is missing a required field.",
40
+ suggestion: "Add the missing required fields to synology-app.yaml (for example package, version, displayname).",
41
+ },
42
+ CONTRACT_SCHEMA_INVALID: {
43
+ code: "CONTRACT_SCHEMA_INVALID",
44
+ description: "synology-app.yaml does not match the JSON Schema.",
45
+ suggestion: "Fix the field formats in synology-app.yaml so they match the schema.",
46
+ },
47
+ PACKAGE_IDENTITY_TOO_LONG: {
48
+ code: "PACKAGE_IDENTITY_TOO_LONG",
49
+ description: "The Package Identity exceeds the length limit (28 characters for a formal identity, 32 for the final identity).",
50
+ suggestion: "Shorten the package name in synology-app.yaml (at most 28 characters for the formal identity, leaving 4 characters for the .dev suffix).",
51
+ },
52
+ CONTRACT_PACKAGE_TOO_LONG: {
53
+ code: "CONTRACT_PACKAGE_TOO_LONG",
54
+ description: "The Package Identity exceeds the DSM limit of 32 characters.",
55
+ suggestion: "Shorten the package name in synology-app.yaml to 32 characters or fewer.",
56
+ },
57
+ CONTRACT_ADMINPORT_INVALID: {
58
+ code: "CONTRACT_ADMINPORT_INVALID",
59
+ description: "adminport must be an integer.",
60
+ suggestion: "Provide an integer adminport in synology-app.yaml that matches the Compose published port.",
61
+ },
62
+ CONTRACT_LAUNCH_INVALID: {
63
+ code: "CONTRACT_LAUNCH_INVALID",
64
+ description: "The integration.web.launch mode is invalid.",
65
+ suggestion: "Use dsm-window (DSM window) or new-page (new tab, the default); omit this field for a headless App.",
66
+ },
67
+ CONTRACT_LAUNCH_REQUIRES_WEB: {
68
+ code: "CONTRACT_LAUNCH_REQUIRES_WEB",
69
+ description: "A headless App does not declare a Web Launch Mode.",
70
+ suggestion: "Remove integration.web.launch, or add the adminport that matches the Compose published port.",
71
+ },
72
+ CONTRACT_PORTAL_REQUIRES_EXECUTOR: {
73
+ code: "CONTRACT_PORTAL_REQUIRES_EXECUTOR",
74
+ description: "The Compose App declares integration.web.portal, but a Compose App has no executor; removing the sign-in alias after uninstall needs root so nginx stops serving it.",
75
+ suggestion: "Remove integration.web.portal; for a sign-in alias, ask an administrator to configure a reverse proxy in DSM instead.",
76
+ },
77
+ CONTRACT_ACCESS_INVALID: {
78
+ code: "CONTRACT_ACCESS_INVALID",
79
+ description: "The integration.web.access mode is invalid.",
80
+ suggestion: "Use dsm-users (signed-in DSM users, the default), dsm-admins, dsm-groups (with groups), or public to keep an anonymous Web Station entry.",
81
+ },
82
+ CONTRACT_ACCESS_GROUPS_INVALID: {
83
+ code: "CONTRACT_ACCESS_GROUPS_INVALID",
84
+ description: "integration.web.groups is missing, malformed, or not combined with access: dsm-groups.",
85
+ suggestion: "Use access: dsm-groups and list DSM group names, or remove groups.",
86
+ },
87
+ CONTRACT_ACCESS_REQUIRES_WEB: {
88
+ code: "CONTRACT_ACCESS_REQUIRES_WEB",
89
+ description: "A headless App does not declare Web access rules.",
90
+ suggestion: "Remove integration.web.access/groups, or add the adminport that matches the Compose published port.",
91
+ },
92
+ GATEWAY_ARCH_FAMILY_REQUIRED: {
93
+ code: "GATEWAY_ARCH_FAMILY_REQUIRED",
94
+ description: "The DSM verification gateway is a native binary, so the SPK must name its target architecture.",
95
+ suggestion: "Add --arch x86_64 or --arch aarch64 (deploy detects it from the NAS); only Apps without DSM verification may choose public in integration.web.access.",
96
+ },
97
+ CONTRACT_INVALID_STRING: {
98
+ code: "CONTRACT_INVALID_STRING",
99
+ description: "A contract string contains a control character or the ${ interpolation syntax, which is not allowed.",
100
+ suggestion: "Remove control characters and ${ interpolation; declare install parameters in the install block.",
101
+ },
102
+ INSTALL_STRING_TOO_LONG: {
103
+ code: "INSTALL_STRING_TOO_LONG",
104
+ description: "An Install Parameter string default exceeds 256 characters.",
105
+ suggestion: "Shorten the default string of the install parameter in synology-app.yaml.",
106
+ },
107
+ INSTALL_STRING_INVALID: {
108
+ code: "INSTALL_STRING_INVALID",
109
+ description: "An Install Parameter string value contains an invalid character or interpolation.",
110
+ suggestion: "Remove control characters and ${ interpolation from the install parameter default.",
111
+ },
112
+ CONTRACT_SECRET_UNSUPPORTED: {
113
+ code: "CONTRACT_SECRET_UNSUPPORTED",
114
+ description: "install.<NAME>.type: secret has no usable install path today: whether or not Compose references it, Package Center install ends in DSM broken/150 and creates no Container Manager project.",
115
+ suggestion: "Use type: string (non-sensitive), or have the App fetch the secret itself at startup or read a file an administrator places in ${SYNO_APP_SECRETS_DIR}. The secret type stays closed until the DSM worker failure reason is observable.",
116
+ },
117
+ CONTRACT_HOMES_UNSUPPORTED: {
118
+ code: "CONTRACT_HOMES_UNSUPPORTED",
119
+ description: "install.<NAME>.type: homes has no usable install path today: the install wizard generates no input control and Package Center install ends in DSM broken/150; CLI deploy can resolve /volume1/homes, but DSM rejects the compound install with 4540.",
120
+ suggestion: "If the App needs an administrator-chosen folder, use type: shared_folder instead. The homes type stays closed until the wizard control and preinst path resolution exist.",
121
+ },
122
+ CONTRACT_HOMES_PATH_FORBIDDEN: {
123
+ code: "CONTRACT_HOMES_PATH_FORBIDDEN",
124
+ description: "A homes Install Parameter must not write a NAS physical path or default value into the contract.",
125
+ suggestion: "Declare only type: homes, reference the parameter in Compose, and let the package resolve this NAS's User Home path at install time.",
126
+ },
127
+ CONTRACT_POSIX_ALL_SHARES_VALUE_FORBIDDEN: {
128
+ code: "CONTRACT_POSIX_ALL_SHARES_VALUE_FORBIDDEN",
129
+ description: "A POSIX all-shares parameter must not specify a default or NAS physical path in the contract.",
130
+ suggestion: "Declare only type: posix_all_shares and let the wizard/CLI take the current Share snapshot at install time.",
131
+ },
132
+ CONTRACT_POSIX_ALL_SHARES_LISTING_UNSUPPORTED: {
133
+ code: "CONTRACT_POSIX_ALL_SHARES_LISTING_UNSUPPORTED",
134
+ description: "posix_all_shares requires the CLI to take a Share list snapshot of this NAS with an administrator session at install time; Package Center and Package Source listing cannot fulfill this contract.",
135
+ suggestion: "Use synopkgland deploy --host <name> so an administrator session takes the Share list snapshot at install time; this type cannot ship through Package Center or Package Source listing.",
136
+ },
137
+ DSM_USER_REQUIRED: {
138
+ code: "DSM_USER_REQUIRED",
139
+ description: "A Shared Folder requires exactly one dsm_user as the Container Run-as.",
140
+ suggestion: "Add install.<NAME>.type: dsm_user to synology-app.yaml and reference ${NAME_UID}:${NAME_GID} in Compose.",
141
+ },
142
+ PROJECT_RUNTIME_EXCLUSIVE: {
143
+ code: "PROJECT_RUNTIME_EXCLUSIVE",
144
+ description: "The project has both compose.yaml and dsm-package.yaml, which are mutually exclusive.",
145
+ suggestion: "Choose compose.yaml (container App) or dsm-package.yaml (native App) and delete the other file.",
146
+ },
147
+ DEPLOY_FAILED: {
148
+ code: "DEPLOY_FAILED",
149
+ description: "An unclassified error occurred while deploying to the NAS.",
150
+ suggestion: "Check NAS connectivity and logs: synopkgland logs --host <nas>.",
151
+ },
152
+ SHARE_VALUE_REQUIRED: {
153
+ code: "SHARE_VALUE_REQUIRED",
154
+ description: "Deploy is missing the physical path mapping for a shared_folder declared in the contract.",
155
+ suggestion: "Provide the matching Shared Folder name with --share <SLOT>=<logical name>.",
156
+ },
157
+ SHARE_LOGICAL_NAME_REQUIRED: {
158
+ code: "SHARE_LOGICAL_NAME_REQUIRED",
159
+ description: "A Share parameter needs a logical name; physical paths such as /volume1/... are forbidden.",
160
+ suggestion: "Give the Shared Folder logical name from DSM Control Panel, for example --share MEDIA_SHARE=photos.",
161
+ },
162
+ SHARE_NOT_FOUND: {
163
+ code: "SHARE_NOT_FOUND",
164
+ description: "The named Shared Folder was not found on the target NAS.",
165
+ suggestion: "Create the Shared Folder in DSM Control Panel, or check the spelling.",
166
+ },
167
+ HOMES_NOT_ENABLED: {
168
+ code: "HOMES_NOT_ENABLED",
169
+ description: "The target NAS has User Home disabled, or the homes physical bind source cannot be resolved.",
170
+ suggestion: "Enable DSM User Home on this NAS, confirm the DSM API returns the resolved path, then deploy again.",
171
+ },
172
+ DSM_USER_LOGICAL_NAME_REQUIRED: {
173
+ code: "DSM_USER_LOGICAL_NAME_REQUIRED",
174
+ description: "A dsm_user parameter needs a DSM user logical name; a numeric UID is forbidden.",
175
+ suggestion: "Pass the DSM account name with --run-as (for example --run-as RUN_AS=alice).",
176
+ },
177
+ SHARE_RUN_AS_NO_ACCESS: {
178
+ code: "SHARE_RUN_AS_NO_ACCESS",
179
+ description: "The deploy pre-install check found that the chosen Container Run-as user has no permission on the chosen Shared Folder (or is explicitly denied). The container mounts the Share as that user, so the install succeeds but runtime reads and writes get Permission denied.",
180
+ suggestion: "Grant that user (or their group) permission on the Shared Folder in DSM Control Panel, or choose another user/Share, then deploy again. This check only runs in deploy; the Package Center install wizard does not go through it, and ACLs remain the administrator's responsibility.",
181
+ },
182
+ PORT_ALREADY_IN_USE: {
183
+ code: "PORT_ALREADY_IN_USE",
184
+ description: "The deploy pre-install check found the Compose published TCP port already taken by another service or container.",
185
+ suggestion: "Change the published host port in synology-app.yaml/compose.yaml; existing containers of the same Package Identity are allowed during upgrade or reinstall.",
186
+ },
187
+ CATALOG_INSTALL_FAILED: {
188
+ code: "CATALOG_INSTALL_FAILED",
189
+ description: "NAS Package Center failed to install the package.",
190
+ suggestion: "Check the install log in DSM Package Center or try installing manually.",
191
+ },
192
+ DEPLOY_PARAM_UNDECLARED: {
193
+ code: "DEPLOY_PARAM_UNDECLARED",
194
+ description: "deploy --param provided an Install Parameter the contract does not declare, or an install wizard value that a native-go/binary package does not have.",
195
+ suggestion: "For Compose pass only string or boolean names declared in synology-app.yaml install; native-go/binary accept only delegate_share or delegate_home from the install-selected delegate.",
196
+ },
197
+ DEPLOY_PARAM_REQUIRED: {
198
+ code: "DEPLOY_PARAM_REQUIRED",
199
+ description: "The first install of an install-selected delegate package did not provide the install wizard scope value, so preinst rejects the install.",
200
+ suggestion: "Add --param delegate_share=<shared folder> or --param delegate_home=true and deploy again.",
201
+ },
202
+ DEPLOY_PARAM_TYPE_INVALID: {
203
+ code: "DEPLOY_PARAM_TYPE_INVALID",
204
+ description: "The deploy --param value does not match the string or boolean type declared in the contract.",
205
+ suggestion: "Pass a non-empty string for string; pass only true or false for boolean.",
206
+ },
207
+ DSM_DEPENDENCY_CONFLICT: {
208
+ code: "DSM_DEPENDENCY_CONFLICT",
209
+ description: "DSM rejected the compound install request because of running dependent packages.",
210
+ suggestion: "Stop the listed dependent packages, then retry the upgrade.",
211
+ },
212
+ DSM_COMPOUND_FAILED: {
213
+ code: "DSM_COMPOUND_FAILED",
214
+ description: "A substep of the DSM compound install request failed.",
215
+ suggestion: "Check the api, method and errorCode in the response and the DSM Package Center log, then retry.",
216
+ },
217
+ DSM_REQUEST_FAILED: {
218
+ code: "DSM_REQUEST_FAILED",
219
+ description: "The DSM SYNO.Entry.Request compound request failed.",
220
+ suggestion: "Check the DSM session, request parameters and Package Center log, then retry.",
221
+ },
222
+ NOT_INSTALLED: {
223
+ code: "NOT_INSTALLED",
224
+ description: "The package is not installed on the target NAS yet.",
225
+ suggestion: "Run synopkgland deploy --host <nas> to install the package first.",
226
+ },
227
+ STANDALONE_MANIFEST_MISSING: {
228
+ code: "STANDALONE_MANIFEST_MISSING",
229
+ description: "dsm-package.yaml was not found.",
230
+ suggestion: "Run from the project root, or create a project with synopkgland init -t native-go.",
231
+ },
232
+ STANDALONE_MANIFEST_YAML: {
233
+ code: "STANDALONE_MANIFEST_YAML",
234
+ description: "dsm-package.yaml is not a valid YAML object.",
235
+ suggestion: "Fix the YAML syntax in dsm-package.yaml.",
236
+ },
237
+ APP_SCRIPT_UNKNOWN: {
238
+ code: "APP_SCRIPT_UNKNOWN",
239
+ description: "A file in the project's dsm-scripts/ folder does not have a DSM package script name the SDK runs.",
240
+ suggestion: "Name it preinst, postinst, preuninst, postuninst, preupgrade, postupgrade or start-stop-status, or move it out of dsm-scripts/. prereplace and postreplace are not supported.",
241
+ },
242
+ APP_SCRIPT_INVALID: {
243
+ code: "APP_SCRIPT_INVALID",
244
+ description: "A dsm-scripts/<DSM script name> entry is a symlink, directory or special file.",
245
+ suggestion: "Make it a regular file in the project's dsm-scripts/ folder.",
246
+ },
247
+ APP_SCRIPT_NOT_EXECUTABLE: {
248
+ code: "APP_SCRIPT_NOT_EXECUTABLE",
249
+ description: "A dsm-scripts/<DSM script name> file is not executable; DSM runs it directly.",
250
+ suggestion: "Run chmod +x on the file.",
251
+ },
252
+ APP_SCRIPT_NO_SHEBANG: {
253
+ code: "APP_SCRIPT_NO_SHEBANG",
254
+ description: "A dsm-scripts/<DSM script name> file does not start with #!.",
255
+ suggestion: "Start the file with an interpreter line such as #!/bin/sh.",
256
+ },
257
+ STANDALONE_FIELD_UNKNOWN: {
258
+ code: "STANDALONE_FIELD_UNKNOWN",
259
+ description: "dsm-package.yaml contains an unsupported field.",
260
+ suggestion: "Remove the field; a Standalone Package does not accept custom lifecycle scripts or DSM conf.",
261
+ },
262
+ STANDALONE_SCHEMA_INVALID: {
263
+ code: "STANDALONE_SCHEMA_INVALID",
264
+ description: "The schema version in dsm-package.yaml is not supported.",
265
+ suggestion: "Set schema to 2 and use backend.kind and capabilities.",
266
+ },
267
+ STANDALONE_ROOT_OPS_MISSING: {
268
+ code: "STANDALONE_ROOT_OPS_MISSING",
269
+ description: "capabilities.rootOps: true is set but there are no root operations.",
270
+ suggestion: "Put at least one <name>.sh in root/, or drop capabilities.rootOps.",
271
+ },
272
+ STANDALONE_DAEMON_INVALID: {
273
+ code: "STANDALONE_DAEMON_INVALID",
274
+ description: "The package daemon declaration does not match the fixed argv, environment and lifecycle contract.",
275
+ suggestion: "Check daemon.exec, the per-architecture targets, args, env, uninstall_args and integer-second timeouts; remove shell, user and custom conf.",
276
+ },
277
+ UNINSTALL_INVALID: {
278
+ code: "UNINSTALL_INVALID",
279
+ description: "The uninstall declaration does not match the fixed exec, argv, environment and timeout contract.",
280
+ suggestion: "Check uninstall.purge's exec (inside the package, not in executor/ or root/), args, env (SYNOPKGLAND_* not allowed) and 2-120 second timeout, and the unique keys of state.items.",
281
+ },
282
+ UNINSTALL_STATE_REQUIRES_PURGE: {
283
+ code: "UNINSTALL_STATE_REQUIRES_PURGE",
284
+ description: "uninstall.state has no App uninstall hook that could delete the state.",
285
+ suggestion: "Add uninstall.purge so the App deletes the listed state when SYNOPKGLAND_DELETE_STATE=1, or remove uninstall.state.",
286
+ },
287
+ UNINSTALL_STATE_TEXT_MISSING: {
288
+ code: "UNINSTALL_STATE_TEXT_MISSING",
289
+ description: "The uninstall wizard cannot list the text of the uninstall.state items.",
290
+ suggestion: "Add uninstall.state.<key> to i18n.messages en.json (and the other languages).",
291
+ },
292
+ UNINSTALL_ROOT_REQUIRES_ROOT_OPS: {
293
+ code: "UNINSTALL_ROOT_REQUIRES_ROOT_OPS",
294
+ description: "uninstall.purge.root needs administrator-approved root operations.",
295
+ suggestion: "Declare capabilities.rootOps: true and provide root/<name>.sh, or remove uninstall.purge.root.",
296
+ },
297
+ UNINSTALL_ROOT_OP_MISSING: {
298
+ code: "UNINSTALL_ROOT_OP_MISSING",
299
+ description: "uninstall.purge.root points at a root operation that does not exist.",
300
+ suggestion: "Add root/<name>.sh, or change uninstall.purge.root to an existing root operation name.",
301
+ },
302
+ UNINSTALL_EXEC_MISSING: {
303
+ code: "UNINSTALL_EXEC_MISSING",
304
+ description: "uninstall.purge.exec is not a regular executable file inside the built package.",
305
+ suggestion: "Point at an executable in the package, for example ui/cgi/api.cgi, background/worker, or an executable file under daemon/payload/.",
306
+ },
307
+ UNINSTALL_EXEC_UNSAFE: {
308
+ code: "UNINSTALL_EXEC_UNSAFE",
309
+ description: "uninstall.purge.exec is a shell script that tries to gain root.",
310
+ suggestion: "Hooks always run as the package account; put cleanup that needs root in root/<name>.sh and declare it with uninstall.purge.root.",
311
+ },
312
+ STANDALONE_DAEMON_BUILD_FAILED: {
313
+ code: "STANDALONE_DAEMON_BUILD_FAILED",
314
+ description: "The package daemon's Go supervisor could not be built.",
315
+ suggestion: "Install Go 1.21 or later (builds pin go1.26.9 and need it offline) and check the compiler stderr; the NAS does not need a Go toolchain.",
316
+ },
317
+ STANDALONE_DAEMON_PAYLOAD_INVALID: {
318
+ code: "STANDALONE_DAEMON_PAYLOAD_INVALID",
319
+ description: "The daemon payload is not a bounded, link-free directory inside the project.",
320
+ suggestion: "Rebuild the per-architecture bundles; remove symlinks, special files and setid bits, and keep the size under 512 MiB.",
321
+ },
322
+ STANDALONE_DAEMON_ARTIFACT_REQUIRED: {
323
+ code: "STANDALONE_DAEMON_ARTIFACT_REQUIRED",
324
+ description: "The built supervisor is missing while assembling the daemon SPK.",
325
+ suggestion: "Build the complete artifacts with synopkgland build; do not call the assembler directly without the controller.",
326
+ },
327
+ STANDALONE_IDENTITY_GATEWAY_INVALID: {
328
+ code: "STANDALONE_IDENTITY_GATEWAY_INVALID",
329
+ description: "The identity_gateway declaration does not match the gateway contract.",
330
+ suggestion: "Declare a daemon; set sdk, mode (oidc, header or token), the App's loopback upstream port and distinct listen ports other than 5000/5001; keep passthrough and redirects local paths outside /_sso; oidc needs issuer_port and clients, header needs headers.user, token needs token.exec in the daemon payload.",
331
+ },
332
+ STANDALONE_IDENTITY_GATEWAY_BUILD_FAILED: {
333
+ code: "STANDALONE_IDENTITY_GATEWAY_BUILD_FAILED",
334
+ description: "The SDK identity gateway could not be built from identity_gateway.sdk.",
335
+ suggestion: "Use an SDK release that contains idgateway/cmd/gateway, install Go (builds pin go1.26.9) and check the compiler stderr.",
336
+ },
337
+ STANDALONE_IDENTITY_GATEWAY_ARTIFACT_REQUIRED: {
338
+ code: "STANDALONE_IDENTITY_GATEWAY_ARTIFACT_REQUIRED",
339
+ description: "The built identity gateway is missing while assembling the SPK.",
340
+ suggestion: "Build the complete artifacts with synopkgland build; do not call the assembler directly without the gateway.",
341
+ },
342
+ STANDALONE_SHARED_FOLDER_INVALID: {
343
+ code: "STANDALONE_SHARED_FOLDER_INVALID",
344
+ description: "The install.sharedFolder declaration in dsm-package.yaml is invalid.",
345
+ suggestion: "Write install: { sharedFolder: { mode: select|create, key: <lowercase alphanumeric and underscore>, permission: ro|rw } }; create also needs name (the DSM shared folder name) and select must not have one.",
346
+ },
347
+ STANDALONE_BACKGROUND_INVALID: {
348
+ code: "STANDALONE_BACKGROUND_INVALID",
349
+ description: "The package background job declaration does not match the fixed worker contract.",
350
+ suggestion: "Write only capabilities.jobs: true or { worker: sha256-v1 }, use backend.kind: go, and do not combine it with daemon or integration; background processes always run as the package account.",
351
+ },
352
+ STANDALONE_BACKGROUND_BUILD_FAILED: {
353
+ code: "STANDALONE_BACKGROUND_BUILD_FAILED",
354
+ description: "The package background worker could not be built.",
355
+ suggestion: "Install Go 1.25 or later, confirm the backend has cmd/background-worker (create it with synopkgland init -t native-go --with jobs), and check the compiler stderr.",
356
+ },
357
+ STANDALONE_BACKGROUND_ARTIFACT_REQUIRED: {
358
+ code: "STANDALONE_BACKGROUND_ARTIFACT_REQUIRED",
359
+ description: "The built worker is missing while assembling the background job SPK.",
360
+ suggestion: "Build the complete artifacts with synopkgland build; do not call the assembler directly without the worker.",
361
+ },
362
+ STANDALONE_ROOT_OP_INVALID: {
363
+ code: "STANDALONE_ROOT_OP_INVALID",
364
+ description: "root/ contains an invalid root operation file.",
365
+ suggestion: "A script must be a regular root/<lowercase name>.sh starting with #!/bin/sh and under 16 KiB; a binary must be root/<arch>/<lowercase name>, an ELF for that architecture, under 64 MiB, and must not share a script's name.",
366
+ },
367
+ STANDALONE_ROOT_PROGRAMS_INVALID: {
368
+ code: "STANDALONE_ROOT_PROGRAMS_INVALID",
369
+ description: "The root settings in dsm-package.yaml are invalid, or the executor does not accept them.",
370
+ suggestion: "root accepts only programs (<name>: {kind, schema, run, job} matching root/<name>.sh or root/<arch>/<name>), stop (a program in root/) and state: true, and requires capabilities.rootOps: true.",
371
+ },
372
+ STANDALONE_ROOT_OPS_UNUSED: {
373
+ code: "STANDALONE_ROOT_OPS_UNUSED",
374
+ description: "The project has a root/ directory but capabilities.rootOps is not enabled.",
375
+ suggestion: "Set capabilities.rootOps: true to use root operations, or delete root/.",
376
+ },
377
+ STANDALONE_PACKAGE_INVALID: {
378
+ code: "STANDALONE_PACKAGE_INVALID",
379
+ description: "The package name is invalid.",
380
+ suggestion: "Use a 2-64 character name that starts with a letter and contains only alphanumerics, for example PhotoTagger.",
381
+ },
382
+ STANDALONE_VERSION_INVALID: {
383
+ code: "STANDALONE_VERSION_INVALID",
384
+ description: "version is not a numeric version.",
385
+ suggestion: "Use a version such as 1.2.0 and put the build number in build.",
386
+ },
387
+ STANDALONE_BUILD_INVALID: {
388
+ code: "STANDALONE_BUILD_INVALID",
389
+ description: "build is not an integer from 1 to 9999.",
390
+ suggestion: "Set build to an integer from 1 to 9999 and increment it for each SPK release.",
391
+ },
392
+ STANDALONE_OS_MIN_VER_INVALID: {
393
+ code: "STANDALONE_OS_MIN_VER_INVALID",
394
+ description: "os_min_ver has the wrong format.",
395
+ suggestion: "Use <major>.<minor>-<build>, for example 7.2-64570.",
396
+ },
397
+ STANDALONE_TEXT_INVALID: {
398
+ code: "STANDALONE_TEXT_INVALID",
399
+ description: "A display text field is invalid.",
400
+ suggestion: 'Use single-line, non-empty text without " or \\.',
401
+ },
402
+ STANDALONE_ARCHES_INVALID: {
403
+ code: "STANDALONE_ARCHES_INVALID",
404
+ description: "arches is invalid.",
405
+ suggestion: "List one or more unique values from x86_64, aarch64 or armv7.",
406
+ },
407
+ STANDALONE_ARCH_NOT_DECLARED: {
408
+ code: "STANDALONE_ARCH_NOT_DECLARED",
409
+ description: "The requested build architecture is not in arches.",
410
+ suggestion: "Add the architecture to arches in dsm-package.yaml, or use a declared --arch.",
411
+ },
412
+ STANDALONE_BACKEND_INVALID: {
413
+ code: "STANDALONE_BACKEND_INVALID",
414
+ description: "The backend configuration is invalid.",
415
+ suggestion: "backend must contain exactly one of go (dir and a ./ main) or binary (keyed by architecture).",
416
+ },
417
+ STANDALONE_BINARY_ARCH_MISSING: {
418
+ code: "STANDALONE_BINARY_ARCH_MISSING",
419
+ description: "An architecture declared in arches has no backend.files executable.",
420
+ suggestion: "Add the executable for that architecture to backend.files, or remove it from arches.",
421
+ },
422
+ STANDALONE_BINARY_MISSING: {
423
+ code: "STANDALONE_BINARY_MISSING",
424
+ description: "The backend executable was not found.",
425
+ suggestion: "Confirm the file backend.files points at exists.",
426
+ },
427
+ STANDALONE_BINARY_NOT_REGULAR: {
428
+ code: "STANDALONE_BINARY_NOT_REGULAR",
429
+ description: "The backend executable is not a regular file.",
430
+ suggestion: "Use a real file; no symlinks, directories or special files.",
431
+ },
432
+ STANDALONE_BINARY_TOO_LARGE: {
433
+ code: "STANDALONE_BINARY_TOO_LARGE",
434
+ description: "The backend executable exceeds 256 MiB.",
435
+ suggestion: 'Shrink the executable (for example -ldflags "-s -w").',
436
+ },
437
+ STANDALONE_BINARY_NOT_ELF: {
438
+ code: "STANDALONE_BINARY_NOT_ELF",
439
+ description: "The backend executable is not the little-endian Linux ELF the architecture needs (64-bit for x86_64 and aarch64, 32-bit for armv7).",
440
+ suggestion: "Provide an x86_64, aarch64 or armv7 executable built for Linux.",
441
+ },
442
+ STANDALONE_BINARY_ARCH_MISMATCH: {
443
+ code: "STANDALONE_BINARY_ARCH_MISMATCH",
444
+ description: "The backend executable's CPU architecture does not match the target.",
445
+ suggestion: "Rebuild for that architecture, or fix the backend.files mapping.",
446
+ },
447
+ STANDALONE_BINARY_DYNAMIC: {
448
+ code: "STANDALONE_BINARY_DYNAMIC",
449
+ description: "The backend executable is dynamically linked.",
450
+ suggestion: "Link statically (set CGO_ENABLED=0 for Go); the libc version differs across DSM models.",
451
+ },
452
+ STANDALONE_PATH_INVALID: {
453
+ code: "STANDALONE_PATH_INVALID",
454
+ description: "The path field is not a relative path inside the project.",
455
+ suggestion: "Use a path relative to the project root.",
456
+ },
457
+ STANDALONE_PATH_OUTSIDE_PROJECT: {
458
+ code: "STANDALONE_PATH_OUTSIDE_PROJECT",
459
+ description: "The path points outside the project directory.",
460
+ suggestion: "Keep the files inside the project directory (also after resolving symlinks).",
461
+ },
462
+ STANDALONE_UI_INVALID: {
463
+ code: "STANDALONE_UI_INVALID",
464
+ description: "The ui configuration is invalid.",
465
+ suggestion: "Provide ui.dir.",
466
+ },
467
+ STANDALONE_UI_BUILD_INVALID: {
468
+ code: "STANDALONE_UI_BUILD_INVALID",
469
+ description: "The ui.build configuration is invalid.",
470
+ suggestion: "ui.build.command must be an argv string array (not going through a shell), for example [npm, run, build].",
471
+ },
472
+ STANDALONE_TIMEOUT_UNSUPPORTED: {
473
+ code: "STANDALONE_TIMEOUT_UNSUPPORTED",
474
+ description: "backend.timeout_seconds needs a Go main that reads the requestTimeout the build passes.",
475
+ suggestion: 'Declare var requestTimeout = "" in main.go and pass its seconds to api.WithRequestDeadline(handler, api.Timeout(...)), or remove timeout_seconds.',
476
+ },
477
+ STANDALONE_REQUEST_TIMEOUT_SHORT: {
478
+ code: "STANDALONE_REQUEST_TIMEOUT_SHORT",
479
+ description: "The request deadline ends before a root program that root.run may run would answer: the executor answers 10 seconds after run.timeout_s and its client waits 10 more.",
480
+ suggestion: "Set backend.timeout_seconds to at least the program's run.timeout_s (20 by default) + 20, or lower run.timeout_s.",
481
+ },
482
+ STANDALONE_UI_WINDOW_INVALID: {
483
+ code: "STANDALONE_UI_WINDOW_INVALID",
484
+ description: "ui.window does not give a valid DSM window size.",
485
+ suggestion: "Give width and height as whole numbers from 320 to 3840 and 240 to 2160, keep minWidth and minHeight (700 and 420 by default) no larger than them, and write only the fields you change.",
486
+ },
487
+ STANDALONE_UI_BUILD_FAILED: {
488
+ code: "STANDALONE_UI_BUILD_FAILED",
489
+ description: "The UI build command failed.",
490
+ suggestion: "Install the UI dependencies in ui.build.cwd (npm ci when package-lock.json exists, otherwise npm install), then run the UI build command and fix the errors.",
491
+ },
492
+ STANDALONE_UI_ENTRY_MISSING: {
493
+ code: "STANDALONE_UI_ENTRY_MISSING",
494
+ description: "The UI directory is missing app.js or app.css.",
495
+ suggestion: "Make ui.dir (or the UI build output) contain app.js and app.css.",
496
+ },
497
+ STANDALONE_UI_SYMLINK: {
498
+ code: "STANDALONE_UI_SYMLINK",
499
+ description: "The UI directory contains a symlink.",
500
+ suggestion: "Replace the symlink with a real file.",
501
+ },
502
+ STANDALONE_UI_SPECIAL_FILE: {
503
+ code: "STANDALONE_UI_SPECIAL_FILE",
504
+ description: "The UI directory contains a special file.",
505
+ suggestion: "The UI directory may contain only regular files and directories.",
506
+ },
507
+ STANDALONE_UI_TOO_LARGE: {
508
+ code: "STANDALONE_UI_TOO_LARGE",
509
+ description: "The total UI file size exceeds 64 MiB.",
510
+ suggestion: "Shrink the UI assets.",
511
+ },
512
+ STANDALONE_GO_MISSING: {
513
+ code: "STANDALONE_GO_MISSING",
514
+ description: "The go command was not found.",
515
+ suggestion: "Install Go 1.25 or later (https://go.dev/dl/), or package an existing executable with backend.files.",
516
+ },
517
+ STANDALONE_GO_BUILD_FAILED: {
518
+ code: "STANDALONE_GO_BUILD_FAILED",
519
+ description: "go build failed.",
520
+ suggestion: "Run go build ./... in the backend directory and fix the errors. If github.com/synopackageland/syno-pkg-land-sdk is unreachable, make sure git can read github.com with credentials (verify with git ls-remote).",
521
+ },
522
+ STANDALONE_PORTAL_INVALID: {
523
+ code: "STANDALONE_PORTAL_INVALID",
524
+ description: "The portal format or alias in dsm-package.yaml is invalid.",
525
+ suggestion: "Use portal: {} or portal: { alias: my-app }; alias is 1-64 lowercase alphanumerics or -, and not a DSM reserved path.",
526
+ },
527
+ STANDALONE_PORTAL_REQUIRES_EXECUTOR: {
528
+ code: "STANDALONE_PORTAL_REQUIRES_EXECUTOR",
529
+ description: "An App without an executor (capabilities.rootOps or delegate) declares portal.",
530
+ suggestion: "Removing the sign-in alias after uninstall needs root so nginx stops serving it, but an App without escalation uses no root at any stage. Add capabilities.rootOps or delegate (executor level), or remove portal and let an administrator configure a reverse proxy in DSM.",
531
+ },
532
+ STANDALONE_LOGIN_STYLE_INVALID: {
533
+ code: "STANDALONE_LOGIN_STYLE_INVALID",
534
+ description: "login_style in dsm-package.yaml is malformed, or set without portal.",
535
+ suggestion: "Declare portal, and give login_style only color (#RRGGBB), wallpaper together with wallpaper_thumbnail, and title; omit login_style to use the CLI default style.",
536
+ },
537
+ STANDALONE_LOGIN_STYLE_IMAGE_INVALID: {
538
+ code: "STANDALONE_LOGIN_STYLE_IMAGE_INVALID",
539
+ description: "An image login_style names is missing, in the wrong format or the wrong size.",
540
+ suggestion: "Use a JPEG wallpaper of at least 1920x1080 and at most 2 MiB, a 320x200 JPEG wallpaper_thumbnail and a 680x124 PNG title, inside the project.",
541
+ },
542
+ STANDALONE_I18N_INVALID: {
543
+ code: "STANDALONE_I18N_INVALID",
544
+ description: "The i18n configuration in dsm-package.yaml is invalid.",
545
+ suggestion: "i18n accepts only messages, glossary and info; info is keyed by a supported language such as zh-TW or ja (English goes at the top level).",
546
+ },
547
+ STANDALONE_I18N_MESSAGES_MISSING: {
548
+ code: "STANDALONE_I18N_MESSAGES_MISSING",
549
+ description: "The i18n.messages directory is missing en.json.",
550
+ suggestion: "Add en.json; it is the fallback text and key list for every language.",
551
+ },
552
+ STANDALONE_I18N_LOCALE_UNSUPPORTED: {
553
+ code: "STANDALONE_I18N_LOCALE_UNSUPPORTED",
554
+ description: "A message file's language is not one of the nine supported locales.",
555
+ suggestion: "Use en, zh-TW, zh-CN, ja, ko, de, fr, es, it; every other DSM language shows English.",
556
+ },
557
+ STANDALONE_I18N_CATALOG_INVALID: {
558
+ code: "STANDALONE_I18N_CATALOG_INVALID",
559
+ description: "A message file is not a flat key -> text JSON object.",
560
+ suggestion: 'Use a format like { "greeting.save": "Save" }.',
561
+ },
562
+ STANDALONE_I18N_KEYS_MISMATCH: {
563
+ code: "STANDALONE_I18N_KEYS_MISMATCH",
564
+ description: "One language's message keys differ from en.json.",
565
+ suggestion: "Give every language file exactly the keys of en.json.",
566
+ },
567
+ STANDALONE_I18N_PLACEHOLDER_MISMATCH: {
568
+ code: "STANDALONE_I18N_PLACEHOLDER_MISMATCH",
569
+ description: "A translation has a missing or extra {placeholder}.",
570
+ suggestion: "Keep every {name} placeholder from the en.json text.",
571
+ },
572
+ STANDALONE_I18N_GLOSSARY_INVALID: {
573
+ code: "STANDALONE_I18N_GLOSSARY_INVALID",
574
+ description: "The glossary format is wrong, or it points at a message key that does not exist.",
575
+ suggestion: "Fix the terms (id, text, keys) in glossary.json.",
576
+ },
577
+ STANDALONE_I18N_GLOSSARY_MISMATCH: {
578
+ code: "STANDALONE_I18N_GLOSSARY_MISMATCH",
579
+ description: "A message does not use the DSM interface term from the glossary.",
580
+ suggestion: "Use that language's term from glossary.json to stay consistent with the DSM interface.",
581
+ },
582
+ DEPLOY_VERSION_INVALID: {
583
+ code: "DEPLOY_VERSION_INVALID",
584
+ description: "The package version is invalid or lower than the installed version.",
585
+ suggestion: "Set a higher valid version/build and deploy again.",
586
+ },
587
+ CAPABILITY_INVALID: {
588
+ code: "CAPABILITY_INVALID",
589
+ description: "The capability declaration is invalid.",
590
+ suggestion: "Use files: session, jobs: true or {worker: sha256-v1}, identity: standalone, rootOps: true.",
591
+ },
592
+ CAPABILITY_UNSUPPORTED: {
593
+ code: "CAPABILITY_UNSUPPORTED",
594
+ description: "This runtime does not support the chosen capability.",
595
+ suggestion: "files, jobs and identity need backend.kind: go; compose does not support native capabilities.",
596
+ },
597
+ CAPABILITY_COMBINATION_INVALID: {
598
+ code: "CAPABILITY_COMBINATION_INVALID",
599
+ description: "The capability combination with the runtime or UI is invalid.",
600
+ suggestion: "daemon cannot combine with capabilities.jobs; identity/login needs a vue UI (--ui vue).",
601
+ },
602
+ ADD_FILE_CONFLICT: {
603
+ code: "ADD_FILE_CONFLICT",
604
+ description: "Existing files conflict with the SDK overlay.",
605
+ suggestion: "Move or merge the listed conflicting files before running add; the CLI never overwrites different content.",
606
+ },
607
+ ADD_LAYOUT_UNSUPPORTED: {
608
+ code: "ADD_LAYOUT_UNSUPPORTED",
609
+ description: "The SDK overlay needs the standard backend directory.",
610
+ suggestion: "Use backend.dir: backend, or merge the SDK overlay manually.",
611
+ },
612
+ MIN_DSM_CONTAINER_FLOOR: {
613
+ code: "MIN_DSM_CONTAINER_FLOOR",
614
+ description: "The Compose minimum DSM version is too low.",
615
+ suggestion: "Compose needs DSM 7.2.1-69057 or later.",
616
+ },
617
+ STANDALONE_PORTAL_ENTRY_INVALID: {
618
+ code: "STANDALONE_PORTAL_ENTRY_INVALID",
619
+ description: "When os_min_ver is below DSM 7.2-64570 the executor registers the portal, and the package name or displayname does not match its rules.",
620
+ suggestion: "The package name may contain only alphanumerics separated by . _ -, at most 64 characters; displayname is 1-64 characters without control characters.",
621
+ },
622
+ STANDALONE_PORTAL_MIN_DSM: {
623
+ code: "STANDALONE_PORTAL_MIN_DSM",
624
+ description: "A package that declares portal has os_min_ver below DSM 7.1-42661.",
625
+ suggestion: "Before DSM 7.2-64570 the executor writes the entry into DSM's app.d, and that format is only verified from DSM 7.1. Raise os_min_ver to 7.1-42661 or later, or remove portal.",
626
+ },
627
+ MIN_DSM_INVALID: {
628
+ code: "MIN_DSM_INVALID",
629
+ description: "The minimum DSM version format is invalid.",
630
+ suggestion: "Specify a 7.x release or a full 7.x-build.",
631
+ },
632
+ NOTICE_UNSAFE: {
633
+ code: "NOTICE_UNSAFE",
634
+ description: "A license file does not meet the regular-file limits.",
635
+ suggestion: "Remove symlinks, special files and oversized license files.",
636
+ },
637
+ NOTICE_MISSING: {
638
+ code: "NOTICE_MISSING",
639
+ description: "Third-party license information is missing.",
640
+ suggestion: "Reinstall the template dependencies and generate the third-party notices.",
641
+ },
642
+ PROJECT_NOT_FOUND: {
643
+ code: "PROJECT_NOT_FOUND",
644
+ description: "The project manifest was not found.",
645
+ suggestion: "Run from the project root or use --project.",
646
+ },
647
+ CLI_OPTION_INVALID: {
648
+ code: "CLI_OPTION_INVALID",
649
+ description: "A command option is invalid.",
650
+ suggestion: "Use --help to check the options and the available runtime.",
651
+ },
652
+ CLI_OUTPUT_ARCH_REQUIRED: {
653
+ code: "CLI_OUTPUT_ARCH_REQUIRED",
654
+ description: "Writing a single file needs an architecture.",
655
+ suggestion: "Combine -o with a single --arch, or use --out-dir.",
656
+ },
657
+ HOST_REQUIRED: {
658
+ code: "HOST_REQUIRED",
659
+ description: "No NAS was specified.",
660
+ suggestion: "Use --host <profile>; for a local deploy check use --dry-run.",
661
+ },
662
+ NAS_CREDENTIALS_MISSING: {
663
+ code: "NAS_CREDENTIALS_MISSING",
664
+ description: "NAS credentials were not found.",
665
+ suggestion: "Set a synoagentcli profile or DSM_URL/DSM_PASSWORD.",
666
+ },
667
+ NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE: {
668
+ code: "NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE",
669
+ description: "A scripts/ lifecycle script in the native layout metadata is not executable (inspect's SPK_SCRIPT_NOT_EXECUTABLE).",
670
+ suggestion: "chmod +x the script in the layout metadata directory, then build again.",
671
+ },
672
+ NATIVE_TOOLKIT_TAR_UNSUPPORTED: {
673
+ code: "NATIVE_TOOLKIT_TAR_UNSUPPORTED",
674
+ description: "Extracting native.toolkits needs GNU tar or bsdtar, but the tar on PATH is neither.",
675
+ suggestion: "Point the tar on PATH at GNU tar or bsdtar (macOS ships bsdtar, most Linux distros GNU tar), then build again.",
676
+ },
677
+ SPK_INSPECTION_FAILED: {
678
+ code: "SPK_INSPECTION_FAILED",
679
+ description: "The SPK does not match the DSM 7 package format (dsm.errors), or a synopkgland-built SPK does not match the build contract (findings).",
680
+ suggestion: "Fix the SPK_* rule codes and findings, then repackage; warnings do not affect the result.",
681
+ },
682
+ IMAGE_PIN_REQUIRED: {
683
+ code: "IMAGE_PIN_REQUIRED",
684
+ description: "The image digest is not pinned.",
685
+ suggestion: "Run images pin.",
686
+ },
687
+ INIT_TEMPLATE_REQUIRED: {
688
+ code: "INIT_TEMPLATE_REQUIRED",
689
+ description: "No template was specified.",
690
+ suggestion: "Use -t native-go, native-python, native-node, native-rust, binary or compose.",
691
+ },
692
+ INIT_TEMPLATE_UNKNOWN: {
693
+ code: "INIT_TEMPLATE_UNKNOWN",
694
+ description: "Unknown template.",
695
+ suggestion: "Use -t native-go, native-python, native-node, native-rust, binary or compose.",
696
+ },
697
+ CLI_ARCH_INVALID: {
698
+ code: "CLI_ARCH_INVALID",
699
+ description: "The architecture option is invalid.",
700
+ suggestion: "Use x86_64, aarch64 or armv7 (armv7 is for native-go and binary, not for Container Manager Apps).",
701
+ },
702
+ CLI_JOBS_INVALID: {
703
+ code: "CLI_JOBS_INVALID",
704
+ description: "--jobs is not a positive integer.",
705
+ suggestion: "Use --jobs 1 (the default) or a larger integer.",
706
+ },
707
+ BUILD_NOT_REPRODUCIBLE: {
708
+ code: "BUILD_NOT_REPRODUCIBLE",
709
+ description: "A second SPK built by build --verify-reproducible differs from the first.",
710
+ suggestion: "Look at the listed payload and metadata files and remove anything that changes per build (timestamps, random or absolute paths, unpinned dependencies).",
711
+ },
712
+ REPRODUCIBLE_INVALID: {
713
+ code: "REPRODUCIBLE_INVALID",
714
+ description: "The reproducible configuration is malformed, or a listed input is not a file inside the project.",
715
+ suggestion: "Use reproducible: {inputs: [files...], clean: [paths...]} with project-relative paths; {arch} is only allowed in clean.",
716
+ },
717
+ DEVELOPMENT_INSTANCE_UNSUPPORTED: {
718
+ code: "DEVELOPMENT_INSTANCE_UNSUPPORTED",
719
+ description: "A native project does not support --dev.",
720
+ suggestion: "Use a different package ID when running init.",
721
+ },
722
+ INIT_OPTIONS_UNSUPPORTED: {
723
+ code: "INIT_OPTIONS_UNSUPPORTED",
724
+ description: "The option does not apply to this template.",
725
+ suggestion: "--from is compose only; --ui is native templates only (native-go, native-python, native-node, native-rust).",
726
+ },
727
+ INIT_TARGET_NOT_EMPTY: {
728
+ code: "INIT_TARGET_NOT_EMPTY",
729
+ description: "The init directory already has conflicting files.",
730
+ suggestion: "Use an empty directory; add capabilities to an existing project with add.",
731
+ },
732
+ INIT_COMPOSE_INVALID: {
733
+ code: "INIT_COMPOSE_INVALID",
734
+ description: "The imported Compose is invalid.",
735
+ suggestion: "Point --from at a YAML file with services.",
736
+ },
737
+ INIT_BINARY_BUILD_FAILED: {
738
+ code: "INIT_BINARY_BUILD_FAILED",
739
+ description: "The binary example failed to compile.",
740
+ suggestion: "Install Go or provide your own static CGI ELF.",
741
+ },
742
+ INIT_SHARED_FOLDER_INVALID: {
743
+ code: "INIT_SHARED_FOLDER_INVALID",
744
+ description: "The shared folder option is invalid.",
745
+ suggestion: "Use select or create; add --shared-folder-name with create.",
746
+ },
747
+ INIT_UI_INVALID: {
748
+ code: "INIT_UI_INVALID",
749
+ description: "The UI option is invalid.",
750
+ suggestion: "Use --ui vue or plain.",
751
+ },
752
+ INIT_PACKAGE_INVALID: {
753
+ code: "INIT_PACKAGE_INVALID",
754
+ description: "The package name is invalid.",
755
+ suggestion: "Use a 2-64 character alphanumeric name that starts with a letter.",
756
+ },
757
+ INIT_DISPLAY_NAME_INVALID: {
758
+ code: "INIT_DISPLAY_NAME_INVALID",
759
+ description: "The display name is invalid.",
760
+ suggestion: "Use single-line text without quotes or backslashes.",
761
+ },
762
+ STANDALONE_API_PERMISSIONS_CHANGED: {
763
+ code: "STANDALONE_API_PERMISSIONS_CHANGED",
764
+ description: "The API policy changed during the build.",
765
+ suggestion: "Freeze permissions.json, then run check/build again.",
766
+ },
767
+ STANDALONE_API_PERMISSIONS_INVALID: {
768
+ code: "STANDALONE_API_PERMISSIONS_INVALID",
769
+ description: "The API policy is invalid.",
770
+ suggestion: "Fix backend/internal/api/permissions.json.",
771
+ },
772
+ CLI_OPTION_UNKNOWN: {
773
+ code: "CLI_OPTION_UNKNOWN",
774
+ description: "Unknown CLI option.",
775
+ suggestion: "Use --help to see the currently supported options.",
776
+ },
777
+ CLI_ARGUMENT_MISSING: {
778
+ code: "CLI_ARGUMENT_MISSING",
779
+ description: "A command argument is missing.",
780
+ suggestion: "Use --help to see the required arguments.",
781
+ },
782
+ CLI_OPTION_MISSING: {
783
+ code: "CLI_OPTION_MISSING",
784
+ description: "A required option is missing.",
785
+ suggestion: "Use --help to see the required options.",
786
+ },
787
+ CLI_OPTION_ARGUMENT_MISSING: {
788
+ code: "CLI_OPTION_ARGUMENT_MISSING",
789
+ description: "An option is missing its value.",
790
+ suggestion: "Pass the value the option needs.",
791
+ },
792
+ CLI_COMMAND_UNKNOWN: {
793
+ code: "CLI_COMMAND_UNKNOWN",
794
+ description: "Unknown command.",
795
+ suggestion: "Use synopkgland --help to see the commands.",
796
+ },
797
+ COMMAND_FAILED: {
798
+ code: "COMMAND_FAILED",
799
+ description: "The command failed.",
800
+ suggestion: "Check the message and stderr, fix the problem and retry.",
801
+ },
802
+ DEPLOY_PLAN_INVALID: {
803
+ code: "DEPLOY_PLAN_INVALID",
804
+ description: "The deploy plan does not match the runtime.",
805
+ suggestion: "Run check again and regenerate the deploy plan.",
806
+ },
807
+ DEPLOY_ARCH_UNDETERMINED: {
808
+ code: "DEPLOY_ARCH_UNDETERMINED",
809
+ description: "The NAS architecture could not be determined.",
810
+ suggestion: "Use --arch x86_64, aarch64 or armv7.",
811
+ },
812
+ CONTAINER_ARCH_UNSUPPORTED: {
813
+ code: "CONTAINER_ARCH_UNSUPPORTED",
814
+ description: "Container Manager Apps support only x86_64 and aarch64; DSM does not provide Container Manager for armv7.",
815
+ suggestion: "Use --arch x86_64 or aarch64, or switch to the native-go template.",
816
+ },
817
+ DEPLOY_BUILD_OUTPUT_INVALID: {
818
+ code: "DEPLOY_BUILD_OUTPUT_INVALID",
819
+ description: "The number of deploy build outputs does not match.",
820
+ suggestion: "Specify a single architecture and build again.",
821
+ },
822
+ EXECUTOR_BUILD_FAILED: {
823
+ code: "EXECUTOR_BUILD_FAILED",
824
+ description: "Compiling the executor or lifecycle helper from the App's SDK version failed.",
825
+ suggestion: "Confirm Go 1.21 or later is installed and go1.26.9 is available (prepare it offline), and check the compiler output in the message.",
826
+ },
827
+ EXECUTOR_DIGEST_MISMATCH: {
828
+ code: "EXECUTOR_DIGEST_MISMATCH",
829
+ description: "The executor built from the SDK version does not match the sha256 table bundled in the CLI, or the table has no entry for this SDK version.",
830
+ suggestion: "Confirm github.com/synopackageland/syno-pkg-land-sdk in backend/go.mod is a release version and Go is go1.26.9; upgrade the CLI when the table has no entry for that version.",
831
+ },
832
+ EXECUTOR_DIGEST_UNVERIFIED: {
833
+ code: "EXECUTOR_DIGEST_UNVERIFIED",
834
+ description: "SYNOPKGLAND_SDK_DIR points at a local SDK, so the executor was not checked against the sha256 table (warning).",
835
+ suggestion: "Development only; for a publishable SPK unset SYNOPKGLAND_SDK_DIR and build again.",
836
+ },
837
+ SDK_REQUIRED: {
838
+ code: "SDK_REQUIRED",
839
+ description: "Python, Node and Rust Apps, and rootOps or delegate, need the SDK, but the backend does not name an SDK version (Go: backend/go.mod; Python and Node: backend.sdk; Rust: syno-pkg-land-app in Cargo.toml).",
840
+ suggestion: "In Go add require github.com/synopackageland/syno-pkg-land-sdk to backend/go.mod; in Python and Node write backend.sdk in dsm-package.yaml; in Rust depend on syno-pkg-land-app by git+tag in the App's Cargo.toml; keep the version on the same minor as the CLI.",
841
+ },
842
+ SDK_TAG_REQUIRED: {
843
+ code: "SDK_TAG_REQUIRED",
844
+ description: "The Rust App's Cargo.lock does not pin exactly one syno-pkg-land-app to a release tag (branch, rev, path, registry and multiple versions are all rejected).",
845
+ suggestion: 'Write syno-pkg-land-app = { git = "https://github.com/synopackageland/syno-pkg-land-sdk", tag = "vX.Y.Z" } in Cargo.toml, run cargo update -p syno-pkg-land-app and commit Cargo.lock.',
846
+ },
847
+ SDK_VERSION_MISMATCH: {
848
+ code: "SDK_VERSION_MISMATCH",
849
+ description: "The @synopackageland/sdk in frontend/package.json differs from the SDK version the backend requires (backend/go.mod or backend/Cargo.lock).",
850
+ suggestion: "Make both sides the same version; for a file: dependency the comparison uses that directory's package.json version.",
851
+ },
852
+ SDK_UNSUPPORTED: {
853
+ code: "SDK_UNSUPPORTED",
854
+ description: "The SDK the backend requires (backend/go.mod or backend/Cargo.lock) is not on this CLI's minor line.",
855
+ suggestion: "Use a CLI on the same minor as the SDK, or raise the SDK to a version line the CLI supports (listed in the message).",
856
+ },
857
+ SDK_DIR_NOT_READY: {
858
+ code: "SDK_DIR_NOT_READY",
859
+ description: "The SDK checkout SYNOPKGLAND_SDK_DIR points at has no go.mod, or the ts package a Vue project needs is not built (no ts/dist).",
860
+ suggestion: "Point at a syno-pkg-land-sdk checkout; for a Vue project first run npm ci && npm run build in $SYNOPKGLAND_SDK_DIR/ts (npm does not build file: packages for you).",
861
+ },
862
+ SDK_DOWNLOAD_FAILED: {
863
+ code: "SDK_DOWNLOAD_FAILED",
864
+ description: "go mod download cannot fetch the SDK version backend/go.mod requires.",
865
+ suggestion: "The CLI fetches the private SDK with git: make sure git can read github.com with credentials (verify with git ls-remote), then confirm the version exists. Offline, run go mod download once on the same machine first.",
866
+ },
867
+ TEMPLATES_MISSING: {
868
+ code: "TEMPLATES_MISSING",
869
+ description: "@synopackageland/templates was not found.",
870
+ suggestion: "Reinstall @synopackageland/cli, or point SYNOPKGLAND_TEMPLATES_DIR at a templates checkout.",
871
+ },
872
+ EXECUTOR_PIN_REQUIRED: {
873
+ code: "EXECUTOR_PIN_REQUIRED",
874
+ description: "A package that declares rootOps or delegate was built without the executor artifact.",
875
+ suggestion: "Use synopkgland build so the CLI compiles and pins the executor; do not call the low-level SPK API directly.",
876
+ },
877
+ EXECUTOR_POLICY: {
878
+ code: "EXECUTOR_POLICY",
879
+ description: "capabilities.delegate has invalid operations or scope, or a binary root program lacks the file for this architecture, so the executor policy cannot be generated.",
880
+ suggestion: "Use the delegate declaration init/add produces and fix scope and operations against the manifest reference; for binary root programs provide root/<arch>/<name> for every arch in arches.",
881
+ },
882
+ EXECUTOR_POLICY_REJECTED: {
883
+ code: "EXECUTOR_POLICY_REJECTED",
884
+ description: "The executor of the App's SDK version refuses the policy the CLI generated, so the NAS would refuse it at approval.",
885
+ suggestion: "The reason names the refused part: policy root/start/stop/purge → root.programs (schema, run, job), root.start, root.stop, uninstall.purge; policy scope/operation/destination/move/lifetime → capabilities.delegate; policy portal → portal; policy entry → entry. Fix that part of dsm-package.yaml; if it already matches the manifest reference, use the SDK version this CLI release names.",
886
+ },
887
+ EXECUTOR_ROOT_PROGRAMS_UNSUPPORTED: {
888
+ code: "EXECUTOR_ROOT_PROGRAMS_UNSUPPORTED",
889
+ description: "The root settings in dsm-package.yaml need an executor that supports root program settings, which the App's SDK version lacks.",
890
+ suggestion: "Use an SDK with root programs, stop and root_state (executor/stop_step.go), or remove the root settings.",
891
+ },
892
+ EXECUTOR_PACKAGE: {
893
+ code: "EXECUTOR_PACKAGE",
894
+ description: "The package name pinned into the root task is invalid.",
895
+ suggestion: "package must start with a letter and contain only alphanumerics (2-64 characters).",
896
+ },
897
+ EXECUTOR_DIGEST: {
898
+ code: "EXECUTOR_DIGEST",
899
+ description: "The sha256 pin for the executor or policy is malformed.",
900
+ suggestion: "Build again so the CLI recomputes the pin; do not hand-edit pins inside the SPK.",
901
+ },
902
+ EXECUTOR_TASK_PERCENT: {
903
+ code: "EXECUTOR_TASK_PERCENT",
904
+ description: "The root task script contains %, which the DSM task scheduler cannot store.",
905
+ suggestion: "Remove % from the root/ scripts (for example rewrite the date format).",
906
+ },
907
+ EXECUTOR_TASK_LENGTH: {
908
+ code: "EXECUTOR_TASK_LENGTH",
909
+ description: "The root task script exceeds the DSM task scheduler's length limit.",
910
+ suggestion: "Shorten the root/ scripts; move the logic into a file inside the package and call it from the script.",
911
+ },
912
+ STANDALONE_ENTRY_INVALID: {
913
+ code: "STANDALONE_ENTRY_INVALID",
914
+ description: "The entry configuration is invalid.",
915
+ suggestion: "Set entry to admin-only or app-privilege; app-privilege needs at least one authenticated route.",
916
+ },
917
+ STANDALONE_ENTRY_WIDENED: {
918
+ code: "STANDALONE_ENTRY_WIDENED",
919
+ description: "entry: admin-only conflicts with routes that widen access.",
920
+ suggestion: "Change the authenticated or public routes in permissions.json to administrators; to open it to regular users use entry: app-privilege or omit entry.",
921
+ },
922
+ STANDALONE_ENTRY_UNSUPPORTED: {
923
+ code: "STANDALONE_ENTRY_UNSUPPORTED",
924
+ description: "The backend cannot enforce the entry mode.",
925
+ suggestion: "Use a Go backend: require github.com/synopackageland/syno-pkg-land-sdk in backend/go.mod and wrap the authenticator with dsm.NewEntryGate in the main.go of backend.main (init defaults to cmd/app/main.go).",
926
+ },
927
+ DSM_TLS_UNTRUSTED: {
928
+ code: "DSM_TLS_UNTRUSTED",
929
+ description: "The DSM TLS certificate is not trusted by this computer (self-signed, expired or a name mismatch).",
930
+ suggestion: "Export the NAS certificate or its CA as PEM and run with NODE_EXTRA_CA_CERTS=<file>; or give DSM a trusted certificate whose name matches the host profile. Do not disable certificate verification.",
931
+ },
932
+ STANDALONE_ICON_INVALID: {
933
+ code: "STANDALONE_ICON_INVALID",
934
+ description: "The icon in dsm-package.yaml is not a usable package icon.",
935
+ suggestion: "Set icon to a square PNG inside the project (at least 256x256), or a directory of icon_<size>.png including icon_256.png; omit it to use the SDK default icon.",
936
+ },
937
+ CONTRACT_ICON_INVALID: {
938
+ code: "CONTRACT_ICON_INVALID",
939
+ description: "The icon in synology-app.yaml is not a usable package icon.",
940
+ suggestion: "Set icon to a square PNG inside the project (at least 256x256), or a directory of icon_<size>.png including icon_256.png; omit it to use the SDK default icon.",
941
+ },
942
+ CONTRACT_I18N_INVALID: {
943
+ code: "CONTRACT_I18N_INVALID",
944
+ description: "The i18n configuration in synology-app.yaml is invalid.",
945
+ suggestion: 'i18n accepts only info; info is keyed by a supported language such as zh-TW or ja with single-line displayname / description values without " or \\ (English goes at the top level).',
946
+ },
947
+ STANDALONE_BRIDGE_APP_IN_UI: {
948
+ code: "STANDALONE_BRIDGE_APP_IN_UI",
949
+ description: "The backend.dir and ui.dir of a Python, Node or Rust App overlap; DSM serves the files under ui/ directly.",
950
+ suggestion: "Put the App in a directory outside ui.dir (for example app/); neither may contain the other.",
951
+ },
952
+ STANDALONE_BRIDGE_METHODS_MISMATCH: {
953
+ code: "STANDALONE_BRIDGE_METHODS_MISMATCH",
954
+ description: "The App's methods do not match permissions.json, or use an SDK method name; the runtime refuses to start.",
955
+ suggestion: "permissions.json lists only the App's own methods; an App method must not share a name with an SDK method (nas.*, identity.*, portal.status and others).",
956
+ },
957
+ STANDALONE_RUNTIME_BUILD_FAILED: {
958
+ code: "STANDALONE_RUNTIME_BUILD_FAILED",
959
+ description: "Compiling the bridge runtime (bridge/cmd/runtime) from the App's SDK version failed, or the result was not built with go1.26.9.",
960
+ suggestion: "Confirm Go 1.21 or later is installed and go1.26.9 is available; that SDK version must contain the bridge runtime; check the compiler output in the message.",
961
+ },
962
+ STANDALONE_BRIDGE_DESCRIBE_FAILED: {
963
+ code: "STANDALONE_BRIDGE_DESCRIBE_FAILED",
964
+ description: "There is no backend.methods, so build starts the App to read hello, but the App did not answer a v1 hello.",
965
+ suggestion: "Install python3 or node on the build machine and make sure the App starts; or list backend.methods in dsm-package.yaml.",
966
+ },
967
+ STANDALONE_NODE_DEPENDENCY_MISSING: {
968
+ code: "STANDALONE_NODE_DEPENDENCY_MISSING",
969
+ description: "The Node App's package.json dependencies are not installed in the App's node_modules.",
970
+ suggestion: "Run npm ci --omit=dev in the App directory and build again; the CLI places @synopackageland/app, so it does not need installing.",
971
+ },
972
+ STANDALONE_BRIDGE_APP_INVALID: {
973
+ code: "STANDALONE_BRIDGE_APP_INVALID",
974
+ description: "The App directory contains a symlink or special file, or exceeds 256 MiB.",
975
+ suggestion: "Keep only regular files and directories in the App directory; move large data out of it.",
976
+ },
977
+ STANDALONE_RUST_BUILD_FAILED: {
978
+ code: "STANDALONE_RUST_BUILD_FAILED",
979
+ description: "The Rust App's cargo zigbuild cross-compile failed, or it produced no binary for backend.entry.",
980
+ suggestion: "Install Rust with rustup and the three targets (x86_64/aarch64-unknown-linux-musl, armv7-unknown-linux-gnueabihf), install cargo-zigbuild and zig; set backend.entry to the Cargo binary name; check the compiler output in the message.",
981
+ },
982
+ STANDALONE_BRIDGE_MANIFEST_INVALID: {
983
+ code: "STANDALONE_BRIDGE_MANIFEST_INVALID",
984
+ description: "The bridge.json the CLI produced does not match spec/bridge/v1 manifest.schema.json.",
985
+ suggestion: "Check backend.entry, methods and node.package; if it still fails, report the CLI bug.",
986
+ },
987
+ STANDALONE_PYTHON_SDK_MISSING: {
988
+ code: "STANDALONE_PYTHON_SDK_MISSING",
989
+ description: "The App's SDK version has no Python SDK (python/synopkgland_app).",
990
+ suggestion: "Point SYNOPKGLAND_SDK_DIR at a syno-pkg-land-sdk checkout that has python/synopkgland_app, or require an SDK release that ships it.",
991
+ },
992
+ STANDALONE_NODE_SDK_NOT_BUILT: {
993
+ code: "STANDALONE_NODE_SDK_NOT_BUILT",
994
+ description: "The App's SDK version has no built Node SDK (node/dist).",
995
+ suggestion: "Run npm ci && npm run build in node/ of the SYNOPKGLAND_SDK_DIR checkout first; a release SDK must contain node/dist.",
996
+ },
997
+ COMPOSE_POSIX_ALL_SHARES_UNDECLARED: {
998
+ code: "COMPOSE_POSIX_ALL_SHARES_UNDECLARED",
999
+ description: "Compose uses an undeclared POSIX all-shares marker.",
1000
+ suggestion: "Declare type: posix_all_shares so the package-only wizard/CLI takes the Share snapshot at install time.",
1001
+ },
1002
+ COMPOSE_POSIX_ALL_SHARES_SUFFIX_FORBIDDEN: {
1003
+ code: "COMPOSE_POSIX_ALL_SHARES_SUFFIX_FORBIDDEN",
1004
+ description: "A POSIX all-shares marker must not carry a Share subpath.",
1005
+ suggestion: "Use the complete ${ALL_SHARES} source only; the package-only preinst expands each Share's container target.",
1006
+ },
1007
+ COMPOSE_ENV_MISSING: {
1008
+ code: "COMPOSE_ENV_MISSING",
1009
+ description: "The project is missing the required environment variable directory structure.",
1010
+ suggestion: "Make sure SYNOPKG_PKGVAR and the secrets directory are created correctly.",
1011
+ },
1012
+ COMPOSE_NOT_HEALTHY: {
1013
+ code: "COMPOSE_NOT_HEALTHY",
1014
+ description: "The Compose service did not reach healthy within the time limit.",
1015
+ suggestion: "Check the container healthcheck and application logs: synopkgland logs --host <nas>.",
1016
+ },
1017
+ COMPOSE_NOT_RUNNING: {
1018
+ code: "COMPOSE_NOT_RUNNING",
1019
+ description: "The Compose service did not stay running within the time limit.",
1020
+ suggestion: "Check the container start script and logs for the crash cause: synopkgland logs --host <nas>.",
1021
+ },
9
1022
  },
10
- "COMPOSE_ROUTE_UNSUPPORTED": {
11
- "code": "COMPOSE_ROUTE_UNSUPPORTED",
12
- "description": "Compose has no supported DSM resource worker execution path.",
13
- "suggestion": "Use a prebuilt image and supported install parameters; remove unsupported privileges, secrets or build settings."
14
- },
15
- "LOCAL_SPK_INSTALL_FAILED": {
16
- "code": "LOCAL_SPK_INSTALL_FAILED",
17
- "description": "The local SPK install in DSM failed.",
18
- "suggestion": "Check the DSM Package Center install error and confirm the package version and requirements."
19
- },
20
- "CONTRACT_SCHEMA_UNSUPPORTED": {
21
- "code": "CONTRACT_SCHEMA_UNSUPPORTED",
22
- "description": "The schema version in synology-app.yaml is not supported.",
23
- "suggestion": "Set the schema field in synology-app.yaml to 1."
24
- },
25
- "CONTRACT_MISSING_FIELD": {
26
- "code": "CONTRACT_MISSING_FIELD",
27
- "description": "synology-app.yaml is missing a required field.",
28
- "suggestion": "Add the missing required fields to synology-app.yaml (for example package, version, displayname)."
29
- },
30
- "CONTRACT_SCHEMA_INVALID": {
31
- "code": "CONTRACT_SCHEMA_INVALID",
32
- "description": "synology-app.yaml does not match the JSON Schema.",
33
- "suggestion": "Fix the field formats in synology-app.yaml so they match the schema."
34
- },
35
- "PACKAGE_IDENTITY_TOO_LONG": {
36
- "code": "PACKAGE_IDENTITY_TOO_LONG",
37
- "description": "The Package Identity exceeds the length limit (28 characters for a formal identity, 32 for the final identity).",
38
- "suggestion": "Shorten the package name in synology-app.yaml (at most 28 characters for the formal identity, leaving 4 characters for the .dev suffix)."
39
- },
40
- "CONTRACT_PACKAGE_TOO_LONG": {
41
- "code": "CONTRACT_PACKAGE_TOO_LONG",
42
- "description": "The Package Identity exceeds the DSM limit of 32 characters.",
43
- "suggestion": "Shorten the package name in synology-app.yaml to 32 characters or fewer."
44
- },
45
- "CONTRACT_ADMINPORT_INVALID": {
46
- "code": "CONTRACT_ADMINPORT_INVALID",
47
- "description": "adminport must be an integer.",
48
- "suggestion": "Provide an integer adminport in synology-app.yaml that matches the Compose published port."
49
- },
50
- "CONTRACT_ADMINPORT_UNMAPPED": {
51
- "code": "CONTRACT_ADMINPORT_UNMAPPED",
52
- "description": "adminport does not map to a container port in the Compose ports declaration.",
53
- "suggestion": "Declare a port mapping for adminport in the compose.yaml ports block (for example \"8080:80\")."
54
- },
55
- "CONTRACT_LAUNCH_INVALID": {
56
- "code": "CONTRACT_LAUNCH_INVALID",
57
- "description": "The integration.web.launch mode is invalid.",
58
- "suggestion": "Use dsm-window (DSM window) or new-page (new tab, the default); omit this field for a headless App."
59
- },
60
- "CONTRACT_LAUNCH_REQUIRES_WEB": {
61
- "code": "CONTRACT_LAUNCH_REQUIRES_WEB",
62
- "description": "A headless App does not declare a Web Launch Mode.",
63
- "suggestion": "Remove integration.web.launch, or add the adminport that matches the Compose published port."
64
- },
65
- "CONTRACT_PORTAL_REQUIRES_EXECUTOR": {
66
- "code": "CONTRACT_PORTAL_REQUIRES_EXECUTOR",
67
- "description": "The Compose App declares integration.web.portal, but a Compose App has no executor; removing the sign-in alias after uninstall needs root so nginx stops serving it.",
68
- "suggestion": "Remove integration.web.portal; for a sign-in alias, ask an administrator to configure a reverse proxy in DSM instead."
69
- },
70
- "CONTRACT_ACCESS_INVALID": {
71
- "code": "CONTRACT_ACCESS_INVALID",
72
- "description": "The integration.web.access mode is invalid.",
73
- "suggestion": "Use dsm-users (signed-in DSM users, the default), dsm-admins, dsm-groups (with groups), or public to keep an anonymous Web Station entry."
74
- },
75
- "CONTRACT_ACCESS_GROUPS_INVALID": {
76
- "code": "CONTRACT_ACCESS_GROUPS_INVALID",
77
- "description": "integration.web.groups is missing, malformed, or not combined with access: dsm-groups.",
78
- "suggestion": "Use access: dsm-groups and list DSM group names, or remove groups."
79
- },
80
- "CONTRACT_ACCESS_REQUIRES_WEB": {
81
- "code": "CONTRACT_ACCESS_REQUIRES_WEB",
82
- "description": "A headless App does not declare Web access rules.",
83
- "suggestion": "Remove integration.web.access/groups, or add the adminport that matches the Compose published port."
84
- },
85
- "COMPOSE_GATEWAY_PORT_EXPOSED": {
86
- "code": "COMPOSE_GATEWAY_PORT_EXPOSED",
87
- "description": "The DSM verification gateway is the container's only entry point, but the Compose port behind adminport binds to every network interface.",
88
- "suggestion": "Bind ports to loopback only (for example \"127.0.0.1:18333:18333\"), or choose public explicitly in integration.web.access."
89
- },
90
- "GATEWAY_ARCH_FAMILY_REQUIRED": {
91
- "code": "GATEWAY_ARCH_FAMILY_REQUIRED",
92
- "description": "The DSM verification gateway is a native binary, so the SPK must name its target architecture.",
93
- "suggestion": "Add --arch x86_64 or --arch aarch64 (deploy detects it from the NAS); only Apps without DSM verification may choose public in integration.web.access."
94
- },
95
- "GATEWAY_GO_MISSING": {
96
- "code": "GATEWAY_GO_MISSING",
97
- "description": "Building the DSM verification gateway needs Go, but the go command was not found.",
98
- "suggestion": "Install Go 1.21 or later (https://go.dev/dl/, builds pin go1.26.9), or choose public explicitly in integration.web.access."
99
- },
100
- "GATEWAY_BUILD_FAILED": {
101
- "code": "GATEWAY_BUILD_FAILED",
102
- "description": "The DSM verification gateway failed to build, or the output is not a static ELF for the target architecture.",
103
- "suggestion": "Check the go build output; the gateway always builds with go1.26.9, so install go1.26.9 offline or let GOPROXY serve golang.org/toolchain, and confirm assets/go/gateway in the CLI package is unmodified."
104
- },
105
- "CONTRACT_INVALID_STRING": {
106
- "code": "CONTRACT_INVALID_STRING",
107
- "description": "A contract string contains a control character or the ${ interpolation syntax, which is not allowed.",
108
- "suggestion": "Remove control characters and ${ interpolation; declare install parameters in the install block."
109
- },
110
- "CONTRACT_DSM_USER_DUPLICATE": {
111
- "code": "CONTRACT_DSM_USER_DUPLICATE",
112
- "description": "Each App may declare at most one dsm_user install parameter.",
113
- "suggestion": "Keep only one Container Run-as parameter in synology-app.yaml."
114
- },
115
- "INSTALL_STRING_TOO_LONG": {
116
- "code": "INSTALL_STRING_TOO_LONG",
117
- "description": "An Install Parameter string default exceeds 256 characters.",
118
- "suggestion": "Shorten the default string of the install parameter in synology-app.yaml."
119
- },
120
- "INSTALL_STRING_INVALID": {
121
- "code": "INSTALL_STRING_INVALID",
122
- "description": "An Install Parameter string value contains an invalid character or interpolation.",
123
- "suggestion": "Remove control characters and ${ interpolation from the install parameter default."
124
- },
125
- "CONTRACT_SECRET_UNSUPPORTED": {
126
- "code": "CONTRACT_SECRET_UNSUPPORTED",
127
- "description": "install.<NAME>.type: secret has no usable install path today: whether or not Compose references it, Package Center install ends in DSM broken/150 and creates no Container Manager project.",
128
- "suggestion": "Use type: string (non-sensitive), or have the App fetch the secret itself at startup or read a file an administrator places in ${SYNO_APP_SECRETS_DIR}. The secret type stays closed until the DSM worker failure reason is observable."
129
- },
130
- "CONTRACT_HOMES_UNSUPPORTED": {
131
- "code": "CONTRACT_HOMES_UNSUPPORTED",
132
- "description": "install.<NAME>.type: homes has no usable install path today: the install wizard generates no input control and Package Center install ends in DSM broken/150; CLI deploy can resolve /volume1/homes, but DSM rejects the compound install with 4540.",
133
- "suggestion": "If the App needs an administrator-chosen folder, use type: shared_folder instead. The homes type stays closed until the wizard control and preinst path resolution exist."
134
- },
135
- "INSTALL_PARAMETER_UNREFERENCED": {
136
- "code": "INSTALL_PARAMETER_UNREFERENCED",
137
- "description": "An install parameter is declared but not referenced in Compose.",
138
- "suggestion": "Reference the parameter in compose.yaml as ${NAME}, or remove the unused declaration from synology-app.yaml."
139
- },
140
- "CONTRACT_HOMES_PATH_FORBIDDEN": {
141
- "code": "CONTRACT_HOMES_PATH_FORBIDDEN",
142
- "description": "A homes Install Parameter must not write a NAS physical path or default value into the contract.",
143
- "suggestion": "Declare only type: homes, reference the parameter in Compose, and let the package resolve this NAS's User Home path at install time."
144
- },
145
- "CONTRACT_POSIX_ALL_SHARES_VALUE_FORBIDDEN": {
146
- "code": "CONTRACT_POSIX_ALL_SHARES_VALUE_FORBIDDEN",
147
- "description": "A POSIX all-shares parameter must not specify a default or NAS physical path in the contract.",
148
- "suggestion": "Declare only type: posix_all_shares and let the wizard/CLI take the current Share snapshot at install time."
149
- },
150
- "CONTRACT_POSIX_ALL_SHARES_LISTING_UNSUPPORTED": {
151
- "code": "CONTRACT_POSIX_ALL_SHARES_LISTING_UNSUPPORTED",
152
- "description": "posix_all_shares requires the CLI to take a Share list snapshot of this NAS with an administrator session at install time; Package Center and Package Source listing cannot fulfill this contract.",
153
- "suggestion": "Use synopkgland deploy --host <name> so an administrator session takes the Share list snapshot at install time; this type cannot ship through Package Center or Package Source listing."
154
- },
155
- "DSM_USER_REQUIRED": {
156
- "code": "DSM_USER_REQUIRED",
157
- "description": "A Shared Folder requires exactly one dsm_user as the Container Run-as.",
158
- "suggestion": "Add install.<NAME>.type: dsm_user to synology-app.yaml and reference ${NAME_UID}:${NAME_GID} in Compose."
159
- },
160
- "PROJECT_RUNTIME_EXCLUSIVE": {
161
- "code": "PROJECT_RUNTIME_EXCLUSIVE",
162
- "description": "The project has both compose.yaml and dsm-package.yaml, which are mutually exclusive.",
163
- "suggestion": "Choose compose.yaml (container App) or dsm-package.yaml (native App) and delete the other file."
164
- },
165
- "COMPOSE_MISSING": {
166
- "code": "COMPOSE_MISSING",
167
- "description": "compose.yaml or dsm-package.yaml was not found.",
168
- "suggestion": "Create compose.yaml or dsm-package.yaml in the project root."
169
- },
170
- "COMPOSE_SERVICE_NOT_FOUND": {
171
- "code": "COMPOSE_SERVICE_NOT_FOUND",
172
- "description": "A service referenced by depends_on does not exist in compose.yaml.",
173
- "suggestion": "Fix depends_on in compose.yaml to point at a defined service name."
174
- },
175
- "COMPOSE_NUMERIC_COMMAND_ARG_FORBIDDEN": {
176
- "code": "COMPOSE_NUMERIC_COMMAND_ARG_FORBIDDEN",
177
- "description": "A Compose command/entrypoint array that goes through the DSM Docker Project resource worker contains a pure numeric string, which a YAML round-trip may turn into an integer and fail to acquire.",
178
- "suggestion": "Wrap the whole command in one string, for example command: [\"sh\", \"-c\", \"httpd -f -p 8080 -h /srv\"], or join it with the flag as \"-p8080\"."
179
- },
180
- "COMPOSE_DYNAMIC_ROUTE_UNSUPPORTED": {
181
- "code": "COMPOSE_DYNAMIC_ROUTE_UNSUPPORTED",
182
- "description": "A Compose dynamic route is outside the D1/D2 allowlist, or uses a secret/config source the DSM worker does not load.",
183
- "suggestion": "Use only ${NAME} interpolation of string/boolean wizard answers; secret/config are rejected because the DSM worker does not read docker-compose.override.yml."
184
- },
185
- "COMPOSE_BUILD_FORBIDDEN": {
186
- "code": "COMPOSE_BUILD_FORBIDDEN",
187
- "description": "The D0 Compose route forbids build: at NAS install time; the build capability is permanently rejected.",
188
- "suggestion": "Remove build: and use an existing image (preferably pinned with image@sha256:...), then run check again."
189
- },
190
- "COMPOSE_SOCKET_MOUNT_FORBIDDEN": {
191
- "code": "COMPOSE_SOCKET_MOUNT_FORBIDDEN",
192
- "description": "Mounting the Docker socket or sensitive system sockets is forbidden.",
193
- "suggestion": "Remove the docker.sock mount from compose.yaml; for file access use a shared_folder or the App's own file API."
194
- },
195
- "COMPOSE_BIND_SOURCE_FORBIDDEN": {
196
- "code": "COMPOSE_BIND_SOURCE_FORBIDDEN",
197
- "description": "A mount source path is invalid or is a protected system path.",
198
- "suggestion": "Use the controlled ${SYNOPKG_PKGVAR}, ${SYNO_APP_SECRETS_DIR}, or a declared ${SHARED_FOLDER} slot."
199
- },
200
- "COMPOSE_BIND_SOURCE_UNDECLARED": {
201
- "code": "COMPOSE_BIND_SOURCE_UNDECLARED",
202
- "description": "A volume mount uses an undeclared Shared Folder variable.",
203
- "suggestion": "Declare the shared_folder parameter in the synology-app.yaml install block first."
204
- },
205
- "COMPOSE_BIND_SUFFIX_INVALID": {
206
- "code": "COMPOSE_BIND_SUFFIX_INVALID",
207
- "description": "A Shared Folder subpath is invalid (it contains .. or a control character).",
208
- "suggestion": "Use a valid relative subpath, for example ${MEDIA_SHARE}/data:/data."
209
- },
210
- "COMPOSE_POSIX_ALL_SHARES_UNDECLARED": {
211
- "code": "COMPOSE_POSIX_ALL_SHARES_UNDECLARED",
212
- "description": "Compose uses an undeclared POSIX all-shares marker.",
213
- "suggestion": "Declare type: posix_all_shares so the package-only wizard/CLI takes the Share snapshot at install time."
214
- },
215
- "COMPOSE_POSIX_ALL_SHARES_SUFFIX_FORBIDDEN": {
216
- "code": "COMPOSE_POSIX_ALL_SHARES_SUFFIX_FORBIDDEN",
217
- "description": "A POSIX all-shares marker must not carry a Share subpath.",
218
- "suggestion": "Use the complete ${ALL_SHARES} source only; the package-only preinst expands each Share's container target."
219
- },
220
- "COMPOSE_HOST_BIND_FORBIDDEN": {
221
- "code": "COMPOSE_HOST_BIND_FORBIDDEN",
222
- "description": "Hard-coded host paths (for example /volume1/... or ./) are forbidden.",
223
- "suggestion": "Declare a shared_folder parameter in synology-app.yaml and reference it as ${SLOT} in Compose."
224
- },
225
- "COMPOSE_NAMED_VOLUME_UNDECLARED": {
226
- "code": "COMPOSE_NAMED_VOLUME_UNDECLARED",
227
- "description": "A service uses a Named Volume that is not declared in the top-level volumes.",
228
- "suggestion": "Add the volume declaration to the top-level volumes block in compose.yaml, or use a controlled directory."
229
- },
230
- "COMPOSE_USER_ROOT_FORBIDDEN": {
231
- "code": "COMPOSE_USER_ROOT_FORBIDDEN",
232
- "description": "The container runs as root (0:0), violating least privilege.",
233
- "suggestion": "Declare dsm_user in synology-app.yaml and use ${USER_UID}:${USER_GID} in Compose."
234
- },
235
- "COMPOSE_USER_CONFLICTS_DSM_USER": {
236
- "code": "COMPOSE_USER_CONFLICTS_DSM_USER",
237
- "description": "The Compose service defines its own user field, which conflicts with the dsm_user declared in the contract.",
238
- "suggestion": "Remove the user field from Compose; dsm_user injects ${NAME_UID}:${NAME_GID} and group_add automatically."
239
- },
240
- "COMPOSE_USER_NUMERIC_FORBIDDEN": {
241
- "code": "COMPOSE_USER_NUMERIC_FORBIDDEN",
242
- "description": "Hard-coding a numeric uid:gid in compose.yaml is forbidden.",
243
- "suggestion": "Declare dsm_user in synology-app.yaml and reference ${NAME_UID}:${NAME_GID} in Compose."
244
- },
245
- "COMPOSE_PRIVILEGED_FORBIDDEN": {
246
- "code": "COMPOSE_PRIVILEGED_FORBIDDEN",
247
- "description": "privileged: true is used but not declared in the contract.",
248
- "suggestion": "Add privilege.privileged: true to synology-app.yaml, or remove privileged: true."
249
- },
250
- "COMPOSE_HOST_NETWORK_FORBIDDEN": {
251
- "code": "COMPOSE_HOST_NETWORK_FORBIDDEN",
252
- "description": "host network mode is used but not declared in the contract.",
253
- "suggestion": "Add privilege.host_network: true to synology-app.yaml, or use a controlled network mode."
254
- },
255
- "COMPOSE_HOST_PID_FORBIDDEN": {
256
- "code": "COMPOSE_HOST_PID_FORBIDDEN",
257
- "description": "pid: host is used but not declared in the contract.",
258
- "suggestion": "Add privilege.host_pid: true to synology-app.yaml, or remove pid: host."
259
- },
260
- "COMPOSE_HOST_IPC_FORBIDDEN": {
261
- "code": "COMPOSE_HOST_IPC_FORBIDDEN",
262
- "description": "ipc: host is used but not declared in the contract.",
263
- "suggestion": "Add privilege.host_ipc: true to synology-app.yaml, or remove ipc: host."
264
- },
265
- "COMPOSE_DEVICES_FORBIDDEN": {
266
- "code": "COMPOSE_DEVICES_FORBIDDEN",
267
- "description": "Device access is used but not declared in the contract.",
268
- "suggestion": "Add privilege.devices: true to synology-app.yaml, or remove the devices block."
269
- },
270
- "COMPOSE_CAP_ADD_FORBIDDEN": {
271
- "code": "COMPOSE_CAP_ADD_FORBIDDEN",
272
- "description": "cap_add privilege escalation is used but not declared in the contract.",
273
- "suggestion": "Add privilege.cap_add: true to synology-app.yaml, or remove the cap_add block."
274
- },
275
- "COMPOSE_ENV_MISSING": {
276
- "code": "COMPOSE_ENV_MISSING",
277
- "description": "The project is missing the required environment variable directory structure.",
278
- "suggestion": "Make sure SYNOPKG_PKGVAR and the secrets directory are created correctly."
279
- },
280
- "COMPOSE_NOT_HEALTHY": {
281
- "code": "COMPOSE_NOT_HEALTHY",
282
- "description": "The Compose service did not reach healthy within the time limit.",
283
- "suggestion": "Check the container healthcheck and application logs: synopkgland logs --host <nas>."
284
- },
285
- "COMPOSE_NOT_RUNNING": {
286
- "code": "COMPOSE_NOT_RUNNING",
287
- "description": "The Compose service did not stay running within the time limit.",
288
- "suggestion": "Check the container start script and logs for the crash cause: synopkgland logs --host <nas>."
289
- },
290
- "BUILD_SPK_FAILED": {
291
- "code": "BUILD_SPK_FAILED",
292
- "description": "The SPK packaging flow failed.",
293
- "suggestion": "Fix the check errors and file permissions, then retry."
294
- },
295
- "VALIDATION_FAILED": {
296
- "code": "VALIDATION_FAILED",
297
- "description": "The project failed contract or security validation; deploy stopped.",
298
- "suggestion": "Run synopkgland check first to fix contract and Compose errors."
299
- },
300
- "DEPLOY_FAILED": {
301
- "code": "DEPLOY_FAILED",
302
- "description": "An unclassified error occurred while deploying to the NAS.",
303
- "suggestion": "Check NAS connectivity and logs: synopkgland logs --host <nas>."
304
- },
305
- "SHARE_VALUE_REQUIRED": {
306
- "code": "SHARE_VALUE_REQUIRED",
307
- "description": "Deploy is missing the physical path mapping for a shared_folder declared in the contract.",
308
- "suggestion": "Provide the matching Shared Folder name with --share <SLOT>=<logical name>."
309
- },
310
- "SHARE_LOGICAL_NAME_REQUIRED": {
311
- "code": "SHARE_LOGICAL_NAME_REQUIRED",
312
- "description": "A Share parameter needs a logical name; physical paths such as /volume1/... are forbidden.",
313
- "suggestion": "Give the Shared Folder logical name from DSM Control Panel, for example --share MEDIA_SHARE=photos."
314
- },
315
- "SHARE_NOT_FOUND": {
316
- "code": "SHARE_NOT_FOUND",
317
- "description": "The named Shared Folder was not found on the target NAS.",
318
- "suggestion": "Create the Shared Folder in DSM Control Panel, or check the spelling."
319
- },
320
- "HOMES_NOT_ENABLED": {
321
- "code": "HOMES_NOT_ENABLED",
322
- "description": "The target NAS has User Home disabled, or the homes physical bind source cannot be resolved.",
323
- "suggestion": "Enable DSM User Home on this NAS, confirm the DSM API returns the resolved path, then deploy again."
324
- },
325
- "DSM_USER_LOGICAL_NAME_REQUIRED": {
326
- "code": "DSM_USER_LOGICAL_NAME_REQUIRED",
327
- "description": "A dsm_user parameter needs a DSM user logical name; a numeric UID is forbidden.",
328
- "suggestion": "Pass the DSM account name with --run-as (for example --run-as RUN_AS=alice)."
329
- },
330
- "SHARE_RUN_AS_NO_ACCESS": {
331
- "code": "SHARE_RUN_AS_NO_ACCESS",
332
- "description": "The deploy pre-install check found that the chosen Container Run-as user has no permission on the chosen Shared Folder (or is explicitly denied). The container mounts the Share as that user, so the install succeeds but runtime reads and writes get Permission denied.",
333
- "suggestion": "Grant that user (or their group) permission on the Shared Folder in DSM Control Panel, or choose another user/Share, then deploy again. This check only runs in deploy; the Package Center install wizard does not go through it, and ACLs remain the administrator's responsibility."
334
- },
335
- "PORT_ALREADY_IN_USE": {
336
- "code": "PORT_ALREADY_IN_USE",
337
- "description": "The deploy pre-install check found the Compose published TCP port already taken by another service or container.",
338
- "suggestion": "Change the published host port in synology-app.yaml/compose.yaml; existing containers of the same Package Identity are allowed during upgrade or reinstall."
339
- },
340
- "CATALOG_INSTALL_FAILED": {
341
- "code": "CATALOG_INSTALL_FAILED",
342
- "description": "NAS Package Center failed to install the package.",
343
- "suggestion": "Check the install log in DSM Package Center or try installing manually."
344
- },
345
- "DEPLOY_PARAM_UNDECLARED": {
346
- "code": "DEPLOY_PARAM_UNDECLARED",
347
- "description": "deploy --param provided an Install Parameter the contract does not declare, or an install wizard value that a native-go/binary package does not have.",
348
- "suggestion": "For Compose pass only string or boolean names declared in synology-app.yaml install; native-go/binary accept only delegate_share or delegate_home from the install-selected delegate."
349
- },
350
- "DEPLOY_PARAM_REQUIRED": {
351
- "code": "DEPLOY_PARAM_REQUIRED",
352
- "description": "The first install of an install-selected delegate package did not provide the install wizard scope value, so preinst rejects the install.",
353
- "suggestion": "Add --param delegate_share=<shared folder> or --param delegate_home=true and deploy again."
354
- },
355
- "DEPLOY_PARAM_TYPE_INVALID": {
356
- "code": "DEPLOY_PARAM_TYPE_INVALID",
357
- "description": "The deploy --param value does not match the string or boolean type declared in the contract.",
358
- "suggestion": "Pass a non-empty string for string; pass only true or false for boolean."
359
- },
360
- "DEPLOY_POSIX_ALL_SHARES_LOOKUP_FAILED": {
361
- "code": "DEPLOY_POSIX_ALL_SHARES_LOOKUP_FAILED",
362
- "description": "The Shared Folder list at deploy time could not be listed with an administrator session.",
363
- "suggestion": "Confirm the DSM administrator session can call SYNO.Core.Share list, or provide the list explicitly with --param NAME=...."
364
- },
365
- "DSM_DEPENDENCY_CONFLICT": {
366
- "code": "DSM_DEPENDENCY_CONFLICT",
367
- "description": "DSM rejected the compound install request because of running dependent packages.",
368
- "suggestion": "Stop the listed dependent packages, then retry the upgrade."
369
- },
370
- "DSM_COMPOUND_FAILED": {
371
- "code": "DSM_COMPOUND_FAILED",
372
- "description": "A substep of the DSM compound install request failed.",
373
- "suggestion": "Check the api, method and errorCode in the response and the DSM Package Center log, then retry."
374
- },
375
- "DSM_REQUEST_FAILED": {
376
- "code": "DSM_REQUEST_FAILED",
377
- "description": "The DSM SYNO.Entry.Request compound request failed.",
378
- "suggestion": "Check the DSM session, request parameters and Package Center log, then retry."
379
- },
380
- "NOT_INSTALLED": {
381
- "code": "NOT_INSTALLED",
382
- "description": "The package is not installed on the target NAS yet.",
383
- "suggestion": "Run synopkgland deploy --host <nas> to install the package first."
384
- },
385
- "STANDALONE_MANIFEST_MISSING": {
386
- "code": "STANDALONE_MANIFEST_MISSING",
387
- "description": "dsm-package.yaml was not found.",
388
- "suggestion": "Run from the project root, or create a project with synopkgland init -t native-go."
389
- },
390
- "STANDALONE_MANIFEST_YAML": {
391
- "code": "STANDALONE_MANIFEST_YAML",
392
- "description": "dsm-package.yaml is not a valid YAML object.",
393
- "suggestion": "Fix the YAML syntax in dsm-package.yaml."
394
- },
395
- "APP_SCRIPT_UNKNOWN": {
396
- "code": "APP_SCRIPT_UNKNOWN",
397
- "description": "A file in the project's dsm-scripts/ folder does not have a DSM package script name the SDK runs.",
398
- "suggestion": "Name it preinst, postinst, preuninst, postuninst, preupgrade, postupgrade or start-stop-status, or move it out of dsm-scripts/. prereplace and postreplace are not supported."
399
- },
400
- "APP_SCRIPT_INVALID": {
401
- "code": "APP_SCRIPT_INVALID",
402
- "description": "A dsm-scripts/<DSM script name> entry is a symlink, directory or special file.",
403
- "suggestion": "Make it a regular file in the project's dsm-scripts/ folder."
404
- },
405
- "APP_SCRIPT_NOT_EXECUTABLE": {
406
- "code": "APP_SCRIPT_NOT_EXECUTABLE",
407
- "description": "A dsm-scripts/<DSM script name> file is not executable; DSM runs it directly.",
408
- "suggestion": "Run chmod +x on the file."
409
- },
410
- "APP_SCRIPT_NO_SHEBANG": {
411
- "code": "APP_SCRIPT_NO_SHEBANG",
412
- "description": "A dsm-scripts/<DSM script name> file does not start with #!.",
413
- "suggestion": "Start the file with an interpreter line such as #!/bin/sh."
414
- },
415
- "STANDALONE_FIELD_UNKNOWN": {
416
- "code": "STANDALONE_FIELD_UNKNOWN",
417
- "description": "dsm-package.yaml contains an unsupported field.",
418
- "suggestion": "Remove the field; a Standalone Package does not accept custom lifecycle scripts or DSM conf."
419
- },
420
- "STANDALONE_SCHEMA_INVALID": {
421
- "code": "STANDALONE_SCHEMA_INVALID",
422
- "description": "The schema version in dsm-package.yaml is not supported.",
423
- "suggestion": "Set schema to 2 and use backend.kind and capabilities."
424
- },
425
- "STANDALONE_ROOT_OPS_MISSING": {
426
- "code": "STANDALONE_ROOT_OPS_MISSING",
427
- "description": "capabilities.rootOps: true is set but there are no root operations.",
428
- "suggestion": "Put at least one <name>.sh in root/, or drop capabilities.rootOps."
429
- },
430
- "STANDALONE_DAEMON_INVALID": {
431
- "code": "STANDALONE_DAEMON_INVALID",
432
- "description": "The package daemon declaration does not match the fixed argv, environment and lifecycle contract.",
433
- "suggestion": "Check daemon.exec, the per-architecture targets, args, env, uninstall_args and integer-second timeouts; remove shell, user and custom conf."
434
- },
435
- "UNINSTALL_INVALID": {
436
- "code": "UNINSTALL_INVALID",
437
- "description": "The uninstall declaration does not match the fixed exec, argv, environment and timeout contract.",
438
- "suggestion": "Check uninstall.purge's exec (inside the package, not in executor/ or root/), args, env (SYNOPKGLAND_* not allowed) and 2-120 second timeout, and the unique keys of state.items."
439
- },
440
- "UNINSTALL_STATE_REQUIRES_PURGE": {
441
- "code": "UNINSTALL_STATE_REQUIRES_PURGE",
442
- "description": "uninstall.state has no App uninstall hook that could delete the state.",
443
- "suggestion": "Add uninstall.purge so the App deletes the listed state when SYNOPKGLAND_DELETE_STATE=1, or remove uninstall.state."
444
- },
445
- "UNINSTALL_STATE_TEXT_MISSING": {
446
- "code": "UNINSTALL_STATE_TEXT_MISSING",
447
- "description": "The uninstall wizard cannot list the text of the uninstall.state items.",
448
- "suggestion": "Add uninstall.state.<key> to i18n.messages en.json (and the other languages)."
449
- },
450
- "UNINSTALL_ROOT_REQUIRES_ROOT_OPS": {
451
- "code": "UNINSTALL_ROOT_REQUIRES_ROOT_OPS",
452
- "description": "uninstall.purge.root needs administrator-approved root operations.",
453
- "suggestion": "Declare capabilities.rootOps: true and provide root/<name>.sh, or remove uninstall.purge.root."
454
- },
455
- "UNINSTALL_ROOT_OP_MISSING": {
456
- "code": "UNINSTALL_ROOT_OP_MISSING",
457
- "description": "uninstall.purge.root points at a root operation that does not exist.",
458
- "suggestion": "Add root/<name>.sh, or change uninstall.purge.root to an existing root operation name."
459
- },
460
- "UNINSTALL_EXEC_MISSING": {
461
- "code": "UNINSTALL_EXEC_MISSING",
462
- "description": "uninstall.purge.exec is not a regular executable file inside the built package.",
463
- "suggestion": "Point at an executable in the package, for example ui/cgi/api.cgi, background/worker, or an executable file under daemon/payload/."
464
- },
465
- "UNINSTALL_EXEC_UNSAFE": {
466
- "code": "UNINSTALL_EXEC_UNSAFE",
467
- "description": "uninstall.purge.exec is a shell script that tries to gain root.",
468
- "suggestion": "Hooks always run as the package account; put cleanup that needs root in root/<name>.sh and declare it with uninstall.purge.root."
469
- },
470
- "STANDALONE_DAEMON_BUILD_FAILED": {
471
- "code": "STANDALONE_DAEMON_BUILD_FAILED",
472
- "description": "The package daemon's Go supervisor could not be built.",
473
- "suggestion": "Install Go 1.21 or later (builds pin go1.26.9 and need it offline) and check the compiler stderr; the NAS does not need a Go toolchain."
474
- },
475
- "STANDALONE_DAEMON_PAYLOAD_INVALID": {
476
- "code": "STANDALONE_DAEMON_PAYLOAD_INVALID",
477
- "description": "The daemon payload is not a bounded, link-free directory inside the project.",
478
- "suggestion": "Rebuild the per-architecture bundles; remove symlinks, special files and setid bits, and keep the size under 512 MiB."
479
- },
480
- "STANDALONE_DAEMON_ARTIFACT_REQUIRED": {
481
- "code": "STANDALONE_DAEMON_ARTIFACT_REQUIRED",
482
- "description": "The built supervisor is missing while assembling the daemon SPK.",
483
- "suggestion": "Build the complete artifacts with synopkgland build; do not call the assembler directly without the controller."
484
- },
485
- "STANDALONE_IDENTITY_GATEWAY_INVALID": {
486
- "code": "STANDALONE_IDENTITY_GATEWAY_INVALID",
487
- "description": "The identity_gateway declaration does not match the gateway contract.",
488
- "suggestion": "Declare a daemon; set sdk, mode (oidc, header or token), the App's loopback upstream port and distinct listen ports other than 5000/5001; keep passthrough and redirects local paths outside /_sso; oidc needs issuer_port and clients, header needs headers.user, token needs token.exec in the daemon payload."
489
- },
490
- "STANDALONE_IDENTITY_GATEWAY_BUILD_FAILED": {
491
- "code": "STANDALONE_IDENTITY_GATEWAY_BUILD_FAILED",
492
- "description": "The SDK identity gateway could not be built from identity_gateway.sdk.",
493
- "suggestion": "Use an SDK release that contains idgateway/cmd/gateway, install Go (builds pin go1.26.9) and check the compiler stderr."
494
- },
495
- "STANDALONE_IDENTITY_GATEWAY_ARTIFACT_REQUIRED": {
496
- "code": "STANDALONE_IDENTITY_GATEWAY_ARTIFACT_REQUIRED",
497
- "description": "The built identity gateway is missing while assembling the SPK.",
498
- "suggestion": "Build the complete artifacts with synopkgland build; do not call the assembler directly without the gateway."
499
- },
500
- "STANDALONE_SHARED_FOLDER_INVALID": {
501
- "code": "STANDALONE_SHARED_FOLDER_INVALID",
502
- "description": "The install.sharedFolder declaration in dsm-package.yaml is invalid.",
503
- "suggestion": "Write install: { sharedFolder: { mode: select|create, key: <lowercase alphanumeric and underscore>, permission: ro|rw } }; create also needs name (the DSM shared folder name) and select must not have one."
504
- },
505
- "STANDALONE_BACKGROUND_INVALID": {
506
- "code": "STANDALONE_BACKGROUND_INVALID",
507
- "description": "The package background job declaration does not match the fixed worker contract.",
508
- "suggestion": "Write only capabilities.jobs: true or { worker: sha256-v1 }, use backend.kind: go, and do not combine it with daemon or integration; background processes always run as the package account."
509
- },
510
- "STANDALONE_BACKGROUND_BUILD_FAILED": {
511
- "code": "STANDALONE_BACKGROUND_BUILD_FAILED",
512
- "description": "The package background worker could not be built.",
513
- "suggestion": "Install Go 1.25 or later, confirm the backend has cmd/background-worker (create it with synopkgland init -t native-go --with jobs), and check the compiler stderr."
514
- },
515
- "STANDALONE_BACKGROUND_ARTIFACT_REQUIRED": {
516
- "code": "STANDALONE_BACKGROUND_ARTIFACT_REQUIRED",
517
- "description": "The built worker is missing while assembling the background job SPK.",
518
- "suggestion": "Build the complete artifacts with synopkgland build; do not call the assembler directly without the worker."
519
- },
520
- "STANDALONE_ROOT_OP_INVALID": {
521
- "code": "STANDALONE_ROOT_OP_INVALID",
522
- "description": "root/ contains an invalid root operation file.",
523
- "suggestion": "A script must be a regular root/<lowercase name>.sh starting with #!/bin/sh and under 16 KiB; a binary must be root/<arch>/<lowercase name>, an ELF for that architecture, under 64 MiB, and must not share a script's name."
524
- },
525
- "STANDALONE_ROOT_PROGRAMS_INVALID": {
526
- "code": "STANDALONE_ROOT_PROGRAMS_INVALID",
527
- "description": "The root settings in dsm-package.yaml are invalid, or the executor does not accept them.",
528
- "suggestion": "root accepts only programs (<name>: {kind, schema, run, job} matching root/<name>.sh or root/<arch>/<name>), stop (a program in root/) and state: true, and requires capabilities.rootOps: true."
529
- },
530
- "STANDALONE_ROOT_OPS_UNUSED": {
531
- "code": "STANDALONE_ROOT_OPS_UNUSED",
532
- "description": "The project has a root/ directory but capabilities.rootOps is not enabled.",
533
- "suggestion": "Set capabilities.rootOps: true to use root operations, or delete root/."
534
- },
535
- "STANDALONE_PACKAGE_INVALID": {
536
- "code": "STANDALONE_PACKAGE_INVALID",
537
- "description": "The package name is invalid.",
538
- "suggestion": "Use a 2-64 character name that starts with a letter and contains only alphanumerics, for example PhotoTagger."
539
- },
540
- "STANDALONE_VERSION_INVALID": {
541
- "code": "STANDALONE_VERSION_INVALID",
542
- "description": "version is not a numeric version.",
543
- "suggestion": "Use a version such as 1.2.0 and put the build number in build."
544
- },
545
- "STANDALONE_BUILD_INVALID": {
546
- "code": "STANDALONE_BUILD_INVALID",
547
- "description": "build is not an integer from 1 to 9999.",
548
- "suggestion": "Set build to an integer from 1 to 9999 and increment it for each SPK release."
549
- },
550
- "STANDALONE_OS_MIN_VER_INVALID": {
551
- "code": "STANDALONE_OS_MIN_VER_INVALID",
552
- "description": "os_min_ver has the wrong format.",
553
- "suggestion": "Use <major>.<minor>-<build>, for example 7.2-64570."
554
- },
555
- "STANDALONE_TEXT_INVALID": {
556
- "code": "STANDALONE_TEXT_INVALID",
557
- "description": "A display text field is invalid.",
558
- "suggestion": "Use single-line, non-empty text without \" or \\."
559
- },
560
- "STANDALONE_ARCHES_INVALID": {
561
- "code": "STANDALONE_ARCHES_INVALID",
562
- "description": "arches is invalid.",
563
- "suggestion": "List one or more unique values from x86_64, aarch64 or armv7."
564
- },
565
- "STANDALONE_ARCH_NOT_DECLARED": {
566
- "code": "STANDALONE_ARCH_NOT_DECLARED",
567
- "description": "The requested build architecture is not in arches.",
568
- "suggestion": "Add the architecture to arches in dsm-package.yaml, or use a declared --arch."
569
- },
570
- "STANDALONE_BACKEND_INVALID": {
571
- "code": "STANDALONE_BACKEND_INVALID",
572
- "description": "The backend configuration is invalid.",
573
- "suggestion": "backend must contain exactly one of go (dir and a ./ main) or binary (keyed by architecture)."
574
- },
575
- "STANDALONE_BINARY_ARCH_MISSING": {
576
- "code": "STANDALONE_BINARY_ARCH_MISSING",
577
- "description": "An architecture declared in arches has no backend.files executable.",
578
- "suggestion": "Add the executable for that architecture to backend.files, or remove it from arches."
579
- },
580
- "STANDALONE_BINARY_MISSING": {
581
- "code": "STANDALONE_BINARY_MISSING",
582
- "description": "The backend executable was not found.",
583
- "suggestion": "Confirm the file backend.files points at exists."
584
- },
585
- "STANDALONE_BINARY_NOT_REGULAR": {
586
- "code": "STANDALONE_BINARY_NOT_REGULAR",
587
- "description": "The backend executable is not a regular file.",
588
- "suggestion": "Use a real file; no symlinks, directories or special files."
589
- },
590
- "STANDALONE_BINARY_TOO_LARGE": {
591
- "code": "STANDALONE_BINARY_TOO_LARGE",
592
- "description": "The backend executable exceeds 256 MiB.",
593
- "suggestion": "Shrink the executable (for example -ldflags \"-s -w\")."
594
- },
595
- "STANDALONE_BINARY_NOT_ELF": {
596
- "code": "STANDALONE_BINARY_NOT_ELF",
597
- "description": "The backend executable is not the little-endian Linux ELF the architecture needs (64-bit for x86_64 and aarch64, 32-bit for armv7).",
598
- "suggestion": "Provide an x86_64, aarch64 or armv7 executable built for Linux."
599
- },
600
- "STANDALONE_BINARY_ARCH_MISMATCH": {
601
- "code": "STANDALONE_BINARY_ARCH_MISMATCH",
602
- "description": "The backend executable's CPU architecture does not match the target.",
603
- "suggestion": "Rebuild for that architecture, or fix the backend.files mapping."
604
- },
605
- "STANDALONE_BINARY_DYNAMIC": {
606
- "code": "STANDALONE_BINARY_DYNAMIC",
607
- "description": "The backend executable is dynamically linked.",
608
- "suggestion": "Link statically (set CGO_ENABLED=0 for Go); the libc version differs across DSM models."
609
- },
610
- "STANDALONE_PATH_INVALID": {
611
- "code": "STANDALONE_PATH_INVALID",
612
- "description": "The path field is not a relative path inside the project.",
613
- "suggestion": "Use a path relative to the project root."
614
- },
615
- "STANDALONE_PATH_OUTSIDE_PROJECT": {
616
- "code": "STANDALONE_PATH_OUTSIDE_PROJECT",
617
- "description": "The path points outside the project directory.",
618
- "suggestion": "Keep the files inside the project directory (also after resolving symlinks)."
619
- },
620
- "STANDALONE_UI_INVALID": {
621
- "code": "STANDALONE_UI_INVALID",
622
- "description": "The ui configuration is invalid.",
623
- "suggestion": "Provide ui.dir."
624
- },
625
- "STANDALONE_UI_BUILD_INVALID": {
626
- "code": "STANDALONE_UI_BUILD_INVALID",
627
- "description": "The ui.build configuration is invalid.",
628
- "suggestion": "ui.build.command must be an argv string array (not going through a shell), for example [npm, run, build]."
629
- },
630
- "STANDALONE_TIMEOUT_UNSUPPORTED": {
631
- "code": "STANDALONE_TIMEOUT_UNSUPPORTED",
632
- "description": "backend.timeout_seconds needs a Go main that reads the requestTimeout the build passes.",
633
- "suggestion": "Declare var requestTimeout = \"\" in main.go and pass its seconds to api.WithRequestDeadline(handler, api.Timeout(...)), or remove timeout_seconds."
634
- },
635
- "STANDALONE_REQUEST_TIMEOUT_SHORT": {
636
- "code": "STANDALONE_REQUEST_TIMEOUT_SHORT",
637
- "description": "The request deadline ends before a root program that root.run may run would answer: the executor answers 10 seconds after run.timeout_s and its client waits 10 more.",
638
- "suggestion": "Set backend.timeout_seconds to at least the program's run.timeout_s (20 by default) + 20, or lower run.timeout_s."
639
- },
640
- "STANDALONE_UI_WINDOW_INVALID": {
641
- "code": "STANDALONE_UI_WINDOW_INVALID",
642
- "description": "ui.window does not give a valid DSM window size.",
643
- "suggestion": "Give width and height as whole numbers from 320 to 3840 and 240 to 2160, keep minWidth and minHeight (700 and 420 by default) no larger than them, and write only the fields you change."
644
- },
645
- "STANDALONE_UI_BUILD_FAILED": {
646
- "code": "STANDALONE_UI_BUILD_FAILED",
647
- "description": "The UI build command failed.",
648
- "suggestion": "Install the UI dependencies in ui.build.cwd (npm ci when package-lock.json exists, otherwise npm install), then run the UI build command and fix the errors."
649
- },
650
- "STANDALONE_UI_ENTRY_MISSING": {
651
- "code": "STANDALONE_UI_ENTRY_MISSING",
652
- "description": "The UI directory is missing app.js or app.css.",
653
- "suggestion": "Make ui.dir (or the UI build output) contain app.js and app.css."
654
- },
655
- "STANDALONE_UI_SYMLINK": {
656
- "code": "STANDALONE_UI_SYMLINK",
657
- "description": "The UI directory contains a symlink.",
658
- "suggestion": "Replace the symlink with a real file."
659
- },
660
- "STANDALONE_UI_SPECIAL_FILE": {
661
- "code": "STANDALONE_UI_SPECIAL_FILE",
662
- "description": "The UI directory contains a special file.",
663
- "suggestion": "The UI directory may contain only regular files and directories."
664
- },
665
- "STANDALONE_UI_TOO_LARGE": {
666
- "code": "STANDALONE_UI_TOO_LARGE",
667
- "description": "The total UI file size exceeds 64 MiB.",
668
- "suggestion": "Shrink the UI assets."
669
- },
670
- "STANDALONE_GO_MISSING": {
671
- "code": "STANDALONE_GO_MISSING",
672
- "description": "The go command was not found.",
673
- "suggestion": "Install Go 1.25 or later (https://go.dev/dl/), or package an existing executable with backend.files."
674
- },
675
- "STANDALONE_GO_BUILD_FAILED": {
676
- "code": "STANDALONE_GO_BUILD_FAILED",
677
- "description": "go build failed.",
678
- "suggestion": "Run go build ./... in the backend directory and fix the errors. If github.com/synopackageland/syno-pkg-land-sdk is unreachable, make sure git can read github.com with credentials (verify with git ls-remote)."
679
- },
680
- "STANDALONE_PORTAL_INVALID": {
681
- "code": "STANDALONE_PORTAL_INVALID",
682
- "description": "The portal format or alias in dsm-package.yaml is invalid.",
683
- "suggestion": "Use portal: {} or portal: { alias: my-app }; alias is 1-64 lowercase alphanumerics or -, and not a DSM reserved path."
684
- },
685
- "STANDALONE_PORTAL_REQUIRES_EXECUTOR": {
686
- "code": "STANDALONE_PORTAL_REQUIRES_EXECUTOR",
687
- "description": "An App without an executor (capabilities.rootOps or delegate) declares portal.",
688
- "suggestion": "Removing the sign-in alias after uninstall needs root so nginx stops serving it, but an App without escalation uses no root at any stage. Add capabilities.rootOps or delegate (executor level), or remove portal and let an administrator configure a reverse proxy in DSM."
689
- },
690
- "STANDALONE_LOGIN_STYLE_INVALID": {
691
- "code": "STANDALONE_LOGIN_STYLE_INVALID",
692
- "description": "login_style in dsm-package.yaml is malformed, or set without portal.",
693
- "suggestion": "Declare portal, and give login_style only color (#RRGGBB), wallpaper together with wallpaper_thumbnail, and title; omit login_style to use the CLI default style."
694
- },
695
- "STANDALONE_LOGIN_STYLE_IMAGE_INVALID": {
696
- "code": "STANDALONE_LOGIN_STYLE_IMAGE_INVALID",
697
- "description": "An image login_style names is missing, in the wrong format or the wrong size.",
698
- "suggestion": "Use a JPEG wallpaper of at least 1920x1080 and at most 2 MiB, a 320x200 JPEG wallpaper_thumbnail and a 680x124 PNG title, inside the project."
699
- },
700
- "STANDALONE_I18N_INVALID": {
701
- "code": "STANDALONE_I18N_INVALID",
702
- "description": "The i18n configuration in dsm-package.yaml is invalid.",
703
- "suggestion": "i18n accepts only messages, glossary and info; info is keyed by a supported language such as zh-TW or ja (English goes at the top level)."
704
- },
705
- "STANDALONE_I18N_MESSAGES_MISSING": {
706
- "code": "STANDALONE_I18N_MESSAGES_MISSING",
707
- "description": "The i18n.messages directory is missing en.json.",
708
- "suggestion": "Add en.json; it is the fallback text and key list for every language."
709
- },
710
- "STANDALONE_I18N_LOCALE_UNSUPPORTED": {
711
- "code": "STANDALONE_I18N_LOCALE_UNSUPPORTED",
712
- "description": "A message file's language is not one of the nine supported locales.",
713
- "suggestion": "Use en, zh-TW, zh-CN, ja, ko, de, fr, es, it; every other DSM language shows English."
714
- },
715
- "STANDALONE_I18N_CATALOG_INVALID": {
716
- "code": "STANDALONE_I18N_CATALOG_INVALID",
717
- "description": "A message file is not a flat key -> text JSON object.",
718
- "suggestion": "Use a format like { \"greeting.save\": \"Save\" }."
719
- },
720
- "STANDALONE_I18N_KEYS_MISMATCH": {
721
- "code": "STANDALONE_I18N_KEYS_MISMATCH",
722
- "description": "One language's message keys differ from en.json.",
723
- "suggestion": "Give every language file exactly the keys of en.json."
724
- },
725
- "STANDALONE_I18N_PLACEHOLDER_MISMATCH": {
726
- "code": "STANDALONE_I18N_PLACEHOLDER_MISMATCH",
727
- "description": "A translation has a missing or extra {placeholder}.",
728
- "suggestion": "Keep every {name} placeholder from the en.json text."
729
- },
730
- "STANDALONE_I18N_GLOSSARY_INVALID": {
731
- "code": "STANDALONE_I18N_GLOSSARY_INVALID",
732
- "description": "The glossary format is wrong, or it points at a message key that does not exist.",
733
- "suggestion": "Fix the terms (id, text, keys) in glossary.json."
734
- },
735
- "STANDALONE_I18N_GLOSSARY_MISMATCH": {
736
- "code": "STANDALONE_I18N_GLOSSARY_MISMATCH",
737
- "description": "A message does not use the DSM interface term from the glossary.",
738
- "suggestion": "Use that language's term from glossary.json to stay consistent with the DSM interface."
739
- },
740
- "DEPLOY_VERSION_INVALID": {
741
- "code": "DEPLOY_VERSION_INVALID",
742
- "description": "The package version is invalid or lower than the installed version.",
743
- "suggestion": "Set a higher valid version/build and deploy again."
744
- }
745
1023
  };
746
- for (const [code, description, suggestion] of [
747
- ["CAPABILITY_INVALID", "The capability declaration is invalid.", "Use files: session, jobs: true or {worker: sha256-v1}, identity: standalone, rootOps: true."],
748
- ["CAPABILITY_UNSUPPORTED", "This runtime does not support the chosen capability.", "files, jobs and identity need backend.kind: go; compose does not support native capabilities."],
749
- ["CAPABILITY_COMBINATION_INVALID", "The capability combination with the runtime or UI is invalid.", "daemon cannot combine with capabilities.jobs; identity/login needs a vue UI (--ui vue)."],
750
- ["ADD_FILE_CONFLICT", "Existing files conflict with the SDK overlay.", "Move or merge the listed conflicting files before running add; the CLI never overwrites different content."],
751
- ])
752
- ERROR_REGISTRY[code] = { code, description, suggestion };
753
- ERROR_REGISTRY["ADD_LAYOUT_UNSUPPORTED"] = { "code": "ADD_LAYOUT_UNSUPPORTED", "description": "The SDK overlay needs the standard backend directory.", "suggestion": "Use backend.dir: backend, or merge the SDK overlay manually." };
754
- ERROR_REGISTRY["MIN_DSM_CONTAINER_FLOOR"] = { "code": "MIN_DSM_CONTAINER_FLOOR", "description": "The Compose minimum DSM version is too low.", "suggestion": "Compose needs DSM 7.2.1-69057 or later." };
755
- ERROR_REGISTRY.STANDALONE_PORTAL_ENTRY_INVALID = { "code": "STANDALONE_PORTAL_ENTRY_INVALID", "description": "When os_min_ver is below DSM 7.2-64570 the executor registers the portal, and the package name or displayname does not match its rules.", "suggestion": "The package name may contain only alphanumerics separated by . _ -, at most 64 characters; displayname is 1-64 characters without control characters." };
756
- ERROR_REGISTRY.STANDALONE_PORTAL_MIN_DSM = { "code": "STANDALONE_PORTAL_MIN_DSM", "description": "A package that declares portal has os_min_ver below DSM 7.1-42661.", "suggestion": "Before DSM 7.2-64570 the executor writes the entry into DSM's app.d, and that format is only verified from DSM 7.1. Raise os_min_ver to 7.1-42661 or later, or remove portal." };
757
- ERROR_REGISTRY["MIN_DSM_INVALID"] = { "code": "MIN_DSM_INVALID", "description": "The minimum DSM version format is invalid.", "suggestion": "Specify a 7.x release or a full 7.x-build." };
758
- ERROR_REGISTRY["NOTICE_UNSAFE"] = { "code": "NOTICE_UNSAFE", "description": "A license file does not meet the regular-file limits.", "suggestion": "Remove symlinks, special files and oversized license files." };
759
- ERROR_REGISTRY["NOTICE_MISSING"] = { "code": "NOTICE_MISSING", "description": "Third-party license information is missing.", "suggestion": "Reinstall the template dependencies and generate the third-party notices." };
760
- ERROR_REGISTRY["PROJECT_NOT_FOUND"] = { "code": "PROJECT_NOT_FOUND", "description": "The project manifest was not found.", "suggestion": "Run from the project root or use --project." };
761
- ERROR_REGISTRY["CLI_OPTION_INVALID"] = { "code": "CLI_OPTION_INVALID", "description": "A command option is invalid.", "suggestion": "Use --help to check the options and the available runtime." };
762
- ERROR_REGISTRY["CLI_OUTPUT_ARCH_REQUIRED"] = { "code": "CLI_OUTPUT_ARCH_REQUIRED", "description": "Writing a single file needs an architecture.", "suggestion": "Combine -o with a single --arch, or use --out-dir." };
763
- ERROR_REGISTRY["HOST_REQUIRED"] = { "code": "HOST_REQUIRED", "description": "No NAS was specified.", "suggestion": "Use --host <profile>; for a local deploy check use --dry-run." };
764
- ERROR_REGISTRY["NAS_CREDENTIALS_MISSING"] = { "code": "NAS_CREDENTIALS_MISSING", "description": "NAS credentials were not found.", "suggestion": "Set a synoagentcli profile or DSM_URL/DSM_PASSWORD." };
765
- ERROR_REGISTRY["NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE"] = { "code": "NATIVE_LAYOUT_SCRIPT_NOT_EXECUTABLE", "description": "A scripts/ lifecycle script in the native layout metadata is not executable (inspect's SPK_SCRIPT_NOT_EXECUTABLE).", "suggestion": "chmod +x the script in the layout metadata directory, then build again." };
766
- ERROR_REGISTRY["NATIVE_TOOLKIT_TAR_UNSUPPORTED"] = { "code": "NATIVE_TOOLKIT_TAR_UNSUPPORTED", "description": "Extracting native.toolkits needs GNU tar or bsdtar, but the tar on PATH is neither.", "suggestion": "Point the tar on PATH at GNU tar or bsdtar (macOS ships bsdtar, most Linux distros GNU tar), then build again." };
767
- ERROR_REGISTRY["SPK_INSPECTION_FAILED"] = { "code": "SPK_INSPECTION_FAILED", "description": "The SPK does not match the DSM 7 package format (dsm.errors), or a synopkgland-built SPK does not match the build contract (findings).", "suggestion": "Fix the SPK_* rule codes and findings, then repackage; warnings do not affect the result." };
768
- ERROR_REGISTRY["IMAGE_PIN_REQUIRED"] = { "code": "IMAGE_PIN_REQUIRED", "description": "The image digest is not pinned.", "suggestion": "Run images pin." };
769
- ERROR_REGISTRY["INIT_TEMPLATE_REQUIRED"] = { "code": "INIT_TEMPLATE_REQUIRED", "description": "No template was specified.", "suggestion": "Use -t native-go, native-python, native-node, native-rust, binary or compose." };
770
- ERROR_REGISTRY["INIT_TEMPLATE_UNKNOWN"] = { "code": "INIT_TEMPLATE_UNKNOWN", "description": "Unknown template.", "suggestion": "Use -t native-go, native-python, native-node, native-rust, binary or compose." };
771
- ERROR_REGISTRY["CLI_ARCH_INVALID"] = { "code": "CLI_ARCH_INVALID", "description": "The architecture option is invalid.", "suggestion": "Use x86_64, aarch64 or armv7 (armv7 is for native-go and binary, not for Container Manager Apps)." };
772
- ERROR_REGISTRY["CLI_JOBS_INVALID"] = { "code": "CLI_JOBS_INVALID", "description": "--jobs is not a positive integer.", "suggestion": "Use --jobs 1 (the default) or a larger integer." };
773
- ERROR_REGISTRY["BUILD_NOT_REPRODUCIBLE"] = { "code": "BUILD_NOT_REPRODUCIBLE", "description": "A second SPK built by build --verify-reproducible differs from the first.", "suggestion": "Look at the listed payload and metadata files and remove anything that changes per build (timestamps, random or absolute paths, unpinned dependencies)." };
774
- ERROR_REGISTRY["REPRODUCIBLE_INVALID"] = { "code": "REPRODUCIBLE_INVALID", "description": "The reproducible configuration is malformed, or a listed input is not a file inside the project.", "suggestion": "Use reproducible: {inputs: [files...], clean: [paths...]} with project-relative paths; {arch} is only allowed in clean." };
775
- ERROR_REGISTRY["NATIVE_ARCH_UNDETERMINED"] = { "code": "NATIVE_ARCH_UNDETERMINED", "description": "The NAS architecture could not be determined.", "suggestion": "Specify --arch x86_64, aarch64 or armv7." };
776
- ERROR_REGISTRY["DEVELOPMENT_INSTANCE_UNSUPPORTED"] = { "code": "DEVELOPMENT_INSTANCE_UNSUPPORTED", "description": "A native project does not support --dev.", "suggestion": "Use a different package ID when running init." };
777
- ERROR_REGISTRY["INIT_OPTIONS_UNSUPPORTED"] = { "code": "INIT_OPTIONS_UNSUPPORTED", "description": "The option does not apply to this template.", "suggestion": "--from is compose only; --ui is native templates only (native-go, native-python, native-node, native-rust)." };
778
- ERROR_REGISTRY["INIT_TARGET_NOT_EMPTY"] = { "code": "INIT_TARGET_NOT_EMPTY", "description": "The init directory already has conflicting files.", "suggestion": "Use an empty directory; add capabilities to an existing project with add." };
779
- ERROR_REGISTRY["INIT_COMPOSE_INVALID"] = { "code": "INIT_COMPOSE_INVALID", "description": "The imported Compose is invalid.", "suggestion": "Point --from at a YAML file with services." };
780
- ERROR_REGISTRY["INIT_BINARY_BUILD_FAILED"] = { "code": "INIT_BINARY_BUILD_FAILED", "description": "The binary example failed to compile.", "suggestion": "Install Go or provide your own static CGI ELF." };
781
- ERROR_REGISTRY["INIT_SHARED_FOLDER_INVALID"] = { "code": "INIT_SHARED_FOLDER_INVALID", "description": "The shared folder option is invalid.", "suggestion": "Use select or create; add --shared-folder-name with create." };
782
- ERROR_REGISTRY["INIT_UI_INVALID"] = { "code": "INIT_UI_INVALID", "description": "The UI option is invalid.", "suggestion": "Use --ui vue or plain." };
783
- ERROR_REGISTRY["INIT_PACKAGE_INVALID"] = { "code": "INIT_PACKAGE_INVALID", "description": "The package name is invalid.", "suggestion": "Use a 2-64 character alphanumeric name that starts with a letter." };
784
- ERROR_REGISTRY["INIT_DISPLAY_NAME_INVALID"] = { "code": "INIT_DISPLAY_NAME_INVALID", "description": "The display name is invalid.", "suggestion": "Use single-line text without quotes or backslashes." };
785
- ERROR_REGISTRY["STANDALONE_API_PERMISSIONS_CHANGED"] = { "code": "STANDALONE_API_PERMISSIONS_CHANGED", "description": "The API policy changed during the build.", "suggestion": "Freeze permissions.json, then run check/build again." };
786
- ERROR_REGISTRY["STANDALONE_API_PERMISSIONS_INVALID"] = { "code": "STANDALONE_API_PERMISSIONS_INVALID", "description": "The API policy is invalid.", "suggestion": "Fix backend/internal/api/permissions.json." };
787
- ERROR_REGISTRY["CLI_OPTION_UNKNOWN"] = { "code": "CLI_OPTION_UNKNOWN", "description": "Unknown CLI option.", "suggestion": "Use --help to see the currently supported options." };
788
- ERROR_REGISTRY["CLI_ARGUMENT_MISSING"] = { "code": "CLI_ARGUMENT_MISSING", "description": "A command argument is missing.", "suggestion": "Use --help to see the required arguments." };
789
- ERROR_REGISTRY["CLI_OPTION_MISSING"] = { "code": "CLI_OPTION_MISSING", "description": "A required option is missing.", "suggestion": "Use --help to see the required options." };
790
- ERROR_REGISTRY["CLI_OPTION_ARGUMENT_MISSING"] = { "code": "CLI_OPTION_ARGUMENT_MISSING", "description": "An option is missing its value.", "suggestion": "Pass the value the option needs." };
791
- ERROR_REGISTRY["CLI_COMMAND_UNKNOWN"] = { "code": "CLI_COMMAND_UNKNOWN", "description": "Unknown command.", "suggestion": "Use synopkgland --help to see the commands." };
792
- ERROR_REGISTRY["COMMAND_FAILED"] = { "code": "COMMAND_FAILED", "description": "The command failed.", "suggestion": "Check the message and stderr, fix the problem and retry." };
793
- ERROR_REGISTRY["DEPLOY_PLAN_INVALID"] = { "code": "DEPLOY_PLAN_INVALID", "description": "The deploy plan does not match the runtime.", "suggestion": "Run check again and regenerate the deploy plan." };
794
- ERROR_REGISTRY["DEPLOY_ARCH_UNDETERMINED"] = { "code": "DEPLOY_ARCH_UNDETERMINED", "description": "The NAS architecture could not be determined.", "suggestion": "Use --arch x86_64, aarch64 or armv7." };
795
- ERROR_REGISTRY["CONTAINER_ARCH_UNSUPPORTED"] = { "code": "CONTAINER_ARCH_UNSUPPORTED", "description": "Container Manager Apps support only x86_64 and aarch64; DSM does not provide Container Manager for armv7.", "suggestion": "Use --arch x86_64 or aarch64, or switch to the native-go template." };
796
- ERROR_REGISTRY["DEPLOY_BUILD_OUTPUT_INVALID"] = { "code": "DEPLOY_BUILD_OUTPUT_INVALID", "description": "The number of deploy build outputs does not match.", "suggestion": "Specify a single architecture and build again." };
797
1024
  export function getErrorCode(code) {
798
1025
  return ERROR_REGISTRY[code];
799
1026
  }
800
- for (const [code, description, suggestion] of [
801
- ["EXECUTOR_BUILD_FAILED", "Compiling the executor or lifecycle helper from the App's SDK version failed.", "Confirm Go 1.21 or later is installed and go1.26.9 is available (prepare it offline), and check the compiler output in the message."],
802
- ["EXECUTOR_DIGEST_MISMATCH", "The executor built from the SDK version does not match the sha256 table bundled in the CLI, or the table has no entry for this SDK version.", "Confirm github.com/synopackageland/syno-pkg-land-sdk in backend/go.mod is a release version and Go is go1.26.9; upgrade the CLI when the table has no entry for that version."],
803
- ["EXECUTOR_DIGEST_UNVERIFIED", "SYNOPKGLAND_SDK_DIR points at a local SDK, so the executor was not checked against the sha256 table (warning).", "Development only; for a publishable SPK unset SYNOPKGLAND_SDK_DIR and build again."],
804
- ["SDK_REQUIRED", "Python, Node and Rust Apps, and rootOps or delegate, need the SDK, but the backend does not name an SDK version (Go: backend/go.mod; Python and Node: backend.sdk; Rust: syno-pkg-land-app in Cargo.toml).", "In Go add require github.com/synopackageland/syno-pkg-land-sdk to backend/go.mod; in Python and Node write backend.sdk in dsm-package.yaml; in Rust depend on syno-pkg-land-app by git+tag in the App's Cargo.toml; keep the version on the same minor as the CLI."],
805
- ["SDK_TAG_REQUIRED", "The Rust App's Cargo.lock does not pin exactly one syno-pkg-land-app to a release tag (branch, rev, path, registry and multiple versions are all rejected).", "Write syno-pkg-land-app = { git = \"https://github.com/synopackageland/syno-pkg-land-sdk\", tag = \"vX.Y.Z\" } in Cargo.toml, run cargo update -p syno-pkg-land-app and commit Cargo.lock."],
806
- ["SDK_VERSION_MISMATCH", "The @synopackageland/sdk in frontend/package.json differs from the SDK version the backend requires (backend/go.mod or backend/Cargo.lock).", "Make both sides the same version; for a file: dependency the comparison uses that directory's package.json version."],
807
- ["SDK_UNSUPPORTED", "The SDK the backend requires (backend/go.mod or backend/Cargo.lock) is not on this CLI's minor line.", "Use a CLI on the same minor as the SDK, or raise the SDK to a version line the CLI supports (listed in the message)."],
808
- ["SDK_DIR_NOT_READY", "The SDK checkout SYNOPKGLAND_SDK_DIR points at has no go.mod, or the ts package a Vue project needs is not built (no ts/dist).", "Point at a syno-pkg-land-sdk checkout; for a Vue project first run npm ci && npm run build in $SYNOPKGLAND_SDK_DIR/ts (npm does not build file: packages for you)."],
809
- ["SDK_DOWNLOAD_FAILED", "go mod download cannot fetch the SDK version backend/go.mod requires.", "The CLI fetches the private SDK with git: make sure git can read github.com with credentials (verify with git ls-remote), then confirm the version exists. Offline, run go mod download once on the same machine first."],
810
- ["TEMPLATES_MISSING", "@synopackageland/templates was not found.", "Reinstall @synopackageland/cli, or point SYNOPKGLAND_TEMPLATES_DIR at a templates checkout."],
811
- ["EXECUTOR_PIN_REQUIRED", "A package that declares rootOps or delegate was built without the executor artifact.", "Use synopkgland build so the CLI compiles and pins the executor; do not call the low-level SPK API directly."],
812
- ["EXECUTOR_POLICY", "capabilities.delegate has invalid operations or scope, or a binary root program lacks the file for this architecture, so the executor policy cannot be generated.", "Use the delegate declaration init/add produces and fix scope and operations against the manifest reference; for binary root programs provide root/<arch>/<name> for every arch in arches."],
813
- ["EXECUTOR_POLICY_REJECTED", "The executor of the App's SDK version refuses the policy the CLI generated, so the NAS would refuse it at approval.", "The reason names the refused part: policy root/start/stop/purge → root.programs (schema, run, job), root.start, root.stop, uninstall.purge; policy scope/operation/destination/move/lifetime → capabilities.delegate; policy portal → portal; policy entry → entry. Fix that part of dsm-package.yaml; if it already matches the manifest reference, use the SDK version this CLI release names."],
814
- ["EXECUTOR_ROOT_PROGRAMS_UNSUPPORTED", "The root settings in dsm-package.yaml need an executor that supports root program settings, which the App's SDK version lacks.", "Use an SDK with root programs, stop and root_state (executor/stop_step.go), or remove the root settings."],
815
- ["EXECUTOR_PACKAGE", "The package name pinned into the root task is invalid.", "package must start with a letter and contain only alphanumerics (2-64 characters)."],
816
- ["EXECUTOR_DIGEST", "The sha256 pin for the executor or policy is malformed.", "Build again so the CLI recomputes the pin; do not hand-edit pins inside the SPK."],
817
- ["EXECUTOR_TASK_PERCENT", "The root task script contains %, which the DSM task scheduler cannot store.", "Remove % from the root/ scripts (for example rewrite the date format)."],
818
- ["EXECUTOR_TASK_LENGTH", "The root task script exceeds the DSM task scheduler's length limit.", "Shorten the root/ scripts; move the logic into a file inside the package and call it from the script."],
819
- ])
820
- ERROR_REGISTRY[code] = { code, description, suggestion };
821
- ERROR_REGISTRY["STANDALONE_ENTRY_INVALID"] = { "code": "STANDALONE_ENTRY_INVALID", "description": "The entry configuration is invalid.", "suggestion": "Set entry to admin-only or app-privilege; app-privilege needs at least one authenticated route." };
822
- ERROR_REGISTRY["STANDALONE_ENTRY_WIDENED"] = { "code": "STANDALONE_ENTRY_WIDENED", "description": "entry: admin-only conflicts with routes that widen access.", "suggestion": "Change the authenticated or public routes in permissions.json to administrators; to open it to regular users use entry: app-privilege or omit entry." };
823
- ERROR_REGISTRY["STANDALONE_ENTRY_UNSUPPORTED"] = { "code": "STANDALONE_ENTRY_UNSUPPORTED", "description": "The backend cannot enforce the entry mode.", "suggestion": "Use a Go backend: require github.com/synopackageland/syno-pkg-land-sdk in backend/go.mod and wrap the authenticator with dsm.NewEntryGate in the main.go of backend.main (init defaults to cmd/app/main.go)." };
824
- ERROR_REGISTRY["DSM_TLS_UNTRUSTED"] = { "code": "DSM_TLS_UNTRUSTED", "description": "The DSM TLS certificate is not trusted by this computer (self-signed, expired or a name mismatch).", "suggestion": "Export the NAS certificate or its CA as PEM and run with NODE_EXTRA_CA_CERTS=<file>; or give DSM a trusted certificate whose name matches the host profile. Do not disable certificate verification." };
825
- ERROR_REGISTRY["STANDALONE_ICON_INVALID"] = { "code": "STANDALONE_ICON_INVALID", "description": "The icon in dsm-package.yaml is not a usable package icon.", "suggestion": "Set icon to a square PNG inside the project (at least 256x256), or a directory of icon_<size>.png including icon_256.png; omit it to use the SDK default icon." };
826
- ERROR_REGISTRY["CONTRACT_ICON_INVALID"] = { "code": "CONTRACT_ICON_INVALID", "description": "The icon in synology-app.yaml is not a usable package icon.", "suggestion": "Set icon to a square PNG inside the project (at least 256x256), or a directory of icon_<size>.png including icon_256.png; omit it to use the SDK default icon." };
827
- ERROR_REGISTRY["CONTRACT_I18N_INVALID"] = { "code": "CONTRACT_I18N_INVALID", "description": "The i18n configuration in synology-app.yaml is invalid.", "suggestion": "i18n accepts only info; info is keyed by a supported language such as zh-TW or ja with single-line displayname / description values without \" or \\ (English goes at the top level)." };
828
- for (const [code, description, suggestion] of [
829
- ["STANDALONE_BRIDGE_APP_IN_UI", "The backend.dir and ui.dir of a Python, Node or Rust App overlap; DSM serves the files under ui/ directly.", "Put the App in a directory outside ui.dir (for example app/); neither may contain the other."],
830
- ["STANDALONE_BRIDGE_METHODS_MISMATCH", "The App's methods do not match permissions.json, or use an SDK method name; the runtime refuses to start.", "permissions.json lists only the App's own methods; an App method must not share a name with an SDK method (nas.*, identity.*, portal.status and others)."],
831
- ["STANDALONE_RUNTIME_BUILD_FAILED", "Compiling the bridge runtime (bridge/cmd/runtime) from the App's SDK version failed, or the result was not built with go1.26.9.", "Confirm Go 1.21 or later is installed and go1.26.9 is available; that SDK version must contain the bridge runtime; check the compiler output in the message."],
832
- ["STANDALONE_BRIDGE_DESCRIBE_FAILED", "There is no backend.methods, so build starts the App to read hello, but the App did not answer a v1 hello.", "Install python3 or node on the build machine and make sure the App starts; or list backend.methods in dsm-package.yaml."],
833
- ["STANDALONE_NODE_DEPENDENCY_MISSING", "The Node App's package.json dependencies are not installed in the App's node_modules.", "Run npm ci --omit=dev in the App directory and build again; the CLI places @synopackageland/app, so it does not need installing."],
834
- ["STANDALONE_BRIDGE_APP_INVALID", "The App directory contains a symlink or special file, or exceeds 256 MiB.", "Keep only regular files and directories in the App directory; move large data out of it."],
835
- ["STANDALONE_RUST_BUILD_FAILED", "The Rust App's cargo zigbuild cross-compile failed, or it produced no binary for backend.entry.", "Install Rust with rustup and the three targets (x86_64/aarch64-unknown-linux-musl, armv7-unknown-linux-gnueabihf), install cargo-zigbuild and zig; set backend.entry to the Cargo binary name; check the compiler output in the message."],
836
- ["STANDALONE_BRIDGE_MANIFEST_INVALID", "The bridge.json the CLI produced does not match spec/bridge/v1 manifest.schema.json.", "Check backend.entry, methods and node.package; if it still fails, report the CLI bug."],
837
- ["STANDALONE_PYTHON_SDK_MISSING", "The App's SDK version has no Python SDK (python/synopkgland_app).", "Point SYNOPKGLAND_SDK_DIR at a syno-pkg-land-sdk checkout that has python/synopkgland_app, or require an SDK release that ships it."],
838
- ["STANDALONE_NODE_SDK_NOT_BUILT", "The App's SDK version has no built Node SDK (node/dist).", "Run npm ci && npm run build in node/ of the SYNOPKGLAND_SDK_DIR checkout first; a release SDK must contain node/dist."],
839
- ])
840
- ERROR_REGISTRY[code] = { code, description, suggestion };