darkbio-wire 0.10.0

Encrypted protocol between Ark and host
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
// wire-rs: encrypted protocol between Ark and host
// Copyright 2025 Dark Bio AG. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

syntax = "proto3";

package darkbio.wire;

// HostToArk represents a message sent from the host to the Ark via the USB
// connection. It embeds all possible message types to keep the protocol simple.
//
// Every message carries an id and is either a request, its id chosen by the
// host, or the host's response to a request of the Ark, echoing the Ark's id.
// To avoid id collisions, hosts allocate odd ids and Arks even ones, so a
// receiver can tell a response from a request by the parity of the id alone.
message HostToArk {
  // Enveloping fields, reserved range 0x001-0x0ff
  uint64 id = 1;           // Request identifier, the host's own or responded to
  optional Error err = 2;  // Error encountered while serving the Ark's request (if any)

  // Body of the message, a request of the host or its response to a request of
  // the Ark. The tags are grouped by area, each area with its own range.
  oneof content {

    // Special factory message

    OnboardingRequest onboard = 0x100; // Installs the signed device attestation at manufacturing

    // System messages, reserved range 0x101-0x200

    DeviceInfoRequest      device_info = 0x101;       // Fetches the hardware and firmware versions
    CloudSyncStartRequest  cloud_sync_start = 0x102;  // Starts a cloud sync with the attested cloud keys
    CloudSyncFinishRequest cloud_sync_finish = 0x103; // Finishes a cloud sync with the signed server time
    GenuinityProofRequest  genuinity_proof = 0x104;   // Requests a proof of the device's genuinity

    // Firmware update messages, reserved range 0x201-0x300

    FirmwareUpdatePrepRequest    firmware_update_prep = 0x201;    // Prepares an update, aborting any in progress
    FirmwareUpdateInitRequest    firmware_update_init = 0x202;    // Initiates a prepared update with the sealed firmware key
    FirmwareUpdateUploadRequest  firmware_update_upload = 0x203;  // Appends a chunk of the firmware archive
    FirmwareUpdateVerifyRequest  firmware_update_verify = 0x204;  // Decrypts and verifies the uploaded firmware
    FirmwareUpdateInstallRequest firmware_update_install = 0x205; // Installs the verified firmware and reboots

    // Pairing messages, reserved range 0x301-0x400

    PairingAuthRequest           pairing_auth = 0x301;            // Asks the Ark to authorize a pairing rendezvous
    PairingSetAppIdentityRequest pairing_set_app_id = 0x302;      // Injects the companion app's identity, relayed by the cloud
    PairingSetAppStorageRequest  pairing_set_app_storage = 0x303; // Injects the companion app's storage key material
    PairingAckArkStorageRequest  pairing_ack_ark_storage = 0x304; // Confirms the app received the Ark's key material
    PairingAcceptanceRequest     pairing_accept = 0x305;          // Waits for the user to accept the pairing
    PairingCompletionRequest     pairing_complete = 0x306;        // Waits for the Ark to finish pairing maintenance

    // Relay messages, reserved range 0x401-0x500

    RelayJoinRequest      relay_join = 0x401; // Asks the Ark to authorize joining the app relay
    RelayAppToArkRequest  relay_req = 0x402;  // Opaque request from the companion app to the Ark
    RelayAppToArkResponse relay_res = 0x403;  // Opaque response from the companion app to an Ark request

    // Operational messages, reserved range 0x501-0x600

    UnlockRequest                unlock = 0x501;            // Starts the unlock, confirmed through the app
    ExecutionUploadStartRequest  exec_upload_start = 0x502; // Begins a chunked upload of an app to execute
    ExecutionUploadChunkRequest  exec_upload_chunk = 0x503; // Appends a chunk to a pending app upload
    ExecutionScheduleRequest     exec_sched = 0x504;        // Runs an uploaded app, confirmed through the app
    ExecutionStatusRequest       exec_status = 0x505;       // Checks on a running app
    ExecutionCancelRequest       exec_cancel = 0x506;       // Cancels an app run or a pending upload

    // Slot messages, reserved range 0x601-0x700

    SlotListRequest          slot_list = 0x601;           // Lists the state of every data slot
    SlotRepairRequest        slot_repair = 0x602;         // Resets a slot to empty whatever its state
    SlotDeleteRequest        slot_delete = 0x603;         // Removes the contents of a filled slot
    SlotIdentifyRequest      slot_identify = 0x604;       // Asks the Ark to identify a file from its first chunk
    SlotUploadStartRequest   slot_upload_start = 0x605;   // Starts uploading a file into a slot
    SlotUploadChunkRequest   slot_upload_chunk = 0x606;   // Appends a chunk to a pending slot upload
    SlotUploadCancelRequest  slot_upload_cancel = 0x607;  // Aborts a pending slot upload
    SlotUploadProcessRequest slot_upload_process = 0x608; // Marks an upload complete and polls its processing

    // Dataset messages, reserved range 0x701-0x800

    DatasetPathsRequest dataset_paths = 0x701; // Maps every path an app can read, from the dataset view

    // Unreleased messages served by development firmware only, carried opaquely
    // so the public protocol is untouched while they are still under development.
    // Their schema is private and production Arks refuse the envelope.
    bytes develop = 0x1000;
  }
}

// ArkToHost represents a message sent from the Ark to the host via the USB
// connection. It embeds all possible message types to keep the protocol simple.
//
// Every message carries an id and is either a request, its id chosen by the
// Ark, or the Ark's response to a request of the host, echoing the host's id.
// To avoid id collisions, hosts allocate odd ids and Arks even ones, so a
// receiver can tell a response from a request by the parity of the id alone.
message ArkToHost {
  // Enveloping fields, reserved range 0x001-0x0ff
  uint64 id = 1;           // Request identifier, responded to or the Ark's own
  optional Error err = 2;  // Error encountered while serving the host's request (if any)

  // Body of the message, a response of the Ark to a request of the host or a
  // request of its own. The tags are grouped by area, each area with its own range.
  oneof content {

    // Special factory message

    OnboardingResponse onboard = 0x100; // Acknowledges the onboarding

    // System messages, reserved range 0x101-0x200

    DeviceInfoResponse      device_info = 0x101;       // Hardware and firmware versions of the Ark
    CloudSyncStartResponse  cloud_sync_start = 0x102;  // Challenge for the cloud to sign along its time
    CloudSyncFinishResponse cloud_sync_finish = 0x103; // Timestamp the Ark accepted from the cloud
    GenuinityProofResponse  genuinity_proof = 0x104;   // Encrypted proof of the Ark's genuinity for the cloud

    // Firmware update messages, reserved range 0x201-0x300

    FirmwareUpdatePrepResponse    firmware_update_prep = 0x201;    // Ephemeral key to receive the firmware key with, sealed for the cloud
    FirmwareUpdateInitResponse    firmware_update_init = 0x202;    // Acknowledges the initiated update
    FirmwareUpdateUploadResponse  firmware_update_upload = 0x203;  // Acknowledges the appended firmware chunk
    FirmwareUpdateVerifyResponse  firmware_update_verify = 0x204;  // Acknowledges the verified firmware
    FirmwareUpdateInstallResponse firmware_update_install = 0x205; // Acknowledges the installed firmware

    // Pairing messages, reserved range 0x301-0x400

    PairingAuthResponse             pairing_auth = 0x301;            // Signed authorization for the cloud to open a rendezvous
    PairingSetAppIdentityResponse   pairing_set_app_id = 0x302;      // Acknowledges the accepted app identity
    PairingSetAppStorageResponse    pairing_set_app_storage = 0x303; // Ark device infos and key material, sealed for the app
    PairingAckArkStorageResponse    pairing_ack_ark_storage = 0x304; // Acknowledges the app's receipt of the Ark's keys
    PairingAcceptanceResponse       pairing_accept = 0x305;          // Signed confirmation that the user accepted the pairing
    PairingCompletionResponse       pairing_complete = 0x306;        // Signed confirmation that the Ark finished pairing

    // Relay messages, reserved range 0x401-0x500

    RelayJoinResponse     relay_join = 0x401; // Signed authorization for the cloud to join the relay
    RelayArkToAppRequest  relay_req = 0x402;  // Opaque request from the Ark to the companion app
    RelayArkToAppResponse relay_res = 0x403;  // Opaque response from the Ark to an app request
    RelayAppToArkFailure  relay_fail = 0x404; // Protocol violation found in an app response, for debugging

    // Operational messages, reserved range 0x501-0x600

    UnlockResponse                unlock = 0x501;            // Acknowledges the completed unlock
    ExecutionUploadStartResponse  exec_upload_start = 0x502; // Task id for the chunk, schedule and cancel messages
    ExecutionUploadChunkResponse  exec_upload_chunk = 0x503; // Acknowledges the appended app chunk
    ExecutionScheduleResponse     exec_sched = 0x504;        // Acknowledges the authorized and started execution
    ExecutionStatusResponse       exec_status = 0x505;       // Whether the app still runs, with its result once done
    ExecutionCancelResponse       exec_cancel = 0x506;       // Acknowledges the cancelled run or upload

    // Slot messages, reserved range 0x601-0x700

    SlotListResponse          slot_list = 0x601;           // Current state of every data slot
    SlotRepairResponse        slot_repair = 0x602;         // Acknowledges the reset slot
    SlotDeleteResponse        slot_delete = 0x603;         // Acknowledges the deleted slot
    SlotIdentifyResponse      slot_identify = 0x604;       // Identification of the file's first chunk
    SlotUploadStartResponse   slot_upload_start = 0x605;   // Session id of the approved upload
    SlotUploadChunkResponse   slot_upload_chunk = 0x606;   // Acknowledges the appended slot chunk
    SlotUploadCancelResponse  slot_upload_cancel = 0x607;  // Acknowledges the aborted upload
    SlotUploadProcessResponse slot_upload_process = 0x608; // Processing progress of the completed upload

    // Dataset messages, reserved range 0x701-0x800

    DatasetPathsResponse dataset_paths = 0x701; // Every path an app can read, with its description, format and examples

    // Unreleased messages emitted by development firmware only, in response to
    // a develop request, carried opaquely so the public protocol is untouched
    // while they are still under development. Their schema is private.
    bytes develop = 0x1000;
  }
}

// Error is sent along a response to a failed request.
//
// Codes 0x00 through 0xff (inclusive) are reserved for protocol-wide errors.
// Codes 0x100 and above are defined by the request type and may be reused
// with different meanings for different requests. The message text is human,
// readable error, not a stable code.
message Error {
  uint64 code = 1; // Error code for programmatic interpretation
  string msg = 2;  // Error message for user interfacing
}

// ReservedErrors names the assigned protocol-wide errors in the reserved range
// 0x00 to 0xff (inclusive). No code in this range may be assigned a request
// specific meaning. Assigned codes must not be repurposed.
enum ReservedErrors {
  // A failure without a standardized reason. Zero does not indicate success;
  // the presence of an Error in the response indicates failure.
  RESERVED_ERRORS_UNSPECIFIED = 0;

  // The request's responder was released without providing an application reply.
  RESERVED_ERRORS_UNANSWERED = 1;

  // The peer does not know this request. Emitted by the protocol layer itself
  // when a request's content is not in its schema.
  RESERVED_ERRORS_UNKNOWN = 2;

  // The peer knows this request but never serves it in this build or role,
  // firmware updates on an emulator or develop envelopes on production.
  RESERVED_ERRORS_UNSUPPORTED = 3;

  // The peer serves this request but not in its current state, before cloud
  // sync or pairing. It may once the state changes.
  RESERVED_ERRORS_UNAVAILABLE = 4;

  // The peer serves this request but the owner refused the approval it asked
  // for on the phone. The owner's choice, not a failure.
  RESERVED_ERRORS_UNAUTHORIZED = 5;

  // The peer serves this request but the approval it asked for did not arrive
  // before its window ran out, on the phone or at the button.
  RESERVED_ERRORS_UNCONFIRMED = 6;
}


// OnboardingRequest is a vendor utility to onboard an Ark. Currently it contains
// the signed device genuinity attestation (certificate).
//
// Note, as this method is only used during initial device setup, there is no API
// compatibility guarantee, it will evolve with the factory tooling.
message OnboardingRequest {
  bytes device_attestation = 1; // Signed device genuinity attestation (CWT) to install
}
// OnboardingResponse is the acknowledgement of the onboarding.
message OnboardingResponse {
}


// DeviceInfoRequest is sent by the host to fetch the Ark's device stats.
message DeviceInfoRequest {
}
// DeviceInfoResponse returns various hardware and software version information.
message DeviceInfoResponse {
  uint32 version_id = 1;   // Ark device version (defines the major features)
  string version_str = 2;  // Ark device version label (as known by the device)
  uint32 revision_id = 3;  // Ark device revision (defines the minor differences)
  string revision_str = 4; // Ark device revision label (as known by the device)

  reserved 5; // Carried the Ark's hex identity before @darkbio/crypto, derived since
  reserved 6; // Carried the Ark's pubkey before the encrypted wire, in handshake now

  string firmware_version = 7; // Current firmware version (X.Y.Z-commit)
  uint64 firmware_publish = 8; // Current firmware publish unix timestamp

  bool   cloud_synced = 9;  // Whether a cloud identity was accepted since boot
  uint64 cloud_clock = 10;  // Current device clock unix timestamp, set by the cloud sync

  bool paired   = 11; // Whether the Ark is paired with a companion app (user data initialized)
  bool unlocked = 12; // Whether the user data storage is open (false if unpaired)
}


// CloudSyncStartRequest requests the device to start a synchronization procedure
// against the cloud servers to establish the current time as well as the currently
// active cloud identity.
message CloudSyncStartRequest {
  bytes signer = 1; // Post-quantum CWT attestation for the server signing (xDSA) key
  bytes crypto = 2; // Post-quantum CWT attestation for the server encryption (xHPKE) key
}
// CloudSyncStartResponse is an initiation of a cloud sync from the device, authed
// to the requested cloud identity.
message CloudSyncStartResponse {
  bytes challenge = 1; // Random nonce to avoid malicious host machines setting bad times
}

// CloudSyncFinishRequest is the completion of a previously initiated cloud sync
// procedure, this time being signed by a single identity currently used by the
// cloud.
message CloudSyncFinishRequest {
  uint64 unixmilli = 1; // Unix timestamp from the server in milliseconds
  bytes  signature = 2; // SignAt[Cloud]["cloudsync-v1"][unixmilli/1000](CBOR(challenge))
}
// CloudSyncFinishResponse is the acknowledgement whether the device accepted the
// cloud sync from the server or rejected for some reason.
message CloudSyncFinishResponse {
  uint64 accepted = 1; // Timestamp that was accepted and set
}


// GenuinityProofRequest requests the device to generate a cryptographic proof of
// its own authenticity.
message GenuinityProofRequest {
}
// GenuinityProofResponse is the device genuinity proof, an encrypted signature
// of the device.
message GenuinityProofResponse {
  bytes proof = 1; // Seal[Ark->Cloud]["genuinity-v1"][null](null)
}


// FirmwareUpdatePrepRequest prepares a firmware update procedure. If any previous
// update procedure was in progress, it is aborted.
message FirmwareUpdatePrepRequest {
  string version = 1; // Version string to update to
  bytes sha256 = 2;   // SHA256 hash of the (encrypted) firmware archive
  uint64 bytes = 3;   // Number of bytes (progress purposes, ignored otherwise)
}
// FirmwareUpdatePrepResponse confirms whether a new firmware update process was
// started, or if the request was rejected and why.
//
// If it was accepted, a new ephemeral encryption identity is generated by the Ark
// to receive the firmware access key to; and sent along with the firmware infos.
message FirmwareUpdatePrepResponse {
  bytes auth = 1; // Seal[Ark->Cloud]["firmware-v1"][[version, sha256]](temp-key)
}

// FirmwareUpdateInitRequest initiates a previously prepared firmware update
// procedure by providing the authenticated and encrypted firmware key.
//
// Note, if the initiation is rejected, the entire update process is torn down.
message FirmwareUpdateInitRequest {
  bytes access = 1; // Seal[Cloud->temp-key]["firmware-v1"][[version, sha256]](sym-key)
}
// FirmwareUpdateInitResponse confirms whether a new firmware update process
// was started, or if the request was rejected and why.
//
// Note, if the init is rejected, the entire update process is torn down.
message FirmwareUpdateInitResponse {
}

// FirmwareUpdateUploadRequest requests appending a new chunk of data to the
// currently pending firmware upload process.
message FirmwareUpdateUploadRequest {
  bytes chunk = 1; // Chunk of firmware blob to append (reasonably capped)
}
// FirmwareUpdateUploadResponse is the response whether the requested chunk was
// accepted or rejected.
//
// Note, if the upload is rejected, the entire update process is torn down.
message FirmwareUpdateUploadResponse {
}

// FirmwareUpdateVerifyRequest requests decrypting the uploaded firmware and
// verifying its contents, preparing for the last step of actually installing
// the firmware update.
message FirmwareUpdateVerifyRequest {
}
// FirmwareUpdateVerifyResponse is the response whether the firmware just uploaded
// passed all verifications and is ready for application.
//
// Note, if the verification is rejected, the entire update process is torn down.
message FirmwareUpdateVerifyResponse {
}

// FirmwareUpdateInstallRequest requests the currently pending (but already
// verified) firmware to be applied to disk. This message will cause the device
// to reboot if applied successfully.
message FirmwareUpdateInstallRequest {
}
// FirmwareUpdateInstallResponse is the response whether the firmware was applied
// successfully or not.
message FirmwareUpdateInstallResponse {
}


// PairingAuthRequest requests the initiation of a pairing.
message PairingAuthRequest {
}
// PairingAuthResponse responds whether the device is in a state compatible with
// pairing (i.e. reset) and if so, it signs an authorization for the server to open
// a new rendezvous point; containing the xHPKE public key to use for encrypting
// messages to this Ark after pairing.
message PairingAuthResponse {
  bytes auth = 1;   // Seal[Ark->Cloud]["pairing-v1"][null](pair-key)
  bytes fprint = 2; // Fingerprint of the pair-key to transmit to the app out-of-protocol
}

// PairingSetAppIdentityRequest injects the companion app's identity, relayed
// by the cloud.
message PairingSetAppIdentityRequest {
  bytes identity = 1; // Seal[Cloud->App]["pairing-v1:identity"][[ark_signer_id, ark_crypto_id]]([app_signer, app_crypto])
}
// PairingSetAppIdentityResponse confirms whether the remote identity was
// accepted by the Ark or not.
message PairingSetAppIdentityResponse {
}
// PairingSetAppStorageRequest injects the app's storage key material into the
// pairing flow.
message PairingSetAppStorageRequest {
  bytes app_key = 1; // Seal[App->Ark]["pairing-v1:storage"][[ark_signer_id, ark_crypto_id, app_signer_id, app_crypto_id]]([app_keymat])
}
// PairingSetAppStorageResponse confirms whether the remote storage material
// was accepted, and if so, bundles the ark device infos and ark key material.
message PairingSetAppStorageResponse {
  bytes ark_keys = 1; // Seal[Ark->App]["pairing-v1:storage"][[ark_signer_id, ark_crypto_id, app_signer_id, app_crypto_id, app_keymat]]([hw_ver, hw_rev, fw_ver, fw_pub, ark_keymat])
}
// PairingAckArkStorageRequest confirms from the app that the Ark's key material
// was received correctly.
message PairingAckArkStorageRequest{
  bytes app_ack = 1; // Seal[App->Ark]["pairing-v1:storage"][[ark_signer_id, ark_crypto_id, app_signer_id, app_crypto_id, ark_keymat]](null)
}
// PairingAckArkStorageResponse confirms that the key ack was accepted.
message PairingAckArkStorageResponse{
}

// PairingAcceptanceRequest is a blocking poller to wait until the user accepts
// a pairing request or it times out.
message PairingAcceptanceRequest {
}
// PairingAcceptanceResponse returns whether the user accepted the pairing or
// if it timed out. At this point, the device still needs to format itself.
message PairingAcceptanceResponse {
  bytes confirm = 1; //Seal[Ark->Cloud]["pairing-v1:accept"][[ark_signer_id, ark_crypto_id, app_signer_id, app_crypto_id]](null)
}

// PairingCompletionRequest is a blocking poller to wait until the device finishes
// any pairing maintenance operation (e.g. encrypting itself).
message PairingCompletionRequest {
}
// PairingCompletionResponse returns whether the device finished preparing for
// live operation.
message PairingCompletionResponse {
  bytes confirm = 1; // Seal[Ark->Cloud]["pairing-v1:complete"][[ark_signer_id, ark_crypto_id, app_signer_id, app_crypto_id]](null)
}


// RelayJoinRequest requests authorization to join the communication relay with
// the companion app.
message RelayJoinRequest {
}
// RelayJoinResponse responds whether the device is in a state compatible with
// relaying (i.e. paired) and if so, it signs an authorization for the server
// to join the rendezvous point.
message RelayJoinResponse {
  bytes auth = 1; // Seal[Ark->Cloud]["relaying-v1"][null](null)
}

// RelayAppToArkRequest is an opaque request from the companion app that the Ark may
// respond to, or may flat out reject.
message RelayAppToArkRequest {
  uint64 id = 1;  // Application layer request ID from the app
  bytes  req = 2; // Seal[App->Ark]["relaying-v1:request"][id]([method, [params...]]))
}
// RelayArkToAppResponse is an opaque response from the Ark to the companion app to an
// opaque request.
message RelayArkToAppResponse {
  uint64 id = 1;  // Application layer request ID from the app being responding to
  bytes  res = 2; // Seal[Ark->App]["relaying-v1:response"][id]([result, [err_code, err_str]])
}
// RelayAppToArkFailure is returned if a low level protocol violation is detected
// when an app-to-ark response was processed.
//
// This is not an actionable message, rather it's just a way to expose a protocol
// failure / violation to the calling app for debugging purposes.
message RelayAppToArkFailure {
  string error = 1; // Reason for rejecting the response at the protocol level
}

// RelayArkToAppRequest is an opaque request from the Ark to the companion app, which
// will generally be sent as an interim response to some other request, requiring
// authorization from the app side.
message RelayArkToAppRequest {
  uint64 id = 1;  // Application layer request ID from the ark
  bytes  req = 2; // Seal[Ark->App]["relaying-v1:request"][id]([method, [params...]])
}
// RelayAppToArkResponse is an opaque response from the companion app to an opaque
// Ark request. The ark will respond with the deferred response to the original
// request.
message RelayAppToArkResponse {
  uint64 id = 1;  // Application layer request ID from the ark being responding to
  bytes  res = 2; // Seal[App->Ark]["relaying-v1:response"][id]([result, [err_code, err_str]])
}


// UnlockRequest requests the device to start the unlock procedure. This request
// is async, potentially returning a response only after confirming with the app.
//
// It will trigger sending a RelayArkToAppRequest with the request content:
//   - method: "unlock"
//   - params: []
//
// The request expects a RelayAppToArkResponse with the response content:
//
//   If approved:
//     - result = key: [u8; 32] // Shared secret from the pairing protocol
//     - err_code: 0
//     - err_str:  ""
//
//   If denied:
//     - result = key = [0u8; 32] // All zeroes (signals a denial)
//     - err_code: 0              // No error, deny is valid user choice
//     - err_str:  ""             // No error, deny is valid user choice
//
// where:
//   - key: Symmetric key for accessing the storage partition
//
// Note, relayed messages are encrypted and authenticated on the timestamp and
// request/response id, so there's no need for further complications.
message UnlockRequest{
}
// UnlockResponse contains whether the unlock was successfully executed.
message UnlockResponse{
}

// ExecutionUploadStartRequest begins a chunked upload of a WASM binary to
// execute. The declared size is used to pre-allocate the receive buffer and
// to reject oversized uploads early; the actual bytes are sent via subsequent
// ExecutionUploadChunkRequest messages.
message ExecutionUploadStartRequest{
  uint64 bytes = 1; // Total binary size in bytes
}
// ExecutionUploadStartResponse contains the task id to use for subsequent
// chunk, schedule and cancel messages.
message ExecutionUploadStartResponse{
  uint64 taskid = 1; // Task id to stream chunks into
}

// ExecutionUploadChunkRequest appends a chunk of data to a pending upload.
// Chunks must be appended in order and must not overrun the declared size.
message ExecutionUploadChunkRequest{
  uint64 taskid = 1; // Task id to append to
  bytes chunk = 2;   // Data chunk to append (reasonably capped)
}
// ExecutionUploadChunkResponse is an empty ack of the chunk upload request.
message ExecutionUploadChunkResponse{
}

// ExecutionScheduleRequest finalizes an upload and requests the device to
// execute the uploaded 3rd party app. The upload must have been completed
// (i.e. the sum of chunk sizes matches the declared size).
//
// This request is async, potentially returning a response only after confirming
// with the app.
//
// It will trigger sending a RelayArkToAppRequest with the request content:
//   - method: "execute"
//   - params: [task: string]
//
// The request expects a RelayAppToArkResponse with the response content:
//
//   If approved:
//     - result = approval = true  // CBOR boolean
//     - err_code: 0
//     - err_str:  ""
//
//   If denied:
//     - result = approval = false // CBOR boolean
//     - err_code: 0              // No error, deny is valid user choice
//     - err_str:  ""             // No error, deny is valid user choice
//
// where:
//   - approval: Whether the user approved or denied the execution request
//
// Note, relayed messages are encrypted and authenticated on the timestamp and
// request/response id, so there's no need for further complications.
message ExecutionScheduleRequest{
  uint64 taskid = 1; // Task id returned by ExecutionUploadStartResponse
}
// ExecutionScheduleResponse acks that the execution was authorized and the
// task is now running. The host already holds the task id from the preceding
// ExecutionUploadStartResponse.
message ExecutionScheduleResponse{
}

// ExecutionCancelRequest cancels a previously started 3rd party app run or
// an in-progress upload.
message ExecutionCancelRequest{
  uint64 taskid = 1; // Task id to cancel
}

// ExecutionCancelResponse contains the data gathered during an app's execution.
message ExecutionCancelResponse{
}

// ExecutionStatusRequest checks the execution status of a previously started
// 3rd party app run.
message ExecutionStatusRequest{
  uint64 taskid = 1; // Pending execution ID to check for updates
}

// ExecutionResultResponse contains the data gathered during an app's execution.
message ExecutionResultResponse{
  string app_name = 2; // Name of the executed app
  string app_version = 3; // Version of the executed app
  bool success = 4; // Whether the task finished successfully
  bytes  stdout = 5;  // Raw standard output of the app
  bytes  stderr = 6;  // Raw standard error the app (only in develop mode)
}

// ExecutionStatusResponse contains the status of a started 3rd party app
// execution.
message ExecutionStatusResponse{
  bool pending = 1; // Whether the app is still running
  optional ExecutionResultResponse result = 2; // The result of the execution
}

// SlotKind identifies a slot on the device. Each value corresponds to a unique
// data type that the device can store and manage.
enum SlotKind {
  SLOT_UNSPECIFIED      = 0; // Never sent, an unset kind
  SLOT_REFERENCE_GENOME = 1; // Human reference genome assembly
  SLOT_GENE_ANNOTATIONS = 2; // Gene-to-coordinate mapping database
  SLOT_SNP_INDEL_CALLS  = 3; // User's SNP/indel variant calls
  SLOT_VARIANT_CATALOG  = 4; // Variant catalog (rsID-to-position), from dbSNP
}

// SlotOrigin describes the nature of the data in a slot, determining what
// actions are available to the user for filling it.
enum SlotOrigin {
  ORIGIN_UNSPECIFIED = 0; // Never sent, an unset origin
  ORIGIN_PERSONAL    = 1; // Unique to the user, uploaded by them
  ORIGIN_REFERENCE   = 2; // Standard reference data, downloaded from public sources
}

// SlotState describes whether a slot holds data and whether that data is sound.
enum SlotState {
  STATE_UNSPECIFIED = 0; // Never sent, an unset state
  STATE_EMPTY       = 1; // Nothing stored
  STATE_FILLED      = 2; // Data present and healthy
  STATE_DAMAGED     = 3; // Files exist but the metadata is missing, corrupt or outdated
}

// SlotConfidence indicates how confident the device is in its identification
// of a file, based on content, filename and size.
enum SlotConfidence {
  CONFIDENCE_UNSPECIFIED = 0; // Never sent, an unset confidence
  CONFIDENCE_LOW         = 1; // Filename/extension only, no content confirmation
  CONFIDENCE_MID         = 2; // Format detected but slot type inferred from size/filename
  CONFIDENCE_HIGH        = 3; // Magic bytes match and format-specific content confirmed
}

// SlotDownload describes the public download the device advertises for a
// reference slot, so a host can fetch it and stream it back in.
message SlotDownload {
  string url    = 1; // Public download URL
  uint64 bytes  = 2; // Download size in bytes
  string sha256 = 3; // Download SHA256 checksum
}

// SlotStatus describes the current state of a single slot on the device. The
// data fields are generic so a host renders a kind it has never seen.
message SlotStatus {
  SlotKind   kind   = 1; // Slot type
  string     name   = 2; // Human-readable slot name
  string     desc   = 3; // Owner-facing description of the data this slot holds
  string     format = 4; // The file that fills this slot, its required shape and what is refused
  SlotOrigin origin = 5; // Nature of the data (personal, reference)
  SlotState  state  = 6; // Whether the slot is empty, filled or damaged
  string     damage = 7; // Why the slot is damaged, empty otherwise
  repeated SlotKind deps = 8; // Slots that must be filled before this one is actionable
  uint64     bytes  = 9; // Bytes on disk for this slot (0 if empty)

  string       build    = 10; // Reference assembly the data is keyed to (e.g. "GRCh38.p14")
  string       version  = 11; // The dataset's own release, if it has one (e.g. dbSNP "157")
  SlotDownload download = 12; // Advertised public download, absent if none
}

// SlotListRequest requests the state of all slots on the device.
message SlotListRequest {
}
// SlotListResponse contains the current state of every slot.
message SlotListResponse {
  repeated SlotStatus slots = 1; // State of every slot, one entry per slot kind
}

// SlotRepairRequest resets a slot to empty regardless of its current state.
// Unlike SlotDelete (which requires the slot to be filled), repair is a stateless
// force cleanup that removes any leftover metadata and/or files on disk. It is
// intended for recovering from corruption or half-written state after crashes.
message SlotRepairRequest {
  SlotKind slot = 1; // Slot to repair (reset to empty)
}
// SlotRepairResponse is an empty ack of the repair request.
message SlotRepairResponse {
}

// SlotDeleteRequest removes the contents of a filled slot. The slot must be
// in a healthy, filled state; corrupted or half-written slots must be cleaned
// up via SlotRepair instead.
message SlotDeleteRequest {
  SlotKind slot = 1; // Slot to delete
}
// SlotDeleteResponse is an empty ack of the delete request.
message SlotDeleteResponse {
}

// SlotIdentifyRequest sends an ephemeral file chunk to the Ark and asks it to
// identify the file. Its purpose is to filter potentially huge files (e.g. a
// 30GB compressed whole genome sequencing) before uploading them, and to
// answer what a file is without uploading it at all.
message SlotIdentifyRequest {
  string name = 1; // File name to guess the contents of
  uint64 size = 2; // File size to guess the contents of
  bytes chunk = 3; // First chunk of the file to guess the contents of

  repeated SlotKind kinds = 4; // Possible slots to interpret as (empty == any)
}
// SlotIdentifyResponse contains the identification based on the chunk shared
// in the request.
message SlotIdentifyResponse {
  SlotKind kind = 1;       // Identified slot type to use during upload
  SlotConfidence conf = 2; // Identification confidence level

  string summary = 3;   // Short summary of the identified data
  string details = 4;   // Detailed description of the identified data
  string rejection = 5; // Reason why the file is rejected, empty otherwise
}

// SlotUploadStartRequest requests uploading a file into a slot of the given
// kind (e.g. a whole genome variant call file).
message SlotUploadStartRequest {
  SlotKind kind = 1; // Slot type requesting to upload

  string name = 2; // File name to guess the contents of
  uint64 size = 3; // File size to guess the contents of
  bytes chunk = 4; // First chunk of the file to guess the contents of
}
// SlotUploadStartResponse contains a unique session id for an approved upload.
message SlotUploadStartResponse {
  uint64 session = 1; // Session id for concurrent uploads (not recommended)
}
// SlotUploadChunkRequest contains a new chunk of data to append to a pending
// upload session.
message SlotUploadChunkRequest {
  uint64 session = 1; // Session id into which to append a new chunk
  bytes chunk = 2;    // Data chunk to append (reasonably capped)
}
// SlotUploadChunkResponse is an empty ack of the chunk upload request.
message SlotUploadChunkResponse {
}
// SlotUploadCancelRequest contains an upload session id to abort.
message SlotUploadCancelRequest {
  uint64 session = 1; // Session id to cancel
}
// SlotUploadCancelResponse is an empty ack of the cancellation request.
message SlotUploadCancelResponse {
}

// SlotPhase names one step of a slot's processing pipeline.
message SlotPhase {
  string name = 1; // Short label for the phase
  string desc = 2; // Description of what the phase does
}

// SlotUploadProcessRequest marks an upload session ready for processing and
// requests a progress report to be sent back. May be called multiple times.
message SlotUploadProcessRequest {
  uint64 session = 1; // Session id to mark as completed
}
// SlotUploadProcessResponse acks the completion of a slot upload and also
// contains the current processing progress.
message SlotUploadProcessResponse {
  uint64 proc_start = 1; // Unix timestamp when data processing started

  repeated SlotPhase phases = 2; // Every phase of the pipeline, in order

  uint64 phase_in = 3;       // Current phase (1-indexed)
  uint64 phase_start = 4;    // Unix timestamp when the current phase started
  uint64 phase_progress = 5; // Approximate progress for this phase [0-10000]

  string failure = 6; // Failure reason if processing failed, empty otherwise
}


// DatasetPathsRequest requests the map of every path an app can read, the
// dataset view the device derives from its slots, lenses included.
message DatasetPathsRequest {
}
// DatasetPathsResponse maps every path an app can read, available or not, in
// tree order. It describes the tree only and never carries a value from the
// owner's data.
message DatasetPathsResponse {
  repeated DatasetPath paths = 1; // Every path pattern, in tree order
}

// DatasetPath describes one path pattern under the data root. A segment in angle
// brackets is a placeholder, described by the directory entry it names. The path
// is the entry's stable key.
message DatasetPath {
  string          path      = 1; // Path as a manifest names it
  bool            directory = 2; // Directory or file
  bool            grantable = 3; // Whether a manifest may name it
  bool            available = 4; // Whether the data this path needs is on the Ark now
  string          desc      = 5; // What it holds, when it is absent and when it fails
  string          format    = 6; // Exact file content, or what a directory lists
  repeated string examples  = 7; // Complete example values, most typical first
}