Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changes/unreleased/+container-local-runtime-user.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
kind: Added
body: Add a blueprint-configurable container-local runtime account, with stable native Windows identity mapping and identity-specific final runtime layers for staging and installation.
15 changes: 9 additions & 6 deletions docs/APT_PROVIDER.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,10 @@ environment.

Container root in Docker Desktop remains root only inside the Linux container
and Desktop VM, not macOS root or Windows Administrator. Native-Linux user-scope
containers use the invoking UID/GID. Docker Desktop user-scope containers use a
stable Reploy-managed non-root Linux identity recorded in deployment state.
containers use the invoking UID/GID. macOS user-scope containers use the
invoking Unix UID/GID, while native Windows maps the invoking SID
deterministically to a stable nonzero Linux UID/GID. The final runtime layer
supplies the blueprint's container-local account name for that numeric identity.
Reploy validates portable output access and mount destinations while building,
then checks host mount-source existence and policy before runtime; Docker and
the workload report identity-dependent mount permission failures. Linux system
Expand Down Expand Up @@ -2420,10 +2422,11 @@ not public `type: apt` components.
neutralized entrypoint/command/healthcheck behavior, explicit working
directories and users, runtime-only base environment defaults, provider
environment isolation, informational exposed ports, and `SIGTERM` shutdown.
- Runtime-identity tests proving native-Linux user scope uses the invoking
UID/GID, Docker Desktop uses a recorded Reploy-managed non-root Linux identity,
system scope uses its service account, base `USER` is ignored, container root
never implies Desktop host root/Administrator, and every declared mount is
- Runtime-identity tests proving Unix user scope uses the invoking UID/GID,
native Windows maps its SID to a stable nonzero Linux identity, the runtime
layer supplies the configured local account, system scope uses its service
account, base `USER` is ignored, container root never implies Desktop host
root/Administrator, and every declared mount is
usable by the selected identity. Output-access cases cover portable
`a+rX`-equivalent Python roots; rejection of base/APT exports that rely on
owner, group, or ACL access; inaccessible parent and link-target directories;
Expand Down
16 changes: 7 additions & 9 deletions docs/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,13 @@ This file is the day-to-day queue for design and implementation gaps.
protection, build locks, cache identity, and actionable missing-export
diagnostics. Do not add a parallel direct-command form such as `base.rm`.

- [ ] `P1` Implement the promised Docker Desktop runtime identity.
Current macOS and Windows user-scope planning does not provide the stable
Reploy-managed non-root Linux identity described by the environment
model; native Windows runtime discovery can currently collapse to
container identity `0:0`. Define and persist a non-root identity for
Docker Desktop, propagate its canonical supplementary groups, reject
accidental root, and add native macOS/Windows plus Docker integration
evidence across staged and installed current-user workloads and
transient commands.
- [ ] `P1` Complete native Docker Desktop identity evidence.
The Linux-container contract now creates a real local account, uses the
invoking Unix UID/GID, and maps native Windows SIDs to stable nonzero
numeric identities. Add native macOS and Windows Docker Desktop evidence
across staged and installed current-user workloads and transient
commands, including account-name resolution, stable Windows mapping, and
explicit confirmation that no accidental `0:0` identity is selected.

- [ ] `P2` Define cancellation at the admission boundary.
Specify the authoritative outcome when a waiting caller is promoted at
Expand Down
9 changes: 5 additions & 4 deletions docs/BLUEPRINT_ENVIRONMENT_IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,11 +436,12 @@ Ownership:

- Staging has no install scope and uses the backend's current-user container
identity policy.
- On native Linux, staged and installed user containers use the invoking user's
- On Unix hosts, staged and installed user containers use the invoking user's
numeric UID/GID.
- On Docker Desktop, staged and installed user containers use a stable,
recorded Reploy-managed non-root Linux UID/GID inside the Desktop VM rather
than the macOS or Windows account's numeric identity.
- On native Windows, staged and installed user containers map the invoking SID
deterministically to a stable nonzero Linux UID/GID.
- The final Linux runtime layer supplies the blueprint's container-local
account name for the effective numeric identity.
- User-scope operations warn when overriding image `USER` or ignoring
`system.account`.
- Installed system scope uses the resolved service account.
Expand Down
83 changes: 57 additions & 26 deletions docs/BLUEPRINT_ENVIRONMENT_MODEL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
status: Active
updated: 2026-07-27
updated: 2026-08-02
summary: Normative blueprint environment, workload, application, provider contribution, lifecycle, and Docker rendering model.
supersedes: docs/CROSS_PLATFORM_INSTALL_LOCATIONS.md
---
Expand Down Expand Up @@ -90,14 +90,23 @@ environment:
packages: {} # Environment-owned package contributions.
applications: {} # Application-owned packages, options, and executables.
allow_concurrent: auto # App-command and shell overlap policy.
runtime:
user: reploy # Container-local account name; defaults to reploy.
terminal: {} # Terminal/color integration.
install: {} # Installation target, identity, and success output.
mounts: {} # Runtime filesystem contracts.
commands: {} # Public commands using application executables.
workload: {} # Optional persistent primary workload.
```

Optional empty nodes are omitted in an actual blueprint. Backend-specific
Optional empty nodes are omitted in an actual blueprint. `runtime.user` names
the ordinary non-root account inside the target environment; it does not select
a host account or grant root. It must be a portable lowercase Unix account name:
one to 32 bytes, beginning with a lowercase ASCII letter or underscore, followed
only by lowercase ASCII letters, digits, underscores, or hyphens. `root` is
reserved and cannot be selected through this field. If the base image already
defines the same account name with a different numeric ID, runtime-layer
construction fails rather than rewriting that unrelated account. Backend-specific
runtime choices remain under the top-level `docker` node.

## Internal Execution Phases
Expand Down Expand Up @@ -249,9 +258,9 @@ workload type.

Every command invocation is one-shot by default and is expected to exit with a
status. In Docker, Reploy runs it in a transient container created from the same
materialized environment image, as the configured non-root runtime user, with the same managed
paths and application configuration as the workload container. The transient
container is removed when the command exits. Selecting a command as
materialized environment image, as the effective runtime user, with the same
managed paths and application configuration as the workload container. The
transient container is removed when the command exits. Selecting a command as
`environment.workload.command` is the only operation that promotes it to the
persistent container entrypoint.

Expand Down Expand Up @@ -1344,17 +1353,39 @@ in the resulting layer. APT/dpkg, RPM/DNF, and Alpine/APK installation may run
as root in their build steps. Python, Go, and Rust layers use the permissions
required to populate their final image paths.

Runtime ownership comes from the backend and install scope, never from the base
image's configured `USER`. Reploy supplies an explicit user for every container:
Runtime authority comes from the backend and install scope, never from the base
image's configured `USER`. Reploy supplies an explicit numeric identity for
every container:

- provider materialization uses the provider-declared build identity, including
container root where system-package installation requires it;
- a native-Linux current-user install uses the invoking user's numeric UID/GID;
- a Docker Desktop current-user install uses a stable Reploy-managed non-root
Linux UID/GID inside the Desktop VM, recorded in deployment state; this is a
container identity, not the macOS or Windows account running Docker Desktop;
- a native-Linux or macOS current-user install uses the invoking user's numeric
UID/GID;
- a native-Windows current-user install maps the invoking Windows SID
deterministically to a stable nonzero Linux UID/GID; this is a container
identity, not a Windows account inside the image;
- a Linux system install uses the resolved service account.

For the current Linux-container backend, Reploy also materializes a real local
account in the final runtime layer. Its name is
`environment.runtime.user`, defaulting to `reploy`, while its UID/GID are the
effective numeric authority above. The account and its numeric identity are
locked build inputs, so changing either makes reuse stale. The blueprint name
is deliberately independent of a Windows domain account or Unix host account.
An effective UID of zero uses the existing local name `root`; a blueprint
cannot request root merely by naming it.

This is a portable blueprint contract with target-specific realization. The
current backend writes Linux account databases. A future native-Windows or
other target backend may realize the same local identity through different OS
mechanisms rather than emulating `/etc/passwd`.

When installation selects a different numeric authority from staging, Reploy
reuses the validated provider graph and rebuilds only the final
account/verifier runtime layer and its validation labels. The installed lock
records that identity-specific final image; the staged generation remains
bound to the staging user.

Docker Desktop mediates explicitly shared host files through the Desktop user.
The container identity still controls permissions inside the container, named
volumes, and the container-visible mode of mounted paths. Reploy does not assume
Expand Down Expand Up @@ -1383,14 +1414,13 @@ no runtime-access record. Docker container creation and the workload report
permission failures that depend on the actual runtime identity or mount
implementation.

Before a current-user install, Reploy reports the selected policy and numeric
container UID/GID. On native Linux it identifies the invoking host user; on
Docker Desktop it explains that the identity exists only inside the Linux
container/VM. The warning also states that the image's configured user is
overridden, the image must tolerate the selected non-root identity, and
persistent writes are available only through declared writable paths. If
system `account` configuration is present, Reploy reports that it does not apply
to current-user scope.
Before a current-user install, Reploy reports the selected policy, local account
name, and numeric container UID/GID. On Unix hosts it identifies the invoking
host identity; on native Windows it explains that the numeric mapping exists
only inside the Linux container/VM. The warning also states that the image's
configured user is overridden and persistent writes are available only through
declared writable paths. If system `account` configuration is present, Reploy
reports that it does not apply to current-user scope.

The materialized image is a private Docker-backend resource, not another
environment-schema object. A provider node has a semantic bundle identity, an
Expand Down Expand Up @@ -1881,7 +1911,8 @@ checks against the exact immutable image:

1. Its destination is a normalized absolute path other than `/` and does not
overlap `/dev`, `/proc`, `/sys`, `/run/secrets`, or Docker-managed
`/etc/hostname`, `/etc/hosts`, or `/etc/resolv.conf`.
`/etc/hostname`, `/etc/hosts`, or `/etc/resolv.conf`, or Reploy-generated
`/etc/passwd` or `/etc/group` account databases.
2. The destination is absent or an empty real directory. Existing files,
symlinks, non-directories, mountpoints, and non-empty directories fail. The
backend validates existing ancestors without following symlinks and needs
Expand Down Expand Up @@ -2170,12 +2201,12 @@ blueprint namespace.
a compatible user runtime. System scope fails clearly rather than silently
degrading to user scope.

`system.account` is ownership and container-process policy for a system install,
not another install scope. A native-Linux current-user install runs workload and
transient containers as the invoking numeric UID/GID; Docker Desktop instead
uses the Reploy-managed non-root container identity defined above. If
`system.account` is present, Reploy reports that it is inapplicable to user scope
along with the non-root image compatibility warning defined above.
`system.account` is host ownership and container-process authority for a system
install, not another install scope and not the container-local account name. A
current-user install uses the invoking Unix numeric identity or the stable
native-Windows SID mapping defined above. If `system.account` is present,
Reploy reports that it is inapplicable to user scope along with the non-root
image compatibility warning defined above.

### Install Target Defaults

Expand Down
52 changes: 33 additions & 19 deletions docs/CONTROLLED_SESSION_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,15 @@ root, network, capability, or filesystem grants required by their provider
contract. Those are explicit construction authorities, not implicit exceptions
or authorities inherited by application containers.

Reploy does not configure a second container-local username. Staged and
installed user-scope containers run as the invoking host user's numeric UID,
GID, and supplementary GIDs. Installed system-scope containers run as the host
account explicitly selected by `environment.install.system.account`, using
that account's numeric identity inside the container. The setting selects the
installation's host account; it is not a second container-user setting.
Every application image receives a container-local account. Its blueprint name
is `environment.runtime.user`, defaulting to `reploy`; it is not a host account
selector or an authority grant. Staged and installed user-scope containers use
the invoking Unix user's numeric identity, while native Windows maps the
invoking SID to a stable nonzero Linux UID/GID. Installed system-scope
containers use the host account selected by
`environment.install.system.account`. The current Linux-container backend
materializes the local account through Linux account databases; other target-OS
backends may realize the same contract differently.

If the effective runtime user is root, Reploy emits a precise warning that the
application can interfere with more of its container. Root does not implicitly
Expand Down Expand Up @@ -602,19 +605,29 @@ identity:
- installed system-scope execution uses the host account explicitly selected
by `environment.install.system.account`, resolved to its numeric identity.

Using the invoking identity for user-scope execution preserves ordinary host
file ownership and avoids predictable permission failures. The container
image's configured `USER` is not the runtime authority, and the image does not
need a matching named account. Reploy passes the effective numeric `UID:GID`
and the host account's supplementary GIDs, then supplies its ordinary transient
writable home. A non-root account with a root primary or supplementary group is
rejected rather than importing privileged host group membership into the
container.
Using the invoking Unix identity for user-scope execution preserves ordinary
host file ownership and avoids predictable permission failures. Native Windows
instead derives a stable nonzero Linux UID/GID from the invoking SID. The
container image's configured `USER` is not the runtime authority. Reploy passes
the effective numeric `UID:GID` and applicable supplementary GIDs, supplies its
ordinary transient writable home, and adds a real local account named by
`environment.runtime.user` (default `reploy`) to the final runtime layer. The
name and numeric identity are locked build inputs. A non-root account with a
root primary or supplementary group is rejected rather than importing
privileged group membership into the container.

A controlled-session client inherits this identity and cannot override it. A
different system-scope identity is an installation configuration decision, not
a session capability.

The local account is an OS-neutral blueprint concept with target-specific
realization. The initial Linux-container backend writes `/etc/passwd` and
`/etc/group`; runtime mounts may not overlap those generated account-database
paths. A future native target backend may use its own account mechanism.
If an installation selects a different numeric account, Reploy preserves the
provider layers and rebuilds the final runtime-account layer for the installed
generation rather than changing the staged generation.

### Root Runtime Identity

Root applies when the effective runtime UID is `0`: because staged or
Expand Down Expand Up @@ -1050,7 +1063,8 @@ commands directly as the final identity, drops all capabilities, enables
prohibits privileged mode, host namespaces, and host devices in the common
plan. Live Docker tests inspect both runtime paths. Trusted production startup
verification is also implemented: Reploy packages the platform-specific probe
in a final runtime layer, records that layer outside the provider graph, and
in a final runtime layer, creates the locked container-local account there,
records that layer outside the provider graph, and
uses its fixed verify-and-exec contract as the outermost process for persistent
workloads, transient commands, shells, and lifecycle commands. The verifier
fails closed unless `/proc/self/status` reports seccomp filtering,
Expand Down Expand Up @@ -1167,11 +1181,11 @@ analysis. Privileged application containers remain outside this design.
- Host Reploy remains in both the PTY and lifecycle paths; the containers do not
receive a direct control connection to one another.
- Staged and installed user-scope application containers retain practical host
ownership by using the invoking host identity, which may be unnamed inside
the image.
ownership through their effective numeric identity and receive a predictable
container-local account name.
- Installed system-scope application containers use the configured host
service account's numeric identity without requiring a corresponding
container-local username.
service account's numeric identity under the blueprint's container-local
account name.
- Root application containers are possible but visibly weaker.
- Root application containers never receive host input or shared-state binds;
local source requires the separately designed disposable-copy capability.
Expand Down
5 changes: 5 additions & 0 deletions internal/blueprint/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,18 @@ type Environment struct {
// provider internals migrate to first-class contribution identities.
Components map[string]Component `json:"-"`
AllowConcurrent ConcurrentRunPolicy
Runtime EnvironmentRuntime
Comment thread
omry marked this conversation as resolved.
Terminal Terminal
Install Install
Mounts map[string]EnvironmentMount
Commands map[string]Command
Workload *Workload
}

type EnvironmentRuntime struct {
User string
}

type EnvironmentPackages struct {
OS []APTPackageRequest
}
Expand Down
Loading
Loading