Skip to content

Environments: layer sandbox-contract/v1 on a foreign image at build - #40

Merged
echarles merged 3 commits into
mainfrom
feat/foreign-image-contract-layer
Oct 30, 2026
Merged

echarles merged 3 commits into
mainfrom
feat/foreign-image-contract-layer

Conversation

@echarles

Copy link
Copy Markdown
Member

An image source never became a sandbox: the build ended in a doctor it had not installed, and uv pip sync removed what the image held. This layers the contract on at build.

  • The contract's files are copied from an approved base pinned in the lock (# datalayer-contract-base:), then PID 1 (static tini, by checksum), the kernel stack over what the image holds (install, not sync), the contract user and home, the entrypoint.
  • A Python-less Debian-derived image gets its distribution's Python. Anything not Debian-derived stops with a sentence. The lock is solved for the image's own interpreter.
  • New source foreignDockerfile: one base, held to the same registries as image. Datalayer builder only. It is a new source rather than a field so existing spec digests do not change.
  • DEFAULT_ALLOWED_REGISTRIES is unchanged, and the test pins it.

Tested: tests/test_environment_foreign_contract_layer.py, 22 passed with DATALAYER_ENVIRONMENT_DOCKER_TESTS=1 and PLANE_DOCKERFILES set: real solve and build on python:3.12-slim and ubuntu:24.04, all 15 doctor rows, the image's entrypoint serving Jupyter, a kernel answering, and alpine refused. A local image stood in for the approved base. The real one, and a BuildKit daemon, have not been used.

…; a Dockerfile on a foreign base, held to the registries an image is
Copilot AI balanced review requested due to automatic review settings October 30, 2026 13:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical validation gaps allow external image references to bypass registry restrictions and continued FROM instructions to retain mutable tags, undermining dependency controls and reproducible builds.

6 open issues
What changed in this PR

This PR extends the environment pipeline to layer sandbox-contract/v1 onto foreign images, enabling them to run as Datalayer sandboxes while preserving packages outside the lock.

Changes:

  • Adds the Datalayer-only foreignDockerfile source with single-base and registry validation.
  • Pins the contract source in lock metadata and adds Python bootstrap, runtime files, tini, and the contract user during builds.
  • Updates the schema, documentation, and tests, including opt-in Docker integration coverage.
File Description
tests/​test_environment_resolve.py Updates the uv bootstrap assertion.
tests/​test_environment_foreign_contract_layer.py Adds validation, rendering, and Docker integration tests.
tests/​test_environment_datalayer_builder.py Updates contract-layer and capability expectations.
schemas/​environment-v1alpha1.json Adds foreignDockerfile to allowed sources.
docs/​docs/​environments/​specification.mdx Documents foreign sources and contract layering.
code_sandboxes/​environments/​spec.py Defines and validates foreign build sources.
code_sandboxes/​environments/​resolve.py Bootstraps foreign Python and records the contract base.
code_sandboxes/​environments/​contract.py Adds foreign Dockerfile validation and base pinning.
code_sandboxes/​environments/​adapters/​datalayer.py Layers the sandbox runtime and preserves unlocked packages.

Review effort: Balanced

Comment on lines +705 to +709
findings += [
finding
for finding in dockerfile_findings_for_build(text)
if finding.instruction in ("COPY", "ADD", "escape")
]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bba02df. COPY --from= and RUN --mount=from= must now name an earlier stage of the Dockerfile, so neither can bring in an image past the base's registry check. Tests cover both forms.

Comment thread code_sandboxes/environments/contract.py Outdated
if in_directives and _DIRECTIVE.match(raw.strip()):
continue
in_directives = False
rewritten.append(raw.replace(image, reference, 1) if number == line else raw)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bba02df. The base is pinned as a token across the whole logical FROM instruction, so a continued FROM gets the resolved reference. Covered by a test.

user, uid, gid, home = contract.user, contract.uid, contract.gid, contract.home
return [
f"RUN pwd > {TASK_WORKDIR_FILE} && chmod 0644 {TASK_WORKDIR_FILE} "
f'&& if [ "$(pwd)" != / ]; then chown -R {uid}:{gid} "$(pwd)"; fi',

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bba02df. The ownership change now leaves /opt and /opt/datalayer with root, and a real build with WORKDIR /opt passes the doctor.

Comment thread code_sandboxes/environments/resolve.py Outdated
find_links = "/solve/wheelhouse"
# Solved for the interpreter the image has, not the one the spec
# names: the layer installs into that one.
python_version = '"$(python3 -c \'import sys; print("%d.%d" % sys.version_info[:2])\')"'

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bba02df. The solve exports the interpreter version it saw, and the lock's # python: header and python_version use that version.

Comment thread code_sandboxes/environments/resolve.py Outdated
Comment on lines +1075 to +1077
"RUN (command -v python3 >/dev/null && python3 -m pip --version >/dev/null 2>&1) || "
"(apt-get update -qq && apt-get install -y --no-install-recommends "
"python3 python3-pip ca-certificates && rm -rf /var/lib/apt/lists/*)",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bba02df. pip is bootstrapped for the selected interpreter with ensurepip --default-pip, and only a distribution's Python goes through apt.

subprocess.run(["docker", "rm", "-f", name], capture_output=True)


@pytest.mark.skipif(not (REAL and PLANE.is_dir()), reason="needs Docker, the network and the plane")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bba02df. The skip now needs PLANE_DOCKERFILES to be set and to name an existing directory.

…es past the one base, the base pinned across continuations, the reserved tree left to root, the lock says the Python it was solved for, pip for the Python the image kept; pytest-asyncio's loop scope set
@echarles
echarles merged commit 2f2d536 into main Oct 30, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants