Run shell blocks in xmd workflows #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 provider preserves the existing process shape:

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

Authored bash and sh blocks both reach the provider as exactly
["bash", "-c", source]. The request's cwd is an absolute logical Workspace
path. No host environment is inherited, and an explicit environment request is
refused. Every other command array fails before a Worker or transaction is
created. It never falls back to the host PATH, native execution, writable
materialization, FUSE, workerd or Containers.

The Worker forwards stdout and stderr progressively through the existing Stdio
operations. The provider honors retain when the contextual API is called
directly. The ordinary executable-block path asks the provider not to retain;
core's per-exec boundary owns capture, binding, display routing and workflow
retention exactly as it does for every other process provider. 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.

A process that reaches an exit status always produces a settled
ProcessOutcome. Exit zero releases shell_mutations; a nonzero status,
including an interpreter refusal represented by an exit status, rolls
shell_mutations back. Both append an already-filtered successful durable
result and commit. This preserves the existing distinction: a bound exec treats
nonzero as data, while core turns an unbound nonzero status into a checked
failure.

A timeout or Worker failure before a status settles rolls shell_mutations
back, appends one already-filtered failed durable result and commits. Replay
raises that recorded failure without starting a Worker.

Structural Effection cancellation is not a completed process result. It fences
new filesystem RPC, force-terminates the Worker immediately, and aborts the
outer transaction. It publishes no exec result; the workflow lifecycle records
interrupted or cancelled, and resume executes the effect again against
the pre-effect Workspace root. A host crash has the same transaction
visibility: SQLite recovery exposes neither 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 and then forcefully
terminates the Worker. There is no graceful-shutdown interval on cancellation:
all durable cleanup is host-owned, and a CPU-bound just-bash program may
never return control to the Worker runtime.

The current proof uses @effectionx/worker@0.5.4 plus a diagnostic
force-termination delta. Production waits for a released upstream
force-termination contract that can preempt a non-cooperative Worker. The open
upstream work is
thefrontside/effectionx#242.

This repository will not carry a pinned patch or fork to deliver #181. Until the
supported upstream contract is released and consumed, #363 is blocked and is
not an #181 delivery dependency. Silent patch drift or graceful-only teardown
remains unacceptable.

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.
  • The exact ["bash", "-c", source] request preserves the existing process result contract; every other command form and every explicit environment request fails without host fallback.
  • Stdout and stderr reach the existing Stdio operations progressively; core remains the authority for capture, binding, display routing and workflow retention.
  • One invocation owns one effect transaction and mutation savepoint.
  • Exit zero publishes filesystem mutations and its filtered settled outcome atomically.
  • A settled nonzero status publishes no filesystem mutation and records an ordinary successful ProcessOutcome; bound and unbound blocks retain their existing semantics.
  • Timeout or Worker failure before a settled status publishes no filesystem mutation and exactly one failed durable result.
  • Structural cancellation fences Worker RPC, force-terminates immediately, publishes neither mutation nor exec result, and leaves lifecycle reporting to the workflow executor.
  • 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 immediately when cancellation or a command timeout begins teardown.
  • 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 the released upstream Worker and force-termination dependencies reproducibly; no repository patch or fork is accepted.
  • architecture.md and specs/workflow-workspace-spec.md are corrected in the implementation PR to use these settled exit-status, failure and structural-cancellation semantics.

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
      Skip to content

      Run shell blocks in xmd workflows #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 provider preserves the existing process shape:

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

      Authored bash and sh blocks both reach the provider as exactly
      ["bash", "-c", source]. The request's cwd is an absolute logical Workspace
      path. No host environment is inherited, and an explicit environment request is
      refused. Every other command array fails before a Worker or transaction is
      created. It never falls back to the host PATH, native execution, writable
      materialization, FUSE, workerd or Containers.

      The Worker forwards stdout and stderr progressively through the existing Stdio
      operations. The provider honors retain when the contextual API is called
      directly. The ordinary executable-block path asks the provider not to retain;
      core's per-exec boundary owns capture, binding, display routing and workflow
      retention exactly as it does for every other process provider. 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.

      A process that reaches an exit status always produces a settled
      ProcessOutcome. Exit zero releases shell_mutations; a nonzero status,
      including an interpreter refusal represented by an exit status, rolls
      shell_mutations back. Both append an already-filtered successful durable
      result and commit. This preserves the existing distinction: a bound exec treats
      nonzero as data, while core turns an unbound nonzero status into a checked
      failure.

      A timeout or Worker failure before a status settles rolls shell_mutations
      back, appends one already-filtered failed durable result and commits. Replay
      raises that recorded failure without starting a Worker.

      Structural Effection cancellation is not a completed process result. It fences
      new filesystem RPC, force-terminates the Worker immediately, and aborts the
      outer transaction. It publishes no exec result; the workflow lifecycle records
      interrupted or cancelled, and resume executes the effect again against
      the pre-effect Workspace root. A host crash has the same transaction
      visibility: SQLite recovery exposes neither 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 and then forcefully
      terminates the Worker. There is no graceful-shutdown interval on cancellation:
      all durable cleanup is host-owned, and a CPU-bound just-bash program may
      never return control to the Worker runtime.

      The current proof uses @effectionx/worker@0.5.4 plus a diagnostic
      force-termination delta. Production waits for a released upstream
      force-termination contract that can preempt a non-cooperative Worker. The open
      upstream work is
      thefrontside/effectionx#242.

      This repository will not carry a pinned patch or fork to deliver #181. Until the
      supported upstream contract is released and consumed, #363 is blocked and is
      not an #181 delivery dependency. Silent patch drift or graceful-only teardown
      remains unacceptable.

      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.
      • The exact ["bash", "-c", source] request preserves the existing process result contract; every other command form and every explicit environment request fails without host fallback.
      • Stdout and stderr reach the existing Stdio operations progressively; core remains the authority for capture, binding, display routing and workflow retention.
      • One invocation owns one effect transaction and mutation savepoint.
      • Exit zero publishes filesystem mutations and its filtered settled outcome atomically.
      • A settled nonzero status publishes no filesystem mutation and records an ordinary successful ProcessOutcome; bound and unbound blocks retain their existing semantics.
      • Timeout or Worker failure before a settled status publishes no filesystem mutation and exactly one failed durable result.
      • Structural cancellation fences Worker RPC, force-terminates immediately, publishes neither mutation nor exec result, and leaves lifecycle reporting to the workflow executor.
      • 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 immediately when cancellation or a command timeout begins teardown.
      • 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 the released upstream Worker and force-termination dependencies reproducibly; no repository patch or fork is accepted.
      • architecture.md and specs/workflow-workspace-spec.md are corrected in the implementation PR to use these settled exit-status, failure and structural-cancellation semantics.

      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

      Activity

      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          Run shell blocks in xmd workflows #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 provider preserves the existing process shape:

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

          Authored bash and sh blocks both reach the provider as exactly
          ["bash", "-c", source]. The request's cwd is an absolute logical Workspace
          path. No host environment is inherited, and an explicit environment request is
          refused. Every other command array fails before a Worker or transaction is
          created. It never falls back to the host PATH, native execution, writable
          materialization, FUSE, workerd or Containers.

          The Worker forwards stdout and stderr progressively through the existing Stdio
          operations. The provider honors retain when the contextual API is called
          directly. The ordinary executable-block path asks the provider not to retain;
          core's per-exec boundary owns capture, binding, display routing and workflow
          retention exactly as it does for every other process provider. 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.

          A process that reaches an exit status always produces a settled
          ProcessOutcome. Exit zero releases shell_mutations; a nonzero status,
          including an interpreter refusal represented by an exit status, rolls
          shell_mutations back. Both append an already-filtered successful durable
          result and commit. This preserves the existing distinction: a bound exec treats
          nonzero as data, while core turns an unbound nonzero status into a checked
          failure.

          A timeout or Worker failure before a status settles rolls shell_mutations
          back, appends one already-filtered failed durable result and commits. Replay
          raises that recorded failure without starting a Worker.

          Structural Effection cancellation is not a completed process result. It fences
          new filesystem RPC, force-terminates the Worker immediately, and aborts the
          outer transaction. It publishes no exec result; the workflow lifecycle records
          interrupted or cancelled, and resume executes the effect again against
          the pre-effect Workspace root. A host crash has the same transaction
          visibility: SQLite recovery exposes neither 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 and then forcefully
          terminates the Worker. There is no graceful-shutdown interval on cancellation:
          all durable cleanup is host-owned, and a CPU-bound just-bash program may
          never return control to the Worker runtime.

          The current proof uses @effectionx/worker@0.5.4 plus a diagnostic
          force-termination delta. Production waits for a released upstream
          force-termination contract that can preempt a non-cooperative Worker. The open
          upstream work is
          thefrontside/effectionx#242.

          This repository will not carry a pinned patch or fork to deliver #181. Until the
          supported upstream contract is released and consumed, #363 is blocked and is
          not an #181 delivery dependency. Silent patch drift or graceful-only teardown
          remains unacceptable.

          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.
          • The exact ["bash", "-c", source] request preserves the existing process result contract; every other command form and every explicit environment request fails without host fallback.
          • Stdout and stderr reach the existing Stdio operations progressively; core remains the authority for capture, binding, display routing and workflow retention.
          • One invocation owns one effect transaction and mutation savepoint.
          • Exit zero publishes filesystem mutations and its filtered settled outcome atomically.
          • A settled nonzero status publishes no filesystem mutation and records an ordinary successful ProcessOutcome; bound and unbound blocks retain their existing semantics.
          • Timeout or Worker failure before a settled status publishes no filesystem mutation and exactly one failed durable result.
          • Structural cancellation fences Worker RPC, force-terminates immediately, publishes neither mutation nor exec result, and leaves lifecycle reporting to the workflow executor.
          • 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 immediately when cancellation or a command timeout begins teardown.
          • 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 the released upstream Worker and force-termination dependencies reproducibly; no repository patch or fork is accepted.
          • architecture.md and specs/workflow-workspace-spec.md are corrected in the implementation PR to use these settled exit-status, failure and structural-cancellation semantics.

          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

          Activity

          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              Run shell blocks in xmd workflows #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 provider preserves the existing process shape:

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

              Authored bash and sh blocks both reach the provider as exactly
              ["bash", "-c", source]. The request's cwd is an absolute logical Workspace
              path. No host environment is inherited, and an explicit environment request is
              refused. Every other command array fails before a Worker or transaction is
              created. It never falls back to the host PATH, native execution, writable
              materialization, FUSE, workerd or Containers.

              The Worker forwards stdout and stderr progressively through the existing Stdio
              operations. The provider honors retain when the contextual API is called
              directly. The ordinary executable-block path asks the provider not to retain;
              core's per-exec boundary owns capture, binding, display routing and workflow
              retention exactly as it does for every other process provider. 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.

              A process that reaches an exit status always produces a settled
              ProcessOutcome. Exit zero releases shell_mutations; a nonzero status,
              including an interpreter refusal represented by an exit status, rolls
              shell_mutations back. Both append an already-filtered successful durable
              result and commit. This preserves the existing distinction: a bound exec treats
              nonzero as data, while core turns an unbound nonzero status into a checked
              failure.

              A timeout or Worker failure before a status settles rolls shell_mutations
              back, appends one already-filtered failed durable result and commits. Replay
              raises that recorded failure without starting a Worker.

              Structural Effection cancellation is not a completed process result. It fences
              new filesystem RPC, force-terminates the Worker immediately, and aborts the
              outer transaction. It publishes no exec result; the workflow lifecycle records
              interrupted or cancelled, and resume executes the effect again against
              the pre-effect Workspace root. A host crash has the same transaction
              visibility: SQLite recovery exposes neither 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 and then forcefully
              terminates the Worker. There is no graceful-shutdown interval on cancellation:
              all durable cleanup is host-owned, and a CPU-bound just-bash program may
              never return control to the Worker runtime.

              The current proof uses @effectionx/worker@0.5.4 plus a diagnostic
              force-termination delta. Production waits for a released upstream
              force-termination contract that can preempt a non-cooperative Worker. The open
              upstream work is
              thefrontside/effectionx#242.

              This repository will not carry a pinned patch or fork to deliver #181. Until the
              supported upstream contract is released and consumed, #363 is blocked and is
              not an #181 delivery dependency. Silent patch drift or graceful-only teardown
              remains unacceptable.

              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.
              • The exact ["bash", "-c", source] request preserves the existing process result contract; every other command form and every explicit environment request fails without host fallback.
              • Stdout and stderr reach the existing Stdio operations progressively; core remains the authority for capture, binding, display routing and workflow retention.
              • One invocation owns one effect transaction and mutation savepoint.
              • Exit zero publishes filesystem mutations and its filtered settled outcome atomically.
              • A settled nonzero status publishes no filesystem mutation and records an ordinary successful ProcessOutcome; bound and unbound blocks retain their existing semantics.
              • Timeout or Worker failure before a settled status publishes no filesystem mutation and exactly one failed durable result.
              • Structural cancellation fences Worker RPC, force-terminates immediately, publishes neither mutation nor exec result, and leaves lifecycle reporting to the workflow executor.
              • 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 immediately when cancellation or a command timeout begins teardown.
              • 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 the released upstream Worker and force-termination dependencies reproducibly; no repository patch or fork is accepted.
              • architecture.md and specs/workflow-workspace-spec.md are corrected in the implementation PR to use these settled exit-status, failure and structural-cancellation semantics.

              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

              Activity

              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  Run shell blocks in xmd workflows #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 provider preserves the existing process shape:

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

                  Authored bash and sh blocks both reach the provider as exactly
                  ["bash", "-c", source]. The request's cwd is an absolute logical Workspace
                  path. No host environment is inherited, and an explicit environment request is
                  refused. Every other command array fails before a Worker or transaction is
                  created. It never falls back to the host PATH, native execution, writable
                  materialization, FUSE, workerd or Containers.

                  The Worker forwards stdout and stderr progressively through the existing Stdio
                  operations. The provider honors retain when the contextual API is called
                  directly. The ordinary executable-block path asks the provider not to retain;
                  core's per-exec boundary owns capture, binding, display routing and workflow
                  retention exactly as it does for every other process provider. 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.

                  A process that reaches an exit status always produces a settled
                  ProcessOutcome. Exit zero releases shell_mutations; a nonzero status,
                  including an interpreter refusal represented by an exit status, rolls
                  shell_mutations back. Both append an already-filtered successful durable
                  result and commit. This preserves the existing distinction: a bound exec treats
                  nonzero as data, while core turns an unbound nonzero status into a checked
                  failure.

                  A timeout or Worker failure before a status settles rolls shell_mutations
                  back, appends one already-filtered failed durable result and commits. Replay
                  raises that recorded failure without starting a Worker.

                  Structural Effection cancellation is not a completed process result. It fences
                  new filesystem RPC, force-terminates the Worker immediately, and aborts the
                  outer transaction. It publishes no exec result; the workflow lifecycle records
                  interrupted or cancelled, and resume executes the effect again against
                  the pre-effect Workspace root. A host crash has the same transaction
                  visibility: SQLite recovery exposes neither 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 and then forcefully
                  terminates the Worker. There is no graceful-shutdown interval on cancellation:
                  all durable cleanup is host-owned, and a CPU-bound just-bash program may
                  never return control to the Worker runtime.

                  The current proof uses @effectionx/worker@0.5.4 plus a diagnostic
                  force-termination delta. Production waits for a released upstream
                  force-termination contract that can preempt a non-cooperative Worker. The open
                  upstream work is
                  thefrontside/effectionx#242.

                  This repository will not carry a pinned patch or fork to deliver #181. Until the
                  supported upstream contract is released and consumed, #363 is blocked and is
                  not an #181 delivery dependency. Silent patch drift or graceful-only teardown
                  remains unacceptable.

                  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.
                  • The exact ["bash", "-c", source] request preserves the existing process result contract; every other command form and every explicit environment request fails without host fallback.
                  • Stdout and stderr reach the existing Stdio operations progressively; core remains the authority for capture, binding, display routing and workflow retention.
                  • One invocation owns one effect transaction and mutation savepoint.
                  • Exit zero publishes filesystem mutations and its filtered settled outcome atomically.
                  • A settled nonzero status publishes no filesystem mutation and records an ordinary successful ProcessOutcome; bound and unbound blocks retain their existing semantics.
                  • Timeout or Worker failure before a settled status publishes no filesystem mutation and exactly one failed durable result.
                  • Structural cancellation fences Worker RPC, force-terminates immediately, publishes neither mutation nor exec result, and leaves lifecycle reporting to the workflow executor.
                  • 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 immediately when cancellation or a command timeout begins teardown.
                  • 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 the released upstream Worker and force-termination dependencies reproducibly; no repository patch or fork is accepted.
                  • architecture.md and specs/workflow-workspace-spec.md are corrected in the implementation PR to use these settled exit-status, failure and structural-cancellation semantics.

                  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

                  Activity

                  Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      Run shell blocks in xmd workflows #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 provider preserves the existing process shape:

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

                      Authored bash and sh blocks both reach the provider as exactly
                      ["bash", "-c", source]. The request's cwd is an absolute logical Workspace
                      path. No host environment is inherited, and an explicit environment request is
                      refused. Every other command array fails before a Worker or transaction is
                      created. It never falls back to the host PATH, native execution, writable
                      materialization, FUSE, workerd or Containers.

                      The Worker forwards stdout and stderr progressively through the existing Stdio
                      operations. The provider honors retain when the contextual API is called
                      directly. The ordinary executable-block path asks the provider not to retain;
                      core's per-exec boundary owns capture, binding, display routing and workflow
                      retention exactly as it does for every other process provider. 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.

                      A process that reaches an exit status always produces a settled
                      ProcessOutcome. Exit zero releases shell_mutations; a nonzero status,
                      including an interpreter refusal represented by an exit status, rolls
                      shell_mutations back. Both append an already-filtered successful durable
                      result and commit. This preserves the existing distinction: a bound exec treats
                      nonzero as data, while core turns an unbound nonzero status into a checked
                      failure.

                      A timeout or Worker failure before a status settles rolls shell_mutations
                      back, appends one already-filtered failed durable result and commits. Replay
                      raises that recorded failure without starting a Worker.

                      Structural Effection cancellation is not a completed process result. It fences
                      new filesystem RPC, force-terminates the Worker immediately, and aborts the
                      outer transaction. It publishes no exec result; the workflow lifecycle records
                      interrupted or cancelled, and resume executes the effect again against
                      the pre-effect Workspace root. A host crash has the same transaction
                      visibility: SQLite recovery exposes neither 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 and then forcefully
                      terminates the Worker. There is no graceful-shutdown interval on cancellation:
                      all durable cleanup is host-owned, and a CPU-bound just-bash program may
                      never return control to the Worker runtime.

                      The current proof uses @effectionx/worker@0.5.4 plus a diagnostic
                      force-termination delta. Production waits for a released upstream
                      force-termination contract that can preempt a non-cooperative Worker. The open
                      upstream work is
                      thefrontside/effectionx#242.

                      This repository will not carry a pinned patch or fork to deliver #181. Until the
                      supported upstream contract is released and consumed, #363 is blocked and is
                      not an #181 delivery dependency. Silent patch drift or graceful-only teardown
                      remains unacceptable.

                      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.
                      • The exact ["bash", "-c", source] request preserves the existing process result contract; every other command form and every explicit environment request fails without host fallback.
                      • Stdout and stderr reach the existing Stdio operations progressively; core remains the authority for capture, binding, display routing and workflow retention.
                      • One invocation owns one effect transaction and mutation savepoint.
                      • Exit zero publishes filesystem mutations and its filtered settled outcome atomically.
                      • A settled nonzero status publishes no filesystem mutation and records an ordinary successful ProcessOutcome; bound and unbound blocks retain their existing semantics.
                      • Timeout or Worker failure before a settled status publishes no filesystem mutation and exactly one failed durable result.
                      • Structural cancellation fences Worker RPC, force-terminates immediately, publishes neither mutation nor exec result, and leaves lifecycle reporting to the workflow executor.
                      • 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 immediately when cancellation or a command timeout begins teardown.
                      • 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 the released upstream Worker and force-termination dependencies reproducibly; no repository patch or fork is accepted.
                      • architecture.md and specs/workflow-workspace-spec.md are corrected in the implementation PR to use these settled exit-status, failure and structural-cancellation semantics.

                      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

                      Activity

                      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          Run shell blocks in xmd workflows #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 provider preserves the existing process shape:

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

                          Authored bash and sh blocks both reach the provider as exactly
                          ["bash", "-c", source]. The request's cwd is an absolute logical Workspace
                          path. No host environment is inherited, and an explicit environment request is
                          refused. Every other command array fails before a Worker or transaction is
                          created. It never falls back to the host PATH, native execution, writable
                          materialization, FUSE, workerd or Containers.

                          The Worker forwards stdout and stderr progressively through the existing Stdio
                          operations. The provider honors retain when the contextual API is called
                          directly. The ordinary executable-block path asks the provider not to retain;
                          core's per-exec boundary owns capture, binding, display routing and workflow
                          retention exactly as it does for every other process provider. 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.

                          A process that reaches an exit status always produces a settled
                          ProcessOutcome. Exit zero releases shell_mutations; a nonzero status,
                          including an interpreter refusal represented by an exit status, rolls
                          shell_mutations back. Both append an already-filtered successful durable
                          result and commit. This preserves the existing distinction: a bound exec treats
                          nonzero as data, while core turns an unbound nonzero status into a checked
                          failure.

                          A timeout or Worker failure before a status settles rolls shell_mutations
                          back, appends one already-filtered failed durable result and commits. Replay
                          raises that recorded failure without starting a Worker.

                          Structural Effection cancellation is not a completed process result. It fences
                          new filesystem RPC, force-terminates the Worker immediately, and aborts the
                          outer transaction. It publishes no exec result; the workflow lifecycle records
                          interrupted or cancelled, and resume executes the effect again against
                          the pre-effect Workspace root. A host crash has the same transaction
                          visibility: SQLite recovery exposes neither 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 and then forcefully
                          terminates the Worker. There is no graceful-shutdown interval on cancellation:
                          all durable cleanup is host-owned, and a CPU-bound just-bash program may
                          never return control to the Worker runtime.

                          The current proof uses @effectionx/worker@0.5.4 plus a diagnostic
                          force-termination delta. Production waits for a released upstream
                          force-termination contract that can preempt a non-cooperative Worker. The open
                          upstream work is
                          thefrontside/effectionx#242.

                          This repository will not carry a pinned patch or fork to deliver #181. Until the
                          supported upstream contract is released and consumed, #363 is blocked and is
                          not an #181 delivery dependency. Silent patch drift or graceful-only teardown
                          remains unacceptable.

                          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.
                          • The exact ["bash", "-c", source] request preserves the existing process result contract; every other command form and every explicit environment request fails without host fallback.
                          • Stdout and stderr reach the existing Stdio operations progressively; core remains the authority for capture, binding, display routing and workflow retention.
                          • One invocation owns one effect transaction and mutation savepoint.
                          • Exit zero publishes filesystem mutations and its filtered settled outcome atomically.
                          • A settled nonzero status publishes no filesystem mutation and records an ordinary successful ProcessOutcome; bound and unbound blocks retain their existing semantics.
                          • Timeout or Worker failure before a settled status publishes no filesystem mutation and exactly one failed durable result.
                          • Structural cancellation fences Worker RPC, force-terminates immediately, publishes neither mutation nor exec result, and leaves lifecycle reporting to the workflow executor.
                          • 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 immediately when cancellation or a command timeout begins teardown.
                          • 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 the released upstream Worker and force-termination dependencies reproducibly; no repository patch or fork is accepted.
                          • architecture.md and specs/workflow-workspace-spec.md are corrected in the implementation PR to use these settled exit-status, failure and structural-cancellation semantics.

                          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

                          Activity

                          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              Run shell blocks in xmd workflows #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 provider preserves the existing process shape:

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

                              Authored bash and sh blocks both reach the provider as exactly
                              ["bash", "-c", source]. The request's cwd is an absolute logical Workspace
                              path. No host environment is inherited, and an explicit environment request is
                              refused. Every other command array fails before a Worker or transaction is
                              created. It never falls back to the host PATH, native execution, writable
                              materialization, FUSE, workerd or Containers.

                              The Worker forwards stdout and stderr progressively through the existing Stdio
                              operations. The provider honors retain when the contextual API is called
                              directly. The ordinary executable-block path asks the provider not to retain;
                              core's per-exec boundary owns capture, binding, display routing and workflow
                              retention exactly as it does for every other process provider. 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.

                              A process that reaches an exit status always produces a settled
                              ProcessOutcome. Exit zero releases shell_mutations; a nonzero status,
                              including an interpreter refusal represented by an exit status, rolls
                              shell_mutations back. Both append an already-filtered successful durable
                              result and commit. This preserves the existing distinction: a bound exec treats
                              nonzero as data, while core turns an unbound nonzero status into a checked
                              failure.

                              A timeout or Worker failure before a status settles rolls shell_mutations
                              back, appends one already-filtered failed durable result and commits. Replay
                              raises that recorded failure without starting a Worker.

                              Structural Effection cancellation is not a completed process result. It fences
                              new filesystem RPC, force-terminates the Worker immediately, and aborts the
                              outer transaction. It publishes no exec result; the workflow lifecycle records
                              interrupted or cancelled, and resume executes the effect again against
                              the pre-effect Workspace root. A host crash has the same transaction
                              visibility: SQLite recovery exposes neither 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 and then forcefully
                              terminates the Worker. There is no graceful-shutdown interval on cancellation:
                              all durable cleanup is host-owned, and a CPU-bound just-bash program may
                              never return control to the Worker runtime.

                              The current proof uses @effectionx/worker@0.5.4 plus a diagnostic
                              force-termination delta. Production waits for a released upstream
                              force-termination contract that can preempt a non-cooperative Worker. The open
                              upstream work is
                              thefrontside/effectionx#242.

                              This repository will not carry a pinned patch or fork to deliver #181. Until the
                              supported upstream contract is released and consumed, #363 is blocked and is
                              not an #181 delivery dependency. Silent patch drift or graceful-only teardown
                              remains unacceptable.

                              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.
                              • The exact ["bash", "-c", source] request preserves the existing process result contract; every other command form and every explicit environment request fails without host fallback.
                              • Stdout and stderr reach the existing Stdio operations progressively; core remains the authority for capture, binding, display routing and workflow retention.
                              • One invocation owns one effect transaction and mutation savepoint.
                              • Exit zero publishes filesystem mutations and its filtered settled outcome atomically.
                              • A settled nonzero status publishes no filesystem mutation and records an ordinary successful ProcessOutcome; bound and unbound blocks retain their existing semantics.
                              • Timeout or Worker failure before a settled status publishes no filesystem mutation and exactly one failed durable result.
                              • Structural cancellation fences Worker RPC, force-terminates immediately, publishes neither mutation nor exec result, and leaves lifecycle reporting to the workflow executor.
                              • 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 immediately when cancellation or a command timeout begins teardown.
                              • 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 the released upstream Worker and force-termination dependencies reproducibly; no repository patch or fork is accepted.
                              • architecture.md and specs/workflow-workspace-spec.md are corrected in the implementation PR to use these settled exit-status, failure and structural-cancellation semantics.

                              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

                              Activity

                              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions