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