geniejars
v0.2.13
Published
Linux geniejars sandboxing — library and CLI for running processes as isolated system users
Readme
GenieJars
Run sandboxed processes as isolated Linux users — without ever needing runtime sudo.
Inspired by Docker, but lightweight and aimed at running AI agents (like Claude Code) in a confined environment where they can operate freely without being able to touch anything outside their own home directory.
How it works
Each geniejars is a real Linux user with its own home directory. A shell server manages agent lifecycle on demand. The manager communicates with agents over Unix sockets to build environments and run processes — no sudo required at runtime.
manager (you)
│
├─ shell server ────────────────► agents (one per geniejars, started on demand)
│
├─ COPY files ──────────────────► geniejars home (via group write access)
│
└─ socket ──────────────────────► agent (running as geniejars)
│
├─ RUN (setup commands)
└─ start main processIsolation guarantees
- Geniejars can only write inside their own home directory
- Geniejars have no sudo access
- Geniejars cannot connect to each other's agent sockets
- The manager can read and write into any geniejars's home (via group membership)
Requirements
- Linux
- Node.js >= 18.17
setfacl(aclpackage)
Installation
npm install geniejars # library + CLI
npx geniejars preconfig # create geniejars.config.json from template
# edit geniejars.config.json
sudo npx geniejars setup # one-time system setup (requires root)
su - $USER # new login to pick up group membership
geniejars sys up # start the shell serverTo remove everything:
sudo geniejars uninstallQuick start
geniejars example install # install helloworld + libcall examples
# run helloworld manually
geniejars alloc -s # allocate and select a geniejars
geniejars build ./geniejars-example/helloworld
geniejars app up # starts the webserver (Ctrl+C to stop)
# or run the library demo
node geniejars-example/libcall.mjsCLI reference
System
geniejars preconfig # create geniejars.config.json in current directory
geniejars setup # one-time system setup (run as root)
geniejars uninstall # remove all geniejars and system config (run as root)
geniejars sys up # start shell server
geniejars sys down # stop shell server and all agents
geniejars sys status # show running agentsPool management
geniejars alloc # allocate a free geniejars
geniejars alloc -s # allocate and select
geniejars alloc --tag myapp # allocate with a tag
geniejars free # deallocate selected geniejars
geniejars free sub002 # deallocate by name
geniejars status # show pool status table
geniejars status --json # full state as JSONSelection
geniejars select sub001 # select geniejars for subsequent commands
geniejars unselect # clear selectionMost commands use the selected geniejars when no name is given. Selection is stored in .geniejars-selected in the current directory, or via the GENIEJARS environment variable.
Building
geniejars build ./myapp # build from folder (loads JFile inside)
geniejars build ./myapp/JFile # build from explicit file
geniejars build sub002 ./myapp # build into specific geniejarsRunning
geniejars app up # start CMD (waits, streams output)
geniejars app up -n # start CMD detached (returns immediately)
geniejars app down # stop CMD
geniejars exec ls -la # run one-off command (direct args)
geniejars exec "ls | grep x" # run one-off shell command (single string → sh -c)Other
geniejars normalize # fix file permissions via ACLs
geniejars mount ./link # symlink geniejars home to ./link
geniejars mount -C ./common # symlink commonDir to ./common
geniejars unmount ./link # remove symlinkJFile
The JFile format mirrors Dockerfile. Supported directives:
| Directive | Description |
|---|---|
| TAG name | Label this build (used for alias generation) |
| ENV KEY=VALUE | Set environment variable |
| WORKDIR path | Set working directory (relative to geniejars home) |
| COPY src dest | Copy files from manager into geniejars environment |
| RUN command | Execute setup command as the geniejars |
| CMD ["cmd", "arg"] | Define the main process |
| DEPEND path | Fail build if path does not exist on the manager |
| ADDPATH path | Prepend path to PATH at runtime |
| IMPORT KEY | Copy env var from manager environment at build time |
Port placeholders ${PORT(1)}, ${PORT(2)} etc. are substituted with the geniejars's pre-assigned ports at build time. ${COMMON} expands to commonDir.
See JFILE.md for full documentation.
Library usage
import * as geniejars from 'geniejars'
const config = await geniejars.loadConfig()
const name = await geniejars.allocate(config, { tag: 'myapp' })
const prepared = await geniejars.prepare(config, name, './JFile')
await geniejars.ensureAgent(config, name)
const [cmd, ...args] = prepared.cmd
await geniejars.startAs(config, name, cmd, args, { cwd: prepared.workdir })
await geniejars.stopProcess(config, name)
await geniejars.deallocate(config, name)loadConfig() reads geniejars.config.json from $GENIEJARS_HOME or the current directory.
See geniejars-example/libcall.mjs (installed via geniejars example install) for a full working example.
Configuration
geniejars.config.json:
| Key | Description |
|---|---|
| poolPrefix | Geniejars name prefix (default sub) |
| poolSize | Number of geniejars in the pool |
| poolPadding | Zero-padding for geniejars numbers |
| homeBase | Parent directory for geniejars homes (default /home) |
| managerUser | The Linux user running the manager |
| managerGroup | Group granting manager access |
| portBase | First port in the allocated range |
| portCount | Total ports in the pool |
| portsPerGeniejars | Ports assigned per geniejars |
| socketDir | Directory for Unix sockets |
| stateFile | Path to pool state JSON |
| lockFile | Path to state lock file |
| logDir | Directory for app log files |
| commonDir | Shared read-only directory accessible to all geniejars |
| commonUser | Linux user owning the common directory |
| backupDir | Directory for home directory backups |
| agentIdleTimeout | Seconds before an idle agent self-terminates (0 to disable) |
| shutdownGraceMs | Milliseconds to wait for busy agents during sys down |
| nodePath | Path to node binary (defaults to current node executable) |
Project structure
src/
agent.mjs agent process — runs as geniejars, handles socket requests
server.mjs shell server — manages agent lifecycle on demand
pool.mjs state file, locking, config
allocate.mjs allocate/deallocate geniejars and ports
prepare.mjs JFile parser and executor
run.mjs socket client (talks to agents and shell server)
normalize.mjs fix file permissions via ACLs
index.mjs public API
script/
geniejars.mjs unified CLI entry point
setup-geniejars.mjs one-time system setup (run as root)
uninstall-geniejars.mjs remove all geniejars and system config (run as root)Common Issues
See COMMON-ISSUES.md for solving common issues.
Links and contact
Github Contact me on my Twitter/X account
License
MIT — see LICENSE.md
