// 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 the // parity of the id alone distinguishes a response from a request. 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 RelayInboundRequest relay_inbound = 0x402; // Forwards one frame from the relay socket to the Ark RelayOutboundResponse relay_outbound = 0x403; // Confirms the relay socket accepted the frame // 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 where a scheduled task stands ExecutionCancelRequest exec_cancel = 0x506; // Cancels a task or a pending upload ExecutionResultRequest exec_result = 0x507; // Fetches the result of a resolved task ExecutionOutputRequest exec_output = 0x508; // Reads the next piece of a released task's output // 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 the // parity of the id alone distinguishes a response from a request. 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 RelayInboundResponse relay_inbound = 0x402; // Confirms the Ark opened the frame RelayOutboundRequest relay_outbound = 0x403; // Asks the host to write one frame to its relay socket // Operational messages, reserved range 0x501-0x600 UnlockResponse unlock = 0x501; // Acknowledges the completed unlock ExecutionUploadStartResponse exec_upload_start = 0x502; // Task id for the later messages ExecutionUploadChunkResponse exec_upload_chunk = 0x503; // Acknowledges the appended app chunk ExecutionScheduleResponse exec_sched = 0x504; // Acknowledges the approved task, whose app now runs ExecutionStatusResponse exec_status = 0x505; // Where the scheduled task stands ExecutionCancelResponse exec_cancel = 0x506; // Acknowledges the canceled task or upload ExecutionResultResponse exec_result = 0x507; // What the owner saw when releasing the output ExecutionOutputResponse exec_output = 0x508; // Next piece of the requested output stream // 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 answers a failed request, in place of the response content. // // 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, 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 phone approval it depends on did not // authorize it. The owner declined, or the phone's answer broke the approval's // rules and counts as a decline. RESERVED_ERRORS_UNAUTHORIZED = 5; // The peer serves this request but the approval it depends on did not arrive // before its window ran out, on the phone or at the button. RESERVED_ERRORS_UNCONFIRMED = 6; // The peer serves this request but the approval it depends on could not be // sent to the phone, since the relay failed on the way. RESERVED_ERRORS_UNDELIVERED = 7; } // 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 that a new firmware update procedure // started. A rejected request gets an error instead. // // The Ark generates a fresh ephemeral encryption key to receive the firmware // access key. Its public half is sealed to the cloud, bound to the requested // version and hash. 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 acknowledges the initiated update procedure. A // rejected request gets an error instead. // // 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->Ark]["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-v2:join"][cloud_crypto_id](null) } // RelayInboundRequest forwards one frame from the host's relay socket to the Ark. // // The relay connects the Ark with its companion app through the host and the // cloud. Its frames are opaque to the host, which forwards them unchanged in // both directions and never interprets them. Each frame is sealed to its // recipient by the other endpoint, or by the cloud itself, and its // authenticated data names the recipient's encryption key. // // The host sends one request for each message its relay socket delivers, in // arrival order. message RelayInboundRequest { bytes frame = 1; // Seal[App|Cloud->Ark]["relaying-v2:frame"][ark_crypto_id](...) } // RelayInboundResponse confirms the Ark opened the frame, which decrypted and // verified. The Ark answers before acting on the frame's content, and refuses // a frame it cannot open with an error whose message names the cause. message RelayInboundResponse { } // RelayOutboundRequest asks the host to write one frame to its relay socket. // // A host without a relay socket attaches one first through relay_join. If it // cannot, it answers with the reserved UNAVAILABLE error. message RelayOutboundRequest { bytes frame = 1; // Seal[Ark->App]["relaying-v2:frame"][app_crypto_id](...) } // RelayOutboundResponse confirms that the relay socket accepted the frame, which // does not mean the frame reached the app. message RelayOutboundResponse { } // UnlockRequest asks the Ark to open its user data storage. // // The Ark answers once the owner approves or declines on their phone, or the // approval window runs out. message UnlockRequest{ } // UnlockResponse acknowledges the completed unlock. Any other outcome gets an // error instead. 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 id of the new task, which every // later message about it carries. The task belongs to the session that opened // it. When that session ends, the Ark drops the task whatever its stage, // canceling a running app. 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). // // The Ark answers once the owner approves or declines on their phone, or the // approval window runs out. message ExecutionScheduleRequest{ uint64 taskid = 1; // Task id returned by ExecutionUploadStartResponse } // ExecutionScheduleResponse acks that the owner approved the task and its app // now runs. The host already holds the task id from the preceding // ExecutionUploadStartResponse. message ExecutionScheduleResponse{ } // ExecutionCancelRequest cancels an upload, a task awaiting the owner's // approval or a running app. An interrupted app fails, and its report goes to // the owner's review like any other. A task whose app already ended cannot be // canceled. message ExecutionCancelRequest{ uint64 taskid = 1; // Task id to cancel } // ExecutionCancelResponse acknowledges the canceled task or upload. message ExecutionCancelResponse{ } // ExecutionStatusRequest checks where a scheduled task stands. Only the // session that scheduled the task can check it. message ExecutionStatusRequest{ uint64 taskid = 1; // Task id to check } // ExecutionState is where a scheduled task stands for its host. enum ExecutionState { EXECUTION_STATE_UNSPECIFIED = 0; // Never sent, an unset state EXECUTION_STATE_RUNNING = 1; // The app still runs EXECUTION_STATE_AWAITING = 2; // The app ended and the owner reviews its report EXECUTION_STATE_RESOLVED = 3; // The review closed, so exec_result answers } // ExecutionStatusResponse reports where a scheduled task stands. message ExecutionStatusResponse{ reserved 1; // Kept free, since older hosts read it as their pending flag reserved 2; // Kept free, since older hosts read it as their result ExecutionState state = 3; // Where the task stands } // ExecutionResultRequest fetches the result of a resolved task. // // A released task answers with what the owner saw, the same until the task // closes for the host. A withheld task fails it with the reserved error its // report's approval closed with, and that failure closes the task. message ExecutionResultRequest{ uint64 taskid = 1; // Task id to fetch the result of } // ExecutionResultResponse holds what the owner saw when releasing the task's // output, which the host then reads through ExecutionOutputRequest. message ExecutionResultResponse{ string name = 1; // App name from its manifest string version = 2; // App version from its manifest bool develop = 3; // Whether the app is a develop build bool success = 4; // Whether the app succeeded repeated string paths = 5; // Paths of the owner's data as mounted, without public grants string media = 6; // Media type of the report, such as text/markdown uint64 stdout_bytes = 7; // Length in bytes of the report, the app's standard output uint64 stderr_bytes = 8; // Length in bytes of a develop build's standard error, 0 otherwise } // ExecutionStream names one of the app's output streams. enum ExecutionStream { EXECUTION_STREAM_UNSPECIFIED = 0; // Never sent, an unset stream EXECUTION_STREAM_STDOUT = 1; // Standard output, the report EXECUTION_STREAM_STDERR = 2; // Standard error, kept for develop builds only } // ExecutionOutputRequest reads the next piece of a released task's output. // // Each stream is read once and in order, in pieces of the size the host asks // for. The Ark refuses a size of zero, or one whose answer would not fit a // wire message. The task closes for the host once exec_result has answered // and both streams are read to their end. message ExecutionOutputRequest{ uint64 taskid = 1; // Task id to read the output of ExecutionStream stream = 2; // Stream to read uint64 offset = 3; // Bytes of the stream read so far, the only offset the Ark accepts uint64 size = 4; // Most bytes to return, chosen by the host } // ExecutionOutputResponse carries the next piece of the requested stream. message ExecutionOutputResponse{ bytes chunk = 1; // The requested bytes, fewer only at the stream's end } // 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_KIND_UNSPECIFIED = 0; // Never sent, an unset kind SLOT_KIND_REFERENCE_GENOME = 1; // Human reference genome assembly SLOT_KIND_GENE_ANNOTATIONS = 2; // Gene-to-coordinate mapping database SLOT_KIND_SMALL_VARIANTS = 3; // User's small variant calls, SNVs and indels SLOT_KIND_VARIANT_CATALOG = 4; // Variant catalog (rsID-to-position), from dbSNP SLOT_KIND_STRUCTURAL_VARIANTS = 5; // User's structural variant calls SLOT_KIND_COPY_NUMBER_VARIANTS = 6; // User's copy number calls } // SlotOrigin describes the nature of the data in a slot, determining what // actions are available to the user for filling it. enum SlotOrigin { SLOT_ORIGIN_UNSPECIFIED = 0; // Never sent, an unset origin SLOT_ORIGIN_PERSONAL = 1; // Unique to the user, uploaded by them SLOT_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 { SLOT_STATE_UNSPECIFIED = 0; // Never sent, an unset state SLOT_STATE_EMPTY = 1; // Nothing stored SLOT_STATE_FILLED = 2; // Data present and healthy SLOT_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 { SLOT_CONFIDENCE_UNSPECIFIED = 0; // Never sent, an unset confidence SLOT_CONFIDENCE_LOW = 1; // Filename/extension only, no content confirmation SLOT_CONFIDENCE_MID = 2; // Format detected but slot type inferred from size/filename SLOT_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. // // A grantable path also carries its wording and whether it is public. A public // path reads nothing about the owner's DNA, such as the reference genome or // which datasets are uploaded. The wording is an English template that // describes a grant to the owner, with each placeholder named after the path's // own, such as "Your genome around variant {rsid}". 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 bool public = 8; // Whether a grant reads nothing about the owner's DNA, for grantable paths string wording = 9; // Template wording a grant to the owner, for grantable paths }