@attest-it/core 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -637,6 +637,522 @@ declare function resolveConfigPaths(config: Config, repoRoot: string): Config;
637
637
  */
638
638
  declare function toAttestItConfig(config: Config): AttestItConfig;
639
639
 
640
+ /**
641
+ * Policy configuration schema and validation for attest-it.
642
+ *
643
+ * Policy files contain trust and security-critical configuration that should
644
+ * be loaded from the default branch to prevent tampering by PR authors.
645
+ *
646
+ * ============================================================================
647
+ * IMPORTANT: DOCUMENTATION SYNC REQUIRED
648
+ * ============================================================================
649
+ * When modifying any schema in this file, you MUST also update:
650
+ *
651
+ * 1. README.md - Update the "Configuration" section's quick example
652
+ * 2. docs/configuration.md - Update the comprehensive configuration reference
653
+ * 3. schemas/policy.schema.json - Run `pnpm --filter @attest-it/core generate:schemas`
654
+ *
655
+ * The configuration format is a key part of the user experience. Any schema
656
+ * changes should be reflected in user-facing documentation.
657
+ * ============================================================================
658
+ */
659
+
660
+ /**
661
+ * Zod schema for the policy configuration file.
662
+ *
663
+ * Policy files define:
664
+ * - Security settings (key paths, attestation storage, max age)
665
+ * - Team members and their public keys
666
+ * - Gates with authorization rules
667
+ *
668
+ * @public
669
+ */
670
+ declare const policySchema: z.ZodObject<{
671
+ version: z.ZodLiteral<1>;
672
+ settings: z.ZodDefault<z.ZodObject<{
673
+ maxAgeDays: z.ZodDefault<z.ZodNumber>;
674
+ publicKeyPath: z.ZodDefault<z.ZodString>;
675
+ attestationsPath: z.ZodDefault<z.ZodString>;
676
+ }, "strict", z.ZodTypeAny, {
677
+ maxAgeDays: number;
678
+ publicKeyPath: string;
679
+ attestationsPath: string;
680
+ }, {
681
+ maxAgeDays?: number | undefined;
682
+ publicKeyPath?: string | undefined;
683
+ attestationsPath?: string | undefined;
684
+ }>>;
685
+ team: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
686
+ name: z.ZodString;
687
+ email: z.ZodOptional<z.ZodString>;
688
+ github: z.ZodOptional<z.ZodString>;
689
+ publicKey: z.ZodString;
690
+ }, "strict", z.ZodTypeAny, {
691
+ name: string;
692
+ publicKey: string;
693
+ email?: string | undefined;
694
+ github?: string | undefined;
695
+ }, {
696
+ name: string;
697
+ publicKey: string;
698
+ email?: string | undefined;
699
+ github?: string | undefined;
700
+ }>>>;
701
+ gates: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
702
+ name: z.ZodString;
703
+ description: z.ZodString;
704
+ authorizedSigners: z.ZodArray<z.ZodString, "many">;
705
+ fingerprint: z.ZodObject<{
706
+ paths: z.ZodArray<z.ZodString, "many">;
707
+ exclude: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
708
+ }, "strict", z.ZodTypeAny, {
709
+ paths: string[];
710
+ exclude?: string[] | undefined;
711
+ }, {
712
+ paths: string[];
713
+ exclude?: string[] | undefined;
714
+ }>;
715
+ maxAge: z.ZodEffects<z.ZodString, string, string>;
716
+ }, "strict", z.ZodTypeAny, {
717
+ name: string;
718
+ description: string;
719
+ authorizedSigners: string[];
720
+ fingerprint: {
721
+ paths: string[];
722
+ exclude?: string[] | undefined;
723
+ };
724
+ maxAge: string;
725
+ }, {
726
+ name: string;
727
+ description: string;
728
+ authorizedSigners: string[];
729
+ fingerprint: {
730
+ paths: string[];
731
+ exclude?: string[] | undefined;
732
+ };
733
+ maxAge: string;
734
+ }>>>;
735
+ }, "strict", z.ZodTypeAny, {
736
+ version: 1;
737
+ settings: {
738
+ maxAgeDays: number;
739
+ publicKeyPath: string;
740
+ attestationsPath: string;
741
+ };
742
+ team?: Record<string, {
743
+ name: string;
744
+ publicKey: string;
745
+ email?: string | undefined;
746
+ github?: string | undefined;
747
+ }> | undefined;
748
+ gates?: Record<string, {
749
+ name: string;
750
+ description: string;
751
+ authorizedSigners: string[];
752
+ fingerprint: {
753
+ paths: string[];
754
+ exclude?: string[] | undefined;
755
+ };
756
+ maxAge: string;
757
+ }> | undefined;
758
+ }, {
759
+ version: 1;
760
+ settings?: {
761
+ maxAgeDays?: number | undefined;
762
+ publicKeyPath?: string | undefined;
763
+ attestationsPath?: string | undefined;
764
+ } | undefined;
765
+ team?: Record<string, {
766
+ name: string;
767
+ publicKey: string;
768
+ email?: string | undefined;
769
+ github?: string | undefined;
770
+ }> | undefined;
771
+ gates?: Record<string, {
772
+ name: string;
773
+ description: string;
774
+ authorizedSigners: string[];
775
+ fingerprint: {
776
+ paths: string[];
777
+ exclude?: string[] | undefined;
778
+ };
779
+ maxAge: string;
780
+ }> | undefined;
781
+ }>;
782
+ /**
783
+ * Policy configuration type inferred from the Zod schema.
784
+ * @public
785
+ */
786
+ type PolicyConfig = z.infer<typeof policySchema>;
787
+ /**
788
+ * Error thrown when policy configuration is invalid.
789
+ * @public
790
+ */
791
+ declare class PolicyValidationError extends Error {
792
+ readonly issues: z.ZodIssue[];
793
+ constructor(message: string, issues: z.ZodIssue[]);
794
+ }
795
+ /**
796
+ * Parse policy configuration content from a string.
797
+ *
798
+ * @param content - The policy file content
799
+ * @param format - The format of the content ('yaml' or 'json')
800
+ * @returns Parsed and validated policy configuration
801
+ * @throws {@link PolicyValidationError} If validation fails
802
+ * @public
803
+ */
804
+ declare function parsePolicyContent(content: string, format: 'yaml' | 'json'): PolicyConfig;
805
+
806
+ /**
807
+ * Operational configuration schema and validation for attest-it.
808
+ *
809
+ * Operational files contain non-security-critical configuration that can
810
+ * be loaded from PR branches (e.g., suite definitions, command execution settings).
811
+ *
812
+ * ============================================================================
813
+ * IMPORTANT: DOCUMENTATION SYNC REQUIRED
814
+ * ============================================================================
815
+ * When modifying any schema in this file, you MUST also update:
816
+ *
817
+ * 1. README.md - Update the "Configuration" section's quick example
818
+ * 2. docs/configuration.md - Update the comprehensive configuration reference
819
+ * 3. schemas/config.schema.json - Run `pnpm --filter @attest-it/core generate:schemas`
820
+ *
821
+ * The configuration format is a key part of the user experience. Any schema
822
+ * changes should be reflected in user-facing documentation.
823
+ * ============================================================================
824
+ */
825
+
826
+ /**
827
+ * Zod schema for the operational configuration file.
828
+ *
829
+ * Operational files define:
830
+ * - Command execution settings (default command, key provider)
831
+ * - Suite definitions with command execution details
832
+ * - Suite groups for organizational purposes
833
+ *
834
+ * @public
835
+ */
836
+ declare const operationalSchema: z.ZodObject<{
837
+ version: z.ZodLiteral<1>;
838
+ settings: z.ZodDefault<z.ZodObject<{
839
+ defaultCommand: z.ZodOptional<z.ZodString>;
840
+ keyProvider: z.ZodOptional<z.ZodObject<{
841
+ type: z.ZodUnion<[z.ZodEnum<["filesystem", "1password"]>, z.ZodString]>;
842
+ options: z.ZodOptional<z.ZodObject<{
843
+ privateKeyPath: z.ZodOptional<z.ZodString>;
844
+ account: z.ZodOptional<z.ZodString>;
845
+ vault: z.ZodOptional<z.ZodString>;
846
+ itemName: z.ZodOptional<z.ZodString>;
847
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
848
+ privateKeyPath: z.ZodOptional<z.ZodString>;
849
+ account: z.ZodOptional<z.ZodString>;
850
+ vault: z.ZodOptional<z.ZodString>;
851
+ itemName: z.ZodOptional<z.ZodString>;
852
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
853
+ privateKeyPath: z.ZodOptional<z.ZodString>;
854
+ account: z.ZodOptional<z.ZodString>;
855
+ vault: z.ZodOptional<z.ZodString>;
856
+ itemName: z.ZodOptional<z.ZodString>;
857
+ }, z.ZodTypeAny, "passthrough">>>;
858
+ }, "strict", z.ZodTypeAny, {
859
+ type: string;
860
+ options?: z.objectOutputType<{
861
+ privateKeyPath: z.ZodOptional<z.ZodString>;
862
+ account: z.ZodOptional<z.ZodString>;
863
+ vault: z.ZodOptional<z.ZodString>;
864
+ itemName: z.ZodOptional<z.ZodString>;
865
+ }, z.ZodTypeAny, "passthrough"> | undefined;
866
+ }, {
867
+ type: string;
868
+ options?: z.objectInputType<{
869
+ privateKeyPath: z.ZodOptional<z.ZodString>;
870
+ account: z.ZodOptional<z.ZodString>;
871
+ vault: z.ZodOptional<z.ZodString>;
872
+ itemName: z.ZodOptional<z.ZodString>;
873
+ }, z.ZodTypeAny, "passthrough"> | undefined;
874
+ }>>;
875
+ }, "strict", z.ZodTypeAny, {
876
+ defaultCommand?: string | undefined;
877
+ keyProvider?: {
878
+ type: string;
879
+ options?: z.objectOutputType<{
880
+ privateKeyPath: z.ZodOptional<z.ZodString>;
881
+ account: z.ZodOptional<z.ZodString>;
882
+ vault: z.ZodOptional<z.ZodString>;
883
+ itemName: z.ZodOptional<z.ZodString>;
884
+ }, z.ZodTypeAny, "passthrough"> | undefined;
885
+ } | undefined;
886
+ }, {
887
+ defaultCommand?: string | undefined;
888
+ keyProvider?: {
889
+ type: string;
890
+ options?: z.objectInputType<{
891
+ privateKeyPath: z.ZodOptional<z.ZodString>;
892
+ account: z.ZodOptional<z.ZodString>;
893
+ vault: z.ZodOptional<z.ZodString>;
894
+ itemName: z.ZodOptional<z.ZodString>;
895
+ }, z.ZodTypeAny, "passthrough"> | undefined;
896
+ } | undefined;
897
+ }>>;
898
+ suites: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodEffects<z.ZodObject<{
899
+ gate: z.ZodOptional<z.ZodString>;
900
+ description: z.ZodOptional<z.ZodString>;
901
+ packages: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
902
+ files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
903
+ ignore: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
904
+ command: z.ZodOptional<z.ZodString>;
905
+ timeout: z.ZodOptional<z.ZodString>;
906
+ interactive: z.ZodOptional<z.ZodBoolean>;
907
+ invalidates: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
908
+ depends_on: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
909
+ }, "strict", z.ZodTypeAny, {
910
+ description?: string | undefined;
911
+ gate?: string | undefined;
912
+ packages?: string[] | undefined;
913
+ files?: string[] | undefined;
914
+ ignore?: string[] | undefined;
915
+ command?: string | undefined;
916
+ timeout?: string | undefined;
917
+ interactive?: boolean | undefined;
918
+ invalidates?: string[] | undefined;
919
+ depends_on?: string[] | undefined;
920
+ }, {
921
+ description?: string | undefined;
922
+ gate?: string | undefined;
923
+ packages?: string[] | undefined;
924
+ files?: string[] | undefined;
925
+ ignore?: string[] | undefined;
926
+ command?: string | undefined;
927
+ timeout?: string | undefined;
928
+ interactive?: boolean | undefined;
929
+ invalidates?: string[] | undefined;
930
+ depends_on?: string[] | undefined;
931
+ }>, {
932
+ description?: string | undefined;
933
+ gate?: string | undefined;
934
+ packages?: string[] | undefined;
935
+ files?: string[] | undefined;
936
+ ignore?: string[] | undefined;
937
+ command?: string | undefined;
938
+ timeout?: string | undefined;
939
+ interactive?: boolean | undefined;
940
+ invalidates?: string[] | undefined;
941
+ depends_on?: string[] | undefined;
942
+ }, {
943
+ description?: string | undefined;
944
+ gate?: string | undefined;
945
+ packages?: string[] | undefined;
946
+ files?: string[] | undefined;
947
+ ignore?: string[] | undefined;
948
+ command?: string | undefined;
949
+ timeout?: string | undefined;
950
+ interactive?: boolean | undefined;
951
+ invalidates?: string[] | undefined;
952
+ depends_on?: string[] | undefined;
953
+ }>>, Record<string, {
954
+ description?: string | undefined;
955
+ gate?: string | undefined;
956
+ packages?: string[] | undefined;
957
+ files?: string[] | undefined;
958
+ ignore?: string[] | undefined;
959
+ command?: string | undefined;
960
+ timeout?: string | undefined;
961
+ interactive?: boolean | undefined;
962
+ invalidates?: string[] | undefined;
963
+ depends_on?: string[] | undefined;
964
+ }>, Record<string, {
965
+ description?: string | undefined;
966
+ gate?: string | undefined;
967
+ packages?: string[] | undefined;
968
+ files?: string[] | undefined;
969
+ ignore?: string[] | undefined;
970
+ command?: string | undefined;
971
+ timeout?: string | undefined;
972
+ interactive?: boolean | undefined;
973
+ invalidates?: string[] | undefined;
974
+ depends_on?: string[] | undefined;
975
+ }>>;
976
+ groups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
977
+ }, "strict", z.ZodTypeAny, {
978
+ version: 1;
979
+ settings: {
980
+ defaultCommand?: string | undefined;
981
+ keyProvider?: {
982
+ type: string;
983
+ options?: z.objectOutputType<{
984
+ privateKeyPath: z.ZodOptional<z.ZodString>;
985
+ account: z.ZodOptional<z.ZodString>;
986
+ vault: z.ZodOptional<z.ZodString>;
987
+ itemName: z.ZodOptional<z.ZodString>;
988
+ }, z.ZodTypeAny, "passthrough"> | undefined;
989
+ } | undefined;
990
+ };
991
+ suites: Record<string, {
992
+ description?: string | undefined;
993
+ gate?: string | undefined;
994
+ packages?: string[] | undefined;
995
+ files?: string[] | undefined;
996
+ ignore?: string[] | undefined;
997
+ command?: string | undefined;
998
+ timeout?: string | undefined;
999
+ interactive?: boolean | undefined;
1000
+ invalidates?: string[] | undefined;
1001
+ depends_on?: string[] | undefined;
1002
+ }>;
1003
+ groups?: Record<string, string[]> | undefined;
1004
+ }, {
1005
+ version: 1;
1006
+ suites: Record<string, {
1007
+ description?: string | undefined;
1008
+ gate?: string | undefined;
1009
+ packages?: string[] | undefined;
1010
+ files?: string[] | undefined;
1011
+ ignore?: string[] | undefined;
1012
+ command?: string | undefined;
1013
+ timeout?: string | undefined;
1014
+ interactive?: boolean | undefined;
1015
+ invalidates?: string[] | undefined;
1016
+ depends_on?: string[] | undefined;
1017
+ }>;
1018
+ settings?: {
1019
+ defaultCommand?: string | undefined;
1020
+ keyProvider?: {
1021
+ type: string;
1022
+ options?: z.objectInputType<{
1023
+ privateKeyPath: z.ZodOptional<z.ZodString>;
1024
+ account: z.ZodOptional<z.ZodString>;
1025
+ vault: z.ZodOptional<z.ZodString>;
1026
+ itemName: z.ZodOptional<z.ZodString>;
1027
+ }, z.ZodTypeAny, "passthrough"> | undefined;
1028
+ } | undefined;
1029
+ } | undefined;
1030
+ groups?: Record<string, string[]> | undefined;
1031
+ }>;
1032
+ /**
1033
+ * Operational configuration type inferred from the Zod schema.
1034
+ * @public
1035
+ */
1036
+ type OperationalConfig = z.infer<typeof operationalSchema>;
1037
+ /**
1038
+ * Error thrown when operational configuration is invalid.
1039
+ * @public
1040
+ */
1041
+ declare class OperationalValidationError extends Error {
1042
+ readonly issues: z.ZodIssue[];
1043
+ constructor(message: string, issues: z.ZodIssue[]);
1044
+ }
1045
+ /**
1046
+ * Parse operational configuration content from a string.
1047
+ *
1048
+ * @param content - The operational config file content
1049
+ * @param format - The format of the content ('yaml' or 'json')
1050
+ * @returns Parsed and validated operational configuration
1051
+ * @throws {@link OperationalValidationError} If validation fails
1052
+ * @public
1053
+ */
1054
+ declare function parseOperationalContent(content: string, format: 'yaml' | 'json'): OperationalConfig;
1055
+
1056
+ /**
1057
+ * Merge policy and operational configurations into a unified AttestItConfig.
1058
+ *
1059
+ * This module handles the combination of security-critical policy configuration
1060
+ * (loaded from the default branch) with operational configuration (can be loaded
1061
+ * from PR branches) to create the complete configuration used for attestation
1062
+ * verification.
1063
+ *
1064
+ * @packageDocumentation
1065
+ */
1066
+
1067
+ /**
1068
+ * Merges policy and operational configurations into a single AttestItConfig.
1069
+ *
1070
+ * The merge strategy prioritizes security-critical fields from the policy
1071
+ * configuration while combining operational fields from both sources:
1072
+ *
1073
+ * - **Policy settings** (maxAgeDays, publicKeyPath, attestationsPath) are used as-is
1074
+ * - **Operational settings** (defaultCommand, keyProvider) are added from operational config
1075
+ * - **Team and gates** come exclusively from policy config
1076
+ * - **Suites and groups** come exclusively from operational config
1077
+ *
1078
+ * @param policy - The policy configuration containing security-critical settings
1079
+ * @param operational - The operational configuration containing suites and execution settings
1080
+ * @returns A complete AttestItConfig ready for use in attestation operations
1081
+ *
1082
+ * @example
1083
+ * ```typescript
1084
+ * const policy = parsePolicyContent(policyYaml, 'yaml')
1085
+ * const operational = parseOperationalContent(operationalYaml, 'yaml')
1086
+ * const config = mergeConfigs(policy, operational)
1087
+ * ```
1088
+ *
1089
+ * @public
1090
+ */
1091
+ declare function mergeConfigs(policy: PolicyConfig, operational: OperationalConfig): AttestItConfig;
1092
+
1093
+ /**
1094
+ * Cross-configuration validation for split policy and operational configs.
1095
+ *
1096
+ * This module validates relationships between policy and operational configurations,
1097
+ * ensuring that suite-gate references are valid and that all authorized signers
1098
+ * are defined in the team.
1099
+ *
1100
+ * @packageDocumentation
1101
+ */
1102
+
1103
+ /**
1104
+ * Validation error types for cross-configuration validation.
1105
+ * @public
1106
+ */
1107
+ type ValidationErrorType = 'UNKNOWN_GATE' | 'MISSING_TEAM_MEMBER';
1108
+ /**
1109
+ * Represents a validation error found during cross-configuration validation.
1110
+ * @public
1111
+ */
1112
+ interface ValidationError {
1113
+ /** The type of validation error */
1114
+ type: ValidationErrorType;
1115
+ /** The suite name where the error was found (if applicable) */
1116
+ suite?: string;
1117
+ /** The gate name involved in the error (if applicable) */
1118
+ gate?: string;
1119
+ /** The signer slug that is missing (if applicable) */
1120
+ signer?: string;
1121
+ /** Human-readable error message explaining the issue */
1122
+ message: string;
1123
+ }
1124
+ /**
1125
+ * Validates that all suite-gate references and authorized signers are valid.
1126
+ *
1127
+ * This function performs cross-configuration validation to ensure:
1128
+ * 1. Every suite that references a gate refers to an existing gate in the policy
1129
+ * 2. Every authorized signer in each referenced gate is defined in the policy team
1130
+ *
1131
+ * These validations are critical because:
1132
+ * - Operational config (suites) can come from PR branches
1133
+ * - Policy config (gates, team) comes from the default branch
1134
+ * - We must ensure PR authors cannot reference non-existent gates or signers
1135
+ *
1136
+ * @param policy - The policy configuration containing gates and team definitions
1137
+ * @param operational - The operational configuration containing suite definitions
1138
+ * @returns An array of validation errors (empty if validation passes)
1139
+ *
1140
+ * @example
1141
+ * ```typescript
1142
+ * const errors = validateSuiteGateReferences(policy, operational)
1143
+ * if (errors.length > 0) {
1144
+ * console.error('Validation failed:')
1145
+ * for (const error of errors) {
1146
+ * console.error(` - ${error.message}`)
1147
+ * }
1148
+ * throw new Error('Configuration validation failed')
1149
+ * }
1150
+ * ```
1151
+ *
1152
+ * @public
1153
+ */
1154
+ declare function validateSuiteGateReferences(policy: PolicyConfig, operational: OperationalConfig): ValidationError[];
1155
+
640
1156
  /**
641
1157
  * Options for computing a package fingerprint.
642
1158
  * @public
@@ -1462,6 +1978,165 @@ declare class MacOSKeychainKeyProvider implements KeyProvider {
1462
1978
  getConfig(): KeyProviderConfig;
1463
1979
  }
1464
1980
 
1981
+ /**
1982
+ * YubiKey-based key provider implementation.
1983
+ *
1984
+ * @remarks
1985
+ * This provider stores private keys encrypted with a key derived from YubiKey
1986
+ * HMAC-SHA1 challenge-response. The key cannot be decrypted without the physical
1987
+ * YubiKey present. Uses HKDF to derive an AES-256-GCM encryption key from the
1988
+ * challenge-response output.
1989
+ *
1990
+ * Requires the `ykman` (YubiKey Manager) CLI tool to be installed.
1991
+ *
1992
+ * **Security Note**: Private keys are temporarily held in memory as JavaScript
1993
+ * strings during encryption/decryption. JavaScript strings are immutable and
1994
+ * cannot be securely zeroed. The key remains in memory until garbage collected.
1995
+ * For maximum security, use full-disk encryption and disable swap on systems
1996
+ * handling sensitive keys.
1997
+ *
1998
+ * @packageDocumentation
1999
+ */
2000
+
2001
+ /**
2002
+ * Options for creating a YubiKeyProvider.
2003
+ * @public
2004
+ */
2005
+ interface YubiKeyProviderOptions {
2006
+ /** Path to the encrypted key file */
2007
+ encryptedKeyPath: string;
2008
+ /** YubiKey slot to use for challenge-response (default: 2) */
2009
+ slot?: 1 | 2;
2010
+ /** Serial number of specific YubiKey to use (optional but recommended) */
2011
+ serial?: string;
2012
+ }
2013
+ /**
2014
+ * Information about a connected YubiKey.
2015
+ * @public
2016
+ */
2017
+ interface YubiKeyInfo {
2018
+ /** Device serial number */
2019
+ serial: string;
2020
+ /** Device type (e.g., "YubiKey 5 NFC") */
2021
+ type: string;
2022
+ /** Firmware version */
2023
+ firmware: string;
2024
+ }
2025
+ /**
2026
+ * Key provider that encrypts private keys using YubiKey HMAC challenge-response.
2027
+ *
2028
+ * @remarks
2029
+ * This provider uses the YubiKey HMAC-SHA1 challenge-response feature (typically slot 2)
2030
+ * to derive an encryption key. The Ed25519 private key is encrypted with AES-256-GCM,
2031
+ * and can only be decrypted when the correct YubiKey is present.
2032
+ *
2033
+ * This approach:
2034
+ * - Works with all YubiKeys that support HMAC-SHA1 challenge-response
2035
+ * - Preserves Ed25519 compatibility (signing happens in software)
2036
+ * - Requires physical YubiKey presence to decrypt and use the key
2037
+ *
2038
+ * **Security Note**: Always use the `serial` option to bind keys to a specific YubiKey.
2039
+ * Without serial verification, any YubiKey with the same HMAC secret could decrypt the key.
2040
+ *
2041
+ * @public
2042
+ */
2043
+ declare class YubiKeyProvider implements KeyProvider {
2044
+ readonly type = "yubikey";
2045
+ readonly displayName = "YubiKey";
2046
+ private readonly encryptedKeyPath;
2047
+ private readonly slot;
2048
+ private readonly serial?;
2049
+ /**
2050
+ * Create a new YubiKeyProvider.
2051
+ * @param options - Provider options
2052
+ * @throws Error if encryptedKeyPath is outside the attest-it config directory
2053
+ */
2054
+ constructor(options: YubiKeyProviderOptions);
2055
+ /**
2056
+ * Check if ykman CLI is installed and available.
2057
+ * @returns true if ykman is available
2058
+ */
2059
+ static isInstalled(): Promise<boolean>;
2060
+ /**
2061
+ * Check if any YubiKey is connected.
2062
+ * @returns true if at least one YubiKey is connected
2063
+ */
2064
+ static isConnected(): Promise<boolean>;
2065
+ /**
2066
+ * Check if HMAC challenge-response is configured on a slot.
2067
+ * @param slot - Slot number (1 or 2)
2068
+ * @param serial - Optional YubiKey serial number
2069
+ * @returns true if challenge-response is configured
2070
+ */
2071
+ static isChallengeResponseConfigured(slot?: 1 | 2, serial?: string): Promise<boolean>;
2072
+ /**
2073
+ * List connected YubiKeys.
2074
+ * @returns Array of YubiKey information
2075
+ */
2076
+ static listDevices(): Promise<YubiKeyInfo[]>;
2077
+ /**
2078
+ * Check if this provider is available on the current system.
2079
+ * Requires ykman to be installed.
2080
+ */
2081
+ isAvailable(): Promise<boolean>;
2082
+ /**
2083
+ * Check if an encrypted key file exists.
2084
+ * @param keyRef - Path to encrypted key file
2085
+ */
2086
+ keyExists(keyRef: string): Promise<boolean>;
2087
+ /**
2088
+ * Get the private key by decrypting with YubiKey.
2089
+ * Downloads to a temporary file and returns a cleanup function.
2090
+ *
2091
+ * **Important**: Always call the cleanup function when done to securely delete
2092
+ * the temporary key file. The cleanup is also registered for process exit handlers.
2093
+ *
2094
+ * @param keyRef - Path to encrypted key file
2095
+ * @throws Error if the key cannot be decrypted
2096
+ */
2097
+ getPrivateKey(keyRef: string): Promise<KeyRetrievalResult>;
2098
+ /**
2099
+ * Generate a new keypair and store encrypted with YubiKey.
2100
+ * Public key is written to filesystem for repository commit.
2101
+ *
2102
+ * **Security Note**: Always specify a serial number to bind the key to a specific YubiKey.
2103
+ *
2104
+ * @param options - Key generation options
2105
+ */
2106
+ generateKeyPair(options: KeygenProviderOptions): Promise<KeyGenerationResult>;
2107
+ /**
2108
+ * Encrypt an existing private key with YubiKey challenge-response.
2109
+ *
2110
+ * @remarks
2111
+ * This static method allows encrypting a private key that was generated
2112
+ * elsewhere (e.g., by the CLI) without having to create a provider instance first.
2113
+ *
2114
+ * **Security Note**: Always specify a serial number to bind the key to a specific YubiKey.
2115
+ * The serial provides defense-in-depth by ensuring only the intended YubiKey can decrypt.
2116
+ *
2117
+ * @param options - Encryption options
2118
+ * @returns Path to the encrypted key file and storage description
2119
+ * @public
2120
+ */
2121
+ static encryptPrivateKey(options: {
2122
+ /** The private key content (PEM format) */
2123
+ privateKey: string;
2124
+ /** Path where the encrypted key file will be saved */
2125
+ encryptedKeyPath: string;
2126
+ /** YubiKey slot to use (default: 2) */
2127
+ slot?: 1 | 2;
2128
+ /** YubiKey serial number (recommended for security) */
2129
+ serial?: string;
2130
+ }): Promise<{
2131
+ encryptedKeyPath: string;
2132
+ storageDescription: string;
2133
+ }>;
2134
+ /**
2135
+ * Get the configuration for this provider.
2136
+ */
2137
+ getConfig(): KeyProviderConfig;
2138
+ }
2139
+
1465
2140
  /**
1466
2141
  * Registry for key provider implementations.
1467
2142
  *
@@ -1531,6 +2206,11 @@ type PrivateKeyRef = {
1531
2206
  vault: string;
1532
2207
  item: string;
1533
2208
  field?: string;
2209
+ } | {
2210
+ type: 'yubikey';
2211
+ encryptedKeyPath: string;
2212
+ slot?: 1 | 2;
2213
+ serial?: string;
1534
2214
  };
1535
2215
  /**
1536
2216
  * A single identity configuration.
@@ -1559,6 +2239,68 @@ interface LocalConfig {
1559
2239
  identities: Record<string, Identity>;
1560
2240
  }
1561
2241
 
2242
+ /**
2243
+ * User preferences management for attest-it.
2244
+ * Stored separately from identity config to allow preferences
2245
+ * before any identity exists.
2246
+ * @packageDocumentation
2247
+ */
2248
+ /**
2249
+ * CLI experience preferences (UX-related settings).
2250
+ * @public
2251
+ */
2252
+ interface CliExperiencePreferences {
2253
+ /** Whether the user has declined shell completion installation */
2254
+ declinedCompletionInstall?: boolean;
2255
+ }
2256
+ /**
2257
+ * User preferences stored in ~/.config/attest-it/preferences.yaml
2258
+ * @public
2259
+ */
2260
+ interface UserPreferences {
2261
+ /** CLI experience and UX settings */
2262
+ cliExperience?: CliExperiencePreferences;
2263
+ }
2264
+ /**
2265
+ * Get the path to the preferences file.
2266
+ *
2267
+ * @returns Path to the preferences file
2268
+ * @public
2269
+ */
2270
+ declare function getPreferencesPath(): string;
2271
+ /**
2272
+ * Load user preferences from file.
2273
+ * Validates the file contents against the schema and returns
2274
+ * only valid, known preferences.
2275
+ *
2276
+ * @returns User preferences, or empty object if file doesn't exist
2277
+ * @public
2278
+ */
2279
+ declare function loadPreferences(): Promise<UserPreferences>;
2280
+ /**
2281
+ * Save user preferences to file.
2282
+ *
2283
+ * @param preferences - Preferences to save
2284
+ * @public
2285
+ */
2286
+ declare function savePreferences(preferences: UserPreferences): Promise<void>;
2287
+ /**
2288
+ * Update a single preference value.
2289
+ *
2290
+ * @param key - Preference key to update
2291
+ * @param value - New value
2292
+ * @public
2293
+ */
2294
+ declare function setPreference<K extends keyof UserPreferences>(key: K, value: UserPreferences[K]): Promise<void>;
2295
+ /**
2296
+ * Get a single preference value.
2297
+ *
2298
+ * @param key - Preference key to get
2299
+ * @returns Preference value, or undefined if not set
2300
+ * @public
2301
+ */
2302
+ declare function getPreference<K extends keyof UserPreferences>(key: K): Promise<UserPreferences[K] | undefined>;
2303
+
1562
2304
  /**
1563
2305
  * Configuration loading for local identity system.
1564
2306
  * @packageDocumentation
@@ -1879,4 +2621,4 @@ declare function verifyAllSeals(config: AttestItConfig, seals: SealsFile, finger
1879
2621
  */
1880
2622
  declare const version = "0.0.0";
1881
2623
 
1882
- export { type AttestItConfig, type AttestItSettings, type Attestation, type AttestationsFile, type Config, ConfigNotFoundError, ConfigValidationError, type CreateSealOptions, type VerifyOptions$1 as CryptoVerifyOptions, type KeyPair as Ed25519KeyPair, FilesystemKeyProvider, type FilesystemKeyProviderOptions, type FingerprintConfig, type FingerprintOptions, type FingerprintResult, type GateConfig, type Identity, type KeyGenerationResult, type KeyPaths, type KeyProvider, type KeyProviderConfig, type KeyProviderFactory, KeyProviderRegistry, type KeyProviderSettings, type KeyRetrievalResult, type KeygenOptions, type KeygenProviderOptions, type LocalConfig, LocalConfigValidationError, type MacOSKeychain, MacOSKeychainKeyProvider, type MacOSKeychainKeyProviderOptions, type OnePasswordAccount, OnePasswordKeyProvider, type OnePasswordKeyProviderOptions, type OnePasswordVault, type PrivateKeyRef, type ReadSignedAttestationsOptions, type Seal, type SealVerificationResult, type SealsFile, type SignOptions, SignatureInvalidError, type SignatureVerificationResult, type SuiteConfig, type SuiteVerificationResult, type TeamMember, type VerificationState, type VerificationStatus, type VerifyOptions, type VerifyResult, type WriteSignedAttestationsOptions, canonicalizeAttestations, checkOpenSSL, computeFingerprint, computeFingerprintSync, createAttestation, createSeal, findAttestation, findConfigPath, findTeamMemberByPublicKey, generateKeyPair as generateEd25519KeyPair, generateKeyPair$1 as generateKeyPair, getActiveIdentity, getAttestItConfigDir, getAttestItHomeDir, getAuthorizedSignersForGate, getDefaultPrivateKeyPath, getDefaultPublicKeyPath, getGate, getLocalConfigPath, getPublicKeyFromPrivate, isAuthorizedSigner, listPackageFiles, loadConfig, loadConfigSync, loadLocalConfig, loadLocalConfigSync, parseDuration, readAndVerifyAttestations, readAttestations, readAttestationsSync, readSeals, readSealsSync, removeAttestation, resolveConfigPaths, saveLocalConfig, saveLocalConfigSync, setAttestItHomeDir, setKeyPermissions, sign$1 as sign, sign as signEd25519, toAttestItConfig, upsertAttestation, verify$1 as verify, verifyAllSeals, verifyAttestations, verify as verifyEd25519, verifyGateSeal, verifySeal, version, writeAttestations, writeAttestationsSync, writeSeals, writeSealsSync, writeSignedAttestations };
2624
+ export { type AttestItConfig, type AttestItSettings, type Attestation, type AttestationsFile, type CliExperiencePreferences, type Config, ConfigNotFoundError, ConfigValidationError, type CreateSealOptions, type VerifyOptions$1 as CryptoVerifyOptions, type KeyPair as Ed25519KeyPair, FilesystemKeyProvider, type FilesystemKeyProviderOptions, type FingerprintConfig, type FingerprintOptions, type FingerprintResult, type GateConfig, type Identity, type KeyGenerationResult, type KeyPaths, type KeyProvider, type KeyProviderConfig, type KeyProviderFactory, KeyProviderRegistry, type KeyProviderSettings, type KeyRetrievalResult, type KeygenOptions, type KeygenProviderOptions, type LocalConfig, LocalConfigValidationError, type MacOSKeychain, MacOSKeychainKeyProvider, type MacOSKeychainKeyProviderOptions, type OnePasswordAccount, OnePasswordKeyProvider, type OnePasswordKeyProviderOptions, type OnePasswordVault, type OperationalConfig, OperationalValidationError, type PolicyConfig, PolicyValidationError, type PrivateKeyRef, type ReadSignedAttestationsOptions, type Seal, type SealVerificationResult, type SealsFile, type SignOptions, SignatureInvalidError, type SignatureVerificationResult, type SuiteConfig, type SuiteVerificationResult, type TeamMember, type UserPreferences, type ValidationError, type ValidationErrorType, type VerificationState, type VerificationStatus, type VerifyOptions, type VerifyResult, type WriteSignedAttestationsOptions, type YubiKeyInfo, YubiKeyProvider, type YubiKeyProviderOptions, canonicalizeAttestations, checkOpenSSL, computeFingerprint, computeFingerprintSync, createAttestation, createSeal, findAttestation, findConfigPath, findTeamMemberByPublicKey, generateKeyPair as generateEd25519KeyPair, generateKeyPair$1 as generateKeyPair, getActiveIdentity, getAttestItConfigDir, getAttestItHomeDir, getAuthorizedSignersForGate, getDefaultPrivateKeyPath, getDefaultPublicKeyPath, getGate, getLocalConfigPath, getPreference, getPreferencesPath, getPublicKeyFromPrivate, isAuthorizedSigner, listPackageFiles, loadConfig, loadConfigSync, loadLocalConfig, loadLocalConfigSync, loadPreferences, mergeConfigs, operationalSchema, parseDuration, parseOperationalContent, parsePolicyContent, policySchema, readAndVerifyAttestations, readAttestations, readAttestationsSync, readSeals, readSealsSync, removeAttestation, resolveConfigPaths, saveLocalConfig, saveLocalConfigSync, savePreferences, setAttestItHomeDir, setKeyPermissions, setPreference, sign$1 as sign, sign as signEd25519, toAttestItConfig, upsertAttestation, validateSuiteGateReferences, verify$1 as verify, verifyAllSeals, verifyAttestations, verify as verifyEd25519, verifyGateSeal, verifySeal, version, writeAttestations, writeAttestationsSync, writeSeals, writeSealsSync, writeSignedAttestations };