revault_api 0.6.0-aarch64-linux → 0.6.2-aarch64-linux

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.
@@ -0,0 +1,1600 @@
1
+ # The owned, class-oriented reVault API. See the
2
+ # {repository README}[https://github.com/onepub-dev/reVault#readme] for the
3
+ # security model and complete examples.
4
+ require_relative 'native_library'
5
+
6
+ # Encrypts lockbox content and manages local vault metadata.
7
+ module Revault
8
+ # Load an explicit, inherited, or installed native carrier.
9
+ def self.load(native_library_path = nil) = Runtime.new(native_library_path)
10
+ # Return a new runtime facade synchronously for factory operations.
11
+ def self.runtime = Runtime.new
12
+
13
+ # Select the process-wide native carrier and load binding operations.
14
+ def self.ensure_native(native_library_path = nil)
15
+ NativeLibrary.path(native_library_path)
16
+ unless const_defined?(:BindingOperations, false)
17
+ require_relative 'binding_operations'
18
+ NativeLibrary.bind_shim
19
+ private_constant(:BindingOperations)
20
+ end
21
+ end
22
+
23
+ # Native operation failure with structured diagnostic details.
24
+ class RevaultError < StandardError
25
+ attr_reader :details
26
+ # Preserve the stable native message and structured details.
27
+ def initialize(message, details = nil)
28
+ super(message); @details = details
29
+ end
30
+ end
31
+
32
+ # Returns the owned handle.
33
+ class OwnedHandle
34
+ attr_reader :native_handle
35
+ # Returns the initialize.
36
+ def initialize(operations, native_handle)
37
+ @operations = operations
38
+ @native_handle = native_handle
39
+ end
40
+ end
41
+
42
+ # Primary API used to open lockboxes, manage keys and metadata, use the
43
+ # Session Agent, and access to the platform credential store.
44
+ class Runtime
45
+ # Open a session for an existing Vault file; retains credentials but no Vault lock.
46
+ def contact_exchange(path, vault_passphrase)
47
+ ContactExchange.new(@operations, @operations.contact_exchange_open(path, vault_passphrase))
48
+ end
49
+ attr_reader :agent, :platform
50
+ # Returns the initialize.
51
+ def initialize(native_library_path = nil)
52
+ Revault.ensure_native(native_library_path)
53
+ @operations = BindingOperations.new
54
+ @agent = Agent.new(@operations)
55
+ @platform = Platform.new(@operations)
56
+ end
57
+ # Returns the last error.
58
+ def last_error = @operations.last_error_message
59
+ # Returns the last error details.
60
+ def last_error_details = @operations.buffer_last_error_details()
61
+
62
+ # Returns the newest Lockbox archive format version supported by this engine.
63
+ def lockbox_format_version()
64
+ @operations.lockbox_format_version()
65
+ end
66
+
67
+ # Reads the format version from serialized Lockbox bytes without opening them.
68
+ def lockbox_probe_format_version(bytes)
69
+ @operations.lockbox_probe_format_version(bytes)
70
+ end
71
+
72
+ # Creates an in memory Lockbox protected by a 32 byte content key.
73
+ def lockbox_create(key)
74
+ Lockbox.new(@operations, @operations.lockbox_create(key))
75
+ end
76
+
77
+ # Creates a lockbox with explicit cache capacity, workload, worker policy, and job count.
78
+ def lockbox_create_with_options(key, cache_mode, cache_bytes, workload, worker, jobs)
79
+ Lockbox.new(@operations, @operations.lockbox_create_with_options(key, cache_mode, cache_bytes, workload, worker, jobs))
80
+ end
81
+
82
+ # Creates an in memory Lockbox protected by the supplied password.
83
+ def lockbox_create_password(password)
84
+ Lockbox.new(@operations, @operations.lockbox_create_password(password))
85
+ end
86
+
87
+ # Creates a password protected Lockbox with its profile signing key.
88
+ def lockbox_create_password_with_signing_key(password, signing_key)
89
+ Lockbox.new(@operations, @operations.lockbox_create_password_with_signing_key(password, signing_key.native_handle))
90
+ end
91
+
92
+ # Creates an in memory Lockbox that the supplied contact can open.
93
+ def lockbox_create_contact(contact)
94
+ Lockbox.new(@operations, @operations.lockbox_create_contact(contact.native_handle))
95
+ end
96
+
97
+ # Creates a contact protected Lockbox with its profile signing key.
98
+ def lockbox_create_contact_with_signing_key(contact, signing_key)
99
+ Lockbox.new(@operations, @operations.lockbox_create_contact_with_signing_key(contact.native_handle, signing_key.native_handle))
100
+ end
101
+
102
+ # Creates an in memory Lockbox and assigns its profile signing key.
103
+ def lockbox_create_with_signing_key(content_key, signing_key)
104
+ Lockbox.new(@operations, @operations.lockbox_create_with_signing_key(content_key, signing_key.native_handle))
105
+ end
106
+
107
+ # Opens serialized Lockbox bytes with a 32 byte content key.
108
+ def lockbox_open(archive, key)
109
+ Lockbox.new(@operations, @operations.lockbox_open(archive, key))
110
+ end
111
+
112
+ # Opens a lockbox with explicit cache capacity, workload, worker policy, and job count.
113
+ def lockbox_open_with_options(archive, key, cache_mode, cache_bytes, workload, worker, jobs)
114
+ Lockbox.new(@operations, @operations.lockbox_open_with_options(archive, key, cache_mode, cache_bytes, workload, worker, jobs))
115
+ end
116
+
117
+ # Opens serialized Lockbox bytes with the supplied password.
118
+ def lockbox_open_password_with_budget(archive, password, max_derivations)
119
+ Lockbox.new(@operations, @operations.lockbox_open_password_with_budget(archive, password, max_derivations))
120
+ end
121
+ # Opens with the default per-credential password work budget.
122
+ def lockbox_open_password(archive, password)
123
+ Lockbox.new(@operations, @operations.lockbox_open_password(archive, password))
124
+ end
125
+
126
+ # Opens serialized Lockbox bytes with the supplied contact private key.
127
+ def lockbox_open_contact(archive, contact)
128
+ Lockbox.new(@operations, @operations.lockbox_open_contact(archive, contact.native_handle))
129
+ end
130
+
131
+ # Reads public header, signature, and access slot metadata from a Lockbox file.
132
+ def lockbox_inspect_file(path)
133
+ @operations.lockbox_inspect_file(path)
134
+ end
135
+
136
+ # Scans a damaged Lockbox file with its 32 byte content key.
137
+ def lockbox_recovery_scan_path(path, key)
138
+ @operations.lockbox_recovery_scan_path(path, key)
139
+ end
140
+
141
+ # Scans damaged serialized Lockbox bytes with their 32 byte content key.
142
+ def lockbox_recovery_scan(bytes, key)
143
+ @operations.lockbox_recovery_scan(bytes, key)
144
+ end
145
+
146
+ # Builds a new Lockbox from recoverable records without changing the source.
147
+ def lockbox_recovery_salvage(bytes, key, signing_key)
148
+ Lockbox.new(@operations, @operations.lockbox_recovery_salvage(bytes, key, signing_key.native_handle))
149
+ end
150
+
151
+ # Generates a contact encryption key pair using secure random data.
152
+ def key_contact_generate()
153
+ ContactKeyPair.new(@operations, @operations.key_contact_generate())
154
+ end
155
+
156
+ # Imports a contact key pair from its private binary record.
157
+ def key_contact_from_private(bytes)
158
+ ContactKeyPair.new(@operations, @operations.key_contact_from_private(bytes))
159
+ end
160
+
161
+ # Imports a contact public key from its binary representation.
162
+ def key_contact_public_from_bytes(bytes)
163
+ ContactPublicKey.new(@operations, @operations.key_contact_public_from_bytes(bytes))
164
+ end
165
+
166
+ # Generates a signing identity owned by a Vault Profile.
167
+ def generate_profile_signing_key_pair()
168
+ ProfileSigningKeyPair.new(@operations, @operations.key_signing_generate())
169
+ end
170
+
171
+ # Imports a Vault Profile signing identity from its private record.
172
+ def profile_signing_key_pair_from_private(bytes)
173
+ ProfileSigningKeyPair.new(@operations, @operations.key_signing_from_private(bytes))
174
+ end
175
+
176
+ # Imports the public half of a Vault Profile signing identity.
177
+ def profile_signing_public_key_from_bytes(bytes)
178
+ ProfileSigningPublicKey.new(@operations, @operations.key_signing_public_from_bytes(bytes))
179
+ end
180
+
181
+ # Exports a private key in the requested key format.
182
+ def vault_key_export_private(key, format)
183
+ @operations.vault_key_export_private(key.native_handle, format)
184
+ end
185
+
186
+ # Exports a public key in the requested key format.
187
+ def vault_key_export_public(key, format)
188
+ @operations.vault_key_export_public(key.native_handle, format)
189
+ end
190
+
191
+ # Imports a private contact key from a detected supported encoding.
192
+ def vault_key_import_private(bytes)
193
+ ContactKeyPair.new(@operations, @operations.vault_key_import_private(bytes))
194
+ end
195
+
196
+ # Imports a public contact key from a detected supported encoding.
197
+ def vault_key_import_public(bytes)
198
+ ContactPublicKey.new(@operations, @operations.vault_key_import_public(bytes))
199
+ end
200
+
201
+ # Returns the stable fingerprint used to verify a public key.
202
+ def vault_key_fingerprint(key)
203
+ @operations.vault_key_fingerprint(key.native_handle)
204
+ end
205
+
206
+ # Encodes key bytes as hexadecimal text.
207
+ def vault_key_format_hex(bytes)
208
+ @operations.vault_key_format_hex(bytes)
209
+ end
210
+
211
+ # Decodes hexadecimal key text and rejects malformed input.
212
+ def vault_key_decode_hex(text)
213
+ @operations.vault_key_decode_hex(text)
214
+ end
215
+
216
+ # Encodes key bytes using Crockford Base32.
217
+ def vault_key_format_crockford(bytes)
218
+ @operations.vault_key_format_crockford(bytes)
219
+ end
220
+
221
+ # Groups a Crockford code for easier reading and transcription.
222
+ def vault_key_format_crockford_reading(code)
223
+ @operations.vault_key_format_crockford_reading(code)
224
+ end
225
+
226
+ # Decodes Crockford Base32 key text and rejects malformed input.
227
+ def vault_key_decode_crockford(code)
228
+ @operations.vault_key_decode_crockford(code)
229
+ end
230
+
231
+ # Encodes arbitrary bytes as hexadecimal text.
232
+ def vault_key_hex_encode(bytes)
233
+ @operations.vault_key_hex_encode(bytes)
234
+ end
235
+
236
+ # Decodes arbitrary hexadecimal text and rejects malformed input.
237
+ def vault_key_hex_decode(text)
238
+ @operations.vault_key_hex_decode(text)
239
+ end
240
+
241
+ # Opens an existing Vault directory with its passphrase.
242
+ def vault_directory_open(root, password)
243
+ Vault.new(@operations, @operations.vault_directory_open(root, password))
244
+ end
245
+
246
+ # Returns the newest Vault structure version supported by this engine.
247
+ def vault_structure_version_current()
248
+ @operations.vault_structure_version_current()
249
+ end
250
+
251
+ # Reads an existing Vault structure version without changing it.
252
+ def vault_directory_probe_structure_version(root, password)
253
+ @operations.vault_directory_probe_structure_version(root, password)
254
+ end
255
+
256
+ # Opens or creates the default Vault without replacing existing state.
257
+ def vault_directory_open_or_create_default(password)
258
+ Vault.new(@operations, @operations.vault_directory_open_or_create_default(password))
259
+ end
260
+
261
+ # Replaces the default Vault and all persistent data it contains.
262
+ def vault_directory_replace_default(password)
263
+ Vault.new(@operations, @operations.vault_directory_replace_default(password))
264
+ end
265
+
266
+ # Changes the passphrase for an existing Vault.
267
+ def vault_directory_change_password(root, old_password, new_password)
268
+ @operations.vault_directory_change_password(root, old_password, new_password)
269
+ end
270
+
271
+ # Changes the passphrase for the default Vault.
272
+ def vault_directory_change_default_password(old_password, new_password)
273
+ @operations.vault_directory_change_default_password(old_password, new_password)
274
+ end
275
+
276
+ # Replaces the selected Vault and all persistent data it contains.
277
+ def vault_directory_replace(root, password)
278
+ Vault.new(@operations, @operations.vault_directory_replace(root, password))
279
+ end
280
+
281
+ # Opens the selected Vault, creating it only when absent.
282
+ def vault_directory_open_or_create(root, password)
283
+ Vault.new(@operations, @operations.vault_directory_open_or_create(root, password))
284
+ end
285
+
286
+ # Writes a backup of the default Vault to the selected path.
287
+ def vault_backup_default(path, overwrite)
288
+ @operations.vault_backup_default(path, overwrite)
289
+ end
290
+
291
+ # Restores the default Vault from the selected backup.
292
+ def vault_restore_default(path, overwrite)
293
+ @operations.vault_restore_default(path, overwrite)
294
+ end
295
+
296
+ # Opens an existing Vault metadata view that cannot load private keys.
297
+ def vault_read_only_open(root, password)
298
+ ReadOnlyVault.new(@operations, @operations.vault_read_only_open(root, password))
299
+ end
300
+
301
+ # Opens the default Vault metadata view without loading private keys.
302
+ def vault_read_only_open_default(password)
303
+ ReadOnlyVault.new(@operations, @operations.vault_read_only_open_default(password))
304
+ end
305
+
306
+ # Returns the platform default Vault directory.
307
+ def vault_default_directory()
308
+ @operations.vault_default_directory()
309
+ end
310
+
311
+ # Returns the path of the default Vault file.
312
+ def vault_default_path()
313
+ @operations.vault_default_path()
314
+ end
315
+
316
+ # Returns the session agent log path.
317
+ def vault_agent_log_path()
318
+ @operations.vault_agent_log_path()
319
+ end
320
+
321
+ # Returns the configured session agent log destination.
322
+ def vault_agent_log_destination()
323
+ @operations.vault_agent_log_destination()
324
+ end
325
+
326
+ end
327
+
328
+ # An open encrypted archive containing files, variables, secrets, and forms.
329
+ class Lockbox < OwnedHandle
330
+ # Creates an in-memory archive protected by exactly one credential.
331
+ def self.create_in_memory(password: nil, content_key: nil, contact: nil, signing_key: nil, options: nil)
332
+ credentials = [password, content_key, contact].compact
333
+ raise ArgumentError, 'supply exactly one of password, content_key, or contact' unless credentials.length == 1
334
+ runtime = Revault.runtime
335
+ box = if password
336
+ signing_key ? runtime.lockbox_create_password_with_signing_key(password.to_str, signing_key) : runtime.lockbox_create_password(password.to_str)
337
+ elsif contact
338
+ signing_key ? runtime.lockbox_create_contact_with_signing_key(contact, signing_key) : runtime.lockbox_create_contact(contact)
339
+ elsif options
340
+ runtime.lockbox_create_with_options(content_key, options[:cache_mode], options[:cache_bytes] || 0, options[:workload], options[:worker], options[:jobs] || 0)
341
+ else
342
+ runtime.lockbox_create(content_key)
343
+ end
344
+ box.set_owner_signing_key(signing_key) if signing_key && !password && !contact
345
+ box
346
+ end
347
+
348
+ # Opens serialized archive bytes without consulting the Session Agent.
349
+ def self.open_bytes(archive, password: nil, content_key: nil, contact: nil, options: nil)
350
+ credentials = [password, content_key, contact].compact
351
+ raise ArgumentError, 'supply exactly one of password, content_key, or contact' unless credentials.length == 1
352
+ runtime = Revault.runtime
353
+ return runtime.lockbox_open_password(archive, password.to_str) if password
354
+ return runtime.lockbox_open_contact(archive, contact) if contact
355
+ options ? runtime.lockbox_open_with_options(archive, content_key, options[:cache_mode], options[:cache_bytes] || 0, options[:workload], options[:worker], options[:jobs] || 0) : runtime.lockbox_open(archive, content_key)
356
+ end
357
+
358
+ # Creates an archive file and returns its process-local handle.
359
+ def self.create(path, overwrite: false, **options)
360
+ file_handle(path, overwrite ? 'replace' : 'create', **options)
361
+ end
362
+
363
+ # Opens with a shared lock; supply signing_key for exclusive write access.
364
+ # Close readers before opening a writer. Example:
365
+ # box = Lockbox.open(path, password: password); begin; puts box.get_file('/hello'); ensure; box.close; end
366
+ def self.open(path, writable: nil, **options)
367
+ file_handle(path, writable.nil? ? 'open' : (writable ? 'write' : 'read'), **options)
368
+ end
369
+
370
+ # Opens an existing plaintext archive with a shared read-only file lock.
371
+ # No credential or Session Agent is used. Close the handle to release the lock;
372
+ # encrypted archives raise an error. Example:
373
+ # box = Lockbox.open_unencrypted(path); begin; puts box.get_file('/index.html'); ensure; box.close; end
374
+ def self.open_unencrypted(path, options: nil)
375
+ Revault.ensure_native
376
+ operations = BindingOperations.new
377
+ tuning = options || {}
378
+ cache = tuning[:cache_mode] || 'bytes'
379
+ cache = 'auto' if cache == 'automatic'
380
+ new(operations, operations.lockbox_file(path, 'read', 'unencrypted', '', nil, nil,
381
+ cache, tuning[:cache_bytes] || (64 << 20), tuning[:workload] || 'interactive',
382
+ tuning[:worker] || 'auto', tuning[:jobs] || 0))
383
+ end
384
+
385
+ # Internal credential and option marshalling for native file operations.
386
+ def self.file_handle(path, mode, password: nil, content_key: nil, contact: nil, signing_key: nil, options: nil)
387
+ raise ArgumentError, 'supply exactly one credential' unless [password, content_key, contact].compact.length == 1
388
+ Revault.ensure_native
389
+ operations = BindingOperations.new
390
+ tuning = options || {}
391
+ cache = tuning[:cache_mode] || 'bytes'
392
+ cache = 'auto' if cache == 'automatic'
393
+ credential = password ? 'password' : contact ? 'contact' : 'content-key'
394
+ new(operations, operations.lockbox_file(path, mode, credential, (password || content_key || '').to_str, contact&.native_handle, signing_key&.native_handle, cache, tuning[:cache_bytes] || (64 << 20), tuning[:workload] || 'interactive', tuning[:worker] || 'auto', tuning[:jobs] || 0))
395
+ end
396
+ private_class_method :file_handle
397
+
398
+ # Stages a file at the Lockbox path; replace controls an existing entry.
399
+ def add_file(path, data, replace)
400
+ @operations.lockbox_add_file(@native_handle, path, data, replace)
401
+ end
402
+
403
+ # Stages a file and its portable Unix permission bits.
404
+ def add_file_with_permissions(path, data, permissions, replace)
405
+ @operations.lockbox_add_file_with_permissions(@native_handle, path, data, permissions, replace)
406
+ end
407
+
408
+ # Reads the complete file stored at the Lockbox path.
409
+ def get_file(path)
410
+ @operations.lockbox_get_file(@native_handle, path)
411
+ end
412
+
413
+ # Writes one Lockbox file to the host filesystem.
414
+ def extract_file_with_policy(source, destination, max_file_bytes, max_total_bytes, max_files, restore_permissions, overwrite)
415
+ @operations.lockbox_extract_file_with_policy(@native_handle, source, destination, max_file_bytes, max_total_bytes, max_files, restore_permissions, overwrite)
416
+ end
417
+ # Extracts one file using the legacy permission policy.
418
+ def extract_file(source, destination, replace)
419
+ @operations.lockbox_extract_file(@native_handle, source, destination, replace)
420
+ end
421
+
422
+ # Extracts the Lockbox with explicit size, count, link, and permission limits.
423
+ def extract_directory(destination, max_file_bytes, max_total_bytes, max_files, restore_symlinks, restore_permissions, overwrite)
424
+ @operations.lockbox_extract_directory(@native_handle, destination, max_file_bytes, max_total_bytes, max_files, restore_symlinks, restore_permissions, overwrite)
425
+ end
426
+
427
+ # Lists logical or physical content chunks for streaming diagnostics.
428
+ def stream_content(physical)
429
+ @operations.lockbox_stream_content(@native_handle, physical)
430
+ end
431
+
432
+ # Returns cache statistics for this lockbox.
433
+ def cache_stats()
434
+ @operations.lockbox_cache_stats(@native_handle)
435
+ end
436
+
437
+ # Returns import statistics for this lockbox.
438
+ def import_stats()
439
+ @operations.lockbox_import_stats(@native_handle)
440
+ end
441
+
442
+ # Updates import stats.
443
+ def reset_import_stats()
444
+ @operations.lockbox_reset_import_stats(@native_handle)
445
+ end
446
+
447
+ # Returns page metadata for diagnostics without exposing plaintext secrets.
448
+ def page_inspection()
449
+ @operations.lockbox_page_inspection(@native_handle)
450
+ end
451
+
452
+ # Scans the open archive and returns its structured recovery report.
453
+ def recovery_report()
454
+ @operations.lockbox_recovery_report(@native_handle)
455
+ end
456
+
457
+ # Renders the recovery report for a person, capped at maxEntries.
458
+ def recovery_report_render(verbose, max_entries)
459
+ @operations.lockbox_recovery_report_render(@native_handle, verbose, max_entries)
460
+ end
461
+
462
+ # Returns the current serialized archive size in bytes.
463
+ def storage_len()
464
+ @operations.lockbox_storage_len(@native_handle)
465
+ end
466
+
467
+ # Sets workload profile.
468
+ def set_workload_profile(profile)
469
+ @operations.lockbox_set_workload_profile(@native_handle, profile)
470
+ end
471
+
472
+ # Sets a soft cache limit (auto, disabled, or bytes).
473
+ def set_cache_limit(mode, bytes)
474
+ @operations.lockbox_set_cache_limit(@native_handle, mode, bytes)
475
+ end
476
+
477
+ # Sets worker policy.
478
+ def set_worker_policy(mode, jobs)
479
+ @operations.lockbox_set_worker_policy(@native_handle, mode, jobs)
480
+ end
481
+
482
+ # Returns the cache, workload, and worker settings used by this Lockbox.
483
+ def runtime_options()
484
+ @operations.lockbox_runtime_options(@native_handle)
485
+ end
486
+
487
+ # Authenticates and publishes the staged changes.
488
+ def commit()
489
+ @operations.lockbox_commit(@native_handle)
490
+ end
491
+
492
+ # Stages a directory entry and optionally creates missing parents.
493
+ def create_dir(path, create_parents)
494
+ @operations.lockbox_create_dir(@native_handle, path, create_parents)
495
+ end
496
+
497
+ # Stages removal of a file, link, or empty directory at path.
498
+ def delete(path)
499
+ @operations.lockbox_delete(@native_handle, path)
500
+ end
501
+
502
+ # Stages removal of a directory, optionally including its descendants.
503
+ def remove_dir(path, recursive)
504
+ @operations.lockbox_remove_dir(@native_handle, path, recursive)
505
+ end
506
+
507
+ # Stages every missing parent directory for path.
508
+ def create_parent_dirs(path)
509
+ @operations.lockbox_create_parent_dirs(@native_handle, path)
510
+ end
511
+
512
+ # Stages an atomic move from one Lockbox path to another.
513
+ def rename(from, to)
514
+ @operations.lockbox_rename(@native_handle, from, to)
515
+ end
516
+
517
+ # Lists entries below path, optionally including descendants.
518
+ def list(path, recursive)
519
+ @operations.lockbox_list(@native_handle, path, recursive)
520
+ end
521
+
522
+ # Lists entries using glob, type, recursion, and result limit filters.
523
+ def list_with_options(path, glob, recursive, include_files, include_symlinks, include_directories, limit)
524
+ @operations.lockbox_list_with_options(@native_handle, path, glob, recursive, include_files, include_symlinks, include_directories, limit)
525
+ end
526
+
527
+ # Returns metadata for the selected lockbox entry.
528
+ def stat(path)
529
+ @operations.lockbox_stat(@native_handle, path)
530
+ end
531
+
532
+ # Stages a plain text variable; commit to publish the change.
533
+ def set_variable(name, value)
534
+ @operations.lockbox_set_variable(@native_handle, name, value)
535
+ end
536
+
537
+ # Stores a secret variable from mutable bytes.
538
+ def set_secret_variable(name, value)
539
+ @operations.lockbox_set_secret_variable(@native_handle, name, value)
540
+ end
541
+
542
+ # Get/set/clear encrypted guidance; commit Lockbox writes. Fields belong to definitions.
543
+ def item_description(target, reference, operation: 'get', field: nil, text: nil)
544
+ require 'json'
545
+ @operations.lockbox_item_description(@native_handle, JSON.generate({ target: target, reference: reference, operation: operation, field: field, text: text }))
546
+ end
547
+
548
+ # Returns a plain variable when it is present.
549
+ def get_variable(name)
550
+ @operations.lockbox_get_variable(@native_handle, name)
551
+ end
552
+
553
+ # Returns the encrypted lockbox description, or nil when unset.
554
+ # Example: set it, commit, then `puts box.description`.
555
+ def description
556
+ get_variable('/.revault/description')
557
+ end
558
+
559
+ # Stages encrypted description text; call commit to publish it.
560
+ # Example: `box.set_description('Production credentials'); box.commit`.
561
+ def set_description(description)
562
+ set_variable('/.revault/description', description)
563
+ end
564
+
565
+ # Stages removal of the encrypted description; call commit.
566
+ # Example: `box.clear_description; box.commit`.
567
+ def clear_description
568
+ delete_variable('/.revault/description')
569
+ end
570
+
571
+ # Yields temporary secret bytes and wipes the native transfer afterwards.
572
+ def with_secret_variable(name, &callback)
573
+ @operations.lockbox_get_secret_variable(@native_handle, name, &callback)
574
+ end
575
+
576
+ # Stages removal of a variable.
577
+ def delete_variable(name)
578
+ @operations.lockbox_delete_variable(@native_handle, name)
579
+ end
580
+
581
+ # Atomically renames variables using source and destination path pairs.
582
+ def move_variables(moves)
583
+ @operations.lockbox_move_variables(@native_handle, Internal::DomainCodec.encode_path_moves(moves))
584
+ end
585
+
586
+ # Lists variable names and metadata without exposing secret values.
587
+ def list_variables()
588
+ @operations.lockbox_list_variables(@native_handle)
589
+ end
590
+
591
+ # Returns whether a variable is plain or secret.
592
+ def variable_sensitivity(name)
593
+ @operations.lockbox_variable_sensitivity(@native_handle, name)
594
+ end
595
+
596
+ # Stages a symbolic link with its stored target text.
597
+ def add_symlink(path, target, replace)
598
+ @operations.lockbox_add_symlink(@native_handle, path, target, replace)
599
+ end
600
+
601
+ # Returns the target text stored for a symbolic link.
602
+ def get_symlink_target(path)
603
+ @operations.lockbox_get_symlink_target(@native_handle, path)
604
+ end
605
+
606
+ # Returns the stable public identifier stored in the Lockbox header.
607
+ def id()
608
+ @operations.lockbox_id(@native_handle)
609
+ end
610
+
611
+ # Reports whether an entry exists at path.
612
+ def exists(path)
613
+ @operations.lockbox_exists(@native_handle, path)
614
+ end
615
+
616
+ # Reports whether path names a directory entry.
617
+ def is_dir(path)
618
+ @operations.lockbox_is_dir(@native_handle, path)
619
+ end
620
+
621
+ # Returns the portable Unix permission bits stored for path.
622
+ def permissions(path)
623
+ @operations.lockbox_permissions(@native_handle, path)
624
+ end
625
+
626
+ # Stages portable Unix permission bits for path.
627
+ def set_permissions(path, permissions)
628
+ @operations.lockbox_set_permissions(@native_handle, path, permissions)
629
+ end
630
+
631
+ # Reads the requested byte range from a stored file.
632
+ def read_range(path, offset, len)
633
+ @operations.lockbox_read_range(@native_handle, path, offset, len)
634
+ end
635
+
636
+ # Adds a password access slot and returns its slot identifier.
637
+ def add_password(password)
638
+ @operations.lockbox_add_password(@native_handle, password)
639
+ end
640
+
641
+ # Grants a named contact access and returns the new slot identifier.
642
+ def add_contact(contact, name)
643
+ @operations.lockbox_add_contact(@native_handle, contact.native_handle, name)
644
+ end
645
+
646
+ # Removes an access slot; at least one usable slot must remain.
647
+ def delete_key(id)
648
+ @operations.lockbox_delete_key(@native_handle, id)
649
+ end
650
+
651
+ # Lists public access slot metadata without returning credentials.
652
+ def list_key_slots()
653
+ @operations.lockbox_list_key_slots(@native_handle)
654
+ end
655
+
656
+ # Assigns a profile signing key to the Lockbox owner role.
657
+ def set_owner_signing_key(key)
658
+ @operations.lockbox_set_owner_signing_key(@native_handle, key.native_handle)
659
+ end
660
+
661
+ # Returns public signing and ownership metadata for the current revision.
662
+ def owner_inspection()
663
+ @operations.lockbox_owner_inspection(@native_handle)
664
+ end
665
+
666
+ # Defines a reusable, versioned form from the supplied field definitions.
667
+ def define_form(alias_name, name, description, fields)
668
+ @operations.lockbox_define_form(@native_handle, alias_name, name, description, Internal::DomainCodec.encode_form_fields(fields))
669
+ end
670
+
671
+ # Lists the form definitions stored in this Lockbox.
672
+ def list_form_definitions()
673
+ @operations.lockbox_list_form_definitions(@native_handle)
674
+ end
675
+
676
+ # Resolves a form alias, type identifier, or revision.
677
+ def resolve_form(reference)
678
+ @operations.lockbox_resolve_form(@native_handle, reference)
679
+ end
680
+
681
+ # Lists every stored revision for a form type identifier.
682
+ def list_form_revisions(type_id)
683
+ @operations.lockbox_list_form_revisions(@native_handle, type_id)
684
+ end
685
+
686
+ # Stages a form record at path using the referenced definition.
687
+ def create_form_record(path, type_reference, name)
688
+ @operations.lockbox_create_form_record(@native_handle, path, type_reference, name)
689
+ end
690
+
691
+ # Stages a plain field value in a form record.
692
+ def set_form_field(path, field, value)
693
+ @operations.lockbox_set_form_field(@native_handle, path, field, value)
694
+ end
695
+
696
+ # Stores a secret form field from mutable bytes.
697
+ def set_secret_form_field(path, field, value)
698
+ @operations.lockbox_set_secret_form_field(@native_handle, path, field, value)
699
+ end
700
+
701
+ # Lists form records without exposing secret field values.
702
+ def list_form_records()
703
+ @operations.lockbox_list_form_records(@native_handle)
704
+ end
705
+
706
+ # Returns the form record at path when present.
707
+ def get_form_record(path)
708
+ @operations.lockbox_get_form_record(@native_handle, path)
709
+ end
710
+
711
+ # Stages removal of a form record.
712
+ def delete_form_record(path)
713
+ @operations.lockbox_delete_form_record(@native_handle, path)
714
+ end
715
+
716
+ # Atomically renames form records using source and destination path pairs.
717
+ def move_form_records(moves)
718
+ @operations.lockbox_move_form_records(@native_handle, Internal::DomainCodec.encode_path_moves(moves))
719
+ end
720
+
721
+ # Returns a plain form field when it exists.
722
+ def get_form_field(path, field)
723
+ @operations.lockbox_get_form_field(@native_handle, path, field)
724
+ end
725
+
726
+ # Yields temporary field bytes and wipes the native transfer afterwards.
727
+ def with_secret_form_field(path, field, &callback)
728
+ @operations.lockbox_get_secret_form_field(@native_handle, path, field, &callback)
729
+ end
730
+
731
+ # Serializes the current Lockbox, including committed changes.
732
+ def to_bytes()
733
+ @operations.lockbox_to_bytes(@native_handle)
734
+ end
735
+
736
+ # Releases the native resources held by this object.
737
+ def free()
738
+ @operations.lockbox_free(@native_handle)
739
+ @native_handle = nil
740
+ end
741
+
742
+ end
743
+
744
+ # A profile's contact-encryption identity used to decrypt keys addressed to it.
745
+ class ContactKeyPair < OwnedHandle
746
+ # Returns the canonical public bytes paired with this identity.
747
+ def public_bytes()
748
+ @operations.key_contact_public(@native_handle)
749
+ end
750
+
751
+ # Returns the private signing-key record for secure binary backup.
752
+ def private_record()
753
+ @operations.key_contact_private(@native_handle)
754
+ end
755
+
756
+ # Releases the native resources held by this object.
757
+ def free()
758
+ @operations.key_contact_free(@native_handle)
759
+ @native_handle = nil
760
+ end
761
+
762
+ # Decrypts a wrapped content key for this contact.
763
+ def decrypt(wrapped)
764
+ @operations.key_contact_decrypt(@native_handle, wrapped.native_handle)
765
+ end
766
+
767
+ end
768
+
769
+ # A recipient's shareable encryption identity used when granting access.
770
+ class ContactPublicKey < OwnedHandle
771
+ # Releases this public contact key.
772
+ def public_free()
773
+ @operations.key_contact_public_free(@native_handle)
774
+ @native_handle = nil
775
+ end
776
+
777
+ # Encrypts a content key for the selected contact.
778
+ def encrypt(content_key)
779
+ WrappedContactKey.new(@operations, @operations.key_contact_encrypt(@native_handle, content_key))
780
+ end
781
+
782
+ end
783
+
784
+ # A content key encrypted for one contact and recoverable by its matching key pair.
785
+ class WrappedContactKey < OwnedHandle
786
+ # Returns the public.
787
+ def public()
788
+ @operations.key_contact_wrapped_public(@native_handle)
789
+ end
790
+
791
+ # Returns the encrypted content key bytes.
792
+ def ciphertext()
793
+ @operations.key_contact_wrapped_ciphertext(@native_handle)
794
+ end
795
+
796
+ # Returns the complete wrapped key record for storage or transport.
797
+ def encrypted()
798
+ @operations.key_contact_wrapped_encrypted(@native_handle)
799
+ end
800
+
801
+ # Releases the native resources held by this object.
802
+ def free()
803
+ @operations.key_contact_wrapped_free(@native_handle)
804
+ @native_handle = nil
805
+ end
806
+
807
+ end
808
+
809
+ # A Vault Profile signing identity used to authorize mutable Lockbox revisions.
810
+ class ProfileSigningKeyPair < OwnedHandle
811
+ # Returns the public.
812
+ def public()
813
+ @operations.key_signing_public(@native_handle)
814
+ end
815
+
816
+ # Returns the private.
817
+ def private()
818
+ @operations.key_signing_private(@native_handle)
819
+ end
820
+
821
+ # Creates an independently owned public verification-key handle.
822
+ def public_key()
823
+ ProfileSigningPublicKey.new(
824
+ @operations,
825
+ @operations.key_signing_public_from_bytes(public)
826
+ )
827
+ end
828
+
829
+ # Releases the native resources held by this object.
830
+ def free()
831
+ @operations.key_signing_free(@native_handle)
832
+ @native_handle = nil
833
+ end
834
+
835
+ end
836
+
837
+ # The public half of a Vault Profile signing identity.
838
+ class ProfileSigningPublicKey < OwnedHandle
839
+ # Releases the native resources held by this object.
840
+ def free()
841
+ @operations.key_signing_public_free(@native_handle)
842
+ @native_handle = nil
843
+ end
844
+
845
+ end
846
+
847
+ # Password-protected storage for Profile keys, contacts, forms, backups, and lockbox paths.
848
+ class VaultStore < OwnedHandle
849
+ # Returns the canonical root directory of this Vault.
850
+ def root()
851
+ @operations.vault_directory_root(@native_handle)
852
+ end
853
+
854
+ # Returns the persistent structure version of this Vault.
855
+ def structure_version()
856
+ @operations.vault_directory_structure_version(@native_handle)
857
+ end
858
+
859
+ # Lists private keys.
860
+ def list_private_keys()
861
+ @operations.vault_directory_list_private_keys(@native_handle)
862
+ end
863
+
864
+ # Lists private key names.
865
+ def list_private_key_names()
866
+ @operations.vault_directory_list_private_key_names(@native_handle)
867
+ end
868
+
869
+ # Lists contact names.
870
+ def list_contact_names()
871
+ @operations.vault_directory_list_contact_names(@native_handle)
872
+ end
873
+
874
+ # Lists form aliases.
875
+ def list_form_aliases()
876
+ @operations.vault_directory_list_form_aliases(@native_handle)
877
+ end
878
+
879
+ # Reports whether the named profile private key exists.
880
+ def private_key_exists(name)
881
+ @operations.vault_directory_private_key_exists(@native_handle, name)
882
+ end
883
+
884
+ # Removes private key.
885
+ def delete_private_key(name)
886
+ @operations.vault_directory_delete_private_key(@native_handle, name)
887
+ end
888
+
889
+ # Stores private key.
890
+ def store_private_key(name, key)
891
+ @operations.vault_directory_store_private_key(@native_handle, name, key.native_handle)
892
+ end
893
+
894
+ # Loads private key.
895
+ def load_private_key(name)
896
+ ContactKeyPair.new(@operations, @operations.vault_directory_load_private_key(@native_handle, name))
897
+ end
898
+
899
+ # Loads private key generation.
900
+ def load_private_key_generation(name, index)
901
+ ContactKeyPair.new(@operations, @operations.vault_directory_load_private_key_generation(@native_handle, name, index))
902
+ end
903
+
904
+ # Stores contact.
905
+ def store_contact(name, key)
906
+ @operations.vault_directory_store_contact(@native_handle, name, key.native_handle)
907
+ end
908
+
909
+ # Loads contact.
910
+ def load_contact(name)
911
+ ContactPublicKey.new(@operations, @operations.vault_directory_load_contact(@native_handle, name))
912
+ end
913
+
914
+ # Reports whether the named contact exists.
915
+ def contact_exists(name)
916
+ @operations.vault_directory_contact_exists(@native_handle, name)
917
+ end
918
+
919
+ # Removes contact.
920
+ def delete_contact(name)
921
+ @operations.vault_directory_delete_contact(@native_handle, name)
922
+ end
923
+
924
+ # Lists contacts.
925
+ def list_contacts()
926
+ @operations.vault_directory_list_contacts(@native_handle)
927
+ end
928
+
929
+ # Stores profile email.
930
+ def store_profile_email(name, email)
931
+ @operations.vault_directory_store_profile_email(@native_handle, name, email)
932
+ end
933
+
934
+ # Get/set/clear encrypted guidance; commit Lockbox writes. Fields belong to definitions.
935
+ def item_description(target, reference, operation: 'get', field: nil, text: nil)
936
+ require 'json'
937
+ @operations.vault_directory_item_description(@native_handle, JSON.generate({ target: target, reference: reference, operation: operation, field: field, text: text }))
938
+ end
939
+
940
+ # Returns the email recorded for a profile, when present.
941
+ def profile_email(name)
942
+ @operations.vault_directory_profile_email(@native_handle, name)
943
+ end
944
+
945
+ # Stores backup.
946
+ def store_backup(id, bytes)
947
+ @operations.vault_directory_store_backup(@native_handle, id, bytes)
948
+ end
949
+
950
+ # Loads backup.
951
+ def load_backup(id)
952
+ @operations.vault_directory_load_backup(@native_handle, id)
953
+ end
954
+
955
+ # Returns the number of stored key recovery backups.
956
+ def backup_count()
957
+ @operations.vault_directory_backup_count(@native_handle)
958
+ end
959
+
960
+ # Restores a profile private key and signing key from recovery material.
961
+ def restore_private_key(name, key, signing_key, overwrite)
962
+ @operations.vault_directory_restore_private_key(@native_handle, name, key.native_handle, signing_key.native_handle, overwrite)
963
+ end
964
+
965
+ # Loads the current signing identity for a Vault Profile.
966
+ def load_profile_signing_key(name)
967
+ ProfileSigningKeyPair.new(@operations, @operations.vault_directory_load_owner_signing_key(@native_handle, name))
968
+ end
969
+
970
+ # Loads one historical signing identity for a Vault Profile.
971
+ def load_profile_signing_key_generation(name, index)
972
+ ProfileSigningKeyPair.new(@operations, @operations.vault_directory_load_owner_signing_key_generation(@native_handle, name, index))
973
+ end
974
+
975
+ # Stores contact signing key.
976
+ def store_contact_signing_key(name, key)
977
+ @operations.vault_directory_store_contact_signing_key(@native_handle, name, key.native_handle)
978
+ end
979
+
980
+ # Loads contact signing key.
981
+ def load_contact_signing_key(name)
982
+ ProfileSigningPublicKey.new(@operations, @operations.vault_directory_load_contact_signing_key(@native_handle, name))
983
+ end
984
+
985
+ # Lists profile generations.
986
+ def list_profile_generations(name)
987
+ @operations.vault_directory_list_profile_generations(@native_handle, name)
988
+ end
989
+
990
+ # Updates private key.
991
+ def rotate_private_key(name)
992
+ @operations.vault_directory_rotate_private_key(@native_handle, name)
993
+ end
994
+
995
+ # Stores lockbox.
996
+ def remember_lockbox(id, path)
997
+ @operations.vault_directory_remember_lockbox(@native_handle, id, path)
998
+ end
999
+
1000
+ # Lists known lockboxes.
1001
+ def list_known_lockboxes()
1002
+ @operations.vault_directory_list_known_lockboxes(@native_handle)
1003
+ end
1004
+
1005
+ # Resolve metadata; verify target identity when opening its path.
1006
+ def resolve_lockbox_alias(name)
1007
+ @operations.vault_directory_resolve_lockbox_alias(@native_handle, name)
1008
+ end
1009
+
1010
+ # Remove only an alias, preserving its target.
1011
+ def remove_lockbox_alias(name)
1012
+ @operations.vault_directory_remove_lockbox_alias(@native_handle, name)
1013
+ end
1014
+
1015
+ # List persisted aliases without opening target archives.
1016
+ def list_lockbox_aliases()
1017
+ @operations.vault_directory_list_lockbox_aliases(@native_handle)
1018
+ end
1019
+
1020
+ # Set or replace a normalized alias for a remembered identity.
1021
+ def set_lockbox_alias(name, id)
1022
+ @operations.vault_directory_set_lockbox_alias(@native_handle, name, id)
1023
+ end
1024
+
1025
+ # Removes lockbox.
1026
+ def forget_lockbox(path)
1027
+ @operations.vault_directory_forget_lockbox(@native_handle, path)
1028
+ end
1029
+
1030
+ # Stores access slot label.
1031
+ def remember_access_slot_label(id, slot_id, name)
1032
+ @operations.vault_directory_remember_access_slot_label(@native_handle, id, slot_id, name)
1033
+ end
1034
+
1035
+ # Lists access slot labels.
1036
+ def list_access_slot_labels(id)
1037
+ @operations.vault_directory_list_access_slot_labels(@native_handle, id)
1038
+ end
1039
+
1040
+ # Finds access slot labels with the supplied name for one Lockbox.
1041
+ def find_access_slot_labels(id, name)
1042
+ @operations.vault_directory_find_access_slot_labels(@native_handle, id, name)
1043
+ end
1044
+
1045
+ # Removes access slot label.
1046
+ def forget_access_slot_label(id, slot_id)
1047
+ @operations.vault_directory_forget_access_slot_label(@native_handle, id, slot_id)
1048
+ end
1049
+
1050
+ # Defines a reusable, versioned form in the local vault.
1051
+ def define_form(alias_name, name, description, fields)
1052
+ @operations.vault_directory_define_form(@native_handle, alias_name, name, description, Internal::DomainCodec.encode_form_fields(fields))
1053
+ end
1054
+
1055
+ # Resolves a form alias, type identifier, or revision.
1056
+ def resolve_form(reference)
1057
+ @operations.vault_directory_resolve_form(@native_handle, reference)
1058
+ end
1059
+
1060
+ # Lists forms.
1061
+ def list_forms()
1062
+ @operations.vault_directory_list_forms(@native_handle)
1063
+ end
1064
+
1065
+ # Lists every stored revision for a form type identifier.
1066
+ def list_form_revisions(type_id)
1067
+ @operations.vault_directory_list_form_revisions(@native_handle, type_id)
1068
+ end
1069
+
1070
+ # Adds missing standard form definitions and returns the number added.
1071
+ def seed_forms()
1072
+ @operations.vault_directory_seed_forms(@native_handle)
1073
+ end
1074
+
1075
+ # Stores password.
1076
+ def remember_password(id, password)
1077
+ @operations.vault_directory_remember_password(@native_handle, id, password)
1078
+ end
1079
+
1080
+ # Returns the Lockbox password encrypted inside this Vault.
1081
+ def remembered_password(id)
1082
+ @operations.vault_directory_remembered_password(@native_handle, id)
1083
+ end
1084
+
1085
+ # Releases the native resources held by this object.
1086
+ def free()
1087
+ @operations.vault_directory_free(@native_handle)
1088
+ @native_handle = nil
1089
+ end
1090
+
1091
+ end
1092
+
1093
+ # A read-only Vault view that can retrieve stored credentials.
1094
+ class ReadOnlyVault < OwnedHandle
1095
+ # Load an existing private key; close it after use.
1096
+ def load_private_key(name)
1097
+ ContactKeyPair.new(@operations, @operations.vault_read_only_load_private_key(@native_handle, name))
1098
+ end
1099
+
1100
+ # Load a stored historical private-key generation.
1101
+ def load_private_key_generation(name, index)
1102
+ ContactKeyPair.new(@operations, @operations.vault_read_only_load_private_key_generation(@native_handle, name, index))
1103
+ end
1104
+
1105
+ # Load an existing Profile signing key without creating one.
1106
+ def load_profile_signing_key(name)
1107
+ ProfileSigningKeyPair.new(@operations, @operations.vault_read_only_load_owner_signing_key(@native_handle, name))
1108
+ end
1109
+
1110
+ # Load a stored historical Profile signing-key generation.
1111
+ def load_profile_signing_key_generation(name, index)
1112
+ ProfileSigningKeyPair.new(@operations, @operations.vault_read_only_load_owner_signing_key_generation(@native_handle, name, index))
1113
+ end
1114
+
1115
+ # Use temporary password-profile bytes during the block, then wipe them.
1116
+ def with_profile_password(name, &callback)
1117
+ @operations.vault_read_only_load_profile_password(@native_handle, name, &callback)
1118
+ end
1119
+
1120
+ # Use remembered Lockbox password bytes, or return nil when absent.
1121
+ def with_remembered_password(lockbox_id, &callback)
1122
+ @operations.vault_read_only_remembered_password(@native_handle, lockbox_id, &callback)
1123
+ end
1124
+ # Lists profile names.
1125
+ def list_profile_names()
1126
+ @operations.vault_read_only_list_profile_names(@native_handle)
1127
+ end
1128
+
1129
+ # Lists contact names.
1130
+ def list_contact_names()
1131
+ @operations.vault_read_only_list_contact_names(@native_handle)
1132
+ end
1133
+
1134
+ # Lists form aliases.
1135
+ def list_form_aliases()
1136
+ @operations.vault_read_only_list_form_aliases(@native_handle)
1137
+ end
1138
+
1139
+ # Lists known lockboxes.
1140
+ def list_known_lockboxes()
1141
+ @operations.vault_read_only_list_known_lockboxes(@native_handle)
1142
+ end
1143
+
1144
+ # Resolve metadata; verify target identity when opening its path.
1145
+ def resolve_lockbox_alias(name)
1146
+ @operations.vault_read_only_resolve_lockbox_alias(@native_handle, name)
1147
+ end
1148
+
1149
+ # List persisted aliases without opening target archives.
1150
+ def list_lockbox_aliases()
1151
+ @operations.vault_read_only_list_lockbox_aliases(@native_handle)
1152
+ end
1153
+
1154
+ # Releases the native resources held by this object.
1155
+ def free()
1156
+ @operations.vault_read_only_free(@native_handle)
1157
+ end
1158
+
1159
+ end
1160
+
1161
+ # Client for the session service that temporarily caches unlock and signing keys.
1162
+ class Agent
1163
+ # Returns the initialize.
1164
+ def initialize(operations)
1165
+ @operations = operations
1166
+ end
1167
+
1168
+ # Reports whether running.
1169
+ def is_running()
1170
+ @operations.vault_is_running()
1171
+ end
1172
+
1173
+ # Removes all.
1174
+ def forget_all()
1175
+ @operations.vault_forget_all()
1176
+ end
1177
+
1178
+ # Runs the session agent server until it is stopped.
1179
+ def serve()
1180
+ @operations.vault_agent_serve()
1181
+ end
1182
+
1183
+ # Verifies transport.
1184
+ def verify_transport()
1185
+ @operations.vault_agent_verify_transport()
1186
+ end
1187
+
1188
+ # Returns get.
1189
+ def get(id)
1190
+ @operations.vault_agent_get(id)
1191
+ end
1192
+
1193
+ # Stores put.
1194
+ def put(id, key)
1195
+ @operations.vault_agent_put(id, key)
1196
+ end
1197
+
1198
+ # Removes forget.
1199
+ def forget(id)
1200
+ @operations.vault_agent_forget(id)
1201
+ end
1202
+
1203
+ # Stops stop.
1204
+ def stop()
1205
+ @operations.vault_agent_stop()
1206
+ end
1207
+
1208
+ # Starts start.
1209
+ def start()
1210
+ @operations.vault_agent_start()
1211
+ end
1212
+
1213
+ # Lists entries below path, optionally including descendants.
1214
+ def list()
1215
+ @operations.vault_agent_list()
1216
+ end
1217
+
1218
+ # Reports how the platform handles agent expiry during system sleep.
1219
+ def sleep_support()
1220
+ @operations.vault_agent_sleep_support()
1221
+ end
1222
+
1223
+ # Returns vault unlock key.
1224
+ def get_vault_unlock_key(vault_id)
1225
+ @operations.vault_agent_get_vault_unlock_key(vault_id)
1226
+ end
1227
+
1228
+ # Stores vault unlock key.
1229
+ def put_vault_unlock_key(vault_id, key, ttl_seconds)
1230
+ @operations.vault_agent_put_vault_unlock_key(vault_id, key, ttl_seconds)
1231
+ end
1232
+
1233
+ # Removes vault unlock key.
1234
+ def forget_vault_unlock_key(vault_id)
1235
+ @operations.vault_agent_forget_vault_unlock_key(vault_id)
1236
+ end
1237
+
1238
+ # Returns the cached signing identity for a Vault Profile.
1239
+ def profile_signing_key(vault_id, profile)
1240
+ ProfileSigningKeyPair.new(@operations, @operations.vault_agent_get_owner_signing_key(vault_id, profile))
1241
+ end
1242
+
1243
+ # Caches a signing identity for a Vault Profile.
1244
+ def cache_profile_signing_key(vault_id, profile, key, ttl_seconds)
1245
+ @operations.vault_agent_put_owner_signing_key(vault_id, profile, key.native_handle, ttl_seconds)
1246
+ end
1247
+
1248
+ # Removes a cached signing identity for a Vault Profile.
1249
+ def forget_profile_signing_key(vault_id, profile)
1250
+ @operations.vault_agent_forget_owner_signing_key(vault_id, profile)
1251
+ end
1252
+
1253
+ # Starts activity.
1254
+ def begin_activity(kind)
1255
+ AgentActivity.new(@operations, @operations.vault_agent_begin_activity(kind))
1256
+ end
1257
+
1258
+ # Stops activity.
1259
+ def end_activity(handle)
1260
+ @operations.vault_agent_end_activity(handle.native_handle)
1261
+ end
1262
+
1263
+ end
1264
+
1265
+ # A token kept alive while an operation needs secrets cached by the agent.
1266
+ class AgentActivity < OwnedHandle
1267
+ end
1268
+
1269
+ # Access to the platform credential store for a scoped Vault passphrase.
1270
+ class Platform
1271
+ # Returns the initialize.
1272
+ def initialize(operations)
1273
+ @operations = operations
1274
+ end
1275
+
1276
+ # Returns availability and user presence guarantees for platform storage.
1277
+ def status()
1278
+ @operations.vault_platform_status()
1279
+ end
1280
+
1281
+ # Sets scope.
1282
+ def set_scope(scope)
1283
+ @operations.vault_platform_set_scope(scope)
1284
+ end
1285
+
1286
+ # Removes password.
1287
+ def forget_password()
1288
+ @operations.vault_platform_forget_password()
1289
+ end
1290
+
1291
+ # Stores password.
1292
+ def put_password(password)
1293
+ @operations.vault_platform_put_password(password)
1294
+ end
1295
+
1296
+ # Enables storage of the Vault passphrase in platform credentials.
1297
+ def enable()
1298
+ @operations.vault_platform_enable()
1299
+ end
1300
+
1301
+ # Disables platform credential use without deleting the stored value.
1302
+ def disable()
1303
+ @operations.vault_platform_disable()
1304
+ end
1305
+
1306
+ # Reports whether platform credential use is disabled.
1307
+ def disabled()
1308
+ @operations.vault_platform_disabled()
1309
+ end
1310
+
1311
+ # Returns password.
1312
+ def get_password()
1313
+ @operations.vault_platform_get_password()
1314
+ end
1315
+
1316
+ end
1317
+
1318
+ # A session that opens lockboxes by host path, caches passwords, and closes local files.
1319
+ class LocalSession < OwnedHandle
1320
+ # Creates Lockbox password.
1321
+ def create_lockbox_password(path, password)
1322
+ Lockbox.new(@operations, @operations.vault_create_lockbox_password(@native_handle, path, password))
1323
+ end
1324
+
1325
+ # Opens Lockbox password.
1326
+ def open_lockbox_password(path, password)
1327
+ Lockbox.new(@operations, @operations.vault_open_lockbox_password(@native_handle, path, password))
1328
+ end
1329
+
1330
+ # Creates lockbox content key.
1331
+ def create_lockbox_content_key(path, content_key, signing_key)
1332
+ Lockbox.new(@operations, @operations.vault_create_lockbox_content_key(@native_handle, path, content_key, signing_key.native_handle))
1333
+ end
1334
+
1335
+ # Creates lockbox contact.
1336
+ def create_lockbox_contact(path, contact, name, signing_key)
1337
+ Lockbox.new(@operations, @operations.vault_create_lockbox_contact(@native_handle, path, contact.native_handle, name, signing_key.native_handle))
1338
+ end
1339
+
1340
+ # Opens lockbox content key.
1341
+ def open_lockbox_content_key(path, content_key, signing_key)
1342
+ Lockbox.new(@operations, @operations.vault_open_lockbox_content_key(@native_handle, path, content_key, signing_key.native_handle))
1343
+ end
1344
+
1345
+ # Stores Lockbox password.
1346
+ def cache_lockbox_password(path, password, ttl_seconds)
1347
+ @operations.vault_cache_lockbox_password(@native_handle, path, password, ttl_seconds)
1348
+ end
1349
+
1350
+ # Releases the native resources held by lockbox.
1351
+ def close_lockbox(path)
1352
+ @operations.vault_close_lockbox(@native_handle, path)
1353
+ end
1354
+
1355
+ # Releases the native resources held by all.
1356
+ def close_all()
1357
+ @operations.vault_close_all(@native_handle)
1358
+ end
1359
+
1360
+ # Releases the native resources held by this object.
1361
+ def free()
1362
+ @operations.vault_free(@native_handle)
1363
+ @native_handle = nil
1364
+ end
1365
+
1366
+ end
1367
+
1368
+ end
1369
+
1370
+ # Reviewed 0.3 facade names. The Fiddle transport classes above remain
1371
+ # implementation details of these domain objects.
1372
+ module Revault
1373
+ module LockboxCacheMode
1374
+ BYTES = 'bytes'; DISABLED = 'disabled'; AUTOMATIC = 'automatic'
1375
+ end
1376
+ module LockboxWorkload
1377
+ INTERACTIVE = 'interactive'; BULK_IMPORT = 'bulk-import'; READ_MOSTLY = 'read-mostly'
1378
+ end
1379
+ module LockboxWorker
1380
+ AUTO = 'auto'; SINGLE = 'single'; THREADS = 'threads'
1381
+ end
1382
+ module AgentActivityKind
1383
+ OPEN = 'open'; CLOSE = 'close'; VARIABLES = 'variables'; FORM = 'form'; RECOVERY = 'recovery'; VAULT = 'vault'
1384
+ end
1385
+ module KeyExportFormat
1386
+ LOCKBOX_PEM = 'lockbox-pem'; JWK = 'jwk'; JWKS = 'jwks'; RAW_HEX = 'raw-hex'
1387
+ end
1388
+
1389
+ # Mutable byte secret that can be wiped after a native operation.
1390
+ class SecretBytes
1391
+ # Copy bytes into owned mutable storage.
1392
+ def initialize(value = ''.b) @bytes = value.to_s.b.dup end
1393
+ # Return a copy of the secret bytes.
1394
+ def to_str = @bytes
1395
+ # Return a defensive copy of the secret bytes.
1396
+ def bytes = @bytes.dup
1397
+ # Wipe the secret in place.
1398
+ def close = @bytes.replace("\0" * @bytes.bytesize)
1399
+ alias dispose close
1400
+ end
1401
+ # UTF-8 secret passphrase with the same wipe semantics as SecretBytes.
1402
+ class SecretString < SecretBytes
1403
+ # Copy a passphrase into mutable UTF-8 storage.
1404
+ def initialize(value = '') super(value.to_s.encode(Encoding::UTF_8)) end
1405
+ end
1406
+
1407
+ # Persistent encrypted local store for profiles, keys, contacts and metadata.
1408
+ class Vault < VaultStore
1409
+ # Constructors for the persistent store lifecycle.
1410
+ class << self
1411
+ # Open an existing store without creating or replacing it.
1412
+ def open(root, vault_passphrase) = Runtime.new.vault_directory_open(root, vault_passphrase.to_str)
1413
+ # Open or create a store at root.
1414
+ def open_or_create(root, vault_passphrase) = Runtime.new.vault_directory_open_or_create(root, vault_passphrase.to_str)
1415
+ # Create a new store at root.
1416
+ def create(root, vault_passphrase) = Runtime.new.vault_directory_replace(root, vault_passphrase.to_str)
1417
+ # Replace an existing store explicitly.
1418
+ def replace(root, vault_passphrase) = Runtime.new.vault_directory_replace(root, vault_passphrase.to_str)
1419
+ end
1420
+ end
1421
+
1422
+ # Shared close convenience for all owned facade handles.
1423
+ class OwnedHandle
1424
+ # Release the native resource.
1425
+ def close = free
1426
+ end
1427
+ # Lockbox-specific close convenience.
1428
+ class Lockbox
1429
+ # Release the lockbox handle.
1430
+ alias close free
1431
+ end
1432
+ # Persistent store close convenience.
1433
+ class VaultStore
1434
+ # Release the store handle.
1435
+ alias close free
1436
+ end
1437
+ # Read-only store close convenience.
1438
+ class ReadOnlyVault
1439
+ # Release the read-only store handle.
1440
+ alias close free
1441
+ end
1442
+ # Local session close convenience.
1443
+ class LocalSession
1444
+ # Release the local session handle.
1445
+ alias close free
1446
+ end
1447
+ # Explicit Session Agent controller with process-local lockbox operations.
1448
+ class AgentSession < Agent
1449
+ # Return the process-wide explicit session controller.
1450
+ def self.instance
1451
+ Revault.ensure_native
1452
+ @instance ||= new(BindingOperations.new)
1453
+ end
1454
+ # Attach this controller to the local native session handle.
1455
+ def initialize(operations)
1456
+ super
1457
+ @local = operations.vault_local
1458
+ end
1459
+ # Remove one cached lockbox key.
1460
+ def close_lockbox(path) = @operations.vault_close_lockbox(@local, path)
1461
+ # Remove all cached lockbox keys.
1462
+ def close_all = @operations.vault_close_all(@local)
1463
+ # Create a password-protected lockbox file.
1464
+ def create_lockbox_password(path, password) = Lockbox.new(@operations, @operations.vault_create_lockbox_password(@local, path, password.to_str))
1465
+ # Open a password-protected lockbox file.
1466
+ def open_lockbox_password(path, password) = Lockbox.new(@operations, @operations.vault_open_lockbox_password(@local, path, password.to_str))
1467
+ # Create a content-key lockbox file.
1468
+ def create_lockbox_content_key(path, content_key, signing_key) = Lockbox.new(@operations, @operations.vault_create_lockbox_content_key(@local, path, content_key, signing_key.native_handle))
1469
+ # Create a contact-addressed lockbox file.
1470
+ def create_lockbox_contact(path, contact, name, signing_key) = Lockbox.new(@operations, @operations.vault_create_lockbox_contact(@local, path, contact.native_handle, name, signing_key.native_handle))
1471
+ # Open a content-key lockbox file.
1472
+ def open_lockbox_content_key(path, content_key, signing_key) = Lockbox.new(@operations, @operations.vault_open_lockbox_content_key(@local, path, content_key, signing_key.native_handle))
1473
+ # Cache a password-derived key for the requested TTL.
1474
+ def cache_lockbox_password(path, password, ttl_seconds) = @operations.vault_cache_lockbox_password(@local, path, password.to_str, ttl_seconds)
1475
+ # Release the local session handle.
1476
+ def close
1477
+ @operations.vault_free(@local) if @local
1478
+ @local = nil
1479
+ end
1480
+ alias free close
1481
+ end
1482
+ end
1483
+
1484
+ # Native handles and operation routing are implementation details of the
1485
+ # application-facing facade above.
1486
+ Revault.private_constant(:OwnedHandle, :Runtime, :VaultStore, :LocalSession)
1487
+ require 'json'
1488
+ module Revault
1489
+ # Full reciprocal exchange. Compare identities and fingerprint independently between calls.
1490
+ module ComparisonChannel
1491
+ IN_PERSON = 'in-person'.freeze
1492
+ KNOWN_PHONE_CALL = 'known-phone-call'.freeze
1493
+ TRUSTED_MESSENGER = 'trusted-messenger'.freeze
1494
+ end
1495
+ # Reciprocal exchange session retaining protected credentials but no Vault lock.
1496
+ class ContactExchange < OwnedHandle
1497
+ # Marshal an internal request to the shared Rust workflow.
1498
+ def execute(request)
1499
+ raise 'Exchange session is closed' unless @native_handle
1500
+ @operations.contact_exchange_execute(@native_handle, JSON.generate(request))
1501
+ end
1502
+ private :execute
1503
+ # Create an invitation; retry pending delivery using list.
1504
+ # Example:
1505
+ # ```
1506
+ # session = api.contact_exchange(vault_path, vault_passphrase)
1507
+ # begin
1508
+ # offer = session.invite(relay_origin, 'default', 'bob@example.com')
1509
+ # session.list if offer.relay_pending
1510
+ # ensure
1511
+ # session.close
1512
+ # end
1513
+ # ```
1514
+ def invite(relay, profile, recipient_email, ttl_hours=24) = execute(operation: 'invite', relay: relay, profile: profile, recipient_email: recipient_email, ttl_hours: ttl_hours).first
1515
+ # Accept with one selected profile's complete public identity.
1516
+ # Example:
1517
+ # ```
1518
+ # session = api.contact_exchange(vault_path, vault_passphrase)
1519
+ # begin
1520
+ # reply = session.accept(invitation_url, 'default')
1521
+ # puts reply.fingerprint
1522
+ # ensure
1523
+ # session.close
1524
+ # end
1525
+ # ```
1526
+ def accept(invitation, profile) = execute(operation: 'accept', invitation: invitation, profile: profile).first
1527
+ # List public status, optionally without relay requests.
1528
+ # Example:
1529
+ # ```
1530
+ # session = api.contact_exchange(vault_path, vault_passphrase)
1531
+ # begin
1532
+ # session.list(true).each { |status| puts status.id }
1533
+ # ensure
1534
+ # session.close
1535
+ # end
1536
+ # ```
1537
+ def list(offline=false) = execute(operation: 'list', offline: offline)
1538
+ # Save both keys after independent full-fingerprint comparison.
1539
+ # Example:
1540
+ # ```
1541
+ # session = api.contact_exchange(vault_path, vault_passphrase)
1542
+ # begin
1543
+ # # After independent identity and full-fingerprint comparison:
1544
+ # session.verify(exchange_id, 'bob', compared_fingerprint, Revault::ComparisonChannel::IN_PERSON)
1545
+ # ensure
1546
+ # session.close
1547
+ # end
1548
+ # ```
1549
+ def verify(id, contact_name, fingerprint, channel) = execute(operation: 'verify', id: id, contact_name: contact_name, fingerprint: fingerprint, channel: channel).first
1550
+ # Authenticate an archive against the saved contact signing key.
1551
+ # Example:
1552
+ # ```
1553
+ # session = api.contact_exchange(vault_path, vault_passphrase)
1554
+ # begin
1555
+ # session.verify_author('alice', received_archive_path)
1556
+ # ensure
1557
+ # session.close
1558
+ # end
1559
+ # ```
1560
+ def verify_author(contact_name, lockbox_path) = execute(operation: 'verify_author', contact_name: contact_name, lockbox_path: lockbox_path)
1561
+
1562
+ # Cancel an owned, unaccepted invitation.
1563
+ # Example:
1564
+ # ```
1565
+ # session = api.contact_exchange(vault_path, vault_passphrase)
1566
+ # begin
1567
+ # session.cancel(exchange_id)
1568
+ # ensure
1569
+ # session.close
1570
+ # end
1571
+ # ```
1572
+ def cancel(id) = execute(operation: 'cancel', id: id)
1573
+ # Forget local state, preserving contacts.
1574
+ # Example:
1575
+ # ```
1576
+ # session = api.contact_exchange(vault_path, vault_passphrase)
1577
+ # begin
1578
+ # session.forget(exchange_id)
1579
+ # ensure
1580
+ # session.close
1581
+ # end
1582
+ # ```
1583
+ def forget(id) = execute(operation: 'forget', id: id)
1584
+ # Release protected credentials without cancelling invitations.
1585
+ # Example:
1586
+ # ```
1587
+ # session = api.contact_exchange(vault_path, vault_passphrase)
1588
+ # begin
1589
+ # puts session.list(true).length
1590
+ # ensure
1591
+ # session.close
1592
+ # end
1593
+ # ```
1594
+ def free
1595
+ @operations.contact_exchange_free(@native_handle) if @native_handle
1596
+ @native_handle=nil
1597
+ end
1598
+ alias close free
1599
+ end
1600
+ end