DeepSeek Harness plugin

dsh-attachment-s3

Content-addressed S3 attachment storage for the DeepSeek Harness attachment seam

Jump to install

Source facts

Repository
guaguasong/dsh-attachment-s3
Latest update
Aug 20, 2026
Category
Tools & Capabilities
GitHub stars
0
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-21

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/guaguasong/dsh-attachment-s3
Plugin: dsh-attachment-s3
Author: guaguasong

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.mdSource · read only
README language

@guaguasong/dsh-attachment-s3

English | 中文

S3 storage for the DeepSeek Harness attachment seam. It implements AttachmentStore — the same abstract service the in-box @deepseek-ai/dsh-attachment-local backend implements over DSH_HOME — so a deployment's session images live in a bucket instead of on the machine that admitted them. The seam takes exactly one provider, so this plugin replaces the local backend rather than joining it.

Nothing about the bucket is model-visible: the durable reference stays the opaque sha256: id already recorded in the session log, so moving between backends does not change what a transcript means.

Install

dsh plugin --profile <name> add @guaguasong/dsh-attachment-s3
export DSH_ATTACHMENT_S3_BUCKET=my-attachments
export DSH_ATTACHMENT_S3_REGION=us-east-1
dsh --profile <name>

The package declares "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }, so dsh plugin appends it to the profile's bundle layer stack. Its patch disables the attachment-local row dsh-base inserts and adds the attachment-s3 row. Check both before booting:

dsh --profile <name> --dump-config | grep -A2 'id: attachment'

dsh plugin --profile <name> remove @guaguasong/dsh-attachment-s3 reverses the install and restores the local backend.

Environment

The bundle patch reads its values from the environment at mount. DSH_-prefixed names must come from the launching environment — export them or set them in the unit that starts dsh; the launcher refuses that prefix inside a .env file. Credential values are yours to name, so they can live in $DSH_HOME/.env.

VariableConfig field
DSH_ATTACHMENT_S3_BUCKETbucket — required; unset fails the boot on this row rather than storing attachments elsewhere
DSH_ATTACHMENT_S3_REGIONregion
DSH_ATTACHMENT_S3_ENDPOINTendpoint — an S3-compatible service
DSH_ATTACHMENT_S3_FORCE_PATH_STYLEforcePathStyletrue enables it
DSH_ATTACHMENT_S3_PREFIXprefix
DSH_ATTACHMENT_S3_ACCESS_KEY_ID_REFaccessKeyIdRef — the name of the variable holding the key, not the key
DSH_ATTACHMENT_S3_SECRET_ACCESS_KEY_REFsecretAccessKeyRef
DSH_ATTACHMENT_S3_SESSION_TOKEN_REFsessionTokenRef

To pin configuration instead of reading the environment, restate the row in $DSH_HOME/profiles/<name>/cordis.patch.yml, which is applied after every bundle layer. An id-targeted patch replaces the row's whole config, so restate the fields you keep:

- id: attachment-s3
  name: '@guaguasong/dsh-attachment-s3'
  config:
    bucket: my-attachments
    region: us-east-1

Configuration

FieldDefaultMeaning
bucket— (required)Bucket holding every attachment object.
regionSDK resolutionBucket region.
endpointAWS S3Endpoint of an S3-compatible service.
forcePathStylefalsePath-style addressing, required by most S3-compatible services.
prefixattachments/v1Key prefix owning this deployment's objects.
accessKeyIdRefEnvironment-variable name holding the access key id.
secretAccessKeyRefEnvironment-variable name holding the secret access key.
sessionTokenRefEnvironment-variable name holding a session token.
maxImageBytes5 MiBMaximum encoded bytes for one image.
maxImagesPerMessage20Maximum images in one submitted message.
maxMessageImageBytes100 MiBMaximum aggregate encoded image bytes in one message.
maxImagePixels40,000,000Maximum intrinsic width × height for one image.
maxImageDimension2000Maximum intrinsic width and height, applied per side.

The admission limits carry the same defaults as the local backend, so switching backends does not change which images a deployment accepts. Object policy a bucket already owns — default encryption, storage class, lifecycle — is left to the bucket.

Configuration carries credential references, never values, matching the harness credential seam. Each is resolved per request, through ctx.credentials when a credential provider is loaded and through the process environment otherwise, so a rotated secret reaches the next request without a restart. Declare both key references or neither: a half-declared pair fails at load instead of silently signing as the SDK's ambient identity. Declaring neither is the normal deployment on an instance role.

How it stores

<prefix>/objects/<first two hex digits>/<sha256 hex>

One immutable object per distinct image, Content-Type set to the verified media type, the SHA-256 sent as the object checksum, and the intrinsic width/height recorded as object metadata. The session log records sha256:<hex> — an opaque id, never a key or a URL. The v1 segment fences a future incompatible layout off from stored objects.

  • Write-once. Uploads are conditional (If-None-Match: *), so concurrent writers of identical bytes never overwrite each other. The writer that loses the race verifies what is already stored instead of republishing it: the recorded checksum settles it without moving bytes, and an object stored without one is read back and compared, because a same-length substitution would otherwise publish a reference no later read can return.
  • Verified reads. A read requests exactly the byte range the reference claims, re-hashes the bytes, and re-derives the image header before returning, so a bucket-side substitution surfaces as ATTACHMENT_CORRUPT instead of reaching a model request.
  • Failure codes. Admission keeps the seam's caller-correctable codes (IMAGE_TOO_LARGE, IMAGE_TYPE_MISMATCH, IMAGE_TOO_MANY_PIXELS, IMAGE_DIMENSION_TOO_LARGE, INVALID_IMAGE); storage failures surface as ATTACHMENT_WRITE_FAILED, ATTACHMENT_READ_FAILED, ATTACHMENT_NOT_FOUND, or ATTACHMENT_CORRUPT, each carrying its cause.

S3-compatible services

The backend needs four things from a bucket: a conditional write that refuses a taken key, an object checksum it can send and read back, ranged reads, and a distinguishable "no such key". AWS S3 provides all four; other services vary. Probe one before pointing a deployment at it:

PROBE_ENDPOINT=https://s3.example.com PROBE_REGION=us-east-1 PROBE_BUCKET=<bucket> \
AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... pnpm run probe

It writes and deletes one small object and reports what the service does with each behavior. A service that ignores the conditional write still works: the key is the digest of the bytes, so an overwrite writes exactly what is already there, and the writer publishing the reference is the one that wrote it — storage still deduplicates, only the upload repeats. A service that rejects the conditional write or the checksum header cannot run this backend as it stands.

Development

pnpm install       # runs `prepare`, which builds lib/
pnpm run test      # unit tests, including the real AWS SDK against a loopback S3-compatible service
pnpm run typecheck
pnpm run build
pnpm run test:e2e  # real bucket; self-skips without DSH_S3_E2E_BUCKET

pnpm run test needs no bucket and no network: tests/support/fake-s3.ts serves the S3 requests the SDK actually sends, so signing, conditional writes, range reads, and status classification are exercised for real. The e2e suite reads DSH_S3_E2E_BUCKET and optionally DSH_S3_E2E_REGION, DSH_S3_E2E_ENDPOINT, DSH_S3_E2E_FORCE_PATH_STYLE, and DSH_S3_E2E_PREFIX; it writes under a random per-run prefix and deletes what it wrote.

Publishing runs prepublishOnly first: clean, typecheck, the full suite, then build.

Known Limitations and Deferred Work

  • No retention or deletion. Objects are written and never removed; the seam has no retention policy on either backend. Bucket lifecycle rules are the only collector, and expiring an object a session still references turns that attachment into ATTACHMENT_NOT_FOUND.
  • Images only. The seam's version-one surface carries PNG, JPEG, WebP, and GIF.
  • One bucket per deployment. Routing sessions or workspaces to different buckets would need a routing layer this package does not have.
  • Full-object transfers. Reads buffer the whole image in memory, bounded by maxImageBytes.
  • Sharing needs more than attachments. Session logs stay wherever the profile's persistence backend puts them — by default $DSH_HOME/sessions, which is machine-local. A bucket makes attachments durable and centrally managed; it does not by itself make a session readable from another machine.