@synopackageland/cli 0.3.10 → 0.4.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/README.md +20 -9
- package/dist/bin.js +1 -2
- package/dist/build-spk/generator.d.ts +0 -3
- package/dist/build-spk/generator.js +135 -287
- package/dist/build-spk/native-lifecycle.js +0 -3
- package/dist/build-spk/native-payload.js +0 -1
- package/dist/cli.js +171 -151
- package/dist/compose/analyze.d.ts +1 -2
- package/dist/compose/analyze.js +5 -48
- package/dist/contract/unsupported-types.d.ts +10 -0
- package/dist/contract/unsupported-types.js +49 -0
- package/dist/contract/validate.js +25 -2
- package/dist/contract/web-launch.js +0 -2
- package/dist/deploy/deploy.d.ts +1 -4
- package/dist/deploy/deploy.js +45 -156
- package/dist/deploy/homes-values.js +1 -1
- package/dist/deploy/plan.d.ts +18 -0
- package/dist/deploy/plan.js +54 -0
- package/dist/deploy/spk-version.d.ts +2 -14
- package/dist/deploy/spk-version.js +25 -111
- package/dist/dev.d.ts +1 -3
- package/dist/dev.js +14 -28
- package/dist/error-registry.js +810 -862
- package/dist/host/dsm-client.js +12 -272
- package/dist/host/dsm-port-profile.d.ts +0 -1
- package/dist/host/dsm-port-profile.js +0 -2
- package/dist/host/types.d.ts +1 -83
- package/dist/index.d.ts +0 -9
- package/dist/index.js +0 -6
- package/dist/info.d.ts +0 -1
- package/dist/info.js +3 -10
- package/dist/init.d.ts +11 -1
- package/dist/init.js +112 -4
- package/dist/log.js +2 -3
- package/dist/native/validate.js +0 -1
- package/dist/standalone/binary.d.ts +9 -0
- package/dist/standalone/binary.js +67 -0
- package/dist/standalone/build.d.ts +25 -0
- package/dist/standalone/build.js +108 -0
- package/dist/standalone/dsm-window.d.ts +26 -0
- package/dist/standalone/dsm-window.js +251 -0
- package/dist/standalone/helper.d.ts +31 -0
- package/dist/standalone/helper.js +104 -0
- package/dist/standalone/i18n.d.ts +28 -0
- package/dist/standalone/i18n.js +161 -0
- package/dist/standalone/inspect-spk.d.ts +21 -0
- package/dist/standalone/inspect-spk.js +160 -0
- package/dist/standalone/manifest.d.ts +71 -0
- package/dist/standalone/manifest.js +276 -0
- package/dist/standalone/root-messages.d.ts +7 -0
- package/dist/standalone/root-messages.js +250 -0
- package/dist/standalone/root-ops.d.ts +42 -0
- package/dist/standalone/root-ops.js +101 -0
- package/dist/standalone/root-window.d.ts +35 -0
- package/dist/standalone/root-window.js +290 -0
- package/dist/standalone/spk.d.ts +22 -0
- package/dist/standalone/spk.js +205 -0
- package/dist/templates/binary/synology-app.yaml +7 -0
- package/dist/templates/hello-files/compose.yaml +0 -1
- package/dist/templates/hello-web/compose.yaml +13 -10
- package/dist/templates/hello-web/html/app.js +1 -23
- package/dist/templates/hello-web/html/index.html +1 -3
- package/dist/templates/hello-web-page/compose.yaml +13 -10
- package/dist/templates/hello-web-page/html/app.js +1 -23
- package/dist/templates/hello-web-page/html/index.html +1 -3
- package/dist/templates/native-go/README.md +268 -0
- package/dist/templates/native-go/backend/cmd/app/main.go +49 -0
- package/dist/templates/native-go/backend/cmd/helper/main.go +38 -0
- package/dist/templates/native-go/backend/go.mod +3 -0
- package/dist/templates/native-go/backend/internal/api/errors.go +33 -0
- package/dist/templates/native-go/backend/internal/api/registry.go +94 -0
- package/dist/templates/native-go/backend/internal/api/registry_test.go +329 -0
- package/dist/templates/native-go/backend/internal/api/server.go +155 -0
- package/dist/templates/native-go/backend/internal/app/root_none.go +9 -0
- package/dist/templates/native-go/backend/internal/app/routes.go +127 -0
- package/dist/templates/native-go/backend/internal/app/routes_test.go +167 -0
- package/dist/templates/native-go/backend/internal/dsm/auth.go +118 -0
- package/dist/templates/native-go/backend/internal/dsm/auth_test.go +139 -0
- package/dist/templates/native-go/backend/internal/helper/control.go +219 -0
- package/dist/templates/native-go/backend/internal/helper/control_test.go +242 -0
- package/dist/templates/native-go/backend/internal/helper/peer_linux.go +44 -0
- package/dist/templates/native-go/backend/internal/helper/peer_other.go +7 -0
- package/dist/templates/native-go/backend/internal/helper/process_linux.go +167 -0
- package/dist/templates/native-go/backend/internal/helper/process_other.go +5 -0
- package/dist/templates/native-go/backend/internal/helper/server_linux.go +83 -0
- package/dist/templates/native-go/backend/internal/helper/status.go +271 -0
- package/dist/templates/native-go/backend/internal/helper/status_test.go +205 -0
- package/dist/templates/native-go/backend/internal/helper/supervision.go +84 -0
- package/dist/templates/native-go/backend/internal/helper/supervision_test.go +124 -0
- package/dist/templates/native-go/backend/internal/helper/supervisor_linux.go +192 -0
- package/dist/templates/native-go/backend/internal/helper/supervisor_linux_test.go +302 -0
- package/dist/templates/native-go/dsm-package.yaml +58 -0
- package/dist/templates/native-go/gitignore +1 -0
- package/dist/templates/native-go/i18n/glossary.json +662 -0
- package/dist/templates/native-go/i18n/messages/de.json +28 -0
- package/dist/templates/native-go/i18n/messages/en.json +28 -0
- package/dist/templates/native-go/i18n/messages/es.json +28 -0
- package/dist/templates/native-go/i18n/messages/fr.json +28 -0
- package/dist/templates/native-go/i18n/messages/it.json +28 -0
- package/dist/templates/native-go/i18n/messages/ja.json +28 -0
- package/dist/templates/native-go/i18n/messages/ko.json +28 -0
- package/dist/templates/native-go/i18n/messages/zh-CN.json +28 -0
- package/dist/templates/native-go/i18n/messages/zh-TW.json +28 -0
- package/dist/templates/native-go/ui/app.css +35 -0
- package/dist/templates/native-go/ui/app.js +94 -0
- package/dist/templates/native-go-better-auth/backend/internal/app/identity.go +208 -0
- package/dist/templates/native-go-better-auth/backend/internal/app/identity_test.go +113 -0
- package/dist/templates/native-go-better-auth/node/app.mjs +72 -0
- package/dist/templates/native-go-better-auth/node/build.mjs +2 -0
- package/dist/templates/native-go-better-auth/node/package-lock.json +862 -0
- package/dist/templates/native-go-better-auth/node/package.json +1 -0
- package/dist/templates/native-go-better-auth/node/test/auth.test.mjs +31 -0
- package/dist/templates/native-go-integration/backend/internal/app/identity_none.go +6 -0
- package/dist/templates/native-go-integration/backend/internal/app/personal.go +203 -0
- package/dist/templates/native-go-integration/backend/internal/app/personal_test.go +119 -0
- package/dist/templates/native-go-root/backend/internal/app/root.go +107 -0
- package/dist/templates/native-go-root/backend/internal/app/root_test.go +124 -0
- package/dist/templates/native-go-root/root/whoami.sh +6 -0
- package/dist/templates/native-go-vue/README.md +110 -0
- package/dist/templates/native-go-vue/frontend/DESIGN.md +78 -0
- package/dist/templates/native-go-vue/frontend/package-lock.json +2507 -0
- package/dist/templates/native-go-vue/frontend/package.json +6 -0
- package/dist/templates/native-go-vue/frontend/src/App.vue +132 -0
- package/dist/templates/native-go-vue/frontend/src/context.ts +17 -0
- package/dist/templates/native-go-vue/frontend/src/main.ts +26 -0
- package/dist/templates/native-go-vue/frontend/src/style.css +14 -0
- package/dist/templates/native-go-vue/frontend/test/apps.test.ts +148 -0
- package/dist/templates/native-go-vue/frontend/tsconfig.json +1 -0
- package/dist/templates/native-go-vue/frontend/vite.config.ts +8 -0
- package/dist/templates/native-go-vue/gitignore +2 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate.js +11 -7
- package/package.json +3 -7
- package/schema/synology-app.schema.json +36 -37
- package/skills/SKILL.md +4 -48
- package/skills/reference/create.md +11 -83
- package/skills/reference/deploy.md +7 -78
- package/skills/reference/diagnose.md +4 -101
- package/skills/reference/files.md +5 -41
- package/skills/reference/identity.md +3 -29
- package/skills/reference/wrap.md +0 -2
- package/templates/binary/synology-app.yaml +7 -0
- package/templates/hello-files/compose.yaml +0 -1
- package/templates/hello-web/compose.yaml +13 -10
- package/templates/hello-web/html/app.js +1 -23
- package/templates/hello-web/html/index.html +1 -3
- package/templates/hello-web-page/compose.yaml +13 -10
- package/templates/hello-web-page/html/app.js +1 -23
- package/templates/hello-web-page/html/index.html +1 -3
- package/templates/native-go/README.md +268 -0
- package/templates/native-go/backend/cmd/app/main.go +49 -0
- package/templates/native-go/backend/cmd/helper/main.go +38 -0
- package/templates/native-go/backend/go.mod +3 -0
- package/templates/native-go/backend/internal/api/errors.go +33 -0
- package/templates/native-go/backend/internal/api/registry.go +94 -0
- package/templates/native-go/backend/internal/api/registry_test.go +329 -0
- package/templates/native-go/backend/internal/api/server.go +155 -0
- package/templates/native-go/backend/internal/app/root_none.go +9 -0
- package/templates/native-go/backend/internal/app/routes.go +127 -0
- package/templates/native-go/backend/internal/app/routes_test.go +167 -0
- package/templates/native-go/backend/internal/dsm/auth.go +118 -0
- package/templates/native-go/backend/internal/dsm/auth_test.go +139 -0
- package/templates/native-go/backend/internal/helper/control.go +219 -0
- package/templates/native-go/backend/internal/helper/control_test.go +242 -0
- package/templates/native-go/backend/internal/helper/peer_linux.go +44 -0
- package/templates/native-go/backend/internal/helper/peer_other.go +7 -0
- package/templates/native-go/backend/internal/helper/process_linux.go +167 -0
- package/templates/native-go/backend/internal/helper/process_other.go +5 -0
- package/templates/native-go/backend/internal/helper/server_linux.go +83 -0
- package/templates/native-go/backend/internal/helper/status.go +271 -0
- package/templates/native-go/backend/internal/helper/status_test.go +205 -0
- package/templates/native-go/backend/internal/helper/supervision.go +84 -0
- package/templates/native-go/backend/internal/helper/supervision_test.go +124 -0
- package/templates/native-go/backend/internal/helper/supervisor_linux.go +192 -0
- package/templates/native-go/backend/internal/helper/supervisor_linux_test.go +302 -0
- package/templates/native-go/dsm-package.yaml +58 -0
- package/templates/native-go/gitignore +1 -0
- package/templates/native-go/i18n/glossary.json +662 -0
- package/templates/native-go/i18n/messages/de.json +28 -0
- package/templates/native-go/i18n/messages/en.json +28 -0
- package/templates/native-go/i18n/messages/es.json +28 -0
- package/templates/native-go/i18n/messages/fr.json +28 -0
- package/templates/native-go/i18n/messages/it.json +28 -0
- package/templates/native-go/i18n/messages/ja.json +28 -0
- package/templates/native-go/i18n/messages/ko.json +28 -0
- package/templates/native-go/i18n/messages/zh-CN.json +28 -0
- package/templates/native-go/i18n/messages/zh-TW.json +28 -0
- package/templates/native-go/ui/app.css +35 -0
- package/templates/native-go/ui/app.js +94 -0
- package/templates/native-go-better-auth/backend/internal/app/identity.go +208 -0
- package/templates/native-go-better-auth/backend/internal/app/identity_test.go +113 -0
- package/templates/native-go-better-auth/node/app.mjs +72 -0
- package/templates/native-go-better-auth/node/build.mjs +2 -0
- package/templates/native-go-better-auth/node/package-lock.json +862 -0
- package/templates/native-go-better-auth/node/package.json +1 -0
- package/templates/native-go-better-auth/node/test/auth.test.mjs +31 -0
- package/templates/native-go-integration/backend/internal/app/identity_none.go +6 -0
- package/templates/native-go-integration/backend/internal/app/personal.go +203 -0
- package/templates/native-go-integration/backend/internal/app/personal_test.go +119 -0
- package/templates/native-go-root/backend/internal/app/root.go +107 -0
- package/templates/native-go-root/backend/internal/app/root_test.go +124 -0
- package/templates/native-go-root/root/whoami.sh +6 -0
- package/templates/native-go-vue/README.md +110 -0
- package/templates/native-go-vue/frontend/DESIGN.md +78 -0
- package/templates/native-go-vue/frontend/package-lock.json +2507 -0
- package/templates/native-go-vue/frontend/package.json +6 -0
- package/templates/native-go-vue/frontend/src/App.vue +132 -0
- package/templates/native-go-vue/frontend/src/context.ts +17 -0
- package/templates/native-go-vue/frontend/src/main.ts +26 -0
- package/templates/native-go-vue/frontend/src/style.css +14 -0
- package/templates/native-go-vue/frontend/test/apps.test.ts +148 -0
- package/templates/native-go-vue/frontend/tsconfig.json +1 -0
- package/templates/native-go-vue/frontend/vite.config.ts +8 -0
- package/templates/native-go-vue/gitignore +2 -0
- package/dist/attestation/app-package.d.ts +0 -86
- package/dist/attestation/app-package.js +0 -206
- package/dist/attestation/install-on-host.d.ts +0 -14
- package/dist/attestation/install-on-host.js +0 -52
- package/dist/attestation/keyring.d.ts +0 -21
- package/dist/attestation/keyring.js +0 -168
- package/dist/attestation/platform-service.d.ts +0 -70
- package/dist/attestation/platform-service.js +0 -250
- package/dist/build-spk/broker-glue.d.ts +0 -16
- package/dist/build-spk/broker-glue.js +0 -40
- package/dist/deploy/catalog-publish.d.ts +0 -3
- package/dist/deploy/catalog-publish.js +0 -7
- package/dist/doctor.d.ts +0 -22
- package/dist/doctor.js +0 -822
- package/dist/error-collector.d.ts +0 -2
- package/dist/error-collector.js +0 -46
- package/dist/host/broker-paths.d.ts +0 -1
- package/dist/host/broker-paths.js +0 -1
- package/dist/host/broker-upgrade-preflight.d.ts +0 -61
- package/dist/host/broker-upgrade-preflight.js +0 -156
- package/dist/host/digests.d.ts +0 -5
- package/dist/host/digests.js +0 -63
- package/dist/host/fake-nas.d.ts +0 -49
- package/dist/host/fake-nas.js +0 -193
- package/dist/mock-broker/bootstrap.d.ts +0 -2
- package/dist/mock-broker/bootstrap.js +0 -8
- package/dist/mock-broker/files.d.ts +0 -60
- package/dist/mock-broker/files.js +0 -408
- package/dist/mock-broker/oidc.d.ts +0 -31
- package/dist/mock-broker/oidc.js +0 -161
- package/dist/mock-broker/path.d.ts +0 -9
- package/dist/mock-broker/path.js +0 -50
- package/dist/mock-broker/server.d.ts +0 -19
- package/dist/mock-broker/server.js +0 -350
- package/dist/publish/build-ledger.d.ts +0 -22
- package/dist/publish/build-ledger.js +0 -26
- package/dist/publish/community-path.d.ts +0 -6
- package/dist/publish/community-path.js +0 -41
- package/dist/publish/curated-release.d.ts +0 -78
- package/dist/publish/curated-release.js +0 -883
- package/dist/publish/file-build-ledger.d.ts +0 -18
- package/dist/publish/file-build-ledger.js +0 -268
- package/dist/publish/github-pr.d.ts +0 -25
- package/dist/publish/github-pr.js +0 -85
- package/dist/publish/github-repo.d.ts +0 -8
- package/dist/publish/github-repo.js +0 -21
- package/dist/publish/platform-service-release.d.ts +0 -60
- package/dist/publish/platform-service-release.js +0 -333
- package/dist/publish/preflight.d.ts +0 -24
- package/dist/publish/preflight.js +0 -138
- package/dist/publish/publish.d.ts +0 -27
- package/dist/publish/publish.js +0 -136
- package/dist/publish/sync-definition.d.ts +0 -4
- package/dist/publish/sync-definition.js +0 -48
- package/dist/release-bin.d.ts +0 -2
- package/dist/release-bin.js +0 -23
- package/dist/templates/hello-web/html/sdk.js +0 -68
- package/dist/templates/hello-web-page/html/sdk.js +0 -68
- package/dist/templates/native/synology-app.yaml +0 -7
- package/dist/test-host.d.ts +0 -15
- package/dist/test-host.js +0 -37
- package/templates/hello-web/html/sdk.js +0 -68
- package/templates/hello-web-page/html/sdk.js +0 -68
- package/templates/native/synology-app.yaml +0 -7
- /package/dist/templates/{native → binary}/native.yaml +0 -0
- /package/templates/{native → binary}/native.yaml +0 -0
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { SignJWT, importJWK, jwtVerify } from "jose";
|
|
4
|
-
/**
|
|
5
|
-
* Trust contract for releasing the platform service (the Broker SPK) itself.
|
|
6
|
-
* This is deliberately a separate issuer, typ and keyring from the curated App
|
|
7
|
-
* chain: the Broker carries the curated trust root as *data*, so that root can
|
|
8
|
-
* never double as the authority that signs the Broker's own releases.
|
|
9
|
-
*/
|
|
10
|
-
export const PLATFORM_ATTESTATION_ISSUER = "synopkgland-platform";
|
|
11
|
-
export const PLATFORM_SERVICE_RELEASE_ATTESTATION_TYP = "platform-service-release-attestation";
|
|
12
|
-
export const PLATFORM_SERVICE_RELEASE_ATTESTATION_VERSION = 1;
|
|
13
|
-
export const PLATFORM_SERVICE_PACKAGE_IDENTITY = "SynoPkgLandService";
|
|
14
|
-
export const PLATFORM_KEYRING_PURPOSE = "platform-service-release";
|
|
15
|
-
export const PLATFORM_ATTESTATION_PRIVATE_JWK_ENV = "SYNOPKGLAND_PLATFORM_ATTESTATION_PRIVATE_JWK";
|
|
16
|
-
export const PLATFORM_ATTESTATION_KID_ENV = "SYNOPKGLAND_PLATFORM_ATTESTATION_KID";
|
|
17
|
-
export const PLATFORM_ATTESTATION_KEYRING_PATH_ENV = "SYNOPKGLAND_PLATFORM_ATTESTATION_KEYRING_PATH";
|
|
18
|
-
export class PlatformServiceReleaseError extends Error {
|
|
19
|
-
code;
|
|
20
|
-
constructor(code, detail) {
|
|
21
|
-
super(detail ? `${code}: ${detail}` : code);
|
|
22
|
-
this.code = code;
|
|
23
|
-
this.name = "PlatformServiceReleaseError";
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
const PRIVATE_JWK_FIELDS = ["d", "p", "q", "dp", "dq", "qi", "oth", "k"];
|
|
27
|
-
const KEY_STATUSES = new Set(["active", "verify-only", "revoked"]);
|
|
28
|
-
export function parsePlatformKeyringDocument(raw) {
|
|
29
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
30
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_INVALID", "not an object");
|
|
31
|
-
}
|
|
32
|
-
const document = raw;
|
|
33
|
-
if (document.version !== 1 || !Array.isArray(document.keys)) {
|
|
34
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_INVALID", "unsupported document shape");
|
|
35
|
-
}
|
|
36
|
-
if (document.purpose !== PLATFORM_KEYRING_PURPOSE) {
|
|
37
|
-
// A curated keyring document has no purpose field at all; refusing here is
|
|
38
|
-
// what keeps the Broker's bundled trust root from signing the Broker.
|
|
39
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_PURPOSE_MISMATCH");
|
|
40
|
-
}
|
|
41
|
-
for (const entry of document.keys) {
|
|
42
|
-
if (!entry ||
|
|
43
|
-
typeof entry.kid !== "string" ||
|
|
44
|
-
entry.kid.length === 0 ||
|
|
45
|
-
!KEY_STATUSES.has(entry.status) ||
|
|
46
|
-
!entry.publicJwk ||
|
|
47
|
-
typeof entry.publicJwk !== "object") {
|
|
48
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_INVALID", "invalid key entry");
|
|
49
|
-
}
|
|
50
|
-
if (PRIVATE_JWK_FIELDS.some((field) => field in entry.publicJwk)) {
|
|
51
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_PRIVATE_MATERIAL");
|
|
52
|
-
}
|
|
53
|
-
if (entry.publicJwk.kty !== "RSA" || typeof entry.publicJwk.n !== "string") {
|
|
54
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_INVALID", "not a public RSA key");
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (!document.keys.some((entry) => entry.status === "active")) {
|
|
58
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_NO_ACTIVE_KEY");
|
|
59
|
-
}
|
|
60
|
-
return document;
|
|
61
|
-
}
|
|
62
|
-
export function readPlatformKeyringFile(path) {
|
|
63
|
-
let raw;
|
|
64
|
-
try {
|
|
65
|
-
raw = readFileSync(path, "utf8");
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_INVALID", `unable to read keyring: ${error instanceof Error ? error.message : String(error)}`);
|
|
69
|
-
}
|
|
70
|
-
let parsed;
|
|
71
|
-
try {
|
|
72
|
-
parsed = JSON.parse(raw);
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_INVALID", "keyring is not valid JSON");
|
|
76
|
-
}
|
|
77
|
-
return parsePlatformKeyringDocument(parsed);
|
|
78
|
-
}
|
|
79
|
-
export function canonicalPlatformServiceReleaseClaims(claims) {
|
|
80
|
-
return JSON.stringify({
|
|
81
|
-
arch: claims.arch,
|
|
82
|
-
artifactDigest: claims.artifactDigest,
|
|
83
|
-
issuedAt: claims.issuedAt,
|
|
84
|
-
osMinVer: claims.osMinVer,
|
|
85
|
-
packageBuild: claims.packageBuild,
|
|
86
|
-
packageIdentity: claims.packageIdentity,
|
|
87
|
-
packageVersion: claims.packageVersion,
|
|
88
|
-
sourceRevision: claims.sourceRevision,
|
|
89
|
-
sourceVersion: claims.sourceVersion,
|
|
90
|
-
typ: claims.typ,
|
|
91
|
-
version: claims.version,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
export function digestPlatformServiceReleaseClaims(claims) {
|
|
95
|
-
return createHash("sha256").update(canonicalPlatformServiceReleaseClaims(claims)).digest("hex");
|
|
96
|
-
}
|
|
97
|
-
export async function signPlatformServiceReleaseAttestationToken(input, key) {
|
|
98
|
-
assertPlatformInput(input);
|
|
99
|
-
if (typeof key.kid !== "string" || key.kid.length === 0) {
|
|
100
|
-
throw new PlatformServiceReleaseError("PLATFORM_SIGNING_MATERIAL_INVALID", "kid required");
|
|
101
|
-
}
|
|
102
|
-
const claims = {
|
|
103
|
-
typ: PLATFORM_SERVICE_RELEASE_ATTESTATION_TYP,
|
|
104
|
-
version: PLATFORM_SERVICE_RELEASE_ATTESTATION_VERSION,
|
|
105
|
-
packageIdentity: input.packageIdentity,
|
|
106
|
-
sourceVersion: input.sourceVersion,
|
|
107
|
-
packageBuild: input.packageBuild,
|
|
108
|
-
packageVersion: input.packageVersion,
|
|
109
|
-
artifactDigest: input.artifactDigest,
|
|
110
|
-
sourceRevision: input.sourceRevision,
|
|
111
|
-
osMinVer: input.osMinVer,
|
|
112
|
-
arch: input.arch,
|
|
113
|
-
issuedAt: new Date().toISOString(),
|
|
114
|
-
};
|
|
115
|
-
const attestationDigest = digestPlatformServiceReleaseClaims(claims);
|
|
116
|
-
const token = await new SignJWT({ ...claims })
|
|
117
|
-
.setProtectedHeader({ alg: "RS256", kid: key.kid, typ: "JWT" })
|
|
118
|
-
.setIssuer(PLATFORM_ATTESTATION_ISSUER)
|
|
119
|
-
.setSubject(claims.packageIdentity)
|
|
120
|
-
.setJti(attestationDigest)
|
|
121
|
-
.sign(key.privateKey);
|
|
122
|
-
return { token, attestationDigest, claims };
|
|
123
|
-
}
|
|
124
|
-
export async function verifyPlatformServiceReleaseAttestationToken(token, keyring) {
|
|
125
|
-
parsePlatformKeyringDocument(keyring);
|
|
126
|
-
try {
|
|
127
|
-
const header = decodeHeader(token);
|
|
128
|
-
const entry = keyring.keys.find((candidate) => candidate.kid === header.kid && candidate.status !== "revoked");
|
|
129
|
-
if (!entry) {
|
|
130
|
-
throw new Error("unknown or revoked kid");
|
|
131
|
-
}
|
|
132
|
-
const publicKey = (await importJWK(entry.publicJwk, "RS256"));
|
|
133
|
-
const result = await jwtVerify(token, publicKey, {
|
|
134
|
-
algorithms: ["RS256"],
|
|
135
|
-
issuer: PLATFORM_ATTESTATION_ISSUER,
|
|
136
|
-
});
|
|
137
|
-
if (result.protectedHeader.alg !== "RS256" ||
|
|
138
|
-
result.protectedHeader.typ !== "JWT" ||
|
|
139
|
-
result.protectedHeader.kid !== entry.kid) {
|
|
140
|
-
throw new Error("token header mismatch");
|
|
141
|
-
}
|
|
142
|
-
const payload = result.payload;
|
|
143
|
-
if (payload.typ !== PLATFORM_SERVICE_RELEASE_ATTESTATION_TYP ||
|
|
144
|
-
payload.version !== PLATFORM_SERVICE_RELEASE_ATTESTATION_VERSION) {
|
|
145
|
-
throw new Error("token typ mismatch");
|
|
146
|
-
}
|
|
147
|
-
const claims = {
|
|
148
|
-
typ: payload.typ,
|
|
149
|
-
version: payload.version,
|
|
150
|
-
packageIdentity: payload.packageIdentity,
|
|
151
|
-
sourceVersion: payload.sourceVersion,
|
|
152
|
-
packageBuild: payload.packageBuild,
|
|
153
|
-
packageVersion: payload.packageVersion,
|
|
154
|
-
artifactDigest: payload.artifactDigest,
|
|
155
|
-
sourceRevision: payload.sourceRevision,
|
|
156
|
-
osMinVer: payload.osMinVer,
|
|
157
|
-
arch: payload.arch,
|
|
158
|
-
issuedAt: payload.issuedAt,
|
|
159
|
-
};
|
|
160
|
-
assertPlatformInput(claims);
|
|
161
|
-
if (typeof claims.issuedAt !== "string" || Number.isNaN(Date.parse(claims.issuedAt))) {
|
|
162
|
-
throw new Error("issuedAt is not a timestamp");
|
|
163
|
-
}
|
|
164
|
-
if (result.payload.jti !== digestPlatformServiceReleaseClaims(claims)) {
|
|
165
|
-
throw new Error("jti does not bind the claims");
|
|
166
|
-
}
|
|
167
|
-
return claims;
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
if (error instanceof PlatformServiceReleaseError) {
|
|
171
|
-
throw new PlatformServiceReleaseError("PLATFORM_ATTESTATION_INVALID", error.message);
|
|
172
|
-
}
|
|
173
|
-
throw new PlatformServiceReleaseError("PLATFORM_ATTESTATION_INVALID", error instanceof Error ? error.message : String(error));
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
export async function createPlatformAttestationSignerFromEnv(env = process.env) {
|
|
177
|
-
const jwkRaw = env[PLATFORM_ATTESTATION_PRIVATE_JWK_ENV]?.trim();
|
|
178
|
-
const kidFromEnv = env[PLATFORM_ATTESTATION_KID_ENV]?.trim();
|
|
179
|
-
const keyringPath = env[PLATFORM_ATTESTATION_KEYRING_PATH_ENV]?.trim();
|
|
180
|
-
if (!jwkRaw) {
|
|
181
|
-
throw new PlatformServiceReleaseError("PLATFORM_SIGNING_MATERIAL_MISSING");
|
|
182
|
-
}
|
|
183
|
-
if (!keyringPath) {
|
|
184
|
-
throw new PlatformServiceReleaseError("PLATFORM_KEYRING_INVALID", `${PLATFORM_ATTESTATION_KEYRING_PATH_ENV} is required`);
|
|
185
|
-
}
|
|
186
|
-
const keyring = readPlatformKeyringFile(keyringPath);
|
|
187
|
-
let privateJwk;
|
|
188
|
-
let privateKey;
|
|
189
|
-
try {
|
|
190
|
-
privateJwk = JSON.parse(jwkRaw);
|
|
191
|
-
privateKey = (await importJWK(privateJwk, "RS256"));
|
|
192
|
-
}
|
|
193
|
-
catch {
|
|
194
|
-
throw new PlatformServiceReleaseError("PLATFORM_SIGNING_MATERIAL_INVALID");
|
|
195
|
-
}
|
|
196
|
-
const kid = kidFromEnv || (typeof privateJwk.kid === "string" ? privateJwk.kid : "");
|
|
197
|
-
if (!kid) {
|
|
198
|
-
throw new PlatformServiceReleaseError("PLATFORM_SIGNING_MATERIAL_INVALID", "kid required");
|
|
199
|
-
}
|
|
200
|
-
const trustEntry = keyring.keys.find((entry) => entry.kid === kid && entry.status === "active");
|
|
201
|
-
if (!trustEntry) {
|
|
202
|
-
throw new PlatformServiceReleaseError("PLATFORM_SIGNING_KEY_UNKNOWN");
|
|
203
|
-
}
|
|
204
|
-
const signingKey = { kid, privateKey };
|
|
205
|
-
return {
|
|
206
|
-
sign: (input) => signPlatformServiceReleaseAttestationToken(input, signingKey),
|
|
207
|
-
verify: (token) => verifyPlatformServiceReleaseAttestationToken(token, keyring),
|
|
208
|
-
verificationJwk: trustEntry.publicJwk,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
function assertPlatformInput(input) {
|
|
212
|
-
const fail = (detail) => {
|
|
213
|
-
throw new PlatformServiceReleaseError("PLATFORM_ATTESTATION_INPUT_INVALID", detail);
|
|
214
|
-
};
|
|
215
|
-
if (input.packageIdentity !== PLATFORM_SERVICE_PACKAGE_IDENTITY) {
|
|
216
|
-
fail("packageIdentity must be the platform service identity");
|
|
217
|
-
}
|
|
218
|
-
if (!/^\d+\.\d+\.\d+$/.test(input.sourceVersion)) {
|
|
219
|
-
fail("sourceVersion must be plain semver without a Build suffix");
|
|
220
|
-
}
|
|
221
|
-
if (!Number.isSafeInteger(input.packageBuild) || input.packageBuild < 1) {
|
|
222
|
-
fail("packageBuild must be a positive safe integer");
|
|
223
|
-
}
|
|
224
|
-
if (input.packageVersion !== `${input.sourceVersion}-${String(input.packageBuild).padStart(4, "0")}`) {
|
|
225
|
-
fail("packageVersion must be sourceVersion plus the zero-padded Build");
|
|
226
|
-
}
|
|
227
|
-
if (!/^[a-f0-9]{64}$/.test(input.artifactDigest)) {
|
|
228
|
-
fail("artifactDigest must be a lowercase SHA-256 digest");
|
|
229
|
-
}
|
|
230
|
-
if (!/^[a-f0-9]{40}$/.test(input.sourceRevision)) {
|
|
231
|
-
fail("sourceRevision must be a full git commit SHA");
|
|
232
|
-
}
|
|
233
|
-
if (!/^\d+\.\d+-\d+$/.test(input.osMinVer)) {
|
|
234
|
-
fail("osMinVer must look like 7.2-69057");
|
|
235
|
-
}
|
|
236
|
-
if (input.arch !== "noarch") {
|
|
237
|
-
fail("the platform service is noarch");
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
function decodeHeader(token) {
|
|
241
|
-
const parts = token.split(".");
|
|
242
|
-
if (parts.length !== 3 || parts.some((part) => part.length === 0)) {
|
|
243
|
-
throw new Error("not a compact JWT");
|
|
244
|
-
}
|
|
245
|
-
const header = JSON.parse(Buffer.from(parts[0], "base64url").toString("utf8"));
|
|
246
|
-
if (!header || typeof header !== "object" || Array.isArray(header)) {
|
|
247
|
-
throw new Error("invalid JWT header");
|
|
248
|
-
}
|
|
249
|
-
return header;
|
|
250
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Broker installer glue every Managed Profile App carries, independent of
|
|
3
|
-
* runtime. Compose and native Apps install through the same socket, so the
|
|
4
|
-
* Local Attestation gate and the registration-status probe live here once.
|
|
5
|
-
*/
|
|
6
|
-
/** Shell variable preamble locating the Broker installer and its socket. */
|
|
7
|
-
export declare const BROKER_INSTALLER_VARS = "INSTALLER=\"${BROKER_INSTALLER:-/var/packages/SynoPkgLandService/target/bin/synopkgland-installer}\"\nSOCKET=\"${BROKER_SOCKET:-/var/packages/SynoPkgLandService/var/installer.sock}\"";
|
|
8
|
-
/**
|
|
9
|
-
* The `status` probe: asks the Broker whether this App is still registered.
|
|
10
|
-
* `removed` exits 150 so DSM shows the App as removed by the platform;
|
|
11
|
-
* `unknown` exits 4. Anything else falls through to the caller, which decides
|
|
12
|
-
* what "running" means for its runtime.
|
|
13
|
-
*/
|
|
14
|
-
export declare function renderBrokerStatusProbe(packageIdentity: string): string;
|
|
15
|
-
/** `preinst` / `preupgrade`: refuse to install when the Broker socket is absent. */
|
|
16
|
-
export declare const BROKER_SOCKET_REQUIRED_SCRIPT = "#!/bin/sh\nSOCKET=\"${BROKER_SOCKET:-/var/packages/SynoPkgLandService/var/installer.sock}\"\nif [ ! -S \"$SOCKET\" ]; then\n echo \"SynoPkgLandService installer socket is not available\" >&2\n exit 1\nfi\nexit 0\n";
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Broker installer glue every Managed Profile App carries, independent of
|
|
3
|
-
* runtime. Compose and native Apps install through the same socket, so the
|
|
4
|
-
* Local Attestation gate and the registration-status probe live here once.
|
|
5
|
-
*/
|
|
6
|
-
/** Shell variable preamble locating the Broker installer and its socket. */
|
|
7
|
-
export const BROKER_INSTALLER_VARS = `INSTALLER="\${BROKER_INSTALLER:-/var/packages/SynoPkgLandService/target/bin/synopkgland-installer}"
|
|
8
|
-
SOCKET="\${BROKER_SOCKET:-/var/packages/SynoPkgLandService/var/installer.sock}"`;
|
|
9
|
-
/**
|
|
10
|
-
* The `status` probe: asks the Broker whether this App is still registered.
|
|
11
|
-
* `removed` exits 150 so DSM shows the App as removed by the platform;
|
|
12
|
-
* `unknown` exits 4. Anything else falls through to the caller, which decides
|
|
13
|
-
* what "running" means for its runtime.
|
|
14
|
-
*/
|
|
15
|
-
export function renderBrokerStatusProbe(packageIdentity) {
|
|
16
|
-
return ` if [ -x "$INSTALLER" ] && [ -S "$SOCKET" ]; then
|
|
17
|
-
"$INSTALLER" --socket "$SOCKET" --phase status --attempt-id "status-$$" \\
|
|
18
|
-
--package-identity "${packageIdentity}" --package-version "\${SYNOPKG_PKGVER}" \\
|
|
19
|
-
--package-path "\${SYNOPKG_PKGDEST}" --bundle-path "\${SYNOPKG_PKGDEST}" \\
|
|
20
|
-
--pkg-var "\${SYNOPKG_PKGVAR}" --pkg-home "\${SYNOPKG_PKGHOME}" >/tmp/synopkgland-status.$$ 2>/dev/null || true
|
|
21
|
-
if grep -q '"registrationStatus":"removed"' /tmp/synopkgland-status.$$ 2>/dev/null; then
|
|
22
|
-
rm -f /tmp/synopkgland-status.$$
|
|
23
|
-
exit 150
|
|
24
|
-
fi
|
|
25
|
-
if grep -q '"registrationStatus":"unknown"' /tmp/synopkgland-status.$$ 2>/dev/null; then
|
|
26
|
-
rm -f /tmp/synopkgland-status.$$
|
|
27
|
-
exit 4
|
|
28
|
-
fi
|
|
29
|
-
rm -f /tmp/synopkgland-status.$$
|
|
30
|
-
fi`;
|
|
31
|
-
}
|
|
32
|
-
/** `preinst` / `preupgrade`: refuse to install when the Broker socket is absent. */
|
|
33
|
-
export const BROKER_SOCKET_REQUIRED_SCRIPT = `#!/bin/sh
|
|
34
|
-
SOCKET="\${BROKER_SOCKET:-/var/packages/SynoPkgLandService/var/installer.sock}"
|
|
35
|
-
if [ ! -S "$SOCKET" ]; then
|
|
36
|
-
echo "SynoPkgLandService installer socket is not available" >&2
|
|
37
|
-
exit 1
|
|
38
|
-
fi
|
|
39
|
-
exit 0
|
|
40
|
-
`;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { publishSpkToSource, resolvePublishTarget, } from "@synopackageland/package-source/publish-spk";
|
|
2
|
-
export async function publishBuiltSpk(spkPath) {
|
|
3
|
-
return publishSpkToSource(spkPath, resolvePublishTarget());
|
|
4
|
-
}
|
|
5
|
-
export function packageSourceOrigin() {
|
|
6
|
-
return resolvePublishTarget().origin;
|
|
7
|
-
}
|
package/dist/doctor.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ProjectRoot } from "./discovery.js";
|
|
2
|
-
import type { NasHost } from "./host/types.js";
|
|
3
|
-
export type DoctorCheckStatus = "pass" | "fail" | "skip" | "warn";
|
|
4
|
-
export interface DoctorCheck {
|
|
5
|
-
id: string;
|
|
6
|
-
status: DoctorCheckStatus;
|
|
7
|
-
code?: string;
|
|
8
|
-
message: string;
|
|
9
|
-
suggestion?: string;
|
|
10
|
-
}
|
|
11
|
-
export interface DoctorReport {
|
|
12
|
-
ok: boolean;
|
|
13
|
-
checks: DoctorCheck[];
|
|
14
|
-
}
|
|
15
|
-
export interface RunDoctorOptions {
|
|
16
|
-
project?: ProjectRoot | null;
|
|
17
|
-
host?: NasHost | null;
|
|
18
|
-
packageId?: string;
|
|
19
|
-
dev?: boolean;
|
|
20
|
-
}
|
|
21
|
-
export declare function runDoctor(options: RunDoctorOptions): Promise<DoctorReport>;
|
|
22
|
-
export declare function formatDoctorReport(report: DoctorReport): string;
|