aube-settings 1.22.0

Settings schema and loader for Aube
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
# Aube Settings Registry
#
# Source of truth for every setting aube supports. Mirrors
# https://pnpm.io/next/settings.
#
# Tooling consumes this file to generate:
#   - docs/settings/*.md (pnpm-style reference pages)
#   - Rust metadata/accessors in `aube-settings`
#
# The `# ===== Category =====` separators below are intentionally
# structured: the docs generator uses them for page grouping while
# keeping each setting table focused on the setting itself.
#
# Schema for each entry:
#   description   = one-line summary shown in list views
#   type          = TOML-ish type spec ("bool", "string", "int",
#                   "path", "url", "list<string>", "object", enum unions)
#   default       = default value rendered as a string
#   docs          = longer markdown explanation
#   sources.cli   = CLI flags that set this setting (list, may be empty)
#   sources.env   = env vars that set this setting (list, may be empty;
#                   later aliases have higher priority; npm-compat aliases
#                   come before AUBE_* so tool-specific env can override)
#   sources.npmrc = .npmrc keys (list, may be empty)
#   sources.workspaceYaml = pnpm-workspace.yaml keys (list, may be empty)
#   examples      = optional list of concrete invocations
#   npmShared     = bool. Marks a key that npm (and yarn) also read
#                   from `.npmrc`. Set on settings where aube's
#                   `config set` must keep the value in `.npmrc` for
#                   cross-tool visibility (auth, proxy, registry,
#                   TLS, npm-standard scalars). Aube-only or
#                   pnpm-only settings leave this `false` and route
#                   to aube's own config.
#
# Key names match pnpm's canonical camelCase spelling so a future generator
# can link directly to anchors on pnpm.io/next/settings. Settings with
# dots in pnpm's name (e.g. `peerDependencyRules.ignoreMissing`) use
# quoted-key form: ["peerDependencyRules.ignoreMissing"].

# ========================================= Dependency Resolution =========================================

[overrides]
description = "Instruct aube to override any dependency in the dependency graph, including peer dependencies."
type = "object"
default = "undefined"
docs = """
A root-level map of package specs to the versions aube should force them
to, regardless of what any package's `dependencies` field requests. The
`$` prefix references a direct dep's declared version; a `-` value removes
the dependency from the graph entirely.
"""
sources.cli = []
sources.env = []
sources.npmrc = ["overrides"]
sources.workspaceYaml = ["overrides"]
examples = []

[packageExtensions]
description = "Extend existing package definitions with additional information."
type = "object"
default = "undefined"
docs = """
Patches a package's `dependencies`, `peerDependencies`, etc. at resolve
time. Used to work around upstream packages that forget to declare their
real peer requirements.
"""
sources.cli = []
sources.env = []
sources.npmrc = ["packageExtensions"]
sources.workspaceYaml = ["packageExtensions"]
examples = []

[allowedDeprecatedVersions]
description = "Mute deprecation warnings for specific package versions."
type = "object"
default = "undefined"
docs = """
Maps a package name to a semver range for which the deprecation warning
should be suppressed. Useful when a deprecated version is still pinned
deep in the dep graph and there's no upgrade path yet.
"""
sources.cli = []
sources.env = []
sources.npmrc = ["allowedDeprecatedVersions"]
sources.workspaceYaml = ["allowedDeprecatedVersions"]
examples = []

[deprecationWarnings]
description = "Scope of deprecation warnings shown during install."
type = '"none" | "direct" | "all" | "summary"'
default = "\"direct\""
docs = """
Controls how deprecation messages surface at the end of install:
- `none`: silent.
- `direct`: print full warnings for direct dependencies only, plus a
  one-line transitive count (default).
- `all`: print full warnings for every deprecated package (pnpm/npm
  parity).
- `summary`: print a single count line covering direct + transitive.

Run `aube deprecations` to see the full list any time after install.
"""
sources.cli = ["--deprecation-warnings"]
sources.env = ["npm_config_deprecation_warnings", "NPM_CONFIG_DEPRECATION_WARNINGS", "AUBE_DEPRECATION_WARNINGS"]
sources.npmrc = ["deprecationWarnings"]
sources.workspaceYaml = ["deprecationWarnings"]
examples = [
    "AUBE_DEPRECATION_WARNINGS=all aube install",
    "aube install --deprecation-warnings=summary",
]

["updateConfig.ignoreDependencies"]
description = "List of packages to ignore during update checks."
type = "list<string>"
default = "undefined"
docs = """
Packages in this list are never bumped by `aube update`, even when a
newer version matching their range exists.
"""
sources.cli = []
sources.env = ["npm_config_update_config_ignore_dependencies", "NPM_CONFIG_UPDATE_CONFIG_IGNORE_DEPENDENCIES", "AUBE_UPDATE_CONFIG_IGNORE_DEPENDENCIES"]
sources.npmrc = ["updateConfig.ignoreDependencies"]
sources.workspaceYaml = ["updateConfig.ignoreDependencies"]
examples = []

[supportedArchitectures]
description = "Specify architectures for optional dependency installation."
type = "object"
default = "undefined"
docs = """
Override the current platform/arch/libc triple used to filter optional
dependencies. Useful when generating a lockfile for a target platform
different from the host.
"""
sources.cli = []
sources.env = []
sources.npmrc = ["supportedArchitectures"]
sources.workspaceYaml = ["supportedArchitectures"]
examples = []
# Read directly off the typed `WorkspaceConfig.supported_architectures`
# field and unioned with `{pnpm,aube}.supportedArchitectures` from
# `package.json` via `aube_manifest::effective_supported_architectures`.
# The typed accessor would only surface the `.npmrc` spelling.
typedAccessorUnused = true

[ignoredOptionalDependencies]
description = "Skip optional dependencies by name."
type = "list<string>"
default = "undefined"
docs = """
Named entries are skipped even if their platform/arch matches. Distinct
from `--no-optional`, which drops *all* optional deps at install time.
"""
sources.cli = []
sources.env = ["npm_config_ignored_optional_dependencies", "NPM_CONFIG_IGNORED_OPTIONAL_DEPENDENCIES", "AUBE_IGNORED_OPTIONAL_DEPENDENCIES"]
sources.npmrc = ["ignoredOptionalDependencies"]
sources.workspaceYaml = ["ignoredOptionalDependencies"]
examples = []
# Read as a list out of the `package.json` pnpm/aube block and the
# typed `WorkspaceConfig.ignored_optional_dependencies` field;
# `aube_manifest::effective_ignored_optional_dependencies` unions
# them. The typed accessor would only surface the `.npmrc` spelling.
typedAccessorUnused = true

[pnpmfilePath]
description = "Location of the pnpmfile hook file."
type = "string"
default = "undefined"
docs = """
Override for the pnpmfile discovery path. Defaults to
`<project>/.pnpmfile.mjs` when present, otherwise `<project>/.pnpmfile.cjs`.
Relative paths resolve against the project root; absolute paths are used
as-is. A path that points at a missing file is a hard miss — aube emits a
warning and runs with no pnpmfile rather than silently falling back to the
default.

`--pnpmfile <path>` mirrors pnpm's CLI flag and takes precedence over the
workspace yaml entry.
"""
sources.cli = ["--pnpmfile"]
sources.env = ["AUBE_PNPMFILE_PATH"]
sources.npmrc = []
sources.workspaceYaml = ["pnpmfilePath"]
examples = [
    "aube install --pnpmfile config/hooks.cjs",
]
# CLI flag is read directly off `InstallOptions.pnpmfile` in
# `pnpmfile::detect`; workspace yaml is read off
# `WorkspaceConfig.pnpmfile_path` in the same call. Inherently
# project-scoped, so no npmrc source.
typedAccessorUnused = true

[globalPnpmfile]
description = "Path to a second pnpmfile that runs before the project's pnpmfile."
type = "string"
default = "undefined"
docs = """
Mirrors pnpm's `--global-pnpmfile <path>`. The global hook runs first and
the local pnpmfile (if any) runs second, so per-project hooks can override
org-wide rules. Relative paths resolve against the project root; absolute
paths are used as-is. A typo (target missing) is a hard miss with a warning
rather than a silent skip.
"""
sources.cli = ["--global-pnpmfile"]
sources.env = ["AUBE_GLOBAL_PNPMFILE"]
sources.npmrc = []
sources.workspaceYaml = []
examples = [
    "aube install --global-pnpmfile ~/.config/aube/hooks.cjs",
]
# Read directly off `InstallOptions.global_pnpmfile` in
# `pnpmfile::detect_global`.
typedAccessorUnused = true

[minimumReleaseAge]
description = "Delay installation of newly published versions (minutes)."
type = "int"
default = "1440"
docs = """
Supply-chain attack mitigation: packages published within the last N
minutes are skipped by the resolver. By default the resolver falls back
to the next-oldest version that satisfies the range; set
`minimumReleaseAgeStrict=true` to fail the install instead. Defaults to
24 hours, matching pnpm v11. Set to `0` to disable.
"""
sources.cli = []
sources.env = ["npm_config_minimum_release_age", "NPM_CONFIG_MINIMUM_RELEASE_AGE", "AUBE_MINIMUM_RELEASE_AGE"]
sources.npmrc = ["minimumReleaseAge"]
sources.workspaceYaml = ["minimumReleaseAge"]
# pnpm v11 documents this as a workspace-level setting, so
# pnpm-workspace.yaml wins over a stale .npmrc entry.
precedence = ["workspaceYaml", "npmrc"]
examples = []

[minimumReleaseAgeExclude]
description = "Packages exempt from the minimumReleaseAge requirement."
type = "list<string>"
default = "undefined"
docs = """
Use for trusted internal packages that need to be rolled out immediately
without waiting for the age gate. `pnpm audit --fix` (when implemented)
will append patched versions to this list automatically.
"""
sources.cli = []
sources.env = ["npm_config_minimum_release_age_exclude", "NPM_CONFIG_MINIMUM_RELEASE_AGE_EXCLUDE", "AUBE_MINIMUM_RELEASE_AGE_EXCLUDE"]
sources.npmrc = ["minimumReleaseAgeExclude"]
sources.workspaceYaml = ["minimumReleaseAgeExclude"]
precedence = ["workspaceYaml", "npmrc"]
examples = []

[minimumReleaseAgeStrict]
description = "Fail the install when no version satisfies the minimumReleaseAge cutoff."
type = "bool"
default = "false"
docs = """
By default the resolver falls back to the lowest satisfying version when
every candidate is younger than `minimumReleaseAge`. With this set, the
resolver fails the install instead.
"""
sources.cli = []
sources.env = ["npm_config_minimum_release_age_strict", "NPM_CONFIG_MINIMUM_RELEASE_AGE_STRICT", "AUBE_MINIMUM_RELEASE_AGE_STRICT"]
sources.npmrc = ["minimumReleaseAgeStrict"]
sources.workspaceYaml = ["minimumReleaseAgeStrict"]
precedence = ["workspaceYaml", "npmrc"]
examples = []

[securityScanner]
description = "Bun-compatible security scanner module."
type = "string"
default = "\"\""
docs = """
Path (or bare npm package name) of a
[Bun-compatible security scanner](https://bun.sh/docs/pm/security-scanner-api)
module. Aube loads it through a `node` bridge that adapts Bun's
in-process plugin contract to a subprocess.

```yaml
# aube-workspace.yaml
securityScanner: \"@acme/bun-security-scanner\"
```

Empty string (the default) disables the integration. Requires
Node 22.6+. Fails closed on any scanner failure.

Full reference, including the Bun-runtime API surface aube shims,
authoring instructions, and the post-resolve firing model:
[/package-manager/security-scanner](/package-manager/security-scanner).
"""
sources.cli = []
sources.env = ["npm_config_security_scanner", "NPM_CONFIG_SECURITY_SCANNER", "AUBE_SECURITY_SCANNER"]
sources.npmrc = ["securityScanner"]
sources.workspaceYaml = ["securityScanner"]
precedence = ["workspaceYaml", "npmrc"]
examples = []

[advisoryCheck]
description = "OSV `MAL-*` advisory check during `aube add` and other fresh-resolution installs."
type = '"on" | "required" | "off"'
default = "\"on\""
docs = """
Live-API OSV `MAL-*` advisory check. aube batch-queries
[OSV](https://osv.dev) at three points, all against `api.osv.dev`:

1. `aube add` CLI names, before they land in `package.json`.
2. The full post-resolve transitive graph during any
   *fresh-resolution* install — `aube add`, `aube update`, an
   install with no lockfile, or an install where the resolver
   picked a `(name, version)` the lockfile didn't already pin.
3. Every install (including frozen reinstalls), if
   `advisoryCheckEveryInstall` is `true`.

A hit at any step fails the install with `ERR_AUBE_MALICIOUS_PACKAGE`
and a link to the advisory.

Plain reinstalls where the lockfile was authoritative don't hit the
network here — they fall through to the local mirror (see
`advisoryCheckOnInstall`) when that's enabled, or no OSV check at all
when it isn't.

- `on` (default): fail closed on a malicious-package hit; fail open
  (continue with a `WARN_AUBE_ADVISORY_CHECK_FAILED`) when the API can't
  be reached, so offline workflows aren't blocked.
- `required`: same fail-closed behavior on hits, plus fail closed on
  fetch errors. Use in hardened CI. Included in the `paranoid` bundle.
- `off`: skip the check entirely.
"""
sources.cli = []
sources.env = ["npm_config_advisory_check", "NPM_CONFIG_ADVISORY_CHECK", "AUBE_ADVISORY_CHECK"]
sources.npmrc = ["advisoryCheck"]
sources.workspaceYaml = ["advisoryCheck"]
precedence = ["workspaceYaml", "npmrc"]
examples = []

[advisoryCheckOnInstall]
description = "Local-mirror OSV `MAL-*` advisory check for plain reinstalls."
type = '"on" | "required" | "off"'
default = "\"off\""
docs = """
Fallback OSV `MAL-*` check for installs the live-API gate
(`advisoryCheck`) didn't fire for — i.e. plain reinstalls where the
lockfile was authoritative (no `aube add` / `aube update`, no
`advisoryCheckEveryInstall`, no lockfile drift). Backed by a local
mirror of OSV's npm advisory dump so there's no per-install
`api.osv.dev` round-trip.

A hit fails the install with `ERR_AUBE_MALICIOUS_PACKAGE` — same
exit as `advisoryCheck`. Fresh-resolution installs (`aube add`,
`aube update`, missing-lockfile, new picked version) always go
through the live API regardless of this setting, so the freshest
signal lands at the moment a human is changing what's installed.

The mirror lives at `$XDG_CACHE_HOME/aube/osv/npm/` and lazily
refreshes from
`https://osv-vulnerabilities.storage.googleapis.com/npm/all.zip`
(roughly tens of MB) with an `If-None-Match` revalidation. A miss
between refreshes won't catch an advisory published in the last
~day; for live signals on every install, use
`advisoryCheckEveryInstall = true` instead.

- `off` (default): plain reinstalls skip OSV entirely. Fresh-resolution
  installs still hit the live API via `advisoryCheck`.
- `on`: plain reinstalls check the resolved graph against the mirror.
  Fail open (continue with `WARN_AUBE_OSV_MIRROR_REFRESH_FAILED`) when
  the mirror can't be refreshed.
- `required`: as `on`, plus fail closed on mirror refresh failures
  with `ERR_AUBE_ADVISORY_CHECK_FAILED`. Use in hardened CI where a
  stale or unreachable mirror should block the install.
"""
sources.cli = []
sources.env = ["npm_config_advisory_check_on_install", "NPM_CONFIG_ADVISORY_CHECK_ON_INSTALL", "AUBE_ADVISORY_CHECK_ON_INSTALL"]
sources.npmrc = ["advisoryCheckOnInstall"]
sources.workspaceYaml = ["advisoryCheckOnInstall"]
precedence = ["workspaceYaml", "npmrc"]
examples = []

[advisoryBloomCheck]
description = "Bloom-filter prefilter for OSV `MAL-*` advisories on lockfile-driven installs."
type = '"on" | "required" | "off"'
default = "\"off\""
docs = """
Fast bloom-filter prefilter that aube downloads (~380 KB) from
`endevco/osv-bloom`. The upstream filter contains one entry per
`(npm package name, semver major bucket)` pair drawn from OSV's
malicious-package archive and is regenerated every 10 minutes.

When enabled, aube probes the resolved transitive graph against the
filter and escalates *only* the bloom hits to the live OSV API for
exact `(name, version)` confirmation. Bloom false-positive rate is
~0.1%, so a typical lockfile of ~1000 packages either triggers no
escalation at all or one extra live-API round trip per install — much
cheaper than `advisoryCheckEveryInstall` which round-trips the full
graph.

Designed to coexist with `advisoryCheck` and `advisoryCheckOnInstall`
rather than replace them. The bloom check fires on every install path
the live-API gate didn't already cover for that install: if
`advisoryCheckEveryInstall = true` or the install is a fresh-resolution
path that already hits the live API, the bloom is skipped (the live API
strictly dominates a bloom probe).

- `off` (default): bloom prefilter disabled.
- `on`: probe the lockfile against the bloom, escalate hits to the
  live API, fail closed on a confirmed `MAL-*` hit with
  `ERR_AUBE_MALICIOUS_PACKAGE`. Fail open (continue with
  `WARN_AUBE_OSV_BLOOM_REFRESH_FAILED`) when the bloom can't be
  refreshed.
- `required`: as `on`, plus fail closed on bloom refresh failures
  with `ERR_AUBE_ADVISORY_CHECK_FAILED`. Use in hardened CI where a
  stale or unreachable bloom should block the install.
"""
sources.cli = []
sources.env = ["npm_config_advisory_bloom_check", "NPM_CONFIG_ADVISORY_BLOOM_CHECK", "AUBE_ADVISORY_BLOOM_CHECK"]
sources.npmrc = ["advisoryBloomCheck"]
sources.workspaceYaml = ["advisoryBloomCheck"]
precedence = ["workspaceYaml", "npmrc"]
examples = []

[advisoryCheckEveryInstall]
description = "Force the live-API OSV `MAL-*` check on every install (including frozen reinstalls)."
type = "bool"
default = "false"
docs = """
By default, the live-API OSV check (`advisoryCheck`) fires on
*fresh-resolution* installs only — `aube add`, `aube update`,
missing-lockfile installs, and installs where the resolver picks a
version the lockfile didn't pin. Plain reinstalls fall through to the
local mirror (`advisoryCheckOnInstall`) when that's enabled.

Setting `advisoryCheckEveryInstall = true` forces the live API on
every install entry point, including strict frozen reinstalls and
`aube ci`. Every install round-trips through `api.osv.dev` for the
freshest signal; mirror lookups are skipped because the live API
strictly dominates.

Useful in hardened CI where every job must observe the latest
advisories regardless of whether the lockfile changed. The trade-off
is per-install latency — for a large transitive graph, batch queries
chunk at 500 names per request, so an `aube install` against a graph
of 2000 packages incurs four sequential OSV requests.

- `false` (default): live-API check on fresh-resolution installs only.
- `true`: live-API check on every install. Honors `advisoryCheck`'s
  fail-open / fail-closed policy on fetch errors.
"""
sources.cli = []
sources.env = ["npm_config_advisory_check_every_install", "NPM_CONFIG_ADVISORY_CHECK_EVERY_INSTALL", "AUBE_ADVISORY_CHECK_EVERY_INSTALL"]
sources.npmrc = ["advisoryCheckEveryInstall"]
sources.workspaceYaml = ["advisoryCheckEveryInstall"]
precedence = ["workspaceYaml", "npmrc"]
examples = []

[lowDownloadThreshold]
description = "Weekly-download floor for `aube add` (typosquat prompt)."
type = "int"
default = "1000"
docs = """
`aube add` looks up each candidate's weekly download count via
`api.npmjs.org/downloads/point/last-week/<pkg>` and prompts for
confirmation when the count falls below this threshold — the floor
catches typosquats and impersonations, which have near-zero downloads
on day one regardless of how cleverly they're named.

Interactive sessions get a `[y/N]` prompt showing the weekly
download count. Non-interactive contexts fail with
`ERR_AUBE_LOW_DOWNLOAD_PACKAGE` unless `--allow-low-downloads` is
passed.

Packages that route through a non-`registry.npmjs.org` registry are
skipped automatically: a scoped override like
`@myorg:registry=https://npm.internal.example/` or a swapped-out
default registry both mean npmjs has no signal on the package, so
neither the downloads gate nor the OSV `MAL-*` check fires.
Workspace deps and git/local specs are also skipped. To exempt
specific names that *do* resolve through npmjs (e.g. you publish a
low-download but trusted package internally), see
`allowedUnpopularPackages`.

Set to `0` to disable.
"""
sources.cli = []
sources.env = ["npm_config_low_download_threshold", "NPM_CONFIG_LOW_DOWNLOAD_THRESHOLD", "AUBE_LOW_DOWNLOAD_THRESHOLD"]
sources.npmrc = ["lowDownloadThreshold"]
sources.workspaceYaml = ["lowDownloadThreshold"]
precedence = ["workspaceYaml", "npmrc"]
examples = []

[allowedUnpopularPackages]
description = "Glob patterns exempted from the `lowDownloadThreshold` gate."
type = "list<string>"
default = "undefined"
docs = """
Each pattern is matched against the registry name (`@scope/foo` or
`bar`) of every candidate the `lowDownloadThreshold` gate would
otherwise probe. Matches skip the weekly-downloads lookup entirely,
so internal/low-traffic packages don't trip the prompt in CI or the
`y/N` prompt locally.

Patterns are full-name globs (the [`glob`](https://docs.rs/glob)
crate's syntax — `*`, `?`, `[…]`). Match-everything (`*`) is allowed
but defeats the gate; prefer to set `lowDownloadThreshold = 0` if
that is what you want.

The OSV `MAL-*` advisory check (`advisoryCheck`) is **not** affected
— a hit there is confirmed-malicious and not the kind of judgement
call this list is meant to suppress.

```yaml
# aube-workspace.yaml
allowedUnpopularPackages:
  - "@mycompany/*"
  - "internal-*"
```
"""
sources.cli = []
sources.env = ["npm_config_allowed_unpopular_packages", "NPM_CONFIG_ALLOWED_UNPOPULAR_PACKAGES", "AUBE_ALLOWED_UNPOPULAR_PACKAGES"]
sources.npmrc = ["allowedUnpopularPackages"]
sources.workspaceYaml = ["allowedUnpopularPackages"]
precedence = ["workspaceYaml", "npmrc"]
examples = []

[paranoid]
description = "Turn on the strict-security setting bundle in one switch."
type = "bool"
default = "false"
docs = """
When true, aube forces every individual setting in the strict-security
bundle on, regardless of how each is configured individually:

- `trustPolicy = no-downgrade` (overrides explicit `off`)
- `jailBuilds = true`
- `minimumReleaseAgeStrict = true` (makes the age gate hard, not advisory)
- `strictStoreIntegrity = true` (fail on missing `dist.integrity`)
- `strictDepBuilds = true` (fail when deps have unreviewed build scripts)
- `advisoryCheck = required` (fail closed on OSV fetch errors instead of
  falling through with a warning)

Set to `false` (the default) to honor the underlying settings as-is.
"""
sources.cli = []
sources.env = ["npm_config_paranoid", "NPM_CONFIG_PARANOID", "AUBE_PARANOID"]
sources.npmrc = ["paranoid"]
sources.workspaceYaml = ["paranoid"]
examples = []

[trustPolicy]
description = "Fail install when a package's trust evidence weakens between releases."
type = '"no-downgrade" | "off"'
default = "\"no-downgrade\""
docs = """
When `no-downgrade` (the default), aube rejects a version that carries weaker
trust evidence than any earlier-published version of the same package.
Recognized evidence: npm staged-publish approval metadata (`approver`)
outranks structured npm trusted-publisher metadata
(`_npmUser.trustedPublisher.id`), which outranks structured SLSA provenance
metadata (`dist.attestations.provenance.predicateType`). Set to `off` to
disable, or use `trustPolicyExclude` to whitelist specific packages or
versions. This policy validates registry metadata shape; it does not
cryptographically verify the attached attestation bundle.
"""
sources.cli = []
sources.env = ["npm_config_trust_policy", "NPM_CONFIG_TRUST_POLICY", "AUBE_TRUST_POLICY"]
sources.npmrc = ["trustPolicy"]
sources.workspaceYaml = ["trustPolicy"]
examples = []

[trustPolicyExclude]
description = "Packages exempt from `trustPolicy` checks."
type = "list<string>"
default = "[]"
docs = """
Patterns: `name`, `name@1.0.0`, `name@1.0.0 || 1.0.1` (exact versions only —
no `^`/`~`/`>=`), `is-*` (name glob, no version), `@scope/name@1.0.0`.
Empty list disables user-provided exclusions; aube still applies its built-in
exclusions for known registry provenance metadata churn.
"""
sources.cli = []
sources.env = ["npm_config_trust_policy_exclude", "NPM_CONFIG_TRUST_POLICY_EXCLUDE", "AUBE_TRUST_POLICY_EXCLUDE"]
sources.npmrc = ["trustPolicyExclude"]
sources.workspaceYaml = ["trustPolicyExclude"]
examples = []

[trustPolicyIgnoreAfter]
description = "Skip the trust check for versions older than this many minutes."
type = "int"
default = "undefined"
docs = """
Versions whose publish time is older than the cutoff are exempted from
`trustPolicy`. Leave unset to apply the check to every version.
"""
sources.cli = []
sources.env = ["npm_config_trust_policy_ignore_after", "NPM_CONFIG_TRUST_POLICY_IGNORE_AFTER", "AUBE_TRUST_POLICY_IGNORE_AFTER"]
sources.npmrc = ["trustPolicyIgnoreAfter"]
sources.workspaceYaml = ["trustPolicyIgnoreAfter"]
examples = []

[blockExoticSubdeps]
description = "Restrict transitive dependencies to trusted sources (registries, not git/tarball URLs)."
type = "bool"
default = "true"
docs = """
When true, transitive deps referenced via `git+`, `file:`, or direct
tarball URLs are rejected. Helps prevent supply-chain attacks via
unexpected download sources.
"""
sources.cli = []
sources.env = ["npm_config_block_exotic_subdeps", "NPM_CONFIG_BLOCK_EXOTIC_SUBDEPS", "AUBE_BLOCK_EXOTIC_SUBDEPS"]
sources.npmrc = ["blockExoticSubdeps"]
sources.workspaceYaml = ["blockExoticSubdeps"]
examples = []

[registries]
description = "Registry URLs, including scoped registry overrides."
type = "object"
default = "{ default = \"https://registry.npmjs.org/\" }"
docs = """
Maps `default` and `@scope` keys to registry URLs. aube reads these from
`.npmrc` via `aube_registry::config::NpmConfig` (see
`crates/aube-registry/src/config.rs`). Bearer tokens and basic auth per
registry are parsed from `.npmrc` and URL-scoped `npm_config_//...` /
`pnpm_config_//...` environment variables. `tokenHelper` is only accepted
from trusted user-level config, not from URL-scoped environment variables.
"""
sources.cli = []
sources.env = []
sources.npmrc = ["registry", "@scope:registry", "//host/:_authToken", "//host/:_auth"]
examples = [
  "registry=https://registry.npmmirror.com/",
  "@mycorp:registry=https://npm.mycorp.internal/",
  "pnpm_config_//registry.example.com/:_authToken=$NPM_TOKEN aube install",
]
npmShared = true

# ========================================= Dependency Hoisting =========================================

[hoist]
description = "Hoist all dependencies to the hidden modules directory."
type = "bool"
default = "true"
docs = """
Controls whether aube populates `node_modules/.aube/node_modules/` —
the *hidden* hoist tree that lives inside the private virtual store.
When enabled (the default), every non-local package whose name
matches `hoistPattern` gets a symlink into that directory so Node's
parent-directory walk can satisfy undeclared deps from inside the
virtual store (e.g. `react-dom` reaching `scheduler` without
declaring it).

The hidden tree is distinct from `publicHoistPattern` /
`shamefullyHoist`, which add symlinks at the visible root
`node_modules/<name>`. Hidden-hoist links are only reachable during
Node's resolution of a dependency that itself lives under
`.aube/<dep_path>/`. Setting `hoist=false` skips the pass entirely
and sweeps any previously-populated directory so stale entries
don't keep resolving.
"""
sources.cli = []
sources.env = ["npm_config_hoist", "NPM_CONFIG_HOIST", "AUBE_HOIST"]
sources.npmrc = ["hoist"]
sources.workspaceYaml = ["hoist"]
examples = [
  "echo 'hoist=false' >> .npmrc && aube install",
]

[hoistWorkspacePackages]
description = "Symlink workspace packages into node_modules."
type = "bool"
default = "true"
docs = """
Controls whether workspace packages get their own symlinks in each
importer's `node_modules/`. When true (the default), every importer
gets a `node_modules/<ws-pkg>` symlink to every workspace package it
depends on, matching pnpm. When false, those symlinks are omitted —
cross-importer `workspace:` dependencies still resolve through the
lockfile, but a top-level `require('<ws-pkg>')` from a package that
doesn't declare the workspace dep stops working.
"""
sources.cli = []
sources.env = ["npm_config_hoist_workspace_packages", "NPM_CONFIG_HOIST_WORKSPACE_PACKAGES", "AUBE_HOIST_WORKSPACE_PACKAGES"]
sources.npmrc = ["hoist-workspace-packages", "hoistWorkspacePackages"]
sources.workspaceYaml = ["hoistWorkspacePackages"]
examples = []

[hoistingLimits]
description = "Limit how far dependencies are hoisted in nodeLinker=hoisted installs."
type = '"none" | "workspaces" | "dependencies"'
default = "\"none\""
docs = """
Controls how far packages can be promoted when `nodeLinker=hoisted` is
active, mirroring Yarn's `nmHoistingLimits` and pnpm's
`hoistingLimits` setting:

- `none`: hoist as far as possible (default).
- `workspaces`: hoist only as far as each workspace package. Aube plans
  hoisted installs per physical importer, so this currently matches
  `none`.
- `dependencies`: hoist only up to each workspace package's direct
  dependencies. Transitive packages stay under the direct dependency
  that introduced them instead of being promoted to the importer root.

Ignored by the default isolated linker.
"""
sources.cli = []
sources.env = ["npm_config_hoisting_limits", "NPM_CONFIG_HOISTING_LIMITS", "AUBE_HOISTING_LIMITS"]
sources.npmrc = ["hoistingLimits", "hoisting-limits"]
sources.workspaceYaml = ["hoistingLimits"]
examples = [
  "echo 'node-linker=hoisted' >> .npmrc",
  "echo 'hoisting-limits=dependencies' >> .npmrc",
]

[hoistPattern]
description = "Packages to hoist to the hidden modules directory."
type = "list<string>"
default = "[\"*\"]"
docs = """
Glob list matched against package names. Any non-local package
whose name matches at least one positive pattern (and no
`!`-prefixed negation) gets a symlink at
`node_modules/.aube/node_modules/<name>`. The default `*` matches
everything, which mirrors pnpm's default of hoisting every
transitive dep into the hidden tree. Only consulted when
`hoist=true`.

Matching is case-insensitive; first-writer-wins on name clashes
across versions, using BTree iteration order for determinism. Set
to `[]` or a list of only `!` negations to hoist nothing while
leaving `hoist=true` (equivalent to setting `hoist=false`).
"""
sources.cli = []
sources.env = ["npm_config_hoist_pattern", "NPM_CONFIG_HOIST_PATTERN", "AUBE_HOIST_PATTERN"]
sources.npmrc = ["hoist-pattern", "hoistPattern"]
sources.workspaceYaml = ["hoistPattern"]
examples = []

[publicHoistPattern]
description = "Packages to hoist directly to the root node_modules."
type = "list<string>"
default = "[]"
docs = """
Glob list matched against package names. Any non-local package in the
resolved graph whose name matches at least one positive pattern (and
no `!`-prefixed negation) gets a top-level `node_modules/<name>`
symlink in addition to the usual direct-dep entries, so frameworks
like Next.js, Storybook, and Jest can resolve transitive deps from the
project root without adding them to `package.json`.

Matching is case-insensitive; direct deps always win on name clashes,
and the pattern pass runs before `shamefullyHoist`. Use sparingly --
anything hoisted becomes a phantom dep at the root.
"""
sources.cli = ["public-hoist-pattern"]
sources.env = ["npm_config_public_hoist_pattern", "NPM_CONFIG_PUBLIC_HOIST_PATTERN", "AUBE_PUBLIC_HOIST_PATTERN"]
sources.npmrc = ["public-hoist-pattern", "publicHoistPattern"]
sources.workspaceYaml = ["publicHoistPattern"]
examples = []

[shamefullyHoist]
description = "Hoist all dependencies to the root node_modules (shortcut for publicHoistPattern=[\"*\"])."
type = "bool"
default = "false"
docs = """
Emulates npm's flat `node_modules` layout. Enables phantom dep bugs by
design — only use as a last-resort compatibility knob.
"""
sources.cli = ["shamefully-hoist"]
sources.env = ["npm_config_shamefully_hoist", "NPM_CONFIG_SHAMEFULLY_HOIST", "AUBE_SHAMEFULLY_HOIST"]
sources.npmrc = ["shamefully-hoist", "shamefullyHoist"]
sources.workspaceYaml = ["shamefullyHoist"]
examples = []

# ========================================= node_modules =========================================

[modulesDir]
description = "Directory to install dependencies into."
type = "path"
default = "\"node_modules\""
docs = """
The project-level directory that holds the top-level `<name>` entries
the user sees under the project root. Defaults to `"node_modules"`.
The linker, bin handler, scripts runner, and every command that
touches the project-level directory (`bin`, `root`, `prune`, `clean`,
`ci`, `link`, `unlink`, `run`, `exec`, `patch`, `licenses`, `inject`,
…) all honor this setting.

The inner virtual-store paths -- `<modulesDir>/.aube/<dep>/node_modules/`
-- keep the literal `node_modules` name regardless of this setting.
Node.js's own module resolver walks up from `<pkg>/src/file.js`
looking for a literal `node_modules/` directory, so a renamed outer
directory only works when Node can still find its deps: set
`NODE_PATH=<project>/<modulesDir>` (or use a custom loader) before
running node. The inner dir name is what the walk actually hits, so
it stays fixed.
"""
sources.cli = []
sources.env = ["npm_config_modules_dir", "NPM_CONFIG_MODULES_DIR", "AUBE_MODULES_DIR"]
sources.npmrc = ["modulesDir", "modules-dir"]
sources.workspaceYaml = ["modulesDir"]
examples = []

[nodeLinker]
description = "Strategy for linking Node packages into node_modules."
type = '"isolated" | "hoisted" | "pnp"'
default = "\"isolated\""
docs = """
aube defaults to `isolated`, a strict symlink layout under
`node_modules/.aube/`. `hoisted` is also supported for projects that need an
npm-style flatter `node_modules` tree with conflicting versions nested under
the requiring package. `pnp` is accepted as a known value but rejected with a
clear error because Yarn Plug'n'Play is not supported.
"""
sources.cli = ["node-linker"]
sources.env = ["npm_config_node_linker", "NPM_CONFIG_NODE_LINKER", "AUBE_NODE_LINKER"]
sources.npmrc = ["nodeLinker"]
sources.workspaceYaml = ["nodeLinker"]
examples = []

[symlink]
description = "Create symlinks in the virtual store directory."
type = "bool"
default = "true"
docs = """
Accepted for pnpm parity. aube's isolated layout is structurally
defined by the symlink graph under `node_modules/.aube/` — each
`.aube/<dep_path>/node_modules/` contains the real package alongside
sibling symlinks that Node's directory walk follows to reach declared
deps. Removing those symlinks in favor of hard copies would defeat
the isolation guarantee and blow up disk usage by every duplicated
transitive.

`symlink=true` (the default) is a silent no-op — it is what aube
already does. `symlink=false` is accepted so a `.npmrc` ported from a
hard-copy-only pnpm setup keeps loading, but aube emits a single
warning at install start and keeps building the symlink graph.
Materialized *files* inside the store are still imported via reflink
→ hardlink → copy (controlled by `packageImportMethod`), unaffected
by this setting.
"""
sources.cli = []
sources.env = ["npm_config_symlink", "NPM_CONFIG_SYMLINK", "AUBE_SYMLINK"]
sources.npmrc = ["symlink"]
examples = [
    "echo 'symlink=false' >> .npmrc",
]

[enableModulesDir]
description = "Write files to the modules directory."
type = "bool"
default = "true"
docs = """
When `false`, aube resolves the dependency graph and writes
`aube-lock.yaml` but skips every `node_modules/` side effect: no
virtual store is populated, no top-level symlinks are created, and the
per-project install-state file is not written. Functionally
equivalent to `--lockfile-only` as a persistent `.npmrc` /
`aube-workspace.yaml` setting, which is how pnpm exposes it.
"""
sources.cli = []
sources.env = ["npm_config_enable_modules_dir", "NPM_CONFIG_ENABLE_MODULES_DIR", "AUBE_ENABLE_MODULES_DIR"]
sources.npmrc = ["enableModulesDir"]
examples = []

[virtualStoreDir]
description = "Directory with links to the store."
type = "path"
default = "\"node_modules/.aube\""
docs = """
Relocates the per-project `.aube/<dep>/node_modules/` tree that the
isolated linker writes into. Relative paths resolve against the project
root (`~` expands to `$HOME`).

The generated accessor's declared default is the literal
`"node_modules/.aube"` — but callers should resolve through
`aube_cli::commands::resolve_virtual_store_dir`, which additionally
substitutes `<modulesDir>/.aube` when `modulesDir` itself has been
overridden. That's the "real" effective default and matches pnpm's
documented `<modulesDir>/.pnpm` behavior: a project that renames
`node_modules/` alone still gets a coherent layout without having
to set both.

The linker, engines check, fetch-phase "already linked" fast path,
orphan sweep, `apply_injected` (dependenciesMeta.injected), `aube
patch` (extract source), `aube rebuild` (dep lifecycle scripts),
`aube unlink` (classify internal symlinks), `aube prune` (orphan
cleanup), and `aube licenses` (virtual-store manifest read) all
consult the setting through that helper.
"""
sources.cli = []
sources.env = ["npm_config_virtual_store_dir", "NPM_CONFIG_VIRTUAL_STORE_DIR", "AUBE_VIRTUAL_STORE_DIR"]
sources.npmrc = ["virtualStoreDir", "virtual-store-dir"]
sources.workspaceYaml = ["virtualStoreDir"]
examples = []

[virtualStoreDirMaxLength]
description = "Max length for virtual store directory names."
type = "int"
default = "120 (Linux/macOS), 60 (Windows)"
docs = """
Caps the number of characters in a single `node_modules/.aube/<dep>`
directory name. `dep_path_to_filename` already truncates-and-hashes
dep_paths that would otherwise overflow the cap, so lowering this
value lets peer-heavy graphs (e.g. ESLint + TypeScript plugin
matrices) stay under filesystem `NAME_MAX` limits on unusual setups
(ecryptfs, some CI filesystems). The default is 120 on Linux/macOS and
60 on Windows; aube currently uses the POSIX default on every platform
(the Windows tightening lands with native Windows support).
"""
sources.cli = []
sources.env = ["npm_config_virtual_store_dir_max_length", "NPM_CONFIG_VIRTUAL_STORE_DIR_MAX_LENGTH", "AUBE_VIRTUAL_STORE_DIR_MAX_LENGTH"]
sources.npmrc = ["virtualStoreDirMaxLength"]
examples = []

[virtualStoreOnly]
description = "Populate the virtual store without creating top-level symlinks."
type = "bool"
default = "false"
docs = """
When `true`, aube still materializes every package into
`node_modules/.aube/<dep>/node_modules/<name>` (and, in global-store
mode, into the shared virtual store), but skips the final pass that
creates the top-level `node_modules/<name>` symlinks. Useful in CI
pipelines that warm the store for downstream jobs and in
`aube fetch`-style flows that want the dep graph on disk without
exposing it to Node's resolver. `shamefullyHoist` and
`publicHoistPattern` hoisting passes are also skipped, since both
target the same top-level directory.
"""
sources.cli = []
sources.env = ["npm_config_virtual_store_only", "NPM_CONFIG_VIRTUAL_STORE_ONLY", "AUBE_VIRTUAL_STORE_ONLY"]
sources.npmrc = ["virtualStoreOnly"]
examples = []

[packageImportMethod]
description = "Method for importing packages from the store into node_modules."
type = '"auto" | "hardlink" | "copy" | "clone" | "clone-or-copy"'
default = "\"auto\""
docs = """
Controls how aube materializes files from the global content-addressable
store into the virtual store.

- `auto` (default) probes the destination filesystem and picks
  `hardlink` with a `copy` fallback on cross-filesystem boundaries.
  Hardlink benchmarks faster than reflink across every target reflink
  supports (APFS clonefile, btrfs/xfs FICLONE), so `auto` skips the
  reflink probe.
- `hardlink` hard-links from the store, with a copy fallback on
  cross-filesystem errors.
- `copy` always writes a full copy.
- `clone` uses reflink. Currently falls back to copy when reflink is
  unsupported; strict enforcement is planned for a future release
  (`WARN_AUBE_CLONE_STRATEGY_FALLBACK`).
- `clone-or-copy` tries reflink first and falls back to a plain copy
  instead of hardlinking.

Overridable per-invocation with `--package-import-method`.
"""
sources.cli = ["package-import-method"]
sources.env = ["npm_config_package_import_method", "NPM_CONFIG_PACKAGE_IMPORT_METHOD", "AUBE_PACKAGE_IMPORT_METHOD"]
sources.npmrc = ["packageImportMethod", "package-import-method"]
sources.workspaceYaml = ["packageImportMethod"]
examples = []

[modulesCacheMaxAge]
description = "Minutes before orphan packages are removed from the virtual store."
type = "int"
default = "10080"
docs = """
After each successful install, aube sweeps the per-project
`node_modules/.aube/` virtual store and removes entries whose
directory `mtime` is older than this threshold AND that the just-run
install did not touch. The mtime is refreshed every time the linker
visits an entry (including the cached-fast-path branches), so entries
still in use are effectively immortal. Default is 7 days (10 080
minutes). Set to `0` to disable the sweep entirely. The sweep only
touches per-project entries; the shared global virtual store under
`~/.cache/aube/virtual-store/` is managed separately by `aube store
prune`.
"""
sources.cli = []
sources.env = ["npm_config_modules_cache_max_age", "NPM_CONFIG_MODULES_CACHE_MAX_AGE", "AUBE_MODULES_CACHE_MAX_AGE"]
sources.npmrc = ["modulesCacheMaxAge"]
examples = []

[dlxCacheMaxAge]
description = "Minutes before the dlx cache is considered stale."
type = "int"
default = "1440"
docs = """
Accepted for pnpm parity. `aube dlx` currently installs into a fresh
`tempfile::TempDir` per invocation and removes it on exit, so there is
no persistent dlx cache to expire — the configured value is read and
validated, but no eviction runs against it. If aube grows a persistent
dlx cache later, this setting will gate its TTL without any further
config-surface change.
"""
sources.cli = []
sources.env = ["npm_config_dlx_cache_max_age", "NPM_CONFIG_DLX_CACHE_MAX_AGE", "AUBE_DLX_CACHE_MAX_AGE"]
sources.npmrc = ["dlx-cache-max-age", "dlxCacheMaxAge"]
examples = []

[enableGlobalVirtualStore]
description = "Use a per-user virtual store for all projects."
type = "bool"
default = "undefined"
docs = """
aube ships its own global virtual store under `~/.cache/aube/virtual-store/`.
It's enabled by default outside CI and disabled under CI (see
`aube-linker`, which checks the `CI` env var). Set
`enableGlobalVirtualStore=false` in `.npmrc` or `pnpm-workspace.yaml`
to force per-project materialization for a project.

`aube dlx` defaults this setting to `false` for its scratch installs so
CLIs with undeclared runtime imports can still use the hidden-hoist
fallback inside the temporary project. Pass
`aube dlx --enable-gvs <pkg>` when you want to force the shared virtual
store on for a dlx invocation.

The global flags are one-shot CLI sources for the same setting:
`--disable-global-virtual-store` resolves this setting to `false`, and
`--enable-global-virtual-store` resolves it to `true`. The enable flag
can force the shared virtual store on even in CI or when package
compatibility heuristics would normally disable it.
"""
sources.cli = ["enable-global-virtual-store", "disable-global-virtual-store"]
sources.env = ["npm_config_enable_global_virtual_store", "NPM_CONFIG_ENABLE_GLOBAL_VIRTUAL_STORE", "AUBE_ENABLE_GLOBAL_VIRTUAL_STORE"]
sources.npmrc = ["enableGlobalVirtualStore", "enable-global-virtual-store"]
sources.workspaceYaml = ["enableGlobalVirtualStore"]
examples = [
  "echo 'enableGlobalVirtualStore=false' >> .npmrc",
  "aube --disable-global-virtual-store install",
  "aube dlx --enable-gvs create-vite",
]

[disableGlobalVirtualStoreForPackages]
description = "Package names whose presence in any importer forces per-project materialization."
type = "list<string>"
default = "[\"next\", \"nuxt\", \"vite\", \"vitepress\", \"parcel\"]"
docs = """
aube's global virtual store makes `node_modules/.aube/<pkg>` an
absolute symlink into `~/.cache/aube/virtual-store/`. Tools whose
module resolvers follow symlinks to real paths and then walk up the
directory tree can't reach the project's `node_modules/` from inside
the global store and produce errors like `Symlink ... is invalid, it
points out of the filesystem root`.

When `aube install` finds one of these names in any importer's
`dependencies`, `devDependencies`, or `optionalDependencies`, it
forces per-project materialization for that install and prints a
one-line warning naming the trigger.

The default list — `next`, `nuxt`, `vite`, `vitepress`, `parcel` —
covers the tools with concrete walk-up failures: Next.js's Turbopack
canonicalizes through symlinks and walks up for app-router/monorepo
detection, Vite/VitePress/Nuxt plugins walk up for config discovery
(see [jdx/mise#9261](https://github.com/jdx/mise/pull/9261) for the
VitePress case), and Parcel's resolver walks up for `.parcelrc`.
Webpack and Rollup are *not* on the default list: plain Webpack
resolves via the sibling symlinks aube already places inside
`.aube/<pkg>/node_modules/`, and Rollup is rarely a direct dep (it's
typically transitive of Vite). Add them back here if a specific
plugin needs the fallback, or set the list to `[]` to disable the
heuristic entirely. `CI=1` already forces per-project mode, so the
warning suppresses itself in that case.
"""
sources.cli = []
sources.env = ["npm_config_disable_global_virtual_store_for_packages", "NPM_CONFIG_DISABLE_GLOBAL_VIRTUAL_STORE_FOR_PACKAGES", "AUBE_DISABLE_GLOBAL_VIRTUAL_STORE_FOR_PACKAGES"]
sources.npmrc = ["disableGlobalVirtualStoreForPackages", "disable-global-virtual-store-for-packages"]
sources.workspaceYaml = ["disableGlobalVirtualStoreForPackages"]
examples = []

# ========================================= Store =========================================

[storeDir]
description = "Location where packages are saved on disk (content-addressable store)."
type = "path"
default = "$XDG_DATA_HOME/aube/store/"
docs = """
Defaults to aube's own XDG-compliant store path
(`$XDG_DATA_HOME/aube/store/`, falling back to
`~/.local/share/aube/store/`). aube does not read from or write to
pnpm's `~/.pnpm-store/`. Set in `.npmrc` or `aube-workspace.yaml` to point at
a different directory, which is useful for isolating CI runners, putting the
store on a faster disk, or sharing one store across multiple users on the
same host.

Path interpretation matches pnpm: `~` expands to the user's home
directory and a relative path is resolved against the project root,
not the current working directory. aube appends its own schema suffix
(`v1`) to the user-supplied directory, so `store-dir=/srv/aube-store`
stores package content under `/srv/aube-store/v1/`. Run
`aube store path` to print the resolved store location.
"""
sources.cli = []
sources.env = ["npm_config_store_dir", "NPM_CONFIG_STORE_DIR", "AUBE_STORE_DIR"]
sources.npmrc = ["store-dir", "storeDir"]
sources.workspaceYaml = ["storeDir"]
examples = [
    "echo 'store-dir=/srv/aube-store' >> .npmrc && aube install",
]

[verifyStoreIntegrity]
description = "Check store file integrity before linking."
type = "bool"
default = "true"
docs = """
aube verifies each package's SRI `integrity` (sha512, or legacy
sha1/sha256/sha384) against the tarball bytes at import time in
`aube_store::verify_integrity`, before extraction.
Set to `false` via `.npmrc`, env, or `--no-verify-store-integrity` to
skip the check — useful in trusted CI environments where the registry
is already known-good and the tarball bytes have been vetted upstream.
"""
sources.cli = ["verify-store-integrity"]
sources.env = ["npm_config_verify_store_integrity", "NPM_CONFIG_VERIFY_STORE_INTEGRITY", "AUBE_VERIFY_STORE_INTEGRITY"]
sources.npmrc = ["verify-store-integrity", "verifyStoreIntegrity"]
sources.workspaceYaml = ["verifyStoreIntegrity"]
examples = [
    "aube install --no-verify-store-integrity",
    "echo 'verify-store-integrity=false' >> .npmrc",
]

[strictStoreIntegrity]
description = "Fail the install when a packument ships no dist.integrity."
type = "bool"
default = "false"
docs = """
Companion to `verifyStoreIntegrity`. When both are true and a packument
comes back without a `dist.integrity` field, aube refuses to import the
tarball rather than warning and continuing. Matches the behavior a
security-conscious operator wants when a registry proxy or MITM has
stripped the integrity field from an in-flight packument. Defaults to
false for ecosystem parity with pnpm (which only warns), but is the
recommended setting on production CI.
"""
sources.cli = ["strict-store-integrity"]
sources.env = ["npm_config_strict_store_integrity", "NPM_CONFIG_STRICT_STORE_INTEGRITY", "AUBE_STRICT_STORE_INTEGRITY"]
sources.npmrc = ["strict-store-integrity", "strictStoreIntegrity"]
sources.workspaceYaml = ["strictStoreIntegrity"]
examples = [
    "echo 'strict-store-integrity=true' >> .npmrc",
]

[useRunningStoreServer]
description = "Only allow installs when the store server is running."
type = "bool"
default = "false"
docs = """
Accepted for pnpm parity. aube has no long-running store-daemon
component — every install talks directly to the on-disk CAS at
`storeDir`. Setting this to `true` does not fail the install; aube
emits a single warning at install start so a `.npmrc` ported from a
pnpm store-server setup keeps working unchanged. Setting it to `false`
(the default) is silently a no-op.
"""
sources.cli = []
sources.env = ["npm_config_use_running_store_server", "NPM_CONFIG_USE_RUNNING_STORE_SERVER", "AUBE_USE_RUNNING_STORE_SERVER"]
sources.npmrc = ["use-running-store-server", "useRunningStoreServer"]
examples = []

[strictStorePkgContentCheck]
description = "Validate package names and versions in the store."
type = "bool"
default = "true"
docs = """
After each registry tarball is imported, aube reads the freshly stored
`package.json` and confirms its `name` and `version` match what the
resolver asked for. A mismatch fails the install before the package
can be linked into `node_modules`, defending against
registry-substitution attacks where a tarball is served under one
(name, version) but contains a different package on disk. Set to
`false` via `.npmrc` to skip the check (e.g. when intentionally
installing a republished tarball whose manifest lists the upstream
name). Local sources (`file:`, `link:`, git deps) are not checked
since they have no registry-asserted (name, version) to compare
against.
"""
sources.cli = []
sources.env = ["npm_config_strict_store_pkg_content_check", "NPM_CONFIG_STRICT_STORE_PKG_CONTENT_CHECK", "AUBE_STRICT_STORE_PKG_CONTENT_CHECK"]
sources.npmrc = ["strict-store-pkg-content-check", "strictStorePkgContentCheck"]
examples = [
    "echo 'strict-store-pkg-content-check=false' >> .npmrc",
]

# ========================================= Network =========================================

[httpsProxy]
description = "Proxy URL for outgoing HTTPS requests."
type = "url"
default = "null"
docs = """
Forwards every HTTPS registry fetch through the given proxy URL.
Honored by the `aube-registry` reqwest client. Resolution mirrors
pnpm: `.npmrc https-proxy` ?? `.npmrc proxy` ?? `HTTPS_PROXY` /
`https_proxy` env var.
"""
sources.cli = []
sources.env = ["npm_config_proxy", "NPM_CONFIG_PROXY", "npm_config_https_proxy", "NPM_CONFIG_HTTPS_PROXY", "AUBE_HTTPS_PROXY", "https_proxy", "HTTPS_PROXY"]
sources.npmrc = ["https-proxy", "httpsProxy", "proxy"]
examples = []
# Consumed inside `aube_registry::config::NpmConfig::load`, before
# the CLI has a `ResolveCtx`; keep this metadata grepable for docs.
typedAccessorUnused = true
npmShared = true

[httpProxy]
description = "Proxy URL for outgoing HTTP requests."
type = "url"
default = "null"
docs = """
HTTP counterpart to `httpsProxy`. Resolution mirrors pnpm:
`.npmrc http-proxy` ?? resolved `httpsProxy` ?? `HTTP_PROXY` /
`http_proxy` env var ?? `PROXY` / `proxy` env var. The inheritance
from `httpsProxy` means a single `https-proxy=...` line in `.npmrc`
configures both schemes.
"""
sources.cli = []
sources.env = ["PROXY", "proxy", "npm_config_http_proxy", "NPM_CONFIG_HTTP_PROXY", "AUBE_HTTP_PROXY", "http_proxy", "HTTP_PROXY"]
sources.npmrc = ["http-proxy", "httpProxy"]
examples = []
# Consumed inside `aube_registry::config::NpmConfig::load`, before
# the CLI has a `ResolveCtx`; keep this metadata grepable for docs.
typedAccessorUnused = true
npmShared = true

[noProxy]
description = "Comma-separated list of domains that bypass the proxy."
type = "string"
default = "null"
docs = """
Passed through to `reqwest::NoProxy::from_string` verbatim, so
wildcard and port-qualified hosts behave the same as curl / node.
Applies to both `httpsProxy` and `httpProxy`. Falls back to the
standard `NO_PROXY` / `no_proxy` environment variables.
"""
sources.cli = []
sources.env = ["npm_config_noproxy", "NPM_CONFIG_NOPROXY", "npm_config_no_proxy", "NPM_CONFIG_NO_PROXY", "AUBE_NO_PROXY", "no_proxy", "NO_PROXY"]
sources.npmrc = ["noproxy", "noProxy", "no-proxy"]
examples = []
# Consumed inside `aube_registry::config::NpmConfig::apply` via a
# string-keyed match on the raw `.npmrc` entries, because the registry
# client is built before a `ResolveCtx` exists. Same pattern as every
# other reqwest-level knob below.
typedAccessorUnused = true
npmShared = true

[localAddress]
description = "Local interface IP address to bind registry connections to."
type = "string"
default = "undefined"
docs = """
Used on multi-homed hosts where outbound traffic must leave a
specific interface. Parsed as `IpAddr`; unparseable values are
dropped at load time with a warning.
"""
sources.cli = []
sources.env = ["npm_config_local_address", "NPM_CONFIG_LOCAL_ADDRESS", "AUBE_LOCAL_ADDRESS"]
sources.npmrc = ["local-address", "localAddress"]
examples = []
# Consumed inside `aube_registry::config::NpmConfig::apply` via a
# string-keyed match on the raw `.npmrc` entries — same reason as
# `noProxy` above.
typedAccessorUnused = true
npmShared = true

[maxsockets]
description = "Maximum concurrent connections per origin."
type = "int"
default = "networkConcurrency x 3"
docs = """
Plumbed into reqwest's `pool_max_idle_per_host`. This is the
closest analogue to pnpm's per-origin socket cap — reqwest doesn't
expose a hard maximum, but capping the idle pool keeps the steady
state bounded.
"""
sources.cli = []
sources.env = ["npm_config_maxsockets", "NPM_CONFIG_MAXSOCKETS", "AUBE_MAXSOCKETS"]
sources.npmrc = ["maxsockets"]
examples = []
# Consumed inside `aube_registry::config::NpmConfig::apply` via a
# string-keyed match on the raw `.npmrc` entries — same reason as
# `noProxy` above.
typedAccessorUnused = true
npmShared = true

[strictSsl]
description = "Validate SSL certificates for HTTPS requests."
type = "bool"
default = "true"
docs = """
Defaults to `true`. Setting `strict-ssl=false` disables TLS
certificate verification entirely via
`danger_accept_invalid_certs` — required to get through corporate
MITM proxies that present a self-signed CA until aube grows a
proper per-registry `cafile` setting.
"""
sources.cli = []
sources.env = ["npm_config_strict_ssl", "NPM_CONFIG_STRICT_SSL", "AUBE_STRICT_SSL"]
sources.npmrc = ["strict-ssl", "strictSsl"]
examples = []
# Consumed inside `aube_registry::config::NpmConfig::apply` via a
# string-keyed match on the raw `.npmrc` entries — same reason as
# `noProxy` above.
typedAccessorUnused = true
npmShared = true

# ========================================= Lockfile =========================================

[lockfile]
description = "Read and generate aube-lock.yaml."
type = "bool"
default = "true"
docs = """
Controls whether aube reads and writes a lockfile during install. When
false (npm's `--no-package-lock` equivalent), every `aube install` runs
a fresh resolve, drift checks against an on-disk lockfile are skipped,
and the writer is a no-op — useful in lockfile-free workflows and
one-off `aube install` invocations inside isolated throwaway
environments.

Setting `lockfile=false` overrides the frozen-lockfile modes: the
install never errors on missing lockfiles and never preserves a
format-compatible file alongside `aube-lock.yaml`. `--lockfile-only`
combined with `lockfile=false` is rejected as a contradiction.
"""
sources.cli = []
sources.env = ["npm_config_lockfile", "NPM_CONFIG_LOCKFILE", "AUBE_LOCKFILE"]
sources.npmrc = ["lockfile"]
sources.workspaceYaml = ["lockfile"]
examples = [
  "echo 'lockfile=false' >> .npmrc && aube install",
]

[lockfileDir]
description = "Directory the lockfile is written to and read from."
type = "path"
default = "null"
docs = """
By default the lockfile lives at `<project_root>/aube-lock.yaml`. Set
this to relocate it. When the resolved path differs from the project
root, the project becomes an importer keyed by its relative path
(e.g. `project` if the lockfile is one directory above).

Single-project relocation only — multi-project shared lockfiles
require a `pnpm-workspace.yaml` workspace. Pointing two unrelated
projects at the same `lockfileDir` is rejected at install time.

Mirrors pnpm's `--lockfile-dir` / `lockfile-dir`. A relative path is
resolved against the project root, not the current working directory.
"""
sources.cli = ["lockfile-dir"]
sources.env = ["npm_config_lockfile_dir", "NPM_CONFIG_LOCKFILE_DIR", "AUBE_LOCKFILE_DIR"]
sources.npmrc = ["lockfile-dir", "lockfileDir"]
sources.workspaceYaml = ["lockfileDir"]
examples = ["aube install --lockfile-dir .."]

[preferFrozenLockfile]
description = "Perform a headless install if the lockfile already satisfies package.json."
type = "bool"
default = "true"
docs = """
aube's default outside CI. Maps to `FrozenMode::Prefer` in
`crates/aube/src/commands/install.rs`. Inside CI the default flips
to `FrozenMode::Frozen` (see `default_for_env`).
"""
sources.cli = ["prefer-frozen-lockfile"]
sources.env = ["npm_config_prefer_frozen_lockfile", "NPM_CONFIG_PREFER_FROZEN_LOCKFILE", "AUBE_PREFER_FROZEN_LOCKFILE"]
sources.npmrc = ["prefer-frozen-lockfile"]
sources.workspaceYaml = ["preferFrozenLockfile"]
examples = ["aube install --prefer-frozen-lockfile"]

[lockfileIncludeTarballUrl]
description = "Add the full tarball URL to each lockfile entry."
type = "bool"
default = "false"
docs = """
When true, aube's lockfile writer records the registry tarball URL in
each package's `resolution:` block alongside the `integrity:` hash.
This bloats the lockfile (every entry gets the full download URL) but
makes the file self-contained — installs no longer need the configured
registry to derive the tarball path, which is handy in air-gapped
environments or when the `.npmrc` registry differs from the one the
lockfile was generated against.

Only registry packages are affected; `file:`, `link:`, `git+` and
remote-tarball entries already store their source URL unconditionally.

The setting round-trips through the lockfile's `settings:` header, so
once enabled subsequent installs preserve the tarball field without
re-reading `.npmrc`.
"""
sources.cli = []
sources.env = ["npm_config_lockfile_include_tarball_url", "NPM_CONFIG_LOCKFILE_INCLUDE_TARBALL_URL", "AUBE_LOCKFILE_INCLUDE_TARBALL_URL"]
sources.npmrc = ["lockfileIncludeTarballUrl", "lockfile-include-tarball-url"]
sources.workspaceYaml = ["lockfileIncludeTarballUrl"]
examples = [
  "echo 'lockfile-include-tarball-url=true' >> .npmrc && aube install",
]

[excludeLinksFromLockfile]
description = "Skip local `link:` dependencies when writing the lockfile."
type = "bool"
default = "false"
docs = """
When true, `link:` dependencies are omitted from the lockfile's
`importers.*.dependencies:` (and `devDependencies:` /
`optionalDependencies:`) maps on write, so adding or removing a purely
local symlink dep doesn't churn the lockfile. The setting round-trips
through the lockfile's `settings:` header — once enabled, subsequent
installs preserve it even without re-reading `.npmrc`.

Aube already omits `link:` packages from the `packages:` / `snapshots:`
sections unconditionally (pnpm parity). This flag controls the
importer-level visibility. `file:` directory deps and git deps are
unaffected; only `link:` entries are filtered.
"""
sources.cli = []
sources.env = ["npm_config_exclude_links_from_lockfile", "NPM_CONFIG_EXCLUDE_LINKS_FROM_LOCKFILE", "AUBE_EXCLUDE_LINKS_FROM_LOCKFILE"]
sources.npmrc = ["exclude-links-from-lockfile", "excludeLinksFromLockfile"]
sources.workspaceYaml = ["excludeLinksFromLockfile"]
examples = []

[gitBranchLockfile]
description = "Generate branch-specific lockfile names (aube-lock.<branch>.yaml)."
type = "bool"
default = "false"
docs = """
When enabled, aube writes the lockfile to `aube-lock.<branch>.yaml`
instead of `aube-lock.yaml`, where `<branch>` is the current git branch
with `/` replaced by `!` (matching pnpm). This reduces merge conflicts
on lockfiles for long-lived branches.

Reads fall back to `aube-lock.yaml` if no branch-specific file exists,
so the setting can be turned on mid-project without re-resolving.
Detached HEAD or a missing/failing `git` falls back to the plain name.

Set in `aube-workspace.yaml`:

```yaml
gitBranchLockfile: true
```

See [`mergeGitBranchLockfilesBranchPattern`](#setting-mergegitbranchlockfilesbranchpattern)
and the `--merge-git-branch-lockfiles` install flag for folding branch
lockfiles back into `aube-lock.yaml` automatically or on demand.
"""
sources.cli = []
sources.env = ["npm_config_git_branch_lockfile", "NPM_CONFIG_GIT_BRANCH_LOCKFILE", "AUBE_GIT_BRANCH_LOCKFILE"]
sources.npmrc = ["gitBranchLockfile"]
sources.workspaceYaml = ["gitBranchLockfile"]
examples = []

[mergeGitBranchLockfilesBranchPattern]
description = "Branch-name glob list for auto-merging branch lockfiles."
type = "list<string>"
default = "null"
docs = """
Complements `gitBranchLockfile`. Accepts a list of glob patterns. When
`aube install` runs on a branch whose name matches any pattern, aube
discovers every `aube-lock.*.yaml` file in the project directory,
merges their package graphs into `aube-lock.yaml`, and deletes the
branch-specific files. Typical usage:

```yaml
mergeGitBranchLockfilesBranchPattern:
  - main
  - release/*
  - "!release/legacy-*"
```

`!`-prefixed patterns are negations — a branch that matches any
positive pattern AND does NOT match any negative pattern triggers the
merge. The `--merge-git-branch-lockfiles` install flag forces the
same merge regardless of the pattern list.

Conflict rule: when two branch lockfiles record the same `dep_path`
with different `version` or `integrity`, the higher semver version
wins and a warning is logged.
"""
sources.cli = []
sources.env = ["npm_config_merge_git_branch_lockfiles_branch_pattern", "NPM_CONFIG_MERGE_GIT_BRANCH_LOCKFILES_BRANCH_PATTERN", "AUBE_MERGE_GIT_BRANCH_LOCKFILES_BRANCH_PATTERN"]
sources.npmrc = ["mergeGitBranchLockfilesBranchPattern"]
sources.workspaceYaml = ["mergeGitBranchLockfilesBranchPattern"]
examples = []

[sharedWorkspaceLockfile]
description = "Write one lockfile per workspace package instead of a single shared root lockfile."
type = "bool"
default = "true"
docs = """
Default `true` matches pnpm: a workspace records every importer's
resolved graph in a single root lockfile (`aube-lock.yaml` or
`pnpm-lock.yaml`), so `aube install` from anywhere in the workspace
sees every package's locked versions.

Flip to `false` for the per-project layout: each workspace member
gets its own lockfile next to its `package.json` containing only
that member's importer (remapped to `.`) plus the transitive packages
reachable from it. The workspace-root lockfile is not written.

Set in `aube-workspace.yaml` / `pnpm-workspace.yaml`:

```yaml
sharedWorkspaceLockfile: false
```

Trade-offs to know about before flipping the default:
- Auto-install freshness state (`node_modules/.aube-state`) and
  the frozen-lockfile fast path are anchored at the workspace root,
  so a `false` install re-resolves more aggressively than a shared
  install would.
- Workspace deps (`workspace:*`) still resolve correctly because
  the resolver runs once over the whole workspace before lockfile
  writes are split.
"""
sources.cli = []
sources.env = ["npm_config_shared_workspace_lockfile", "NPM_CONFIG_SHARED_WORKSPACE_LOCKFILE", "AUBE_SHARED_WORKSPACE_LOCKFILE"]
sources.npmrc = ["sharedWorkspaceLockfile"]
sources.workspaceYaml = ["sharedWorkspaceLockfile"]
examples = []

[peersSuffixMaxLength]
description = "Max length of the peer-ID suffix in lockfile dep_paths."
type = "int"
default = "1000"
docs = """
Caps the length of the peer-ID suffix appended to a `dep_path` in the
lockfile (e.g. `react-dom@18.2.0(react@18.2.0)`). When the suffix body
would exceed this many bytes, aube replaces the whole suffix with a
parenthesized short hash `(<short-hash>)` — the first 32 chars of the
SHA-256 of the suffix body — matching pnpm's `createPeerDepGraphHash`
so lockfiles stay portable.

Mutual-peer cycles in large graphs can otherwise grow suffixes
unboundedly across fixed-point iterations of the resolver. The default
of 1000 bytes is pnpm's default and rarely fires in practice.
"""
sources.cli = []
sources.env = ["npm_config_peers_suffix_max_length", "NPM_CONFIG_PEERS_SUFFIX_MAX_LENGTH", "AUBE_PEERS_SUFFIX_MAX_LENGTH"]
sources.npmrc = ["peersSuffixMaxLength"]
sources.workspaceYaml = ["peersSuffixMaxLength"]
examples = []

# ========================================= Request =========================================

[gitShallowHosts]
description = "Hosts for which aube performs shallow git clones."
type = "list<string>"
default = "[\"github.com\", \"gist.github.com\", \"gitlab.com\", \"bitbucket.com\", \"bitbucket.org\"]"
docs = """
Consulted by `aube-store::git_shallow_clone` when cloning a git
dependency. When the URL's hostname matches an entry in this list
(exact match, same as pnpm — `github.com` does not match
`api.github.com`), aube fetches only the commit it needs with
`git fetch --depth 1 origin <sha>`, falling back to a full fetch if
the server refuses by-SHA shallow fetches. When the hostname is
not in the list, aube does a plain `git fetch origin` before
checkout, since many self-hosted servers disable
`uploadpack.allowReachableSHA1InWant` and a shallow fetch would
either fail or silently waste a round-trip.

The cache key for the resolved checkout is still `(url, commit)`,
so two deps that resolve to the same commit share a clone
regardless of which strategy produced it.
"""
sources.cli = []
sources.env = ["npm_config_git_shallow_hosts", "NPM_CONFIG_GIT_SHALLOW_HOSTS", "AUBE_GIT_SHALLOW_HOSTS"]
sources.npmrc = ["git-shallow-hosts", "gitShallowHosts"]
examples = []

[networkConcurrency]
description = "Maximum concurrent HTTP(S) requests."
type = "int"
default = "auto (workers x3 clamped to 16-64)"
docs = """
Caps the tokio semaphores that gate concurrent tarball downloads
inside `crates/aube/src/commands/install.rs`. When unset, aube
matches pnpm's dynamic default: worker count x3, clamped to 16-64.
Set this value explicitly to override the automatic scaling. The
resolver's packument fetcher still uses its own internal cap for now;
plumbing that cap through is tracked as a follow-up.
"""
sources.cli = ["network-concurrency"]
sources.env = ["npm_config_network_concurrency", "NPM_CONFIG_NETWORK_CONCURRENCY", "AUBE_NETWORK_CONCURRENCY"]
sources.npmrc = ["network-concurrency", "networkConcurrency"]
sources.workspaceYaml = ["networkConcurrency"]
examples = [
    "aube install --network-concurrency 8",
    "echo 'network-concurrency=8' >> .npmrc",
]

[fetchRetries]
description = "Number of retry attempts for failed registry fetches."
type = "int"
default = "2"
docs = """
Number of *additional* attempts the registry client makes after a
transient failure (5xx / 429 / connection error). `2` means up to 3
total attempts. Applied to every idempotent GET — packument reads,
tarball downloads, dist-tag reads. Writes (`put_packument`,
`put_dist_tag`, `delete_dist_tag`, audit POST) are not retried because
a second attempt could double-apply or race.

Backoff is governed by `fetchRetryFactor`, `fetchRetryMintimeout`,
`fetchRetryMaxtimeout`.
"""
sources.cli = ["fetch-retries"]
sources.env = ["npm_config_fetch_retries", "NPM_CONFIG_FETCH_RETRIES", "AUBE_FETCH_RETRIES"]
sources.npmrc = ["fetch-retries", "fetchRetries"]
examples = ["aube install --fetch-retries=5"]
npmShared = true

[fetchRetryFactor]
description = "Exponential backoff factor for fetch retries."
type = "int"
default = "10"
docs = """
Multiplier used between retry attempts. Attempt `n` waits
`min(fetchRetryMintimeout * fetchRetryFactor ^ (n-1), fetchRetryMaxtimeout)`
milliseconds before retrying. With the defaults (factor=10,
min=10000ms, max=60000ms), the sequence is 10s → 60s → 60s.
"""
sources.cli = ["fetch-retry-factor"]
sources.env = ["npm_config_fetch_retry_factor", "NPM_CONFIG_FETCH_RETRY_FACTOR", "AUBE_FETCH_RETRY_FACTOR"]
sources.npmrc = ["fetch-retry-factor", "fetchRetryFactor"]
examples = []
npmShared = true

[fetchRetryMintimeout]
description = "Minimum retry timeout in milliseconds."
type = "int"
default = "10000"
docs = "Lower bound on the computed retry backoff. See `fetchRetryFactor`."
sources.cli = ["fetch-retry-mintimeout"]
sources.env = ["npm_config_fetch_retry_mintimeout", "NPM_CONFIG_FETCH_RETRY_MINTIMEOUT", "AUBE_FETCH_RETRY_MINTIMEOUT"]
sources.npmrc = ["fetch-retry-mintimeout", "fetchRetryMintimeout"]
examples = []
npmShared = true

[fetchRetryMaxtimeout]
description = "Maximum retry timeout in milliseconds."
type = "int"
default = "60000"
docs = "Upper bound on the computed retry backoff. See `fetchRetryFactor`."
sources.cli = ["fetch-retry-maxtimeout"]
sources.env = ["npm_config_fetch_retry_maxtimeout", "NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT", "AUBE_FETCH_RETRY_MAXTIMEOUT"]
sources.npmrc = ["fetch-retry-maxtimeout", "fetchRetryMaxtimeout"]
examples = []
npmShared = true

[fetchTimeout]
description = "Max time (ms) to wait for an HTTP request."
type = "int"
default = "300000"
docs = """
Per-request HTTP timeout, applied via `reqwest`'s single-knob
`.timeout()` so it covers headers + body together. A request that
exceeds this limit fails with a transport error, which is then
retriable (see `fetchRetries`). Default matches npm's 5 minutes.
"""
sources.cli = ["fetch-timeout"]
sources.env = ["npm_config_fetch_timeout", "NPM_CONFIG_FETCH_TIMEOUT", "AUBE_FETCH_TIMEOUT"]
sources.npmrc = ["fetch-timeout", "fetchTimeout"]
examples = ["aube add lodash --fetch-timeout=60000"]
npmShared = true

[fetchWarnTimeoutMs]
description = "Warn if a metadata request exceeds this threshold (ms)."
type = "int"
default = "10000"
docs = """
Observability threshold for registry *metadata* requests (packument,
dist-tags). When a successful response takes longer than
`fetchWarnTimeoutMs` milliseconds of wall-clock time — including any
retry backoff — aube emits a `tracing::warn!` line naming the resource
and the elapsed time. The request itself is never aborted by this
setting; the hard cut-off is still `fetchTimeout`.

Set to `0` to disable the warning entirely, matching pnpm's convention
for "observability knob off". Tarball downloads are intentionally out
of scope: `fetchMinSpeedKiBps` is the tarball-side analogue.
"""
sources.cli = []
sources.env = ["npm_config_fetch_warn_timeout_ms", "NPM_CONFIG_FETCH_WARN_TIMEOUT_MS", "AUBE_FETCH_WARN_TIMEOUT_MS"]
sources.npmrc = ["fetchWarnTimeoutMs"]
examples = []

[fetchMinSpeedKiBps]
description = "Warn if download speed falls below this threshold (KiB/s)."
type = "int"
default = "50"
docs = """
Warn when a tarball's end-to-end average throughput falls below this
many KiB/s. Set to `0` to disable.
"""
sources.cli = []
sources.env = ["npm_config_fetch_min_speed_ki_bps", "NPM_CONFIG_FETCH_MIN_SPEED_KI_BPS", "AUBE_FETCH_MIN_SPEED_KI_BPS"]
sources.npmrc = ["fetchMinSpeedKiBps"]
examples = []

[packumentMaxBytes]
description = "Hard cap on a packument response body size in bytes."
type = "int"
default = "209715200"
docs = """
Refuses any packument response whose `Content-Length` exceeds this
many bytes. A hostile or misconfigured registry (including a MITM on
a compromised mirror) could otherwise stream gigabytes of JSON into
the resolver and OOM the install; the cap makes that fail loudly.

Default: 200 MiB. Raise if you hit the cap, or set to `0` to disable
it entirely (only reasonable against a registry you fully trust).

Applies to every packument fetch: corgi and non-corgi variants, the
cached-resolve path, and the fresh-read path used by `deprecate` /
`undeprecate`. Tarball downloads are capped separately via
`tarballMaxBytes`.
"""
sources.cli = []
sources.env = ["npm_config_packument_max_bytes", "NPM_CONFIG_PACKUMENT_MAX_BYTES", "AUBE_PACKUMENT_MAX_BYTES"]
sources.npmrc = ["packumentMaxBytes"]
examples = []

[tarballMaxBytes]
description = "Hard cap on a tarball response body size in bytes (on-wire, still compressed)."
type = "int"
default = "1073741824"
docs = """
Refuses any tarball response whose `Content-Length` exceeds this many
bytes before any decompression runs. Without a wire-level cap a
hostile mirror could stream a multi-GiB compressed payload into
memory before the gzip reader ever sees a byte; the separate
decompressed ceiling in `aube-store` would only fire after that.

Default: 1 GiB. Raise if a legitimate tarball exceeds it, or set to
`0` to disable the cap entirely (only reasonable against a registry
you fully trust).
"""
sources.cli = []
sources.env = ["npm_config_tarball_max_bytes", "NPM_CONFIG_TARBALL_MAX_BYTES", "AUBE_TARBALL_MAX_BYTES"]
sources.npmrc = ["tarballMaxBytes"]
examples = []

# ========================================= Peer Dependencies =========================================

[autoInstallPeers]
description = "Automatically install missing peer dependencies."
type = "bool"
default = "true"
docs = """
When true (the default), missing peer dependencies are auto-installed
during resolution and hoisted into the importer. Set to `false` to
match pnpm's opt-out behavior: peers are left alone and unmet peers
are silent (set `strict-peer-dependencies=true` for diagnostics).
"""
sources.cli = ["auto-install-peers"]
sources.env = ["npm_config_auto_install_peers", "NPM_CONFIG_AUTO_INSTALL_PEERS", "AUBE_AUTO_INSTALL_PEERS"]
sources.npmrc = ["auto-install-peers", "autoInstallPeers"]
sources.workspaceYaml = ["autoInstallPeers"]
examples = []

[dedupePeerDependents]
description = "Deduplicate packages that have peer dependencies."
type = "bool"
default = "true"
docs = """
When true (the default), aube collapses packages that landed at
different peer-suffixed dep_paths but resolved every declared peer to
the same version into a single canonical variant. Ancestor dedupe
happens inside the per-package DFS; this flag additionally controls
the cross-subtree intersection pass that runs inside the fixed-point
loop. Set to `false` to keep every distinct peer-suffixed variant
(matching pnpm's opt-out).
"""
sources.cli = []
sources.env = ["npm_config_dedupe_peer_dependents", "NPM_CONFIG_DEDUPE_PEER_DEPENDENTS", "AUBE_DEDUPE_PEER_DEPENDENTS"]
sources.npmrc = ["dedupePeerDependents"]
sources.workspaceYaml = ["dedupePeerDependents"]
examples = []

[dedupePeers]
description = "Use version-only identifiers for peer suffixes in the lockfile."
type = "bool"
default = "false"
docs = """
When true, lockfile peer suffixes emit `(18.2.0)` instead of the
default `(react@18.2.0)`. Applied as a post-pass over the resolved
graph — the resolver's cycle detection still runs against the full
`name@version` form, so mutual-peer cycles converge the same way
either form.
"""
sources.cli = []
sources.env = ["npm_config_dedupe_peers", "NPM_CONFIG_DEDUPE_PEERS", "AUBE_DEDUPE_PEERS"]
sources.npmrc = ["dedupePeers"]
sources.workspaceYaml = ["dedupePeers"]
examples = []

[strictPeerDependencies]
description = "Fail if peer dependencies are missing or invalid."
type = "bool"
default = "false"
docs = """
When true, any unmet peer dependency (missing, or resolved to a version
outside the declared range) fails the install with a miette diagnostic
listing every mismatch. This is also the only way aube surfaces peer
mismatches — by default aube is silent, matching bun/npm/yarn. Set
this to `false` (the default) to disable.
"""
sources.cli = []
sources.env = ["npm_config_strict_peer_dependencies", "NPM_CONFIG_STRICT_PEER_DEPENDENCIES", "AUBE_STRICT_PEER_DEPENDENCIES"]
sources.npmrc = ["strict-peer-dependencies", "strictPeerDependencies"]
sources.workspaceYaml = ["strictPeerDependencies"]
examples = []

[resolvePeersFromWorkspaceRoot]
description = "Use root workspace dependencies for peer resolution."
type = "bool"
default = "true"
docs = """
When true (the default), an unresolved peer falls back to the root
workspace importer's direct deps before the graph-wide scan tier.
Common monorepo pattern where the root pins shared peers (e.g.
`react`) that leaf packages peer on without declaring them in their
own subtree. Set to `false` to skip the root tier and go straight to
graph-wide scanning.
"""
sources.cli = []
sources.env = ["npm_config_resolve_peers_from_workspace_root", "NPM_CONFIG_RESOLVE_PEERS_FROM_WORKSPACE_ROOT", "AUBE_RESOLVE_PEERS_FROM_WORKSPACE_ROOT"]
sources.npmrc = ["resolvePeersFromWorkspaceRoot"]
sources.workspaceYaml = ["resolvePeersFromWorkspaceRoot"]
examples = []

["peerDependencyRules.ignoreMissing"]
description = "Suppress warnings for specific missing peer dependencies."
type = "list<string>"
default = "undefined"
docs = """
Glob list of peer dependency names to exclude from the
`strict-peer-dependencies` check when they're missing entirely. A peer
present at the wrong version is still reported (use `allowedVersions`
or `allowAny` for that). Has no effect on the default install — aube
is silent about peer mismatches unless strict mode is on. Read from
the root `package.json` (`pnpm.peerDependencyRules.ignoreMissing`),
`pnpm-workspace.yaml`, and `.npmrc`; later sources fully replace the
previous source's list.
"""
sources.cli = []
sources.env = ["npm_config_peer_dependency_rules_ignore_missing", "NPM_CONFIG_PEER_DEPENDENCY_RULES_IGNORE_MISSING", "AUBE_PEER_DEPENDENCY_RULES_IGNORE_MISSING"]
sources.npmrc = ["peerDependencyRules.ignoreMissing"]
sources.workspaceYaml = ["peerDependencyRules.ignoreMissing"]
examples = []

["peerDependencyRules.allowedVersions"]
description = "Override the accepted semver range for specific peer dependencies."
type = "object"
default = "undefined"
docs = """
Map of peer selector to an additional semver range. Keys are either a
bare peer name (e.g. `react`) which applies regardless of parent, or
`parent>peer` (e.g. `styled-components>react`) which scopes the
override to peers declared by that specific parent. A peer resolving
inside *either* the declared range or this override is treated as
satisfied — widens the accepted range rather than replacing it. Merged
across `pnpm.peerDependencyRules.allowedVersions` in `package.json`,
`pnpm-workspace.yaml`, and `.npmrc` (later sources win per key).
"""
sources.cli = []
sources.env = []
sources.npmrc = ["peerDependencyRules.allowedVersions"]
sources.workspaceYaml = ["peerDependencyRules.allowedVersions"]
examples = []

["peerDependencyRules.allowAny"]
description = "Allow any peer version to resolve, bypassing semver checks."
type = "list<string>"
default = "undefined"
docs = """
Glob list of peer dependency names whose semver check should be
bypassed entirely — any resolved version counts as satisfying the
declared range. Also excludes missing peers for matching names. Escape
hatch for packages with incompatible peer declarations. Has no effect
on the default install — aube is silent about peer mismatches unless
`strict-peer-dependencies` is on. Read from the root `package.json`,
`pnpm-workspace.yaml`, and `.npmrc`; later sources fully replace the
previous source's list.
"""
sources.cli = []
sources.env = ["npm_config_peer_dependency_rules_allow_any", "NPM_CONFIG_PEER_DEPENDENCY_RULES_ALLOW_ANY", "AUBE_PEER_DEPENDENCY_RULES_ALLOW_ANY"]
sources.npmrc = ["peerDependencyRules.allowAny"]
sources.workspaceYaml = ["peerDependencyRules.allowAny"]
examples = []

# ========================================= CLI =========================================

[color]
description = "Control color output in aube's CLI."
type = '"auto" | "always" | "never"'
default = "\"auto\""
docs = "`--color` / `--no-color`, `color=always|never|auto` in `.npmrc`, and `NPM_CONFIG_COLOR` all resolve before output initializes. The resolved choice is translated into `FORCE_COLOR` / `CLICOLOR_FORCE` / `NO_COLOR` so aube, diagnostics, progress rendering, and child processes agree."
sources.cli = ["color", "no-color"]
sources.env = ["npm_config_color", "NPM_CONFIG_COLOR", "AUBE_COLOR"]
sources.npmrc = ["color"]
examples = []
# Resolved in `aube/src/main.rs` via `string_from_env` +
# `string_from_npmrc` before argv parsing completes and before a
# `ResolveCtx` exists — the color choice drives miette/tracing init
# and has to happen first. Uses the same underlying helpers as
# `resolved::color` would, just without the ctx wrapper.
typedAccessorUnused = true
npmShared = true

[loglevel]
description = "Minimum log level to display."
type = '"debug" | "info" | "warn" | "error" | "silent"'
default = "\"warn\""
docs = "Controls aube's tracing filter. `-v` / `--verbose` is a shortcut for `debug`; `--silent`, `--reporter=silent`, and `loglevel=silent` suppress aube's own non-error stderr output. Also readable from `.npmrc` `loglevel`. CLI flags override `.npmrc`."
sources.cli = ["loglevel", "verbose", "v", "silent"]
sources.env = ["npm_config_loglevel", "NPM_CONFIG_LOGLEVEL", "AUBE_LOGLEVEL"]
sources.npmrc = ["loglevel"]
examples = []
# Resolved in `aube/src/main.rs` via `string_from_env` +
# `string_from_npmrc` at the very top of `main` so the tracing
# subscriber can be configured before any command runs — the typed
# `resolved::loglevel` accessor would need a `ResolveCtx` that
# doesn't exist yet at that point.
typedAccessorUnused = true
npmShared = true

[useBetaCli]
description = "Opt into experimental CLI features."
type = "bool"
default = "false"
docs = "Accepted from env and `.npmrc` for pnpm parity. aube currently has no beta-gated commands, so the setting is a no-op after validation."
sources.cli = []
sources.env = ["npm_config_use_beta_cli", "NPM_CONFIG_USE_BETA_CLI", "AUBE_USE_BETA_CLI"]
sources.npmrc = ["useBetaCli"]
examples = []
# Parity no-op: accepted but changes no behavior because aube has no
# beta-gated commands. Remove this flag once a caller starts gating on it.
typedAccessorUnused = true

[recursiveInstall]
description = "Install on all workspace packages by default."
type = "bool"
default = "true"
docs = "When true, workspace installs resolve and link all importers by default. Set to false to opt out of implicit workspace-wide install behavior; explicit `--filter` / `--recursive` still wins."
sources.cli = []
sources.env = ["npm_config_recursive_install", "NPM_CONFIG_RECURSIVE_INSTALL", "AUBE_RECURSIVE_INSTALL"]
sources.npmrc = ["recursiveInstall"]
examples = []

[engineStrict]
description = "Fail if a package is incompatible with the current Node version."
type = "bool"
default = "false"
docs = "When on, an `engines.node` mismatch on the root project or any dependency fails the install. When off, mismatches are warnings only."
sources.cli = []
sources.env = ["npm_config_engine_strict", "NPM_CONFIG_ENGINE_STRICT", "AUBE_ENGINE_STRICT"]
sources.npmrc = ["engine-strict", "engineStrict"]
examples = []
npmShared = true

[npmPath]
description = "Path to the npm binary aube should shell out to when needed."
type = "path"
default = "undefined"
docs = "Used for npm-only compatibility commands (`owner`, `pkg`, `search`, `set-script`, `token`, `whoami`) when configured. Without it, aube keeps the explicit `use npm` error."
sources.cli = []
sources.env = ["npm_config_npm_path", "NPM_CONFIG_NPM_PATH", "AUBE_NPM_PATH"]
sources.npmrc = ["npmPath"]
examples = []

[packageManagerStrict]
description = "Enforce the `packageManager` field in package.json (`off` | `warn` | `error`)."
type = '"off" | "warn" | "error" | true | false'
default = "\"warn\""
docs = """
Controls how aube reacts when a project's `packageManager` field
names something other than `aube` or `pnpm` (npm, yarn, bun, …).
`warn` (the default) prints a warning and continues; install-class
commands also disable the implicit auto-install probe so aube does
not silently install on top of another package manager's layout.
`error` fails install-class commands hard (run-class commands still
warn). `off` skips the check entirely. The bool spellings are
accepted for back-compat: `true` maps to `error`, `false` to `off`.
"""
sources.cli = []
sources.env = ["npm_config_package_manager_strict", "NPM_CONFIG_PACKAGE_MANAGER_STRICT", "AUBE_PACKAGE_MANAGER_STRICT"]
sources.npmrc = ["package-manager-strict", "packageManagerStrict"]
examples = []

[packageManagerStrictVersion]
description = "Enforce the exact `packageManager` version from package.json."
type = "bool"
default = "false"
docs = "When enabled, `packageManager: \"aube@<version>\"` must match the running aube version exactly. `pnpm@...` cannot be exact-version satisfied by aube and fails with a clear diagnostic."
sources.cli = []
sources.env = ["npm_config_package_manager_strict_version", "NPM_CONFIG_PACKAGE_MANAGER_STRICT_VERSION", "AUBE_PACKAGE_MANAGER_STRICT_VERSION"]
sources.npmrc = ["package-manager-strict-version", "packageManagerStrictVersion"]
examples = []

[managePackageManagerVersions]
description = "Switch to the aube version pinned by `packageManager` / `devEngines.packageManager`, downloading it when missing."
type = "bool"
default = "true"
docs = """
pnpm parity (corepack semantics). When the project pins aube —
`packageManager: "aube@1.17.2"` (exact) or
`devEngines.packageManager: {name: "aube", version: "^1.17"}` (ranges
allowed) — and the running aube doesn't satisfy the pin, aube locates
or installs the pinned version (mise installs are reused;
`runtimeInstaller` controls who downloads) and re-execs it with the
same arguments. Set to `false` for validation-only behavior:
`packageManagerStrict` / `packageManagerStrictVersion` then warn or
error on mismatch instead of switching. Pins naming *other* package
managers (`pnpm@…`) are never switched to — only validated.
"""
sources.cli = []
sources.env = ["npm_config_manage_package_manager_versions", "NPM_CONFIG_MANAGE_PACKAGE_MANAGER_VERSIONS", "AUBE_MANAGE_PACKAGE_MANAGER_VERSIONS"]
sources.npmrc = ["manage-package-manager-versions", "managePackageManagerVersions"]
sources.workspaceYaml = ["managePackageManagerVersions"]
examples = []

# ========================================= Build =========================================

[ignoreScripts]
description = "Skip all lifecycle scripts in package.json."
type = "bool"
default = "false"
docs = """
aube already skips dependency install scripts by default (security-first).
The `--ignore-scripts` flag additionally skips root lifecycle hooks
(`preinstall`, `install`, `postinstall`, `prepare`) and flows through
install, ci, and add.
"""
sources.cli = ["ignore-scripts"]
sources.env = ["npm_config_ignore_scripts", "NPM_CONFIG_IGNORE_SCRIPTS", "AUBE_IGNORE_SCRIPTS"]
sources.npmrc = ["ignore-scripts", "ignoreScripts"]
sources.workspaceYaml = ["ignoreScripts"]
examples = [
  "aube install --ignore-scripts",
  "aube ci --ignore-scripts",
]
# Plumbed as a typed bool via `InstallArgs::ignore_scripts` and the
# matching field on `add` / `remove` / `ci`, plus the typed
# `WorkspaceConfig.ignore_scripts` serde field. The script runner
# reads both paths directly, so the typed accessor would be a third,
# redundant surface.
typedAccessorUnused = true
npmShared = true

[childConcurrency]
description = "Maximum number of concurrent script-executing child processes."
type = "int"
default = "5"
docs = """
Caps how many dependency lifecycle scripts run in parallel during the
post-link `allowBuilds` phase. Inside a single package the
`preinstall` / `install` / `postinstall` hooks still run sequentially
— pnpm's execution model is "at most N packages building in
parallel," not "at most N scripts running." Defaults to 5, matching
pnpm. Zero and negative values are clamped up to 1.
"""
sources.cli = []
sources.env = ["npm_config_child_concurrency", "NPM_CONFIG_CHILD_CONCURRENCY", "AUBE_CHILD_CONCURRENCY"]
sources.npmrc = ["child-concurrency", "childConcurrency"]
sources.workspaceYaml = ["childConcurrency"]
examples = [
  "child-concurrency=10",
]

[sideEffectsCache]
description = "Cache the results of install hooks."
type = "bool"
default = "true"
docs = """
When an allowlisted dependency runs lifecycle scripts, aube snapshots
the post-build package directory under the cache dir keyed by
`(name, version, input hash)`. Future installs with the same inputs
hardlink that cached tree back into the materialized package and skip
the build. Packages still have to pass the active `allowBuilds` /
`onlyBuiltDependencies` policy before scripts can run or populate the
cache.
"""
sources.cli = ["side-effects-cache"]
sources.env = ["npm_config_side_effects_cache", "NPM_CONFIG_SIDE_EFFECTS_CACHE", "AUBE_SIDE_EFFECTS_CACHE"]
sources.npmrc = ["side-effects-cache", "sideEffectsCache"]
sources.workspaceYaml = ["sideEffectsCache"]
examples = [
    "aube install --no-side-effects-cache",
    "echo 'side-effects-cache=false' >> .npmrc",
]

[sideEffectsCacheReadonly]
description = "Only read from the side-effects cache; don't write."
type = "bool"
default = "false"
docs = """
When true, aube may restore allowlisted dependency build output from the
side-effects cache but will not write new cache entries after scripts run.
"""
sources.cli = []
sources.env = ["npm_config_side_effects_cache_readonly", "NPM_CONFIG_SIDE_EFFECTS_CACHE_READONLY", "AUBE_SIDE_EFFECTS_CACHE_READONLY"]
sources.npmrc = ["sideEffectsCacheReadonly"]
examples = []

[jailBuilds]
description = "Run approved dependency lifecycle scripts in a restricted build jail."
type = "bool"
default = "false"
docs = """
When enabled, dependency lifecycle scripts that pass the active
`allowBuilds` / `onlyBuiltDependencies` policy run with a scrubbed
environment and temporary HOME. On macOS, aube wraps the script with a
native Seatbelt profile. On Linux, aube applies Landlock and seccomp in
the child before exec. Both deny network access and limit filesystem
writes to the package directory and temporary directories.
Root lifecycle scripts are not jailed.

This defaults to `false` today and is planned to default to `true` in
the next major version.
"""
sources.cli = []
sources.env = ["npm_config_jail_builds", "NPM_CONFIG_JAIL_BUILDS", "AUBE_JAIL_BUILDS"]
sources.npmrc = ["jail-builds", "jailBuilds"]
sources.workspaceYaml = ["jailBuilds"]
examples = [
    "jail-builds=true",
]

[jailBuildExclusions]
description = "Exclude specific dependency packages from jailed builds."
type = "list<string>"
default = "[]"
docs = """
Package patterns in this list still follow the active `allowBuilds` /
`onlyBuiltDependencies` policy, but run outside the build jail when
`jailBuilds` is enabled. Use this for reviewed native packages whose
install scripts need network access, shared caches, or filesystem
writes outside the restricted jail profile.

Patterns use the same forms as `neverBuiltDependencies`: bare package
names, exact `name@version` pins, exact version unions, and `*`
wildcards such as `@scope/*`. Explicit jail exclusions win over the
global `jailBuilds=true` setting.
"""
sources.cli = []
sources.env = ["npm_config_jail_build_exclusions", "NPM_CONFIG_JAIL_BUILD_EXCLUSIONS", "AUBE_JAIL_BUILD_EXCLUSIONS"]
sources.npmrc = ["jailBuildExclusions", "jail-build-exclusions"]
sources.workspaceYaml = ["jailBuildExclusions"]
examples = ['jailBuildExclusions: ["sharp", "@vendor/*"]']

[jailBuildPermissions]
description = "Grant package-specific privileges inside jailed builds."
type = "object"
default = "undefined"
docs = """
Package-pattern map of extra privileges for approved dependency scripts
that still run inside the build jail. Keys use the same package glob
forms as `allowBuilds` (`sharp`, `@scope/*`, `*-native`,
`pkg@1.2.3 || 1.2.4`). Values may grant specific environment variables,
extra readable paths, extra writable paths, or network access.

`env` entries are exact variable names inherited from the parent process.
Use this sparingly: explicit env grants can expose secrets. `write` entries
are added to the native write allowlist on macOS and Linux. `read` entries
are accepted now and reserved for the stricter read-deny profile; reads are
currently unrestricted.
"""
sources.cli = []
sources.env = []
sources.npmrc = []
sources.workspaceYaml = ["jailBuildPermissions"]
examples = ['jailBuildPermissions: { sharp: { env: ["SHARP_DIST_BASE_URL"], write: ["~/.cache/sharp"] } }']
typedAccessorUnused = true

[unsafePerm]
description = "Drop to a non-root user when running scripts as root."
type = "bool"
default = "false (as root), true (otherwise)"
docs = """
aube exports the resolved value to lifecycle and `run` scripts as
`npm_config_unsafe_perm`, matching the environment surface npm-style
script tooling expects. aube does not currently switch users itself.
"""
sources.cli = []
sources.env = ["npm_config_unsafe_perm", "NPM_CONFIG_UNSAFE_PERM", "AUBE_UNSAFE_PERM"]
sources.npmrc = ["unsafePerm"]
examples = []

[nodeOptions]
description = "Options passed to Node.js via NODE_OPTIONS."
type = "string"
default = "null"
docs = """
When set in `.npmrc`, aube exports the value as `NODE_OPTIONS` for
lifecycle scripts and `aube run` scripts. An existing `NODE_OPTIONS`
environment variable is also honored through the same setting path.
"""
sources.cli = []
sources.env = ["npm_config_node_options", "NPM_CONFIG_NODE_OPTIONS", "AUBE_NODE_OPTIONS", "NODE_OPTIONS"]
sources.npmrc = ["nodeOptions"]
examples = []
npmShared = true

[verifyDepsBeforeRun]
description = "Check dependencies before running scripts."
type = '"install" | "warn" | "error" | "prompt" | false'
default = "\"install\""
docs = """
Controls `run`, lifecycle shortcuts, `exec`, and implicit script commands.
`install` preserves aube's auto-install behavior, `warn` reports stale
dependencies without installing, `error` fails, `false` skips the check, and
`prompt` currently behaves like `install` in non-interactive aube.
"""
sources.cli = []
sources.env = ["npm_config_verify_deps_before_run", "NPM_CONFIG_VERIFY_DEPS_BEFORE_RUN", "AUBE_VERIFY_DEPS_BEFORE_RUN"]
sources.npmrc = ["verifyDepsBeforeRun"]
examples = []

[strictDepBuilds]
description = "Exit with an error if dependencies have unreviewed build scripts."
type = "bool"
default = "false"
docs = """
aube never runs dependency lifecycle scripts unless the package is
listed in `allowBuilds` or `--dangerously-allow-all-builds` is set.
With `strictDepBuilds = true`, an install that sees unreviewed build
scripts fails after linking and before any dependency build scripts run.
Add reviewed packages to `allowBuilds` with `true`, keep intentionally
skipped packages as `false`, or leave the default `strictDepBuilds=false`
behavior to skip unreviewed builds.
"""
sources.cli = []
sources.env = ["npm_config_strict_dep_builds", "NPM_CONFIG_STRICT_DEP_BUILDS", "AUBE_STRICT_DEP_BUILDS"]
sources.npmrc = ["strictDepBuilds"]
examples = []

[allowBuilds]
description = "Explicitly allow or disallow script execution per package."
type = "object"
default = "undefined"
docs = """
Per-package review map for dependency lifecycle scripts. Read from
`package.json`'s `pnpm.allowBuilds` field and workspace yaml's
`allowBuilds`. Keys are package name patterns (`esbuild`, `@scope/*`,
`pkg@1.0.0 || 2.0.0`, exact non-registry sources like
`pkg@https://example.com/pkg.tgz`); values are `true` to allow `preinstall` /
`install` / `postinstall` scripts for that package or `false` to record
an intentional skip. Packages not listed are skipped by default, and
install adds unreviewed build packages to workspace `allowBuilds` as
`false` for later review.
"""
sources.cli = []
sources.env = []
sources.npmrc = ["allowBuilds"]
sources.workspaceYaml = ["allowBuilds"]
examples = ['pnpm.allowBuilds: { esbuild: true, "@some/pkg": false }']

[dangerouslyAllowAllBuilds]
description = "Allow all dependency build scripts automatically."
type = "bool"
default = "false"
docs = """
Opt-out escape hatch for the `allowBuilds` allowlist: when set, every
dependency's `preinstall` / `install` / `postinstall` / `prepare`
scripts run regardless of the allowlist. Equivalent to pnpm's
`--dangerously-allow-all-builds`. Useful for ad-hoc debugging; do not
use in CI.
"""
sources.cli = ["dangerously-allow-all-builds"]
sources.env = ["npm_config_dangerously_allow_all_builds", "NPM_CONFIG_DANGEROUSLY_ALLOW_ALL_BUILDS", "AUBE_DANGEROUSLY_ALLOW_ALL_BUILDS"]
sources.npmrc = ["dangerouslyAllowAllBuilds"]
examples = ["aube install --dangerously-allow-all-builds"]
# Plumbed through `InstallArgs::dangerously_allow_all_builds` (clap)
# and then into `aube_scripts::policy::BuildPolicy`, which is where the
# build allowlist actually lives. A typed accessor would add a third
# spelling without simplifying any call site.
typedAccessorUnused = true

# ========================================= Node.js =========================================

[nodeVersion]
description = "Node.js version aube reports when evaluating `engines` checks."
type = "string"
default = "output of `node -v` with the leading `v` stripped"
docs = """
Paired with `engineStrict`. Set this in .npmrc to pin the Node version
engines checks validate against, rather than probing `node --version`
at install time.

This setting is validation-only — it never switches the Node version
scripts actually run on (pnpm semantics). Runtime switching is driven
by `devEngines.runtime` in package.json, `.node-version`, or `.nvmrc`;
when one of those resolves, engines checks validate against the
resolved runtime unless `nodeVersion` overrides the reported version.
"""
sources.cli = []
sources.env = ["npm_config_node_version", "NPM_CONFIG_NODE_VERSION", "AUBE_NODE_VERSION"]
sources.npmrc = ["node-version", "nodeVersion"]
examples = []

[nodeDownloadMirrors]
description = "Custom Node.js download mirror URLs."
type = "object"
default = "undefined"
docs = """
Mirror map for Node.js runtime downloads, keyed like pnpm's setting
(`release` is the only key aube currently uses; `rc`/`nightly` are
parsed for parity). When the project requests a Node version aube has
to download (see `devEngines.runtime` and `runtimeInstaller`), the
`release` mirror replaces `https://nodejs.org/dist`.

```yaml
nodeDownloadMirrors:
  release: https://npmmirror.com/mirrors/node/
```
"""
sources.cli = []
sources.env = []
sources.npmrc = ["nodeDownloadMirrors"]
sources.workspaceYaml = ["nodeDownloadMirrors"]
examples = []

[runtimeInstaller]
description = "Who installs a missing runtime: Node.js versions and, with `managePackageManagerVersions`, pinned aube versions."
type = '"auto" | "mise" | "aube"'
default = "\"auto\""
docs = """
When the project pins a Node version that isn't installed anywhere
aube can see (PATH, mise installs, aube's own runtime dir), this
decides who fetches it:

- `auto` (default): delegate to `mise install node@<version>` when
  `mise` is on PATH — one shared Node store for mise users — and fall
  back to aube's own nodejs.org download otherwise (or when mise
  fails).
- `mise`: always delegate; error (`ERR_AUBE_RUNTIME_MISE_INSTALL_FAILED`)
  if mise is missing or fails.
- `aube`: never delegate; download from nodejs.org (or
  `nodeDownloadMirrors.release`) into `$XDG_DATA_HOME/aube/nodejs/`.

The same policy governs aube *self*-installs when a
`packageManager` / `devEngines.packageManager` pin needs a version
that isn't installed (see `managePackageManagerVersions`): `auto`/`mise`
delegate to `mise install aube@<version>`, `aube` downloads the GitHub
release archive into `$XDG_DATA_HOME/aube/self/`.
"""
sources.cli = []
sources.env = ["npm_config_runtime_installer", "NPM_CONFIG_RUNTIME_INSTALLER", "AUBE_RUNTIME_INSTALLER"]
sources.npmrc = ["runtime-installer", "runtimeInstaller"]
sources.workspaceYaml = ["runtimeInstaller"]
examples = ["echo 'runtime-installer=aube' >> .npmrc"]

[runtimeOnFail]
description = "Override the `onFail` policy applied when the active Node.js doesn't satisfy the project's runtime requirement."
type = '"download" | "error" | "warn" | "ignore"'
default = "undefined"
docs = """
pnpm 11 parity. When unset, `devEngines.runtime`'s own `onFail` field
applies (spec default `error`), and `.node-version`/`.nvmrc` pins —
which have no `onFail` vocabulary — behave as `download`. Setting this
forces one policy everywhere: `error` is the air-gapped-CI "never
download a runtime" switch; `download` makes a bare `devEngines.runtime`
auto-fetch.
"""
sources.cli = []
sources.env = ["npm_config_runtime_on_fail", "NPM_CONFIG_RUNTIME_ON_FAIL", "AUBE_RUNTIME_ON_FAIL"]
sources.npmrc = ["runtime-on-fail", "runtimeOnFail"]
sources.workspaceYaml = ["runtimeOnFail"]
examples = []

# ========================================= Other =========================================

[savePrefix]
description = "Version prefix used when installing a package."
type = '"^" | "~" | ""'
default = "\"^\""
docs = "Resolved from `.npmrc`. `--save-exact` overrides to empty prefix."
sources.cli = []
sources.env = ["npm_config_save_prefix", "NPM_CONFIG_SAVE_PREFIX", "AUBE_SAVE_PREFIX"]
sources.npmrc = ["save-prefix", "savePrefix"]
examples = []

[linkWorkspacePackages]
description = "Resolve `aube add <name>` against local workspace siblings before falling back to the registry."
type = '"false" | "true" | "deep"'
default = "\"false\""
docs = """
When `true` or `"deep"`, `aube add <name>` checks the workspace for
a package whose `name` matches the spec before falling back to the registry.
A match wires the dep up as a workspace link; the manifest specifier
written to `package.json` is controlled by `saveWorkspaceProtocol`.

If the user typed an explicit range (`aube add pkg@^1.2.0`), the
sibling's version must satisfy it — otherwise the spec falls through
to the registry path so an incompatible local copy isn't silently
linked.

Off by default to match pnpm 8+ — opt in via `pnpm-workspace.yaml`
when you want every `aube add` to prefer the local copy of a sibling.
Aube's resolver already prefers workspace siblings on bare semver
ranges at install time, including transitives, so pnpm's `"deep"`
mode is accepted as an alias for the add-time manifest behavior.
"""
sources.cli = []
sources.env = [
    "npm_config_link_workspace_packages",
    "NPM_CONFIG_LINK_WORKSPACE_PACKAGES",
    "AUBE_LINK_WORKSPACE_PACKAGES",
]
sources.npmrc = ["link-workspace-packages", "linkWorkspacePackages"]
sources.workspaceYaml = ["linkWorkspacePackages"]
examples = []

[saveWorkspaceProtocol]
description = "Spec form written to `package.json` when `aube add` resolves against a workspace sibling."
type = '"true" | "false" | "rolling"'
default = "\"rolling\""
docs = """
- `"true"` writes a version-pinned workspace spec (`workspace:^1.0.0`,
  honoring `savePrefix`). The exact lockfile entry never moves
  without an explicit `aube update`.
- `"rolling"` (default) writes the rolling form `workspace:^`
  (or `workspace:~` / `workspace:*` per `savePrefix`). Sibling
  version bumps flow into dependents on the next install without
  re-running `aube add`.
- `"false"` writes a plain registry-style spec (`^1.0.0`). The dep
  is still linked locally on install (controlled by
  `linkWorkspacePackages`), but the manifest looks identical to a
  registry dep.

The `--save-workspace-protocol` / `--no-save-workspace-protocol` CLI
flags on `aube add` override this setting per-invocation.
"""
sources.cli = []
sources.env = [
    "npm_config_save_workspace_protocol",
    "NPM_CONFIG_SAVE_WORKSPACE_PROTOCOL",
    "AUBE_SAVE_WORKSPACE_PROTOCOL",
]
sources.npmrc = ["save-workspace-protocol", "saveWorkspaceProtocol"]
sources.workspaceYaml = ["saveWorkspaceProtocol"]
examples = []

[tag]
description = "Default dist-tag used by `aube add` without a version."
type = "string"
default = "\"latest\""
docs = "Resolved from `.npmrc`. Used by `aube add` when no version or dist-tag is specified."
sources.cli = []
sources.env = ["npm_config_tag", "NPM_CONFIG_TAG", "AUBE_TAG"]
sources.npmrc = ["tag"]
examples = []
npmShared = true

[globalDir]
description = "Directory where globally installed packages live."
type = "path"
default = "platform-specific"
docs = "Overrides the directory where globally installed packages live. Falls back to `AUBE_HOME` / `PNPM_HOME` / platform default."
sources.cli = []
sources.env = ["npm_config_global_dir", "NPM_CONFIG_GLOBAL_DIR", "AUBE_GLOBAL_DIR"]
sources.npmrc = ["globalDir"]
examples = []

[globalBinDir]
description = "Directory where global binaries are symlinked."
type = "path"
default = "platform-specific"
docs = "Overrides the directory where global binaries are symlinked. Independent of `globalDir`; falls back to `AUBE_HOME` / `PNPM_HOME` / platform default."
sources.cli = []
sources.env = ["npm_config_global_bin_dir", "NPM_CONFIG_GLOBAL_BIN_DIR", "AUBE_GLOBAL_BIN_DIR"]
sources.npmrc = ["globalBinDir"]
examples = []

[npmrcAuthFile]
description = "Path to an additional .npmrc file consulted for registry authentication tokens."
type = "path"
default = "undefined"
docs = """
Points at an extra `.npmrc`-formatted file that aube reads *after*
the user and project `.npmrc` files when resolving registry auth.
Anything declared in this file wins, so it's the right home for CI
secrets mounted at a fixed path (e.g. `/run/secrets/npm`) or for a
per-user token override that you don't want to put in `~/.npmrc`.

The setting itself can be declared in either `~/.npmrc` or the
project `.npmrc`. Path interpretation matches pnpm's other path
settings: `~` expands to the user's home directory and a relative
path resolves against the project root.

Implementation: parsed values are appended to the merged entry list
returned by `aube_registry::config::load_npmrc_entries`, so the
auth-token lookup picks them up automatically — no separate loader.
"""
sources.cli = []
sources.env = ["npm_config_npmrc_auth_file", "NPM_CONFIG_NPMRC_AUTH_FILE", "AUBE_NPMRC_AUTH_FILE"]
sources.npmrc = ["npmrc-auth-file", "npmrcAuthFile"]
examples = [
    "echo 'npmrc-auth-file=/run/secrets/npm' >> .npmrc && aube install",
]
# Read by `aube_registry::config::load_npmrc_entries` via a string-keyed
# lookup (see `resolve_npmrc_auth_file_setting` in that module), not via
# the typed `resolved::npmrc_auth_file` accessor, because the load runs
# before a full `ResolveCtx` is available.
typedAccessorUnused = true

[stateDir]
description = "Directory for aube install-state files."
type = "path"
default = "node_modules"
docs = "Overrides the directory that holds the `.aube-state` install-state file. Defaults to the resolved `modulesDir` (usually `node_modules`), so the state file lives at `<modulesDir>/.aube-state` and `rm -rf <modulesDir>` naturally invalidates it."
sources.cli = []
sources.env = ["npm_config_state_dir", "NPM_CONFIG_STATE_DIR", "AUBE_STATE_DIR"]
sources.npmrc = ["stateDir"]
examples = []

[cacheDir]
description = "Directory for package metadata and dlx cache."
type = "path"
default = "~/.cache/aube"
docs = "Overrides the cache directory. `XDG_CACHE_HOME` is honored by the platform default (`aube_store::dirs::cache_dir`) which appends `/aube`; this setting takes a complete path."
sources.cli = []
sources.env = ["npm_config_cache_dir", "NPM_CONFIG_CACHE_DIR", "AUBE_CACHE_DIR"]
sources.npmrc = ["cache-dir", "cacheDir"]
examples = []

[useStderr]
description = "Write all output to stderr instead of stdout."
type = "bool"
default = "false"
docs = "Redirects stdout to stderr for the process lifetime. Resolved from `.npmrc` or the `--use-stderr` CLI flag."
sources.cli = []
sources.env = ["npm_config_use_stderr", "NPM_CONFIG_USE_STDERR", "AUBE_USE_STDERR"]
sources.npmrc = ["useStderr"]
examples = []

[updateNotifier]
description = "Show an update notification when a newer aube is available."
type = "bool"
default = "true"
docs = """
After a successful `install`, `add`, or `update`, aube fetches
`https://aube.jdx.dev/VERSION` and prints a one-line notice if the
advertised version is newer than the running binary. The result is
cached under `<cacheDir>/update-check.json` so only the first run in
any 24h window touches the network. Failures (DNS, timeout, non-200,
unparseable response) are swallowed silently so a network hiccup
never disturbs the install summary. The check is also skipped when
`CI` or `AUBE_NO_UPDATE_CHECK` is set, or when `--offline` /
`--prefer-offline` was requested for the install itself. Set to
`false` to opt out permanently.
"""
sources.cli = []
sources.env = ["npm_config_update_notifier", "NPM_CONFIG_UPDATE_NOTIFIER", "AUBE_UPDATE_NOTIFIER"]
sources.npmrc = ["updateNotifier"]
examples = []
npmShared = true

[updateRewritesSpecifier]
description = "Rewrite caret/tilde manifest specifiers on `aube update` without `--latest`."
type = "bool"
default = "true"
docs = """
When `aube update <pkg>` (no `--latest`) bumps the lockfile to a newer
in-range version, the matching `^X.Y.Z` / `~X.Y.Z` entry in
`package.json` is rewritten to track the new version. Set to `false`
to keep the manifest specifier frozen and only update the lockfile.
Other range shapes (`>=`, `1.x`, exact pins, dist-tags, git, workspace)
are never rewritten by the no-`--latest` path regardless of this setting.
"""
sources.cli = []
sources.env = ["npm_config_update_rewrites_specifier", "NPM_CONFIG_UPDATE_REWRITES_SPECIFIER", "AUBE_UPDATE_REWRITES_SPECIFIER"]
sources.npmrc = ["updateRewritesSpecifier"]
examples = []

[preferSymlinkedExecutables]
description = "Create symlinks instead of shims for `.bin` entries."
type = "bool"
default = "true under `nodeLinker=hoisted`, false otherwise"
docs = """
POSIX only. When unset, defaults to `false` for the standard
isolated layout (`.bin/<name>` is a shell-script shim that exports
`NODE_PATH` covering the project's top-level `node_modules/` and the
hidden `.aube/node_modules/`, so transitives like an auto-installed
`typescript` peer resolve when the bin asks Node for them) and to
`true` under `nodeLinker=hoisted` (every dep is already on the
top-level `node_modules/` walk-up path, so the symlink is enough).
Setting it explicitly overrides that logic. A bare symlink can't
export env vars, so `preferSymlinkedExecutables=true` makes
`extendNodePath` a no-op. Ignored on Windows —
`.bin/<name>.{cmd,ps1,}` wrappers are always written there since real
symlinks require Developer Mode / admin rights.
"""
sources.cli = []
sources.env = ["npm_config_prefer_symlinked_executables", "NPM_CONFIG_PREFER_SYMLINKED_EXECUTABLES", "AUBE_PREFER_SYMLINKED_EXECUTABLES"]
sources.npmrc = ["preferSymlinkedExecutables"]
examples = []

[ignoreCompatibilityDb]
description = "Disable pnpm's automatic dependency patching database."
type = "bool"
default = "false"
docs = """
Accepted for pnpm config parity. pnpm ships a built-in compatibility
database of auto-patches for known-broken packages; aube has no such
database, so this setting has nothing to toggle. Parsed without
warning so shared `.npmrc` files that set it remain portable.
"""
sources.cli = []
sources.env = ["npm_config_ignore_compatibility_db", "NPM_CONFIG_IGNORE_COMPATIBILITY_DB", "AUBE_IGNORE_COMPATIBILITY_DB"]
sources.npmrc = ["ignoreCompatibilityDb"]
examples = []
# Parity no-op: aube has no compatibility database to disable. Remove
# this flag if aube ever ships a patch database.
typedAccessorUnused = true

[resolutionMode]
description = "Dependency version resolution strategy."
type = '"highest" | "time-based" | "lowest-direct"'
default = "\"highest\""
docs = """
Controls how aube chooses versions during resolution. `highest` picks
the newest satisfying version. `time-based` filters candidates through
the lockfile / packument publish-time cutoff before picking. `lowest-direct`
is accepted for pnpm parity and currently maps to the same time-aware
resolver mode.
"""
sources.cli = ["resolution-mode"]
sources.env = ["npm_config_resolution_mode", "NPM_CONFIG_RESOLUTION_MODE", "AUBE_RESOLUTION_MODE"]
sources.npmrc = ["resolution-mode", "resolutionMode"]
examples = []

[registrySupportsTimeField]
description = "Whether the configured registry returns a `time` field in metadata."
type = "bool"
default = "false"
docs = """
When `false` (the default, matching pnpm and npmjs.org's behavior),
aube fetches the full (non-corgi) packument to read the `time:` map
whenever it's needed — that is, under `resolutionMode = time-based` or
when `minimumReleaseAge` is in play. When `true`, aube trusts the
abbreviated (corgi) packument to carry `time:` itself and skips the
extra full-packument fetch, cutting one request per distinct package
on those resolution paths. Safe to enable against registries known to
include `time` in their abbreviated responses — Verdaccio 5.15.1+,
JSR, and most in-house mirrors derived from those — and leave at the
default for npmjs.org. The flag has no effect when neither time-based
resolution nor `minimumReleaseAge` is active, since nothing asks for
`time` on the hot path then.
"""
sources.cli = []
sources.env = ["npm_config_registry_supports_time_field", "NPM_CONFIG_REGISTRY_SUPPORTS_TIME_FIELD", "AUBE_REGISTRY_SUPPORTS_TIME_FIELD"]
sources.npmrc = ["registry-supports-time-field", "registrySupportsTimeField"]
examples = [
    "echo 'registry-supports-time-field=true' >> .npmrc",
]

[forceMetadataPrimer]
description = "Force the bundled metadata primer on for custom registries."
type = "bool"
default = "false"
docs = """
By default aube only uses its bundled npm metadata primer when the
effective registry is npmjs.org, because the primer is generated from
npmjs metadata. Enable this for trusted npm-compatible mirrors and
controlled benchmarks where the mirror serves the same packages but
uses a different registry URL. When forced, aube rewrites primer
tarball URLs to the configured registry before seeding the cache, so
tarball bytes still come from the mirror rather than npmjs.org.
"""
sources.cli = []
sources.env = ["npm_config_force_metadata_primer", "NPM_CONFIG_FORCE_METADATA_PRIMER", "AUBE_FORCE_METADATA_PRIMER"]
sources.npmrc = ["force-metadata-primer", "forceMetadataPrimer"]
examples = [
    "echo 'force-metadata-primer=true' >> .npmrc",
]

[extendNodePath]
description = "Set NODE_PATH in command shims."
type = "bool"
default = "true"
docs = """
When `true` (default), aube-generated `.bin` shims export a
`NODE_PATH` covering the project's top-level `node_modules/` *and*
the hidden `.aube/node_modules/` (when using the isolated linker)
so the shimmed binary can resolve hoisted transitives — e.g. an
auto-installed `typescript` peer — even when invoked from an
unusual working directory. Has no effect on POSIX with
`preferSymlinkedExecutables=true` — only shim scripts can export
env vars. Windows shims always
honor this setting.
"""
sources.cli = []
sources.env = ["npm_config_extend_node_path", "NPM_CONFIG_EXTEND_NODE_PATH", "AUBE_EXTEND_NODE_PATH"]
sources.npmrc = ["extendNodePath"]
examples = []

[deployAllFiles]
description = "Copy all files when deploying a workspace package."
type = "bool"
default = "false"
docs = """
When true, `aube deploy` copies every file in the source workspace
package into the target directory instead of running pack's selection
(the `files` field + `.npmignore` / `.gitignore`). Skips only
filesystem-level cruft that could never be part of a package payload
(`node_modules/`, `.git/`) and the target directory itself when it
sits inside the source. Useful when runtime-needed files (config
fixtures, local scripts, non-published assets) live outside the set
that `npm publish` would ship. Default `false` keeps pack parity so
the deployed tree matches what would be published.
"""
sources.cli = []
sources.env = ["npm_config_deploy_all_files", "NPM_CONFIG_DEPLOY_ALL_FILES", "AUBE_DEPLOY_ALL_FILES"]
sources.npmrc = ["deploy-all-files", "deployAllFiles"]
sources.workspaceYaml = ["deployAllFiles"]
examples = []

[dedupeDirectDeps]
description = "Skip symlinking workspace-root dependencies if identical across packages."
type = "bool"
default = "false"
docs = """
When true, the linker skips creating a `node_modules/<name>` symlink in a
workspace package whose root importer already declares the same workspace
package as a direct dep with the identical version. Reduces symlink churn
in monorepos that ship a single shared version of an internal library.
Only affects the per-importer top-level symlink — cross-importer
`workspace:` resolution keeps working because those still resolve
through the lockfile + root-level tree. No-op under
`node-linker=hoisted` (each importer gets its own flat tree) and under
`virtualStoreOnly=true` (no per-importer symlink pass runs at all).
"""
sources.cli = []
sources.env = ["npm_config_dedupe_direct_deps", "NPM_CONFIG_DEDUPE_DIRECT_DEPS", "AUBE_DEDUPE_DIRECT_DEPS"]
sources.npmrc = ["dedupe-direct-deps", "dedupeDirectDeps"]
sources.workspaceYaml = ["dedupeDirectDeps"]
examples = []

[optimisticRepeatInstall]
description = "Fast-path check before running a full install."
type = "bool"
default = "true"
docs = """
When `true` (default), `aube run` / `aube exec` / `aube start` /
`aube test` / `aube restart` consult `node_modules/.aube-state`
and skip the auto-install if the recorded lockfile + root `package.json`
hashes match the current files. Set `false` to force every auto-install
check to run the full install pipeline — useful when the state file
is out of sync with reality (e.g. manual edits under `node_modules/`)
and you want every command to reconcile. `aube install` itself always
runs its pipeline regardless of this setting.
"""
sources.cli = []
sources.env = ["npm_config_optimistic_repeat_install", "NPM_CONFIG_OPTIMISTIC_REPEAT_INSTALL", "AUBE_OPTIMISTIC_REPEAT_INSTALL"]
sources.npmrc = ["optimisticRepeatInstall"]
examples = []

[requiredScripts]
description = "Scripts that must be present in every workspace project."
type = "list<string>"
default = "undefined"
docs = """
During install, aube verifies that the root package and every discovered
workspace package define each required script in `package.json`.
"""
sources.cli = []
sources.env = ["npm_config_required_scripts", "NPM_CONFIG_REQUIRED_SCRIPTS", "AUBE_REQUIRED_SCRIPTS"]
sources.npmrc = ["requiredScripts"]
examples = []

[enablePrePostScripts]
description = "Run pre/post scripts automatically when a named script is invoked."
type = "bool"
default = "true"
docs = """
Controls whether `aube run build` also runs `prebuild` before `build`
and `postbuild` after it when those scripts exist.
"""
sources.cli = []
sources.env = ["npm_config_enable_pre_post_scripts", "NPM_CONFIG_ENABLE_PRE_POST_SCRIPTS", "AUBE_ENABLE_PRE_POST_SCRIPTS"]
sources.npmrc = ["enablePrePostScripts"]
examples = []

[scriptShell]
description = "Shell used to invoke package scripts."
type = "path"
default = "null (uses /bin/sh on Unix, cmd on Windows)"
docs = """
Overrides the shell executable used for lifecycle and `aube run`
scripts. On Unix, aube invokes the configured shell with `-c`.
"""
sources.cli = []
sources.env = ["npm_config_script_shell", "NPM_CONFIG_SCRIPT_SHELL", "AUBE_SCRIPT_SHELL"]
sources.npmrc = ["scriptShell"]
examples = []

[shellEmulator]
description = "Use a JavaScript bash-like shell to run scripts cross-platform."
type = "bool"
default = "false"
docs = """
Accepted for pnpm config parity. aube does not embed pnpm's JavaScript
shell emulator, but it exports `npm_config_shell_emulator=true` for
scripts when the setting is enabled.
"""
sources.cli = []
sources.env = ["npm_config_shell_emulator", "NPM_CONFIG_SHELL_EMULATOR", "AUBE_SHELL_EMULATOR"]
sources.npmrc = ["shellEmulator"]
examples = []

[catalogMode]
description = "How catalog references in package.json are handled by `add`."
type = '"manual" | "strict" | "prefer"'
default = "\"manual\""
docs = """
`manual` (the default) writes whatever range `aube add` resolved, even
when the package is declared in the default catalog. `prefer` rewrites
the saved specifier to `catalog:` whenever the added package appears in
the default catalog and the user's range is compatible with the catalog
entry (i.e. they didn't ask for something different). `strict` goes
further: if the package is in the default catalog the manifest *always*
gets `catalog:` written, and an explicit `aube add pkg@range` whose
range disagrees with the catalog fails fast instead of silently drifting
from the catalog.

Named catalogs (`catalog:<name>`) are never auto-picked — users still
have to opt in by naming the catalog. Specs written as `npm:` aliases
are also left alone since aliasing and catalog rewrites can't both
apply cleanly.
"""
sources.cli = []
sources.env = ["npm_config_catalog_mode", "NPM_CONFIG_CATALOG_MODE", "AUBE_CATALOG_MODE"]
sources.npmrc = ["catalogMode"]
examples = []

[ci]
description = "Explicitly mark the environment as CI."
type = "bool"
default = "auto-detected"
docs = """
aube detects CI via `env::var("CI").is_ok()` in two places:
`aube-linker` (disables the global virtual store) and
`install::FrozenMode::default_for_env` (flips the default to `Frozen`).
"""
sources.cli = []
sources.env = ["npm_config_ci", "NPM_CONFIG_CI", "AUBE_CI", "CI"]
sources.npmrc = ["ci"]
examples = ["CI=1 aube install"]
# Read via a direct `std::env::var("CI")` check in `aube-linker` and
# `install::FrozenMode::default_for_env` — the typed accessor would
# still boil down to the same env read, but routing it through a
# `ResolveCtx` for a one-liner isn't worth the plumbing.
typedAccessorUnused = true

[cleanupUnusedCatalogs]
description = "Remove unused catalog entries during install."
type = "bool"
default = "false"
docs = """
When enabled, `aube install` rewrites `aube-workspace.yaml` (or
`pnpm-workspace.yaml`, whichever is present) after resolution to drop
entries no importer references. A catalog that ends up empty is
removed entirely. The rewrite is comment- and format-preserving:
yaml comments around surviving entries (and on the rest of the file)
stay intact. yamlpatch's `Remove` op only deletes the line carrying
the entry's `key: value`, so a `# annotation` line above a pruned
entry is left in place rather than guessed-at; clean those up by
hand if you don't want orphaned annotations.
"""
sources.cli = []
sources.env = ["npm_config_cleanup_unused_catalogs", "NPM_CONFIG_CLEANUP_UNUSED_CATALOGS", "AUBE_CLEANUP_UNUSED_CATALOGS"]
sources.npmrc = ["cleanupUnusedCatalogs"]
sources.workspaceYaml = ["cleanupUnusedCatalogs"]
examples = []

# ========================================= aube-specific =========================================
#
# Settings that have no pnpm equivalent. These mostly exist as env-var
# escape hatches for aube's own implementation details.

[linkConcurrency]
description = "Maximum concurrent package materialization/linking tasks."
type = "int"
default = "platform-specific"
docs = """
Caps the dedicated linker worker pool used for filesystem-heavy
materialization in `aube-linker`: creating package directories,
reflinking / hardlinking files, and writing dependency symlinks.
Defaults are platform-aware because APFS reflink metadata work and
Linux hardlink work saturate at different points (currently 4 on
macOS, 16 elsewhere, bounded by available parallelism). Set this when
you know your filesystem prefers a different amount of link-phase
parallelism.
"""
sources.cli = []
sources.env = ["npm_config_link_concurrency", "NPM_CONFIG_LINK_CONCURRENCY", "AUBE_LINK_CONCURRENCY"]
sources.npmrc = ["link-concurrency", "linkConcurrency"]
sources.workspaceYaml = ["linkConcurrency"]
examples = [
  "link-concurrency=8",
  "AUBE_LINK_CONCURRENCY=8 aube install",
]

[aubeNoLock]
description = "Disable aube's project-level advisory lock."
type = "bool"
default = "false"
docs = """
aube takes an advisory lock on `node_modules/` at the start of every
mutating command (install, add, remove, etc.) so concurrent invocations
in the same project serialize cleanly. Set this to a truthy value to
bypass the lock — useful in CI matrices where separate jobs share the
same HOME, or in deliberately-parallel test rigs.

Canonical name is `aubeNoLock` so it can be set from
`aube-workspace.yaml`, `pnpm-workspace.yaml`, or `.npmrc` (as
`aubeNoLock` / `aube-no-lock`). The `AUBE_NO_LOCK` env-var alias
is kept as a convenient shell-export form.

Values are parsed as strict booleans via the shared
`aube_settings::values::parse_bool` rule: `1` / `true` are truthy,
`0` / `false` are explicitly off, and anything else (including
unset, empty string, or arbitrary text) leaves the default
(`false`, i.e. locking stays on).
"""
sources.cli = []
sources.env = ["npm_config_aube_no_lock", "NPM_CONFIG_AUBE_NO_LOCK", "AUBE_NO_LOCK"]
sources.npmrc = ["aubeNoLock", "aube-no-lock"]
sources.workspaceYaml = ["aubeNoLock"]
examples = [
    "AUBE_NO_LOCK=1 aube install",
    "echo 'aubeNoLock=true' >> .npmrc",
]

[aubeNoAutoInstall]
description = "Skip the auto-install staleness check in `aube run` / `aube exec`."
type = "bool"
default = "false"
docs = """
`aube run <script>` normally checks `node_modules/.aube-state` and auto-installs
before running if package.json or the lockfile has drifted. Setting
this to a truthy value skips that check — the same effect as passing
`--no-install` on every invocation. Useful in long-lived dev shells
where you control installs yourself, or in workspace monorepos that
want a consistent policy across every importer.

Canonical name is `aubeNoAutoInstall` so it can be set from
`aube-workspace.yaml`, `pnpm-workspace.yaml`, or `.npmrc` (as
`aubeNoAutoInstall` / `aube-no-auto-install`). The
`AUBE_NO_AUTO_INSTALL` env-var alias is kept as a convenient
shell-export form.

Values are parsed as strict booleans via the shared
`aube_settings::values::parse_bool` rule: `1` / `true` are truthy,
`0` / `false` are explicitly off, and anything else (including
unset, empty string, or arbitrary text) leaves the default
(`false`, i.e. auto-install stays on).
"""
sources.cli = ["no-install"]
sources.env = ["npm_config_aube_no_auto_install", "NPM_CONFIG_AUBE_NO_AUTO_INSTALL", "AUBE_NO_AUTO_INSTALL"]
sources.npmrc = ["aubeNoAutoInstall", "aube-no-auto-install"]
sources.workspaceYaml = ["aubeNoAutoInstall"]
examples = [
    "AUBE_NO_AUTO_INSTALL=1 aube run dev",
    "echo 'aubeNoAutoInstall=true' >> .npmrc",
]