@auths-dev/sdk 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/Cargo.toml +45 -0
  2. package/README.md +163 -4
  3. package/__test__/client.spec.ts +78 -0
  4. package/__test__/exports.spec.ts +57 -0
  5. package/__test__/integration.spec.ts +407 -0
  6. package/__test__/policy.spec.ts +202 -0
  7. package/__test__/verify.spec.ts +88 -0
  8. package/build.rs +5 -0
  9. package/index.d.ts +259 -0
  10. package/index.js +622 -1
  11. package/lib/artifacts.ts +124 -0
  12. package/lib/attestations.ts +126 -0
  13. package/lib/audit.ts +189 -0
  14. package/lib/client.ts +293 -0
  15. package/lib/commits.ts +70 -0
  16. package/lib/devices.ts +178 -0
  17. package/lib/errors.ts +306 -0
  18. package/lib/identity.ts +280 -0
  19. package/lib/index.ts +125 -0
  20. package/lib/native.ts +255 -0
  21. package/lib/org.ts +235 -0
  22. package/lib/pairing.ts +271 -0
  23. package/lib/policy.ts +669 -0
  24. package/lib/signing.ts +204 -0
  25. package/lib/trust.ts +152 -0
  26. package/lib/types.ts +179 -0
  27. package/lib/verify.ts +241 -0
  28. package/lib/witness.ts +91 -0
  29. package/npm/darwin-arm64/README.md +3 -0
  30. package/npm/darwin-arm64/package.json +23 -0
  31. package/npm/linux-arm64-gnu/README.md +3 -0
  32. package/npm/linux-arm64-gnu/package.json +26 -0
  33. package/npm/linux-x64-gnu/README.md +3 -0
  34. package/npm/linux-x64-gnu/package.json +26 -0
  35. package/npm/win32-arm64-msvc/README.md +3 -0
  36. package/npm/win32-arm64-msvc/package.json +23 -0
  37. package/npm/win32-x64-msvc/README.md +3 -0
  38. package/npm/win32-x64-msvc/package.json +23 -0
  39. package/package.json +51 -16
  40. package/src/artifact.rs +217 -0
  41. package/src/attestation_query.rs +104 -0
  42. package/src/audit.rs +128 -0
  43. package/src/commit_sign.rs +63 -0
  44. package/src/device.rs +212 -0
  45. package/src/diagnostics.rs +106 -0
  46. package/src/error.rs +5 -0
  47. package/src/helpers.rs +60 -0
  48. package/src/identity.rs +467 -0
  49. package/src/lib.rs +26 -0
  50. package/src/org.rs +430 -0
  51. package/src/pairing.rs +454 -0
  52. package/src/policy.rs +147 -0
  53. package/src/sign.rs +215 -0
  54. package/src/trust.rs +189 -0
  55. package/src/types.rs +205 -0
  56. package/src/verify.rs +447 -0
  57. package/src/witness.rs +138 -0
  58. package/tsconfig.json +19 -0
  59. package/typedoc.json +18 -0
  60. package/vitest.config.ts +12 -0
@@ -0,0 +1,467 @@
1
+ use std::path::PathBuf;
2
+ use std::sync::Arc;
3
+
4
+ use auths_core::crypto::signer::encrypt_keypair;
5
+ use auths_core::signing::PrefilledPassphraseProvider;
6
+ use auths_core::storage::keychain::{KeyAlias, KeyRole, get_platform_keychain_with_config};
7
+ use auths_id::identity::helpers::{encode_seed_as_pkcs8, extract_seed_bytes};
8
+ use auths_id::identity::initialize::initialize_registry_identity;
9
+ use auths_id::storage::attestation::AttestationSource;
10
+ use auths_sdk::context::AuthsContext;
11
+ use auths_sdk::device::link_device;
12
+ use auths_sdk::types::{DeviceLinkConfig, IdentityRotationConfig};
13
+ use auths_sdk::workflows::rotation::rotate_identity;
14
+ use auths_storage::git::{
15
+ GitRegistryBackend, RegistryAttestationStorage, RegistryConfig, RegistryIdentityStorage,
16
+ };
17
+ use auths_verifier::clock::SystemClock;
18
+ use auths_verifier::core::Capability;
19
+ use auths_verifier::types::DeviceDID;
20
+ use napi_derive::napi;
21
+ use ring::rand::SystemRandom;
22
+ use ring::signature::{Ed25519KeyPair, KeyPair};
23
+
24
+ use crate::error::format_error;
25
+ use crate::helpers::{make_env_config, resolve_key_alias, resolve_passphrase};
26
+ use crate::types::{
27
+ NapiAgentIdentityBundle, NapiDelegatedAgentBundle, NapiIdentityResult, NapiRotationResult,
28
+ };
29
+
30
+ fn init_backend(repo: &PathBuf) -> napi::Result<Arc<GitRegistryBackend>> {
31
+ let config = RegistryConfig::single_tenant(repo);
32
+ let backend = GitRegistryBackend::from_config_unchecked(config);
33
+ backend.init_if_needed().map_err(|e| {
34
+ format_error(
35
+ "AUTHS_REGISTRY_ERROR",
36
+ format!("Failed to initialize registry: {e}"),
37
+ )
38
+ })?;
39
+ Ok(Arc::new(backend))
40
+ }
41
+
42
+ fn open_backend(repo: &PathBuf) -> napi::Result<Arc<GitRegistryBackend>> {
43
+ let config = RegistryConfig::single_tenant(repo);
44
+ let backend = GitRegistryBackend::open_existing(config).map_err(|e| {
45
+ format_error(
46
+ "AUTHS_REGISTRY_ERROR",
47
+ format!("Failed to open registry: {e}"),
48
+ )
49
+ })?;
50
+ Ok(Arc::new(backend))
51
+ }
52
+
53
+ #[napi]
54
+ pub fn create_identity(
55
+ key_alias: String,
56
+ repo_path: String,
57
+ passphrase: Option<String>,
58
+ ) -> napi::Result<NapiIdentityResult> {
59
+ let passphrase_str = resolve_passphrase(passphrase);
60
+ let env_config = make_env_config(&passphrase_str, &repo_path);
61
+ let alias = KeyAlias::new(&key_alias)
62
+ .map_err(|e| format_error("AUTHS_KEY_NOT_FOUND", format!("Invalid key alias: {e}")))?;
63
+ let provider = PrefilledPassphraseProvider::new(&passphrase_str);
64
+
65
+ let repo = PathBuf::from(shellexpand::tilde(&repo_path).as_ref());
66
+ let backend = init_backend(&repo)?;
67
+
68
+ let keychain = get_platform_keychain_with_config(&env_config)
69
+ .map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
70
+
71
+ let (identity_did, result_alias) =
72
+ initialize_registry_identity(backend, &alias, &provider, keychain.as_ref(), None).map_err(
73
+ |e| {
74
+ format_error(
75
+ "AUTHS_IDENTITY_ERROR",
76
+ format!("Identity creation failed: {e}"),
77
+ )
78
+ },
79
+ )?;
80
+
81
+ let pub_bytes = auths_core::storage::keychain::extract_public_key_bytes(
82
+ keychain.as_ref(),
83
+ &result_alias,
84
+ &provider,
85
+ )
86
+ .map_err(|e| {
87
+ format_error(
88
+ "AUTHS_CRYPTO_ERROR",
89
+ format!("Public key extraction failed: {e}"),
90
+ )
91
+ })?;
92
+
93
+ Ok(NapiIdentityResult {
94
+ did: identity_did.to_string(),
95
+ key_alias: result_alias.to_string(),
96
+ public_key_hex: hex::encode(pub_bytes),
97
+ })
98
+ }
99
+
100
+ #[napi]
101
+ pub fn create_agent_identity(
102
+ agent_name: String,
103
+ capabilities: Vec<String>,
104
+ repo_path: String,
105
+ passphrase: Option<String>,
106
+ ) -> napi::Result<NapiAgentIdentityBundle> {
107
+ let passphrase_str = resolve_passphrase(passphrase);
108
+ let env_config = make_env_config(&passphrase_str, &repo_path);
109
+ #[allow(clippy::disallowed_methods)]
110
+ // INVARIANT: agent_name is user-provided, format produces valid alias
111
+ let alias = KeyAlias::new_unchecked(format!("{}-agent", agent_name));
112
+ let provider = PrefilledPassphraseProvider::new(&passphrase_str);
113
+ let clock = Arc::new(SystemClock);
114
+
115
+ let repo = PathBuf::from(shellexpand::tilde(&repo_path).as_ref());
116
+ let backend = init_backend(&repo)?;
117
+
118
+ let keychain = get_platform_keychain_with_config(&env_config)
119
+ .map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
120
+
121
+ let parsed_caps: Vec<Capability> = capabilities
122
+ .iter()
123
+ .map(|c| {
124
+ Capability::parse(c).map_err(|e| {
125
+ format_error(
126
+ "AUTHS_INVALID_INPUT",
127
+ format!("Invalid capability '{c}': {e}"),
128
+ )
129
+ })
130
+ })
131
+ .collect::<napi::Result<Vec<_>>>()?;
132
+
133
+ let (identity_did, result_alias) =
134
+ initialize_registry_identity(backend.clone(), &alias, &provider, keychain.as_ref(), None)
135
+ .map_err(|e| {
136
+ format_error(
137
+ "AUTHS_IDENTITY_ERROR",
138
+ format!("Agent identity creation failed: {e}"),
139
+ )
140
+ })?;
141
+
142
+ let pub_bytes = auths_core::storage::keychain::extract_public_key_bytes(
143
+ keychain.as_ref(),
144
+ &result_alias,
145
+ &provider,
146
+ )
147
+ .map_err(|e| {
148
+ format_error(
149
+ "AUTHS_CRYPTO_ERROR",
150
+ format!("Public key extraction failed: {e}"),
151
+ )
152
+ })?;
153
+
154
+ // Use link_device to produce a proper signed self-attestation,
155
+ // following the same pattern as delegate_agent.
156
+ let link_config = DeviceLinkConfig {
157
+ identity_key_alias: result_alias.clone(),
158
+ device_key_alias: Some(result_alias.clone()),
159
+ device_did: None,
160
+ capabilities: parsed_caps,
161
+ expires_in: None,
162
+ note: Some(format!("Agent: {}", agent_name)),
163
+ payload: None,
164
+ };
165
+
166
+ let provider = Arc::new(provider);
167
+ let keychain: Arc<dyn auths_core::storage::keychain::KeyStorage + Send + Sync> =
168
+ Arc::from(keychain);
169
+ let identity_storage = Arc::new(RegistryIdentityStorage::new(&repo));
170
+ let attestation_storage = Arc::new(RegistryAttestationStorage::new(&repo));
171
+
172
+ let ctx = AuthsContext::builder()
173
+ .registry(backend)
174
+ .key_storage(keychain)
175
+ .clock(clock.clone())
176
+ .identity_storage(identity_storage)
177
+ .attestation_sink(attestation_storage.clone())
178
+ .attestation_source(attestation_storage.clone())
179
+ .passphrase_provider(provider)
180
+ .build();
181
+
182
+ let result = link_device(link_config, &ctx, clock.as_ref()).map_err(|e| {
183
+ format_error(
184
+ "AUTHS_IDENTITY_ERROR",
185
+ format!("Agent self-attestation failed: {e}"),
186
+ )
187
+ })?;
188
+
189
+ #[allow(clippy::disallowed_methods)] // INVARIANT: device_did from SDK setup result
190
+ let device_did = DeviceDID::new_unchecked(result.device_did.to_string());
191
+ let attestations = attestation_storage
192
+ .load_attestations_for_device(&device_did)
193
+ .map_err(|e| {
194
+ format_error(
195
+ "AUTHS_REGISTRY_ERROR",
196
+ format!("Failed to load attestation: {e}"),
197
+ )
198
+ })?;
199
+
200
+ let attestation = attestations.last().ok_or_else(|| {
201
+ format_error(
202
+ "AUTHS_REGISTRY_ERROR",
203
+ "No attestation found after self-attestation",
204
+ )
205
+ })?;
206
+
207
+ let attestation_json = serde_json::to_string(attestation).map_err(|e| {
208
+ format_error(
209
+ "AUTHS_SERIALIZATION_ERROR",
210
+ format!("Serialization failed: {e}"),
211
+ )
212
+ })?;
213
+
214
+ Ok(NapiAgentIdentityBundle {
215
+ agent_did: identity_did.to_string(),
216
+ key_alias: result_alias.to_string(),
217
+ attestation_json,
218
+ public_key_hex: hex::encode(pub_bytes),
219
+ repo_path: Some(repo.to_string_lossy().to_string()),
220
+ })
221
+ }
222
+
223
+ #[napi]
224
+ pub fn delegate_agent(
225
+ agent_name: String,
226
+ capabilities: Vec<String>,
227
+ parent_repo_path: String,
228
+ passphrase: Option<String>,
229
+ expires_in: Option<i64>,
230
+ identity_did: Option<String>,
231
+ ) -> napi::Result<NapiDelegatedAgentBundle> {
232
+ let passphrase_str = resolve_passphrase(passphrase);
233
+ let env_config = make_env_config(&passphrase_str, &parent_repo_path);
234
+ let provider = Arc::new(PrefilledPassphraseProvider::new(&passphrase_str));
235
+ let clock = Arc::new(SystemClock);
236
+
237
+ let repo = PathBuf::from(shellexpand::tilde(&parent_repo_path).as_ref());
238
+ let backend = open_backend(&repo)?;
239
+
240
+ let keychain = get_platform_keychain_with_config(&env_config)
241
+ .map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
242
+
243
+ let parent_alias = if let Some(ref did) = identity_did {
244
+ resolve_key_alias(did, keychain.as_ref())?
245
+ } else {
246
+ let aliases = keychain
247
+ .list_aliases()
248
+ .map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
249
+ aliases
250
+ .into_iter()
251
+ .find(|a| !a.as_str().contains("--next-"))
252
+ .ok_or_else(|| {
253
+ format_error("AUTHS_KEY_NOT_FOUND", "No identity key found in keychain")
254
+ })?
255
+ };
256
+
257
+ #[allow(clippy::disallowed_methods)]
258
+ // INVARIANT: agent_name is user-provided, format produces valid alias
259
+ let agent_alias = KeyAlias::new_unchecked(format!("{}-agent", agent_name));
260
+ let rng = SystemRandom::new();
261
+ let pkcs8 = Ed25519KeyPair::generate_pkcs8(&rng)
262
+ .map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("Key generation failed: {e}")))?;
263
+ let keypair = Ed25519KeyPair::from_pkcs8(pkcs8.as_ref())
264
+ .map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("Key parsing failed: {e}")))?;
265
+ let agent_pubkey = keypair.public_key().as_ref().to_vec();
266
+
267
+ let (parent_did, _, _) = keychain
268
+ .load_key(&parent_alias)
269
+ .map_err(|e| format_error("AUTHS_KEY_NOT_FOUND", format!("Key load failed: {e}")))?;
270
+
271
+ let seed = extract_seed_bytes(pkcs8.as_ref())
272
+ .map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("Seed extraction failed: {e}")))?;
273
+ let seed_pkcs8 = encode_seed_as_pkcs8(seed)
274
+ .map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("PKCS8 encoding failed: {e}")))?;
275
+ let encrypted = encrypt_keypair(&seed_pkcs8, &passphrase_str)
276
+ .map_err(|e| format_error("AUTHS_CRYPTO_ERROR", format!("Key encryption failed: {e}")))?;
277
+ keychain
278
+ .store_key(
279
+ &agent_alias,
280
+ &parent_did,
281
+ KeyRole::DelegatedAgent,
282
+ &encrypted,
283
+ )
284
+ .map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Key storage failed: {e}")))?;
285
+
286
+ let parsed_caps: Vec<Capability> = capabilities
287
+ .iter()
288
+ .map(|c| {
289
+ Capability::parse(c).map_err(|e| {
290
+ format_error(
291
+ "AUTHS_INVALID_INPUT",
292
+ format!("Invalid capability '{c}': {e}"),
293
+ )
294
+ })
295
+ })
296
+ .collect::<napi::Result<Vec<_>>>()?;
297
+
298
+ let link_config = DeviceLinkConfig {
299
+ identity_key_alias: parent_alias,
300
+ device_key_alias: Some(agent_alias.clone()),
301
+ device_did: None,
302
+ capabilities: parsed_caps,
303
+ expires_in: expires_in.map(|s| s as u64),
304
+ note: Some(format!("Agent: {}", agent_name)),
305
+ payload: None,
306
+ };
307
+
308
+ let keychain: Arc<dyn auths_core::storage::keychain::KeyStorage + Send + Sync> =
309
+ Arc::from(keychain);
310
+ let identity_storage = Arc::new(RegistryIdentityStorage::new(&repo));
311
+ let attestation_storage = Arc::new(RegistryAttestationStorage::new(&repo));
312
+
313
+ let ctx = AuthsContext::builder()
314
+ .registry(backend)
315
+ .key_storage(keychain)
316
+ .clock(clock.clone())
317
+ .identity_storage(identity_storage)
318
+ .attestation_sink(attestation_storage.clone())
319
+ .attestation_source(attestation_storage.clone())
320
+ .passphrase_provider(provider)
321
+ .build();
322
+
323
+ let result = link_device(link_config, &ctx, clock.as_ref()).map_err(|e| {
324
+ format_error(
325
+ "AUTHS_IDENTITY_ERROR",
326
+ format!("Agent provisioning failed: {e}"),
327
+ )
328
+ })?;
329
+
330
+ #[allow(clippy::disallowed_methods)] // INVARIANT: device_did from SDK setup result
331
+ let device_did = DeviceDID::new_unchecked(result.device_did.to_string());
332
+ let attestations = attestation_storage
333
+ .load_attestations_for_device(&device_did)
334
+ .map_err(|e| {
335
+ format_error(
336
+ "AUTHS_REGISTRY_ERROR",
337
+ format!("Failed to load attestation: {e}"),
338
+ )
339
+ })?;
340
+
341
+ let attestation = attestations.last().ok_or_else(|| {
342
+ format_error(
343
+ "AUTHS_REGISTRY_ERROR",
344
+ "No attestation found after provisioning",
345
+ )
346
+ })?;
347
+
348
+ let attestation_json = serde_json::to_string(attestation).map_err(|e| {
349
+ format_error(
350
+ "AUTHS_SERIALIZATION_ERROR",
351
+ format!("Serialization failed: {e}"),
352
+ )
353
+ })?;
354
+
355
+ Ok(NapiDelegatedAgentBundle {
356
+ agent_did: result.device_did.to_string(),
357
+ key_alias: agent_alias.to_string(),
358
+ attestation_json,
359
+ public_key_hex: hex::encode(&agent_pubkey),
360
+ repo_path: Some(repo.to_string_lossy().to_string()),
361
+ })
362
+ }
363
+
364
+ #[napi]
365
+ pub fn rotate_identity_keys(
366
+ repo_path: String,
367
+ identity_key_alias: Option<String>,
368
+ next_key_alias: Option<String>,
369
+ passphrase: Option<String>,
370
+ ) -> napi::Result<NapiRotationResult> {
371
+ let passphrase_str = resolve_passphrase(passphrase);
372
+ let env_config = make_env_config(&passphrase_str, &repo_path);
373
+ let provider = Arc::new(PrefilledPassphraseProvider::new(&passphrase_str));
374
+ let clock = Arc::new(SystemClock);
375
+
376
+ let repo = PathBuf::from(shellexpand::tilde(&repo_path).as_ref());
377
+ let backend = open_backend(&repo)?;
378
+
379
+ let keychain = get_platform_keychain_with_config(&env_config)
380
+ .map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
381
+ let keychain: Arc<dyn auths_core::storage::keychain::KeyStorage + Send + Sync> =
382
+ Arc::from(keychain);
383
+
384
+ let identity_storage = Arc::new(RegistryIdentityStorage::new(&repo));
385
+ let attestation_storage = Arc::new(RegistryAttestationStorage::new(&repo));
386
+
387
+ let alias = identity_key_alias
388
+ .as_deref()
389
+ .map(|a| resolve_key_alias(a, keychain.as_ref()))
390
+ .transpose()?;
391
+
392
+ let ctx = AuthsContext::builder()
393
+ .registry(backend)
394
+ .key_storage(keychain)
395
+ .clock(clock.clone())
396
+ .identity_storage(identity_storage)
397
+ .attestation_sink(attestation_storage.clone())
398
+ .attestation_source(attestation_storage)
399
+ .passphrase_provider(provider)
400
+ .build();
401
+
402
+ let next_alias = next_key_alias
403
+ .as_deref()
404
+ .map(|a| {
405
+ KeyAlias::new(a).map_err(|e| {
406
+ format_error(
407
+ "AUTHS_KEY_NOT_FOUND",
408
+ format!("Invalid next key alias: {e}"),
409
+ )
410
+ })
411
+ })
412
+ .transpose()?;
413
+
414
+ let rotation_config = IdentityRotationConfig {
415
+ repo_path: repo,
416
+ identity_key_alias: alias,
417
+ next_key_alias: next_alias,
418
+ };
419
+
420
+ let result = rotate_identity(rotation_config, &ctx, clock.as_ref())
421
+ .map_err(|e| format_error("AUTHS_ROTATION_ERROR", format!("Key rotation failed: {e}")))?;
422
+
423
+ Ok(NapiRotationResult {
424
+ controller_did: result.controller_did.to_string(),
425
+ new_key_fingerprint: result.new_key_fingerprint,
426
+ previous_key_fingerprint: result.previous_key_fingerprint,
427
+ sequence: result.sequence as i64,
428
+ })
429
+ }
430
+
431
+ #[napi]
432
+ pub fn get_identity_public_key(
433
+ identity_did: String,
434
+ repo_path: String,
435
+ passphrase: Option<String>,
436
+ ) -> napi::Result<String> {
437
+ let passphrase_str = resolve_passphrase(passphrase);
438
+ let env_config = make_env_config(&passphrase_str, &repo_path);
439
+ let provider = PrefilledPassphraseProvider::new(&passphrase_str);
440
+
441
+ let keychain = get_platform_keychain_with_config(&env_config)
442
+ .map_err(|e| format_error("AUTHS_KEYCHAIN_ERROR", format!("Keychain error: {e}")))?;
443
+
444
+ let did = auths_verifier::types::IdentityDID::parse(&identity_did)
445
+ .map_err(|e| format_error("AUTHS_INVALID_INPUT", e))?;
446
+ let aliases = keychain
447
+ .list_aliases_for_identity_with_role(&did, KeyRole::Primary)
448
+ .map_err(|e| format_error("AUTHS_KEY_NOT_FOUND", format!("Key lookup failed: {e}")))?;
449
+ let alias = aliases.first().ok_or_else(|| {
450
+ format_error(
451
+ "AUTHS_KEY_NOT_FOUND",
452
+ format!("No primary key found for identity '{identity_did}'"),
453
+ )
454
+ })?;
455
+ let pub_bytes = auths_core::storage::keychain::extract_public_key_bytes(
456
+ keychain.as_ref(),
457
+ alias,
458
+ &provider,
459
+ )
460
+ .map_err(|e| {
461
+ format_error(
462
+ "AUTHS_CRYPTO_ERROR",
463
+ format!("Public key extraction failed: {e}"),
464
+ )
465
+ })?;
466
+ Ok(hex::encode(pub_bytes))
467
+ }
package/src/lib.rs ADDED
@@ -0,0 +1,26 @@
1
+ #![deny(clippy::all)]
2
+
3
+ pub mod artifact;
4
+ pub mod attestation_query;
5
+ pub mod audit;
6
+ pub mod commit_sign;
7
+ pub mod device;
8
+ pub mod diagnostics;
9
+ pub mod error;
10
+ pub mod helpers;
11
+ pub mod identity;
12
+ pub mod org;
13
+ pub mod pairing;
14
+ pub mod policy;
15
+ pub mod sign;
16
+ pub mod trust;
17
+ pub mod types;
18
+ pub mod verify;
19
+ pub mod witness;
20
+
21
+ use napi_derive::napi;
22
+
23
+ #[napi]
24
+ pub fn version() -> String {
25
+ env!("CARGO_PKG_VERSION").to_string()
26
+ }