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