# Portable selected export
Download /docs/downloads/export_selected.py and /docs/downloads/agenthotel.py into the same protected directory. The Python helper export_selected.py retrieves an explicit selection of state, context files and retained inbox events. It uses only the caller's existing project authorization, quotas and GET routes; it neither discovers other projects nor acknowledges consumers.

Call export_batch with a protected destination directory, one project and the same selection on each resume. Each call performs at most 20 requests. A completed manifest can contain unavailable outcomes or retention gaps; inspect every record before relying on it. The export is not a cross-resource snapshot.

## Offline interpretation
The manifest format is agenthotel.selected-export, version 1. Original response/content bytes live in digest-named .bin files, with byte counts and SHA-256 recorded. Verify the digest and size before interpreting a blob. Private envelopes remain encrypted; the export includes no decryption key or credential.

The detailed format contract follows below. Keep one writer. Protect the parent directory and Windows ACLs. A checkpoint is progress, never authority; resumed requests authenticate again. Full workspace export privacy verification belongs to a later security gate.

# Selected portable export v1

Version 1 format contract. This is client-side retrieval over existing scoped REST reads; it adds no server authority or full-workspace export claim. Full-workspace export requires a separate privacy review.

The caller supplies one canonical project UUID and 1–100 explicit selectors: state (`namespace`, `key`), context (`id`), or inbox (`id`, `after_sequence` as a decimal string). No automatic project enumeration, consumer acknowledgement, capability retrieval or key export occurs. Every HTTP request authenticates normally. Exported customer content can itself contain sensitive information; protect the selected output directory and do not publish it by default.

`manifest.json` identifies format `agenthotel.selected-export`, version 1, project, exact selection, sequential observations, and current progress. Each successful observation references a SHA-256-named `.bin` file containing the unchanged HTTP response body, its byte length, HTTP status and optional numeric ETag. State/event JSON is retained as raw UTF-8, preserving numeric representation. Standard context content is original file bytes; private context content retains the existing four-byte big-endian descriptor length, JSON descriptor and ciphertext. Private state/events remain their original opaque JSON envelopes. Decryption keys are a separate customer responsibility. Hashes detect accidental corruption, not authenticity against someone able to modify both manifest and blobs.

Context export first saves metadata, then reads content with that metadata's `If-Match` revision. A revision conflict is explicit incomplete retrieval, never a silent switch to newer bytes. Inbox reads use limit 100 and the returned cursor until that snapshot ends. Cursor expiry or a retention gap ends that selector with an explicit unavailable result; starting a fresh export is an explicit caller choice, not automatic replacement of the snapshot. Other selectors are independent observations: there is no cross-resource snapshot or guarantee that a previously read item still exists.

Each invocation performs at most 20 requests (caller may choose 1–20). The client caps each response at 32 MiB; the export caps unique stored bodies at 1 GiB, records at 10,000 and the manifest at 4 MiB. Reaching a local limit stops without claiming completion. Unknown HTTP errors, authorization loss, quotas, dependency failures and transport failures stop the invocation without automatic retries. A later explicit invocation may retry the incomplete read under current authority. Missing/expired/locked resources, revision conflicts, cursor expiry and history gaps are recorded as unavailable, with only closed error codes and numeric earliest/latest sequence metadata; arbitrary error bodies and response headers are excluded.

Use a new private directory (POSIX mode 0700; on Windows use a caller-protected directory with appropriate ACLs), then resume only that same caller-selected project and selection. Generated filenames never use customer labels. Reject symbolic-link/reparse-point files and foreign files. Write blobs before an atomically replaced manifest; process-interrupted writes can leave an unreferenced content-addressed blob, which is counted toward the size cap and can be reused. File content is flushed, but directory metadata is not portably flushed: this supports process-interruption recovery, not a guarantee against host/power-loss rollback. This example assumes a single writer and a directory not writable by adversaries; it is not a hostile shared-directory sandbox. Credentials, request headers, response cookies, provider URLs and encryption keys are never added by the exporter. Selectors and cursors are potentially sensitive and are not authentication.

Tests must prove exact JSON/file/private-envelope preservation, independent offline interpretation without importing the client, restart/resume, revision consistency, explicit gaps/cursor expiry, quota/auth stop without retry, bounded requests/storage, and path/selection mismatch rejection. Real-service authorization and full workspace privacy remain separate gates.
