A local-first event pipeline for independent agents, built on Jazz.
thought-stream spec artifacts.md
7.4 kB

Private durable artifacts #

Purpose and boundary #

An agent workspace (including a future exe.dev workspace) is a persistent working filesystem, not durable thought stream storage. Promotion is a separate trusted capability: one explicitly named regular file under one explicitly named workspace lease root is copied into the thought stream's private content-addressed blob root and represented by append-only evidence.

This release implements the host-side capability seam. It does not implement an exe.dev transport or a model-callable tool. A future bridge may stage exactly one remote file into an authorized local lease root and invoke the same materializer without changing this contract. Importing code from an artifact into a host worktree remains a separate capability.

Events #

stream.thought.artifact.requested@1 #

A request is append-only evidence, not publication authority. It identifies:

  • requestId: stable bounded request identity;
  • workspaceLeaseId: explicit lease identity;
  • workspaceRootLabel: non-path label for the explicit root supplied to the trusted process;
  • relativeFilePath: one normalized relative path, never absolute and never containing . or .. components;
  • requested artifact metadata: stable artifactId, immutable positive artifactVersion, kind, title, summary, declared mediaType, provenance labels, visibility, optional supersession;
  • optional requestingAgentId, requestingRunId, and sourceEventId;
  • publicationEligible: false and publicationAuthority: false;
  • status: pending, failed, or denied, with a bounded content-dark reason code for inert terminal evidence.

The accepted request event never contains the workspace's absolute root. The trusted API receives that root out of band. Structurally invalid requests fail before durable acceptance. A valid but non-materializable request may be recorded as failed/denied evidence; the CLI uses a pending request followed by exact materialization and reports failure without broadening the selected path.

stream.thought.artifact@1 #

The artifact event is immutable, private/sensitive, self-rooted, and metadata/reference-only. It contains:

  • artifactId, artifactVersion, kind, title, summary;
  • declared and verified mediaType;
  • blob: { algorithm: "sha256", sha256, relativePath, byteCount };
  • provenance labels and bounded relations;
  • a required strong materialized-from-request relation to the request event;
  • visibility, publicationEligible: false, and optional append-only supersession.

It contains no inline body, base64 bytes, workspace path, absolute path, or publication capability. Blob paths are canonical POSIX paths of the exact form sha256/<first-two-hex>/<64-hex-digest>.

Formats and limits #

Supported pairs are strict:

Extensions Media type Validation
.md, .markdown text/markdown valid UTF-8; no NUL
.txt text/plain valid UTF-8; no NUL
.json application/json valid UTF-8 JSON
.yaml, .yml application/yaml or text/yaml valid UTF-8 YAML
.png image/png PNG signature
.jpg, .jpeg image/jpeg JPEG SOI and EOI markers

Text/structured artifacts are capped at 262,144 bytes. PNG/JPEG artifacts are capped at 8,388,608 bytes. Empty files are rejected. Reads are bounded and fail as soon as the exact cap is exceeded.

Workspace containment #

The broker is supplied one explicit local lease root and one relative file path. It canonicalizes the root, rejects a symlink root, verifies every selected parent component with lstat, rejects file and parent symlinks, and opens only the resulting regular file. The canonical file must remain strictly beneath the canonical root. Missing, special, changed-size, or path-swapped files fail closed. No scan, wildcard, fallback root, or path broadening occurs.

Absolute workspace roots are process-local authority and are never copied into request/artifact payloads, catalog data, errors intended for durable evidence, or provenance labels.

Blob storage #

Artifact bytes share JazzThoughtStore.getArtifactRoot() with existing downloaded image blobs. The canonical destination is sha256/<prefix>/<hash>. Directories are mode 0700; blobs are mode 0600. Creation uses a private temporary file and an atomic hard-link no-overwrite publication step. An already existing blob is accepted only after its regular-file type, mode-independent bytes, exact size, and SHA-256 are verified. Blob-root and parent symlinks are rejected.

Every read resolves only the canonical relative path under the canonical blob root, rejects symlinks including parents, performs a bounded read, and re-verifies size, hash, extension-independent media magic, and structured/text validity against event metadata. Tampering fails closed.

Identity and supersession #

The artifact idempotency key is artifact:<artifactId>:v<artifactVersion>. Same identity and same complete event payload/bytes is a no-op. Same (artifactId, artifactVersion) with different bytes or metadata is a hard conflict. Blob deduplication is independent: identical bytes are stored once even when referenced by different artifacts.

A later event may name supersedesArtifactEventId. The old event remains; the metadata-only catalog marks it superseded.

Trusted API and CLI #

The trusted API has two explicit operations: append a validated request, then materialize that request using its matching explicit lease root. The CLI seam is:

thought artifact-request --workspace-root <absolute-local-lease-root> --workspace-lease-id <id> \
  --file <relative-path> --request-id <id> --artifact-id <id> --artifact-version <n> \
  --kind <kind> --title <title> --summary <summary> --media-type <type> \
  [--visibility private|sensitive] [--requesting-agent <id>] [--requesting-run <id>] \
  [--source-event <event-id>] [--provenance-source <label>] [--provenance-label <label>] \
  [--supersedes <event-id>]

The CLI appends request evidence and invokes the exact trusted materializer. It prints identifiers, hashes, byte counts, and insertion status only; never artifact bytes or absolute paths.

Catalog and private inspector #

GET /api/artifacts is metadata-only. It contains no body or encoded bytes. GET /api/artifacts/:eventId resolves and verifies the blob on demand. Text detail returns bounded UTF-8 text. Image detail returns metadata and a private same-origin contentPath; raw bytes are available only from GET /api/artifacts/:eventId/content, with a verified image content type and nosniff. Catalog/list/log output never embeds image bytes.

The inspector remains loopback-only and read-only. The authenticated private proxy may forward its existing authenticated GET/HEAD requests. No generic browser mutation route or public artifact route is introduced.

Privacy and leakage rules #

Both event families have minimum privacy private; sensitive visibility maps to sensitive event privacy. publicationEligible is always false. Provenance and workspace labels reject absolute filesystem paths. Artifact bytes are excluded from events, catalog projections, ordinary logs, notifications, and training/publication paths. Tests scan serialized events/catalogs and command output for source roots, credentials, and private bodies.