Skip to content

Commit de06d57

Browse files
committed
document env-aware workspace reads
1 parent f2c49e4 commit de06d57

1 file changed

Lines changed: 51 additions & 2 deletions

File tree

README.md

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ That distinction is the point of this project. Agent Container gives coding agen
2929
<p align="center">
3030
<a href="#quick-start">Quick Start</a> &middot;
3131
<a href="#why">Why</a> &middot;
32-
<a href="#threat-model"</a> &middot;
32+
<a href="#threat-model">Threat Model</a> &middot;
3333
<a href="#how-it-works">How It Works</a> &middot;
3434
<a href="#bindings">Bindings</a> &middot;
3535
<a href="#api">API</a> &middot;
@@ -145,6 +145,8 @@ Implemented today:
145145
- `WORKSPACE` supports `readText`, `writeText`, `list`, `stat`, `glob`, `grep`, and `remove`.
146146
- Workspace mode can be `live` or `shadow`; `shadow` copies the repository to a disposable temp directory.
147147
- Workspace mounts can expose additional paths as read-only or read-write logical mount points.
148+
- Workspace reads are env-aware: root `.env*` sources are exposed as filtered dotenv views, and env-like files outside configured env sources are denied.
149+
- `workspace.denyRead` can deny additional non-env paths from `WORKSPACE.readText` and `WORKSPACE.grep`.
148150
- `EXEC.run` starts allowlisted host commands with workspace-scoped cwd resolution, timeout handling, and selected env projection.
149151
- `EXEC.shell` exists, but only works when `allowShell` is enabled.
150152
- `ENV` exposes public variables and `SECRETS` exposes secret-classified variables.
@@ -199,6 +201,38 @@ const container = await createAgentContainer({
199201

200202
The workspace controller resolves logical paths against the matching mount and rejects path traversal outside that mount's physical root.
201203

204+
Env file reads:
205+
206+
```ts
207+
const container = await createAgentContainer({
208+
workspace: { root: process.cwd() },
209+
env: {
210+
include: ["PUBLIC_*"],
211+
processEnv: "none",
212+
},
213+
});
214+
215+
const envFile = await WORKSPACE.readText(".env");
216+
// PUBLIC_READ_KEY="hello-world"
217+
```
218+
219+
If `env.sources` is omitted, root-level `.env` and `.env.*` files are treated as env sources. Reading one of those files through `WORKSPACE` returns a synthetic dotenv file filtered by the same `env.include` and `env.exclude` rules used by `ENV`.
220+
221+
If `env.sources` is provided, only those file sources are readable as filtered env files. Other env-like files, such as `.env.local` when only `.env` is configured, are denied with `Path is not readable: <path>`.
222+
223+
Additional read denies:
224+
225+
```ts
226+
const container = await createAgentContainer({
227+
workspace: {
228+
root: process.cwd(),
229+
denyRead: ["**/*.secret"],
230+
},
231+
});
232+
```
233+
234+
`denyRead` applies to non-env file content reads and search. It does not select env sources; use `env.sources` for that. `list`, `stat`, and `glob` may still reveal filenames.
235+
202236
### EXEC
203237

204238
`EXEC` is brokered subprocess execution.
@@ -251,7 +285,7 @@ const token = await SECRETS.get("API_SECRET_TOKEN");
251285
const secretKeys = await SECRETS.keys();
252286
```
253287

254-
Env policy can load from `.env`, `.env.local`, inline values, and selected process env values:
288+
Env policy can load from root `.env*` files, explicit file sources, inline values, and selected process env values:
255289

256290
```ts
257291
const container = await createAgentContainer({
@@ -265,6 +299,19 @@ const container = await createAgentContainer({
265299
});
266300
```
267301

302+
When `sources` is omitted, Agent Container discovers root-level `.env` and `.env.*` files. When `sources` is provided, only those sources are used.
303+
304+
```ts
305+
const container = await createAgentContainer({
306+
workspace: { root: process.cwd() },
307+
env: {
308+
sources: [{ type: "file", path: ".env" }],
309+
include: ["PUBLIC_*"],
310+
processEnv: "none",
311+
},
312+
});
313+
```
314+
268315
### Network
269316

270317
There is no first-class `NET` binding yet. Current network policy controls `workerd`'s global outbound fetch behavior:
@@ -320,11 +367,13 @@ interface AgentContainerOptions {
320367
sourcePath: string;
321368
mode: "ro" | "rw";
322369
}[];
370+
denyRead?: readonly string[];
323371
};
324372
env?: {
325373
sources?: readonly EnvSource[];
326374
include?: readonly string[];
327375
exclude?: readonly string[];
376+
publicPatterns?: readonly string[];
328377
secretPatterns?: readonly string[];
329378
processEnv?: "none" | "allow-matching" | "all";
330379
};

0 commit comments

Comments
 (0)