dsm-package.yaml reference
Use dsm-package.yaml for a native-go or binary project. This example passes check in a native-go project created with --with files,jobs,login --package MyApp.
# Package identity and SPK version: 0.1.0-0001.schema: 2package: MyAppversion: "0.1.0"build: 1displayname: My Appdescription: A native DSM package with files, jobs, and login.maintainer: Your Nameos_min_ver: "7.1-42661"arches: [x86_64, aarch64]
# Go CGI backend. A binary project uses kind: binary and files instead.backend: kind: go dir: backend main: ./cmd/app
# Build the Vue frontend before copying its static output.ui: dir: frontend/dist build: cwd: frontend command: [npm, run, build]
# CLI login maps to the identity field.capabilities: files: session jobs: {worker: sha256-v1} identity: standalone # Add root scripts and capability code with synopkgland add root-ops. # rootOps: true # Add delegate code with synopkgland add delegate. # delegate: # scope: install-selected # operations: [files.moveFromStaging] # destinations: [save] # lifetime: until-revoked
# Catalogs and glossary are included in the generated project.i18n: messages: i18n/messages glossary: i18n/glossary.json info: zh-TW: displayname: 我的 App description: 提供檔案、背景工作與登入功能的 DSM 套件。
# Select an existing Shared Folder during installation.install: sharedFolder: mode: select key: shared permission: rw
# Optional publisher information.metadata: license: MIT contact_email: you@example.com homepage: https://example.com
# Optional entry policy; requires the Go entry gate and suitable API routes.# entry: app-privilege
# Enable with synopkgland add portal; requires rootOps or delegate and the portal DSM minimum.# portal: {alias: my-app}
# Optional foreground service; supply your own payload directories.# With jobs, this also requires an until-revoked delegate.# daemon:# exec: bin/service# targets: {x86_64: daemon/x86_64, aarch64: daemon/aarch64}# args: []# env: {}# timeouts: {start: 30, stop: 30, status: 5, uninstall: 60}# uninstall_args: [cleanup]Top-level fields
Section titled “Top-level fields”Defaults below are parser defaults; values supplied by templates are labelled separately. dsm-package.yaml and compose.yaml cannot coexist (PROJECT_RUNTIME_EXCLUSIVE). Unknown top-level keys raise STANDALONE_FIELD_UNKNOWN. See errors for fixes.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
schema | integer | Yes | — | Exactly 2; STANDALONE_SCHEMA_INVALID. |
package | string | Yes | — | Matches ^[A-Za-z][A-Za-z0-9]{1,63}$; STANDALONE_PACKAGE_INVALID. |
version | string | Yes | — | One to four numeric parts separated by dots; STANDALONE_VERSION_INVALID. Quote the value in YAML. |
build | integer | Yes | — | Range 1–9999; appended to version with four-digit padding; STANDALONE_BUILD_INVALID. |
displayname | string | Yes | — | Nonempty single line without double quotes or backslashes; STANDALONE_TEXT_INVALID. |
description | string | Yes | — | Same text rule as displayname; STANDALONE_TEXT_INVALID. |
maintainer | string | Yes | — | Same text rule as displayname; STANDALONE_TEXT_INVALID. |
os_min_ver | string | Yes | Template: 7.1-42661 | Full version/build matching ^\d+\.\d+(\.\d+)?-\d+$; STANDALONE_OS_MIN_VER_INVALID. Feature floors are in platforms. |
arches | string[] | Yes | Template: [x86_64, aarch64] | Nonempty unique list of x86_64, aarch64, armv7; STANDALONE_ARCHES_INVALID. |
backend | object | Yes | — | Go source or per-architecture CGI binaries; STANDALONE_BACKEND_INVALID. |
ui | object | Yes | — | Static output directory and optional build command; STANDALONE_UI_INVALID. |
icon | path | No | SDK default icon | Square PNG of at least 256×256, or a directory of icon_<size>.png (16, 24, 32, 48, 64, 72, 256; icon_256.png required, missing sizes scaled from it). SVG is not accepted. The build writes PACKAGE_ICON.PNG, PACKAGE_ICON_256.PNG and ui/images/app_<size>.png; each Package Center icon must stay within the package source’s 256 KiB limit. check and inspect report whether a build uses a custom or the default icon; STANDALONE_ICON_INVALID. |
capabilities | object | No | {} | Explicit capability declarations; invalid names or values raise CAPABILITY_INVALID. |
i18n | object | No | None | Message catalogs, glossary, and translated Package Center text; STANDALONE_I18N_INVALID. |
daemon | object | No | None | Fixed foreground service; STANDALONE_DAEMON_INVALID. |
install | object | No | None | Only sharedFolder is accepted; STANDALONE_SHARED_FOLDER_INVALID. |
entry | string | No | API permissions policy | admin-only or app-privilege; STANDALONE_ENTRY_INVALID. |
portal | object | No | Disabled | {} or {alias: ...}; STANDALONE_PORTAL_INVALID. Needs rootOps or delegate; STANDALONE_PORTAL_REQUIRES_EXECUTOR. |
login_style | object | No | CLI default style | Login Portal colour, wallpaper and title image; needs portal. See login_style; STANDALONE_LOGIN_STYLE_INVALID, STANDALONE_LOGIN_STYLE_IMAGE_INVALID. |
root | object | No | None | Settings of the root/ programs: programs.<name> (kind, params schema, run and job limits), stop (the program the executor runs at package stop and upgrade) and state: true (a root-only /volumeN/@synopkgland/<package> kept across upgrades). Needs capabilities.rootOps: true; STANDALONE_ROOT_PROGRAMS_INVALID. The App’s SDK executor must support them; EXECUTOR_ROOT_PROGRAMS_UNSUPPORTED. |
uninstall | object | No | None | purge (exec, args, env, timeout 2–120 s, optional root) runs as the package account on a real uninstall before the executor purge; state.items[].key lists the state the uninstall wizard may delete, each with an uninstall.state.<key> text. UNINSTALL_INVALID. |
metadata | object | No | None | Publisher text, links, and screenshots; invalid metadata raises STANDALONE_TEXT_INVALID. |
reproducible | object | No | None | Settings for build --verify-reproducible: inputs lists dependency files for the fingerprint, clean lists paths removed before the second build (may use {arch}). See reproducible builds; REPRODUCIBLE_INVALID. |
Use project-relative paths in the fields below.
| Rule | Validation error |
|---|---|
| Path must be a nonempty string. | STANDALONE_PATH_INVALID |
| Path must resolve inside the project and cannot be absolute. | STANDALONE_PATH_OUTSIDE_PROJECT |
backend
Section titled “backend”Choose one backend shape.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
kind | string | Yes | — | go or binary; STANDALONE_BACKEND_INVALID. Unknown backend keys raise the same code. |
dir | path | For Go | — | Source directory; path rules apply. Go API policy in backend/internal/api/permissions.json must be valid; STANDALONE_API_PERMISSIONS_INVALID. |
main | string | No; Go only | ./cmd/app | Matches ^\./[A-Za-z0-9_./-]*$ and contains no ..; STANDALONE_BACKEND_INVALID. |
files | map of architecture to path | For binary | — | Exactly the declared architectures; missing entry: STANDALONE_BINARY_ARCH_MISSING; extra entry: STANDALONE_BACKEND_INVALID; path rules apply. |
| Binary file rule | Validation error |
|---|---|
| File must exist. | STANDALONE_BINARY_MISSING |
| File must be regular, not a symlink or directory. | STANDALONE_BINARY_NOT_REGULAR |
| Resolved file must stay inside the project. | STANDALONE_PATH_OUTSIDE_PROJECT |
| Maximum size: 256 MiB. | STANDALONE_BINARY_TOO_LARGE |
| Little-endian Linux ELF with the correct bit width. | STANDALONE_BINARY_NOT_ELF |
| ELF machine must match the architecture. | STANDALONE_BINARY_ARCH_MISMATCH |
| Static linking; no dynamic interpreter. | STANDALONE_BINARY_DYNAMIC |
Build produces the static UI files that the package copies.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
dir | path | Yes | — | Must contain app.js and app.css at build time; STANDALONE_UI_ENTRY_MISSING; path rules apply. |
build | object | With identity | None | capabilities.identity requires a build declaration; CAPABILITY_COMBINATION_INVALID. |
build.cwd | path | With build | — | Working directory; path rules apply. |
build.command | string[] | With build | — | Nonempty array of nonempty strings, executed as arguments without a shell; STANDALONE_UI_BUILD_INVALID. |
capabilities
Section titled “capabilities”CLI login writes identity; CLI root-ops writes rootOps.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
files | string | No | Disabled | Only session; binary rejects this capability with CAPABILITY_UNSUPPORTED. Other values: CAPABILITY_INVALID. |
jobs | boolean or object | No | Disabled | true or {worker: sha256-v1}; binary rejects it with CAPABILITY_UNSUPPORTED. Invalid declaration: CAPABILITY_INVALID. |
identity | string | No | Disabled | Only standalone; requires a UI build declaration (CAPABILITY_COMBINATION_INVALID); binary: CAPABILITY_UNSUPPORTED; other values: CAPABILITY_INVALID. |
rootOps | boolean | No | Disabled | Only true; requires valid root/*.sh files. Other values: CAPABILITY_INVALID. Administrator approval is required to run operations. |
delegate | object | No | Disabled | Enables jobs automatically; invalid policy: CAPABILITY_INVALID; binary: CAPABILITY_UNSUPPORTED. Administrator approval is required. |
capabilities.jobs
Section titled “capabilities.jobs”Use true as shorthand for this worker declaration.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
worker | string | In object form | sha256-v1 with jobs: true | Exactly sha256-v1, with no other object keys; CAPABILITY_INVALID. |
capabilities.rootOps
Section titled “capabilities.rootOps”Place only fixed operation scripts in root/.
| Rule | Validation error |
|---|---|
| At least one script is required when enabled. | STANDALONE_ROOT_OPS_MISSING |
A root/ directory requires rootOps: true. | STANDALONE_ROOT_OPS_UNUSED |
Each filename matches ^[a-z][a-z0-9_]{0,31}\.sh$; only regular files, no symlinks. | STANDALONE_ROOT_OP_INVALID |
Each script is at most 16 KiB and starts with #!/bin/sh followed by a newline; valid UTF-8, no NUL bytes. | STANDALONE_ROOT_OP_INVALID |
Minimum DSM: 7.1-42661. | STANDALONE_OS_MIN_VER_INVALID |
capabilities.delegate
Section titled “capabilities.delegate”init --with delegate and add delegate write {scope: install-selected, operations: [files.moveFromStaging], destinations: [save], lifetime: until-revoked}. This is a CLI-generated declaration, not a parser default.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
scope | string | Yes | CLI: install-selected | home, shared:<name>, or install-selected. Shared names cannot contain slashes, backslashes, control characters, colons, edge whitespace, or equal . / ... install-selected requires until-revoked; CAPABILITY_INVALID. |
operations | string[] | Yes | CLI: [files.moveFromStaging] | Nonempty unique list. With until-revoked, exactly [files.moveFromStaging]. Without lifetime and destinations, choose from files.list, files.info, files.createFolder, files.checkPermission, files.write; CAPABILITY_INVALID. |
destinations | string[] | With until-revoked | CLI: [save] | 1–64 unique keys matching ^[a-z][a-z0-9_-]{0,63}$; CAPABILITY_INVALID. Omit when lifetime is omitted. |
lifetime | string | With destinations or moveFromStaging | CLI: until-revoked | Only until-revoked; omit when using the other listed file operations; CAPABILITY_INVALID. |
daemon
Section titled “daemon”Declare one fixed foreground executable and its architecture-specific payload directories.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
exec | string | Yes | — | Payload-relative path; at most 200 characters, no . / .. segments; first segment uses letters, digits, _, -, later segments also allow .; STANDALONE_DAEMON_INVALID. |
targets | map of architecture to path | Yes | — | Exactly one directory per declared architecture; relative, nonempty, within the project, no .. segments or NUL; STANDALONE_DAEMON_INVALID. |
args | string[] | No | [] | At most 64 strings, each at most 4096 bytes, no NUL; substitutions only ${SYNOPKG_PKGDEST} and ${SYNOPKG_PKGVAR}; STANDALONE_DAEMON_INVALID. |
uninstall_args | string[] | No | None | Same argument rules; must be nonempty if present; runs the fixed executable after stop on uninstall; STANDALONE_DAEMON_INVALID. |
env | map of string to string | No | {} | At most 64 entries; values use the argument string rules; STANDALONE_DAEMON_INVALID. |
env.<name> | string | No | — | Key matches ^[A-Z][A-Z0-9_]{0,63}$. Rejects PATH, HOME, TMPDIR, ENV, BASH_ENV, SHELLOPTS, BASHOPTS, GODEBUG, GOTRACEBACK, LD_*, DYLD_*, SYNOPKG_*; STANDALONE_DAEMON_INVALID. |
timeouts | object | No | {start: 30, stop: 30, status: 5, uninstall: 60} | Only the four keys below; STANDALONE_DAEMON_INVALID. |
timeouts.start | integer seconds | No | 30 | How long start waits for the daemon to stay up. Range 2–120; STANDALONE_DAEMON_INVALID. |
timeouts.stop | integer seconds | No | 30 | After SIGTERM, the daemon has stop - 1 seconds before SIGKILL. Range 2–120; STANDALONE_DAEMON_INVALID. |
timeouts.status | integer seconds | No | 5 | Range 1–10; STANDALONE_DAEMON_INVALID. |
timeouts.uninstall | integer seconds | No | 60 | How long uninstall_args may run on uninstall, after the stop, before SIGTERM. Independent of stop. Range 2–120; STANDALONE_DAEMON_INVALID. |
Choose the timeouts from what you measure on a loaded NAS, not an idle one. At boot many packages start together, and a busy volume can make a start take more than 5 seconds and a revoke more than 9 seconds. Time start, stop and the uninstall_args command while the NAS is busy (for example during a scrub or a large copy), then allow about twice the slowest time, within the ranges above. A higher value only makes DSM wait longer when something hangs; a daemon that is ready or exits quickly is not slowed down. DSM’s own package unit allows 3600 seconds for start and stop; a separate limit for preuninst is unknown.
| Combination or payload rule | Validation error |
|---|---|
Jobs may coexist with daemon only with delegate.lifetime: until-revoked. | CAPABILITY_COMBINATION_INVALID |
| Payload directories contain regular files and directories only, stay in the project, have no symlinks or setuid/setgid files, and total at most 512 MiB. | STANDALONE_DAEMON_PAYLOAD_INVALID |
| Executable must pass the binary file rules. | Binary errors listed above |
install.sharedFolder
Section titled “install.sharedFolder”Select an existing Shared Folder or create a named one; neither mode deletes the Shared Folder on uninstall.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
mode | string | Yes | — | select or create; STANDALONE_SHARED_FOLDER_INVALID. |
key | string | Yes | CLI init: shared | Matches ^[a-z][a-z0-9_]{0,31}$; cannot start with pkgwizard_, synopkg, or change_; STANDALONE_SHARED_FOLDER_INVALID. |
name | string | With create only | — | Matches ^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$; DSM system names such as homes, users, volume1, usbshare1 are refused. Forbidden with select; STANDALONE_SHARED_FOLDER_INVALID. |
permission | string | Yes | CLI init: rw | ro or rw; select mode needs an administrator to grant the package account this access after installation. Create mode grants it through DSM; STANDALONE_SHARED_FOLDER_INVALID. |
Catalog locales and DSM language codes are listed in platforms.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
messages | path | With glossary | None | Directory of <locale>.json catalogs; path rules apply. Missing English catalog: STANDALONE_I18N_MESSAGES_MISSING; unsupported locale: STANDALONE_I18N_LOCALE_UNSUPPORTED. |
glossary | path | No | None | JSON glossary; requires messages (STANDALONE_I18N_INVALID); path rules apply; invalid file: STANDALONE_I18N_GLOSSARY_INVALID. |
info | map of locale to object | No | {} | Supported locales except en; English uses top-level text. Unknown keys or locale: STANDALONE_I18N_INVALID. |
info.<locale>.displayname | string | No | Top-level text | Same single-line text rule as displayname; STANDALONE_TEXT_INVALID. |
info.<locale>.description | string | No | Top-level text | Same single-line text rule as description; STANDALONE_TEXT_INVALID. |
info also names the app in DSM. With at least one translation the build writes ui/texts/<DSM language>/strings for English and each translated locale and points the main-menu entry and window title at them. A locale without its own displayname or description shows the English text. Without info the title stays the literal English displayname.
| Catalog or glossary field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
| Catalog message key | string | Per message | — | Flat JSON object; dotted keys match ^[A-Za-z][A-Za-z0-9_-]*(\.[A-Za-z0-9_-]+)*$, values are strings; STANDALONE_I18N_CATALOG_INVALID. |
| Catalog key set | string keys | Yes | English keys | Exactly the en.json keys; STANDALONE_I18N_KEYS_MISMATCH. |
| Catalog placeholders | {placeholder} set | Yes | English placeholders | Same placeholders for each translated message; STANDALONE_I18N_PLACEHOLDER_MISMATCH. |
terms | object[] | Yes in glossary | — | JSON array; STANDALONE_I18N_GLOSSARY_INVALID. |
terms[].id | string | Yes | — | Term identifier; STANDALONE_I18N_GLOSSARY_INVALID. |
terms[].text | map of locale to string | Yes | — | Supported locales, nonempty text; STANDALONE_I18N_GLOSSARY_INVALID. |
terms[].keys | string[] | No | [] | Existing English message keys; STANDALONE_I18N_GLOSSARY_INVALID. Each listed translation must contain its locale’s term, ignoring case; STANDALONE_I18N_GLOSSARY_MISMATCH. |
Entry policy supplements your API permissions.
| Value or rule | Effect and validation error |
|---|---|
admin-only | Only administrators can enter. Administrators are the floor of every route, including the routes a capability adds, and the launcher is hidden from other users. A permissions.json route marked authenticated or public raises STANDALONE_ENTRY_WIDENED. |
| Any other value | STANDALONE_ENTRY_INVALID. |
app-privilege | Checks DSM Application Privileges; requires at least one authenticated route in permissions.json, otherwise STANDALONE_ENTRY_INVALID. |
| Omitted | Uses the API permissions policy. |
| Backend prerequisite | Go backend with the entry gate wired in main.go; missing support or a binary backend raises STANDALONE_ENTRY_UNSUPPORTED. |
portal
Section titled “portal”An empty object enables Application Portal with a generated alias.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
alias | string | No | Lowercase package ID, or app- plus a stable hash if reserved | 1–64 lowercase letters, digits, and hyphens; starts and ends with a letter or digit. Unknown portal keys or invalid alias: STANDALONE_PORTAL_INVALID. |
| Reserved alias | string | — | — | dsfile, indexdb, mobile, oauth, scripts, sharing, ssdp, synosdsjslib, synohdpack, webapi, webman, calendar, chatplus, contacts, drive, file, photo, sso, synologymeet are refused; STANDALONE_PORTAL_INVALID. |
| Minimum DSM | version/build | Yes when enabled | — | 7.1-42661; STANDALONE_PORTAL_MIN_DSM. |
| Executor | capability | Yes when enabled | — | capabilities.rootOps or capabilities.delegate; STANDALONE_PORTAL_REQUIRES_EXECUTOR. |
login_style
Section titled “login_style”A portal App carries a DSM login style: Control Panel > Login Portal > Applications shows a thumbnail, and the alias login page gets a wallpaper, a button colour and a title image. Without login_style the CLI uses its neutral default style with the App icon as the title. check and inspect print the style in use.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
color | string | No | #2F6BD8 | #RRGGBB; STANDALONE_LOGIN_STYLE_INVALID. |
wallpaper | path | No | CLI default wallpaper | JPEG inside the project, at least 1920×1080 and at most 2 MiB; set together with wallpaper_thumbnail. STANDALONE_LOGIN_STYLE_IMAGE_INVALID. |
wallpaper_thumbnail | path | With wallpaper | CLI default thumbnail | 320×200 JPEG inside the project; STANDALONE_LOGIN_STYLE_IMAGE_INVALID. |
title | path | No | App icon | 680×124 PNG inside the project; STANDALONE_LOGIN_STYLE_IMAGE_INVALID. |
login_style without portal, or any other key: STANDALONE_LOGIN_STYLE_INVALID.
metadata
Section titled “metadata”All metadata fields are optional; invalid fields or values raise STANDALONE_TEXT_INVALID.
| Field | Type | Required | Default | Rule and validation error |
|---|---|---|---|---|
long_description | string or locale map | No | None | Text rules below; STANDALONE_TEXT_INVALID. |
changelog | string or locale map | No | None | Text rules below; English map entry supplies INFO text, or the first entry if English is absent; STANDALONE_TEXT_INVALID. |
contact_email | string | No | None | Email address with a domain containing a dot, no whitespace; STANDALONE_TEXT_INVALID. |
support_email | string | No | None | Same email rule; supplies a mailto: support link when support_url is absent; STANDALONE_TEXT_INVALID. |
homepage | string | No | None | URL rules below; STANDALONE_TEXT_INVALID. |
source_url | string | No | None | URL rules below; STANDALONE_TEXT_INVALID. |
issues_url | string | No | None | URL rules below; STANDALONE_TEXT_INVALID. |
license | string | No | None | Text rules below; STANDALONE_TEXT_INVALID. |
maintainer_url | string | No | None | URL rules below; STANDALONE_TEXT_INVALID. |
distributor | string | No | None | Text rules below; STANDALONE_TEXT_INVALID. |
distributor_url | string | No | None | URL rules below; STANDALONE_TEXT_INVALID. |
support_url | string | No | None | HTTP(S) URL or valid mailto: address; STANDALONE_TEXT_INVALID. |
helpurl | string | No | None | URL rules below; STANDALONE_TEXT_INVALID. |
screenshots | string[] | No | None | At most 12 HTTP(S) URLs; STANDALONE_TEXT_INVALID. |
| Metadata rule | Limit and validation error |
|---|---|
| Text values | At most 16,000 characters; no control characters except tab, newline, and carriage return; STANDALONE_TEXT_INVALID. |
| Locale maps | At most 32 entries; keys match ^[a-z]{2}(-[A-Za-z]{2})?$; values use the text rules; STANDALONE_TEXT_INVALID. |
| URLs | HTTP(S), at most 2048 characters, no username or password; STANDALONE_TEXT_INVALID. |
| Entire object | JSON representation at most 32,000 characters; unknown keys are refused; STANDALONE_TEXT_INVALID. |