/** * `sdk.assets` — high-level API for the Aithos assets sub-protocol PDS. * * Stores binary content (images, PDFs, audio, video) owned by a * subject, encrypted client-side under per-asset AMKs (Asset Master * Keys), accessible to authorized apps via signed mandates (v0.2). * * const assets = sdk.assets; * const avatar = await assets.upload({ * bytes: pngBuffer, * mediaType: "image/png", * attachTo: { ethos: { zone: "public", sectionId: "sec_identity" } }, * }); * // avatar.url is a stable CloudFront URL (public asset) * * const cv = await assets.upload({ * bytes: pdfBuffer, * mediaType: "application/pdf", * attachTo: { ethos: { zone: "circle", sectionId: "sec_career_docs" } }, * }); * // cv is private: stored encrypted, fetched via short-lived presigned URL. * * const bytes = await assets.fetch(cv.urn); * // decrypted plaintext returned to the caller * * The module wires: * - AMK generation + wrap (via @aithos/assets-crypto) * - Bytes encryption with the canonical nonce-prefix on-disk layout * - RecipientResolver (v0.2-ethos: maps {zone} → recipient set) * - Direct S3 PUT against the presigned URL returned by init_upload * - In-memory AMK cache (per asset URN) for sub-second re-fetches * - Signed envelope JSON-RPC dispatch to /mcp/primitives/{read,write} * * Spec ref: spec/assets/ in the aithos-protocol repo. */ import { type AssetMetadata, type AssetReference } from "@aithos/assets-crypto"; export interface CreateAssetsClientArgs { /** Base URL of the assets PDS. Defaults to `https://assets.aithos.be` (the * production vanity domain — SEPARATE from the data PDS) when omitted. * Override for self-hosting/staging. */ readonly pdsUrl?: string; /** * Subject DID that owns the assets. The canonical owner is a `did:aithos:…` * account signing under its dedicated `#data` sphere; a `did:key:…` is a * throwaway identity for demos/tests only. */ readonly did: string; /** * Ed25519 sphere seed (32 bytes) that signs every assets-PDS envelope. For a * `did:aithos` account this MUST be the subject's dedicated **`#data`** sphere * seed (root stays cold). For a `did:key` it is the single embedded key. * * Note: this is the SIGNING key. Per-asset AMKs for private uploads are * wrapped to the attaching context's X25519 key (`#data-kex` / `#circle-kex` * / `#self-kex`) by the RecipientResolver — a separate mechanism. */ readonly sphereSeed: Uint8Array; /** * Verification method URL within the DID document used to sign envelopes. * For a `did:aithos` account this is **`#data`**; for a `did:key` it is * `#`. */ readonly verificationMethod: string; /** Optional fetch implementation. Defaults to globalThis.fetch. */ readonly fetch?: typeof fetch; /** * Optional override for the recipient resolver. By default the SDK * uses a "self-only" resolver that maps every private upload to the * subject's own X25519 sphere key. Apps that already orchestrate * grantees explicitly may pass a custom resolver. */ readonly recipientResolver?: RecipientResolver; } export interface AttachedContext { readonly ethos?: { readonly zone: "public" | "circle" | "self"; readonly sectionId?: string; }; readonly data?: { readonly collectionUrn: string; readonly recordId?: string; readonly field?: string; }; } export interface AssetUploadInput { readonly bytes: Uint8Array; readonly mediaType: string; readonly attachTo?: AttachedContext; /** * OPTIONAL — force a regime. Defaults to "auto": * - ethos.zone === "public" → public * - anything else → private */ readonly regime?: "auto" | "public" | "private"; /** OPTIONAL — strict forward secrecy at AMK rotation time. */ readonly forwardSecrecy?: "best_effort" | "strict"; } export interface AssetUploadResult { readonly urn: string; readonly assetId: string; readonly mediaType: string; readonly sizeBytes: number; readonly sha256OfPlaintext: string; readonly encrypted: boolean; /** * Stable URL for public assets (CloudFront-served). Absent for * private assets — fetch them via {@link AssetsClient.fetch}. */ readonly url?: string; /** Whether this URN was returned by intra-subject dedup (existing asset). */ readonly dedupHit: boolean; } export interface AssetFetchResult { readonly urn: string; readonly mediaType: string; readonly sizeBytes: number; readonly bytes: Uint8Array; readonly sha256OfPlaintext: string; } export interface AssetBrief { readonly urn: string; readonly assetId: string; readonly mediaType: string; readonly sizeBytes: number; readonly sha256OfPlaintext: string; readonly encrypted: boolean; readonly state: "ACTIVE" | "ORPHANED" | "TOMBSTONED"; readonly referenceCount: number; readonly createdAt: string; readonly modifiedAt: string; } export interface ListAssetsOpts { readonly filter?: { readonly mediaTypePrefix?: string; readonly sizeBytes?: { gte?: number; lte?: number; }; readonly createdAfter?: string; readonly createdBefore?: string; }; readonly limit?: number; readonly cursor?: string; readonly order?: "newest" | "oldest"; readonly includeOrphaned?: boolean; readonly includeTombstoned?: boolean; } export interface ThumbnailUploadInput extends AssetUploadInput { /** Long-edge sizes to produce (e.g. [64, 256]). */ readonly sizes: readonly number[]; /** * Downscaler. The SDK does NOT bundle an image library; callers pass * a function that takes the original bytes and a target size and * returns downscaled bytes. Typical implementations: `pica` in the * browser, `sharp` in Node. */ readonly downscale: (bytes: Uint8Array, targetLongEdge: number) => Promise; } export interface ThumbnailUploadResult { readonly primary: AssetUploadResult; readonly thumbnails: readonly { size: number; result: AssetUploadResult; }[]; } /** * Maps an attaching context to the set of recipients whose wraps the * AMK must carry. The v0.1 default returns the subject's own X25519 * sphere key derived from the SDK's seed. * * v0.2 will introduce an Ethos-aware resolver that inspects the * current manifest's `zones..cipher.wraps[]` (or, in v0.3, the * per-section wraps) to mirror grantees on attached assets. */ export interface RecipientResolver { resolve(input: { subjectDid: string; context: AttachedContext | undefined; }): Promise; } export interface RecipientSet { readonly recipients: ReadonlyArray<{ readonly didUrl: string; readonly x25519PublicKey: Uint8Array; }>; } export declare function createAssetsClient(args: CreateAssetsClientArgs): AssetsClient; export declare class AssetsClient { #private; constructor(args: CreateAssetsClientArgs); upload(input: AssetUploadInput): Promise; /** * Upload a primary asset plus one or more thumbnails (downscaled * client-side). Convenience method for the Deep avatar use case and * any UI that displays the same asset at multiple resolutions. * * The thumbnails are attached to the same context as the primary * and uploaded in parallel. They carry the {@link AssetReference} * role `"thumbnail"` when referenced from a section (see * spec/assets/03-asset-descriptors.md §3.2.3). */ uploadWithThumbnails(input: ThumbnailUploadInput): Promise; fetch(urn: string): Promise; head(urn: string): Promise; list(opts?: ListAssetsOpts): Promise<{ items: AssetBrief[]; nextCursor?: string; }>; ref(urn: string, reference: AssetReference): Promise<{ referenceCount: number; gammaRef: string; }>; unref(urn: string, reference: AssetReference): Promise<{ referenceCount: number; gammaRef: string; }>; listReferences(urn: string): Promise; delete(urn: string): Promise<{ tombstonedAt: string; gammaRef: string; }>; /** Zero in-memory AMK cache. Useful at user logout. */ reset(): void; } //# sourceMappingURL=assets.d.ts.map