@synopackageland/templates
v0.17.10
Published
Project templates for synopkgland init: native-go, native-python, native-node and native-rust (plain or Vue, with jobs, delegate and root-ops overlays), binary and compose.
Readme
@synopackageland/templates
Project templates for synopkgland init and synopkgland add. They hold only
the files an App owns; the SDK (github.com/synopackageland/syno-pkg-land-sdk, npm
@synopackageland/sdk) is a dependency of the generated project, never a copy.
An opt-in Shared Folder permission example shows the Go status-handler registration and a Vue notice for startup checks and storage failures. It targets the next SDK minor release and leaves default scaffold versions unchanged.
| Template | What it adds |
| --- | --- |
| native-go | Base: dsm-package.yaml, Go module (backend/cmd/{app,background-worker}, backend/internal/app, backend/internal/api/permissions.json), plain ui/, i18n |
| native-go-vue | Vue shell in frontend/ (App.vue, main.ts, app i18n, CapabilityPanels.vue, ExecutorGate.vue) |
| native-go-jobs | Background job routes in internal/app and their page test |
| native-go-delegate | Delegate routes, the delegate background worker, the delegate CapabilityPanels.vue |
| native-go-root-ops | The SDK's executor routes and root.run in internal/app, an example root/whoami.sh, and the Vue ExecutorGate.vue that holds the pages until the executor is approved |
| native-python | Base: dsm-package.yaml (backend.runtime: python), the App in backend/app (routes.py, stub capability modules, permissions.json), unittest tests |
| native-node | Base: dsm-package.yaml (backend.runtime: node), TypeScript in backend/src compiled into backend/app, node:test tests, package-lock.json |
| native-rust | Base: dsm-package.yaml (backend.runtime: rust, methods), the Cargo project in backend on the syno-pkg-land-app crate, Cargo.lock, rust-toolchain.toml |
| native-{python,node,rust}-{jobs,root-ops,delegate} | The capability's example module, replacing the base's stub, with its tests |
| native-{python,node,rust}-vue | The README and .gitignore of the Vue shell |
| binary | A prebuilt static CGI (example/main.go) with a plain UI |
| compose | Container Manager project (upstream compose.yaml, schema-2 dsm-package.yaml) |
| _shared | Agent guides, template license, Go license texts, third-party notice writer, dsm-scripts/ (DSM lifecycle scripts) |
Overlays are copied over native-go in the order root-ops, vue, jobs, delegate.
An overlay may replace internal/app/*_none.go (the CLI deletes the matching
_none.go file); nothing outside internal/app and the Vue shell differs.
The Python, Node.js and Rust templates borrow the Go template's UI: ui/ and
i18n/ from native-go, frontend/ from native-go-vue (and the jobs and
delegate overlays' frontend files), root/whoami.sh from native-go-root-ops.
Their overlays are copied in the order vue, root-ops, jobs, delegate; delegate
also makes app.status and report.save authenticated, and the Rust
manifest gets the overlays' methods. init copies the App SDK into
backend/vendor (Python synopkgland_app; Node @synopackageland/app, its
package.json without devDependencies and scripts, and dist/).
_shared/dsm-scripts/ holds DSM's lifecycle scripts (preinst, postinst, preupgrade,
postupgrade, preuninst, postuninst, start-stop-status), each commented and doing
nothing; init (CLI 0.10.3 and later) copies them into every project's dsm-scripts/, and build
calls each one from the generated script of the same name (synopkgland skill show lifecycle).
The placeholders __PACKAGE__ (also the Go module path), __DISPLAY_NAME__ and
__BACKGROUND_ENABLED__ are substituted by init.
backend/go.mod requires github.com/synopackageland/syno-pkg-land-sdk v0.17.7; that line is
the single source of the SDK version and frontend/package.json pins
@synopackageland/sdk to the same version. Python and Node.js name it in
backend.sdk, Rust in the syno-pkg-land-app tag of backend/Cargo.toml
(and Cargo.lock); all of them pin 0.17.7. The Vue frontend pins
@synopackageland/ui 0.12.4. SDK 0.17.7 accepts UI >=0.10.1 <0.13.0,
so no UI peer override is needed. Tests use jsdom 30 for UI floating-layer styles.
Notice writer
init copies the writer to frontend/third-party-notices.mjs. Tools that keep
their own copy of the templates import it from the package instead; no other
path is exported:
import {writeThirdPartyNotices} from '@synopackageland/templates/notices'
writeThirdPartyNotices('frontend', 'ui/LICENSES', {ui: true})It replaces only THIRD_PARTY_NOTICES.txt, inventory.json, UI-LICENSE
and UI-THIRD_PARTY.md in the output directory; other files there stay.
Develop
export GOPRIVATE='github.com/synopackageland/*' # the SDK repository is private
export SYNOPKGLAND_SDK_DIR=~/Public/synopackageland/sdk # optional sdk checkout: Go module at the root, ts/
export SYNOPKGLAND_DOCKER_SSH=my-docker-host # optional: the Docker engine for the Linux tests
scripts/assemble.sh /tmp/app vue delegate # one combination, as init lays it out
scripts/assemble.sh --language rust /tmp/app-rs plain jobs # python, node or rust
scripts/gate.sh # layout, every combination, binary, npm pack
scripts/gate.sh --fast # only the combinations the changes reach
scripts/assemble.sh --list vue jobs # the templates a combination usesWithout SYNOPKGLAND_SDK_DIR the combinations use the released SDK that
backend/go.mod and frontend/package.json pin. With it, assemble.sh adds
replace github.com/synopackageland/syno-pkg-land-sdk => $SYNOPKGLAND_SDK_DIR
and installs $SYNOPKGLAND_SDK_DIR/ts as an npm pack tarball (not a link, so Vue
resolves once). Set SYNOPKGLAND_SDK_TARBALL to a published tarball to test that
release without rebuilding the SDK frontend. With SYNOPKGLAND_UI_TARBALL set it installs that tarball as
@synopackageland/ui, for a UI version not yet on npm.
The Python and Node.js combinations need SYNOPKGLAND_SDK_DIR (its python/,
and node/ built with npm ci && npm run build) for their App SDK, and the
Rust ones fetch the SDK crate from it (scripts/rust-env.sh), so it must hold
the tag Cargo.lock pins. The gate runs each language's tests on Linux in
Docker (scripts/bridge-linux-test.sh: Python 3.8, Node.js 18, Rust).
CI is not available; scripts/gate.sh is the gate.
Use scripts/gate.sh --fast while developing: it tests only the combinations
that use a template changed since origin/main (SYNOPKGLAND_GATE_BASE),
committed or not, and prints the ones it skipped; a change under scripts/
tests them all. Run the full gate once before a release. The gate runs each
phase's jobs in parallel, SYNOPKGLAND_GATE_JOBS (default 3) at a time, and
prints a failed job's whole log. On SYNOPKGLAND_DOCKER_SSH the Rust checks
run in synopkgland-gate-templates:<hash> (scripts/gate.Dockerfile) with
the host's shared synopkgland-cache-cargo volume, and wait for one of its
two gate slots. There the Vue frontends' checks (scripts/frontend-check.sh:
npm install, vitest, the build) run too, in the official node:24.20.0 image
with the shared synopkgland-cache-npm volume
(scripts/frontend-docker-checks.sh; SYNOPKGLAND_DOCKER_NPM_REGISTRY to select an alternate registry), so the Mac's load cannot slow them.
Without SYNOPKGLAND_DOCKER_SSH they run here.
SDK usage, protocol and API documentation: https://github.com/synopackageland/syno-pkg-land-sdk.
License
MIT, see LICENSE. Generated projects receive
templates/_shared/LICENSES/TEMPLATE.txt.
App launch mode
Native Apps declare ui.launch: dsm-window | new-page in dsm-package.yaml.
Use dsm-window for frontends built with @synopackageland/ui; use new-page
for every other frontend. Plain/binary scaffolds explicitly choose new-page; Vue uses the native default. An omitted
native ui.launch resolves to dsm-window; the CLI does not guess a frontend's
framework from its build command. new-page registers DSM type: "standalone",
so the main menu and Package Center open a full page on the DSM origin.
ctx.fullPage, ctx.appIcon, session/SynoToken, launch parameters and entry
permissions use the same wrapper in both modes. The wrapper sets the standalone
page title, favicon and device-width viewport. It retains ExtJS; pureEnv is
not appropriate, and alternate Portal ports are not needed (allowAltPort is omitted).
A full page does not need Application Portal. Omit portal unless an alias is
explicitly wanted; only that opt-in adds defaultAlias, login styling, Portal
panels and capabilities. No Portal is a healthy default for inspect/info/doctor.
Web service Apps declare integration.web.launch; its built-in default is new-page.
Native SDK Apps default to dsm-window; non-UI-SDK native frontends must
explicitly set ui.launch: new-page. Headless Compose Apps have no launcher.
The new-page main menu uses type: "url" with an explicit url at the existing trusted entry: the authenticated
gateway, or a Web Station alias only with explicit access: public. DSM rewrites standalone URLs to a launchApp shell, which needs an AppInstance
and AppWindow JS class. A schema-1 web service has no such class, so it uses url.
The schema-2 identity_gateway + compose SDK wrapper has the class and uses standalone.
Authenticated web Apps do not create a Web Station alias or Application Portal.
dsm-window remains an explicit web launch option with its URL launcher.
Schema 2 dsm-package.yaml with identity_gateway and compose is also a web
service: omitted ui.launch resolves to new-page, without Application Portal.
Both Compose routes share contract/app-launch.ts for defaults and launcher types.
An explicit ui.launch overrides this default. Native SDK Apps without a web
service keep dsm-window when ui.launch is absent.
Compose identity and file access
The CLI preserves upstream bytes and the SDK planner generates an override and one worker model. No dummy backend, UI or wrapper container is needed. Containers use the fixed package identity and do not inherit each signed-in user's File Station ACL. Shared Folder intent is explicit composeMounts, confirmed by an admin after install; activation is unsupported until official protected ACL/project control is verified. Fine egress allowlists are rejected; isolated/internet are the two declared levels. See the SDK Compose support matrix before deployment.
AI commands
All four native templates explicitly declare status and greeting commands in ctl.commands. Build packages a private synopkgland-api.json; only the authenticated SDK description endpoint returns it. Internal routes are not automatically commands. Read synopkgland skill show ctl before adding command schemas or workflow bindings.
The Vue scaffold batches independent startup status/greeting reads when the
window already identifies an administrator and supplies ctx.batch. Rebuild
with the current CLI and SDK to get that client. Older generated contexts keep
the single-call path. Each batch result is handled separately; the shell stays
visible while reads run. The backend permissions remain authoritative.
