@xemahq/xema 0.56.0 → 0.57.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.
@@ -2599,355 +2599,355 @@
2599
2599
  },
2600
2600
  {
2601
2601
  "package": "@xemahq/xema",
2602
- "version": "0.56.0",
2602
+ "version": "0.57.0",
2603
2603
  "path": "edition/adapter-host-versions.mjs",
2604
2604
  "sha256": "sha256:1b7ebd3592f4da1225fba74aaba912e7d85fbceeb1626f7dcdac83491cbb4d69"
2605
2605
  },
2606
2606
  {
2607
2607
  "package": "@xemahq/xema",
2608
- "version": "0.56.0",
2608
+ "version": "0.57.0",
2609
2609
  "path": "edition/authorization-base.mjs",
2610
2610
  "sha256": "sha256:815c9be9f71731dcc805d85da2d733350ddb6fbdf32b6f1cf34f228ce6381c7c"
2611
2611
  },
2612
2612
  {
2613
2613
  "package": "@xemahq/xema",
2614
- "version": "0.56.0",
2614
+ "version": "0.57.0",
2615
2615
  "path": "edition/build-ledger.mjs",
2616
2616
  "sha256": "sha256:05202cbdd606aee086c8e9c62d28be68c429653a8b1d73a289ed0fe4ff14da85"
2617
2617
  },
2618
2618
  {
2619
2619
  "package": "@xemahq/xema",
2620
- "version": "0.56.0",
2620
+ "version": "0.57.0",
2621
2621
  "path": "edition/canonical-evidence-json.mjs",
2622
2622
  "sha256": "sha256:3b32f6786c9fb1c0deadf2f687077356a503b0db766392d2e0254aa1ab78feec"
2623
2623
  },
2624
2624
  {
2625
2625
  "package": "@xemahq/xema",
2626
- "version": "0.56.0",
2626
+ "version": "0.57.0",
2627
2627
  "path": "edition/canonical-repository-url.mjs",
2628
2628
  "sha256": "sha256:f36e4f7cbe7849789422ec2f0ebce2d62f7dc3684afecc3ac51a32560e72d8a1"
2629
2629
  },
2630
2630
  {
2631
2631
  "package": "@xemahq/xema",
2632
- "version": "0.56.0",
2632
+ "version": "0.57.0",
2633
2633
  "path": "edition/collected-refusals.mjs",
2634
2634
  "sha256": "sha256:ae21c069dbff0afa9aca18b7b909d5de405df2277f6431bc3fda20528115bcd1"
2635
2635
  },
2636
2636
  {
2637
2637
  "package": "@xemahq/xema",
2638
- "version": "0.56.0",
2638
+ "version": "0.57.0",
2639
2639
  "path": "edition/connector-source-authority.mjs",
2640
2640
  "sha256": "sha256:ea94475f4324737673accd7de1303c44db1cbc4dac61a865a06cd83abe310164"
2641
2641
  },
2642
2642
  {
2643
2643
  "package": "@xemahq/xema",
2644
- "version": "0.56.0",
2644
+ "version": "0.57.0",
2645
2645
  "path": "edition/cosign-certificate-constraints.mjs",
2646
2646
  "sha256": "sha256:bb9ba7038f1de9211c9cbcf99f02255f3623ce72d86c71fdcfa146a4e0edbd9a"
2647
2647
  },
2648
2648
  {
2649
2649
  "package": "@xemahq/xema",
2650
- "version": "0.56.0",
2650
+ "version": "0.57.0",
2651
2651
  "path": "edition/develop-graph.mjs",
2652
2652
  "sha256": "sha256:decebd437c9f4c8bdd6bee3baabfdc340fd7b9d175aca7e702cb330e912708d5"
2653
2653
  },
2654
2654
  {
2655
2655
  "package": "@xemahq/xema",
2656
- "version": "0.56.0",
2656
+ "version": "0.57.0",
2657
2657
  "path": "edition/dispatched-runtime-components.mjs",
2658
2658
  "sha256": "sha256:9712823d183a60108cd002b2d36a9e5c0867b9d576dd02825264a18f7b8ece69"
2659
2659
  },
2660
2660
  {
2661
2661
  "package": "@xemahq/xema",
2662
- "version": "0.56.0",
2662
+ "version": "0.57.0",
2663
2663
  "path": "edition/distribution-paths.mjs",
2664
2664
  "sha256": "sha256:d78a5c8405bc6c25d36fcec61b0030e1fdacac7d528e698adb96f906f4b70279"
2665
2665
  },
2666
2666
  {
2667
2667
  "package": "@xemahq/xema",
2668
- "version": "0.56.0",
2668
+ "version": "0.57.0",
2669
2669
  "path": "edition/edition-artifact.mjs",
2670
2670
  "sha256": "sha256:3b588c9c50f64f3e7a45b954fe7b5fc67e11fc8ce3e7b323727af4541d29ae7f"
2671
2671
  },
2672
2672
  {
2673
2673
  "package": "@xemahq/xema",
2674
- "version": "0.56.0",
2674
+ "version": "0.57.0",
2675
2675
  "path": "edition/edition-channel.mjs",
2676
2676
  "sha256": "sha256:3c0c1564dc50be133cad500452d953353aa5beb703142c05a17bbce8175ba9f3"
2677
2677
  },
2678
2678
  {
2679
2679
  "package": "@xemahq/xema",
2680
- "version": "0.56.0",
2680
+ "version": "0.57.0",
2681
2681
  "path": "edition/edition-chart-set.mjs",
2682
2682
  "sha256": "sha256:f9f16b58279c8c2619a39c5cc92bdbc334543d1a324304cd5d3b120d66dc8f3a"
2683
2683
  },
2684
2684
  {
2685
2685
  "package": "@xemahq/xema",
2686
- "version": "0.56.0",
2686
+ "version": "0.57.0",
2687
2687
  "path": "edition/edition-config.mjs",
2688
2688
  "sha256": "sha256:58af55e9a2b36ca23485246d44a295260afa240bcdc0649c697563b36085ddaa"
2689
2689
  },
2690
2690
  {
2691
2691
  "package": "@xemahq/xema",
2692
- "version": "0.56.0",
2692
+ "version": "0.57.0",
2693
2693
  "path": "edition/edition-constants.mjs",
2694
2694
  "sha256": "sha256:217fa5b1fa25cb50ee4e1b46ea5e086061ff1f44e00198e1dc28f52376f236a0"
2695
2695
  },
2696
2696
  {
2697
2697
  "package": "@xemahq/xema",
2698
- "version": "0.56.0",
2698
+ "version": "0.57.0",
2699
2699
  "path": "edition/edition-contract-parse.mjs",
2700
2700
  "sha256": "sha256:e2c6e66378f878499f44b62d060ae50197f40757b2044d9ce05290e367dfe21d"
2701
2701
  },
2702
2702
  {
2703
2703
  "package": "@xemahq/xema",
2704
- "version": "0.56.0",
2704
+ "version": "0.57.0",
2705
2705
  "path": "edition/edition-contract.json",
2706
2706
  "sha256": "sha256:566a17301ad15b94a06fef12b26bbe39e292ed2bf30a2659a9bf07ce5ce487e0"
2707
2707
  },
2708
2708
  {
2709
2709
  "package": "@xemahq/xema",
2710
- "version": "0.56.0",
2710
+ "version": "0.57.0",
2711
2711
  "path": "edition/edition-kind.mjs",
2712
2712
  "sha256": "sha256:b4f3358ffa81bbafb8c37d158b9e01032a7358c38b9d6e5aca409091f989e47a"
2713
2713
  },
2714
2714
  {
2715
2715
  "package": "@xemahq/xema",
2716
- "version": "0.56.0",
2716
+ "version": "0.57.0",
2717
2717
  "path": "edition/edition-protocol.mjs",
2718
2718
  "sha256": "sha256:f02ea6427e2fd7ceea2c3cb49f3fa6d85b982870e487082a7466f8037cd2bed9"
2719
2719
  },
2720
2720
  {
2721
2721
  "package": "@xemahq/xema",
2722
- "version": "0.56.0",
2722
+ "version": "0.57.0",
2723
2723
  "path": "edition/edition-publisher.mjs",
2724
2724
  "sha256": "sha256:8803b41067d81acbc35a8974426f606b5b8168b7a9c2581c16586a86f36db50f"
2725
2725
  },
2726
2726
  {
2727
2727
  "package": "@xemahq/xema",
2728
- "version": "0.56.0",
2728
+ "version": "0.57.0",
2729
2729
  "path": "edition/edition-source-pins.mjs",
2730
2730
  "sha256": "sha256:b141474fff33b06734fb62cd2552752b991c8267bb1c9bccb22a81dd80e91c6c"
2731
2731
  },
2732
2732
  {
2733
2733
  "package": "@xemahq/xema",
2734
- "version": "0.56.0",
2734
+ "version": "0.57.0",
2735
2735
  "path": "edition/edition-workspace.mjs",
2736
2736
  "sha256": "sha256:892b360f9f80d2308b0b12f9b99658ae06173878d9b71cf5d6fc8bd717b3c86e"
2737
2737
  },
2738
2738
  {
2739
2739
  "package": "@xemahq/xema",
2740
- "version": "0.56.0",
2740
+ "version": "0.57.0",
2741
2741
  "path": "edition/evidence-checkout.mjs",
2742
2742
  "sha256": "sha256:31fb669769b31de4c6f2b2501bb96959548172ef166e7568edbbfed7f5079a11"
2743
2743
  },
2744
2744
  {
2745
2745
  "package": "@xemahq/xema",
2746
- "version": "0.56.0",
2746
+ "version": "0.57.0",
2747
2747
  "path": "edition/evidence-grammars.mjs",
2748
2748
  "sha256": "sha256:4bea69437af5a36c139b5b41d0ecf2d1ddcf3f871e10c78e3d351f608eedca3b"
2749
2749
  },
2750
2750
  {
2751
2751
  "package": "@xemahq/xema",
2752
- "version": "0.56.0",
2752
+ "version": "0.57.0",
2753
2753
  "path": "edition/evidence-trust-pin.mjs",
2754
2754
  "sha256": "sha256:eb46e4d6a79dfac5536b7a439ab9df7dd4b03af369bdcfa9fcdd522aa1aab8dd"
2755
2755
  },
2756
2756
  {
2757
2757
  "package": "@xemahq/xema",
2758
- "version": "0.56.0",
2758
+ "version": "0.57.0",
2759
2759
  "path": "edition/first-party-source-ids.mjs",
2760
2760
  "sha256": "sha256:20cb1ad7cfb8c6e6686a6be5a51289da4bba76c20b58d9c1b5b6b3813a38dc1c"
2761
2761
  },
2762
2762
  {
2763
2763
  "package": "@xemahq/xema",
2764
- "version": "0.56.0",
2764
+ "version": "0.57.0",
2765
2765
  "path": "edition/fulcio-certificate-extensions.mjs",
2766
2766
  "sha256": "sha256:6f1f5a8ea110a489f7aff4bfb8e0b2d058919fe3e8ba4a1e5e8396fca24861b7"
2767
2767
  },
2768
2768
  {
2769
2769
  "package": "@xemahq/xema",
2770
- "version": "0.56.0",
2770
+ "version": "0.57.0",
2771
2771
  "path": "edition/git-json.mjs",
2772
2772
  "sha256": "sha256:d0d25cda0c19007becabc9a2827c3dcf759b295cd0b41596eb4480c9549a1f21"
2773
2773
  },
2774
2774
  {
2775
2775
  "package": "@xemahq/xema",
2776
- "version": "0.56.0",
2776
+ "version": "0.57.0",
2777
2777
  "path": "edition/git-source-auth.mjs",
2778
2778
  "sha256": "sha256:c1b32aa6f7d59798db58238d632cafeadbd4f07376396a31acdeb5f0ac56dcac"
2779
2779
  },
2780
2780
  {
2781
2781
  "package": "@xemahq/xema",
2782
- "version": "0.56.0",
2782
+ "version": "0.57.0",
2783
2783
  "path": "edition/image-delivery-entitlement-descriptor.mjs",
2784
2784
  "sha256": "sha256:4726b66116f01bf2523fafe2b7a356591eca8fb5ca6bd8b154dd4821873e921b"
2785
2785
  },
2786
2786
  {
2787
2787
  "package": "@xemahq/xema",
2788
- "version": "0.56.0",
2788
+ "version": "0.57.0",
2789
2789
  "path": "edition/image-evidence-replay-state.mjs",
2790
2790
  "sha256": "sha256:eba0cf1d38af0ab2f00c5099fa8bc2edc543136ce92f895544d4d284b3d6af0f"
2791
2791
  },
2792
2792
  {
2793
2793
  "package": "@xemahq/xema",
2794
- "version": "0.56.0",
2794
+ "version": "0.57.0",
2795
2795
  "path": "edition/image-evidence.mjs",
2796
2796
  "sha256": "sha256:c6a202f3fc8efe97f00c7e69532c656646bdc5c93365d68676046c3aabd33233"
2797
2797
  },
2798
2798
  {
2799
2799
  "package": "@xemahq/xema",
2800
- "version": "0.56.0",
2800
+ "version": "0.57.0",
2801
2801
  "path": "edition/image-subject-identity.mjs",
2802
2802
  "sha256": "sha256:6a64e44f9865f68484540e3264a1d87040ae021dfa6ee4b79239e66805754ac1"
2803
2803
  },
2804
2804
  {
2805
2805
  "package": "@xemahq/xema",
2806
- "version": "0.56.0",
2806
+ "version": "0.57.0",
2807
2807
  "path": "edition/kernel-capability-domain.mjs",
2808
2808
  "sha256": "sha256:3ecf887dd8d399e91a8285f6f115f6e418fb59bbf3f004539fe65ec667219356"
2809
2809
  },
2810
2810
  {
2811
2811
  "package": "@xemahq/xema",
2812
- "version": "0.56.0",
2812
+ "version": "0.57.0",
2813
2813
  "path": "edition/kernel-contract-authority.mjs",
2814
2814
  "sha256": "sha256:9de2645dca22274cad9fa68d0364bf57e4de704e9d49a8bbadde0e322ae317df"
2815
2815
  },
2816
2816
  {
2817
2817
  "package": "@xemahq/xema",
2818
- "version": "0.56.0",
2818
+ "version": "0.57.0",
2819
2819
  "path": "edition/kernel-contract-source-path-set.mjs",
2820
2820
  "sha256": "sha256:bc4756f217d80fd8f802932f47da71ab14e782873fa9fa977002a94e058d8a9e"
2821
2821
  },
2822
2822
  {
2823
2823
  "package": "@xemahq/xema",
2824
- "version": "0.56.0",
2824
+ "version": "0.57.0",
2825
2825
  "path": "edition/kernel-package.mjs",
2826
2826
  "sha256": "sha256:e97a9491991d8332a4feed14ac48fe393e44d12c63bdfec89fac8b5b4ea2a574"
2827
2827
  },
2828
2828
  {
2829
2829
  "package": "@xemahq/xema",
2830
- "version": "0.56.0",
2830
+ "version": "0.57.0",
2831
2831
  "path": "edition/lib.mjs",
2832
2832
  "sha256": "sha256:a0695c1e1649da205f4e5d0e8d411079d6af4eb00c7d753bb2e9bf9a5ed1232e"
2833
2833
  },
2834
2834
  {
2835
2835
  "package": "@xemahq/xema",
2836
- "version": "0.56.0",
2836
+ "version": "0.57.0",
2837
2837
  "path": "edition/no-follow-file.mjs",
2838
2838
  "sha256": "sha256:c17bdf5e441f3c079aa06d4b48bbed05cad087855b9ad2a5e6dc43d5ee2b17c3"
2839
2839
  },
2840
2840
  {
2841
2841
  "package": "@xemahq/xema",
2842
- "version": "0.56.0",
2842
+ "version": "0.57.0",
2843
2843
  "path": "edition/operation-requirements.mjs",
2844
2844
  "sha256": "sha256:d3b6807953f162ab4204dcd1a0f752e0f9b447726fc17a12534d28abe8e570b0"
2845
2845
  },
2846
2846
  {
2847
2847
  "package": "@xemahq/xema",
2848
- "version": "0.56.0",
2848
+ "version": "0.57.0",
2849
2849
  "path": "edition/pin-source.mjs",
2850
2850
  "sha256": "sha256:32fa918729a83587b4d3bca6e5fe381f5a525397ebc49d52729ec1f8cf216c37"
2851
2851
  },
2852
2852
  {
2853
2853
  "package": "@xemahq/xema",
2854
- "version": "0.56.0",
2854
+ "version": "0.57.0",
2855
2855
  "path": "edition/sigstore-bundle-media-type.mjs",
2856
2856
  "sha256": "sha256:e0aa5a3992f0385ddbbe69eb0307d642ac428ef516c8394816ac4fd52e0ac39c"
2857
2857
  },
2858
2858
  {
2859
2859
  "package": "@xemahq/xema",
2860
- "version": "0.56.0",
2860
+ "version": "0.57.0",
2861
2861
  "path": "edition/source-authority.mjs",
2862
2862
  "sha256": "sha256:9c351f60e8ea838f445f89c3709c60b7f13b021b78d6e40eaef91350a793f0ff"
2863
2863
  },
2864
2864
  {
2865
2865
  "package": "@xemahq/xema",
2866
- "version": "0.56.0",
2866
+ "version": "0.57.0",
2867
2867
  "path": "edition/strict-json.mjs",
2868
2868
  "sha256": "sha256:f95778396df7f38b086f55d2ec4f3b6644a8aeff2b32282a62829145fb544562"
2869
2869
  },
2870
2870
  {
2871
2871
  "package": "@xemahq/xema",
2872
- "version": "0.56.0",
2872
+ "version": "0.57.0",
2873
2873
  "path": "edition/string-order.mjs",
2874
2874
  "sha256": "sha256:b3210cd273e4b2e01fb8b1a91e28734cd211d4010c080d0ce95f5e5ff631cffb"
2875
2875
  },
2876
2876
  {
2877
2877
  "package": "@xemahq/xema",
2878
- "version": "0.56.0",
2878
+ "version": "0.57.0",
2879
2879
  "path": "edition/third-party-admission.mjs",
2880
2880
  "sha256": "sha256:7e32f631a8948e2b93a5cfa163390d52d2a8d9ade87a637c7fe1fc6351fe5974"
2881
2881
  },
2882
2882
  {
2883
2883
  "package": "@xemahq/xema",
2884
- "version": "0.56.0",
2884
+ "version": "0.57.0",
2885
2885
  "path": "edition/third-party-certificate-extensions.mjs",
2886
2886
  "sha256": "sha256:3293267b09452199f605001d8c14070d1c822b77390caee4088e17b059b7b05d"
2887
2887
  },
2888
2888
  {
2889
2889
  "package": "@xemahq/xema",
2890
- "version": "0.56.0",
2890
+ "version": "0.57.0",
2891
2891
  "path": "edition/third-party-source-pins.mjs",
2892
2892
  "sha256": "sha256:6f12feb9061bd4ea3e5aeab3f369c081ab58da335c0d281e21b250251e1c0dd9"
2893
2893
  },
2894
2894
  {
2895
2895
  "package": "@xemahq/xema",
2896
- "version": "0.56.0",
2896
+ "version": "0.57.0",
2897
2897
  "path": "edition/trust-policy-arming.mjs",
2898
2898
  "sha256": "sha256:f6ed96ee1d0b7698b1eea5066ba10abef1babeede8403a7c481bba53636cb1a5"
2899
2899
  },
2900
2900
  {
2901
2901
  "package": "@xemahq/xema",
2902
- "version": "0.56.0",
2902
+ "version": "0.57.0",
2903
2903
  "path": "edition/trust-policy-image-ownership.mjs",
2904
2904
  "sha256": "sha256:be0c787c6a75124f0143fc0ce3b127a94e3bb7f6aa64e282c91dd84e566e550e"
2905
2905
  },
2906
2906
  {
2907
2907
  "package": "@xemahq/xema",
2908
- "version": "0.56.0",
2908
+ "version": "0.57.0",
2909
2909
  "path": "edition/trusted-executable.mjs",
2910
2910
  "sha256": "sha256:37d46cdbc999ddd55d4ea8aeed4850ddd1efde89ed7b532f5a2ddec87105d796"
2911
2911
  },
2912
2912
  {
2913
2913
  "package": "@xemahq/xema",
2914
- "version": "0.56.0",
2914
+ "version": "0.57.0",
2915
2915
  "path": "edition/verifier-binding.mjs",
2916
2916
  "sha256": "sha256:80732658617f5255258c538eeb2383331ec8553b44b58d552b567845a7b916aa"
2917
2917
  },
2918
2918
  {
2919
2919
  "package": "@xemahq/xema",
2920
- "version": "0.56.0",
2920
+ "version": "0.57.0",
2921
2921
  "path": "edition/verifier-files.mjs",
2922
2922
  "sha256": "sha256:cdd14a20a63f0f194a8abb55680a5a7e3ced1f09f8b210a90d349e655f6de0c0"
2923
2923
  },
2924
2924
  {
2925
2925
  "package": "@xemahq/xema",
2926
- "version": "0.56.0",
2926
+ "version": "0.57.0",
2927
2927
  "path": "edition/verifier-provenance.mjs",
2928
2928
  "sha256": "sha256:3e2fb8e07cf1f66b8ad6c7ac2555fe3af64ec7abeb07cecbaf1939a18c2b3663"
2929
2929
  },
2930
2930
  {
2931
2931
  "package": "@xemahq/xema",
2932
- "version": "0.56.0",
2932
+ "version": "0.57.0",
2933
2933
  "path": "edition/verify-catalogue-evidence.mjs",
2934
2934
  "sha256": "sha256:156d0ee39210658ec489609629c5b9cddd3ce5b3938d8980dcf8282ae4be60bb"
2935
2935
  },
2936
2936
  {
2937
2937
  "package": "@xemahq/xema",
2938
- "version": "0.56.0",
2938
+ "version": "0.57.0",
2939
2939
  "path": "edition/verify-image-evidence.mjs",
2940
2940
  "sha256": "sha256:958360f7889985c48abb048852c5bbba349cd7353ce4b532c4511b4902380fac"
2941
2941
  },
2942
2942
  {
2943
2943
  "package": "@xemahq/xema",
2944
- "version": "0.56.0",
2944
+ "version": "0.57.0",
2945
2945
  "path": "edition/workspace-sections.mjs",
2946
2946
  "sha256": "sha256:1d2aff08bcd9e94b644c85c5b55c90c05b6b02bab662731fe3fcd53fcdf86e93"
2947
2947
  },
2948
2948
  {
2949
2949
  "package": "@xemahq/xema",
2950
- "version": "0.56.0",
2950
+ "version": "0.57.0",
2951
2951
  "path": "scripts/build-evidence-verifier.mjs",
2952
2952
  "sha256": "sha256:5a688d20c47dc88aff37aa0c6cc02daf2c370a5bc610fef8446ce7be222d4c0b"
2953
2953
  },
package/edition/README.md CHANGED
@@ -180,7 +180,13 @@ The edition library builds, validates and verifies evidence statements; it never
180
180
 
181
181
  ## Previous seal
182
182
 
183
- A write of `images.lock.json` (`resolve-images`, `xema edition develop resolve`) takes exactly one of `--previous-lock <lock> --previous-lock-sha256 <hex>` or `--first-seal`; the CLI refuses unless the lock's bytes hash to the digest, which the caller takes from the images.lock layer of the signed, published edition (the latest, under the per-edition concurrency group). The in-repo resolve and pin/unpin take the committed `images.lock.json` at the target path as the previous lock (first seal when absent). The the composed artifacts' generations carry forward from it.
183
+ A write of `images.lock.json` (`resolve-images`, `xema edition develop resolve`) takes exactly one previous seal, whose composed artifacts' generations carry forward:
184
+
185
+ - `--previous-seal-signer <certificate identity>` (develop resolve, vendor form): the CLI lists the edition's own repository (`<registry root>/develop-editions/xema-edition-<id>`), reads every snapshot tag, and takes the newest edition by its `org.opencontainers.image.created` annotation (inside the signed manifest). An edition with no signature was never published and is passed over; the newest signed one must verify, offline through the pinned cosign, for exactly that identity and the GitHub OIDC issuer, or the resolve is refused (it never steps back to an older edition). Its images.lock layer is fetched by digest. The run must be under the per-edition concurrency group, so the edition read is the one the previous run pushed.
186
+ - `--previous-lock <lock> --previous-lock-sha256 <hex>`: the same layer, taken by the caller; the CLI refuses unless the bytes hash to the digest.
187
+ - `--first-seal`: generation 1, only for a distribution that has never had an edition. The CLI never infers it: a 404 for the repository (a registry may hide a repository from a credential) and a repository with no signed edition (retention) are refused, naming `--first-seal`.
188
+
189
+ The in-repo resolve and pin/unpin take the committed `images.lock.json` at the target path as the previous lock (first seal when absent).
184
190
 
185
191
  ## Kernel protocol and pins
186
192
 
@@ -34,6 +34,7 @@ import { canonicalJson, sha256Digest } from './image-evidence.mjs';
34
34
  import { adoptCatalogueKernel } from './kernel-package.mjs';
35
35
  import { loadKernelDistributionSchemas, serializeJson } from './lib.mjs';
36
36
  import { isSourcePinKey, sortedSnapshotPins, sourcePinKey } from './pin-source.mjs';
37
+ import { PREVIOUS_SEAL_SIGNER_FLAG } from './previous-seal-registry.mjs';
37
38
  import { assertSealedImageLock } from './release-edition-artifact.mjs';
38
39
  import { assertSourceImagesCoverDefinition, parseSourceImages, resolveCustomerEdition } from './resolve-customer-edition.mjs';
39
40
  import { externalSourceTrees, imageRefForSubject, sourceTreeLocator } from './resolve-images.mjs';
@@ -191,12 +192,13 @@ const COMMANDS = Object.freeze({
191
192
  resolve: {
192
193
  positional: true,
193
194
  // --images-from: the everyday job's image build record (images-from-record.mjs) instead of the registry probe; vendor only.
194
- // --previous-lock with --previous-lock-sha256 (the digest of the latest signed, published edition's images.lock layer, passed by the workflow under
195
- // the per-edition concurrency group) or --first-seal: the previous seal whose composed artifacts carry their generation forward; vendor only.
196
- values: ['--pins', '--out', '--registry', ENGINE_REPOSITORY_FLAG, ENGINE_SIGNER_FLAG, COSIGN_FLAG, IMAGES_FROM_FLAG, PREVIOUS_LOCK_FLAG, PREVIOUS_LOCK_SHA256_FLAG],
195
+ // The previous seal whose composed artifacts carry their generation forward, exactly one of: --previous-seal-signer <identity> (the CLI reads
196
+ // the latest edition that identity signed in this edition's repository; the workflow runs under the per-edition concurrency group),
197
+ // --previous-lock with --previous-lock-sha256 (that edition's images.lock layer, taken by the caller) or --first-seal; vendor only.
198
+ values: ['--pins', '--out', '--registry', ENGINE_REPOSITORY_FLAG, ENGINE_SIGNER_FLAG, COSIGN_FLAG, IMAGES_FROM_FLAG, PREVIOUS_LOCK_FLAG, PREVIOUS_LOCK_SHA256_FLAG, PREVIOUS_SEAL_SIGNER_FLAG],
197
199
  switches: ['--local', FIRST_SEAL_FLAG],
198
200
  required: ['--pins', '--out', '--registry', ENGINE_REPOSITORY_FLAG, ENGINE_SIGNER_FLAG, COSIGN_FLAG],
199
- customer: { values: [DEFINITION_FLAG, ...CREDENTIAL_FLAGS, SOURCE_IMAGE_FLAG, COSIGN_FLAG, ENGINE_IMAGE_FLAG], required: ['--pins', '--out', '--registry', '--engine-image'], refused: [IMAGES_FROM_FLAG, PREVIOUS_LOCK_FLAG, PREVIOUS_LOCK_SHA256_FLAG, FIRST_SEAL_FLAG], cosign: true },
201
+ customer: { values: [DEFINITION_FLAG, ...CREDENTIAL_FLAGS, SOURCE_IMAGE_FLAG, COSIGN_FLAG, ENGINE_IMAGE_FLAG], required: ['--pins', '--out', '--registry', '--engine-image'], refused: [IMAGES_FROM_FLAG, PREVIOUS_LOCK_FLAG, PREVIOUS_LOCK_SHA256_FLAG, FIRST_SEAL_FLAG, PREVIOUS_SEAL_SIGNER_FLAG], cosign: true },
200
202
  // A vendor resolve finds its engine itself; the old flag is refused by name, never read as a customer flag.
201
203
  removedFromVendor: { [ENGINE_IMAGE_FLAG]: `use ${ENGINE_REPOSITORY_FLAG} and ${ENGINE_SIGNER_FLAG}` },
202
204
  },
@@ -130,7 +130,7 @@ import { runImageRecord } from './develop-image-record.mjs';
130
130
  import { runSelectRecords } from './develop-select-records.mjs';
131
131
  import { readSelectionPins } from './develop-selection-pins.mjs';
132
132
  import { runVerifyImageRecord } from './develop-verify-image-record.mjs';
133
- import { LOCAL_WORKFLOW, assertExistingArtifactMatches, developEditionTarget, workflowFromEnv, writeEditionArtifact } from './edition-artifact.mjs';
133
+ import { LOCAL_WORKFLOW, assertExistingArtifactMatches, developEditionTarget, editionRepository, workflowFromEnv, writeEditionArtifact } from './edition-artifact.mjs';
134
134
  import { EditionChannel, EditionKind } from './edition-constants.mjs';
135
135
  import { WorkspaceRoot, configureEditionWorkspaceFromProcess, isEntryModule, kernelRepoDir } from './edition-workspace.mjs';
136
136
  import { descriptorEvidenceTrust } from './evidence-trust-policy.mjs';
@@ -138,6 +138,7 @@ import { createSatelliteVerifier, imageBuildRecordDigest, readImageBuildRecord }
138
138
  import { assertKernelCommitSupported, chooseKernelCommit } from './kernel-protocol-choice.mjs';
139
139
  import { loadKernelDistributionSchemas } from './lib.mjs';
140
140
  import { snapshotPinSource } from './pin-source.mjs';
141
+ import { PREVIOUS_SEAL_SIGNER_FLAG, previousSealFromRegistry } from './previous-seal-registry.mjs';
141
142
  import { composeEdition } from './resolve-distribution.mjs';
142
143
  import { resolvePreviousSeal } from './resolve-images.mjs';
143
144
  import { readStrictJsonFile } from './strict-json.mjs';
@@ -206,7 +207,22 @@ async function run(argv, env) {
206
207
  env,
207
208
  engine: { repository: options['--engine-repository'], signer: options['--engine-signer'] },
208
209
  cosignBinary: options['--cosign'],
209
- previousLock: await resolvePreviousSeal({ distributionId: id, previousLockPath: options[PREVIOUS_LOCK_FLAG], previousLockSha256: options[PREVIOUS_LOCK_SHA256_FLAG], firstSeal: Boolean(options[FIRST_SEAL_FLAG]) }),
210
+ previousLock: await resolvePreviousSeal({
211
+ distributionId: id,
212
+ previousLockPath: options[PREVIOUS_LOCK_FLAG],
213
+ previousLockSha256: options[PREVIOUS_LOCK_SHA256_FLAG],
214
+ firstSeal: Boolean(options[FIRST_SEAL_FLAG]),
215
+ ...(options[PREVIOUS_SEAL_SIGNER_FLAG] === undefined ? {} : {
216
+ registry: {
217
+ find: () => previousSealFromRegistry({
218
+ repository: editionRepository(id, options['--registry'], EditionChannel.Develop),
219
+ signerIdentity: options[PREVIOUS_SEAL_SIGNER_FLAG],
220
+ cosignBinary: options['--cosign'],
221
+ env,
222
+ }),
223
+ },
224
+ }),
225
+ }),
210
226
  ...(options[IMAGES_FROM_FLAG] === undefined ? {} : {
211
227
  imagesFrom: await readImageBuildRecord(options[IMAGES_FROM_FLAG]),
212
228
  verifySatellites: createSatelliteVerifier({ registry: options['--registry'], cosignBinary: options['--cosign'], env }),
@@ -0,0 +1,149 @@
1
+ // previous-seal-registry — the previous seal of a snapshot edition, read from the edition's own registry repository.
2
+ //
3
+ // A write of an images lock carries its composed artifacts' generations forward from the previous seal (resolve-images.mjs
4
+ // resolvePreviousSeal). In CI that is the images.lock layer of the LATEST SIGNED edition already published to the
5
+ // edition repository `<registry root>/<channel segment>/xema-edition-<id>`. `--previous-seal-signer <identity>` finds it:
6
+ //
7
+ // - every snapshot tag (64 hex) is read by tag, deduplicated by digest; each must be an edition artifact with an
8
+ // `org.opencontainers.image.created` annotation (oras writes it on push; it is inside the signed manifest);
9
+ // - the newest by that annotation is the candidate. A candidate with no cosign signature tag (`sha256-<hex>.sig`) was never
10
+ // published (a failed sign) and is passed over, named; a candidate WITH a signature must verify against exactly the given
11
+ // certificate identity and the GitHub OIDC issuer, offline, through the pinned cosign. A failure there is a refusal,
12
+ // never a step back to an older edition (that would regress or fork the generation);
13
+ // - its images.lock layer is fetched by digest (the bytes hash to the layer digest the signed manifest names).
14
+ //
15
+ // FIRST SEAL is never inferred here. A registry that answers 404 for the repository's tag list may be one that hides a
16
+ // private repository from a credential that cannot read it, and a repository that exists but holds no signed edition may
17
+ // have lost its editions to retention: neither proves that no edition was ever published. Both are refused, naming the
18
+ // operator's explicit `--first-seal` (once, for a distribution that has never had an edition).
19
+ //
20
+ // Concurrency is the caller's: the per-edition concurrency group serializes the runs that push to one repository, so the
21
+ // edition read here is the one the previous run of that group pushed and signed.
22
+
23
+ import { RefusalError } from './collected-refusals.mjs';
24
+ import { EDITION_ARTIFACT_TYPE, EditionLockFile } from './edition-constants.mjs';
25
+ import { cosignEnvironment, withPinnedCosign } from './pinned-cosign.mjs';
26
+ import { splitImageRef } from './registry-credentials.mjs';
27
+ import { isNotPublishedYet } from './seal-image-digests.mjs';
28
+ import { GITHUB_ACTIONS_OIDC_ISSUER } from './third-party-source-evidence.mjs';
29
+
30
+ export const PREVIOUS_SEAL_SIGNER_FLAG = '--previous-seal-signer';
31
+
32
+ const SNAPSHOT_TAG_PATTERN = /^[0-9a-f]{64}$/;
33
+ const SIGNATURE_TAG_PATTERN = /^sha256-([0-9a-f]{64})\.sig$/;
34
+ const CREATED_ANNOTATION = 'org.opencontainers.image.created';
35
+ const TITLE_ANNOTATION = 'org.opencontainers.image.title';
36
+ const RFC3339_UTC_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
37
+ const IDENTITY_PATTERN = /^https:\/\/github\.com\/[^/@\s]+\/[^/@\s]+\/\.github\/workflows\/[^/@\s]+@refs\/[^\s]+$/;
38
+ const MAX_IMAGES_LOCK_BYTES = 64 * 1024 * 1024;
39
+ const MANIFEST_READ_CONCURRENCY = 4;
40
+ const STDERR_TAIL_BYTES = 800;
41
+
42
+ const tail = (text) => String(text).trim().slice(-STDERR_TAIL_BYTES);
43
+
44
+ /**
45
+ * The signer `--previous-seal-signer` names: the EXACT certificate identity of the workflow that signs this repository's
46
+ * editions (`https://github.com/<owner>/<name>/.github/workflows/<file>@refs/...`) and the GitHub OIDC issuer. Only the
47
+ * identity is held, not the workflow-repository extension: a reusable workflow called from another repository signs
48
+ * with its own identity but the CALLER's repository extension.
49
+ */
50
+ export function previousSealSignerOf(identity) {
51
+ if (typeof identity !== 'string' || !IDENTITY_PATTERN.test(identity)) {
52
+ throw new RefusalError(
53
+ `${PREVIOUS_SEAL_SIGNER_FLAG} must be the exact certificate identity https://github.com/<owner>/<name>/.github/workflows/<file>@refs/<...>; got ${JSON.stringify(identity)}`,
54
+ );
55
+ }
56
+ return { certificateIdentity: identity, oidcIssuer: GITHUB_ACTIONS_OIDC_ISSUER };
57
+ }
58
+
59
+ async function mapBounded(items, limit, fn) {
60
+ const results = new Array(items.length);
61
+ let next = 0;
62
+ const worker = async () => {
63
+ while (next < items.length) {
64
+ const index = next;
65
+ next += 1;
66
+ results[index] = await fn(items[index]);
67
+ }
68
+ };
69
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
70
+ return results;
71
+ }
72
+
73
+ /**
74
+ * Find the latest signed edition of `repository` (`<authority>/<path>`) and return its images.lock bytes.
75
+ * @returns {Promise<{edition: string, created: string, imagesLockSha256: string, bytes: Buffer}>}
76
+ */
77
+ export async function findPreviousSealOnRegistry({ repository, signer, clientFor, runCosign, cosignEnvFor, trustedRootPath, log = (line) => console.error(line) }) {
78
+ const { authority, repository: path } = splitImageRef(repository);
79
+ const client = clientFor(authority);
80
+ let tags;
81
+ try {
82
+ tags = await client.listTags(path);
83
+ } catch (error) {
84
+ if (isNotPublishedYet(error)) {
85
+ throw new RefusalError(
86
+ `[previous-seal] the registry answered 404 for the tags of ${repository}: either no edition was ever published there or this credential cannot see it, ` +
87
+ 'and a 404 does not tell which. If this distribution has never had an edition, resolve once with --first-seal; otherwise fix the credential.',
88
+ );
89
+ }
90
+ throw new Error(`[previous-seal] the registry did not answer for the tags of ${repository}; refusing to guess the previous seal: ${error.message}`);
91
+ }
92
+ const signed = new Set(tags.map((tag) => SIGNATURE_TAG_PATTERN.exec(tag)?.[1]).filter((hex) => hex !== undefined).map((hex) => `sha256:${hex}`));
93
+ const snapshotTags = tags.filter((tag) => SNAPSHOT_TAG_PATTERN.test(tag)).sort();
94
+ const read = await mapBounded(snapshotTags, MANIFEST_READ_CONCURRENCY, async (tag) => ({ tag, manifest: await client.getManifestByTag(path, tag) }));
95
+ const byDigest = new Map();
96
+ for (const { tag, manifest } of read) {
97
+ const ref = `${repository}:${tag}`;
98
+ if (manifest.isIndex || manifest.document?.artifactType !== EDITION_ARTIFACT_TYPE) {
99
+ throw new RefusalError(`[previous-seal] ${ref} is not an edition artifact (artifactType ${JSON.stringify(manifest.document?.artifactType)}); refusing to choose a previous seal past it`);
100
+ }
101
+ const created = manifest.document.annotations?.[CREATED_ANNOTATION];
102
+ if (typeof created !== 'string' || !RFC3339_UTC_PATTERN.test(created) || Number.isNaN(Date.parse(created))) {
103
+ throw new RefusalError(`[previous-seal] ${ref} (${manifest.digest}) has no ${CREATED_ANNOTATION} annotation in RFC 3339 UTC; the latest edition cannot be told`);
104
+ }
105
+ if (!byDigest.has(manifest.digest)) {byDigest.set(manifest.digest, { digest: manifest.digest, tag, created, time: Date.parse(created), document: manifest.document });}
106
+ }
107
+ const editions = [...byDigest.values()].sort((a, b) => b.time - a.time);
108
+ for (let i = 0; i < editions.length; i += 1) {
109
+ const edition = editions[i];
110
+ const ref = `${repository}@${edition.digest}`;
111
+ if (i + 1 < editions.length && editions[i + 1].time === edition.time) {
112
+ throw new RefusalError(`[previous-seal] ${ref} and ${repository}@${editions[i + 1].digest} were both created at ${edition.created}; the latest edition cannot be told`);
113
+ }
114
+ if (!signed.has(edition.digest)) {
115
+ log(`[previous-seal] ${ref} (${edition.created}) carries no signature: it was never published; passing over it`);
116
+ continue;
117
+ }
118
+ const result = await runCosign(
119
+ ['verify', '--offline', '--trusted-root', trustedRootPath, '--certificate-identity', signer.certificateIdentity, '--certificate-oidc-issuer', signer.oidcIssuer, ref],
120
+ cosignEnvironment(cosignEnvFor, authority),
121
+ );
122
+ if (result.code !== 0) {
123
+ throw new RefusalError(
124
+ `[previous-seal] the latest edition ${ref} (${edition.created}) carries a signature that does not verify for ${signer.certificateIdentity}; ` +
125
+ `refusing it and refusing to fall back to an older edition (cosign exit ${result.code}): ${tail(result.stderr)}`,
126
+ );
127
+ }
128
+ const layers = (edition.document.layers ?? []).filter((layer) => layer.annotations?.[TITLE_ANNOTATION] === EditionLockFile.Images);
129
+ if (layers.length !== 1) {
130
+ throw new RefusalError(`[previous-seal] ${ref} carries ${layers.length} layers titled ${EditionLockFile.Images}; an edition carries exactly one`);
131
+ }
132
+ const [layer] = layers;
133
+ const bytes = await client.getBlob(path, layer.digest, MAX_IMAGES_LOCK_BYTES);
134
+ log(`[previous-seal] previous seal: ${ref} (${edition.created}), ${EditionLockFile.Images} ${layer.digest}`);
135
+ return { edition: ref, created: edition.created, imagesLockSha256: layer.digest.slice('sha256:'.length), bytes };
136
+ }
137
+ throw new RefusalError(
138
+ `[previous-seal] ${repository} exists but holds no signed edition (${editions.length} unsigned); that no edition was ever published cannot be proven. ` +
139
+ 'If none ever was, resolve once with --first-seal.',
140
+ );
141
+ }
142
+
143
+ /** `findPreviousSealOnRegistry` under the data root's pinned cosign and declared registry credentials. */
144
+ export async function previousSealFromRegistry({ repository, signerIdentity, cosignBinary, env, pinned = {}, log }) {
145
+ const signer = previousSealSignerOf(signerIdentity);
146
+ return withPinnedCosign({ cosignBinary, env, ...pinned }, ({ runCosign, cosignEnvFor, clientFor, trustedRootPath }) =>
147
+ findPreviousSealOnRegistry({ repository, signer, clientFor, runCosign, cosignEnvFor, trustedRootPath, ...(log === undefined ? {} : { log }) }),
148
+ );
149
+ }
@@ -73,7 +73,7 @@ import {
73
73
  } from './lib.mjs';
74
74
  import { assertReleaseContent } from './release-channel-guard.mjs';
75
75
  import { composeEdition } from './resolve-distribution.mjs';
76
- import { readStrictJsonFile } from './strict-json.mjs';
76
+ import { parseStrictJson, readStrictJsonFile } from './strict-json.mjs';
77
77
  import { loadThirdPartySourcePins } from './third-party-source-pins.mjs';
78
78
  import {
79
79
  assertOciBundleSourceOrigin,
@@ -2089,22 +2089,35 @@ export async function resolveImageRows(id, { inputs, lock, onSubjectHashed, reco
2089
2089
  }
2090
2090
 
2091
2091
  export const PREVIOUS_SEAL_USAGE =
2092
- 'exactly one of `--previous-lock <images.lock.json> --previous-lock-sha256 <hex>` (the caller takes the digest from the images.lock ' +
2093
- 'layer of the latest signed, published edition of this distribution, under the per-edition concurrency group) or `--first-seal`';
2092
+ 'exactly one of `--previous-seal-signer <certificate identity>` (the CLI reads the images.lock layer of the latest edition ' +
2093
+ 'signed by that identity in this edition\'s registry repository; run under the per-edition concurrency group), ' +
2094
+ '`--previous-lock <images.lock.json> --previous-lock-sha256 <hex>` (the same layer, taken by the caller) or `--first-seal` ' +
2095
+ '(only for a distribution that has never had an edition)';
2094
2096
 
2095
2097
  /**
2096
- * The previous seal an images-lock write carries composed artifacts forward from, as an EXPLICIT input: the previous lock
2097
- * with the sha256 of its bytes (taken by the caller from the signed, published edition's images.lock layer), or
2098
- * `--first-seal` (null: generation 1). Neither, both kinds, or half of the pair is refused; the bytes must hash to the given
2099
- * digest, and the lock is parsed with the kernel's `parseImageLock` and must be this edition's own (`distributionId`).
2098
+ * The previous seal an images-lock write carries composed artifacts forward from, as an EXPLICIT input of exactly one kind:
2099
+ * - `registry` (`--previous-seal-signer`): `{ find }`, where `find()` returns the latest signed edition's images.lock bytes
2100
+ * and their sha256 (previous-seal-registry.mjs);
2101
+ * - the previous lock with the sha256 of its bytes (taken by the caller from the signed, published edition's images.lock
2102
+ * layer);
2103
+ * - `--first-seal` (null: generation 1).
2104
+ * None, more than one kind, or half of the pair is refused; the bytes must hash to the given digest, and the lock is parsed
2105
+ * with the kernel's `parseImageLock` and must be this edition's own (`distributionId`).
2100
2106
  */
2101
- export async function resolvePreviousSeal({ distributionId, previousLockPath, previousLockSha256, firstSeal = false }) {
2107
+ export async function resolvePreviousSeal({ distributionId, previousLockPath, previousLockSha256, firstSeal = false, registry = undefined }) {
2102
2108
  const given = [previousLockPath, previousLockSha256].filter((value) => value !== undefined);
2103
- if (firstSeal) {
2104
- if (given.length > 0) {
2105
- throw new Error(`--first-seal and a previous lock (--previous-lock/--previous-lock-sha256) are both given; give ${PREVIOUS_SEAL_USAGE}`);
2109
+ const kinds = [registry !== undefined, given.length > 0, firstSeal].filter(Boolean).length;
2110
+ if (kinds > 1) {
2111
+ throw new Error(`more than one previous seal is given (--previous-seal-signer, --previous-lock/--previous-lock-sha256, --first-seal); give ${PREVIOUS_SEAL_USAGE}`);
2112
+ }
2113
+ if (firstSeal) {return null;}
2114
+ if (registry !== undefined) {
2115
+ const { bytes, imagesLockSha256, edition } = await registry.find();
2116
+ const actual = createHash('sha256').update(bytes).digest('hex');
2117
+ if (actual !== imagesLockSha256) {
2118
+ throw new Error(`the images lock of ${edition} has sha256 ${actual}, not its layer digest ${imagesLockSha256}`);
2106
2119
  }
2107
- return null;
2120
+ return parsePreviousLock(distributionId, parseStrictJson(bytes.toString('utf8'), `${edition} images.lock.json`), `the images lock of ${edition}`);
2108
2121
  }
2109
2122
  if (given.length !== 2) {
2110
2123
  throw new Error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/xema",
3
- "version": "0.56.0",
3
+ "version": "0.57.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/xema-dev/xema-cli.git",
@@ -100,6 +100,7 @@
100
100
  "typecheck": "tsc --noEmit"
101
101
  },
102
102
  "xemaRelease": {
103
- "sourceDigest": "sha256:80acaaf576705b4a71ecd5e67794fea62c6fc834652a2830bbd03a2d855d2409"
103
+ "sourceDigest": "sha256:23c811648b74b4daf999382a1d0f6aa4cad8bfaad8b3f150e51326d709a4982b",
104
+ "sha": "b7d08af5d37faac60b04770fd2b0701811b96459"
104
105
  }
105
106
  }