@synopackageland/sdk 0.9.9 → 0.10.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 (43) hide show
  1. package/README.md +121 -0
  2. package/dist/i18n/bridge/de.json +6 -0
  3. package/dist/i18n/bridge/en.json +6 -0
  4. package/dist/i18n/bridge/es.json +6 -0
  5. package/dist/i18n/bridge/fr.json +6 -0
  6. package/dist/i18n/bridge/it.json +6 -0
  7. package/dist/i18n/bridge/ja.json +6 -0
  8. package/dist/i18n/bridge/ko.json +6 -0
  9. package/dist/i18n/bridge/zh-CN.json +6 -0
  10. package/dist/i18n/bridge/zh-TW.json +6 -0
  11. package/dist/i18n/shell/de.json +5 -1
  12. package/dist/i18n/shell/en.json +5 -1
  13. package/dist/i18n/shell/es.json +5 -1
  14. package/dist/i18n/shell/fr.json +5 -1
  15. package/dist/i18n/shell/it.json +5 -1
  16. package/dist/i18n/shell/ja.json +5 -1
  17. package/dist/i18n/shell/ko.json +5 -1
  18. package/dist/i18n/shell/zh-CN.json +5 -1
  19. package/dist/i18n/shell/zh-TW.json +5 -1
  20. package/dist/index.js +2492 -2338
  21. package/dist/src/bridge/i18n.d.ts +5 -0
  22. package/dist/src/context.d.ts +23 -2
  23. package/dist/src/delegate/root.d.ts +40 -0
  24. package/dist/src/index.d.ts +5 -2
  25. package/dist/src/protocol/v1.d.ts +49 -4
  26. package/dist/src/shell/AppShell.vue.d.ts +31 -10
  27. package/dist/src/shell/app-pages.d.ts +37 -0
  28. package/dist/src/shell/i18n.d.ts +4 -0
  29. package/dist/src/shell/pages.d.ts +7 -2
  30. package/dist/src/shell/slot-content.d.ts +2 -0
  31. package/dist/src/standalone.d.ts +5 -0
  32. package/dist/src/vue.d.ts +1 -0
  33. package/dist/style.css +1 -1
  34. package/dist/vue.js +1639 -1510
  35. package/package.json +4 -4
  36. package/spec/protocol/v1/README.md +154 -3
  37. package/spec/protocol/v1/errors.json +36 -4
  38. package/spec/protocol/v1/schema/root.jobs.get.json +19 -0
  39. package/spec/protocol/v1/schema/root.jobs.list.json +11 -0
  40. package/spec/protocol/v1/schema/root.jobs.stop.json +19 -0
  41. package/spec/protocol/v1/schema/root.run.json +6 -2
  42. package/spec/protocol/v1/schema/root.start.json +23 -0
  43. package/spec/protocol/v1/vectors/files.jsonl +11 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synopackageland/sdk",
3
- "version": "0.9.9",
3
+ "version": "0.10.0",
4
4
  "description": "Frontend SDK for DSM packages built with synopkgland: headless client and Vue components",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/synopackageland/syno-pkg-land-sdk/tree/main/ts#readme",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "files": [
32
32
  "dist",
33
- "spec"
33
+ "spec/protocol"
34
34
  ],
35
35
  "scripts": {
36
36
  "prepack": "test -d node_modules || { echo '@synopackageland/sdk: run npm ci before npm pack' >&2; exit 1; }; npm run build",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@lucide/vue": "^1.48.0",
43
- "@synopackageland/ui": "^0.9.3",
43
+ "@synopackageland/ui": "^0.9.4",
44
44
  "vue": "^3.5.0"
45
45
  },
46
46
  "peerDependenciesMeta": {
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@lucide/vue": "1.48.0",
56
- "@synopackageland/ui": "0.9.3",
56
+ "@synopackageland/ui": "0.9.4",
57
57
  "@types/node": "24.19.2",
58
58
  "@vitejs/plugin-vue": "6.0.9",
59
59
  "jsdom": "27.4.0",
@@ -22,8 +22,8 @@ the executor against the vectors here.
22
22
  accept a connection only from the package user or root (peer credentials).
23
23
  2. A connection carries exactly one request and one response.
24
24
  3. A request is one line of UTF-8 JSON ending in `\n`. A request line MUST NOT
25
- exceed 64 KiB including the newline; the executor answers
26
- `REQUEST_TOO_LARGE`.
25
+ exceed 64 KiB including the newline, or 1 MiB for `root.run` and
26
+ `root.start`; the executor answers `REQUEST_TOO_LARGE`.
27
27
  4. The executor MUST answer one line of JSON ending in `\n` and close.
28
28
  5. The executor SHOULD close a connection that sends nothing for 15 seconds.
29
29
 
@@ -114,6 +114,157 @@ packages.
114
114
  own files and DSM's generated conf), `unregistered`, or `cleanup-required`
115
115
  (anything else, including a failed change).
116
116
 
117
+ ## Root programs
118
+
119
+ Since 0.10.0. Each `root` entry pins a program by its SHA-256 digest; a bare
120
+ digest is a script `root.run` may run with the defaults. The object form is
121
+ `{"digest", "kind", "schema", "run"}`: `kind` `script` (`root/<name>.sh`) or
122
+ `binary` (`root/<name>`), `schema` a closed JSON Schema subset for its params,
123
+ `run` its `timeout_s` (1–120, default 20) and `max_output` (≤ 64 MiB,
124
+ default 1 MiB), `job` its background limits: `timeout_s` (1–604800, default
125
+ 86400), `grace_s` (1–60, default 10), `max_concurrent` (1–4, default 1) and
126
+ `nice` (0–19, default 10). `executor/README.md` lists the schema keywords.
127
+ `root.run` needs `run`, `root.start` needs `job`; an entry may have both.
128
+
129
+ Every root program, whatever starts it:
130
+
131
+ - runs as root in `/`, in its own process group, without core dumps, with
132
+ stdin the params JSON (`{}` without params) followed by EOF;
133
+ - gets only `PATH=/usr/syno/bin:/usr/syno/sbin:/usr/bin:/bin`, `LANG=C`,
134
+ `HOME=/` and these variables:
135
+
136
+ | Variable | Set for | Value |
137
+ |---|---|---|
138
+ | `SYNOPKGLAND_PACKAGE` | all | the package name |
139
+ | `SYNOPKGLAND_ROOT_MODE` | all | `run`, `job`, `stop` or `purge` |
140
+ | `SYNOPKGLAND_REQUESTED_BY` | run, job | the verified DSM user name |
141
+ | `SYNOPKGLAND_REQUEST_ID` | run | 64 hex digits, unique per call |
142
+ | `SYNOPKGLAND_JOB_ID` | job | the job's `job_id` |
143
+ | `SYNOPKGLAND_CONTROL_FD` | job | `4` |
144
+ | `SYNOPKGLAND_PROGRESS_FD` | job | `5` |
145
+ | `SYNOPKGLAND_ROOT_STATE` | all, with `root_state` | the root state directory |
146
+
147
+ - `root.run` MUST require an administrator session whose Application
148
+ Privileges allow the App, and an entry with `run`; params outside the
149
+ schema, or any params without one, are `INVALID_REQUEST`.
150
+ - The executor MUST give the params to the program only as JSON on stdin,
151
+ and MUST set `SYNOPKGLAND_REQUESTED_BY` to the verified user name. The
152
+ environment holds nothing else from the caller.
153
+ - It MUST execute the descriptor whose content it checked against the digest,
154
+ never the path again.
155
+ - A program that exits, whatever its status, is a result: `request_id`,
156
+ `exit_code`, `stderr` and either `output` (UTF-8, in a reply under 64 KiB)
157
+ or `staging` and `size`, a package-owned staging file the client reads and
158
+ removes. Past the timeout the process group is killed (`ROOT_TIMEOUT`);
159
+ output past `max_output` is `limit-exceeded`; a full staging quota is
160
+ `ROOT_STAGING_FULL`; two running programs make a third `ROOT_BUSY`.
161
+ - A `root.run` request MAY be up to 1 MiB; the executor answers within 130
162
+ seconds and a client SHOULD wait 140.
163
+ - `root.run` cannot be canceled: a closed connection does not stop the
164
+ program, which runs until it exits or its timeout. Work that may be stopped
165
+ is a root job (`root.start`, `root.jobs.stop`).
166
+
167
+ ## Root jobs
168
+
169
+ Since 0.10.0. `root.start`, `root.jobs.list`, `root.jobs.get` and
170
+ `root.jobs.stop` come together in `operations`. Each requires an administrator
171
+ session whose Application Privileges allow the App, like `root.run`; any such
172
+ administrator sees and stops every job of the package.
173
+
174
+ | op | params | result |
175
+ |---|---|---|
176
+ | `root.start` | `name`, `params` (object, optional) | `{"job_id", "state": "running"}` |
177
+ | `root.jobs.list` | none | `{"jobs": [summary, ...]}`, newest first |
178
+ | `root.jobs.get` | `job_id` | job |
179
+ | `root.jobs.stop` | `job_id` | job, `stopping` or already ended |
180
+
181
+ A job is:
182
+
183
+ ```json
184
+ {
185
+ "job_id": "<64 hex digits>",
186
+ "name": "clean",
187
+ "state": "running",
188
+ "requested_by": "alice",
189
+ "started_at": "2026-10-13T05:00:00Z",
190
+ "ended_at": "2026-10-13T05:03:12Z",
191
+ "exit_code": 0,
192
+ "cancel_reason": "user",
193
+ "progress": {"percent": 40, "message": "scanning", "phase": "scan"},
194
+ "result": {"freed": 3},
195
+ "output": "first 4 KiB of stdout and stderr"
196
+ }
197
+ ```
198
+
199
+ - `state` is `running`, `stopping` (a cancel was sent), or an end:
200
+ `succeeded` (exit 0), `failed` (another exit or a signal), `canceled` (it
201
+ ended after a cancel, however it exited), `timed-out` or `interrupted`
202
+ (the executor ended without seeing it end). `ended_at`, `exit_code` (an
203
+ exit, not a signal) and `cancel_reason` appear once they apply; `progress`
204
+ and `result` once the job reported them. `output` is valid UTF-8.
205
+ - A summary is a job without `result`, `output` and the progress `message`
206
+ and `phase`, so the 64 records fit a reply; `root.jobs.get` answers the
207
+ whole job.
208
+ - `root.start` MUST refuse with `ROOT_BUSY` while the entry runs
209
+ `max_concurrent` jobs or the package runs four. Params follow the entry's
210
+ schema as for `root.run`. An entry without `job` is `POLICY_DENIED`.
211
+ - An unknown `job_id` is `ROOT_JOB_NOT_FOUND`. The executor keeps the 64
212
+ newest records; older ones are removed.
213
+ - `root.jobs.stop` of an ended job returns it unchanged.
214
+ - Jobs do not survive the executor: when it stops, every running job is
215
+ canceled, and a job a crashed executor left is `interrupted` (its process
216
+ group is killed when the same process still runs).
217
+
218
+ ### Progress and cancel
219
+
220
+ The job reports on fd 5 and is told to stop on fd 4; both are pipes.
221
+
222
+ - **Progress** (fd 5, job to executor): JSON lines of at most 32 KiB, each an
223
+ object with any of `percent` (number 0–100), `message` (string, at most 512
224
+ characters), `phase` (string, at most 64 characters) and `result` (any JSON,
225
+ at most 16 KiB encoded). Each member present replaces the previous one; a
226
+ line with anything else is ignored whole. `result` is the job's answer for
227
+ its caller, kept after it ends.
228
+ - **Cancel** (fd 4, executor to job): one line `{"cancel":"<reason>"}`, with
229
+ reason `user` (`root.jobs.stop`), `stop` (package stop), `upgrade`
230
+ (package upgrade), `purge` (uninstall) or `executor-stop`. EOF on fd 4
231
+ means the executor ended: the job should stop too. The job ends
232
+ gracefully; `grace_s` after the cancel (at most 15 seconds on package stop,
233
+ upgrade and uninstall) the executor kills its process group.
234
+ - **Timeout**: `timeout_s` after the start the process group is killed
235
+ without a cancel (`timed-out`).
236
+ - A job MAY also keep its own progress files (for example in
237
+ `SYNOPKGLAND_ROOT_STATE`) for its App to read; the executor does not
238
+ interpret them.
239
+ - Clients poll `root.jobs.get`; each answer is the job's latest progress. A
240
+ bridge MAY turn successive answers into progress events.
241
+
242
+ ## Package stop step
243
+
244
+ Since 0.10.0. A policy MAY carry `"stop": "<name>"`, naming a `root` entry the
245
+ executor runs when the package stops, after its jobs ended, with no session.
246
+ Its argument is `prepare-upgrade` when the package account wrote `upgrade`
247
+ to `var/executor/stop-reason` before the stop request (the
248
+ `executor/lifecycle stop --upgrade` helper does; DSM's `preupgrade` should
249
+ use it), and `stop` otherwise; jobs are canceled with `upgrade` or `stop`
250
+ accordingly. It runs for at most 30 seconds, only while the approval is live
251
+ (as for the purge step), its exit and output go to the executor log, and a
252
+ failure does not stop the stop. `root.run` and `root.start` refuse it with
253
+ `POLICY_DENIED`.
254
+
255
+ ## Root state directory
256
+
257
+ Since 0.10.0. A policy with `"root_state": true` gives its root programs a
258
+ directory only root can read: `/volumeN/@synopkgland/<package>`, root:root
259
+ 0700, on the volume that holds the package's `var` (DSM's
260
+ `/volumeN/@appdata/<package>`); `/volumeN/@synopkgland` is root:root 0755.
261
+ The executor creates it at start and passes it as `SYNOPKGLAND_ROOT_STATE`;
262
+ when the package `var` is not on a volume it MUST NOT start root programs
263
+ that need it (`ROOT_FAILED`). The directory is outside every package
264
+ directory, so upgrades keep it. At uninstall the purge step sees it; after
265
+ the step the executor deletes it for `delete-state` and keeps it for
266
+ `keep-state`, so a reinstall on the same volume finds it again.
267
+
117
268
  ## Uninstall purge step
118
269
 
119
270
  Since 0.9.7. A policy MAY carry `"purge": "<name>"`, naming one of its `root`
@@ -131,7 +282,7 @@ runs as root at uninstall, with no session.
131
282
  skip the op and log `purge root step skipped: not approved`.
132
283
  - The op gets the argument `delete-state` when the package account wrote
133
284
  `delete-state` to `var/executor/uninstall` before its request (the
134
- `executor/lifecycle purge --delete-state` helper does), and no argument
285
+ `executor/lifecycle purge --delete-state` helper does), and `keep-state`
135
286
  otherwise. It runs for at most 60 seconds; its exit status and output go to
136
287
  the purge log, and a failure MUST NOT stop the rest of the purge.
137
288
 
@@ -151,8 +151,40 @@
151
151
  "kind": "refusal",
152
152
  "retryable": false,
153
153
  "httpStatus": 500,
154
- "en": "The root operation exited with a non-zero status or failed to run.",
155
- "zh-TW": "root 操作以非零狀態結束,或無法執行。"
154
+ "en": "The root program could not run, ended by a signal, or its output could not be kept.",
155
+ "zh-TW": "root 程式無法執行、被訊號終止,或其輸出無法保存。"
156
+ },
157
+ {
158
+ "code": "ROOT_TIMEOUT",
159
+ "kind": "refusal",
160
+ "retryable": true,
161
+ "httpStatus": 504,
162
+ "en": "The root program ran past its policy timeout and was killed.",
163
+ "zh-TW": "root 程式超過 policy 的時限,已被終止。"
164
+ },
165
+ {
166
+ "code": "ROOT_BUSY",
167
+ "kind": "refusal",
168
+ "retryable": true,
169
+ "httpStatus": 429,
170
+ "en": "The package already runs as many root programs as it may.",
171
+ "zh-TW": "此套件同時執行的 root 程式已達上限。"
172
+ },
173
+ {
174
+ "code": "ROOT_STAGING_FULL",
175
+ "kind": "refusal",
176
+ "retryable": true,
177
+ "httpStatus": 507,
178
+ "en": "Root output waiting in the staging folder already fills its quota.",
179
+ "zh-TW": "staging 資料夾中等待取走的 root 輸出已達配額。"
180
+ },
181
+ {
182
+ "code": "ROOT_JOB_NOT_FOUND",
183
+ "kind": "refusal",
184
+ "retryable": false,
185
+ "httpStatus": 404,
186
+ "en": "No root job of the package has this job_id.",
187
+ "zh-TW": "此套件沒有這個 job_id 的 root 工作。"
156
188
  },
157
189
  {
158
190
  "code": "PORTAL_FAILED",
@@ -335,8 +367,8 @@
335
367
  "kind": "refusal",
336
368
  "retryable": false,
337
369
  "httpStatus": 413,
338
- "en": "The file is larger than max_bytes.",
339
- "zh-TW": "檔案超過 max_bytes。"
370
+ "en": "The file is larger than max_bytes, or root output is larger than its policy max_output.",
371
+ "zh-TW": "檔案超過 max_bytes,或 root 輸出超過 policy 的 max_output。"
340
372
  },
341
373
  {
342
374
  "code": "OPERATION_CANCELED",
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/synopackageland/syno-pkg-land-sdk/spec/protocol/v1/schema/root.jobs.get.json",
4
+ "title": "root.jobs.get",
5
+ "description": "Administrator whom Application Privileges allow to use the App: one root job with its state, latest progress and result.",
6
+ "since": "0.10.0",
7
+ "auth": "required",
8
+ "type": "object",
9
+ "properties": {
10
+ "job_id": {
11
+ "type": "string",
12
+ "pattern": "^[a-f0-9]{64}$"
13
+ }
14
+ },
15
+ "additionalProperties": false,
16
+ "required": [
17
+ "job_id"
18
+ ]
19
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/synopackageland/syno-pkg-land-sdk/spec/protocol/v1/schema/root.jobs.list.json",
4
+ "title": "root.jobs.list",
5
+ "description": "Administrator whom Application Privileges allow to use the App: list the package's root jobs, newest first.",
6
+ "since": "0.10.0",
7
+ "auth": "required",
8
+ "type": "object",
9
+ "properties": {},
10
+ "additionalProperties": false
11
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/synopackageland/syno-pkg-land-sdk/spec/protocol/v1/schema/root.jobs.stop.json",
4
+ "title": "root.jobs.stop",
5
+ "description": "Administrator whom Application Privileges allow to use the App: ask a running root job to cancel; it is killed after its grace.",
6
+ "since": "0.10.0",
7
+ "auth": "required",
8
+ "type": "object",
9
+ "properties": {
10
+ "job_id": {
11
+ "type": "string",
12
+ "pattern": "^[a-f0-9]{64}$"
13
+ }
14
+ },
15
+ "additionalProperties": false,
16
+ "required": [
17
+ "job_id"
18
+ ]
19
+ }
@@ -2,14 +2,18 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://github.com/synopackageland/syno-pkg-land-sdk/spec/protocol/v1/schema/root.run.json",
4
4
  "title": "root.run",
5
- "description": "Administrator: run a root operation pinned by digest in the policy.",
5
+ "description": "Administrator whom Application Privileges allow to use the App: run a root program pinned by digest in the policy. Params reach the program on stdin as JSON, checked against the entry's schema; the reply carries its exit code, stderr and output, or a staging file for large output.",
6
6
  "since": "0.9.0",
7
7
  "auth": "required",
8
8
  "type": "object",
9
9
  "properties": {
10
10
  "name": {
11
11
  "type": "string",
12
- "minLength": 1
12
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$"
13
+ },
14
+ "params": {
15
+ "type": "object",
16
+ "description": "The program's params; without a schema in the policy only {} is accepted."
13
17
  }
14
18
  },
15
19
  "additionalProperties": false,
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/synopackageland/syno-pkg-land-sdk/spec/protocol/v1/schema/root.start.json",
4
+ "title": "root.start",
5
+ "description": "Administrator whom Application Privileges allow to use the App: start a root job, a pinned program with job limits, in the background. Params reach it on stdin; it reports progress on fd 5 and is told to cancel on fd 4.",
6
+ "since": "0.10.0",
7
+ "auth": "required",
8
+ "type": "object",
9
+ "properties": {
10
+ "name": {
11
+ "type": "string",
12
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$"
13
+ },
14
+ "params": {
15
+ "type": "object",
16
+ "description": "The job's params; without a schema in the policy only {} is accepted."
17
+ }
18
+ },
19
+ "additionalProperties": false,
20
+ "required": [
21
+ "name"
22
+ ]
23
+ }
@@ -7,5 +7,16 @@
7
7
  {"name":"move-missing-size","request":{"v":1,"op":"files.moveFromStaging","params":{"delegation_id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job_id":"export-20261009","staging":"exports/report.csv","name":"report.csv"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{"grants":[{"id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","username":"alice","key":"save","path":"/home/exports"}]},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
8
8
  {"name":"operation-status-unknown","request":{"v":1,"op":"files.operationStatus","params":{"delegation_id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job_id":"export-20261009"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{"grants":[{"id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","username":"alice","key":"save","path":"/home/exports"}]},"expect":{"ok":false,"code":"operation-not-found"}}
9
9
  {"name":"root-run-unpinned","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"whoami"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{},"expect":{"ok":false,"code":"POLICY_DENIED"}}
10
+ {"name":"root-run-params-outside-schema","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"scan","params":{"mode":"slow"}}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{"scan":{"digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","schema":{"type":"object","additionalProperties":false,"properties":{"mode":{"type":"string","enum":["fast","full"]}},"required":["mode"]},"run":{}}}},"state":{},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
11
+ {"name":"root-run-params-unknown-member","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"scan","params":{"mode":"fast","path":"/"}}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{"scan":{"digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","schema":{"type":"object","additionalProperties":false,"properties":{"mode":{"type":"string","enum":["fast","full"]}},"required":["mode"]},"run":{}}}},"state":{},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
12
+ {"name":"root-run-params-without-schema","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"scan","params":{"mode":"fast"}}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{"scan":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}},"state":{},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
13
+ {"name":"root-run-job-only-entry","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"scan"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{"scan":{"digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job":{}}}},"state":{},"expect":{"ok":false,"code":"POLICY_DENIED"}}
10
14
  {"name":"root-run-purge-step","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"purge"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{"purge":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},"purge":"purge"},"state":{},"expect":{"ok":false,"code":"POLICY_DENIED"}}
11
15
  {"name":"root-run-bad-name","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"../x"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
16
+ {"name":"root-start-run-only-entry","request":{"v":1,"op":"root.start","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"scan"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run","root.start","root.jobs.list","root.jobs.get","root.jobs.stop"],"destinations":["save"],"lifetime":"until-revoked","root":{"scan":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}},"state":{},"expect":{"ok":false,"code":"POLICY_DENIED"}}
17
+ {"name":"root-start-params-outside-schema","request":{"v":1,"op":"root.start","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"clean","params":{"days":0}}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run","root.start","root.jobs.list","root.jobs.get","root.jobs.stop"],"destinations":["save"],"lifetime":"until-revoked","root":{"clean":{"digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","schema":{"type":"object","additionalProperties":false,"properties":{"days":{"type":"integer","minimum":1}}},"job":{}}}},"state":{},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
18
+ {"name":"root-jobs-list-empty","request":{"v":1,"op":"root.jobs.list","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run","root.start","root.jobs.list","root.jobs.get","root.jobs.stop"],"destinations":["save"],"lifetime":"until-revoked","root":{"clean":{"digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job":{}}}},"state":{},"expect":{"ok":true,"result":{"jobs":[]}}}
19
+ {"name":"root-jobs-get-unknown","request":{"v":1,"op":"root.jobs.get","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"job_id":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run","root.start","root.jobs.list","root.jobs.get","root.jobs.stop"],"destinations":["save"],"lifetime":"until-revoked","root":{"clean":{"digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job":{}}}},"state":{},"expect":{"ok":false,"code":"ROOT_JOB_NOT_FOUND"}}
20
+ {"name":"root-jobs-stop-unknown","request":{"v":1,"op":"root.jobs.stop","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"job_id":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run","root.start","root.jobs.list","root.jobs.get","root.jobs.stop"],"destinations":["save"],"lifetime":"until-revoked","root":{"clean":{"digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job":{}}}},"state":{},"expect":{"ok":false,"code":"ROOT_JOB_NOT_FOUND"}}
21
+ {"name":"root-jobs-get-bad-id","request":{"v":1,"op":"root.jobs.get","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"job_id":"../x"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run","root.start","root.jobs.list","root.jobs.get","root.jobs.stop"],"destinations":["save"],"lifetime":"until-revoked","root":{"clean":{"digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job":{}}}},"state":{},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
22
+ {"name":"root-jobs-list-not-admin","request":{"v":1,"op":"root.jobs.list","auth":{"dsm_cookie":"alice","dsm_synotoken":"contract-token"},"params":{}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run","root.start","root.jobs.list","root.jobs.get","root.jobs.stop"],"destinations":["save"],"lifetime":"until-revoked","root":{"clean":{"digest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job":{}}}},"state":{},"expect":{"ok":false,"code":"ADMIN_REQUIRED"}}