Retain Warehouse originals
Use clouddrive-originals to retain the PDFs selected by a clouddrive
Warehouse source, including files that have never been indexed. It shares the connector’s
selection rules. It does no OCR, embedding, indexing or Function
queue work. In the clouddrive-source image, set the container command to
/usr/local/bin/clouddrive-originals; the default command remains the metadata
worker. Both binaries provide --help without network or credential access.
Inventory first
Mount the same credentials and item-list files as the source worker. Supply
its HEVLAYER_SOURCE_REF and projected HEVLAYER_WAREHOUSE connection JSON;
the latter is not the full Kubernetes resource. Sources with keep retention also
need the verified history binding below. Then choose the destination:
export HEVLAYER_ORIGINAL_BUCKET=private-layer-bucket
export HEVLAYER_ORIGINAL_NAMESPACE=documents
export HEVLAYER_ORIGINAL_STATE_PREFIX=original-retention/documents
clouddrive-originals --inventory-only
HEVLAYER_ORIGINAL_REGION defaults to us-east-1.
HEVLAYER_ORIGINAL_ENDPOINT selects an S3-compatible endpoint. The destination
must be the private bucket the gateway uses for that namespace’s blobs.
Use one writer per state prefix, and keep that prefix outside pipelines/,
blobs/ and original-catalog/.
Inventory reads metadata, without downloading source files. It follows every configured drive’s complete page chain and checks that roots resolve, including empty folders. Explicit include/exclude identities must resolve too. Root and multi-root selection, paths, mounted item lists, MIME/size rules and exclusions are shared with the metadata connector. A missing provider content hash does not exclude an otherwise selected PDF.
The report’s inventoryComplete must be true before treating its selected
count as the denominator. Declared and resolved roots, include identities and
exclude identities are counted separately. selectedBytes sums known source
sizes; unknownSizes records omissions. A null counter means it was not
recorded by an older checkpoint. Missing roots, identities or ancestry leave explicit
unresolved evidence and a nonzero exit. Metadata entries can repeat across
pages; selected identities are unique by drive/item.
Preserve prior source membership
A source with retention.deselected: keep includes still-current prior members,
not only files that match today’s literal folder paths. Supply a pinned Layer
source snapshot; this is drive-listing.json, not an extraction-success manifest
or a search-index export:
export HEVLAYER_ORIGINAL_HISTORY_KEY=pipelines/example/extract/cache/drive-listing.json
export HEVLAYER_ORIGINAL_HISTORY_VERSION_ID=immutable-s3-version
export HEVLAYER_ORIGINAL_HISTORY_SHA256=expected-full-object-sha256
export HEVLAYER_ORIGINAL_HISTORY_BYTES=expected-byte-count
export HEVLAYER_ORIGINAL_HISTORY_PIPELINE_ID=example
export HEVLAYER_ORIGINAL_HISTORY_TARGET=s3://private-layer-bucket/pipelines/example/extract/cache/
clouddrive-originals --inventory-only --reselect-inventory
The importer verifies the complete object and each source row’s committed hash, generation and provider identity. Drive manifests must match the current Warehouse, mapping, MIME rules, configured roots and exact cache target. Item lists and retention are not part of that historical config hash: current explicit exclusions always win. Keep retention carries proven prior PDFs across later folder, item-list, MIME or size-filter changes; it does not turn a current non-PDF into a PDF. Prior membership alone proves neither current PDF bytes nor a successful copy. Current metadata, ancestry and versions determine what can be copied; missing members or those no longer selected as PDFs by current metadata remain explicit unresolved cases.
Pinned history reads first use an ordinary S3 GetObject request. The response
VersionId must equal HEVLAYER_ORIGINAL_HISTORY_VERSION_ID before any bytes
are read. This route needs no GetObjectVersion permission when the pin is still
current. If the current version differs, the worker tries an explicit-version
GET and checks its returned VersionId too. A missing VersionId or denied version
read fails closed. Full SHA-256 and size checks still apply. An existing authorized
operator can preserve the requested version at a dedicated key, verify its full
checksum and size, and supply a new binding for that preserved object; never
substitute the latest object for the pin. A saved generation retains its binding,
so preserve its evidence and reconcile any binding change before resuming.
Reselection creates a new generation from completed immutable metadata pages.
It preserves the old generation and acquired byte proofs and performs no new
Graph delta walk. Subsequent generations also carry the worker’s own source
membership, including new files selected on an earlier run. Missing members
remain unresolved across repeats. A missing literal root can be repaired only by a unique folder
identity proven in the verified old tree and present in the current tree. Similar
folder names or a broader parent are insufficient. Independent valid files can
still copy while scope gaps remain unresolved. Changed file versions require
an explicit new current inventory with --refresh-inventory; no stale version
is silently accepted.
Inspect source metadata
Use an existing completed inventory to diagnose a selected file without copying it or changing retention evidence:
umask 077
clouddrive-originals --inspect-item DRIVE ITEM > private-item-inspection.json
Keep the same source configuration, projected Warehouse, namespace, bucket and state prefix. This mode requires the existing binding and fully committed selected inventory, with no unresolved inventory scope errors. A copy run may still be incomplete or have changed/missing source outcomes. Inspection reads binding, state and bounded selected shards, rejects an unselected identity before Graph access, then makes one logical authenticated item-metadata lookup. Existing Graph authentication, pacing and bounded HTTP retries apply; the JSON counts actual attempts and received metadata/auth bytes. There is no content request, source discovery, metadata traversal, source-history/hint import or sink write.
The JSON is private evidence: it contains the inventory generation, selected and current source metadata, observation time, ETag, version, QuickXor, size, provider-declared MIME, citation, changed fields, HTTP error class/status and request/byte counters. Authenticated download URLs, provider error bodies and credentials are excluded. Citation query strings and fragments are removed from this diagnostic output. Logs go to stderr; stdout contains the diagnostic JSON.
sourceStatus is unchanged, changed, missing or error. Missing 404/410 and
changed metadata are successful diagnostic observations, so the command exits
zero after recording them. Provider errors emit safe diagnostic JSON and exit
nonzero. retentionVerified is always false: this mode never verifies bytes,
resolves a retention gap or updates counts. It does not change binding, phase,
cursors, outcomes, acquired proofs, original objects, catalog or queues, and
never starts a new inventory even when the old copy phase is complete.
--inspect-item cannot be combined with inventory, refresh, reselection or copy
flags. History and hint import environment variables are not used. The selected
snapshot remains unchanged; any later source refresh or retention repair is a
separate explicit operation.
Typed source originals
Selection follows the configured PDF scope, including files whose provider calls
them PDFs. Retention does not change that scope or alter source bytes. If a
selected original is a valid PNG despite its PDF metadata, the worker retains
the exact PNG with actual MIME image/png, provider-declared MIME and explicit
sourceFormatMismatch provenance. It verifies the PNG container, chunk CRCs,
zlib stream and scanline framing without rendering pixels. Indexed-color PNGs
remain unsupported because palette index validation needs pixel reconstruction.
Unsupported or malformed formats, HTML responses, missing files and changed sources remain
unresolved. PNG validation uses bounded buffers and a 1 GiB inflated scanline
limit; exceeding that bound is unsupported, never successful retention.
The same before/after source checks, provider QuickXor check when present,
SHA-256, size and full S3 readback apply to typed originals, including checksum
reuse. A verified PNG resolves its original-copy gap but never counts as a
verified PDF. Reports expose candidateTotal, verifiedOriginals,
verifiedPdfs, verifiedNonPdfOriginalsWithFormatMismatch, pending, missing,
changed, corrupt, unsupported and failed. Successful original types are
disjoint, and pending plus verified originals plus unsuccessful candidates
conserves the candidate total. unresolved is the sum of pending and unsuccessful
candidates plus scopeUnresolved, the separate inventory scope errors. The legacy verified aggregate now equals verifiedOriginals, not
verifiedPdfs; invalidPdf retains its legacy outcome name for unsupported
selected bytes. Old reports without typed fields leave those counts unknown.
For one known selected identity, use clouddrive-originals --only-item DRIVE ITEM
on the existing state prefix. No new inventory is needed while its generation
is resumable. Historical invalid-PDF quarantine receipts remain in place; the
new verified outcome records the format mismatch separately. No conversion,
OCR, indexing or source metadata update takes place.
MCP returns the whole PNG as an embedded resource with its actual MIME and
source link. A pages request for a non-PDF explicitly fails. Namespace grants,
pins and source/version bindings are unchanged. Existing v1 PDF catalogs remain
readable; typed catalog fields are optional additions. The MCP arguments and
HTTP API are unchanged, so this capability needs no generated SDK changes.
Copy, resume and repeat
With the same environment, run:
clouddrive-originals
For a bounded copy check after inventory, use clouddrive-originals --max-files 2.
It processes at most two selected identities in that invocation. Remaining files
stay pending and the command exits nonzero until the full inventory reconciles.
Remove the flag to process the entire selection.
The worker inventories first if needed, then copies its selected PDFs. Each file
must have a stable source ETag and size. It streams to scratch disk while
hashing, checks the source again, writes the object, and fully reads it back to
verify SHA-256, size and supported actual format. Originals over the older Function’s
10 MiB cap are copied too. Objects use blobs/<namespace>/<sha256> and the
existing blob://<namespace>/<sha256> reference format.
A failed run resumes from its committed pages and per-file evidence. A run that finished starts a fresh inventory on the next invocation. Unchanged source-version receipts can avoid another Graph download, but every reused S3 object is read and hashed. Size and S3 ETag alone never prove reuse. Files that changed, disappeared, failed verification or contain unsupported bytes remain unresolved. A run succeeds only when its complete inventory reconciles with verified objects and catalog references, with unresolved zero.
Use --refresh-inventory after repairing source scope or when a source change
invalidates the saved inventory. Old generations remain under the prefix.
Changing Warehouse, resolved selection or destination requires a new prefix.
To inspect one inventoried file without copying other members, run
clouddrive-originals --only-item DRIVE ITEM. Other files stay pending, so this
bounded diagnostic run exits nonzero. Invalid PDF bytes retain their observed
SHA-256, byte count and before/after metadata. A private quarantine/ receipt
classifies HTML-like, JSON-like or missing-PDF-header responses and points to
at most 64 KiB of raw bytes, with its own checksum and truncation flag. These
bytes are never published as an original or silently repaired.
State and resource use
Metadata pages are immutable shards, written before their compact cursor checkpoint. Selected shards contain at most 500 files. A disposable SQLite projection resolves ancestry; the accumulated tree is never rewritten per page or file. Outcomes are small per-file objects, and aggregate progress is saved in batches. Private reports and invocation meters remain under the state prefix, separate from extraction state. They contain source identities and citations; keep them private.
HEVLAYER_ORIGINAL_CONCURRENCY defaults to 2 and accepts 1–4 files. Each has
its own scratch directory; all share the Warehouse Graph rate limiter. Scratch
disk must hold current and historical metadata projections, the pinned history
file, optional hint manifest and up to that many originals at once.
Downloads do not buffer a whole file in memory. S3 multipart uploads use
8 MiB parts, increasing only to stay within 10,000 parts; the worker’s object
safety bound is 5 TiB. Known upload IDs are checkpointed before any parts and
reconciled on restart. A crash between initiation and saving that ID can leave
an empty upload for bucket lifecycle cleanup. Other writers’ uploads and
completed objects are not deleted.
Recovery requires AbortMultipartUpload for the exact owned key and upload ID; GetObject and PutObject alone do not grant it. If the worker cannot abort, it fails and preserves the marker. The error reports the S3 operation, safe service code/status, owned key, upload ID and marker location without SDK request URLs or response bodies. An existing authorized operator can read that private marker, abort only its recorded upload, and resume the same generation. NoSuchUpload is safe to reconcile after an operator abort; the worker then removes the marker. Do not delete markers to bypass denied cleanup. If initiation succeeded but checkpointing failed, the error carries the upload identity and intended marker location for operator recovery. Check this recovery capability before a large copy test; permission expansion is not required.
Reports separate copied, checksum-verified reuse, source changes, missing
files, invalid PDF signatures, failures and unrepaired corruption. Root aliases
and content aliases are separate counts. Graph/auth/content requests include
retries, and content bytes count received chunks. Successful metadata and auth response
payload bytes are counted separately. S3 counters report request attempts,
received object bytes and acknowledged write payloads, not wire billing.
reusedHints and reusedReceipts distinguish verified reuse through extraction
hints from the worker’s own acquired proofs; outcomes also identify checksum
reuse after a source download. Meters identify their invocation and checkpoint boundary; a hard crash can
leave later attempts unrecorded. Such intervals are not evidence of zero work.
Reuse an extraction receipt
An existing hev layer extraction manifest can supply optional source-version-to-SHA hints:
export HEVLAYER_ORIGINAL_HINT_MANIFEST_KEY=pipelines/example/extract/cache/manifest.json
export HEVLAYER_ORIGINAL_HINT_PIPELINE_ID=example
clouddrive-originals
The manifest is streamed to scratch and imported into SQLite. Its version and
Pipeline identity must match. A hint requires the same namespace’s retained
blob reference and exact drive/item/ETag/provider version. The source version
uses the connector’s quickXor:<hash> spelling. Conflicting hints
are ignored. Parser status is not proof. A matching blob still receives full
SHA-256/size/PDF-signature validation; a mismatch falls back to source copying.
Fetch indexed and unindexed originals
Enable originals on the gateway’s MCP namespace.
Per-file outcomes contain a stable original:<source-id> catalog ID. Pass
that ID to fetch_original_<toolName>, or to generic fetch_original with the
namespace. It uses the same id and optional pages arguments as indexed
originals. The returned metadata includes source drive/item/version/ETag,
SHA-256 and the provider citation. SharePoint links are preserved.
Existing indexed IDs also work when their rows lack original_blob: the
gateway lazily matches the authorized row’s exact content hash or source
identity/version to a catalog receipt. It does not scan or patch rows. Unknown
legacy identity remains an error.
Namespace read authorization applies before catalog access. For direct catalog IDs, every pinned filter must be provable from catalog attributes; a missing value is denied. Indexed IDs retain their row authorization and pinned filters. Their fallback adds only the verified blob binding, preserving the row’s own citation and source fields. Catalog IDs cannot widen a tool’s scope. Retention does not raise MCP whole-file, page-render or response-size limits; oversized responses still return an error with the source citation.
Probe access without copying
clouddrive-source --preflight reads metadata from each discovered drive and
streams one selected PDF into SHA-256, checking byte count and before/after
source identity. It constructs no sink or state store and makes no S3, index
or queue writes. This establishes access, not a complete inventory or retention.
HEVLAYER_PREFLIGHT_MAX_PAGES defaults to 100 per drive (1–10,000), and
HEVLAYER_PREFLIGHT_MAX_BYTES to 512 MiB. Larger candidates are counted while
the probe searches for a sample within its cap; they are not skip-success.
No sample within the budgets, an unreadable drive or unstable bytes fails.
Do not combine --preflight with --once, --verify or --reset-cursors.
Set HEVLAYER_PREFLIGHT_RECEIPT to a new absolute file path for an atomic,
owner-only private sample receipt. It binds resolved selection, Warehouse,
drive/item/path/citation, before/after metadata and observed SHA/bytes.
Identifiers stay out of routine progress logs. Credentials and signed download
URLs are never written. Raw sourceRef parsing strips only existing worker-owned
parse, extract, cacheUri, rescanSeconds, suffixes, uri, merge and
dedupe settings (dedupe must be content or item); other unknown fields
fail validation.
Retained snapshot when the source is unavailable
After deploying a gateway that reads snapshot catalogs, quiesce the sole copy
worker before enabling --allow-historical-reuse. This opt-in policy preserves
an exact selected inventory revision whose authenticated metadata lookup returns
404 or 410. It does not select more files or substitute an older original for
known changed content. Start with one selected identity:
clouddrive-originals --allow-historical-reuse --only-item DRIVE ITEM
This mode requires an existing completed inventory binding and cannot be combined with inspection, refresh, reselection or inventory-only flags. A bound acquisition receipt, or verified source history plus a pinned extraction manifest, supplies a byte-reference lead. For the manifest path, keep the source-history variables from prior membership and supply:
export HEVLAYER_ORIGINAL_HINT_MANIFEST_KEY=pipelines/example/manifest.json
export HEVLAYER_ORIGINAL_HINT_PIPELINE_ID=example
export HEVLAYER_ORIGINAL_HINT_VERSION_ID=exact-provider-version-id
export HEVLAYER_ORIGINAL_HINT_SHA256=full-manifest-sha256
export HEVLAYER_ORIGINAL_HINT_BYTES=exact-manifest-byte-count
Private per-file outcomes record objectVersionId only after accepted full S3 readback.
Legacy outcomes and unversioned live objects have an unknown version binding; historical
proof still requires a nonempty S3 VersionId. Empty bodies and entirely zero-filled
selected originals are established corruption, not unknown formats. They remain unresolved
and cannot use unclassified or historical retention; MCP refuses empty originals too.
Both evidence objects must match their pinned VersionId, full SHA and size. The receipt binds provider, tenant and client identity as well as selection, namespace and destination; a Warehouse name alone is insufficient. Layer must fully read the retained S3 object and verify its VersionId, SHA-256, size, actual format and inventory provider checksum. It checks source metadata again before publication; both observations must be 404 or 410. Missing checksums, ambiguous evidence, known newer content and corrupt or absent objects remain unresolved. No source body is read and no raw original is uploaded on this path.
The returned immutable original: reference identifies that inventory snapshot.
MCP reports retentionBasis=inventorySnapshot, sourceAvailability=unavailable,
the observed status and time, and deletionStatus=unknown. Its SourceLink is the
inventory citation, not a claim that the source still opens. Internal object and
evidence locations remain private. Existing namespace grants and pins apply.
Snapshot IDs use a canonical versioned tuple and exclude timestamps and attempt
IDs. Verification receipts, prior outcome archives and snapshots use conditional
create, with conflict validation. A retry after any publication boundary can
verify and finish the same snapshot without replacing a newer source alias.
Each validation writes a private receipt with its own invocation and both
status/time observations. A new proof lead or retained object version can create
a new receipt for the same frozen snapshot. The first snapshot and its first
receipt pointer stay unchanged; the current outcome names the new receipt.
The earlier missing outcome stays archived. Verified historical originals count
as originals and as their PDF, mismatched PNG or opted-in unclassified partition; source unavailability
remains a separate anomaly. Reports add verifiedHistoricalOriginals as a subset
of verifiedOriginals, and disjoint sourceAvailable, sourceUnavailable and
sourceAvailabilityUnknown counters. The availability counters conserve the
candidate total: authenticated 200 is available, 404/410 is unavailable, and
unvisited candidates or a failed metadata recheck are unknown. Earlier reports
without these fields remain unknown. historicalProofPolicy records the enabled
proof-policy revision.
Refreshing unchanged keep-retention selection must carry previously authenticated missing members, preserving their exact requested revision or an explicit unknown-version unresolved placeholder. It must not shrink the denominator or clear root and scope errors. Changed content requires fresh inventory and a copy of that newly requested version. Historical writing requires a compatible reader; old readers reject snapshot schema 2. Rollback disables writing and preserves the compatible reader and immutable evidence. Schema 1 aliases remain unchanged.
Isolated snapshot acceptance
Use an isolated MinIO instance through an HTTP loopback endpoint. An existing
authorized executor owns its lifecycle and supplies synthetic credentials. The
harness creates an owned scratch-original-snapshot-* bucket, enables versioning,
and creates a separate unversioned bucket to check refusal of missing VersionIds.
It never disables versioning on an existing bucket or uses ambient AWS credentials.
export LAYER_SNAPSHOT_TEST_ENDPOINT=http://127.0.0.1:9000
export LAYER_SNAPSHOT_TEST_BUCKET=scratch-original-snapshot-test
export LAYER_SNAPSHOT_TEST_ACCESS_KEY_ID=fixture-access
export LAYER_SNAPSHOT_TEST_SECRET_ACCESS_KEY=fixture-secret
cargo test -p clouddrive-source minio_real_ -- --ignored --nocapture --test-threads=1
Use the fixture’s supplied test credentials. The client requires a loopback URL,
an owned bucket prefix and both explicit credential variables before any request.
For an executor’s custom scratch prefix, set LAYER_SNAPSHOT_TEST_BUCKET_PREFIX
to that exact owned prefix; it must start with scratch-.
Tests retain only fixed synthetic PDF/PNG bytes and metadata, with a 16 MiB
successful-PUT budget. Source-body calls and raw uploads during historical
verification are prohibited by the test adapter. Request timeouts and per-case
progress bound an interrupted run. The fixture audit lists at most 1,000 owned
keys and verifies producer receipts and their exact retained object versions;
LAYER_SNAPSHOT_TEST_REQUIRE_COMPLETE=1 also requires the complete matrix.
Preserve interrupted evidence and audit effects before a retry. Leave the fixture
available for independent inspection, then let its owner remove it.
The ordinary source tests cover unavailable observations, changed-content refusal, checksums, format and binding failures, refresh conservation, and interruption after each durable publication boundary. Gateway MCP integration tests separately cover schema 2 PDF/PNG, PDF pages, schema 1 compatibility, tuple tampering and namespace and pin denials. Passing synthetic tests does not establish live historical retention: validate the exact source candidate, its two authenticated unavailable observations, pinned proof, counters, restart and authenticated MCP response after the reader rollout. Keep earlier missing outcomes and acceptance receipts.
Preserve unclassified source bytes
--allow-unclassified-originals opts into exact-byte retention when the selected
source format cannot be established. It does not broaden selection or transform
content. It requires the existing complete inventory binding, keeps its
generation and rejects refresh, reselection and inventory-only combinations.
After the reader rollout, run clouddrive-originals --allow-unclassified-originals --only-item DRIVE ITEM with the same projected configuration and state prefix.
The source must still match its inventoried ETag, version and size; a
provider checksum is required. Layer checks the whole SHA-256 and checksum, reads
the complete S3 object back, and rechecks source metadata before publication.
The same policy can accompany --allow-historical-reuse with its exact-version
evidence and two unavailable observations.
These originals use application/octet-stream, preserve declared MIME, and
report formatValidation=unverified. Actual format and source-format mismatch
are unknown, not false. MCP serves whole bytes with the original citation and
refuses PDF pages. Namespace grants and pins still apply. Roll out the reader
before enabling this writer policy; older readers reject the new catalog shape.
verifiedUnclassifiedOriginals is a third disjoint partition:
verifiedOriginals = verifiedPdfs + verifiedNonPdfOriginalsWithFormatMismatch + verifiedUnclassifiedOriginals.
unverifiedFormatOriginals reports the format anomaly separately; raw retention
completion says nothing about container validity. Legacy reports without these
fields remain unknown.
Known malformed or truncated PNGs, HTML/JSON error responses, checksum or size mismatches, changed sources, and missing or unbound proof remain unresolved. A format outside validation limits is unassessed, not a validated image. Existing unsupported outcomes and quarantine evidence are archived before replacement. If later analysis establishes corruption, preserve an append-only reassessment and restore the unresolved outcome; never rewrite the original proof. The writer records established PNG corruption as a conditional, append-only assessment bound to the namespace and whole-object SHA. Source reuse, historical reuse and MCP refuse assessed bytes; rerunning restores an unresolved outcome while preserving the predecessor and quarantine. Assessment records are private operator evidence, not MCP object locations.