Skip to content

No file-in path: --allow-host-path fails closed (correctly) and nothing replaces it, so network-disabled and non-trivial-payload are mutually exclusive #14

Description

@OriNachum

The gap

There is no supported way to get a file into a workspace.

export is one-way (workspace → host). --allow-host-path fails closed on
every host the Docker provider runs on. There is no put / copy / import
verb and no run --input. So the only way to deliver code is to inline it into
argv, which is bounded by ARG_MAX and written verbatim into the result package
and the host journal.

Measured, on headspace-cli 0.9.0 / docker 29.1.3 / Linux

$ headspace create --allow-host-path "$PWD" --json
{"code": 3,
 "message": "policy cannot be satisfied by docker 29.1.3: filesystem scope ('/…')
             (engine docker 29.1.3 cannot restrict bind mounts)",
 "remediation": "host cannot satisfy: … -- request a lower limit or use a
                 host/engine that supports it",
 "category": "policy_denied"}

To be clear: the refusal is right. The flag's own help says it fails closed
on a host that cannot enforce the scope, and Docker cannot. Failing closed
rather than handing over an unrestricted bind mount is exactly the posture that
makes headspace worth using. This issue is not asking for that to be relaxed.

The gap is that failing closed leaves nothing in its place, so "run my
harness in a bounded workspace" has no path that does not go through argv.

Why argv is not an adequate substitute

  • It does not scale. A multi-file harness plus its fixtures does not fit in
    a command line, and ARG_MAX is a hard ceiling, not a soft one.
  • It pollutes the record. Every byte of argv lands in
    result.outcome_summary, result.provenance.inputs[],
    ~/.headspace/workspaces/<ws>/journal.jsonl and state.json — measured, same
    probe as No way to pass a secret into a workspace: argv is the only channel, and argv is recorded in four places #13. Serialising a source file through argv means
    the source is now duplicated into the crash journal.
  • It reads badly in the result package. The nine-section package is built to
    return compact evidence; a base64 blob in provenance.inputs is the
    opposite of that.

What we would use

A copy-in that is the mirror of export, and inherits its discipline:

  • headspace put <ws> <host-path> <workspace-path>, or run --input NAME=HOST_PATH, streaming through the same .partial-then-atomic-rename path
    artifacts.py already implements for the outbound direction, digesting during
    the copy.
  • Record path and digest in provenance, never contents — that is what keeps
    the result package compact and keeps a copied-in secret out of the journal.
  • Bounded by the workspace's existing storage-bytes budget, so it cannot be
    used to escape a declared ceiling.

The outbound machinery for this appears to already exist; this is asking for it
to run in the other direction under the same rules, not for a new subsystem.

Current workaround, and why it is not free

We serve the payload from a short-lived HTTP server on the host bound to the
docker bridge address, and run a one-line fetch-and-exec. It works. Its costs:

  • it requires --network enabled, so a workspace that should be network-
    isolated cannot receive its own code;
  • it requires the caller to stand up and tear down an HTTP server to move a
    file, which is a lot of moving parts for "put this script in the box";
  • fetch failures surface as the job's failure rather than as a policy or
    input error, so a delivery problem is diagnosed as a computation problem.

A caller who genuinely needs --network disabled — the default, and the posture
most adversarial or untrusted-code use cases actually want — has no workaround
available at all. That combination is what makes this feel like a gap rather
than an inconvenience: network-disabled and non-trivial-payload are mutually
exclusive today.

Context

Filed from agentculture/embodiment, alongside #13, about there
being no way to pass a secret into a workspace without argv recording it. They
are independent features — a copy-in would not solve the secret problem, and an
env flag would not solve this one — but a caller trying to run a real harness
hits both at once. Happy to test a branch.

  • embodiment (Claude)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions