← Book map

Chapter 08 · Tabs

Six flags, one payload

The most general request type, and the one where the flags genuinely change the shape of the details object — six optional fields, plus a known workaround.

Data packet — six flags, one payload Data packet flags decide which optional fields exist: request id, statements, a signature, whether the user signs, whether it is transmitted to the user, and whether a URL is downloaded. 1 requestID 2 statements 4 signature 8 for user's signature 16 transmittal to user 32 URL download signableObjects: DataDescriptor[] recipient auth (optional) Only one of these flags is not obvious from its name: 16 means 'hand this to the user', not 'send it now'.

FIGURE 8.1 · DataPacketRequestDetails flags used by this tab.

The most general request in the app

A data packet asks an identity to sign an arbitrary data blob — often a hash of a URL-fetched resource. At 748 lines it is the biggest route file, and most of that is validation and descriptor assembly rather than request construction.

Flags are computed, then partially overridden

buildFlags():  requestID | statements | signature
             | forUsersSignature | transmittalToUser | hasUrlForDownload

applyUserControlledFlags(details, flags):
  // calcFlags() in the primitive ignores three of them, so the route re-applies
  forUsersSignature · transmittalToUser · hasUrlForDownload

That workaround is a real piece of institutional knowledge: the primitive's own flag calculation does not derive those three, so a request built only from the constructor can silently lose them. If a data packet arrives at the wallet missing flags the form clearly set, this is the line to check.

URL download mode

When FLAG_HAS_URL_FOR_DOWNLOAD is set, signableObjects becomes exactly one descriptor: a DataDescriptor whose object data is a URLRef, optionally carrying a hash (URLRef.FLAG_HAS_HASH). fetchAndHashUrl exists so the UI can fill that hash by downloading the resource first — the signature then commits to those exact bytes.

DataDescriptor.fromJson({
  version: 1,
  flags: …,
  objectdata: new VdxfUniValue({ values: [{ [URLRef.vdxfid]: urlRef }] }).toBuffer()
})

Recipient constraints via a preceding auth detail

When transmitting to a specific user, the route prepends an AuthenticationRequestOrdinalVDXFObject with a recipient constraint — the same ordering trick the encryption tab uses. The comment in the code is explicit that the auth must come first so the wallet can enforce it before showing the data.

Required-field rules

If the flag is set……this field becomes required
FLAG_HAS_STATEMENTSstatements non-empty
FLAG_HAS_REQUEST_IDA request id
FLAG_HAS_SIGNATUREA VerifiableSignatureData blob
FLAG_FOR_TRANSMITTAL_TO_USERA recipient identity
FLAG_HAS_URL_FOR_DOWNLOADA download URL (and only the URL descriptor is signed)
Eight of the nine tabs build one detail. This one builds a variable-length array whose shape depends on the flags — which is why its validation is longer than every other route combined.

Reading the code

src/routes/requests/dataPacket.ts
  buildFlags / applyUserControlledFlags
  parseSignableObjects / buildUrlDataDescriptor
  buildRecipientAuthDetails / buildDataPacketRequest
  generateDataPacketQr / signDataPacket / fetchAndHashUrl