@warpgogol/werkstatt-shared 0.2.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.
Files changed (403) hide show
  1. package/package.json +352 -0
  2. package/src/checks/index.ts +24 -0
  3. package/src/checks/lib/astro-site-url.ts +30 -0
  4. package/src/checks/lib/i18n.ts +30 -0
  5. package/src/checks/result-helpers.ts +102 -0
  6. package/src/checks/suppressions-config.ts +195 -0
  7. package/src/index.ts +14 -0
  8. package/src/integration/README.md +38 -0
  9. package/src/integration/adapters.ts +143 -0
  10. package/src/integration/crm-buffer.ts +491 -0
  11. package/src/integration/dispatch.ts +106 -0
  12. package/src/integration/funnel.ts +379 -0
  13. package/src/integration/index.ts +56 -0
  14. package/src/integration/lifecycle.ts +155 -0
  15. package/src/integration/orchestration.ts +439 -0
  16. package/src/integration/port-barrel.ts +124 -0
  17. package/src/integration/port.ts +143 -0
  18. package/src/integration/qstash.ts +134 -0
  19. package/src/integration/sharding.ts +133 -0
  20. package/src/integration/tests/deliver-event.test.ts +52 -0
  21. package/src/integration/tests/dispatch.test.ts +88 -0
  22. package/src/integration/tests/funnel-bridge.test.ts +46 -0
  23. package/src/integration/tests/hub-runtime.test.ts +128 -0
  24. package/src/integration/tests/lifecycle.test.ts +69 -0
  25. package/src/integration/tests/qstash.test.ts +83 -0
  26. package/src/integration/tests/sharding.test.ts +64 -0
  27. package/src/integration/vitest.config.ts +8 -0
  28. package/src/integration-adapter-supabase-crm/adapter.ts +437 -0
  29. package/src/integration-adapter-supabase-crm/client.ts +605 -0
  30. package/src/integration-adapter-supabase-crm/index.ts +28 -0
  31. package/src/integration-adapter-supabase-crm/pipedrive-sync-target.ts +482 -0
  32. package/src/integration-adapter-supabase-crm/supabase-rest.ts +89 -0
  33. package/src/integration-adapter-supabase-crm/tenant-registry.ts +334 -0
  34. package/src/integration-adapter-supabase-crm/tests/change-balance.pbt.test.ts +83 -0
  35. package/src/integration-adapter-supabase-crm/tests/funnel-persistence.test.ts +409 -0
  36. package/src/integration-adapter-supabase-crm/tests/lifecycle-sync.test.ts +288 -0
  37. package/src/integration-adapter-supabase-crm/tests/stage-map.test.ts +80 -0
  38. package/src/integration-adapter-supabase-crm/vitest.config.ts +8 -0
  39. package/src/integration-adapter-supabase-crm/worker.ts +223 -0
  40. package/src/observability/conventions.ts +93 -0
  41. package/src/observability/env-resolver.ts +57 -0
  42. package/src/observability/index.ts +57 -0
  43. package/src/observability/metric-registry.ts +184 -0
  44. package/src/observability/otlp-converter.ts +125 -0
  45. package/src/observability/otlp-json.ts +135 -0
  46. package/src/observability/otlp-transport.ts +68 -0
  47. package/src/observability/pusher.ts +142 -0
  48. package/src/observability/redact.ts +27 -0
  49. package/src/observability/tests/conventions.test.ts +100 -0
  50. package/src/observability/tests/metric-registry.test.ts +117 -0
  51. package/src/observability/tests/otlp-converter.pbt.test.ts +77 -0
  52. package/src/observability/tests/otlp-converter.test.ts +157 -0
  53. package/src/observability/tests/otlp-json.test.ts +120 -0
  54. package/src/observability/tests/pusher.test.ts +75 -0
  55. package/src/observability/tests/redact.pbt.test.ts +71 -0
  56. package/src/observability/tests/redact.test.ts +37 -0
  57. package/src/observability/typed-refs.ts +137 -0
  58. package/src/observability/vitest.config.ts +8 -0
  59. package/src/ontology/analytics/matomo-binding.yaml +172 -0
  60. package/src/ontology/analytics/matomo-fleet.registry.yaml +26 -0
  61. package/src/ontology/analytics/messkanon.yaml +151 -0
  62. package/src/ontology/archetype-registry.ts +61 -0
  63. package/src/ontology/archetypes/components/brand-label.yaml +19 -0
  64. package/src/ontology/archetypes/components/copyright.yaml +19 -0
  65. package/src/ontology/archetypes/components/currency-aware-price-display.yaml +19 -0
  66. package/src/ontology/archetypes/components/currency-selector.yaml +19 -0
  67. package/src/ontology/archetypes/components/footer-promo.yaml +19 -0
  68. package/src/ontology/archetypes/components/lang-switcher.yaml +19 -0
  69. package/src/ontology/archetypes/components/layout.yaml +19 -0
  70. package/src/ontology/archetypes/components/live-photo.yaml +20 -0
  71. package/src/ontology/archetypes/components/material-credit.yaml +20 -0
  72. package/src/ontology/archetypes/components/media.yaml +21 -0
  73. package/src/ontology/archetypes/components/nachweis-card.yaml +34 -0
  74. package/src/ontology/archetypes/components/nachweis-detail.yaml +43 -0
  75. package/src/ontology/archetypes/components/nachweis-list.yaml +37 -0
  76. package/src/ontology/archetypes/components/nachweis-verify.yaml +31 -0
  77. package/src/ontology/archetypes/components/not-found.yaml +22 -0
  78. package/src/ontology/archetypes/components/offer-capacity.yaml +21 -0
  79. package/src/ontology/archetypes/components/passport-header.yaml +19 -0
  80. package/src/ontology/archetypes/components/passport-provenance.yaml +20 -0
  81. package/src/ontology/archetypes/components/passport-score-grid.yaml +20 -0
  82. package/src/ontology/archetypes/components/passport-star-map.yaml +20 -0
  83. package/src/ontology/archetypes/components/person-profile.yaml +20 -0
  84. package/src/ontology/archetypes/components/pulsar.yaml +20 -0
  85. package/src/ontology/archetypes/components/responsive-image.yaml +19 -0
  86. package/src/ontology/archetypes/components/scroll-to-top.yaml +22 -0
  87. package/src/ontology/archetypes/components/social-meta.yaml +20 -0
  88. package/src/ontology/archetypes/components/structured-data.yaml +19 -0
  89. package/src/ontology/archetypes/index.json +1003 -0
  90. package/src/ontology/archetypes/index.yaml +821 -0
  91. package/src/ontology/archetypes/sections/approach.yaml +23 -0
  92. package/src/ontology/archetypes/sections/article-list.yaml +30 -0
  93. package/src/ontology/archetypes/sections/audience-cards.yaml +30 -0
  94. package/src/ontology/archetypes/sections/breadcrumbs.yaml +21 -0
  95. package/src/ontology/archetypes/sections/chat-widget.yaml +37 -0
  96. package/src/ontology/archetypes/sections/comparison-cards.yaml +31 -0
  97. package/src/ontology/archetypes/sections/controlled-responsibility-block.yaml +34 -0
  98. package/src/ontology/archetypes/sections/credits.yaml +29 -0
  99. package/src/ontology/archetypes/sections/donation-card.yaml +53 -0
  100. package/src/ontology/archetypes/sections/donation-use.yaml +23 -0
  101. package/src/ontology/archetypes/sections/dynamic-status-block.yaml +35 -0
  102. package/src/ontology/archetypes/sections/faq-list.yaml +26 -0
  103. package/src/ontology/archetypes/sections/final-cta.yaml +53 -0
  104. package/src/ontology/archetypes/sections/hero-decision-card.yaml +65 -0
  105. package/src/ontology/archetypes/sections/hero.yaml +50 -0
  106. package/src/ontology/archetypes/sections/impact.yaml +23 -0
  107. package/src/ontology/archetypes/sections/markdown.yaml +43 -0
  108. package/src/ontology/archetypes/sections/mountain-journey.yaml +43 -0
  109. package/src/ontology/archetypes/sections/navigation.yaml +20 -0
  110. package/src/ontology/archetypes/sections/notausgang-block.yaml +41 -0
  111. package/src/ontology/archetypes/sections/open-source-registry.yaml +26 -0
  112. package/src/ontology/archetypes/sections/ownership-block.yaml +57 -0
  113. package/src/ontology/archetypes/sections/people.yaml +48 -0
  114. package/src/ontology/archetypes/sections/price-card.yaml +50 -0
  115. package/src/ontology/archetypes/sections/problem.yaml +23 -0
  116. package/src/ontology/archetypes/sections/send-message.yaml +63 -0
  117. package/src/ontology/archetypes/sections/service-metadata-block.yaml +40 -0
  118. package/src/ontology/archetypes/sections/social-proof.yaml +27 -0
  119. package/src/ontology/archetypes/sections/transparency.yaml +32 -0
  120. package/src/ontology/archetypes/sections/trust-strip.yaml +42 -0
  121. package/src/ontology/archetypes/sections/video-section.yaml +27 -0
  122. package/src/ontology/archetypes/sections/women.yaml +29 -0
  123. package/src/ontology/biome-token-projection.ts +229 -0
  124. package/src/ontology/biomes/check-concrete-blueprint.yaml +118 -0
  125. package/src/ontology/biomes/handwerk-material-warm.yaml +135 -0
  126. package/src/ontology/biomes/nonprofit-trust.yaml +123 -0
  127. package/src/ontology/blueprints/offer.yaml +39 -0
  128. package/src/ontology/blueprints/prompts/local-market-signal.md +13 -0
  129. package/src/ontology/blueprints/prompts/website-local-narrative.md +24 -0
  130. package/src/ontology/blueprints/ratgeber.yaml +62 -0
  131. package/src/ontology/blueprints/website-local.yaml +309 -0
  132. package/src/ontology/blueprints/website-service.yaml +84 -0
  133. package/src/ontology/capabilities/lead.submit.yaml +44 -0
  134. package/src/ontology/cosmic/index.ts +48 -0
  135. package/src/ontology/cosmic/moon-catalog.ts +173 -0
  136. package/src/ontology/cosmic/planet-catalog.ts +82 -0
  137. package/src/ontology/cosmic/star-catalog.ts +337 -0
  138. package/src/ontology/enums.ts +159 -0
  139. package/src/ontology/external-surfaces/index.ts +158 -0
  140. package/src/ontology/external-surfaces/jsonld-types.yaml +135 -0
  141. package/src/ontology/external-surfaces/sitemap-shape.yaml +6 -0
  142. package/src/ontology/external-surfaces/url-schema.yaml +95 -0
  143. package/src/ontology/growth/events/contact-email-click.yaml +21 -0
  144. package/src/ontology/growth/events/contact-form-submit.yaml +25 -0
  145. package/src/ontology/growth/events/contact-phone-click.yaml +21 -0
  146. package/src/ontology/growth/events/contact-route-click.yaml +21 -0
  147. package/src/ontology/growth/events/contact-submit.yaml +28 -0
  148. package/src/ontology/growth/events/contact-whatsapp-click.yaml +21 -0
  149. package/src/ontology/growth/events/cta-click.yaml +30 -0
  150. package/src/ontology/growth/events/donation-confirmed.yaml +32 -0
  151. package/src/ontology/growth/events/donation-form-start.yaml +21 -0
  152. package/src/ontology/growth/events/donation-form-submit.yaml +29 -0
  153. package/src/ontology/growth/events/donation-intent.yaml +29 -0
  154. package/src/ontology/growth/events/form-error.yaml +27 -0
  155. package/src/ontology/growth/events/form-start.yaml +21 -0
  156. package/src/ontology/growth/events/form-submit.yaml +21 -0
  157. package/src/ontology/growth/events/outbound-click.yaml +25 -0
  158. package/src/ontology/growth/events/page-view.yaml +24 -0
  159. package/src/ontology/growth/events/passport-view.yaml +28 -0
  160. package/src/ontology/growth/events/star-map-navigate.yaml +30 -0
  161. package/src/ontology/index.ts +165 -0
  162. package/src/ontology/manifest.ts +344 -0
  163. package/src/ontology/operations/index.ts +1 -0
  164. package/src/ontology/schemas/biome.ts +303 -0
  165. package/src/ontology/schemas/capability.ts +104 -0
  166. package/src/ontology/schemas/constellation.ts +114 -0
  167. package/src/ontology/schemas/dns-records.ts +53 -0
  168. package/src/ontology/schemas/index.ts +95 -0
  169. package/src/ontology/schemas/manifest-resolver.ts +123 -0
  170. package/src/ontology/schemas/page-entry.ts +213 -0
  171. package/src/ontology/schemas/section-archetype.ts +109 -0
  172. package/src/ontology/schemas/site-family.ts +98 -0
  173. package/src/ontology/schemas/system/growth.ts +80 -0
  174. package/src/ontology/schemas/system/integrations.ts +143 -0
  175. package/src/ontology/schemas/system/manifest.ts +492 -0
  176. package/src/ontology/schemas/system/page-output.ts +99 -0
  177. package/src/ontology/schemas/system/release.ts +58 -0
  178. package/src/ontology/schemas/system/text.ts +58 -0
  179. package/src/ontology/schemas/system.ts +53 -0
  180. package/src/ontology/shared-section-props/body-fragments.ts +182 -0
  181. package/src/ontology/shared-section-props/catalog.ts +195 -0
  182. package/src/ontology/shared-section-props/common.ts +178 -0
  183. package/src/ontology/shared-section-props/index.ts +34 -0
  184. package/src/ontology/shared-section-props/visual-header.ts +163 -0
  185. package/src/ontology/site-families/charity-donation-trust/cultural-rules.yaml +6 -0
  186. package/src/ontology/site-families/charity-donation-trust/family.yaml +44 -0
  187. package/src/ontology/site-families/charity-donation-trust/linguistic-rules.yaml +6 -0
  188. package/src/ontology/site-families/check-quality-operator/cultural-rules.yaml +6 -0
  189. package/src/ontology/site-families/check-quality-operator/family.yaml +40 -0
  190. package/src/ontology/site-families/check-quality-operator/linguistic-rules.yaml +6 -0
  191. package/src/ontology/site-families/handwerk-trust-engineering/cultural-rules.yaml +6 -0
  192. package/src/ontology/site-families/handwerk-trust-engineering/family.yaml +53 -0
  193. package/src/ontology/site-families/handwerk-trust-engineering/linguistic-rules.yaml +6 -0
  194. package/src/ontology/tests/catalogs.pbt.test.ts +89 -0
  195. package/src/ontology/tests/enums.test.ts +203 -0
  196. package/src/ontology/tests/sternsystem-owner.test.ts +63 -0
  197. package/src/ontology/vitest.config.ts +8 -0
  198. package/src/passport/dht-sign.test.ts +99 -0
  199. package/src/passport/dht-sign.ts +73 -0
  200. package/src/passport/identity-sign.test.ts +134 -0
  201. package/src/passport/identity-sign.ts +85 -0
  202. package/src/passport/index.ts +53 -0
  203. package/src/passport/key-rotate.ts +133 -0
  204. package/src/passport/schema.ts +198 -0
  205. package/src/passport/sign.ts +225 -0
  206. package/src/passport/tests/schema.pbt.test.ts +93 -0
  207. package/src/passport/tests/schema.test.ts +222 -0
  208. package/src/passport/vitest.config.ts +8 -0
  209. package/src/share/agent/api-catalog.ts +77 -0
  210. package/src/share/agent/capability.ts +68 -0
  211. package/src/share/agent/dns-aid.test.ts +74 -0
  212. package/src/share/agent/dns-aid.ts +53 -0
  213. package/src/share/agent/fleet-catalog.ts +149 -0
  214. package/src/share/agent/index.ts +29 -0
  215. package/src/share/agent/knowledge.ts +96 -0
  216. package/src/share/agent/manifest.ts +126 -0
  217. package/src/share/agent/mcp-card.ts +63 -0
  218. package/src/share/agent/omit-empty.ts +77 -0
  219. package/src/share/agent/openapi.ts +157 -0
  220. package/src/share/agent/proof.ts +61 -0
  221. package/src/share/attribution-display.ts +86 -0
  222. package/src/share/check-hints.ts +38 -0
  223. package/src/share/content/entity-id.ts +65 -0
  224. package/src/share/content/index.ts +16 -0
  225. package/src/share/content/merge.ts +86 -0
  226. package/src/share/content/resolve-field-path.ts +39 -0
  227. package/src/share/content/substitute-deep.ts +47 -0
  228. package/src/share/content-discipline/index.ts +14 -0
  229. package/src/share/content-discipline/parsers.ts +66 -0
  230. package/src/share/content-discipline/types.ts +118 -0
  231. package/src/share/content-reference.ts +333 -0
  232. package/src/share/counter-utils.ts +75 -0
  233. package/src/share/css-value-normalize.ts +48 -0
  234. package/src/share/dev-props-validator.ts +131 -0
  235. package/src/share/entitlement.ts +172 -0
  236. package/src/share/env.d.ts +73 -0
  237. package/src/share/feature-policy.ts +367 -0
  238. package/src/share/formula-eval.ts +364 -0
  239. package/src/share/fs/index.ts +96 -0
  240. package/src/share/i18n/localization.ts +57 -0
  241. package/src/share/image-provider.ts +314 -0
  242. package/src/share/index.ts +86 -0
  243. package/src/share/knowledge/claim.ts +129 -0
  244. package/src/share/knowledge/derivation.ts +55 -0
  245. package/src/share/knowledge/freshness.ts +127 -0
  246. package/src/share/knowledge/index.ts +23 -0
  247. package/src/share/knowledge/ledger.ts +222 -0
  248. package/src/share/knowledge/plan.ts +122 -0
  249. package/src/share/knowledge/source.ts +170 -0
  250. package/src/share/legal/translation-policy.ts +133 -0
  251. package/src/share/material-credits.ts +181 -0
  252. package/src/share/middleware/language-redirect.ts +49 -0
  253. package/src/share/offer-capacity.ts +149 -0
  254. package/src/share/onboarding-yaml/index.ts +162 -0
  255. package/src/share/page.ts +351 -0
  256. package/src/share/redirects.test.ts +62 -0
  257. package/src/share/redirects.ts +54 -0
  258. package/src/share/rfc0042-utils.ts +95 -0
  259. package/src/share/runtime-context.ts +70 -0
  260. package/src/share/schemas/claim-records.ts +69 -0
  261. package/src/share/schemas/claims.ts +150 -0
  262. package/src/share/schemas/effects.ts +212 -0
  263. package/src/share/schemas/features.ts +147 -0
  264. package/src/share/schemas/horizontal-align.ts +22 -0
  265. package/src/share/schemas/icon-color.ts +27 -0
  266. package/src/share/schemas/index.ts +43 -0
  267. package/src/share/schemas/live-photo.ts +53 -0
  268. package/src/share/schemas/material-credit.ts +292 -0
  269. package/src/share/schemas/media.ts +220 -0
  270. package/src/share/schemas/navigation.ts +74 -0
  271. package/src/share/schemas/page-base.ts +33 -0
  272. package/src/share/schemas/participant.ts +247 -0
  273. package/src/share/schemas/person.ts +24 -0
  274. package/src/share/schemas/print.ts +93 -0
  275. package/src/share/schemas/section-background.ts +106 -0
  276. package/src/share/schemas/section-body.ts +138 -0
  277. package/src/share/schemas/section-cards.ts +37 -0
  278. package/src/share/schemas/section-cta.ts +74 -0
  279. package/src/share/schemas/section-header.ts +77 -0
  280. package/src/share/schemas/section-image.ts +77 -0
  281. package/src/share/schemas/section-motion.ts +52 -0
  282. package/src/share/schemas/section-shell.ts +63 -0
  283. package/src/share/schemas/section-stats.ts +37 -0
  284. package/src/share/schemas/site-background.ts +78 -0
  285. package/src/share/schemas/standard-list-item.ts +38 -0
  286. package/src/share/scripts/external-links.ts +45 -0
  287. package/src/share/scripts/gsap-counter.ts +168 -0
  288. package/src/share/scripts/gsap-mountain-journey.ts +236 -0
  289. package/src/share/scripts/gsap-parallax.ts +50 -0
  290. package/src/share/scripts/gsap-reveal.ts +65 -0
  291. package/src/share/scripts/gsap-stagger.ts +54 -0
  292. package/src/share/scripts/index.ts +25 -0
  293. package/src/share/scripts/inline-number-animation.ts +120 -0
  294. package/src/share/scripts/lenis.ts +109 -0
  295. package/src/share/scripts/live-photos.ts +250 -0
  296. package/src/share/scripts/lordicon.ts +94 -0
  297. package/src/share/scripts/orchestrator.ts +138 -0
  298. package/src/share/scripts/scheduler.ts +53 -0
  299. package/src/share/scripts/video-player.ts +116 -0
  300. package/src/share/semantic/ai.ts +140 -0
  301. package/src/share/semantic/block-extraction.ts +52 -0
  302. package/src/share/semantic/block-extractors/index.ts +502 -0
  303. package/src/share/semantic/breadcrumbs.ts +120 -0
  304. package/src/share/semantic/build-page.ts +309 -0
  305. package/src/share/semantic/business-projection.ts +278 -0
  306. package/src/share/semantic/derived-prices-loader.ts +36 -0
  307. package/src/share/semantic/extract.ts +118 -0
  308. package/src/share/semantic/feed.ts +124 -0
  309. package/src/share/semantic/ids.ts +76 -0
  310. package/src/share/semantic/image-sitemap.ts +153 -0
  311. package/src/share/semantic/index.ts +36 -0
  312. package/src/share/semantic/jsonld/article.ts +47 -0
  313. package/src/share/semantic/jsonld/breadcrumb.ts +47 -0
  314. package/src/share/semantic/jsonld/collection-list.ts +30 -0
  315. package/src/share/semantic/jsonld/context.ts +46 -0
  316. package/src/share/semantic/jsonld/faq.ts +49 -0
  317. package/src/share/semantic/jsonld/initiative.ts +53 -0
  318. package/src/share/semantic/jsonld/organization.ts +101 -0
  319. package/src/share/semantic/jsonld/person.ts +71 -0
  320. package/src/share/semantic/jsonld/service.ts +80 -0
  321. package/src/share/semantic/jsonld/shared.ts +31 -0
  322. package/src/share/semantic/jsonld/types.ts +18 -0
  323. package/src/share/semantic/jsonld/webpage.ts +98 -0
  324. package/src/share/semantic/jsonld/website.ts +27 -0
  325. package/src/share/semantic/jsonld.ts +75 -0
  326. package/src/share/semantic/llms-policy.ts +74 -0
  327. package/src/share/semantic/llms.ts +394 -0
  328. package/src/share/semantic/markdown-hygiene.ts +68 -0
  329. package/src/share/semantic/markdown-twin-provenance.ts +242 -0
  330. package/src/share/semantic/models.ts +402 -0
  331. package/src/share/semantic/organization-profile.ts +160 -0
  332. package/src/share/semantic/output-projection.ts +131 -0
  333. package/src/share/semantic/page-builders/markdown-page.ts +84 -0
  334. package/src/share/semantic/page-markdown.ts +234 -0
  335. package/src/share/semantic/page-utils.ts +125 -0
  336. package/src/share/semantic/price-marker-resolver.ts +92 -0
  337. package/src/share/semantic/robots.ts +127 -0
  338. package/src/share/semantic/update-stamp.ts +136 -0
  339. package/src/share/shared-context.ts +177 -0
  340. package/src/share/string-utils.ts +19 -0
  341. package/src/share/tests/api-catalog.test.ts +97 -0
  342. package/src/share/tests/breadcrumbs.test.ts +126 -0
  343. package/src/share/tests/build-page-price-markers.test.ts +160 -0
  344. package/src/share/tests/capability.test.ts +107 -0
  345. package/src/share/tests/claim.test.ts +90 -0
  346. package/src/share/tests/content-ref-index.test.ts +328 -0
  347. package/src/share/tests/create-dev-props-validator.test.ts +38 -0
  348. package/src/share/tests/derivation.test.ts +39 -0
  349. package/src/share/tests/dev-props-validator.test.ts +88 -0
  350. package/src/share/tests/fleet-catalog.test.ts +134 -0
  351. package/src/share/tests/formula-eval.test.ts +619 -0
  352. package/src/share/tests/freshness.test.ts +60 -0
  353. package/src/share/tests/hero-section-numbering-parity.test.ts +51 -0
  354. package/src/share/tests/image-sitemap.test.ts +101 -0
  355. package/src/share/tests/jsonld-service.test.ts +282 -0
  356. package/src/share/tests/knowledge.test.ts +86 -0
  357. package/src/share/tests/ledger.test.ts +125 -0
  358. package/src/share/tests/llms-0789.test.ts +63 -0
  359. package/src/share/tests/manifest.test.ts +100 -0
  360. package/src/share/tests/markdown-twin-provenance.test.ts +108 -0
  361. package/src/share/tests/mcp-card.test.ts +57 -0
  362. package/src/share/tests/onboarding-yaml.test.ts +111 -0
  363. package/src/share/tests/ontology-role-by-cosmic-name.test.ts +28 -0
  364. package/src/share/tests/openapi.test.ts +116 -0
  365. package/src/share/tests/organization-jsonld.test.ts +74 -0
  366. package/src/share/tests/output-projection.test.ts +61 -0
  367. package/src/share/tests/plan.test.ts +97 -0
  368. package/src/share/tests/price-marker-resolver.test.ts +141 -0
  369. package/src/share/tests/proof.test.ts +72 -0
  370. package/src/share/tests/resolve-field-path.test.ts +52 -0
  371. package/src/share/tests/source.test.ts +81 -0
  372. package/src/share/tests/substitute-deep.test.ts +65 -0
  373. package/src/share/tests/text-normalize.pbt.test.ts +143 -0
  374. package/src/share/tests/text-normalize.test.ts +285 -0
  375. package/src/share/tests/twin-path.test.ts +43 -0
  376. package/src/share/text-normalize.ts +548 -0
  377. package/src/share/text-position.ts +19 -0
  378. package/src/share/types/hls-js-light.d.ts +16 -0
  379. package/src/share/visibility.ts +167 -0
  380. package/src/share/vitest.config.ts +8 -0
  381. package/src/share/wrap-inline-numbers.ts +139 -0
  382. package/src/surface/blueprint-schema.ts +314 -0
  383. package/src/surface/blueprint-types.ts +256 -0
  384. package/src/surface/blueprint.ts +300 -0
  385. package/src/surface/breaker.ts +57 -0
  386. package/src/surface/decision-composer.ts +156 -0
  387. package/src/surface/eligibility.ts +372 -0
  388. package/src/surface/evidence-records.ts +97 -0
  389. package/src/surface/fleet.ts +60 -0
  390. package/src/surface/geo.ts +170 -0
  391. package/src/surface/governance.ts +146 -0
  392. package/src/surface/index.ts +216 -0
  393. package/src/surface/io/index.ts +18 -0
  394. package/src/surface/io/surface-module-context-io.ts +60 -0
  395. package/src/surface/io/visibility-outcomes-io.ts +46 -0
  396. package/src/surface/module-context.ts +120 -0
  397. package/src/surface/substance.ts +161 -0
  398. package/src/surface/tests/eligibility.pbt.test.ts +137 -0
  399. package/src/surface/tests/eligibility.test.ts +302 -0
  400. package/src/surface/types.ts +289 -0
  401. package/src/surface/visibility.ts +57 -0
  402. package/src/surface/vitest.config.ts +8 -0
  403. package/tsconfig.json +17 -0
@@ -0,0 +1,63 @@
1
+ import { test, expect, describe } from "vitest";
2
+ import { systemConfigSchema } from "@warpgogol/werkstatt/schemas";
3
+
4
+ const validBaseEntry = {
5
+ schemaVersion: "system-config/v1",
6
+ id: "test-site",
7
+ cosmicStar: "Vega",
8
+ mirrors: [{ path: "https://github.com/org/test-site", storageType: "non-bare" }],
9
+ pinnedPlatform: "1.0.0",
10
+ status: "active" as const,
11
+ registeredAt: "2026-07-27T00:00:00Z",
12
+ notes: "",
13
+ };
14
+
15
+ describe("systemConfigSchema owner field (RFC-0561)", () => {
16
+ test("accepts entry without owner (backwards compatible)", () => {
17
+ const result = systemConfigSchema.parse(validBaseEntry);
18
+ expect(result.owner).toBeUndefined();
19
+ });
20
+
21
+ test("accepts entry with valid did:web owner", () => {
22
+ const result = systemConfigSchema.parse({
23
+ ...validBaseEntry,
24
+ owner: "did:web:warpgogol.com#operator-v1",
25
+ });
26
+ expect(result.owner).toBe("did:web:warpgogol.com#operator-v1");
27
+ });
28
+
29
+ test("rejects entry with non-did:web owner", () => {
30
+ expect(() =>
31
+ systemConfigSchema.parse({
32
+ ...validBaseEntry,
33
+ owner: "not-a-did",
34
+ }),
35
+ ).toThrow();
36
+ });
37
+
38
+ test("rejects entry with empty string owner", () => {
39
+ expect(() =>
40
+ systemConfigSchema.parse({
41
+ ...validBaseEntry,
42
+ owner: "",
43
+ }),
44
+ ).toThrow();
45
+ });
46
+
47
+ test("rejects entry with did:web but no key-version fragment", () => {
48
+ expect(() =>
49
+ systemConfigSchema.parse({
50
+ ...validBaseEntry,
51
+ owner: "did:web:warpgogol.com",
52
+ }),
53
+ ).toThrow();
54
+ });
55
+
56
+ test("accepts entry with did:web owner with subdomain", () => {
57
+ const result = systemConfigSchema.parse({
58
+ ...validBaseEntry,
59
+ owner: "did:web:sub.example.com#key-1",
60
+ });
61
+ expect(result.owner).toBe("did:web:sub.example.com#key-1");
62
+ });
63
+ });
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ include: ["src/**/*.test.ts"],
7
+ },
8
+ });
@@ -0,0 +1,99 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { dhtEntryBytes, signDhtEntry, verifyDhtEntry, type DHTEntryData } from "./dht-sign.ts";
3
+ import { generateKeypair } from "./sign.ts";
4
+ import type { DHTSiteEntry } from "@warpgogol/werkstatt-shared/ontology/operations";
5
+
6
+ describe("dht-sign", () => {
7
+ it("dhtEntryBytes produces deterministic canonical bytes", () => {
8
+ const entry: DHTEntryData = {
9
+ siteId: "test-site",
10
+ owner: "did:web:example.com#v1",
11
+ workshopEndpoint: "10.0.0.1:7947",
12
+ mirrors: ["10.0.0.2:7947"],
13
+ registeredAt: "2025-01-01T00:00:00.000Z",
14
+ lastUpdated: "2025-01-01T00:00:00.000Z",
15
+ };
16
+
17
+ const bytes1 = dhtEntryBytes(entry);
18
+ const bytes2 = dhtEntryBytes(entry);
19
+
20
+ expect(bytes1).toEqual(bytes2);
21
+ expect(bytes1.length).toBeGreaterThan(0);
22
+ });
23
+
24
+ it("dhtEntryBytes excludes signature field", () => {
25
+ const entryData: DHTEntryData = {
26
+ siteId: "test-site",
27
+ owner: "did:web:example.com#v1",
28
+ workshopEndpoint: "10.0.0.1:7947",
29
+ mirrors: [],
30
+ registeredAt: "2025-01-01T00:00:00.000Z",
31
+ lastUpdated: "2025-01-01T00:00:00.000Z",
32
+ };
33
+
34
+ const bytes = dhtEntryBytes(entryData);
35
+ const decoded = new TextDecoder().decode(bytes);
36
+ expect(decoded).not.toContain("signature");
37
+ });
38
+
39
+ it("signDhtEntry and verifyDhtEntry round-trip", async () => {
40
+ const keypair = await generateKeypair();
41
+ const entryData: DHTEntryData = {
42
+ siteId: "test-site",
43
+ owner: "did:web:example.com#v1",
44
+ workshopEndpoint: "10.0.0.1:7947",
45
+ mirrors: ["10.0.0.2:7947"],
46
+ registeredAt: "2025-01-01T00:00:00.000Z",
47
+ lastUpdated: "2025-01-01T00:00:00.000Z",
48
+ };
49
+
50
+ const signature = await signDhtEntry(entryData, keypair.privateKeyHex);
51
+ expect(signature).toBeTruthy();
52
+ expect(signature.length).toBeGreaterThan(0);
53
+
54
+ const fullEntry: DHTSiteEntry = { ...entryData, signature };
55
+ const isValid = await verifyDhtEntry(fullEntry, keypair.publicKeyMultibase);
56
+ expect(isValid).toBe(true);
57
+ });
58
+
59
+ it("verifyDhtEntry rejects tampered entries", async () => {
60
+ const keypair = await generateKeypair();
61
+ const entryData: DHTEntryData = {
62
+ siteId: "test-site",
63
+ owner: "did:web:example.com#v1",
64
+ workshopEndpoint: "10.0.0.1:7947",
65
+ mirrors: [],
66
+ registeredAt: "2025-01-01T00:00:00.000Z",
67
+ lastUpdated: "2025-01-01T00:00:00.000Z",
68
+ };
69
+
70
+ const signature = await signDhtEntry(entryData, keypair.privateKeyHex);
71
+ const tamperedEntry: DHTSiteEntry = {
72
+ ...entryData,
73
+ workshopEndpoint: "10.0.0.99:7947",
74
+ signature,
75
+ };
76
+
77
+ const isValid = await verifyDhtEntry(tamperedEntry, keypair.publicKeyMultibase);
78
+ expect(isValid).toBe(false);
79
+ });
80
+
81
+ it("verifyDhtEntry rejects wrong public key", async () => {
82
+ const keypair1 = await generateKeypair();
83
+ const keypair2 = await generateKeypair();
84
+ const entryData: DHTEntryData = {
85
+ siteId: "test-site",
86
+ owner: "did:web:example.com#v1",
87
+ workshopEndpoint: "10.0.0.1:7947",
88
+ mirrors: [],
89
+ registeredAt: "2025-01-01T00:00:00.000Z",
90
+ lastUpdated: "2025-01-01T00:00:00.000Z",
91
+ };
92
+
93
+ const signature = await signDhtEntry(entryData, keypair1.privateKeyHex);
94
+ const fullEntry: DHTSiteEntry = { ...entryData, signature };
95
+
96
+ const isValid = await verifyDhtEntry(fullEntry, keypair2.publicKeyMultibase);
97
+ expect(isValid).toBe(false);
98
+ });
99
+ });
@@ -0,0 +1,73 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>
4
+ RFC-0565: DHT entry signing and verification — canonicalization and sign/verify
5
+ wrappers for DHTSiteEntry, built on the existing Ed25519 signBytes/verifyBytes
6
+ primitives from sign.ts. Analogous to identity-sign.ts but for the DHT site
7
+ entry shape.
8
+ </purpose>
9
+ <non-goals>
10
+ <item>Do not handle key storage or management — private keys arrive as env vars.</item>
11
+ <item>Do not assemble W3C VC envelopes — DHT entries use a simple signature string, not VCProof.</item>
12
+ <item>Do not modify sign.ts — this module wraps signBytes/verifyBytes.</item>
13
+ </non-goals>
14
+ </MODULE_CONTRACT>
15
+ <CHANGE_SUMMARY>
16
+ <item>RFC-0565: initial DHT entry signing module.</item>
17
+ </CHANGE_SUMMARY>
18
+ */
19
+
20
+ /**
21
+ * @warpgogol/werkstatt-shared/passport/dht-sign — DHT entry signing (RFC-0565)
22
+ *
23
+ * Uses signBytes/verifyBytes from sign.ts with a dedicated canonicalization
24
+ * function for DHT site entries. The signature field is excluded from
25
+ * canonicalization (it is computed over the entry data without the signature).
26
+ */
27
+
28
+ import { signBytes, verifyBytes } from "./sign.ts";
29
+ import type { DHTSiteEntry } from "@warpgogol/werkstatt-shared/ontology/operations";
30
+
31
+ /**
32
+ * DHTSiteEntry without the signature field — the data that is actually signed.
33
+ */
34
+ export type DHTEntryData = Omit<DHTSiteEntry, "signature">;
35
+
36
+ /**
37
+ * Produce canonical UTF-8 bytes from a DHT entry (excluding signature).
38
+ * DETERMINISM: identical inputs → identical bytes (sorted-key JSON, no whitespace).
39
+ */
40
+ export function dhtEntryBytes(entry: DHTEntryData): Uint8Array {
41
+ const sorted: Record<string, unknown> = {};
42
+ for (const key of Object.keys(entry).sort()) {
43
+ sorted[key] = (entry as Record<string, unknown>)[key];
44
+ }
45
+ const canonical = JSON.stringify(sorted);
46
+ return new TextEncoder().encode(canonical);
47
+ }
48
+
49
+ /**
50
+ * Sign a DHT entry and return a multibase Ed25519 signature string.
51
+ *
52
+ * @param entry — DHT entry data (without signature)
53
+ * @param privateKeyHex — 32-byte Ed25519 private key as hex (from env secret)
54
+ * @returns multibase base58btc signature string
55
+ */
56
+ export async function signDhtEntry(entry: DHTEntryData, privateKeyHex: string): Promise<string> {
57
+ return signBytes(privateKeyHex, dhtEntryBytes(entry));
58
+ }
59
+
60
+ /**
61
+ * Verify a DHT entry signature against the published public key.
62
+ *
63
+ * @param entry — full DHT site entry (including signature)
64
+ * @param publicKeyMultibase — multibase public key from werkstatt.identity.json
65
+ * @returns true if signature is valid
66
+ */
67
+ export async function verifyDhtEntry(
68
+ entry: DHTSiteEntry,
69
+ publicKeyMultibase: string,
70
+ ): Promise<boolean> {
71
+ const { signature, ...entryData } = entry;
72
+ return verifyBytes(publicKeyMultibase, dhtEntryBytes(entryData), signature);
73
+ }
@@ -0,0 +1,134 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>RFC-0558: unit tests for identity credential signing and verification.</purpose>
4
+ </MODULE_CONTRACT>
5
+ <CHANGE_SUMMARY>
6
+ <item>RFC-0558: initial identity-sign tests.</item>
7
+ </CHANGE_SUMMARY>
8
+ */
9
+
10
+ import { describe, it, expect } from "vitest";
11
+ import { generateKeypair } from "./sign.ts";
12
+ import {
13
+ identityCredentialBytes,
14
+ signIdentityCredential,
15
+ verifyIdentityCredential,
16
+ } from "./identity-sign.ts";
17
+ import type { SiteOwnershipCredentialSubject, ActorDelegationCredentialSubject } from "./schema.ts";
18
+
19
+ describe("identityCredentialBytes", () => {
20
+ it("produces deterministic bytes for SiteOwnershipCredentialSubject", () => {
21
+ const subject: SiteOwnershipCredentialSubject = {
22
+ id: "did:web:warpgogol.com#operator-v1",
23
+ siteId: "warpgogol-com",
24
+ role: "owner",
25
+ };
26
+ const bytes1 = identityCredentialBytes(subject);
27
+ const bytes2 = identityCredentialBytes(subject);
28
+ expect(bytes1).toEqual(bytes2);
29
+ });
30
+
31
+ it("produces deterministic bytes for ActorDelegationCredentialSubject", () => {
32
+ const subject: ActorDelegationCredentialSubject = {
33
+ id: "did:web:warpgogol.com#agent-v1",
34
+ siteId: "warpgogol-com",
35
+ delegatedBy: "did:web:warpgogol.com#operator-v1",
36
+ expiresAt: "2027-01-01T00:00:00.000Z",
37
+ scopes: ["*"],
38
+ };
39
+ const bytes1 = identityCredentialBytes(subject);
40
+ const bytes2 = identityCredentialBytes(subject);
41
+ expect(bytes1).toEqual(bytes2);
42
+ });
43
+
44
+ it("produces different bytes for different subjects", () => {
45
+ const subject1: SiteOwnershipCredentialSubject = {
46
+ id: "did:web:warpgogol.com#operator-v1",
47
+ siteId: "warpgogol-com",
48
+ role: "owner",
49
+ };
50
+ const subject2: SiteOwnershipCredentialSubject = {
51
+ id: "did:web:warpgogol.com#operator-v2",
52
+ siteId: "warpgogol-com",
53
+ role: "owner",
54
+ };
55
+ expect(identityCredentialBytes(subject1)).not.toEqual(identityCredentialBytes(subject2));
56
+ });
57
+ });
58
+
59
+ describe("signIdentityCredential / verifyIdentityCredential", () => {
60
+ it("round-trips for SiteOwnershipCredentialSubject", async () => {
61
+ const keypair = await generateKeypair();
62
+ const subject: SiteOwnershipCredentialSubject = {
63
+ id: "did:web:warpgogol.com#operator-v1",
64
+ siteId: "warpgogol-com",
65
+ role: "owner",
66
+ };
67
+ const proof = await signIdentityCredential(
68
+ subject,
69
+ keypair.privateKeyHex,
70
+ "did:web:warpgogol.com#v1",
71
+ "2026-07-27T00:00:00.000Z",
72
+ );
73
+ const valid = await verifyIdentityCredential(subject, proof, keypair.publicKeyMultibase);
74
+ expect(valid).toBe(true);
75
+ });
76
+
77
+ it("round-trips for ActorDelegationCredentialSubject", async () => {
78
+ const keypair = await generateKeypair();
79
+ const subject: ActorDelegationCredentialSubject = {
80
+ id: "did:web:warpgogol.com#agent-v1",
81
+ siteId: "warpgogol-com",
82
+ delegatedBy: "did:web:warpgogol.com#operator-v1",
83
+ expiresAt: "2027-01-01T00:00:00.000Z",
84
+ scopes: ["mission.open", "workpiece.write"],
85
+ };
86
+ const proof = await signIdentityCredential(
87
+ subject,
88
+ keypair.privateKeyHex,
89
+ "did:web:warpgogol.com#v1",
90
+ "2026-07-27T00:00:00.000Z",
91
+ );
92
+ const valid = await verifyIdentityCredential(subject, proof, keypair.publicKeyMultibase);
93
+ expect(valid).toBe(true);
94
+ });
95
+
96
+ it("fails verification for tampered subject", async () => {
97
+ const keypair = await generateKeypair();
98
+ const subject: SiteOwnershipCredentialSubject = {
99
+ id: "did:web:warpgogol.com#operator-v1",
100
+ siteId: "warpgogol-com",
101
+ role: "owner",
102
+ };
103
+ const proof = await signIdentityCredential(
104
+ subject,
105
+ keypair.privateKeyHex,
106
+ "did:web:warpgogol.com#v1",
107
+ "2026-07-27T00:00:00.000Z",
108
+ );
109
+ const tampered: SiteOwnershipCredentialSubject = {
110
+ ...subject,
111
+ siteId: "other-site",
112
+ };
113
+ const valid = await verifyIdentityCredential(tampered, proof, keypair.publicKeyMultibase);
114
+ expect(valid).toBe(false);
115
+ });
116
+
117
+ it("fails verification with wrong public key", async () => {
118
+ const keypair1 = await generateKeypair();
119
+ const keypair2 = await generateKeypair();
120
+ const subject: SiteOwnershipCredentialSubject = {
121
+ id: "did:web:warpgogol.com#operator-v1",
122
+ siteId: "warpgogol-com",
123
+ role: "owner",
124
+ };
125
+ const proof = await signIdentityCredential(
126
+ subject,
127
+ keypair1.privateKeyHex,
128
+ "did:web:warpgogol.com#v1",
129
+ "2026-07-27T00:00:00.000Z",
130
+ );
131
+ const valid = await verifyIdentityCredential(subject, proof, keypair2.publicKeyMultibase);
132
+ expect(valid).toBe(false);
133
+ });
134
+ });
@@ -0,0 +1,85 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>
4
+ RFC-0558: Identity credential signing and verification — canonicalization and
5
+ sign/verify wrappers for SiteOwnershipCredential and ActorDelegationCredential
6
+ subjects, built on the existing Ed25519 signBytes/verifyBytes primitives.
7
+ </purpose>
8
+ <non-goals>
9
+ <item>Do not handle key storage or management — private keys arrive as env vars.</item>
10
+ <item>Do not assemble full W3C VC envelopes — callers attach the proof after signing.</item>
11
+ <item>Do not modify sign.ts — this module wraps signBytes/verifyBytes, not signCredential.</item>
12
+ </non-goals>
13
+ </MODULE_CONTRACT>
14
+ <CHANGE_SUMMARY>
15
+ <item>RFC-0558: initial identity credential signing module.</item>
16
+ </CHANGE_SUMMARY>
17
+ */
18
+
19
+ /**
20
+ * @warpgogol/werkstatt-shared/passport/identity-sign — Identity credential signing (RFC-0558)
21
+ *
22
+ * Uses signBytes/verifyBytes from sign.ts with a dedicated canonicalization
23
+ * function for identity credential subjects. signCredential is NOT reused —
24
+ * it is typed to CredentialSubjectDigest (build provenance fields) and cannot
25
+ * accept identity credential subjects.
26
+ */
27
+
28
+ import { signBytes, verifyBytes } from "./sign.ts";
29
+ import type {
30
+ SiteOwnershipCredentialSubject,
31
+ ActorDelegationCredentialSubject,
32
+ VCProof,
33
+ } from "./schema.ts";
34
+
35
+ export type IdentityCredentialSubject =
36
+ SiteOwnershipCredentialSubject | ActorDelegationCredentialSubject;
37
+
38
+ /**
39
+ * Produce canonical UTF-8 bytes from an identity credential subject.
40
+ * DETERMINISM: identical inputs → identical bytes (sorted-key JSON, no whitespace).
41
+ */
42
+ export function identityCredentialBytes(subject: IdentityCredentialSubject): Uint8Array {
43
+ const canonical = JSON.stringify(subject, Object.keys(subject).sort());
44
+ return new TextEncoder().encode(canonical);
45
+ }
46
+
47
+ /**
48
+ * Sign an identity credential subject and produce a W3C VC proof.
49
+ *
50
+ * @param subject — identity credential subject fields
51
+ * @param privateKeyHex — 32-byte Ed25519 private key as hex (from env secret)
52
+ * @param verificationMethod — DID key reference (e.g. "did:web:warpgogol.com#v1")
53
+ * @param issuedAt — ISO-8601 timestamp for the proof creation date
54
+ */
55
+ export async function signIdentityCredential(
56
+ subject: IdentityCredentialSubject,
57
+ privateKeyHex: string,
58
+ verificationMethod: string,
59
+ issuedAt: string,
60
+ ): Promise<VCProof> {
61
+ const proofValue = await signBytes(privateKeyHex, identityCredentialBytes(subject));
62
+ return {
63
+ type: "Ed25519Signature2020",
64
+ created: issuedAt,
65
+ verificationMethod,
66
+ proofPurpose: "assertionMethod",
67
+ proofValue,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Verify an identity credential proof against the published public key.
73
+ *
74
+ * @param subject — identity credential subject fields
75
+ * @param proof — proof object from the credential
76
+ * @param publicKeyMultibase — multibase public key from werkstatt.identity.json
77
+ * @returns true if signature is valid
78
+ */
79
+ export async function verifyIdentityCredential(
80
+ subject: IdentityCredentialSubject,
81
+ proof: VCProof,
82
+ publicKeyMultibase: string,
83
+ ): Promise<boolean> {
84
+ return verifyBytes(publicKeyMultibase, identityCredentialBytes(subject), proof.proofValue);
85
+ }
@@ -0,0 +1,53 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Facilitates the export of passport-related schemas, types, and utility functions for credential management.</purpose>
4
+ <non-goals>
5
+ <item>Do not implement business logic for credential validation or user authentication.</item>
6
+ <item>Do not manage raw data parsing or transport orchestration.</item>
7
+ </non-goals>
8
+ </MODULE_CONTRACT>
9
+ <CHANGE_SUMMARY>
10
+ <item>Tidied by compass.changesummary.tidy; see git history for prior entries.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+
14
+ /**
15
+ * @warpgogol/werkstatt-shared/passport — Cosmic Passport
16
+ * DNA-31, DNA-34 / RFC-0028
17
+ */
18
+
19
+ export {
20
+ PassportSchema,
21
+ PassportPublicKeyFileSchema,
22
+ PassportPublicKeyEntrySchema,
23
+ SiteOwnershipCredentialSubjectSchema,
24
+ ActorDelegationCredentialSubjectSchema,
25
+ WerkstattCredentialSchema,
26
+ WerkstattIdentityConfigSchema,
27
+ } from "./schema.ts";
28
+ export type {
29
+ PassportJson,
30
+ PassportPublicKeyFile,
31
+ PassportPublicKeyEntry,
32
+ VCProof,
33
+ VerifiableCredential,
34
+ SiteOwnershipCredentialSubject,
35
+ ActorDelegationCredentialSubject,
36
+ WerkstattCredential,
37
+ WerkstattIdentityConfig,
38
+ } from "./schema.ts";
39
+
40
+ export { rotateKey } from "./key-rotate.ts";
41
+ export type { KeyRotateOptions, KeyRotateResult } from "./key-rotate.ts";
42
+
43
+ export { signBytes, verifyBytes, generateKeypair } from "./sign.ts";
44
+
45
+ export {
46
+ identityCredentialBytes,
47
+ signIdentityCredential,
48
+ verifyIdentityCredential,
49
+ } from "./identity-sign.ts";
50
+ export type { IdentityCredentialSubject } from "./identity-sign.ts";
51
+
52
+ export { dhtEntryBytes, signDhtEntry, verifyDhtEntry } from "./dht-sign.ts";
53
+ export type { DHTEntryData } from "./dht-sign.ts";
@@ -0,0 +1,133 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Facilitates the generation and rotation of Ed25519 keypairs for secure application authentication.</purpose>
4
+ <non-goals>
5
+ <item>Do not handle raw key parsing or validation beyond schema checks.</item>
6
+ <item>Do not manage application configuration or orchestration.</item>
7
+ </non-goals>
8
+ </MODULE_CONTRACT>
9
+ <CHANGE_SUMMARY>
10
+ <item>Tidied by compass.changesummary.tidy; see git history for prior entries.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+
14
+ /**
15
+ * @warpgogol/werkstatt-shared/passport — Key rotation
16
+ *
17
+ * DNA-34 / RFC-0028
18
+ *
19
+ * SECURITY CONTRACT:
20
+ * - Private key is NEVER written to disk. It is printed to stdout ONCE
21
+ * for the engineer to paste into GitHub Actions secret PASSPORT_SIGNING_KEY.
22
+ * - The public key file is written to apps/<app>/public/.well-known/cosmic-passport-key.json
23
+ * and committed to the repository.
24
+ * - Old public keys remain in the file (active: false) for historical verification.
25
+ */
26
+
27
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
28
+ import { join, dirname } from "node:path";
29
+ import { generateKeypair } from "./sign.ts";
30
+ import { PassportPublicKeyFileSchema } from "./schema.ts";
31
+ import type { PassportPublicKeyFile, PassportPublicKeyEntry } from "./schema.ts";
32
+
33
+ export interface KeyRotateOptions {
34
+ /** Absolute path to the app directory */
35
+ appDirectory: string;
36
+ /** App identifier (from system.yaml) */
37
+ appId: string;
38
+ /** If true, this is the initial key generation (no previous key file exists) */
39
+ initial?: boolean;
40
+ }
41
+
42
+ export interface KeyRotateResult {
43
+ /** New key version string (e.g. "v2") */
44
+ newVersion: string;
45
+ /** New public key multibase — written to key file */
46
+ publicKeyMultibase: string;
47
+ /**
48
+ * Private key as hex string — print to stdout and discard.
49
+ * Store as GitHub Actions secret PASSPORT_SIGNING_KEY.
50
+ * NEVER log, write, or return this beyond the immediate CLI call.
51
+ */
52
+ privateKeyHex: string;
53
+ /** Path where the updated public key file was written */
54
+ publicKeyFilePath: string;
55
+ }
56
+
57
+ /**
58
+ * Generate a new Ed25519 keypair and update the public key file.
59
+ *
60
+ * Steps:
61
+ * 1. Generate new keypair
62
+ * 2. Load existing public key file (if any)
63
+ * 3. Mark all existing keys as active: false
64
+ * 4. Append new key as active: true
65
+ * 5. Write updated file to apps/<app>/public/.well-known/cosmic-passport-key.json
66
+ * 6. Return private key for secure storage (never written to disk)
67
+ *
68
+ * After calling this function:
69
+ * - Update system.yaml release.passport.keyVersion to the new version
70
+ * - Paste the returned privateKeyHex into GitHub Actions secret PASSPORT_SIGNING_KEY
71
+ * - Commit the updated public key file
72
+ */
73
+ export async function rotateKey(options: KeyRotateOptions): Promise<KeyRotateResult> {
74
+ const { appDirectory, appId, initial = false } = options;
75
+ const publicKeyFilePath = join(appDirectory, "public", ".well-known", "cosmic-passport-key.json");
76
+
77
+ // Step 1: Generate new keypair
78
+ const { privateKeyHex, publicKeyMultibase } = await generateKeypair();
79
+
80
+ // Step 2: Load existing key file
81
+ let existingKeys: PassportPublicKeyEntry[] = [];
82
+ let nextVersionNumber = 1;
83
+
84
+ if (!initial) {
85
+ try {
86
+ const raw = await readFile(publicKeyFilePath, "utf8");
87
+ const existing = PassportPublicKeyFileSchema.parse(JSON.parse(raw));
88
+ existingKeys = existing.keys;
89
+
90
+ // Determine next version number
91
+ const versionNumbers = existingKeys
92
+ .map((k) => parseInt(k.version.replace("v", ""), 10))
93
+ .filter((n) => !isNaN(n));
94
+ nextVersionNumber = Math.max(0, ...versionNumbers) + 1;
95
+ } catch {
96
+ // No existing file — treat as initial
97
+ }
98
+ }
99
+
100
+ const newVersion = `v${nextVersionNumber}`;
101
+
102
+ // Step 3: Mark existing keys inactive
103
+ const updatedKeys: PassportPublicKeyEntry[] = existingKeys.map((k) => ({
104
+ ...k,
105
+ active: false,
106
+ }));
107
+
108
+ // Step 4: Append new key
109
+ updatedKeys.push({
110
+ version: newVersion,
111
+ active: true,
112
+ type: "Ed25519VerificationKey2020",
113
+ publicKeyMultibase,
114
+ createdAt: new Date().toISOString(),
115
+ });
116
+
117
+ // Step 5: Write updated file
118
+ const updatedFile: PassportPublicKeyFile = {
119
+ schemaVersion: "1.0",
120
+ appId,
121
+ keys: updatedKeys,
122
+ };
123
+
124
+ await mkdir(dirname(publicKeyFilePath), { recursive: true });
125
+ await writeFile(publicKeyFilePath, JSON.stringify(updatedFile, null, 2), "utf8");
126
+
127
+ return {
128
+ newVersion,
129
+ publicKeyMultibase,
130
+ privateKeyHex,
131
+ publicKeyFilePath,
132
+ };
133
+ }