Skip to content

Tenants, key domains and datasets

These identifiers and structures appear throughout the SDK API. Every namespace works with them.

Identifiers

TypeWhat it is
TenantIdImmutable. Device certificates (cards), stored objects and evidence carry it for the tenant’s whole lifetime.
RecipientIdSixteen bytes of SHA-256 over a device’s public key, base64url-encoded. Every part of the SDK names a device by its RecipientId, including shares, cards and access events.
LocatorThe opaque id of a record, a file, a folder or a key domain root. It stays the same across every version of the object it names.
EventIdAn access event, assigned by the Seald Healthcare Cloud. Every Decided<T> carries one, whatever the decision.
BytesRaw bytes: Uint8Array in TypeScript, Data in Swift, ByteArray in Kotlin.
SecondsA duration: a number of seconds in TypeScript, TimeInterval in Swift, Duration in Kotlin.

People, groups and roles

Person is how the identity provider names someone, never a name or an email address your application would have to protect separately:

interface Person {
iss: string;
sub: string;
}

iss and sub are the issuer and subject of the identity token, as the identity provider set them. The SDK treats these two strings as the whole identity of a person. Nothing else about them passes through the SDK API.

A Group is a group as the identity provider names it, a plain string. Groups let a tenant share a key domain with everyone in a group at once. When a person joins the group later, a member device gives them access. No new share is needed.

Role fixes four names and leaves the rest to the tenant:

type Role = 'owner' | 'admin' | 'employee' | 'contractor' | string;

owner, admin, employee and contractor carry meaning the SDK and the Seald Healthcare Cloud both act on. The Owner role can unshare any key domain. Only an Owner or Admin can approve certain changes with a fresh multi-factor sign-in. The Owner role is not the same as being a domain owner. Only a domain owner can share their key domain. The tenant creates any other role itself. Such a role has no special behavior in the SDK.

Classification is the sensitivity of a dataset or a field:

type Classification = 'phi' | 'pii' | 'public' | 'de-identified';

phi is protected health information. pii is personal information that is not health data. public carries neither. de-identified is data a tenant has stripped of both under its own process. The classification travels with a dataset’s registration and with a record’s segment fields. It shapes what a policy may decide about the data.

Cards

A Card is a device certificate, signed by its tenant’s identity authority.

interface Card {
recipientId: RecipientId;
tenantId: TenantId;
person: Person;
issuedAt: Date;
state: 'active' | 'superseded' | 'revoked';
replaces?: RecipientId;
}

state is the card’s status, as the withdrawal feed reports it:

  • active: the device can still open objects.
  • superseded: the device enrolled a replacement. This card gave way to the newer one. replaces on the newer card names the RecipientId it took over from.
  • revoked: the card was withdrawn outright, after device revocation or a lost device.

A card that is no longer active locks the client that holds it. See The trust model for how that check runs.

Key domains, epochs, datasets and folders

A key domain is the unit of sharing. It has one key. Every device of every person and group who may open the key domain has access to that key. Sharing, unsharing and making someone a domain owner all act on a key domain’s root Locator through client.domains. Unsharing starts the key domain’s next epoch, a fresh key generation. A person who leaves cannot open anything saved after they left. The SDK does not change any object saved under an older epoch.

A dataset is a named collection. A person with the Owner or Admin role registers it once, through client.datasets. Its root is itself a key domain. Registering a dataset creates the first key for everything saved under it. A dataset also carries:

  • its indexFields
  • its optional subjectField, for tokenizing a customer’s own identifier
  • its segmentFields

Segment fields get their own key. The SDK can then withhold a masked field without hiding the whole record.

A folder is a location inside a dataset that client.folders.list opens as one decision. The folder index itself is a stored object like any other, encrypted to the key domain it belongs to. A folder can itself be a sub-domain, domainRoot on its Entry, when a tenant wants a subtree shared differently from its parent.

Records hold structured fields. Files hold bytes and a media type. Both are objects. You open, save and delete both the same way. Both carry a Locator, version and writer. They differ only in the content you pass to save or create: RecordContent or FileContent.

Every save is a new version, never an edit in place. A VersionInfo records:

  • who wrote the version
  • the version it was based on
  • whether it is pending: an AI agent’s version its delegator has not accepted

Deleting an object or a key domain retires it into the recycle bin, domains.recycleBin. From there, you can restore it or shred it for good.

Tenant
└─ Dataset "encounters" (classification: phi, subject field: mrn)
└─ Key domain (root Locator, current epoch)
└─ Folder
├─ Record (Locator, version 3, fields)
└─ File (Locator, version 1, bytes)
Recycle bin: retired versions, objects and domains, until restored or shredded

Next