Skip to content

Add transactional Worker Shell to workflow Workspaces #363

Description

@taras

Story

As a workflow author, I want executable shell blocks to run through the retained Workspace, so a repeatable workflow can use a constrained interpreted shell without gaining native process or host-filesystem authority.

Product contract

Under xmd workflow, the host installs a Workspace-scoped implementation of the existing contextual runtime.process API. Supported shell execution runs through Cloudflare's Workspace Shell model:

durable exec expansion
  → stable effect identity
  → one serialized Workspace effect transaction
  → Deno Worker
  → just-bash
  → Cloudflare WorkspaceFsAdapter
  → authoritative DOFS connection

The result retains the existing process shape:

{
  exitCode: number;
  stdout: string;
  stderr: string;
}

Unsupported command forms fail explicitly. They never fall back to the host PATH, native execution, writable materialization, FUSE, workerd or Containers. Ordinary xmd run keeps its existing host-process behavior.

Worker Shell names the capability. just-bash is its initial interpreter, not a promise of native Bash, POSIX completeness or a permanent public engine choice.

Effect transaction

One shell invocation owns one effect identity, one immediate SQLite transaction and one shell_mutations savepoint. DOFS operations use nested savepoints inside the caller-owned transaction.

Success:

BEGIN IMMEDIATE
  SAVEPOINT shell_mutations
  route all Worker filesystem RPCs through the active effect
  RELEASE shell_mutations
  append the already-filtered successful result
COMMIT

Known failure:

BEGIN IMMEDIATE
  SAVEPOINT shell_mutations
  route Worker filesystem RPCs
  ROLLBACK TO shell_mutations
  RELEASE shell_mutations
  append one already-filtered failed result
COMMIT

Known failure includes nonzero exit, interpreter error, timeout, explicit cancellation and forced Worker termination. A host crash leaves the outer transaction open; SQLite recovery publishes neither its filesystem mutations nor a result event.

The journal security policy filters the result before it enters this transaction boundary. This issue does not create another filtering system.

Connection and concurrency boundary

One host-owned DOFS connection is authoritative for each workflow database. Workspace-local effect transactions execute serially on that connection. Production code does not keep a second long-lived DOFS reader whose negative resolve-cache entries can survive another connection's commit.

All filesystem RPCs carry the stable effect ID, a per-invocation token and a request identity. The router rejects requests for a missing, foreign, cancelled or completed effect and rejects stale or late messages from an earlier invocation.

Worker lifecycle

Cancellation first prevents new filesystem routing, then forcefully terminates the Worker, rolls back shell_mutations, appends the failed result and commits. A CPU-bound just-bash program cannot rely on graceful shutdown because it may never return control to the Worker runtime.

The current proof uses @effectionx/worker@0.5.4 plus a version- and source-checked force-termination delta. Production either:

  • consumes an upstream supported force-termination contract; or
  • owns an equally explicit pinned patch/fork with provenance and a failure-on-version-mismatch guard.

Silent patch drift or graceful-only teardown is not acceptable.

Isolation

The production provider preserves the #351 and #357 proof boundaries:

  • no host PATH or native executable;
  • no host filesystem access;
  • no inherited host environment;
  • no default network route;
  • Workspace path authorization on every filesystem operation; and
  • CPU-bound execution is preemptible without starving host cancellation.

Replay

A committed shell result restores from the journal without starting a Worker. A host interruption before commit leaves no result and reruns the effect against the pre-effect Workspace root under the same durable identity.

Acceptance

  • The Deno workflow adapter installs the Worker Shell process provider; shared production modules contain no Deno, Cloudflare or runtime-detection logic.
  • xmd run process behavior is unchanged.
  • Supported shell blocks preserve the existing process result contract.
  • Unknown or native command requests fail without host fallback.
  • One invocation owns one effect transaction and mutation savepoint.
  • Success publishes filesystem mutations and the filtered result atomically.
  • Nonzero exit, interpreter error, timeout, cancellation and forced termination publish no filesystem mutation and exactly one failed result.
  • A real process crash after a shell write but before result publication leaves neither mutation nor result after restart.
  • One authoritative DOFS connection serves the workflow database and local effect transactions are serialized.
  • Missing, foreign, completed, cancelled and stale Worker messages are fenced.
  • CPU-bound shell code is forcefully terminated within the configured cancellation bound.
  • Replay of a committed result starts no Worker.
  • Containment probes cover host path, native execution, environment, network and filesystem escape.
  • Tests exercise the contextual API through the same executable-block path a workflow document uses.
  • The production binary records its Worker runtime dependency and any maintained patch provenance reproducibly.

Intentionally excluded

  • Worker JavaScript.
  • Native subprocesses, writable FUSE, workerd and Containers.
  • A new public shell component when the contextual process API already carries the operation.
  • External-effect atomicity.
  • Changes to ordinary xmd run.
  • Another journal secret policy.

Evidence and dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions