logo logo
Design: Database: Bundles meta

Database Bundles
Purpose Track all the bundles in the engagement.
Cardinality one Bundles database per engagement
one NextBundleItem per engagement
one BundleItem per bundle uploaded to the engagement
Source Code
Items src/ts/dbitems/Bundles.ts
Zod Models
item BundlesItem - union of NextBundleItem, BundleItem below
item.item one of NextBundleRecord, BundleRecord
itemId 'nextbundle'
item: NextBundleRecord
    kind 'nextbundle'
    nextbnum bnum to assign to next bundle
itemId number assigned to bundle. first bundle is 1.
item: BundleRecord
    kind 'bundle'
    bnum number assigned to bundle (same as itemId)
    bid BID of the bundle
    datadbid dbid of the corresponding BID-Data database
    entriesdbid dbid of the corresponding BID-Entries database
    name name of bundle
    description description of bundle
    restricted true if bundle is restricted
    shared_with array of member numbers bundle is shared with
        folders number of folders in the bundle
        files number of files in the bundle
        size approximate total size of the bundle


The Bundles database is created when the host creates the engagement.
It holds one item to track the next bundle number and also one item for each bundle uploaded by the host that has not been subsequently removed.

  (see userbase-js/types/index.d.ts)

  export interface Item {              userbase item             userbase file
    itemId: string                    ╭─────────────────────┐   ┌─────────────────┐
    item: any ────────────────────┬──▶│ kind: 'bundle'      │╶╶ │ bundle settings │
    createdBy: Attribution        │   │ bnum: 1             │   └─────────────────┘
    updatedBy?: Attribution       │   │ ...                 │
    fileId?: string               │   ╰─────────────────────┘
    fileName?: string             │
    fileSize?: number             │
    fileUploadedBy?: Attribution  │   ╭─────────────────────┐
    writeAccess?: AccessControl   └──▶│ kind: 'nextbundle'  │
  }                                   │ nextbnum: 2         │

  BundlesRecord represents the specific type of Item.item for an entry
  in the Bundles database - either NextBundleRecord or BundleRecord.


The Bundles database holds one NextBundleItem which keeps track of the next "bnum" (bundle number) which will be assigned to the next bundle uploaded by the host.

Bundle numbers are monotonically increasing integers starting with 1.


The Bundles database holds one BundleItem for each uploaded bundle.

Ownership and Access

The engagement host is the owner of the Bundles database.

The Bundles database is not shared. Only the host may access it.

Zod models

The following Zod models show how the Userbase items are represented in memory.

Note that in addition to the values specified by the Userbase Item interface the kind and database attributes are added during deserialization.

export type BundlesItem =
  | EscrowCredentialsItem
  | NextBundleItem
  | BundleItem
export const BundlesItem = z.discriminatedUnion('kind', [


 * EscrowCredentialsItem      only in ULID-Bundles
export type  EscrowCredentialsItem = z.infer<typeof EscrowCredentialsItem>;
export const EscrowCredentialsItem = z.object({
  kind:                 z.literal('escrowcredentialsitem'),
  itemId:               z.string(),
  item:                 EscrowCredentialsRecord,
  updatedBy:            z.custom<Attribution>(),
  database:             z.custom<Database>()
export type  EscrowCredentialsRecord = z.infer<typeof EscrowCredentialsRecord>;
export const EscrowCredentialsRecord = z.object({
  kind:                 z.literal('escrowcredentials'),
  mnum:                 z.number(),
  message:              z.string(),
  username:             z.string(),
  password:             z.string()


 * NextBundleItem       only in Bundles
export type  NextBundleItem = z.infer<typeof NextBundleItem>;
export const NextBundleItem = z.object({
  kind:                 z.literal('nextbundleitem'),
  itemId:               z.string(),
  item:                 NextBundleRecord,
  updatedBy:            z.custom<Attribution>(),
  database:             z.custom<Database>()
 * NextBundleRecord
export type  NextBundleRecord = z.infer<typeof NextBundleRecord>;
export const NextBundleRecord = z.object({
  kind:                 z.literal('nextbundle'),
  nextbnum:             z.number()


export type  BundleItem = z.infer<typeof BundleItem>;
export const BundleItem = z.object({
  kind:                 z.literal('bundleitem'),
  itemId:               z.string(),
  item:                 BundleRecord,
  updatedBy:            z.custom<Attribution>(),
  database:             z.custom<Database>()
 * BundleRecord         bundle details in Bundles and ULID-Bundles
export type  BundleRecord = z.infer<typeof BundleRecord>;
export const BundleRecord = z.object({
  kind:                 z.literal('bundles'),
  bnum:                 z.number(),
  bid:                  z.string(),
  datadbid:             z.string(),
  entriesdbid:          z.string(),
  name:                 z.string(),
  description:          z.string(),
  restricted:           z.boolean(),
  shared_with:          z.number().array(),
  stats:                BundleStats,
export type  BundleStats = z.infer<typeof BundleStats>;
export const BundleStats = z.object({
  folders:              z.number(),
  files:                z.number(),
  size:                 z.number()