Technical Details

secdev is a tree of container images, a launcher, a graded sandbox profile, and one unusual idea: the container writes a description of itself for the agent to read.

The workflow showed a session from the user’s side. This page opens the machinery: which images exist and why, what the sandbox profile enforces, what the manifest, the file the container writes about itself, tells the agent, and how host skills and host services are let into the container.

The image family

A single container carrying every tool anyone might need would be enormous and mostly wasted. Instead secdev builds a tree of images that inherit from one another. Each carries the same set of coding agents; each adds the toolchain for one kind of work. The tree of images:

The image tree. Each image inherits everything from its parent, so a physics image is also a LaTeX image is also a general development image.

What each image adds, nested as in the tree, so that an indented image inherits everything from the image above it:

  • base is ordinary development: C/C++, Rust, Go, Python and Node toolchains, uv for Python environments, and the usual search and text tools. Every agent lives here.
    • browser adds Playwright driving Chromium and Firefox without a screen (headless browsers), for automation, not for logging in.
      • web adds Apache serving the working directory, PHP and Hugo. This site is built in the web image.
      • opencms adds the skill for the university’s content management system; see OpenCMS .
    • tex adds TeX Live, latexmk, biber, pandoc, linters and two skills (editorial pass, sketch-to-TikZ).
      • newton adds the scientific Python stack, Maxima, Octave, gnuplot, Fortran, the Lean proof system, and the fixed project layout for research (the research scaffold) documented under Research .
        • einstein adds SSH access to a host with graphics processors (GPUs), a computer-algebra host and the compute cluster with its SLURM job scheduler, the program that queues jobs and starts them when nodes are free, and a shared remote home. Hosts come from the config, never from the image.
      • office adds LibreOffice without its window, text recognition for scans (OCR) and PDF-form tooling with document skills.
    • local routes OpenCode to a model on institute hardware.

./build.sh <variant> builds one image and its parents.

The sandbox profile

A sandbox profile is the set of limits a container runs under. --security N selects one at launch, and the config sets the default. In the table, sudo means running a command as the machine’s administrator; withholding it stops the agent changing the container itself. The two profiles in daily use:

LevelsudoCaps and privileges/tmpTypical use
0yesnonecontainer filesystem, no size capTrusted work that must install system packages
1noRAM, CPU and process-count caps from the config; no new privileges; almost all Linux capabilities (individual administrator powers) droppedan in-memory filesystem, one per containerThe everyday default

The “Caps and privileges” column has one exception: when einstein mounts a remote home, the launcher keeps the few privileges that sshfs, a remote directory reached over SSH, needs.

Higher levels exist that also restrict the container’s network to an allow list of destinations, with the container running behind a firewall of its own. They are experimental and not fully operational, so the table leaves them out.

When a resource cap (memory, processor or process count) cannot be applied because the container runs without root privileges, the launcher drops it with a warning, and host_info inside the container shows which caps applied.

The manifest: telling the agent where it is

An agent dropped into an unfamiliar environment discovers its constraints by running into them: it tries sudo, fails, tries something else; it assumes a tool exists and burns a minute finding out it does not. So at every container start, secdev writes SECDEV.md into the working directory: the tools, services, isolation, active security settings, reachable endpoints, and the things the agent must not attempt. The manifest is assembled from one fragment per image layer and filled in with the current settings at start, so the file describes the container that is running, at the level it is running at. AGENTS.md, the file most agents read first, receives a short managed block that points at the manifest; content outside that block is never touched.

Skills: bundled and mounted

A skill is a directory holding a SKILL.md procedure plus any scripts or reference material it needs; an agent loads it when a task matches. secdev links two sources into every agent’s skill directory at container start: bundled skills, baked into the image layer whose tooling they need, and host skills from a directory named in the config or given with --skills DIR for one run, mounted read-only. A bundled skill wins over a host skill of the same name. Editing a host skill takes effect at once; adding or removing one needs a container restart.

The configuration file

The installer writes a first ~/.config/secdev/config.toml, readable by you alone because it can hold keys. The sections a user edits:

[container]
memory     = "8g"       # caps at level 1
cpus       = "4"
pids_limit = "512"
tmpfs_size = "1g"

[security]
default_level = 1

[runtime.skills]
# your own skills, mounted into every container
dir = ""

# einstein: user@host per helper, empty disables it
[hosts]
gpu = ""
mathematica = ""
slurm = ""
remote_home = ""

Changes take effect on the next launch, except [hosts], which needs a rerun of ./install.sh to regenerate the SSH configuration. Further sections configure three things: the local variant’s route to its model (optionally through an SSH tunnel to a model elsewhere on the institute network), the logins to keep across containers, and the agent identities described next.

Agent identities (optional)

A container is anonymous by default: it holds none of your credentials, and the manifest forbids it to push. An identity lifts that ban for one named agent, so several agents can work on one project through a shared Git remote. secdev identity add generates an SSH keypair, records the Git server’s host key so that a server presenting a different key is refused, and prints the public key for you to register with the Git-server account you have set up for that agent; secrets never enter the config file. A container started with --identity commits and pushes as that account, keeps its own agent logins, and gains a manifest section that states the narrow conditions under which the ban is lifted: push to origin only, no new remotes, no force-push, no tags. The identity commands:

# once per agent, on the host; asks for account and email
secdev identity add pi
secdev identity list
secdev identity show pi       # settings and public key
secdev --identity pi newton   # run as that agent
secdev attach --identity pi
secdev identity remove pi     # credentials and saved logins

Give each agent its own clone: two containers in one directory fight over the working tree. The project layout for several agents that goes with identities is described under Research’s Technical Details .

MCP services (optional)

MCP (Model Context Protocol) is the standard by which an agent calls tools that live in another process. A container needs none, but secdev lets OpenCode use MCP servers declared in the configuration file, one section each. A remote server is a URL. A server on the host can instead expose a Unix socket, a file on the host’s disk: the launcher mounts the socket’s directory read-only and relays it to an address inside the container that only the container itself can reach (a loopback address). No endpoint or token is baked into an image; both are read from the config at launch. Wingman uses this route to give an agent hands on the institute’s servers without giving it the key, and Wingman’s Technical Details shows the configuration and the relay.

OpenCode in a browser (optional)

OpenCode ships a web front end; secdev can start it instead of a shell:

# host loopback, HTTP basic auth
secdev --opencode-web
secdev local --opencode-web --port N   # a different host port

The front end requires a password in the config. It listens only on the host’s own loopback address, which other machines cannot reach, unless an explicit --publish says otherwise. It speaks plain HTTP, so any exposure beyond the machine belongs behind a TLS proxy. A system prompt file named in the config sets the instructions of the default agent without a rebuild.