every-plugin 3.0.0-rc.15 → 3.0.0-rc.17

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.
Files changed (75) hide show
  1. package/dist/build/artifact-names.cjs +8 -5
  2. package/dist/build/artifact-names.cjs.map +1 -1
  3. package/dist/build/artifact-names.d.cts +7 -5
  4. package/dist/build/artifact-names.d.cts.map +1 -1
  5. package/dist/build/artifact-names.d.mts +7 -5
  6. package/dist/build/artifact-names.d.mts.map +1 -1
  7. package/dist/build/artifact-names.mjs +8 -5
  8. package/dist/build/artifact-names.mjs.map +1 -1
  9. package/dist/build/ui/stubs.cjs +2 -7
  10. package/dist/build/ui/stubs.cjs.map +1 -1
  11. package/dist/build/ui/stubs.d.cts.map +1 -1
  12. package/dist/build/ui/stubs.d.mts.map +1 -1
  13. package/dist/build/ui/stubs.mjs +2 -7
  14. package/dist/build/ui/stubs.mjs.map +1 -1
  15. package/dist/client.cjs +15 -63
  16. package/dist/client.cjs.map +1 -1
  17. package/dist/client.d.cts +12 -11
  18. package/dist/client.d.cts.map +1 -1
  19. package/dist/client.d.mts +12 -11
  20. package/dist/client.d.mts.map +1 -1
  21. package/dist/client.mjs +16 -64
  22. package/dist/client.mjs.map +1 -1
  23. package/dist/errors.cjs +11 -1
  24. package/dist/errors.cjs.map +1 -1
  25. package/dist/errors.d.cts +15 -1
  26. package/dist/errors.d.cts.map +1 -1
  27. package/dist/errors.d.mts +15 -1
  28. package/dist/errors.d.mts.map +1 -1
  29. package/dist/errors.mjs +11 -2
  30. package/dist/errors.mjs.map +1 -1
  31. package/dist/index.cjs +1 -0
  32. package/dist/index.d.cts +4 -4
  33. package/dist/index.d.mts +4 -4
  34. package/dist/index.mjs +2 -2
  35. package/dist/runtime/mf-config.cjs +1 -1
  36. package/dist/runtime/mf-config.mjs +1 -1
  37. package/dist/ui/manifest/construct.cjs +13 -38
  38. package/dist/ui/manifest/construct.cjs.map +1 -1
  39. package/dist/ui/manifest/construct.d.cts +4 -13
  40. package/dist/ui/manifest/construct.d.cts.map +1 -1
  41. package/dist/ui/manifest/construct.d.mts +4 -13
  42. package/dist/ui/manifest/construct.d.mts.map +1 -1
  43. package/dist/ui/manifest/construct.mjs +14 -39
  44. package/dist/ui/manifest/construct.mjs.map +1 -1
  45. package/dist/ui/manifest/descriptor.cjs +1 -1
  46. package/dist/ui/manifest/descriptor.mjs +1 -1
  47. package/dist/ui/manifest/index.d.cts +3 -3
  48. package/dist/ui/manifest/index.d.mts +3 -3
  49. package/dist/ui/manifest/mount-registry.cjs +6 -13
  50. package/dist/ui/manifest/mount-registry.cjs.map +1 -1
  51. package/dist/ui/manifest/mount-registry.d.cts +29 -36
  52. package/dist/ui/manifest/mount-registry.d.cts.map +1 -1
  53. package/dist/ui/manifest/mount-registry.d.mts +29 -36
  54. package/dist/ui/manifest/mount-registry.d.mts.map +1 -1
  55. package/dist/ui/manifest/mount-registry.mjs +6 -13
  56. package/dist/ui/manifest/mount-registry.mjs.map +1 -1
  57. package/dist/version-manifest.cjs +70 -5
  58. package/dist/version-manifest.cjs.map +1 -1
  59. package/dist/version-manifest.d.cts +41 -1
  60. package/dist/version-manifest.d.cts.map +1 -1
  61. package/dist/version-manifest.d.mts +41 -1
  62. package/dist/version-manifest.d.mts.map +1 -1
  63. package/dist/version-manifest.mjs +66 -6
  64. package/dist/version-manifest.mjs.map +1 -1
  65. package/package.json +1 -1
  66. package/skills/plugin-client/SKILL.md +3 -3
  67. package/src/build/artifact-names.ts +8 -5
  68. package/src/build/ui/stubs.ts +2 -7
  69. package/src/client.ts +14 -111
  70. package/src/errors.ts +10 -0
  71. package/src/index.ts +2 -0
  72. package/src/ui/manifest/construct.ts +20 -65
  73. package/src/ui/manifest/index.ts +0 -2
  74. package/src/ui/manifest/mount-registry.ts +20 -20
  75. package/src/version-manifest.ts +77 -10
@@ -12,10 +12,35 @@ let node_crypto = require("node:crypto");
12
12
  * stops growing with artifact kinds and old versions stay fully servable.
13
13
  */
14
14
  const SRI = zod.z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);
15
+ /** Slot-relative directory holding one deploy's immutable file copies (#826). */
16
+ const VERSION_DIR_PATTERN = /^versions\/[0-9a-f]{16}$/;
17
+ /** The attribution block stored in the manifest's `signature` field. The
18
+ * verifying key rides along so a verifier can distinguish "wrong signature"
19
+ * from "signed by a key the config never registered" — both are
20
+ * `SignatureInvalid`, but the reasons name different compromises. */
21
+ const ManifestSignatureSchema = zod.z.object({
22
+ algorithm: zod.z.literal("ed25519"),
23
+ /** NEAR-format verifying key (`ed25519:<base58>`) — must equal the
24
+ * config's `publish.verification.publicKey`. */
25
+ publicKey: zod.z.string().min(1),
26
+ /** base64 ed25519 signature over the canonical payload bytes. */
27
+ value: zod.z.string().min(1)
28
+ });
15
29
  const WorkspaceVersionManifestSchema = zod.z.object({
16
30
  version: zod.z.string().regex(/^[0-9a-f]{16}$/),
17
31
  builtAt: zod.z.string().optional(),
18
32
  gitSha: zod.z.string().optional(),
33
+ /**
34
+ * Slot-relative directory (`versions/<dirId>`) holding this deploy's
35
+ * byte-immutable copies of every dist file (#826). When present, all
36
+ * artifact coordinates below resolve against `<slotBase>/<dir>/` — so a
37
+ * pinned child's entry fetches its expose chunk and chunks from the same
38
+ * frozen directory instead of the mutable slot root. Absent on legacy
39
+ * manifests: coordinates resolve against the slot root exactly as before.
40
+ * Derived from the `files` map (see `versionDirPath`), so it participates
41
+ * in the version hash without circularity.
42
+ */
43
+ dir: zod.z.string().regex(VERSION_DIR_PATTERN).optional(),
19
44
  entry: zod.z.string().min(1),
20
45
  entryIntegrity: SRI,
21
46
  ssr: zod.z.object({
@@ -48,8 +73,38 @@ const WorkspaceVersionManifestSchema = zod.z.object({
48
73
  source: zod.z.object({
49
74
  file: zod.z.string().min(1),
50
75
  integrity: SRI
51
- }).optional()
76
+ }).optional(),
77
+ /**
78
+ * Attribution (ADR 0046 ticket 05): the deploy train's ed25519 signature
79
+ * over `signaturePayloadOf(this document)` — SRI attests WHAT the bytes
80
+ * are, the signature attests WHO published them, with the verifying key
81
+ * anchored in the on-chain config (`publish.verification`). Absent on
82
+ * rollouts with attribution off; readers that predate the field ignore it
83
+ * (zod strips unknown keys). Excluded from the version-id derivation —
84
+ * like `builtAt`, it is not a coordinate of the build.
85
+ */
86
+ signature: ManifestSignatureSchema.optional()
52
87
  });
88
+ /** Canonical key-sorted JSON serialization — the same form the version-id
89
+ * derivation hashes (undefined-valued entries dropped). Exported so the
90
+ * signature payload and the version id can never disagree about what
91
+ * "canonical" means. */
92
+ function stableStringify(value) {
93
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
94
+ if (value && typeof value === "object") return `{${Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).sort().join(",")}}`;
95
+ return JSON.stringify(value) ?? "null";
96
+ }
97
+ /**
98
+ * The bytes a manifest signature attests: the document minus its reserved
99
+ * `signature` field, canonically serialized (key-sorted — the version-id
100
+ * rule). The signer signs this before the field exists; the verifier
101
+ * re-derives it from the parsed document, so the stored document's
102
+ * whitespace never matters.
103
+ */
104
+ function signaturePayloadOf(manifest) {
105
+ const { signature: _signature, ...payload } = manifest;
106
+ return stableStringify(payload);
107
+ }
53
108
  /**
54
109
  * Compose the manifest with its `version` derived from the canonical content
55
110
  * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —
@@ -65,13 +120,23 @@ function composeVersionManifest(input) {
65
120
  version
66
121
  });
67
122
  }
68
- function stableStringify(value) {
69
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
70
- if (value && typeof value === "object") return `{${Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).sort().join(",")}}`;
71
- return JSON.stringify(value) ?? "null";
123
+ /**
124
+ * The content-addressed directory name for one dist's file set (#826): a
125
+ * 16-hex hash over the canonical files map. Identical bytes → identical
126
+ * directory → re-deploying unchanged content is a server-side no-op, and a
127
+ * published pin's directory can never be rewritten by a later deploy
128
+ * (the upload service refuses conflicting writes under `versions/`).
129
+ */
130
+ function versionDirPath(files) {
131
+ return `versions/${(0, node_crypto.createHash)("sha256").update(stableStringify(files)).digest("hex").slice(0, 16)}`;
72
132
  }
73
133
 
74
134
  //#endregion
135
+ exports.ManifestSignatureSchema = ManifestSignatureSchema;
136
+ exports.VERSION_DIR_PATTERN = VERSION_DIR_PATTERN;
75
137
  exports.WorkspaceVersionManifestSchema = WorkspaceVersionManifestSchema;
76
138
  exports.composeVersionManifest = composeVersionManifest;
139
+ exports.signaturePayloadOf = signaturePayloadOf;
140
+ exports.stableStringify = stableStringify;
141
+ exports.versionDirPath = versionDirPath;
77
142
  //# sourceMappingURL=version-manifest.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"version-manifest.cjs","names":["z","createHash"],"sources":["../src/version-manifest.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * WorkspaceVersionManifest — the immutable per-deploy document at\n * `bundles/<account>/<gateway>/<workspace>/versions/<version>.json`\n * (ADR 0020, as amended; atomic-deploys MAP decision 1). The published\n * config's slot pins this document via its `pin` (`manifest` + the\n * manifest's own SRI); every artifact coordinate lives here, so the config\n * stops growing with artifact kinds and old versions stay fully servable.\n */\n\nconst SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);\n\nexport const WorkspaceVersionManifestSchema = z.object({\n version: z.string().regex(/^[0-9a-f]{16}$/),\n builtAt: z.string().optional(),\n gitSha: z.string().optional(),\n entry: z.string().min(1),\n entryIntegrity: SRI,\n ssr: z\n .object({\n entry: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n browserManifest: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /** extra assets (css, icons) — logical name → served-object SRI */\n assets: z.record(z.string(), SRI).optional(),\n /** shared-dependency versions this build was built against */\n shared: z.record(z.string(), z.string()).optional(),\n /**\n * Full per-file digest map of the dist (object path → served-object SRI).\n * Written by `every-plugin`-based deploys so the next deploy can diff against\n * it and skip re-uploading unchanged files; readers that predate the field\n * ignore it (zod strips unknown keys). Participates in the version hash.\n */\n files: z.record(z.string(), SRI).optional(),\n /**\n * Source archive of the workspace subtree (object path → served-object SRI)\n * published beside the dists when the runtime opts in via\n * `publish.sourcePublish` — lets `bos import` dereference a published\n * config slot to the exact source bytes that build came from, with no\n * GitHub dependency. Readers that predate the field ignore it (zod strips\n * unknown keys). Participates in the version hash.\n */\n source: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n});\nexport type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;\n\n/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */\nexport type SourceRef = NonNullable<WorkspaceVersionManifest[\"source\"]>;\n\nexport interface VersionManifestInput {\n /** Omit: a per-build timestamp makes the same version id map to different\n * manifest bytes per machine, breaking the pin's SRI against the image's\n * staged copy and the CDN's immutability for republished versions. */\n builtAt?: string;\n gitSha?: string;\n entry: string;\n entryIntegrity: string;\n ssr?: { entry: string; integrity: string };\n browserManifest?: { file: string; integrity: string };\n assets?: Record<string, string>;\n shared?: Record<string, string>;\n files?: Record<string, string>;\n source?: { file: string; integrity: string };\n}\n\n/**\n * Compose the manifest with its `version` derived from the canonical content\n * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —\n * identical bytes → identical version id regardless of when they were built,\n * so republishing unchanged content is a pointer no-op. Any changed\n * coordinate → a new version (and a new file).\n */\nexport function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest {\n const { builtAt: _builtAt, ...hashedInputs } = input;\n const version = createHash(\"sha256\")\n .update(stableStringify(hashedInputs))\n .digest(\"hex\")\n .slice(0, 16);\n return WorkspaceVersionManifestSchema.parse({ ...input, version });\n}\n\nfunction stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n .sort()\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,MAAM,MAAMA,MAAE,OAAO,CAAC,CAAC,MAAM,0BAA0B;AAEvD,MAAa,iCAAiCA,MAAE,OAAO;CACrD,SAASA,MAAE,OAAO,CAAC,CAAC,MAAM,gBAAgB;CAC1C,SAASA,MAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,QAAQA,MAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,gBAAgB;CAChB,KAAKA,MACF,OAAO;EACN,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACvB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;CACZ,iBAAiBA,MACd,OAAO;EACN,MAAMA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;CAEZ,QAAQA,MAAE,OAAOA,MAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;CAE3C,QAAQA,MAAE,OAAOA,MAAE,OAAO,GAAGA,MAAE,OAAO,CAAC,CAAC,CAAC,SAAS;;;;;;;CAOlD,OAAOA,MAAE,OAAOA,MAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;;;;;;;;CAS1C,QAAQA,MACL,OAAO;EACN,MAAMA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;AACd,CAAC;;;;;;;;AA6BD,SAAgB,uBAAuB,OAAuD;CAC5F,MAAM,EAAE,SAAS,UAAU,GAAG,iBAAiB;CAC/C,MAAM,cAAUC,wBAAW,QAAQ,CAAC,CACjC,OAAO,gBAAgB,YAAY,CAAC,CAAC,CACrC,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;CACd,OAAO,+BAA+B,MAAM;EAAE,GAAG;EAAO;CAAQ,CAAC;AACnE;AAEA,SAAS,gBAAgB,OAAwB;CAC/C,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,IAAI,eAAe,CAAC,CAAC,KAAK,GAAG,EAAE;CAC1E,IAAI,SAAS,OAAO,UAAU,UAC5B,OAAO,IAAI,OAAO,QAAQ,KAAK,CAAC,CAC7B,QAAQ,GAAG,OAAO,MAAM,MAAS,CAAC,CAClC,KAAK,CAAC,GAAG,OAAO,GAAG,KAAK,UAAU,CAAC,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAC7D,KAAK,CAAC,CACN,KAAK,GAAG,EAAE;CAEf,OAAO,KAAK,UAAU,KAAK,KAAK;AAClC"}
1
+ {"version":3,"file":"version-manifest.cjs","names":["z","createHash"],"sources":["../src/version-manifest.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * WorkspaceVersionManifest — the immutable per-deploy document at\n * `bundles/<account>/<gateway>/<workspace>/versions/<version>.json`\n * (ADR 0020, as amended; atomic-deploys MAP decision 1). The published\n * config's slot pins this document via its `pin` (`manifest` + the\n * manifest's own SRI); every artifact coordinate lives here, so the config\n * stops growing with artifact kinds and old versions stay fully servable.\n */\n\nconst SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);\n\n/** Slot-relative directory holding one deploy's immutable file copies (#826). */\nexport const VERSION_DIR_PATTERN = /^versions\\/[0-9a-f]{16}$/;\n\n/** The attribution block stored in the manifest's `signature` field. The\n * verifying key rides along so a verifier can distinguish \"wrong signature\"\n * from \"signed by a key the config never registered\" — both are\n * `SignatureInvalid`, but the reasons name different compromises. */\nexport const ManifestSignatureSchema = z.object({\n algorithm: z.literal(\"ed25519\"),\n /** NEAR-format verifying key (`ed25519:<base58>`) — must equal the\n * config's `publish.verification.publicKey`. */\n publicKey: z.string().min(1),\n /** base64 ed25519 signature over the canonical payload bytes. */\n value: z.string().min(1),\n});\nexport type ManifestSignature = z.infer<typeof ManifestSignatureSchema>;\n\nexport const WorkspaceVersionManifestSchema = z.object({\n version: z.string().regex(/^[0-9a-f]{16}$/),\n builtAt: z.string().optional(),\n gitSha: z.string().optional(),\n /**\n * Slot-relative directory (`versions/<dirId>`) holding this deploy's\n * byte-immutable copies of every dist file (#826). When present, all\n * artifact coordinates below resolve against `<slotBase>/<dir>/` — so a\n * pinned child's entry fetches its expose chunk and chunks from the same\n * frozen directory instead of the mutable slot root. Absent on legacy\n * manifests: coordinates resolve against the slot root exactly as before.\n * Derived from the `files` map (see `versionDirPath`), so it participates\n * in the version hash without circularity.\n */\n dir: z.string().regex(VERSION_DIR_PATTERN).optional(),\n entry: z.string().min(1),\n entryIntegrity: SRI,\n ssr: z\n .object({\n entry: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n browserManifest: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /** extra assets (css, icons) — logical name → served-object SRI */\n assets: z.record(z.string(), SRI).optional(),\n /** shared-dependency versions this build was built against */\n shared: z.record(z.string(), z.string()).optional(),\n /**\n * Full per-file digest map of the dist (object path → served-object SRI).\n * Written by `every-plugin`-based deploys so the next deploy can diff against\n * it and skip re-uploading unchanged files; readers that predate the field\n * ignore it (zod strips unknown keys). Participates in the version hash.\n */\n files: z.record(z.string(), SRI).optional(),\n /**\n * Source archive of the workspace subtree (object path → served-object SRI)\n * published beside the dists when the runtime opts in via\n * `publish.sourcePublish` — lets `bos import` dereference a published\n * config slot to the exact source bytes that build came from, with no\n * GitHub dependency. Readers that predate the field ignore it (zod strips\n * unknown keys). Participates in the version hash.\n */\n source: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /**\n * Attribution (ADR 0046 ticket 05): the deploy train's ed25519 signature\n * over `signaturePayloadOf(this document)` — SRI attests WHAT the bytes\n * are, the signature attests WHO published them, with the verifying key\n * anchored in the on-chain config (`publish.verification`). Absent on\n * rollouts with attribution off; readers that predate the field ignore it\n * (zod strips unknown keys). Excluded from the version-id derivation —\n * like `builtAt`, it is not a coordinate of the build.\n */\n signature: ManifestSignatureSchema.optional(),\n});\nexport type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;\n\n/** Canonical key-sorted JSON serialization — the same form the version-id\n * derivation hashes (undefined-valued entries dropped). Exported so the\n * signature payload and the version id can never disagree about what\n * \"canonical\" means. */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n .sort()\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\n/**\n * The bytes a manifest signature attests: the document minus its reserved\n * `signature` field, canonically serialized (key-sorted — the version-id\n * rule). The signer signs this before the field exists; the verifier\n * re-derives it from the parsed document, so the stored document's\n * whitespace never matters.\n */\nexport function signaturePayloadOf(manifest: WorkspaceVersionManifest): string {\n const { signature: _signature, ...payload } = manifest;\n return stableStringify(payload);\n}\n\n/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */\nexport type SourceRef = NonNullable<WorkspaceVersionManifest[\"source\"]>;\n\nexport interface VersionManifestInput {\n /** Omit: a per-build timestamp makes the same version id map to different\n * manifest bytes per machine, breaking the pin's SRI against the image's\n * staged copy and the CDN's immutability for republished versions. */\n builtAt?: string;\n gitSha?: string;\n dir?: string;\n entry: string;\n entryIntegrity: string;\n ssr?: { entry: string; integrity: string };\n browserManifest?: { file: string; integrity: string };\n assets?: Record<string, string>;\n shared?: Record<string, string>;\n files?: Record<string, string>;\n source?: { file: string; integrity: string };\n}\n\n/**\n * Compose the manifest with its `version` derived from the canonical content\n * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —\n * identical bytes → identical version id regardless of when they were built,\n * so republishing unchanged content is a pointer no-op. Any changed\n * coordinate → a new version (and a new file).\n */\nexport function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest {\n const { builtAt: _builtAt, ...hashedInputs } = input;\n const version = createHash(\"sha256\")\n .update(stableStringify(hashedInputs))\n .digest(\"hex\")\n .slice(0, 16);\n return WorkspaceVersionManifestSchema.parse({ ...input, version });\n}\n\n/**\n * The content-addressed directory name for one dist's file set (#826): a\n * 16-hex hash over the canonical files map. Identical bytes → identical\n * directory → re-deploying unchanged content is a server-side no-op, and a\n * published pin's directory can never be rewritten by a later deploy\n * (the upload service refuses conflicting writes under `versions/`).\n */\nexport function versionDirPath(files: Record<string, string>): string {\n const id = createHash(\"sha256\").update(stableStringify(files)).digest(\"hex\").slice(0, 16);\n return `versions/${id}`;\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,MAAM,MAAMA,MAAE,OAAO,CAAC,CAAC,MAAM,0BAA0B;;AAGvD,MAAa,sBAAsB;;;;;AAMnC,MAAa,0BAA0BA,MAAE,OAAO;CAC9C,WAAWA,MAAE,QAAQ,SAAS;;;CAG9B,WAAWA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;;CAE3B,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;AACzB,CAAC;AAGD,MAAa,iCAAiCA,MAAE,OAAO;CACrD,SAASA,MAAE,OAAO,CAAC,CAAC,MAAM,gBAAgB;CAC1C,SAASA,MAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,QAAQA,MAAE,OAAO,CAAC,CAAC,SAAS;;;;;;;;;;;CAW5B,KAAKA,MAAE,OAAO,CAAC,CAAC,MAAM,mBAAmB,CAAC,CAAC,SAAS;CACpD,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,gBAAgB;CAChB,KAAKA,MACF,OAAO;EACN,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACvB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;CACZ,iBAAiBA,MACd,OAAO;EACN,MAAMA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;CAEZ,QAAQA,MAAE,OAAOA,MAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;CAE3C,QAAQA,MAAE,OAAOA,MAAE,OAAO,GAAGA,MAAE,OAAO,CAAC,CAAC,CAAC,SAAS;;;;;;;CAOlD,OAAOA,MAAE,OAAOA,MAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;;;;;;;;CAS1C,QAAQA,MACL,OAAO;EACN,MAAMA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;;;;;;;;;CAUZ,WAAW,wBAAwB,SAAS;AAC9C,CAAC;;;;;AAOD,SAAgB,gBAAgB,OAAwB;CACtD,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,IAAI,eAAe,CAAC,CAAC,KAAK,GAAG,EAAE;CAC1E,IAAI,SAAS,OAAO,UAAU,UAC5B,OAAO,IAAI,OAAO,QAAQ,KAAK,CAAC,CAC7B,QAAQ,GAAG,OAAO,MAAM,MAAS,CAAC,CAClC,KAAK,CAAC,GAAG,OAAO,GAAG,KAAK,UAAU,CAAC,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAC7D,KAAK,CAAC,CACN,KAAK,GAAG,EAAE;CAEf,OAAO,KAAK,UAAU,KAAK,KAAK;AAClC;;;;;;;;AASA,SAAgB,mBAAmB,UAA4C;CAC7E,MAAM,EAAE,WAAW,YAAY,GAAG,YAAY;CAC9C,OAAO,gBAAgB,OAAO;AAChC;;;;;;;;AA6BA,SAAgB,uBAAuB,OAAuD;CAC5F,MAAM,EAAE,SAAS,UAAU,GAAG,iBAAiB;CAC/C,MAAM,cAAUC,wBAAW,QAAQ,CAAC,CACjC,OAAO,gBAAgB,YAAY,CAAC,CAAC,CACrC,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;CACd,OAAO,+BAA+B,MAAM;EAAE,GAAG;EAAO;CAAQ,CAAC;AACnE;;;;;;;;AASA,SAAgB,eAAe,OAAuC;CAEpE,OAAO,gBADIA,wBAAW,QAAQ,CAAC,CAAC,OAAO,gBAAgB,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAClE;AACtB"}
@@ -1,9 +1,22 @@
1
1
  import { z } from "zod";
2
2
  //#region src/version-manifest.d.ts
3
+ /** Slot-relative directory holding one deploy's immutable file copies (#826). */
4
+ declare const VERSION_DIR_PATTERN: RegExp;
5
+ /** The attribution block stored in the manifest's `signature` field. The
6
+ * verifying key rides along so a verifier can distinguish "wrong signature"
7
+ * from "signed by a key the config never registered" — both are
8
+ * `SignatureInvalid`, but the reasons name different compromises. */
9
+ declare const ManifestSignatureSchema: z.ZodObject<{
10
+ algorithm: z.ZodLiteral<"ed25519">;
11
+ publicKey: z.ZodString;
12
+ value: z.ZodString;
13
+ }, z.core.$strip>;
14
+ type ManifestSignature = z.infer<typeof ManifestSignatureSchema>;
3
15
  declare const WorkspaceVersionManifestSchema: z.ZodObject<{
4
16
  version: z.ZodString;
5
17
  builtAt: z.ZodOptional<z.ZodString>;
6
18
  gitSha: z.ZodOptional<z.ZodString>;
19
+ dir: z.ZodOptional<z.ZodString>;
7
20
  entry: z.ZodString;
8
21
  entryIntegrity: z.ZodString;
9
22
  ssr: z.ZodOptional<z.ZodObject<{
@@ -21,8 +34,26 @@ declare const WorkspaceVersionManifestSchema: z.ZodObject<{
21
34
  file: z.ZodString;
22
35
  integrity: z.ZodString;
23
36
  }, z.core.$strip>>;
37
+ signature: z.ZodOptional<z.ZodObject<{
38
+ algorithm: z.ZodLiteral<"ed25519">;
39
+ publicKey: z.ZodString;
40
+ value: z.ZodString;
41
+ }, z.core.$strip>>;
24
42
  }, z.core.$strip>;
25
43
  type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;
44
+ /** Canonical key-sorted JSON serialization — the same form the version-id
45
+ * derivation hashes (undefined-valued entries dropped). Exported so the
46
+ * signature payload and the version id can never disagree about what
47
+ * "canonical" means. */
48
+ declare function stableStringify(value: unknown): string;
49
+ /**
50
+ * The bytes a manifest signature attests: the document minus its reserved
51
+ * `signature` field, canonically serialized (key-sorted — the version-id
52
+ * rule). The signer signs this before the field exists; the verifier
53
+ * re-derives it from the parsed document, so the stored document's
54
+ * whitespace never matters.
55
+ */
56
+ declare function signaturePayloadOf(manifest: WorkspaceVersionManifest): string;
26
57
  /** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */
27
58
  type SourceRef = NonNullable<WorkspaceVersionManifest["source"]>;
28
59
  interface VersionManifestInput {
@@ -31,6 +62,7 @@ interface VersionManifestInput {
31
62
  * staged copy and the CDN's immutability for republished versions. */
32
63
  builtAt?: string;
33
64
  gitSha?: string;
65
+ dir?: string;
34
66
  entry: string;
35
67
  entryIntegrity: string;
36
68
  ssr?: {
@@ -57,6 +89,14 @@ interface VersionManifestInput {
57
89
  * coordinate → a new version (and a new file).
58
90
  */
59
91
  declare function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest;
92
+ /**
93
+ * The content-addressed directory name for one dist's file set (#826): a
94
+ * 16-hex hash over the canonical files map. Identical bytes → identical
95
+ * directory → re-deploying unchanged content is a server-side no-op, and a
96
+ * published pin's directory can never be rewritten by a later deploy
97
+ * (the upload service refuses conflicting writes under `versions/`).
98
+ */
99
+ declare function versionDirPath(files: Record<string, string>): string;
60
100
  //#endregion
61
- export { SourceRef, VersionManifestInput, WorkspaceVersionManifest, WorkspaceVersionManifestSchema, composeVersionManifest };
101
+ export { ManifestSignature, ManifestSignatureSchema, SourceRef, VERSION_DIR_PATTERN, VersionManifestInput, WorkspaceVersionManifest, WorkspaceVersionManifestSchema, composeVersionManifest, signaturePayloadOf, stableStringify, versionDirPath };
62
102
  //# sourceMappingURL=version-manifest.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version-manifest.d.cts","names":[],"sources":["../src/version-manifest.ts"],"mappings":";;cAca,gCAA8B,EAAA;;;;;;;;;;;;;;;;;;;;;GA2CzC,EAAA,KAAA;KACU,2BAA2B,EAAE,aAAa;;KAG1C,YAAY,YAAY;UAEnB;;;;EAIf;EACA;EACA;EACA;EACA;IAAQ;IAAe;;EACvB;IAAoB;IAAc;;EAClC,SAAS;EACT,SAAS;EACT,QAAQ;EACR;IAAW;IAAc;;;;;;;;;;iBAUX,uBAAuB,OAAO,uBAAuB"}
1
+ {"version":3,"file":"version-manifest.d.cts","names":[],"sources":["../src/version-manifest.ts"],"mappings":";;;cAea,qBAAmB;;;;;cAMnB,yBAAuB,EAAA;;;;GAOlC,EAAA,KAAA;KACU,oBAAoB,EAAE,aAAa;cAElC,gCAA8B,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEzC,EAAA,KAAA;KACU,2BAA2B,EAAE,aAAa;;;;;iBAMtC,gBAAgB;;;;;;;;iBAmBhB,mBAAmB,UAAU;;KAMjC,YAAY,YAAY;UAEnB;;;;EAIf;EACA;EACA;EACA;EACA;EACA;IAAQ;IAAe;;EACvB;IAAoB;IAAc;;EAClC,SAAS;EACT,SAAS;EACT,QAAQ;EACR;IAAW;IAAc;;;;;;;;;;iBAUX,uBAAuB,OAAO,uBAAuB;;;;;;;;iBAgBrD,eAAe,OAAO"}
@@ -1,9 +1,22 @@
1
1
  import { z } from "zod";
2
2
  //#region src/version-manifest.d.ts
3
+ /** Slot-relative directory holding one deploy's immutable file copies (#826). */
4
+ declare const VERSION_DIR_PATTERN: RegExp;
5
+ /** The attribution block stored in the manifest's `signature` field. The
6
+ * verifying key rides along so a verifier can distinguish "wrong signature"
7
+ * from "signed by a key the config never registered" — both are
8
+ * `SignatureInvalid`, but the reasons name different compromises. */
9
+ declare const ManifestSignatureSchema: z.ZodObject<{
10
+ algorithm: z.ZodLiteral<"ed25519">;
11
+ publicKey: z.ZodString;
12
+ value: z.ZodString;
13
+ }, z.core.$strip>;
14
+ type ManifestSignature = z.infer<typeof ManifestSignatureSchema>;
3
15
  declare const WorkspaceVersionManifestSchema: z.ZodObject<{
4
16
  version: z.ZodString;
5
17
  builtAt: z.ZodOptional<z.ZodString>;
6
18
  gitSha: z.ZodOptional<z.ZodString>;
19
+ dir: z.ZodOptional<z.ZodString>;
7
20
  entry: z.ZodString;
8
21
  entryIntegrity: z.ZodString;
9
22
  ssr: z.ZodOptional<z.ZodObject<{
@@ -21,8 +34,26 @@ declare const WorkspaceVersionManifestSchema: z.ZodObject<{
21
34
  file: z.ZodString;
22
35
  integrity: z.ZodString;
23
36
  }, z.core.$strip>>;
37
+ signature: z.ZodOptional<z.ZodObject<{
38
+ algorithm: z.ZodLiteral<"ed25519">;
39
+ publicKey: z.ZodString;
40
+ value: z.ZodString;
41
+ }, z.core.$strip>>;
24
42
  }, z.core.$strip>;
25
43
  type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;
44
+ /** Canonical key-sorted JSON serialization — the same form the version-id
45
+ * derivation hashes (undefined-valued entries dropped). Exported so the
46
+ * signature payload and the version id can never disagree about what
47
+ * "canonical" means. */
48
+ declare function stableStringify(value: unknown): string;
49
+ /**
50
+ * The bytes a manifest signature attests: the document minus its reserved
51
+ * `signature` field, canonically serialized (key-sorted — the version-id
52
+ * rule). The signer signs this before the field exists; the verifier
53
+ * re-derives it from the parsed document, so the stored document's
54
+ * whitespace never matters.
55
+ */
56
+ declare function signaturePayloadOf(manifest: WorkspaceVersionManifest): string;
26
57
  /** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */
27
58
  type SourceRef = NonNullable<WorkspaceVersionManifest["source"]>;
28
59
  interface VersionManifestInput {
@@ -31,6 +62,7 @@ interface VersionManifestInput {
31
62
  * staged copy and the CDN's immutability for republished versions. */
32
63
  builtAt?: string;
33
64
  gitSha?: string;
65
+ dir?: string;
34
66
  entry: string;
35
67
  entryIntegrity: string;
36
68
  ssr?: {
@@ -57,6 +89,14 @@ interface VersionManifestInput {
57
89
  * coordinate → a new version (and a new file).
58
90
  */
59
91
  declare function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest;
92
+ /**
93
+ * The content-addressed directory name for one dist's file set (#826): a
94
+ * 16-hex hash over the canonical files map. Identical bytes → identical
95
+ * directory → re-deploying unchanged content is a server-side no-op, and a
96
+ * published pin's directory can never be rewritten by a later deploy
97
+ * (the upload service refuses conflicting writes under `versions/`).
98
+ */
99
+ declare function versionDirPath(files: Record<string, string>): string;
60
100
  //#endregion
61
- export { SourceRef, VersionManifestInput, WorkspaceVersionManifest, WorkspaceVersionManifestSchema, composeVersionManifest };
101
+ export { ManifestSignature, ManifestSignatureSchema, SourceRef, VERSION_DIR_PATTERN, VersionManifestInput, WorkspaceVersionManifest, WorkspaceVersionManifestSchema, composeVersionManifest, signaturePayloadOf, stableStringify, versionDirPath };
62
102
  //# sourceMappingURL=version-manifest.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version-manifest.d.mts","names":[],"sources":["../src/version-manifest.ts"],"mappings":";;cAca,gCAA8B,EAAA;;;;;;;;;;;;;;;;;;;;;GA2CzC,EAAA,KAAA;KACU,2BAA2B,EAAE,aAAa;;KAG1C,YAAY,YAAY;UAEnB;;;;EAIf;EACA;EACA;EACA;EACA;IAAQ;IAAe;;EACvB;IAAoB;IAAc;;EAClC,SAAS;EACT,SAAS;EACT,QAAQ;EACR;IAAW;IAAc;;;;;;;;;;iBAUX,uBAAuB,OAAO,uBAAuB"}
1
+ {"version":3,"file":"version-manifest.d.mts","names":[],"sources":["../src/version-manifest.ts"],"mappings":";;;cAea,qBAAmB;;;;;cAMnB,yBAAuB,EAAA;;;;GAOlC,EAAA,KAAA;KACU,oBAAoB,EAAE,aAAa;cAElC,gCAA8B,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEzC,EAAA,KAAA;KACU,2BAA2B,EAAE,aAAa;;;;;iBAMtC,gBAAgB;;;;;;;;iBAmBhB,mBAAmB,UAAU;;KAMjC,YAAY,YAAY;UAEnB;;;;EAIf;EACA;EACA;EACA;EACA;EACA;IAAQ;IAAe;;EACvB;IAAoB;IAAc;;EAClC,SAAS;EACT,SAAS;EACT,QAAQ;EACR;IAAW;IAAc;;;;;;;;;;iBAUX,uBAAuB,OAAO,uBAAuB;;;;;;;;iBAgBrD,eAAe,OAAO"}
@@ -11,10 +11,35 @@ import { createHash } from "node:crypto";
11
11
  * stops growing with artifact kinds and old versions stay fully servable.
12
12
  */
13
13
  const SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);
14
+ /** Slot-relative directory holding one deploy's immutable file copies (#826). */
15
+ const VERSION_DIR_PATTERN = /^versions\/[0-9a-f]{16}$/;
16
+ /** The attribution block stored in the manifest's `signature` field. The
17
+ * verifying key rides along so a verifier can distinguish "wrong signature"
18
+ * from "signed by a key the config never registered" — both are
19
+ * `SignatureInvalid`, but the reasons name different compromises. */
20
+ const ManifestSignatureSchema = z.object({
21
+ algorithm: z.literal("ed25519"),
22
+ /** NEAR-format verifying key (`ed25519:<base58>`) — must equal the
23
+ * config's `publish.verification.publicKey`. */
24
+ publicKey: z.string().min(1),
25
+ /** base64 ed25519 signature over the canonical payload bytes. */
26
+ value: z.string().min(1)
27
+ });
14
28
  const WorkspaceVersionManifestSchema = z.object({
15
29
  version: z.string().regex(/^[0-9a-f]{16}$/),
16
30
  builtAt: z.string().optional(),
17
31
  gitSha: z.string().optional(),
32
+ /**
33
+ * Slot-relative directory (`versions/<dirId>`) holding this deploy's
34
+ * byte-immutable copies of every dist file (#826). When present, all
35
+ * artifact coordinates below resolve against `<slotBase>/<dir>/` — so a
36
+ * pinned child's entry fetches its expose chunk and chunks from the same
37
+ * frozen directory instead of the mutable slot root. Absent on legacy
38
+ * manifests: coordinates resolve against the slot root exactly as before.
39
+ * Derived from the `files` map (see `versionDirPath`), so it participates
40
+ * in the version hash without circularity.
41
+ */
42
+ dir: z.string().regex(VERSION_DIR_PATTERN).optional(),
18
43
  entry: z.string().min(1),
19
44
  entryIntegrity: SRI,
20
45
  ssr: z.object({
@@ -47,8 +72,38 @@ const WorkspaceVersionManifestSchema = z.object({
47
72
  source: z.object({
48
73
  file: z.string().min(1),
49
74
  integrity: SRI
50
- }).optional()
75
+ }).optional(),
76
+ /**
77
+ * Attribution (ADR 0046 ticket 05): the deploy train's ed25519 signature
78
+ * over `signaturePayloadOf(this document)` — SRI attests WHAT the bytes
79
+ * are, the signature attests WHO published them, with the verifying key
80
+ * anchored in the on-chain config (`publish.verification`). Absent on
81
+ * rollouts with attribution off; readers that predate the field ignore it
82
+ * (zod strips unknown keys). Excluded from the version-id derivation —
83
+ * like `builtAt`, it is not a coordinate of the build.
84
+ */
85
+ signature: ManifestSignatureSchema.optional()
51
86
  });
87
+ /** Canonical key-sorted JSON serialization — the same form the version-id
88
+ * derivation hashes (undefined-valued entries dropped). Exported so the
89
+ * signature payload and the version id can never disagree about what
90
+ * "canonical" means. */
91
+ function stableStringify(value) {
92
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
93
+ if (value && typeof value === "object") return `{${Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).sort().join(",")}}`;
94
+ return JSON.stringify(value) ?? "null";
95
+ }
96
+ /**
97
+ * The bytes a manifest signature attests: the document minus its reserved
98
+ * `signature` field, canonically serialized (key-sorted — the version-id
99
+ * rule). The signer signs this before the field exists; the verifier
100
+ * re-derives it from the parsed document, so the stored document's
101
+ * whitespace never matters.
102
+ */
103
+ function signaturePayloadOf(manifest) {
104
+ const { signature: _signature, ...payload } = manifest;
105
+ return stableStringify(payload);
106
+ }
52
107
  /**
53
108
  * Compose the manifest with its `version` derived from the canonical content
54
109
  * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —
@@ -64,12 +119,17 @@ function composeVersionManifest(input) {
64
119
  version
65
120
  });
66
121
  }
67
- function stableStringify(value) {
68
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
69
- if (value && typeof value === "object") return `{${Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).sort().join(",")}}`;
70
- return JSON.stringify(value) ?? "null";
122
+ /**
123
+ * The content-addressed directory name for one dist's file set (#826): a
124
+ * 16-hex hash over the canonical files map. Identical bytes → identical
125
+ * directory → re-deploying unchanged content is a server-side no-op, and a
126
+ * published pin's directory can never be rewritten by a later deploy
127
+ * (the upload service refuses conflicting writes under `versions/`).
128
+ */
129
+ function versionDirPath(files) {
130
+ return `versions/${createHash("sha256").update(stableStringify(files)).digest("hex").slice(0, 16)}`;
71
131
  }
72
132
 
73
133
  //#endregion
74
- export { WorkspaceVersionManifestSchema, composeVersionManifest };
134
+ export { ManifestSignatureSchema, VERSION_DIR_PATTERN, WorkspaceVersionManifestSchema, composeVersionManifest, signaturePayloadOf, stableStringify, versionDirPath };
75
135
  //# sourceMappingURL=version-manifest.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"version-manifest.mjs","names":[],"sources":["../src/version-manifest.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * WorkspaceVersionManifest — the immutable per-deploy document at\n * `bundles/<account>/<gateway>/<workspace>/versions/<version>.json`\n * (ADR 0020, as amended; atomic-deploys MAP decision 1). The published\n * config's slot pins this document via its `pin` (`manifest` + the\n * manifest's own SRI); every artifact coordinate lives here, so the config\n * stops growing with artifact kinds and old versions stay fully servable.\n */\n\nconst SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);\n\nexport const WorkspaceVersionManifestSchema = z.object({\n version: z.string().regex(/^[0-9a-f]{16}$/),\n builtAt: z.string().optional(),\n gitSha: z.string().optional(),\n entry: z.string().min(1),\n entryIntegrity: SRI,\n ssr: z\n .object({\n entry: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n browserManifest: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /** extra assets (css, icons) — logical name → served-object SRI */\n assets: z.record(z.string(), SRI).optional(),\n /** shared-dependency versions this build was built against */\n shared: z.record(z.string(), z.string()).optional(),\n /**\n * Full per-file digest map of the dist (object path → served-object SRI).\n * Written by `every-plugin`-based deploys so the next deploy can diff against\n * it and skip re-uploading unchanged files; readers that predate the field\n * ignore it (zod strips unknown keys). Participates in the version hash.\n */\n files: z.record(z.string(), SRI).optional(),\n /**\n * Source archive of the workspace subtree (object path → served-object SRI)\n * published beside the dists when the runtime opts in via\n * `publish.sourcePublish` — lets `bos import` dereference a published\n * config slot to the exact source bytes that build came from, with no\n * GitHub dependency. Readers that predate the field ignore it (zod strips\n * unknown keys). Participates in the version hash.\n */\n source: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n});\nexport type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;\n\n/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */\nexport type SourceRef = NonNullable<WorkspaceVersionManifest[\"source\"]>;\n\nexport interface VersionManifestInput {\n /** Omit: a per-build timestamp makes the same version id map to different\n * manifest bytes per machine, breaking the pin's SRI against the image's\n * staged copy and the CDN's immutability for republished versions. */\n builtAt?: string;\n gitSha?: string;\n entry: string;\n entryIntegrity: string;\n ssr?: { entry: string; integrity: string };\n browserManifest?: { file: string; integrity: string };\n assets?: Record<string, string>;\n shared?: Record<string, string>;\n files?: Record<string, string>;\n source?: { file: string; integrity: string };\n}\n\n/**\n * Compose the manifest with its `version` derived from the canonical content\n * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —\n * identical bytes → identical version id regardless of when they were built,\n * so republishing unchanged content is a pointer no-op. Any changed\n * coordinate → a new version (and a new file).\n */\nexport function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest {\n const { builtAt: _builtAt, ...hashedInputs } = input;\n const version = createHash(\"sha256\")\n .update(stableStringify(hashedInputs))\n .digest(\"hex\")\n .slice(0, 16);\n return WorkspaceVersionManifestSchema.parse({ ...input, version });\n}\n\nfunction stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n .sort()\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n"],"mappings":";;;;;;;;;;;;AAYA,MAAM,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,0BAA0B;AAEvD,MAAa,iCAAiC,EAAE,OAAO;CACrD,SAAS,EAAE,OAAO,CAAC,CAAC,MAAM,gBAAgB;CAC1C,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,gBAAgB;CAChB,KAAK,EACF,OAAO;EACN,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACvB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;CACZ,iBAAiB,EACd,OAAO;EACN,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;CAEZ,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;CAE3C,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS;;;;;;;CAOlD,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;;;;;;;;CAS1C,QAAQ,EACL,OAAO;EACN,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;AACd,CAAC;;;;;;;;AA6BD,SAAgB,uBAAuB,OAAuD;CAC5F,MAAM,EAAE,SAAS,UAAU,GAAG,iBAAiB;CAC/C,MAAM,UAAU,WAAW,QAAQ,CAAC,CACjC,OAAO,gBAAgB,YAAY,CAAC,CAAC,CACrC,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;CACd,OAAO,+BAA+B,MAAM;EAAE,GAAG;EAAO;CAAQ,CAAC;AACnE;AAEA,SAAS,gBAAgB,OAAwB;CAC/C,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,IAAI,eAAe,CAAC,CAAC,KAAK,GAAG,EAAE;CAC1E,IAAI,SAAS,OAAO,UAAU,UAC5B,OAAO,IAAI,OAAO,QAAQ,KAAK,CAAC,CAC7B,QAAQ,GAAG,OAAO,MAAM,MAAS,CAAC,CAClC,KAAK,CAAC,GAAG,OAAO,GAAG,KAAK,UAAU,CAAC,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAC7D,KAAK,CAAC,CACN,KAAK,GAAG,EAAE;CAEf,OAAO,KAAK,UAAU,KAAK,KAAK;AAClC"}
1
+ {"version":3,"file":"version-manifest.mjs","names":[],"sources":["../src/version-manifest.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * WorkspaceVersionManifest — the immutable per-deploy document at\n * `bundles/<account>/<gateway>/<workspace>/versions/<version>.json`\n * (ADR 0020, as amended; atomic-deploys MAP decision 1). The published\n * config's slot pins this document via its `pin` (`manifest` + the\n * manifest's own SRI); every artifact coordinate lives here, so the config\n * stops growing with artifact kinds and old versions stay fully servable.\n */\n\nconst SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);\n\n/** Slot-relative directory holding one deploy's immutable file copies (#826). */\nexport const VERSION_DIR_PATTERN = /^versions\\/[0-9a-f]{16}$/;\n\n/** The attribution block stored in the manifest's `signature` field. The\n * verifying key rides along so a verifier can distinguish \"wrong signature\"\n * from \"signed by a key the config never registered\" — both are\n * `SignatureInvalid`, but the reasons name different compromises. */\nexport const ManifestSignatureSchema = z.object({\n algorithm: z.literal(\"ed25519\"),\n /** NEAR-format verifying key (`ed25519:<base58>`) — must equal the\n * config's `publish.verification.publicKey`. */\n publicKey: z.string().min(1),\n /** base64 ed25519 signature over the canonical payload bytes. */\n value: z.string().min(1),\n});\nexport type ManifestSignature = z.infer<typeof ManifestSignatureSchema>;\n\nexport const WorkspaceVersionManifestSchema = z.object({\n version: z.string().regex(/^[0-9a-f]{16}$/),\n builtAt: z.string().optional(),\n gitSha: z.string().optional(),\n /**\n * Slot-relative directory (`versions/<dirId>`) holding this deploy's\n * byte-immutable copies of every dist file (#826). When present, all\n * artifact coordinates below resolve against `<slotBase>/<dir>/` — so a\n * pinned child's entry fetches its expose chunk and chunks from the same\n * frozen directory instead of the mutable slot root. Absent on legacy\n * manifests: coordinates resolve against the slot root exactly as before.\n * Derived from the `files` map (see `versionDirPath`), so it participates\n * in the version hash without circularity.\n */\n dir: z.string().regex(VERSION_DIR_PATTERN).optional(),\n entry: z.string().min(1),\n entryIntegrity: SRI,\n ssr: z\n .object({\n entry: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n browserManifest: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /** extra assets (css, icons) — logical name → served-object SRI */\n assets: z.record(z.string(), SRI).optional(),\n /** shared-dependency versions this build was built against */\n shared: z.record(z.string(), z.string()).optional(),\n /**\n * Full per-file digest map of the dist (object path → served-object SRI).\n * Written by `every-plugin`-based deploys so the next deploy can diff against\n * it and skip re-uploading unchanged files; readers that predate the field\n * ignore it (zod strips unknown keys). Participates in the version hash.\n */\n files: z.record(z.string(), SRI).optional(),\n /**\n * Source archive of the workspace subtree (object path → served-object SRI)\n * published beside the dists when the runtime opts in via\n * `publish.sourcePublish` — lets `bos import` dereference a published\n * config slot to the exact source bytes that build came from, with no\n * GitHub dependency. Readers that predate the field ignore it (zod strips\n * unknown keys). Participates in the version hash.\n */\n source: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /**\n * Attribution (ADR 0046 ticket 05): the deploy train's ed25519 signature\n * over `signaturePayloadOf(this document)` — SRI attests WHAT the bytes\n * are, the signature attests WHO published them, with the verifying key\n * anchored in the on-chain config (`publish.verification`). Absent on\n * rollouts with attribution off; readers that predate the field ignore it\n * (zod strips unknown keys). Excluded from the version-id derivation —\n * like `builtAt`, it is not a coordinate of the build.\n */\n signature: ManifestSignatureSchema.optional(),\n});\nexport type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;\n\n/** Canonical key-sorted JSON serialization — the same form the version-id\n * derivation hashes (undefined-valued entries dropped). Exported so the\n * signature payload and the version id can never disagree about what\n * \"canonical\" means. */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n .sort()\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\n/**\n * The bytes a manifest signature attests: the document minus its reserved\n * `signature` field, canonically serialized (key-sorted — the version-id\n * rule). The signer signs this before the field exists; the verifier\n * re-derives it from the parsed document, so the stored document's\n * whitespace never matters.\n */\nexport function signaturePayloadOf(manifest: WorkspaceVersionManifest): string {\n const { signature: _signature, ...payload } = manifest;\n return stableStringify(payload);\n}\n\n/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */\nexport type SourceRef = NonNullable<WorkspaceVersionManifest[\"source\"]>;\n\nexport interface VersionManifestInput {\n /** Omit: a per-build timestamp makes the same version id map to different\n * manifest bytes per machine, breaking the pin's SRI against the image's\n * staged copy and the CDN's immutability for republished versions. */\n builtAt?: string;\n gitSha?: string;\n dir?: string;\n entry: string;\n entryIntegrity: string;\n ssr?: { entry: string; integrity: string };\n browserManifest?: { file: string; integrity: string };\n assets?: Record<string, string>;\n shared?: Record<string, string>;\n files?: Record<string, string>;\n source?: { file: string; integrity: string };\n}\n\n/**\n * Compose the manifest with its `version` derived from the canonical content\n * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —\n * identical bytes → identical version id regardless of when they were built,\n * so republishing unchanged content is a pointer no-op. Any changed\n * coordinate → a new version (and a new file).\n */\nexport function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest {\n const { builtAt: _builtAt, ...hashedInputs } = input;\n const version = createHash(\"sha256\")\n .update(stableStringify(hashedInputs))\n .digest(\"hex\")\n .slice(0, 16);\n return WorkspaceVersionManifestSchema.parse({ ...input, version });\n}\n\n/**\n * The content-addressed directory name for one dist's file set (#826): a\n * 16-hex hash over the canonical files map. Identical bytes → identical\n * directory → re-deploying unchanged content is a server-side no-op, and a\n * published pin's directory can never be rewritten by a later deploy\n * (the upload service refuses conflicting writes under `versions/`).\n */\nexport function versionDirPath(files: Record<string, string>): string {\n const id = createHash(\"sha256\").update(stableStringify(files)).digest(\"hex\").slice(0, 16);\n return `versions/${id}`;\n}\n"],"mappings":";;;;;;;;;;;;AAYA,MAAM,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,0BAA0B;;AAGvD,MAAa,sBAAsB;;;;;AAMnC,MAAa,0BAA0B,EAAE,OAAO;CAC9C,WAAW,EAAE,QAAQ,SAAS;;;CAG9B,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;;CAE3B,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;AACzB,CAAC;AAGD,MAAa,iCAAiC,EAAE,OAAO;CACrD,SAAS,EAAE,OAAO,CAAC,CAAC,MAAM,gBAAgB;CAC1C,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;;;;;;;;;;;CAW5B,KAAK,EAAE,OAAO,CAAC,CAAC,MAAM,mBAAmB,CAAC,CAAC,SAAS;CACpD,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,gBAAgB;CAChB,KAAK,EACF,OAAO;EACN,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACvB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;CACZ,iBAAiB,EACd,OAAO;EACN,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;CAEZ,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;CAE3C,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS;;;;;;;CAOlD,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;;;;;;;;CAS1C,QAAQ,EACL,OAAO;EACN,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;;;;;;;;;CAUZ,WAAW,wBAAwB,SAAS;AAC9C,CAAC;;;;;AAOD,SAAgB,gBAAgB,OAAwB;CACtD,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,IAAI,eAAe,CAAC,CAAC,KAAK,GAAG,EAAE;CAC1E,IAAI,SAAS,OAAO,UAAU,UAC5B,OAAO,IAAI,OAAO,QAAQ,KAAK,CAAC,CAC7B,QAAQ,GAAG,OAAO,MAAM,MAAS,CAAC,CAClC,KAAK,CAAC,GAAG,OAAO,GAAG,KAAK,UAAU,CAAC,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAC7D,KAAK,CAAC,CACN,KAAK,GAAG,EAAE;CAEf,OAAO,KAAK,UAAU,KAAK,KAAK;AAClC;;;;;;;;AASA,SAAgB,mBAAmB,UAA4C;CAC7E,MAAM,EAAE,WAAW,YAAY,GAAG,YAAY;CAC9C,OAAO,gBAAgB,OAAO;AAChC;;;;;;;;AA6BA,SAAgB,uBAAuB,OAAuD;CAC5F,MAAM,EAAE,SAAS,UAAU,GAAG,iBAAiB;CAC/C,MAAM,UAAU,WAAW,QAAQ,CAAC,CACjC,OAAO,gBAAgB,YAAY,CAAC,CAAC,CACrC,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;CACd,OAAO,+BAA+B,MAAM;EAAE,GAAG;EAAO;CAAQ,CAAC;AACnE;;;;;;;;AASA,SAAgB,eAAe,OAAuC;CAEpE,OAAO,YADI,WAAW,QAAQ,CAAC,CAAC,OAAO,gBAAgB,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAClE;AACtB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "every-plugin",
3
- "version": "3.0.0-rc.15",
3
+ "version": "3.0.0-rc.17",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -416,7 +416,7 @@ A child project inherits the parent's host, API, auth, and plugins via the `exte
416
416
 
417
417
  ```json
418
418
  {
419
- "extends": "bos://dev.everything.near/dev.everything.dev",
419
+ "extends": "bos://dev.everything.near/everything.dev",
420
420
  "account": "myapp.near",
421
421
  "domain": "myapp.everything.dev"
422
422
  }
@@ -433,7 +433,7 @@ This inherits:
433
433
 
434
434
  ```bash
435
435
  bos init myapp.everything.dev \
436
- --extends dev.everything.near/dev.everything.dev \
436
+ --extends dev.everything.near/everything.dev \
437
437
  --overrides ui,plugins \
438
438
  --plugins apps,template
439
439
  ```
@@ -470,6 +470,6 @@ See the `extends-config` skill for deep merge semantics, per-environment extends
470
470
  | 401 `UNAUTHORIZED` "API key required" with `data: { authType: "apiKey", hint: "Provide a valid API key via x-api-key header" }` | the route is `requireApiKey(...)`-protected and your key was absent — usually `Authorization: Bearer` was used instead | switch to the `x-api-key` header |
471
471
  | 403 `FORBIDDEN` "API key lacks permission: <resource>:<action>" with `data: { requiredPermissions, keyPermissions }` | the key's `permissions` map does not cover every required resource/action — partial matches are rejected | re-create (server-side) or update the key with the needed permissions |
472
472
  | 403 `FORBIDDEN` "Requires role: ..." / "Requires organization role: ..." | session role gate | sign in with an account holding the role; a key cannot satisfy these |
473
- | 403 `FORBIDDEN` "Organization requires platform-admin approval" | the org's `status` is not `"active"` | stop — needs platform-admin approval, no client-side fix |
473
+ | 403 `FORBIDDEN` "Organization requires platform-admin approval" | a downstream app still runs its own approval middleware (this platform ships none) | app-level policy — surface the org's approval state to the user |
474
474
  | `authClient.apiKey.list` / `update` return only `prefix` and metadata | expected — the full secret is returned once at `create` | create a new key; the old secret is unrecoverable |
475
475
  | Browser request blocked by CORS despite a valid key | session cookies not sent (`credentials: "include"` missing) or origin not allowed by `CORS_ORIGIN` | add `credentials: "include"` to the fetch wrapper, or fix `CORS_ORIGIN` |
@@ -34,13 +34,16 @@ export function uiEntryFilename(input: { isBuild: boolean; server?: boolean }):
34
34
  const HASH_SEGMENT_PATTERN = /\.[a-f0-9]{8,}\./;
35
35
 
36
36
  /**
37
- * Serving classification for bundle objects: anything whose name carries a
38
- * content-hash segment (including hashed entrypoints like
39
- * `remoteEntry.8f3a….js`) is immutable by construction; fixed-name
40
- * entrypoints and non-hashed files a redeploy replaces in place must
41
- * revalidate.
37
+ * Serving classification for bundle objects: anything under a version
38
+ * directory (`versions/<dirId>/…`, #826) or a `versions/<id>.json` manifest
39
+ * is immutable by construction — the directory name is content-addressed and
40
+ * the upload service refuses conflicting writes under `versions/`; likewise
41
+ * any name carrying a content-hash segment (including hashed entrypoints
42
+ * like `remoteEntry.8f3a….js`). Fixed-name entrypoints and non-hashed files
43
+ * a redeploy replaces in place must revalidate.
42
44
  */
43
45
  export function isImmutableBundlePath(name: string): boolean {
46
+ if (name === "versions" || name.startsWith("versions/")) return true;
44
47
  const base = name.split("/").pop() ?? name;
45
48
  return HASH_SEGMENT_PATTERN.test(base);
46
49
  }
@@ -65,19 +65,14 @@ export type {
65
65
  ConstructedTree,
66
66
  ConstructInput,
67
67
  ConstructPluginRef,
68
- GateUser,
69
- HostContext,
70
68
  NavManifest,
71
69
  PluginManifest,
72
70
  ResolvedPlugin,
73
71
  RouteConfigModule,
74
72
  RouteOptionsBundle,
75
73
  } from "everything-dev/ui/manifest";
76
- export {
77
- CORE_UI_PLUGIN_KEY,
78
- ComposePayloadSchema,
79
- constructTree,
80
- } from "everything-dev/ui/manifest";
74
+ export { CORE_UI_PLUGIN_KEY, ComposePayloadSchema } from "everything-dev/ui/manifest";
75
+ export { constructTree } from "everything-dev/ui/compose";
81
76
  `,
82
77
  "src/globals.gen.ts": `// GENERATED FILE — do not edit. Regenerated from the installed framework version
83
78
  // by \`bos dev\` / \`bos build\` / \`bos typecheck\` (gitignored).