geniejars 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMMON-ISSUES.md +55 -0
- package/LICENSE.md +9 -0
- package/README.md +218 -0
- package/geniejars.config.example.json +21 -0
- package/package.json +22 -0
- package/script/geniejars.mjs +667 -0
- package/script/setup-geniejars.mjs +208 -0
- package/script/uninstall-geniejars.mjs +122 -0
- package/src/agent.mjs +194 -0
- package/src/allocate.mjs +96 -0
- package/src/index.mjs +5 -0
- package/src/normalize.mjs +26 -0
- package/src/pool.mjs +158 -0
- package/src/prepare.mjs +219 -0
- package/src/run.mjs +162 -0
- package/src/server.mjs +262 -0
package/COMMON-ISSUES.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# GenieJars — Common Issues
|
|
2
|
+
|
|
3
|
+
## "No config found" when running sudo geniejars setup
|
|
4
|
+
|
|
5
|
+
You need to create a config file in your project directory first:
|
|
6
|
+
|
|
7
|
+
geniejars preconfig
|
|
8
|
+
# edit geniejars.config.json to match your system
|
|
9
|
+
sudo geniejars setup
|
|
10
|
+
|
|
11
|
+
GenieJars looks for `geniejars.config.json` in the current directory.
|
|
12
|
+
Make sure you are in your project directory when running setup.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Group membership not active after setup
|
|
17
|
+
|
|
18
|
+
Setup adds your user to new groups, but the change only takes effect in a new login session.
|
|
19
|
+
|
|
20
|
+
# Temporary workaround (stays in current directory):
|
|
21
|
+
su flamsidoo
|
|
22
|
+
|
|
23
|
+
# Recommended: log out and log back in fully.
|
|
24
|
+
|
|
25
|
+
After that, no more sudo or su required.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Permission denied on geniejars alloc
|
|
30
|
+
|
|
31
|
+
Example error: `EACCES: permission denied, rmdir '/home/gjar001/.config/...'`
|
|
32
|
+
|
|
33
|
+
The manager user cannot delete files that were created by the gjar user itself,
|
|
34
|
+
even with group write access. This happens when a previously used jar left files
|
|
35
|
+
behind that the manager has no permission to remove.
|
|
36
|
+
|
|
37
|
+
Workaround: run `sudo geniejars alloc` for the first alloc after setup,
|
|
38
|
+
or manually clear the home directory as root:
|
|
39
|
+
|
|
40
|
+
sudo rm -rf /home/gjar001/*
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## "You cannot publish over the previously published version"
|
|
45
|
+
|
|
46
|
+
Bump the version before publishing:
|
|
47
|
+
|
|
48
|
+
npm version patch
|
|
49
|
+
npm publish --registry http://localhost:4873
|
|
50
|
+
|
|
51
|
+
If the registry already has a higher version (e.g. 0.2.0) than your local package,
|
|
52
|
+
set the version explicitly:
|
|
53
|
+
|
|
54
|
+
npm version 0.2.1
|
|
55
|
+
npm publish --registry http://localhost:4873
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
Copyright 2026 Dusty Murray
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# GenieJars
|
|
2
|
+
|
|
3
|
+
Run sandboxed processes as isolated Linux users — without ever needing runtime sudo.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
manager (you)
|
|
13
|
+
│
|
|
14
|
+
├─ shell server ────────────────► agents (one per geniejars, started on demand)
|
|
15
|
+
│
|
|
16
|
+
├─ COPY files ──────────────────► geniejars home (via group write access)
|
|
17
|
+
│
|
|
18
|
+
└─ socket ──────────────────────► agent (running as geniejars)
|
|
19
|
+
│
|
|
20
|
+
├─ RUN (setup commands)
|
|
21
|
+
└─ start main process
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Isolation guarantees
|
|
25
|
+
|
|
26
|
+
- Geniejars can only write inside their own home directory
|
|
27
|
+
- Geniejars have no sudo access
|
|
28
|
+
- Geniejars cannot connect to each other's agent sockets
|
|
29
|
+
- The manager can read and write into any geniejars's home (via group membership)
|
|
30
|
+
|
|
31
|
+
## Requirements
|
|
32
|
+
|
|
33
|
+
- Linux
|
|
34
|
+
- Node.js >= 18.17
|
|
35
|
+
- `setfacl` (`acl` package)
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install geniejars # library + CLI
|
|
41
|
+
npx geniejars preconfig # create geniejars.config.json from template
|
|
42
|
+
# edit geniejars.config.json
|
|
43
|
+
sudo npx geniejars setup # one-time system setup (requires root)
|
|
44
|
+
su - $USER # new login to pick up group membership
|
|
45
|
+
geniejars sys up # start the shell server
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
To remove everything:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
sudo geniejars uninstall
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
geniejars example install # install helloworld + libcall examples
|
|
58
|
+
|
|
59
|
+
# run helloworld manually
|
|
60
|
+
geniejars alloc -s # allocate and select a geniejars
|
|
61
|
+
geniejars build ./geniejars-example/helloworld
|
|
62
|
+
geniejars app up # starts the webserver (Ctrl+C to stop)
|
|
63
|
+
|
|
64
|
+
# or run the library demo
|
|
65
|
+
node geniejars-example/libcall.mjs
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## CLI reference
|
|
69
|
+
|
|
70
|
+
### System
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
geniejars preconfig # create geniejars.config.json in current directory
|
|
74
|
+
geniejars setup # one-time system setup (run as root)
|
|
75
|
+
geniejars uninstall # remove all geniejars and system config (run as root)
|
|
76
|
+
geniejars sys up # start shell server
|
|
77
|
+
geniejars sys down # stop shell server and all agents
|
|
78
|
+
geniejars sys status # show running agents
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Pool management
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
geniejars alloc # allocate a free geniejars
|
|
85
|
+
geniejars alloc -s # allocate and select
|
|
86
|
+
geniejars alloc --tag myapp # allocate with a tag
|
|
87
|
+
geniejars free # deallocate selected geniejars
|
|
88
|
+
geniejars free sub002 # deallocate by name
|
|
89
|
+
geniejars status # show pool status table
|
|
90
|
+
geniejars status --json # full state as JSON
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Selection
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
geniejars select sub001 # select geniejars for subsequent commands
|
|
97
|
+
geniejars unselect # clear selection
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Most 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.
|
|
101
|
+
|
|
102
|
+
### Building
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
geniejars build ./myapp # build from folder (loads JFile inside)
|
|
106
|
+
geniejars build ./myapp/JFile # build from explicit file
|
|
107
|
+
geniejars build sub002 ./myapp # build into specific geniejars
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Running
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
geniejars app up # start CMD (waits, streams output)
|
|
114
|
+
geniejars app up -n # start CMD detached (returns immediately)
|
|
115
|
+
geniejars app down # stop CMD
|
|
116
|
+
geniejars exec ls -la # run one-off command (direct args)
|
|
117
|
+
geniejars exec "ls | grep x" # run one-off shell command (single string → sh -c)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Other
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
geniejars normalize # fix file permissions via ACLs
|
|
124
|
+
geniejars mount ./link # symlink geniejars home to ./link
|
|
125
|
+
geniejars mount -C ./common # symlink commonDir to ./common
|
|
126
|
+
geniejars unmount ./link # remove symlink
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## JFile
|
|
130
|
+
|
|
131
|
+
The `JFile` format mirrors Dockerfile. Supported directives:
|
|
132
|
+
|
|
133
|
+
| Directive | Description |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `TAG name` | Label this build (used for alias generation) |
|
|
136
|
+
| `ENV KEY=VALUE` | Set environment variable |
|
|
137
|
+
| `WORKDIR path` | Set working directory (relative to geniejars home) |
|
|
138
|
+
| `COPY src dest` | Copy files from manager into geniejars environment |
|
|
139
|
+
| `RUN command` | Execute setup command as the geniejars |
|
|
140
|
+
| `CMD ["cmd", "arg"]` | Define the main process |
|
|
141
|
+
| `DEPEND path` | Fail build if path does not exist on the manager |
|
|
142
|
+
| `ADDPATH path` | Prepend path to PATH at runtime |
|
|
143
|
+
| `IMPORT KEY` | Copy env var from manager environment at build time |
|
|
144
|
+
|
|
145
|
+
Port placeholders `${PORT(1)}`, `${PORT(2)}` etc. are substituted with the geniejars's pre-assigned ports at build time. `${COMMON}` expands to `commonDir`.
|
|
146
|
+
|
|
147
|
+
See [SUBFILE.md](./SUBFILE.md) for full documentation.
|
|
148
|
+
|
|
149
|
+
## Library usage
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
import * as geniejars from 'geniejars'
|
|
153
|
+
|
|
154
|
+
const config = await geniejars.loadConfig()
|
|
155
|
+
|
|
156
|
+
const name = await geniejars.allocate(config, { tag: 'myapp' })
|
|
157
|
+
const prepared = await geniejars.prepare(config, name, './JFile')
|
|
158
|
+
await geniejars.ensureAgent(config, name)
|
|
159
|
+
|
|
160
|
+
const [cmd, ...args] = prepared.cmd
|
|
161
|
+
await geniejars.startAs(config, name, cmd, args, { cwd: prepared.workdir })
|
|
162
|
+
|
|
163
|
+
await geniejars.stopProcess(config, name)
|
|
164
|
+
await geniejars.deallocate(config, name)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`loadConfig()` reads `geniejars.config.json` from `$GENIEJARS_HOME` or the current directory.
|
|
168
|
+
|
|
169
|
+
See `geniejars-example/libcall.mjs` (installed via `geniejars example install`) for a full working example.
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
`geniejars.config.json`:
|
|
174
|
+
|
|
175
|
+
| Key | Description |
|
|
176
|
+
|---|---|
|
|
177
|
+
| `poolPrefix` | Geniejars name prefix (default `sub`) |
|
|
178
|
+
| `poolSize` | Number of geniejars in the pool |
|
|
179
|
+
| `poolPadding` | Zero-padding for geniejars numbers |
|
|
180
|
+
| `homeBase` | Parent directory for geniejars homes (default `/home`) |
|
|
181
|
+
| `managerUser` | The Linux user running the manager |
|
|
182
|
+
| `managerGroup` | Group granting manager access |
|
|
183
|
+
| `portBase` | First port in the allocated range |
|
|
184
|
+
| `portCount` | Total ports in the pool |
|
|
185
|
+
| `portsPerGeniejars` | Ports assigned per geniejars |
|
|
186
|
+
| `socketDir` | Directory for Unix sockets |
|
|
187
|
+
| `stateFile` | Path to pool state JSON |
|
|
188
|
+
| `lockFile` | Path to state lock file |
|
|
189
|
+
| `logDir` | Directory for app log files |
|
|
190
|
+
| `commonDir` | Shared read-only directory accessible to all geniejars |
|
|
191
|
+
| `commonUser` | Linux user owning the common directory |
|
|
192
|
+
| `backupDir` | Directory for home directory backups |
|
|
193
|
+
| `agentIdleTimeout` | Seconds before an idle agent self-terminates (0 to disable) |
|
|
194
|
+
| `shutdownGraceMs` | Milliseconds to wait for busy agents during `sys down` |
|
|
195
|
+
| `nodePath` | Path to node binary (defaults to current node executable) |
|
|
196
|
+
|
|
197
|
+
## Project structure
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
src/
|
|
201
|
+
agent.mjs agent process — runs as geniejars, handles socket requests
|
|
202
|
+
server.mjs shell server — manages agent lifecycle on demand
|
|
203
|
+
pool.mjs state file, locking, config
|
|
204
|
+
allocate.mjs allocate/deallocate geniejars and ports
|
|
205
|
+
prepare.mjs JFile parser and executor
|
|
206
|
+
run.mjs socket client (talks to agents and shell server)
|
|
207
|
+
normalize.mjs fix file permissions via ACLs
|
|
208
|
+
index.mjs public API
|
|
209
|
+
|
|
210
|
+
script/
|
|
211
|
+
geniejars.mjs unified CLI entry point
|
|
212
|
+
setup-geniejars.mjs one-time system setup (run as root)
|
|
213
|
+
uninstall-geniejars.mjs remove all geniejars and system config (run as root)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT — see [LICENSE.md](./LICENSE.md)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"poolPrefix": "gjar",
|
|
3
|
+
"poolSize": 5,
|
|
4
|
+
"poolPadding": 3,
|
|
5
|
+
"homeBase": "/home",
|
|
6
|
+
"managerUser": "flamsidoo",
|
|
7
|
+
"managerGroup": "geniejars-mgr",
|
|
8
|
+
"stateFile": "/tmp/geniejars-state.json",
|
|
9
|
+
"lockFile": "/tmp/geniejars-state.lock",
|
|
10
|
+
"backupDir": "/tmp/geniejars-backups",
|
|
11
|
+
"socketDir": "/tmp/geniejars-sockets",
|
|
12
|
+
"nodePath": "/usr/bin/node",
|
|
13
|
+
"portBase": 10000,
|
|
14
|
+
"portCount": 50,
|
|
15
|
+
"portsPerGeniejars": 2,
|
|
16
|
+
"commonUser": "gjarcommon",
|
|
17
|
+
"commonDir": "/home/gjarcommon",
|
|
18
|
+
"logDir": "/tmp/geniejars-logs",
|
|
19
|
+
"agentIdleTimeout": 300,
|
|
20
|
+
"shutdownGraceMs": 10000
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "geniejars",
|
|
3
|
+
"version": "0.2.7",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Linux geniejars sandboxing — library and CLI for running processes as isolated system users",
|
|
6
|
+
"main": "src/index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.mjs"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"geniejars": "script/geniejars.mjs"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src/",
|
|
15
|
+
"script/",
|
|
16
|
+
"geniejars.config.example.json",
|
|
17
|
+
"COMMON-ISSUES.md"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18.17.0"
|
|
21
|
+
}
|
|
22
|
+
}
|