@objectstack/core 7.5.0 → 7.6.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/dist/index.d.cts CHANGED
@@ -6,7 +6,8 @@ import { ObjectLogger } from './logger.cjs';
6
6
  export { createLogger } from './logger.cjs';
7
7
  import { ConflictResolutionStrategy, ApiRegistryEntryInput, ApiRegistryEntry, ApiDiscoveryQuery, ApiDiscoveryResponse, ApiEndpointRegistration, ApiRegistry as ApiRegistry$1 } from '@objectstack/spec/api';
8
8
  import * as QA from '@objectstack/spec/qa';
9
- import { PluginCapability, PluginPermissionSet, ResourceType, PermissionAction, PluginPermission, SandboxConfig, KernelSecurityScanResult, KernelSecurityVulnerability, PluginHealthCheck, PluginHealthStatus as PluginHealthStatus$1, PluginHealthReport, HotReloadConfig, VersionConstraint, DependencyConflict, SemanticVersion, CompatibilityLevel } from '@objectstack/spec/kernel';
9
+ import { KeyObject } from 'node:crypto';
10
+ import { PluginCapability, PluginPermissions as PluginPermissions$1, PluginPermissionSet, ResourceType, PermissionAction, PluginPermission, SandboxConfig, KernelSecurityScanResult, KernelSecurityVulnerability, PluginHealthCheck, PluginHealthStatus as PluginHealthStatus$1, PluginHealthReport, HotReloadConfig, VersionConstraint, DependencyConflict, SemanticVersion, CompatibilityLevel } from '@objectstack/spec/kernel';
10
11
 
11
12
  /**
12
13
  * Service Lifecycle Types
@@ -1000,6 +1001,120 @@ declare class PluginSignatureVerifier {
1000
1001
  private validateConfig;
1001
1002
  }
1002
1003
 
1004
+ /**
1005
+ * Plugin artifact signing & verification (ADR-0025 §3.4–§3.7, framework F3).
1006
+ *
1007
+ * This is the CANONICAL Ed25519 detached-signature contract shared by the
1008
+ * whole plugin distribution pipeline. It is intentionally byte-for-byte
1009
+ * compatible with the cloud control plane's `package-signing.ts` so the
1010
+ * two never drift:
1011
+ *
1012
+ * - signature string format: `ed25519:<keyId>:<base64url(signature)>`
1013
+ * - publisher signature: Ed25519 over the raw `.osplugin` artifact bytes,
1014
+ * produced by `os plugin sign`, verified by cloud at publish time and by
1015
+ * the runtime when it materializes the artifact.
1016
+ * - platform counter-signature: Ed25519 over {@link counterSignPayload}
1017
+ * (the version identity), produced by cloud at approval, verified by the
1018
+ * runtime at load time as the marketplace's "reviewed + approved" attest.
1019
+ *
1020
+ * Algorithm: Ed25519 via node:crypto (`sign(null, …)` / `verify(null, …)`):
1021
+ * short, deterministic, no padding ambiguity. The `keyId` is an opaque
1022
+ * rotation handle used to resolve the verifying public key.
1023
+ *
1024
+ * The two trust chains the runtime checks before loading a third-party
1025
+ * plugin are combined in {@link verifyPluginArtifact}.
1026
+ */
1027
+
1028
+ declare const SIGNATURE_ALG = "ed25519";
1029
+ type KeyInput = string | KeyObject;
1030
+ /** Generate an Ed25519 keypair as PEM strings (publisher bootstrap / tests). */
1031
+ declare function generateEd25519KeyPair(): {
1032
+ publicKeyPem: string;
1033
+ privateKeyPem: string;
1034
+ };
1035
+ /**
1036
+ * Sign `payload` with an Ed25519 private key, returning the formatted
1037
+ * signature string `ed25519:<keyId>:<base64url(sig)>`.
1038
+ */
1039
+ declare function signPayload(payload: string | Uint8Array, privateKey: KeyInput, keyId?: string): string;
1040
+ interface ParsedSignature {
1041
+ alg: 'ed25519';
1042
+ keyId: string;
1043
+ signature: Uint8Array;
1044
+ }
1045
+ /** Parse an `ed25519:<keyId>:<base64url>` signature string. Returns null if malformed. */
1046
+ declare function parseSignature(s: string | undefined | null): ParsedSignature | null;
1047
+ /** Verify a formatted signature string over `payload` with the given public key. */
1048
+ declare function verifyPayload(payload: string | Uint8Array, signature: string, publicKey: KeyInput): boolean;
1049
+ /**
1050
+ * Canonical payload the platform counter-signs at approval. Binds the
1051
+ * attestation to the version identity + artifact location + the publisher
1052
+ * signature (which itself binds the artifact bytes). MUST match the cloud
1053
+ * control plane's `counterSignPayload` exactly.
1054
+ */
1055
+ declare function counterSignPayload(version: {
1056
+ package_id: string;
1057
+ version: string;
1058
+ blob_key?: string | null;
1059
+ signature?: string | null;
1060
+ }): string;
1061
+ interface PublisherVerifyResult {
1062
+ /** Whether loading may proceed on signature grounds. */
1063
+ ok: boolean;
1064
+ /** True when a signature was present AND cryptographically verified. */
1065
+ verified: boolean;
1066
+ reason?: string;
1067
+ }
1068
+ /**
1069
+ * Verify a publisher signature over the raw artifact bytes. `getPublicKey`
1070
+ * resolves the verifying key from the signature's embedded keyId.
1071
+ *
1072
+ * Mirrors cloud's publish-time policy:
1073
+ * - no signature → ok, verified=false (caller decides via trust tier).
1074
+ * - malformed / fails verification → NOT ok.
1075
+ * - unknown keyId → NOT ok (never silently trust).
1076
+ */
1077
+ declare function verifyPublisherSignature(args: {
1078
+ artifact: Uint8Array;
1079
+ signature?: string | null;
1080
+ }, getPublicKey?: (keyId: string) => Promise<KeyInput | null> | KeyInput | null): Promise<PublisherVerifyResult>;
1081
+ /** Verify a platform counter-signature against the version identity + platform public key. */
1082
+ declare function verifyPlatformSignature(version: {
1083
+ package_id: string;
1084
+ version: string;
1085
+ blob_key?: string | null;
1086
+ signature?: string | null;
1087
+ platform_signature?: string | null;
1088
+ }, platformPublicKey: KeyInput): boolean;
1089
+ interface PluginArtifactVerifyResult {
1090
+ /** Overall verdict: both required chains satisfied under the given policy. */
1091
+ ok: boolean;
1092
+ publisherVerified: boolean;
1093
+ platformVerified: boolean;
1094
+ reason?: string;
1095
+ }
1096
+ /**
1097
+ * Verify both trust chains for a downloaded plugin artifact at load time
1098
+ * (ADR-0025 §3.7). The platform counter-signature is the authoritative
1099
+ * marketplace attestation; the publisher signature additionally binds the
1100
+ * exact bytes. `requirePlatform` (default true) rejects artifacts that lack
1101
+ * a valid platform counter-sign — set false for first-party / local builds.
1102
+ */
1103
+ declare function verifyPluginArtifact(input: {
1104
+ artifact: Uint8Array;
1105
+ version: {
1106
+ package_id: string;
1107
+ version: string;
1108
+ blob_key?: string | null;
1109
+ signature?: string | null;
1110
+ platform_signature?: string | null;
1111
+ };
1112
+ }, keys: {
1113
+ platformPublicKey?: KeyInput;
1114
+ getPublisherPublicKey?: (keyId: string) => Promise<KeyInput | null> | KeyInput | null;
1115
+ requirePlatform?: boolean;
1116
+ }): Promise<PluginArtifactVerifyResult>;
1117
+
1003
1118
  /**
1004
1119
  * Plugin Configuration Validator
1005
1120
  *
@@ -1136,6 +1251,19 @@ declare class PluginPermissionEnforcer {
1136
1251
  * @param capabilities - Array of capability declarations
1137
1252
  */
1138
1253
  registerPluginPermissions(pluginName: string, capabilities: PluginCapability[]): void;
1254
+ /**
1255
+ * Register the install-time GRANTED permission set for a plugin
1256
+ * (ADR-0025 F4). This is the structured `{ services, hooks, network, fs }`
1257
+ * grant that the cloud control plane persists to
1258
+ * `sys_package_installation.granted_permissions` after the user consents
1259
+ * at install (ADR §3.5 step 2). The runtime calls this when materializing
1260
+ * a third-party plugin so {@link SecurePluginContext} enforces exactly the
1261
+ * consented surface — independent of whatever the manifest *requested*.
1262
+ *
1263
+ * Prefer this over {@link registerPluginPermissions} for distributed
1264
+ * plugins: it enforces what was granted, not what was declared.
1265
+ */
1266
+ registerGrantedPermissions(pluginName: string, granted: PluginPermissions$1 | null | undefined): void;
1139
1267
  /**
1140
1268
  * Enforce service access permission
1141
1269
  *
@@ -1231,6 +1359,17 @@ declare class SecurePluginContext implements PluginContext {
1231
1359
  * @returns Plugin permission enforcer
1232
1360
  */
1233
1361
  declare function createPluginPermissionEnforcer(logger: Logger): PluginPermissionEnforcer;
1362
+ /**
1363
+ * Build the runtime {@link PluginPermissions} bag from a structured
1364
+ * install-time grant set (ADR-0025 §3.2 `{ services, hooks, network, fs }`).
1365
+ *
1366
+ * Matching: an entry allows when it equals the requested value, is a glob
1367
+ * that matches it, or is the wildcard `*`. Network grants match against the
1368
+ * request URL's host (or the raw URL). `fs` governs both read and write —
1369
+ * the structured grant set does not split the two. A null/empty grant set
1370
+ * denies everything (principle of least privilege).
1371
+ */
1372
+ declare function buildPermissionsFromGrants(granted: PluginPermissions$1 | null | undefined): PluginPermissions;
1234
1373
 
1235
1374
  /**
1236
1375
  * Permission Grant
@@ -1941,4 +2080,4 @@ declare class NamespaceResolver {
1941
2080
  private suggestAlternative;
1942
2081
  }
1943
2082
 
1944
- export { ApiRegistry, type ApiRegistryPluginConfig, CORE_FALLBACK_FACTORIES, DependencyResolver, HotReloadManager, type KernelState, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, index as QA, type ResourceUsage, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type VersionCompatibility, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, getEnv, getMemoryUsage, isNode, resolveLocale, safeExit };
2083
+ export { ApiRegistry, type ApiRegistryPluginConfig, CORE_FALLBACK_FACTORIES, DependencyResolver, HotReloadManager, type KernelState, type KeyInput, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PublisherVerifyResult, index as QA, type ResourceUsage, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type VersionCompatibility, buildPermissionsFromGrants, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, generateEd25519KeyPair, getEnv, getMemoryUsage, isNode, parseSignature, resolveLocale, safeExit, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature };
package/dist/index.d.ts CHANGED
@@ -6,7 +6,8 @@ import { ObjectLogger } from './logger.js';
6
6
  export { createLogger } from './logger.js';
7
7
  import { ConflictResolutionStrategy, ApiRegistryEntryInput, ApiRegistryEntry, ApiDiscoveryQuery, ApiDiscoveryResponse, ApiEndpointRegistration, ApiRegistry as ApiRegistry$1 } from '@objectstack/spec/api';
8
8
  import * as QA from '@objectstack/spec/qa';
9
- import { PluginCapability, PluginPermissionSet, ResourceType, PermissionAction, PluginPermission, SandboxConfig, KernelSecurityScanResult, KernelSecurityVulnerability, PluginHealthCheck, PluginHealthStatus as PluginHealthStatus$1, PluginHealthReport, HotReloadConfig, VersionConstraint, DependencyConflict, SemanticVersion, CompatibilityLevel } from '@objectstack/spec/kernel';
9
+ import { KeyObject } from 'node:crypto';
10
+ import { PluginCapability, PluginPermissions as PluginPermissions$1, PluginPermissionSet, ResourceType, PermissionAction, PluginPermission, SandboxConfig, KernelSecurityScanResult, KernelSecurityVulnerability, PluginHealthCheck, PluginHealthStatus as PluginHealthStatus$1, PluginHealthReport, HotReloadConfig, VersionConstraint, DependencyConflict, SemanticVersion, CompatibilityLevel } from '@objectstack/spec/kernel';
10
11
 
11
12
  /**
12
13
  * Service Lifecycle Types
@@ -1000,6 +1001,120 @@ declare class PluginSignatureVerifier {
1000
1001
  private validateConfig;
1001
1002
  }
1002
1003
 
1004
+ /**
1005
+ * Plugin artifact signing & verification (ADR-0025 §3.4–§3.7, framework F3).
1006
+ *
1007
+ * This is the CANONICAL Ed25519 detached-signature contract shared by the
1008
+ * whole plugin distribution pipeline. It is intentionally byte-for-byte
1009
+ * compatible with the cloud control plane's `package-signing.ts` so the
1010
+ * two never drift:
1011
+ *
1012
+ * - signature string format: `ed25519:<keyId>:<base64url(signature)>`
1013
+ * - publisher signature: Ed25519 over the raw `.osplugin` artifact bytes,
1014
+ * produced by `os plugin sign`, verified by cloud at publish time and by
1015
+ * the runtime when it materializes the artifact.
1016
+ * - platform counter-signature: Ed25519 over {@link counterSignPayload}
1017
+ * (the version identity), produced by cloud at approval, verified by the
1018
+ * runtime at load time as the marketplace's "reviewed + approved" attest.
1019
+ *
1020
+ * Algorithm: Ed25519 via node:crypto (`sign(null, …)` / `verify(null, …)`):
1021
+ * short, deterministic, no padding ambiguity. The `keyId` is an opaque
1022
+ * rotation handle used to resolve the verifying public key.
1023
+ *
1024
+ * The two trust chains the runtime checks before loading a third-party
1025
+ * plugin are combined in {@link verifyPluginArtifact}.
1026
+ */
1027
+
1028
+ declare const SIGNATURE_ALG = "ed25519";
1029
+ type KeyInput = string | KeyObject;
1030
+ /** Generate an Ed25519 keypair as PEM strings (publisher bootstrap / tests). */
1031
+ declare function generateEd25519KeyPair(): {
1032
+ publicKeyPem: string;
1033
+ privateKeyPem: string;
1034
+ };
1035
+ /**
1036
+ * Sign `payload` with an Ed25519 private key, returning the formatted
1037
+ * signature string `ed25519:<keyId>:<base64url(sig)>`.
1038
+ */
1039
+ declare function signPayload(payload: string | Uint8Array, privateKey: KeyInput, keyId?: string): string;
1040
+ interface ParsedSignature {
1041
+ alg: 'ed25519';
1042
+ keyId: string;
1043
+ signature: Uint8Array;
1044
+ }
1045
+ /** Parse an `ed25519:<keyId>:<base64url>` signature string. Returns null if malformed. */
1046
+ declare function parseSignature(s: string | undefined | null): ParsedSignature | null;
1047
+ /** Verify a formatted signature string over `payload` with the given public key. */
1048
+ declare function verifyPayload(payload: string | Uint8Array, signature: string, publicKey: KeyInput): boolean;
1049
+ /**
1050
+ * Canonical payload the platform counter-signs at approval. Binds the
1051
+ * attestation to the version identity + artifact location + the publisher
1052
+ * signature (which itself binds the artifact bytes). MUST match the cloud
1053
+ * control plane's `counterSignPayload` exactly.
1054
+ */
1055
+ declare function counterSignPayload(version: {
1056
+ package_id: string;
1057
+ version: string;
1058
+ blob_key?: string | null;
1059
+ signature?: string | null;
1060
+ }): string;
1061
+ interface PublisherVerifyResult {
1062
+ /** Whether loading may proceed on signature grounds. */
1063
+ ok: boolean;
1064
+ /** True when a signature was present AND cryptographically verified. */
1065
+ verified: boolean;
1066
+ reason?: string;
1067
+ }
1068
+ /**
1069
+ * Verify a publisher signature over the raw artifact bytes. `getPublicKey`
1070
+ * resolves the verifying key from the signature's embedded keyId.
1071
+ *
1072
+ * Mirrors cloud's publish-time policy:
1073
+ * - no signature → ok, verified=false (caller decides via trust tier).
1074
+ * - malformed / fails verification → NOT ok.
1075
+ * - unknown keyId → NOT ok (never silently trust).
1076
+ */
1077
+ declare function verifyPublisherSignature(args: {
1078
+ artifact: Uint8Array;
1079
+ signature?: string | null;
1080
+ }, getPublicKey?: (keyId: string) => Promise<KeyInput | null> | KeyInput | null): Promise<PublisherVerifyResult>;
1081
+ /** Verify a platform counter-signature against the version identity + platform public key. */
1082
+ declare function verifyPlatformSignature(version: {
1083
+ package_id: string;
1084
+ version: string;
1085
+ blob_key?: string | null;
1086
+ signature?: string | null;
1087
+ platform_signature?: string | null;
1088
+ }, platformPublicKey: KeyInput): boolean;
1089
+ interface PluginArtifactVerifyResult {
1090
+ /** Overall verdict: both required chains satisfied under the given policy. */
1091
+ ok: boolean;
1092
+ publisherVerified: boolean;
1093
+ platformVerified: boolean;
1094
+ reason?: string;
1095
+ }
1096
+ /**
1097
+ * Verify both trust chains for a downloaded plugin artifact at load time
1098
+ * (ADR-0025 §3.7). The platform counter-signature is the authoritative
1099
+ * marketplace attestation; the publisher signature additionally binds the
1100
+ * exact bytes. `requirePlatform` (default true) rejects artifacts that lack
1101
+ * a valid platform counter-sign — set false for first-party / local builds.
1102
+ */
1103
+ declare function verifyPluginArtifact(input: {
1104
+ artifact: Uint8Array;
1105
+ version: {
1106
+ package_id: string;
1107
+ version: string;
1108
+ blob_key?: string | null;
1109
+ signature?: string | null;
1110
+ platform_signature?: string | null;
1111
+ };
1112
+ }, keys: {
1113
+ platformPublicKey?: KeyInput;
1114
+ getPublisherPublicKey?: (keyId: string) => Promise<KeyInput | null> | KeyInput | null;
1115
+ requirePlatform?: boolean;
1116
+ }): Promise<PluginArtifactVerifyResult>;
1117
+
1003
1118
  /**
1004
1119
  * Plugin Configuration Validator
1005
1120
  *
@@ -1136,6 +1251,19 @@ declare class PluginPermissionEnforcer {
1136
1251
  * @param capabilities - Array of capability declarations
1137
1252
  */
1138
1253
  registerPluginPermissions(pluginName: string, capabilities: PluginCapability[]): void;
1254
+ /**
1255
+ * Register the install-time GRANTED permission set for a plugin
1256
+ * (ADR-0025 F4). This is the structured `{ services, hooks, network, fs }`
1257
+ * grant that the cloud control plane persists to
1258
+ * `sys_package_installation.granted_permissions` after the user consents
1259
+ * at install (ADR §3.5 step 2). The runtime calls this when materializing
1260
+ * a third-party plugin so {@link SecurePluginContext} enforces exactly the
1261
+ * consented surface — independent of whatever the manifest *requested*.
1262
+ *
1263
+ * Prefer this over {@link registerPluginPermissions} for distributed
1264
+ * plugins: it enforces what was granted, not what was declared.
1265
+ */
1266
+ registerGrantedPermissions(pluginName: string, granted: PluginPermissions$1 | null | undefined): void;
1139
1267
  /**
1140
1268
  * Enforce service access permission
1141
1269
  *
@@ -1231,6 +1359,17 @@ declare class SecurePluginContext implements PluginContext {
1231
1359
  * @returns Plugin permission enforcer
1232
1360
  */
1233
1361
  declare function createPluginPermissionEnforcer(logger: Logger): PluginPermissionEnforcer;
1362
+ /**
1363
+ * Build the runtime {@link PluginPermissions} bag from a structured
1364
+ * install-time grant set (ADR-0025 §3.2 `{ services, hooks, network, fs }`).
1365
+ *
1366
+ * Matching: an entry allows when it equals the requested value, is a glob
1367
+ * that matches it, or is the wildcard `*`. Network grants match against the
1368
+ * request URL's host (or the raw URL). `fs` governs both read and write —
1369
+ * the structured grant set does not split the two. A null/empty grant set
1370
+ * denies everything (principle of least privilege).
1371
+ */
1372
+ declare function buildPermissionsFromGrants(granted: PluginPermissions$1 | null | undefined): PluginPermissions;
1234
1373
 
1235
1374
  /**
1236
1375
  * Permission Grant
@@ -1941,4 +2080,4 @@ declare class NamespaceResolver {
1941
2080
  private suggestAlternative;
1942
2081
  }
1943
2082
 
1944
- export { ApiRegistry, type ApiRegistryPluginConfig, CORE_FALLBACK_FACTORIES, DependencyResolver, HotReloadManager, type KernelState, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, index as QA, type ResourceUsage, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type VersionCompatibility, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, getEnv, getMemoryUsage, isNode, resolveLocale, safeExit };
2083
+ export { ApiRegistry, type ApiRegistryPluginConfig, CORE_FALLBACK_FACTORIES, DependencyResolver, HotReloadManager, type KernelState, type KeyInput, LiteKernel, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PublisherVerifyResult, index as QA, type ResourceUsage, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type VersionCompatibility, buildPermissionsFromGrants, counterSignPayload, createApiRegistryPlugin, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, generateEd25519KeyPair, getEnv, getMemoryUsage, isNode, parseSignature, resolveLocale, safeExit, signPayload, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature };
package/dist/index.js CHANGED
@@ -508,6 +508,108 @@ function createPluginConfigValidator(logger) {
508
508
  return new PluginConfigValidator(logger);
509
509
  }
510
510
 
511
+ // src/security/plugin-artifact-signature.ts
512
+ import {
513
+ sign as cryptoSign,
514
+ verify as cryptoVerify,
515
+ createPublicKey,
516
+ createPrivateKey,
517
+ generateKeyPairSync
518
+ } from "crypto";
519
+ var SIGNATURE_ALG = "ed25519";
520
+ var SIG_PREFIX = "ed25519:";
521
+ function toPrivateKey(key) {
522
+ return typeof key === "string" ? createPrivateKey(key) : key;
523
+ }
524
+ function toPublicKey(key) {
525
+ return typeof key === "string" ? createPublicKey(key) : key;
526
+ }
527
+ function toBytes(payload) {
528
+ return typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
529
+ }
530
+ function generateEd25519KeyPair() {
531
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
532
+ return {
533
+ publicKeyPem: publicKey.export({ type: "spki", format: "pem" }).toString(),
534
+ privateKeyPem: privateKey.export({ type: "pkcs8", format: "pem" }).toString()
535
+ };
536
+ }
537
+ function signPayload(payload, privateKey, keyId = "default") {
538
+ if (keyId.includes(":")) throw new Error('keyId must not contain ":"');
539
+ const sig = cryptoSign(null, toBytes(payload), toPrivateKey(privateKey));
540
+ return `${SIG_PREFIX}${keyId}:${sig.toString("base64url")}`;
541
+ }
542
+ function parseSignature(s) {
543
+ if (typeof s !== "string" || !s.startsWith(SIG_PREFIX)) return null;
544
+ const rest = s.slice(SIG_PREFIX.length);
545
+ const idx = rest.indexOf(":");
546
+ if (idx <= 0) return null;
547
+ const keyId = rest.slice(0, idx);
548
+ const b64 = rest.slice(idx + 1);
549
+ if (!keyId || !b64) return null;
550
+ try {
551
+ return { alg: "ed25519", keyId, signature: new Uint8Array(Buffer.from(b64, "base64url")) };
552
+ } catch {
553
+ return null;
554
+ }
555
+ }
556
+ function verifyPayload(payload, signature, publicKey) {
557
+ const parsed = parseSignature(signature);
558
+ if (!parsed) return false;
559
+ try {
560
+ return cryptoVerify(null, toBytes(payload), toPublicKey(publicKey), parsed.signature);
561
+ } catch {
562
+ return false;
563
+ }
564
+ }
565
+ function counterSignPayload(version) {
566
+ return [
567
+ version.package_id,
568
+ version.version,
569
+ version.blob_key ?? "",
570
+ version.signature ?? ""
571
+ ].join("\n");
572
+ }
573
+ async function verifyPublisherSignature(args, getPublicKey) {
574
+ const sig = args.signature;
575
+ if (!sig) return { ok: true, verified: false, reason: "no signature supplied" };
576
+ const parsed = parseSignature(sig);
577
+ if (!parsed) return { ok: false, verified: false, reason: "signature is malformed" };
578
+ if (!getPublicKey) {
579
+ return { ok: true, verified: false, reason: "no publisher key registry configured" };
580
+ }
581
+ const pub = await getPublicKey(parsed.keyId);
582
+ if (!pub) return { ok: false, verified: false, reason: `unknown publisher key '${parsed.keyId}'` };
583
+ return verifyPayload(args.artifact, sig, pub) ? { ok: true, verified: true } : { ok: false, verified: false, reason: "publisher signature does not match artifact" };
584
+ }
585
+ function verifyPlatformSignature(version, platformPublicKey) {
586
+ if (!version.platform_signature) return false;
587
+ return verifyPayload(counterSignPayload(version), version.platform_signature, platformPublicKey);
588
+ }
589
+ async function verifyPluginArtifact(input, keys) {
590
+ const requirePlatform = keys.requirePlatform ?? true;
591
+ const publisher = await verifyPublisherSignature(
592
+ { artifact: input.artifact, signature: input.version.signature },
593
+ keys.getPublisherPublicKey
594
+ );
595
+ if (!publisher.ok) {
596
+ return { ok: false, publisherVerified: false, platformVerified: false, reason: publisher.reason };
597
+ }
598
+ let platformVerified = false;
599
+ if (keys.platformPublicKey) {
600
+ platformVerified = verifyPlatformSignature(input.version, keys.platformPublicKey);
601
+ }
602
+ if (requirePlatform && !platformVerified) {
603
+ return {
604
+ ok: false,
605
+ publisherVerified: publisher.verified,
606
+ platformVerified,
607
+ reason: keys.platformPublicKey ? "platform counter-signature missing or invalid" : "no platform public key configured"
608
+ };
609
+ }
610
+ return { ok: true, publisherVerified: publisher.verified, platformVerified };
611
+ }
612
+
511
613
  // src/plugin-loader.ts
512
614
  var ServiceLifecycle = /* @__PURE__ */ ((ServiceLifecycle2) => {
513
615
  ServiceLifecycle2["SINGLETON"] = "singleton";
@@ -765,7 +867,15 @@ var PluginLoader = class {
765
867
  if (!plugin.signature) {
766
868
  return;
767
869
  }
768
- this.logger.debug(`Plugin ${plugin.name} has signature (use PluginSignatureVerifier for verification)`);
870
+ const parsed = parseSignature(plugin.signature);
871
+ if (!parsed) {
872
+ throw new Error(
873
+ `Plugin ${plugin.name} carries a malformed signature (expected ed25519:<keyId>:<base64url>)`
874
+ );
875
+ }
876
+ this.logger.debug(
877
+ `Plugin ${plugin.name} signature well-formed (alg=${parsed.alg}, keyId=${parsed.keyId}); artifact verification occurs at materialize time`
878
+ );
769
879
  }
770
880
  async getSingletonService(registration) {
771
881
  let instance = this.serviceInstances.get(registration.name);
@@ -2709,9 +2819,31 @@ var PluginPermissionEnforcer = class {
2709
2819
  capabilityCount: capabilities.length
2710
2820
  });
2711
2821
  }
2822
+ /**
2823
+ * Register the install-time GRANTED permission set for a plugin
2824
+ * (ADR-0025 F4). This is the structured `{ services, hooks, network, fs }`
2825
+ * grant that the cloud control plane persists to
2826
+ * `sys_package_installation.granted_permissions` after the user consents
2827
+ * at install (ADR §3.5 step 2). The runtime calls this when materializing
2828
+ * a third-party plugin so {@link SecurePluginContext} enforces exactly the
2829
+ * consented surface — independent of whatever the manifest *requested*.
2830
+ *
2831
+ * Prefer this over {@link registerPluginPermissions} for distributed
2832
+ * plugins: it enforces what was granted, not what was declared.
2833
+ */
2834
+ registerGrantedPermissions(pluginName, granted) {
2835
+ this.permissionRegistry.set(pluginName, buildPermissionsFromGrants(granted));
2836
+ this.logger.info(`Granted permissions registered for plugin: ${pluginName}`, {
2837
+ plugin: pluginName,
2838
+ services: granted?.services?.length ?? 0,
2839
+ hooks: granted?.hooks?.length ?? 0,
2840
+ network: granted?.network?.length ?? 0,
2841
+ fs: granted?.fs?.length ?? 0
2842
+ });
2843
+ }
2712
2844
  /**
2713
2845
  * Enforce service access permission
2714
- *
2846
+ *
2715
2847
  * @param pluginName - Plugin requesting access
2716
2848
  * @param serviceName - Service to access
2717
2849
  * @throws Error if permission denied
@@ -2974,6 +3106,32 @@ var SecurePluginContext = class {
2974
3106
  function createPluginPermissionEnforcer(logger) {
2975
3107
  return new PluginPermissionEnforcer(logger);
2976
3108
  }
3109
+ function grantGlobMatch(pattern, value) {
3110
+ if (pattern === "*" || pattern === "**") return true;
3111
+ const regexStr = pattern.split("**").map((segment) => segment.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*")).join(".*");
3112
+ return new RegExp(`^${regexStr}$`).test(value);
3113
+ }
3114
+ function hostOf(url) {
3115
+ try {
3116
+ return new URL(url).host;
3117
+ } catch {
3118
+ return url;
3119
+ }
3120
+ }
3121
+ var inList = (list, value) => Array.isArray(list) && list.some((p) => p === value || grantGlobMatch(p, value));
3122
+ function buildPermissionsFromGrants(granted) {
3123
+ const services = granted?.services;
3124
+ const hooks = granted?.hooks;
3125
+ const network = granted?.network;
3126
+ const fs = granted?.fs;
3127
+ return {
3128
+ canAccessService: (name) => inList(services, name),
3129
+ canTriggerHook: (name) => inList(hooks, name),
3130
+ canReadFile: (path) => inList(fs, path),
3131
+ canWriteFile: (path) => inList(fs, path),
3132
+ canNetworkRequest: (url) => inList(network, hostOf(url)) || inList(network, url)
3133
+ };
3134
+ }
2977
3135
 
2978
3136
  // src/security/permission-manager.ts
2979
3137
  var PluginPermissionManager = class {
@@ -4658,9 +4816,12 @@ export {
4658
4816
  PluginSecurityScanner,
4659
4817
  PluginSignatureVerifier,
4660
4818
  qa_exports as QA,
4819
+ SIGNATURE_ALG,
4661
4820
  SecurePluginContext,
4662
4821
  SemanticVersionManager,
4663
4822
  ServiceLifecycle,
4823
+ buildPermissionsFromGrants,
4824
+ counterSignPayload,
4664
4825
  createApiRegistryPlugin,
4665
4826
  createLogger,
4666
4827
  createMemoryCache,
@@ -4670,10 +4831,17 @@ export {
4670
4831
  createMemoryQueue,
4671
4832
  createPluginConfigValidator,
4672
4833
  createPluginPermissionEnforcer,
4834
+ generateEd25519KeyPair,
4673
4835
  getEnv,
4674
4836
  getMemoryUsage,
4675
4837
  isNode,
4838
+ parseSignature,
4676
4839
  resolveLocale,
4677
- safeExit
4840
+ safeExit,
4841
+ signPayload,
4842
+ verifyPayload,
4843
+ verifyPlatformSignature,
4844
+ verifyPluginArtifact,
4845
+ verifyPublisherSignature
4678
4846
  };
4679
4847
  //# sourceMappingURL=index.js.map