Skip to main content

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โ€‹

  1. The API authenticates the real channel or portal caller.
  2. It writes each attachment's bytes once to the organization's cloud AgentFS under the authorized conversation's artifacts/inbound/ tree.
  3. The ACP turn contains the exact inbound text plus native structured resource blocks carrying opaque file:///... locators.
  4. 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โ€‹

  1. Hermes writes the output directly through its conversation-scoped view of the supervisor-resident organization AgentFS overlay.
  2. That mutation marks native AgentFS CDC dirty and causes one push after the native reconciliation pull at the file-work boundary.
  3. The canonical reply or evidence carries the exact opaque managed virtual file URI.
  4. 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-dirty sequence. 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_BUCKET may identify the public assets bucket;
  • a legacy R2_CLIENT_BUCKET is not permission to store managed attachments; and
  • no HUMANWORK_FILE_STORAGE_MODE or 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.