Durable Image Attachments
The attachment seam separates binary image ownership from the session log. A producer gives validated encoded bytes to ctx.attachments; the service publishes an immutable content-addressed reference only after the object is durable. Session events and model-visible ImageBlocks contain that reference and metadata, never a browser object URL, host temporary path, provider URL, or base64 payload.
Unsent browser drafts may stay in memory and native clients may stage them in operating-system temporary storage. Once the host accepts a user message, its images move below <DSH_HOME>/attachments/v1 before the user event is appended. Structured model image output follows the same persist-before-event rule.
Source: packages/attachment/attachment/src/types.ts
Identity and verified metadata
AttachmentId is a branded opaque string. The local backend currently emits sha256:<digest>, but consumers must neither parse that representation nor derive a filesystem path from it.
/** Raster image formats accepted by the version-one attachment path. */
type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif'
/** Durable, serializable metadata for one immutable image object. */
interface ImageAttachmentRef {
/** Opaque storage identifier; never a filesystem path or bearer URL. */
attachmentId: AttachmentId
/** Media type verified from the stored bytes. */
mediaType: ImageMediaType
/** Exact encoded byte length. */
bytes: number
/** Intrinsic encoded width in pixels. */
width: number
/** Intrinsic encoded height in pixels. */
height: number
/** Optional display name stripped of local path information. */
name?: string
}
/** Deployment-resolved limits used by upload admission and request buffering. */
interface ImageAttachmentLimits {
maxImageBytes: number
maxImagesPerMessage: number
maxMessageImageBytes: number
maxImagePixels: number
mediaTypes: readonly ImageMediaType[]
}
The reference records intrinsic dimensions and encoded length so clients can lay out history without decoding first, while every authoritative read still re-checks digest, media signature, dimensions, and metadata against the object.
Commit and verified-read payloads
/** Request to validate and durably commit one image. */
interface SaveImageAttachment {
data: Uint8Array
/** Caller-declared media type, checked against fully decoded bytes. */
mediaType: ImageMediaType
/** Optional browser/provider display name; it is never interpreted as a path. */
name?: string
}
/** Stored image bytes returned after reference and digest verification. */
interface StoredImageAttachment {
ref: ImageAttachmentRef
data: Uint8Array
}
saveImage() validates bytes and atomically commits one object before returning its reference. validateImage() runs the same admission checks without persisting anything; batch callers validate every member through it before saving any member, so validation rejection leaves no partial objects behind. readImage() accepts a reference from an authorized session path and returns bytes only after integrity verification. The service is deliberately retention-neutral: resumed and forked sessions may share objects, so reference-aware garbage collection is deferred rather than tied to any one session's deletion.
Cordis API
Generated from source by scripts/gen-cordis-catalog.ts (verified fresh by pnpm run verify-cordis-catalog in doc-sync; regenerate with pnpm run gen-cordis-catalog) — this section is byte-identical in both language sides of the page. Signature blocks use a ts cordis-catalog fence and keep the original source JSDoc; dispatch modes are defined in the primer, and the framework-inherited ctx API lives in cordis-api/inherited.md.
ctx.attachments — AttachmentStore (abstract seam)
Immutable binary attachment service. Implementations validate bytes before publishing a reference.
/**
* Validate one image without persisting it.
* Batch callers validate every member before saving any member.
* @param input - encoded bytes, declared media type, and optional display name.
* @returns completion after the encoded raster has been fully decoded.
*/
abstract validateImage(input: SaveImageAttachment): Promise<void>
/**
* Validate and durably commit one image before its owning session event is appended.
* @param input - encoded bytes, declared media type, and optional display name.
* @returns a durable content-addressed reference.
*/
abstract saveImage(input: SaveImageAttachment): Promise<ImageAttachmentRef>
/**
* Read one image and verify that bytes still match the recorded reference.
* @param ref - durable reference from the session log.
* @param signal - optional cancellation for backend read and verification work.
* @returns the verified bytes and canonical reference.
* @throws the signal reason when aborted, or a storage error when verification fails.
*/
abstract readImage(ref: ImageAttachmentRef, signal?: AbortSignal): Promise<StoredImageAttachment>