Sandbox and Multi-user xunc / xunx¶
xunc runs one session in Docker; xunx runs a long-lived container per user behind a single gateway. Both share the same image (default xun); see Installation for how to build it.
xunc: container session¶
xunc does not run an agent itself. It does exactly three things: prepare /workspace, forward environment variables, and start xuns (or the command you specify) inside the container.
xunc # temporary workspace in the container, starts the web service
xunc . # bind-mount the current directory as /workspace
xunc --copy . # copy it into /workspace instead (changes never sync back to the host)
xunc --exec "xun" # run the terminal entrypoint in the container
xunc --exec "" # empty string, falls back to the image's default CMD
xunc --env "FOO=1,PATH" # direct assignment + forwarding by name
| Argument | Type | Default | Description |
|---|---|---|---|
mount |
positional | none | directory to mount / copy into /workspace |
--copy |
flag | off | copy instead of bind-mounting |
--image |
str | xun |
image to use |
--env |
str, repeatable | [] |
comma-separated: NAME=VALUE assigns directly, anything else is forwarded from the host by wildcard |
--name |
str | xun-<uuid8> |
container name |
--network |
bridge / host |
bridge |
network mode |
--port |
str, repeatable | ["18960"] |
ports published in bridge mode; several may be given comma-separated |
--exec |
str | xuns |
command run inside the container; an empty string falls back to the image's default CMD |
Mount and copy¶
- Without
mount: the container's own working directory (/home/app) becomes the workspace, invisible to the host. - Bind mount (default): changes inside the container land directly in the host directory;
--userns=hostkeeps ownership consistent. --copy: copied in withdocker cpand owned exclusively by the container; the original host directory is untouched.- Either way,
config.json,extensions/, andskills/under the host$XUN_HOMEare copied into the container's/.xun;x/(thexunxuser database) andconversation/(session archives) are skipped.
Environment forwarding¶
The container pins ENV XUN_HOME=/.xun, so the forwarding rules from the host environment are:
XUN_*and_XUN_*are always forwarded;XUN_HOMEis explicitly excluded.- In
--env,NAME=VALUEassigns directly; entries without=are matched against host variables by wildcard; a bareNAMEis forwarded as an exact match. --envmay be repeated, and the values accumulate.
For example, to bring the host's proxy settings and a custom model endpoint into the container:
export XUN_OPENAI_BASE_URL=https://gateway.internal/v1
xunc . --env "HTTP_PROXY,NO_PROXY,XUN_OPENAI_*"
Ports and time zone¶
In bridge mode, only the ports listed in --port get -p <port>:<port>; with host networking everything passes straight through.
The container's time zone follows the host's /etc/localtime.
xunx: multi-user gateway¶
xunx maintains one long-lived container per user and serves them all through a single gateway port; the internal container port always starts at 18960.
xunx user-add alice
xunx serve --host 0.0.0.0 --port 18960
xunx shell alice --cwd /workspace
xunx pause alice
xunx resume alice
xunx upgrade --all
xunx user-list
Subcommands¶
| Subcommand | Purpose |
|---|---|
user-add <user> |
add a user, generate a random token, and print it once |
user-del <user> |
delete a user and its token, and remove its container on a best-effort basis |
user-list |
list user names and tokens |
serve |
run the multi-user gateway |
pause <user> / resume <user> |
pause / resume a container (state is kept; reached through the gateway address) |
upgrade [user …] / --all |
set a flag; the next reconciliation rebuilds the container from the current image |
shell <user> |
enter the user container's shell or run a given command |
serve arguments¶
| Argument | Default | Description |
|---|---|---|
--host |
0.0.0.0 |
gateway listen address |
--port |
18960 |
gateway's external port; 0 lets the system assign one |
--port-range |
17960-18959 |
host port range assigned to user containers; must be disjoint from --port |
--image |
xun |
image; within the range its availability is validated per user-instance |
--env |
[] |
same forwarding logic as xunc --env |
Lifecycle and caveats¶
servereconciles every 5 seconds: if the user is running, forward to its address; if the user exists but the container is gone, rebuild it; containers that should not exist (user deleted, image replaced,--imagechanged) are stopped and removed.- Gateway tokens and user tokens are both cached for 10 seconds (positive and negative caching alike), so after
user-addyou wait at most that long for it to take effect. pauseonly stops, never removes;resumestarts it again. Reclamation triggered byuser-delor a configuration change removes the container outright.- Containers are ephemeral instances: stopping
xunxdoes not stop them, but a Docker daemon restart orxunx upgraderemoves them. - The gateway validates requests by the
Xun-Authheader or?token=. A user token is a 44-character urlsafe string (exactly the length of an internal session token), so the gateway URL can be handed to users directly. user-add,user-list, anduser-delonly read and write$XUN_HOME/x/xunx.db; they do not requireserveto be running.
What the image contains¶
docker/Dockerfile is a multi-stage build that produces a runtime with a full toolchain: a Python virtual environment (dependencies resolved from pyproject.toml),
Node and the frontend build, the Playwright browsers (system dependencies and Chinese fonts included), a Rust toolchain, ffmpeg / Git / ripgrep and other common commands,
plus a dedicated non-root user. Inside the image ENV XUN_HOME=/.xun and XUN_INFO_TOOL_OVERRIDE=1, and the startup command is xuns --host 0.0.0.0 --manage-sessions ..
Containers start with --cap-drop ALL; xunc adds no hardening of its own. If you need stronger isolation, layer your own resource and network limits on top.
Related entrypoints¶
- What runs inside the container is exactly the xun / xuns session.
- To run extension commands inside a container:
xunc --exec "xune stat …", see Extension CLI Entry xune.