R2 non-session storage boundary โ h.work
Status: Current scope clarification (2026-08-05). The original version of this document described R2-backed conversation attachments. That design was retired by ADR-046. Git history is the record of the old design; do not implement it.
Managed-session authority: Cloud AgentFS is the sole durable authority for conversation attachments, generated outputs, skills, memory, logs, and workspace content. SessionDB is the sole transcript authority. R2 is never a managed-session fallback, publication target, or second copy.
Scopeโ
Cloudflare R2 remains available only for explicitly non-session product data, for example:
- public Specialist and user avatars served through the product CDN;
- source documents belonging to a separate ingestion product, when that product's own contract explicitly selects R2; and
- QA/report publication or another isolated product surface with its own retention, authorization, and object identity.
This document does not authorize R2 for any file owned by a managed Hermes session. A feature is not non-session merely because an API or frontend currently exposes an R2 key or signed URL.
Forbidden managed-session usesโ
The following are forbidden:
- storing an inbound conversation attachment in R2 before or after AgentFS;
- copying a generated AgentFS output into R2 for delivery;
- returning an R2 key, signed URL, base64 body, host path, or Postgres message UUID as the identity of a managed file;
- using
R2_CLIENT_BUCKET,R2FileExchangeClient, a local volume, or a host workspace as a managed-runtime fallback; - persisting a signed URL in SessionDB or treating its expiry as file identity;
- deleting managed session files as an implied consequence of client or Specialist offboarding.
Legacy code or configuration that still exposes one of these shapes is an ADR-046 remediation item, not a compatibility contract.
Managed attachment and output flowโ
Inboundโ
- The API authenticates the real channel or portal caller.
- It writes each attachment's bytes once to the organization's cloud AgentFS
under the authorized conversation's
artifacts/inbound/tree. - The ACP turn contains the exact inbound text plus native structured resource
blocks carrying opaque
file:///...locators. - Attachment-only turns are valid. Humanwork does not invent
[Media message]or any other prompt prose.
The bytes, R2 keys, signed URLs, and host paths never travel through
hermes-run or the model message.
Generated outputsโ
- Hermes writes the output directly through its conversation-scoped view of the supervisor-resident organization AgentFS overlay.
- That mutation marks native AgentFS CDC dirty and causes one push after the native reconciliation pull at the file-work boundary.
- The canonical reply or evidence carries the exact opaque managed virtual file URI.
- An authorized API consumer reads the same object by locator. It does not copy, re-key, reread for base64 verification, or publish the bytes to R2.
Each managed file therefore has one durable object and one canonical locator.
AgentFS synchronizationโ
- One supervisor-resident Bun machine retains the organization's local AgentFS writable overlay for its conversation-scoped Hermes children.
- An AgentFS work boundary uses Turso's native plain
pull -> local work -> pull -> push-if-dirtysequence. The second pull applies remote changes and replays unpushed local changes before the conditional push. - Direct API cloud writes need no Redis, application event, revision, ACP sync signal, timer, or long poll. Native last-push-wins conflict handling remains authoritative; Humanwork adds no merge protocol.
- A turn with no AgentFS work performs no sync request, scan, helper spawn, or empty push.
See sandbox artifact persistence boundaries for the binding lifecycle.
R2 object-storage requirementsโ
For an explicitly non-session R2 feature:
- use separate buckets or prefixes per environment;
- scope credentials and authorization to the owning product and tenant;
- keep credentials server-side;
- use opaque, non-user-controlled object keys;
- validate tenant ownership before every private object operation;
- define retention and deletion independently of managed Hermes session lifecycle; and
- document whether the object is public CDN content or private product data.
The public assets bucket may contain avatars and other intentionally public assets. A private non-session bucket must not be named or documented as the conversation-attachment authority.
API boundaryโ
Generic R2 upload or signed-URL endpoints, where retained, are non-session
product APIs. Managed conversation attachments must use the authenticated
conversation/channel ingestion path that writes AgentFS directly. Do not add a
type=attachment R2 upload flow for managed turns.
Configurationโ
R2 credentials may remain on the API for their explicit non-session consumers. Configuration and comments must make that scope visible. In particular:
R2_ASSETS_BUCKETmay identify the public assets bucket;- a legacy
R2_CLIENT_BUCKETis not permission to store managed attachments; and - no
HUMANWORK_FILE_STORAGE_MODEor missing-credential fallback may switch a managed turn from AgentFS to R2 or local disk.
Acceptance checksโ
- A portal and each supported channel write one inbound attachment to AgentFS
and send one structured
file:///...resource block beside the exact message. - An attachment-only turn reaches Hermes without fabricated text.
- A generated file remains the original AgentFS object and is delivered by its exact locator.
- Managed payloads and transcript rows contain no R2 key, signed URL, base64 carrier, host path, or duplicate AgentFS path.
- Missing AgentFS access is a real failure; it never falls back to R2 or a local volume.
- R2 avatar and other approved non-session flows remain tenant-isolated and do not acquire managed-session semantics.
Implementation violations remain tracked in the ADR-046 remediation ledger.